Skip to content

Commit 389b710

Browse files
committed
Merge branch 'develop'
* develop: Version bump. Lets return coverages as we need it anyways. Adding a method that will split track matched regions, useful for streaming scenarios only. Update README.md
2 parents 46a29a5 + 4509ffa commit 389b710

File tree

8 files changed

+87
-23
lines changed

8 files changed

+87
-23
lines changed

README.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -97,7 +97,6 @@ Every `ResultEntry` object will contain the following information:
9797
- `QueryMatchStartsAt` - returns time position where resulting track started to match in the query
9898
- `TrackMatchStartsAt` - returns time position where the query started to match in the resulting track
9999
- `TrackStartsAt` - returns an approximation where does the matched track starts, always relative to the query
100-
- `Coverage` - returns a value between [0, 1], informing how much the query covered the resulting track (i.e. a 2 minutes query found a 30 seconds track within it, starting at 100th second, coverage will be equal to (120 - 100)/30 ~= 0.66)
101100
- `Confidence` - returns a value between [0, 1]. A value below 0.15 is most probably a false positive. A value bigger than 0.15 is very likely to be an exact match. For good audio quality queries you can expect getting a confidence > 0.5.
102101
- `MatchedAt` - returns timestamp showing at what time did the match occured. Usefull for realtime queries.
103102

@@ -107,6 +106,8 @@ Every `ResultEntry` object will contain the following information:
107106
- `TotalTracksAnalyzed` - total # of tracks analyzed during query time. If this number exceeds 50, try optimizing your configuration.
108107
- `TotalFingerprintsAnalyzed` - total # of fingerprints analyzed during query time. If this number exceeds 500, try optimizing your configuration.
109108

109+
Read [Different Types of Coverage](https://github.com/AddictedCS/soundfingerprinting/wiki/Different-Types-of-Coverage) to understand how query coverage is calculated.
110+
110111
### Version 6.2.0
111112
Version 6.2.0 provides ability to query realtime datasources. Usefull for scenarious when you would like to monitor a realtime stream and get matching results as fast as possible.
112113

src/SoundFingerprinting.Tests/Properties/AssemblyInfo.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,5 +11,5 @@
1111
[assembly: AssemblyCulture("")]
1212
[assembly: ComVisible(false)]
1313
[assembly: Guid("4cac962e-ebc5-4006-a1e0-7ffb3e2483c2")]
14-
[assembly: AssemblyVersion("7.4.6.100")]
15-
[assembly: AssemblyInformationalVersion("7.4.6.100")]
14+
[assembly: AssemblyVersion("7.4.7.100")]
15+
[assembly: AssemblyInformationalVersion("7.4.7.100")]

src/SoundFingerprinting.Tests/Unit/Query/MatchesTest.cs

Lines changed: 30 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ namespace SoundFingerprinting.Tests.Unit.Query
33
using System.Collections.Generic;
44
using System.Linq;
55
using NUnit.Framework;
6+
using SoundFingerprinting.LCS;
67
using SoundFingerprinting.Query;
78

89
[TestFixture]
@@ -15,22 +16,22 @@ public void ShouldCollapseByQueryAt1()
1516
// b ------- 8|18 + 10 = 18|28
1617
var a = GetMatches(0f, 10f, 10d, 512f / 5512);
1718
var b = GetMatches(8f, 18f, 10d, 512f / 5512);
18-
19+
1920
Assert.IsTrue(a.TryCollapseWith(b, 1.48d, out var c));
2021
Assert.AreEqual(19.5d, c.TotalLength, 0.1);
2122
Assert.AreEqual(a.Count() + b.Count(), c.Count());
2223
Assert.AreEqual(0d, c.QueryAtStartsAt);
2324
Assert.AreEqual(10f, c.TrackAtStartsAt);
2425
}
25-
26+
2627
[Test]
2728
public void ShouldCollapseByQueryAt2()
2829
{
2930
// a ------------- 0|12 + 12 = 12|24
3031
// b ---- 4|16 + 4 = 8 |20
3132
var a = GetMatches(0f, 12f, 12d, 512f / 5512);
3233
var b = GetMatches(4f, 16f, 4d, 512f / 5512);
33-
34+
3435
Assert.IsTrue(a.TryCollapseWith(b, 1.48d, out var c));
3536
Assert.AreEqual(a.Count() + b.Count(), c.Count());
3637
Assert.AreEqual(13.5d, c.TotalLength, 0.1);
@@ -45,7 +46,7 @@ public void ShouldIdentifyAsContainedWithinItself()
4546

4647
var a = GetMatches(0, 0, 120, 512f / 5512);
4748
var b = GetMatches(10, 20, 10, 512f / 5512);
48-
49+
4950
Assert.IsFalse(a.TryCollapseWith(b, 1.48d, out _));
5051
Assert.IsTrue(a.Contains(b));
5152
Assert.IsFalse(b.Contains(a));
@@ -58,10 +59,10 @@ public void ShouldNotCollapseByQueryAtAsGapIsTooBig()
5859
// b -------- 12|22 + 10 = 22|32
5960
var a = GetMatches(0f, 10f, 10d, 512f / 5512);
6061
var b = GetMatches(12f, 22f, 10d, 512f / 5512);
61-
62+
6263
Assert.IsFalse(a.TryCollapseWith(b, 1.48d, out _));
6364
}
64-
65+
6566
[Test]
6667
public void ShouldNotCollapseAsQueryMatchCorrespondsTo2DifferentTracksLocation2()
6768
{
@@ -94,7 +95,7 @@ public void ShouldNotCollapseAsQueryMatchesSameTrackMultipleTimes()
9495
// query ----------------------
9596
// track --------
9697
// track --------
97-
98+
9899
var a = GetMatches(0f, 0f, 15d, 512f / 5512);
99100
var b = GetMatches(60f, 0f, 15d, 512f / 5512);
100101

@@ -112,20 +113,39 @@ public void ShouldNotMergeWithTrackAtReversed()
112113
Assert.IsFalse(b.TryCollapseWith(a, 1.48f, out _));
113114
}
114115

115-
private static Matches GetMatches(float startQueryAt, float startTrackAt, double length, float stride)
116+
[Test]
117+
public void ShouldSplitMatchedRegionsToGapRegions()
118+
{
119+
// a ------ ------- -------
120+
// t 0 10 20 30 32 40
121+
float stride = 1;
122+
var matches = GetMatches(0f, 0f, 10d, stride)
123+
.Concat(GetMatches(20f, 20f, 10, stride, 20))
124+
.Concat(GetMatches(32f, 32f, 8, stride, 32));
125+
126+
float fingerprintLengthInSeconds = 1f;
127+
double permittedGap = 2.5;
128+
var coverages = matches.SplitTrackMatchedRegions(permittedGap, fingerprintLengthInSeconds).ToList();
129+
130+
Assert.AreEqual(2, coverages.Count);
131+
Assert.AreEqual(10 + fingerprintLengthInSeconds, coverages.First().CoverageWithPermittedGapsLength);
132+
Assert.AreEqual(20 + fingerprintLengthInSeconds, coverages.Last().CoverageWithPermittedGapsLength);
133+
}
134+
135+
private static Matches GetMatches(float startQueryAt, float startTrackAt, double length, float stride, int startIndex = 0)
116136
{
117137
const int hammingSimilarity = 100;
118138
float startAt = 0f;
119139
var matches = new List<MatchedWith>();
120-
uint index = 0;
140+
uint index = (uint)startIndex;
121141
while (startAt <= length)
122142
{
123143
var match = new MatchedWith(index, startQueryAt + startAt, index, startTrackAt + startAt, hammingSimilarity);
124144
matches.Add(match);
125145
startAt += stride;
126146
index++;
127147
}
128-
148+
129149
return new Matches(matches);
130150
}
131151
}

src/SoundFingerprinting/LCS/Coverage.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ private Coverage()
3434
public double TrackMatchStartsAt => BestPath.First().TrackMatchAt;
3535

3636
/// <summary>
37-
/// Gets exact query coverage sum in seconds. Exact length of matched fingerprints, not necessary consecutive, just how much length has been covered by the query
37+
/// Gets the total track length that was covered by the query. Exact length of matched fingerprints, not necessary consecutive.
3838
/// </summary>
3939
public double CoverageLength => DiscreteCoverageLength - BestPath.FindTrackGaps(TrackLength, 0, FingerprintLength).Where(d => !d.IsOnEdge).Sum(d => d.LengthInSeconds);
4040

src/SoundFingerprinting/Properties/AssemblyInfo.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,5 +19,5 @@
1919
[assembly: InternalsVisibleTo("SoundFingerprinting.FFT.FFTW")]
2020
[assembly: InternalsVisibleTo("SoundFingerprinting.FFT.FFTW.Tests")]
2121

22-
[assembly: AssemblyVersion("7.4.6.100")]
23-
[assembly: AssemblyInformationalVersion("7.4.6.100")]
22+
[assembly: AssemblyVersion("7.4.7.100")]
23+
[assembly: AssemblyInformationalVersion("7.4.7.100")]

src/SoundFingerprinting/Query/Extensions.cs

Lines changed: 37 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,8 @@
33
using System;
44
using System.Linq;
55
using System.Collections.Generic;
6-
6+
using SoundFingerprinting.LCS;
7+
78
public static class Extensions
89
{
910
private const double PermittedGapZero = 1e-5;
@@ -23,6 +24,41 @@ public static double StdDev(this IEnumerable<double> values)
2324
return ret;
2425
}
2526

27+
public static IEnumerable<Coverage> SplitTrackMatchedRegions(this IEnumerable<MatchedWith> entries, double permittedGap, double fingerprintLength)
28+
{
29+
var list = new List<Coverage>();
30+
var ordered = entries.OrderBy(_ => _.TrackMatchAt).ToList();
31+
if (!ordered.Any())
32+
{
33+
return list;
34+
}
35+
36+
var stack = new Stack<MatchedWith>();
37+
stack.Push(ordered.First());
38+
foreach (var matchedWith in ordered.Skip(1))
39+
{
40+
var prev = stack.Peek();
41+
if (SubFingerprintsToSeconds.GapLengthToSeconds(matchedWith.TrackMatchAt, prev.TrackMatchAt, fingerprintLength) > permittedGap)
42+
{
43+
list.Add(GetMatchedWithsFromStack(stack, permittedGap, fingerprintLength));
44+
stack = new Stack<MatchedWith>();
45+
}
46+
47+
stack.Push(matchedWith);
48+
}
49+
50+
list.Add(GetMatchedWithsFromStack(stack, permittedGap, fingerprintLength));
51+
return list;
52+
}
53+
54+
private static Coverage GetMatchedWithsFromStack(Stack<MatchedWith> stack, double permittedGap, double fingerprintLengthInSeconds)
55+
{
56+
var matchedWiths = ((IEnumerable<MatchedWith>) stack.ToList()).Reverse().ToList();
57+
var queryLength = SubFingerprintsToSeconds.MatchLengthToSeconds(matchedWiths.Last().QueryMatchAt, matchedWiths.First().QueryMatchAt, fingerprintLengthInSeconds);
58+
var trackLength = SubFingerprintsToSeconds.MatchLengthToSeconds(matchedWiths.Last().TrackMatchAt, matchedWiths.First().TrackMatchAt, fingerprintLengthInSeconds);
59+
return matchedWiths.EstimateCoverage(queryLength, trackLength, fingerprintLengthInSeconds, permittedGap);
60+
}
61+
2662
public static IEnumerable<Gap> FindQueryGaps(this IEnumerable<MatchedWith> entries, double permittedGap, double fingerprintLength)
2763
{
2864
double sanitizedPermittedGap = permittedGap > 0 ? permittedGap : PermittedGapZero;

src/SoundFingerprinting/Query/ResultEntry.cs

Lines changed: 12 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -69,12 +69,23 @@ public ResultEntry(TrackData track,
6969
[ProtoMember(2)]
7070
public Coverage Coverage { get; }
7171

72+
/// <summary>
73+
/// Gets the total track length that was covered by the query. Exact length of matched fingerprints, not necessary consecutive.
74+
/// </summary>
75+
public double CoverageLength => Coverage.CoverageLength;
76+
7277
/// <summary>
7378
/// Gets query coverage length with permitted gaps
7479
/// </summary>
7580
[ProtoMember(3)]
7681
public double CoverageWithPermittedGapsLength { get; }
7782

83+
/// <summary>
84+
/// Gets estimated track coverage inferred from matching start and end of the resulting track in the query
85+
/// </summary>
86+
[ProtoMember(11)]
87+
public double DiscreteCoverageLength { get; }
88+
7889
/// <summary>
7990
/// Gets the exact position in seconds where resulting track started to match in the query
8091
/// </summary>
@@ -136,11 +147,7 @@ public ResultEntry(TrackData track,
136147
[ProtoMember(10)]
137148
public double Score { get; }
138149

139-
/// <summary>
140-
/// Gets estimated track coverage inferred from matching start and end of the resulting track in the query
141-
/// </summary>
142-
[ProtoMember(11)]
143-
public double DiscreteCoverageLength { get; }
150+
144151

145152
/// <summary>
146153
/// Gets information about gaps in the result entry coverage

src/SoundFingerprinting/SoundFingerprinting.csproj

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
<GenerateAssemblyInfo>false</GenerateAssemblyInfo>
66
<Nullable>enable</Nullable>
77
<PackageLicenseUrl>https://opensource.org/licenses/MIT</PackageLicenseUrl>
8-
<PackageVersion>7.4.6</PackageVersion>
8+
<PackageVersion>7.4.7</PackageVersion>
99
<Authors>Sergiu Ciumac</Authors>
1010
<PackageDescription>SoundFingerprinting is a C# framework that implements an efficient algorithm of audio fingerprinting and identification. Designed for developers, enthusiasts, researchers in the fields of audio processing, data mining, digital signal processing.</PackageDescription>
1111
<PackageProjectUrl>https://github.com/addictedcs/soundfingerprinting</PackageProjectUrl>

0 commit comments

Comments
 (0)