Public Access
feat: add local Resemblyzer speaker recognition
PR and Push Build/Test / build-and-test (push) Successful in 12m37s
PR and Push Build/Test / build-and-test (push) Successful in 12m37s
This commit is contained in:
@@ -8,13 +8,22 @@ namespace MeetingAssistant.Tests;
|
||||
public sealed class SpeakerAudioSampleCollectorTests
|
||||
{
|
||||
[Fact]
|
||||
public void CollectorWaitsForConfiguredUninterruptedSpeechDuration()
|
||||
public void DefaultSampleDurationsAreTenAndSixtySeconds()
|
||||
{
|
||||
var options = new SpeakerIdentificationOptions();
|
||||
|
||||
Assert.Equal(TimeSpan.FromSeconds(10), options.MinimumSampleSpeechDuration);
|
||||
Assert.Equal(TimeSpan.FromSeconds(60), options.MaximumSampleDuration);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void CollectorWaitsForConfiguredSpeakerAudioDuration()
|
||||
{
|
||||
var collector = new SpeakerAudioSampleCollector(
|
||||
TimeSpan.FromMinutes(2),
|
||||
maxSamplesPerSpeaker: 3,
|
||||
minimumUninterruptedSpeechDuration: TimeSpan.FromSeconds(30),
|
||||
maximumSegmentGap: TimeSpan.FromSeconds(1));
|
||||
minimumSampleSpeechDuration: TimeSpan.FromSeconds(30),
|
||||
maximumSampleDuration: TimeSpan.FromSeconds(60));
|
||||
collector.AppendAudio(CreateAudio(TimeSpan.FromSeconds(35)));
|
||||
|
||||
collector.TryAdd(Segment(0, 12, "Guest01", "one two three four five."));
|
||||
@@ -37,8 +46,8 @@ public sealed class SpeakerAudioSampleCollectorTests
|
||||
var collector = new SpeakerAudioSampleCollector(
|
||||
TimeSpan.FromMinutes(2),
|
||||
maxSamplesPerSpeaker: 3,
|
||||
minimumUninterruptedSpeechDuration: TimeSpan.FromSeconds(30),
|
||||
maximumSegmentGap: TimeSpan.FromSeconds(1));
|
||||
minimumSampleSpeechDuration: TimeSpan.FromSeconds(30),
|
||||
maximumSampleDuration: TimeSpan.FromSeconds(60));
|
||||
collector.AppendAudio(CreateAudio(TimeSpan.FromSeconds(50)));
|
||||
|
||||
collector.TryAdd(Segment(0, 20, "Guest01", "one two three four five."));
|
||||
@@ -48,6 +57,61 @@ public sealed class SpeakerAudioSampleCollectorTests
|
||||
Assert.DoesNotContain(collector.Snapshot(), sample => sample.Speaker == "Guest01");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void CollectorCombinesConsecutiveSameSpeakerSegmentsAcrossProviderPauses()
|
||||
{
|
||||
var collector = new SpeakerAudioSampleCollector(
|
||||
TimeSpan.FromMinutes(2),
|
||||
maxSamplesPerSpeaker: 3,
|
||||
minimumSampleSpeechDuration: TimeSpan.FromSeconds(10),
|
||||
maximumSampleDuration: TimeSpan.FromSeconds(60));
|
||||
collector.AppendAudio(CreateAudio(TimeSpan.FromSeconds(20)));
|
||||
|
||||
collector.TryAdd(Segment(0, 4, "Guest01", "one two three four."));
|
||||
collector.TryAdd(Segment(7, 13, "Guest01", "five six seven eight."));
|
||||
|
||||
var sample = Assert.Single(collector.Snapshot());
|
||||
Assert.Equal(TimeSpan.Zero, sample.Segment.Start);
|
||||
Assert.Equal(TimeSpan.FromSeconds(13), sample.Segment.End);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void CollectorDoesNotCountProviderPausesAsSpeakerAudio()
|
||||
{
|
||||
var collector = new SpeakerAudioSampleCollector(
|
||||
TimeSpan.FromMinutes(2),
|
||||
maxSamplesPerSpeaker: 3,
|
||||
minimumSampleSpeechDuration: TimeSpan.FromSeconds(10),
|
||||
maximumSampleDuration: TimeSpan.FromSeconds(60));
|
||||
collector.AppendAudio(CreateAudio(TimeSpan.FromSeconds(30)));
|
||||
|
||||
collector.TryAdd(Segment(0, 4, "Guest01", "one two three four."));
|
||||
collector.TryAdd(Segment(20, 25, "Guest01", "five six seven eight."));
|
||||
|
||||
Assert.Empty(collector.Snapshot());
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void CollectorCapsRecognitionWavAtConfiguredMaximumDuration()
|
||||
{
|
||||
var collector = new SpeakerAudioSampleCollector(
|
||||
TimeSpan.FromMinutes(2),
|
||||
maxSamplesPerSpeaker: 3,
|
||||
minimumSampleSpeechDuration: TimeSpan.FromSeconds(10),
|
||||
maximumSampleDuration: TimeSpan.FromSeconds(60));
|
||||
collector.AppendAudio(CreateAudio(TimeSpan.FromSeconds(75)));
|
||||
|
||||
var sample = collector.TryAdd(Segment(
|
||||
0,
|
||||
70,
|
||||
"Guest01",
|
||||
"one two three four five six seven eight nine ten."));
|
||||
|
||||
Assert.NotNull(sample);
|
||||
Assert.Equal(TimeSpan.FromSeconds(60), sample.Segment.End);
|
||||
Assert.True(ReadDuration(sample.WavBytes) <= TimeSpan.FromSeconds(60));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void CollectorLogsWhenSampleIsDiscardedBecauseSpeechIsTooShort()
|
||||
{
|
||||
@@ -55,8 +119,8 @@ public sealed class SpeakerAudioSampleCollectorTests
|
||||
var collector = new SpeakerAudioSampleCollector(
|
||||
TimeSpan.FromMinutes(2),
|
||||
maxSamplesPerSpeaker: 3,
|
||||
minimumUninterruptedSpeechDuration: TimeSpan.FromSeconds(30),
|
||||
maximumSegmentGap: TimeSpan.FromSeconds(1),
|
||||
minimumSampleSpeechDuration: TimeSpan.FromSeconds(30),
|
||||
maximumSampleDuration: TimeSpan.FromSeconds(60),
|
||||
logger: logger);
|
||||
collector.AppendAudio(CreateAudio(TimeSpan.FromSeconds(35)));
|
||||
|
||||
@@ -69,6 +133,47 @@ public sealed class SpeakerAudioSampleCollectorTests
|
||||
Assert.Contains("minimum duration", message);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void CollectorCanResetThePendingSpanAfterEachAcceptedSample()
|
||||
{
|
||||
var collector = new SpeakerAudioSampleCollector(
|
||||
TimeSpan.FromMinutes(2),
|
||||
maxSamplesPerSpeaker: 5,
|
||||
minimumSampleSpeechDuration: TimeSpan.FromSeconds(2),
|
||||
maximumSampleDuration: TimeSpan.FromSeconds(60),
|
||||
requireNonOverlappingSamples: true);
|
||||
collector.AppendAudio(CreateAudio(TimeSpan.FromSeconds(6)));
|
||||
|
||||
collector.TryAdd(Segment(0, 2, "Guest01", "one two three."));
|
||||
collector.TryAdd(Segment(2, 4, "Guest01", "four five six."));
|
||||
collector.TryAdd(Segment(4, 6, "Guest01", "seven eight nine."));
|
||||
|
||||
var samples = collector.Snapshot().OrderBy(sample => sample.Segment.Start).ToList();
|
||||
|
||||
Assert.Collection(
|
||||
samples,
|
||||
sample => Assert.Equal((TimeSpan.Zero, TimeSpan.FromSeconds(2)), (sample.Segment.Start, sample.Segment.End)),
|
||||
sample => Assert.Equal((TimeSpan.FromSeconds(2), TimeSpan.FromSeconds(4)), (sample.Segment.Start, sample.Segment.End)),
|
||||
sample => Assert.Equal((TimeSpan.FromSeconds(4), TimeSpan.FromSeconds(6)), (sample.Segment.Start, sample.Segment.End)));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ResettingCollectorRejectsASegmentThatOverlapsAnAcceptedSample()
|
||||
{
|
||||
var collector = new SpeakerAudioSampleCollector(
|
||||
TimeSpan.FromMinutes(2),
|
||||
maxSamplesPerSpeaker: 5,
|
||||
minimumSampleSpeechDuration: TimeSpan.FromSeconds(2),
|
||||
maximumSampleDuration: TimeSpan.FromSeconds(60),
|
||||
requireNonOverlappingSamples: true);
|
||||
collector.AppendAudio(CreateAudio(TimeSpan.FromSeconds(4)));
|
||||
|
||||
collector.TryAdd(Segment(0, 2, "Guest01", "one two three."));
|
||||
collector.TryAdd(Segment(1, 3, "Guest01", "four five six."));
|
||||
|
||||
Assert.Single(collector.Snapshot());
|
||||
}
|
||||
|
||||
private static TranscriptionSegment Segment(
|
||||
double start,
|
||||
double end,
|
||||
|
||||
Reference in New Issue
Block a user