Skip to content

Commit 97ab1c3

Browse files
committed
Merge branch 'develop'
* develop: Adding silence calculator utility. Asserting how silence maps in the hash domain.
2 parents a4922e5 + 40fa963 commit 97ab1c3

File tree

9 files changed

+111
-12
lines changed

9 files changed

+111
-12
lines changed

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("10.4.0.100")]
15-
[assembly: AssemblyInformationalVersion("10.4.0.100")]
14+
[assembly: AssemblyVersion("10.6.0.100")]
15+
[assembly: AssemblyInformationalVersion("10.6.0.100")]

src/SoundFingerprinting.Tests/Unit/LSH/LocalitySensitiveHashingAlgorithmTest.cs

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -123,15 +123,15 @@ public void FingerprintsCantMatchUniformlyAtRandom()
123123
for (int i = 0; i < 100; ++i)
124124
{
125125
var schema = TestUtilities.GenerateRandomFingerprint(random, 200, 128, 32);
126-
var hash = lshAlgorithm.Hash(new Fingerprint(schema, i * one, (uint)i, Array.Empty<byte>()), config);
126+
var hash = lshAlgorithm.Hash(new Fingerprint(schema, i * one, (uint)i, []), config);
127127
var subFingerprint = new SubFingerprintData(hash.HashBins, hash.SequenceNumber, hash.StartsAt, new ModelReference<uint>((uint)i), track);
128128
storage.AddSubFingerprint(subFingerprint);
129129
}
130130

131131
for (int i = 0; i < 10; ++i)
132132
{
133133
var schema = TestUtilities.GenerateRandomFingerprint(random, 200, 128, 32);
134-
var hash = lshAlgorithm.Hash(new Fingerprint(schema, i * one, (uint)i, Array.Empty<byte>()), config);
134+
var hash = lshAlgorithm.Hash(new Fingerprint(schema, i * one, (uint)i, []), config);
135135
for (int j = 0; j < 25; ++j)
136136
{
137137
var ids = storage.GetSubFingerprintsByHashTableAndHash(j, hash.HashBins[j], MediaType.Audio);
@@ -199,16 +199,16 @@ public void ShouldBeAbleToControlReturnedCandidatesWithThresholdParameter()
199199
var fingerprints = TestUtilities.GenerateSimilarFingerprints(random, howSimilar, topWavelets, width * height * 2);
200200
int hammingDistance = similarity.CalculateHammingDistance(fingerprints.Item1.ConvertToBooleans(), fingerprints.Item2.ConvertToBooleans());
201201
hammingDistances.Add(hammingDistance);
202-
var hashed1 = lsh.HashImage(new Fingerprint(fingerprints.Item1, 0, 0, Array.Empty<byte>()), hashingConfig);
203-
var hashed2 = lsh.HashImage(new Fingerprint(fingerprints.Item2, 0, 0, Array.Empty<byte>()), hashingConfig);
202+
var hashed1 = lsh.HashImage(new Fingerprint(fingerprints.Item1, 0, 0, []), hashingConfig);
203+
var hashed2 = lsh.HashImage(new Fingerprint(fingerprints.Item2, 0, 0, []), hashingConfig);
204204
int agreeCount = AgreeOn(hashed1.HashBins, hashed2.HashBins);
205205
agreeOn.Add(agreeCount);
206206
}
207207

208208
int requested = (int)((1 - howSimilar) * topWavelets * 2);
209209
Assert.AreEqual(requested, hammingDistances.Average(), 1);
210210
Assert.AreEqual(expectedThresholds[r], Math.Floor(agreeOn.Average()));
211-
logger.LogInformation($"Similarity: {howSimilar: 0.00}, Avg. Table Matches {agreeOn.Average(): 0.000}");
211+
logger.LogInformation("Similarity: {HowSimilar}, Avg. Table Matches {Average}", howSimilar, agreeOn.Average());
212212
});
213213
}
214214

src/SoundFingerprinting.Tests/Unit/Math/SimilarityUtilityTest.cs

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,16 @@ public void CalculateJaccardSimilarityCorrect()
6565
Assert.AreEqual(5f / 6, result, 0.0001);
6666
}
6767

68+
[Test]
69+
public void SilenceShouldMapToNegativeOne()
70+
{
71+
byte[] minHashes = [255, 255, 255, 255];
72+
int[] hashes = hashConverter.ToInts(minHashes, 1);
73+
74+
Assert.AreEqual(1, hashes.Length);
75+
Assert.AreEqual(-1, hashes[0]);
76+
}
77+
6878
private byte[] GenerateByteArray(int length)
6979
{
7080
var ran = new Random();
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
namespace SoundFingerprinting.Tests.Unit.Utils;
2+
3+
using System.Linq;
4+
using NUnit.Framework;
5+
using SoundFingerprinting.Data;
6+
using SoundFingerprinting.Utils;
7+
8+
[TestFixture]
9+
public class SilenceCalculatorTest
10+
{
11+
[Test]
12+
public void ShouldCalculateCorrectly()
13+
{
14+
var hashedFingerprints = Enumerable.Range(0, 10).Select(index =>
15+
{
16+
int[] hashBins = index % 2 == 0 ? Enumerable.Repeat(-1, 25).ToArray() : Enumerable.Repeat(1, 25).ToArray();
17+
return new HashedFingerprint(hashBins, (uint)index, (float)index / 2, []);
18+
}).ToArray();
19+
20+
var hashes = new Hashes(hashedFingerprints, 10, MediaType.Audio);
21+
22+
var timespan = SilenceCalculator.Calculate(hashes);
23+
24+
Assert.That(timespan.TotalSeconds, Is.EqualTo(2.5).Within(0.1));
25+
}
26+
}

src/SoundFingerprinting/Data/AVHashes.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -135,7 +135,7 @@ public AVHashes WithStreamId(string streamId)
135135
}
136136

137137
/// <summary>
138-
/// Add an additional property to the Audio/Video hashes object.
138+
/// Add a property to the Audio/Video hashes object.
139139
/// </summary>
140140
/// <param name="key">Property key.</param>
141141
/// <param name="value">Property value.</param>

src/SoundFingerprinting/FingerprintService.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -86,7 +86,7 @@ internal IEnumerable<Fingerprint> CreateOriginalFingerprintsFromFrames(IEnumerab
8686
var images = normalized.ToList();
8787
if (!images.Any())
8888
{
89-
return Enumerable.Empty<Fingerprint>();
89+
return [];
9090
}
9191

9292
var fingerprints = new ConcurrentBag<Fingerprint>();

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("10.4.0.100")]
23-
[assembly: AssemblyInformationalVersion("10.4.0.100")]
22+
[assembly: AssemblyVersion("10.6.0.100")]
23+
[assembly: AssemblyInformationalVersion("10.6.0.100")]

src/SoundFingerprinting/SoundFingerprinting.csproj

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
55
<GenerateAssemblyInfo>false</GenerateAssemblyInfo>
66
<Nullable>enable</Nullable>
7-
<PackageVersion>10.4.0</PackageVersion>
7+
<PackageVersion>10.6.0</PackageVersion>
88
<Authors>Sergiu Ciumac</Authors>
99
<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>
1010
<PackageProjectUrl>https://github.com/addictedcs/soundfingerprinting</PackageProjectUrl>
Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
namespace SoundFingerprinting.Utils;
2+
3+
using System;
4+
using System.Linq;
5+
using SoundFingerprinting.Data;
6+
7+
/// <summary>
8+
/// Silence calculator.
9+
/// </summary>
10+
/// <remarks>
11+
/// A utility class allowing to calculate silence contained within audio and video hashes. <br />
12+
/// It only makes sense to calculate silence for hashes if TreatAsSilence flag is set to true during fingerprinting. <br />
13+
/// Do not set the flag to true unless you've explicitly arrived to the conclusion you need it.
14+
/// </remarks>
15+
public static class SilenceCalculator
16+
{
17+
/// <summary>
18+
/// Silence value, derived from the fact that an array of [255, 255, 255, 255] min hashes will map to -1 during hashes conversion.
19+
/// </summary>
20+
private const int Silence = -1;
21+
22+
/// <summary>
23+
/// Calculate silence contained within audio and video hashes.
24+
/// </summary>
25+
/// <param name="avHashes">AVHashes to analyze.</param>
26+
/// <returns>
27+
/// Tuple containing audio and video silence. For video hashes silence means black frames.
28+
/// </returns>
29+
public static (TimeSpan, TimeSpan) Calculate(AVHashes avHashes)
30+
{
31+
var audioSilence = Calculate(avHashes.Audio);
32+
var videoSilence = Calculate(avHashes.Video);
33+
return (audioSilence, videoSilence);
34+
}
35+
36+
/// <summary>
37+
/// Calculate contained silence within hashes.
38+
/// </summary>
39+
/// <param name="hashes">Hashes to analyze.</param>
40+
/// <returns>Timespan.</returns>
41+
/// <remarks>
42+
/// It only makes sense to calculate silence for hashes if TreatAsSilence flag is set to true during fingerprinting.
43+
/// </remarks>
44+
public static TimeSpan Calculate(Hashes? hashes)
45+
{
46+
if (hashes == null || hashes.IsEmpty)
47+
{
48+
return TimeSpan.Zero;
49+
}
50+
51+
var items = hashes.OrderBy(_ => _.SequenceNumber).ToArray();
52+
double silence = 0;
53+
for (int i = 1; i < items.Length; i++)
54+
{
55+
if (hashes[i - 1].HashBins.All(_ => _ == Silence))
56+
{
57+
silence += items[i].StartsAt - items[i - 1].StartsAt;
58+
}
59+
}
60+
61+
return TimeSpan.FromSeconds(silence);
62+
}
63+
}

0 commit comments

Comments
 (0)