forked from Manuel/meeting-assistant
feat: add local Resemblyzer speaker recognition
This commit is contained in:
@@ -4,56 +4,84 @@ namespace MeetingAssistant.Speakers;
|
||||
|
||||
internal static class SpeakerSampleSpanSelector
|
||||
{
|
||||
public static bool CanExtend(
|
||||
string currentSpeaker,
|
||||
TimeSpan currentEnd,
|
||||
TranscriptionSegment nextSegment,
|
||||
TimeSpan maximumSegmentGap)
|
||||
{
|
||||
return string.Equals(currentSpeaker, nextSegment.Speaker, StringComparison.OrdinalIgnoreCase) &&
|
||||
nextSegment.Start - currentEnd <= maximumSegmentGap;
|
||||
}
|
||||
|
||||
public static IReadOnlyList<TranscriptionSegment> SelectBestContinuousSpan(
|
||||
public static IReadOnlyList<TranscriptionSegment> SelectBestSameSpeakerSpan(
|
||||
IReadOnlyList<TranscriptionSegment> segments,
|
||||
string speaker,
|
||||
TimeSpan maximumSegmentGap,
|
||||
TimeSpan minimumDuration)
|
||||
TimeSpan minimumDuration,
|
||||
TimeSpan maximumSampleDuration)
|
||||
{
|
||||
var best = new List<TranscriptionSegment>();
|
||||
var current = new List<TranscriptionSegment>();
|
||||
|
||||
SpeakerSampleSpan? best = null;
|
||||
SpeakerSampleSpan? current = null;
|
||||
foreach (var segment in segments.OrderBy(segment => segment.Start))
|
||||
{
|
||||
if (current.Count == 0)
|
||||
if (!IsSpeaker(segment, speaker))
|
||||
{
|
||||
if (IsSpeaker(segment, speaker))
|
||||
{
|
||||
current.Add(segment);
|
||||
best = LongerSpan(current, best);
|
||||
}
|
||||
|
||||
current = null;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!CanExtend(speaker, current[^1].End, segment, maximumSegmentGap))
|
||||
current = ExtendOrStart(current, segment, maximumSampleDuration);
|
||||
if (current is not null &&
|
||||
(best is null || current.SpeechDuration > best.SpeechDuration))
|
||||
{
|
||||
current.Clear();
|
||||
if (!IsSpeaker(segment, speaker))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
best = current;
|
||||
}
|
||||
|
||||
current.Add(segment);
|
||||
best = LongerSpan(current, best);
|
||||
}
|
||||
|
||||
return SpanDuration(best) >= minimumDuration
|
||||
? best
|
||||
return best is not null && best.SpeechDuration >= minimumDuration
|
||||
? best.Segments
|
||||
: [];
|
||||
}
|
||||
|
||||
public static IReadOnlyList<IReadOnlyList<TranscriptionSegment>> SelectBestSameSpeakerSpans(
|
||||
IReadOnlyList<TranscriptionSegment> segments,
|
||||
string speaker,
|
||||
TimeSpan minimumDuration,
|
||||
TimeSpan maximumSampleDuration,
|
||||
int maxSpans)
|
||||
{
|
||||
if (maxSpans <= 0)
|
||||
{
|
||||
return [];
|
||||
}
|
||||
|
||||
var completed = new List<SpeakerSampleSpan>();
|
||||
SpeakerSampleSpan? current = null;
|
||||
TimeSpan? lastCompletedEnd = null;
|
||||
foreach (var segment in segments.OrderBy(segment => segment.Start))
|
||||
{
|
||||
if (!IsSpeaker(segment, speaker))
|
||||
{
|
||||
current = null;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (lastCompletedEnd is { } end && segment.Start < end)
|
||||
{
|
||||
current = null;
|
||||
continue;
|
||||
}
|
||||
|
||||
current = ExtendOrStart(current, segment, maximumSampleDuration);
|
||||
if (current is null || current.SpeechDuration < minimumDuration)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
completed.Add(current);
|
||||
lastCompletedEnd = current.End;
|
||||
current = null;
|
||||
}
|
||||
|
||||
return completed
|
||||
.OrderByDescending(span => span.SpeechDuration)
|
||||
.ThenBy(span => span.Start)
|
||||
.Take(maxSpans)
|
||||
.OrderBy(span => span.Start)
|
||||
.Select(span => span.Segments)
|
||||
.ToList();
|
||||
}
|
||||
|
||||
public static TimeSpan SpanDuration(IReadOnlyList<TranscriptionSegment> segments)
|
||||
{
|
||||
return segments.Count == 0
|
||||
@@ -61,19 +89,20 @@ internal static class SpeakerSampleSpanSelector
|
||||
: segments[^1].End - segments[0].Start;
|
||||
}
|
||||
|
||||
private static SpeakerSampleSpan? ExtendOrStart(
|
||||
SpeakerSampleSpan? current,
|
||||
TranscriptionSegment segment,
|
||||
TimeSpan maximumSampleDuration)
|
||||
{
|
||||
return current is not null && current.TryExtend(segment, out var extended)
|
||||
? extended
|
||||
: SpeakerSampleSpan.Create(segment, maximumSampleDuration);
|
||||
}
|
||||
|
||||
private static bool IsSpeaker(
|
||||
TranscriptionSegment segment,
|
||||
string speaker)
|
||||
{
|
||||
return string.Equals(segment.Speaker, speaker, StringComparison.OrdinalIgnoreCase);
|
||||
}
|
||||
|
||||
private static List<TranscriptionSegment> LongerSpan(
|
||||
List<TranscriptionSegment> current,
|
||||
List<TranscriptionSegment> best)
|
||||
{
|
||||
return SpanDuration(current) > SpanDuration(best)
|
||||
? current.ToList()
|
||||
: best;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user