Files
meeting-assistant/MeetingAssistant/Speakers/SpeakerCompositeWav.cs
codex a72cda0c03
PR and Push Build/Test / build-and-test (push) Successful in 7m0s
Harden speaker identity samples
2026-05-28 12:02:44 +02:00

108 lines
2.7 KiB
C#

using MeetingAssistant.Transcription;
using NAudio.Wave;
namespace MeetingAssistant.Speakers;
internal static class SpeakerCompositeWav
{
public static WaveFileReader? OpenFirstReadableWave(IEnumerable<byte[]> snippets)
{
foreach (var bytes in snippets)
{
if (bytes.Length == 0)
{
continue;
}
try
{
return new WaveFileReader(new MemoryStream(bytes));
}
catch (FormatException)
{
}
}
return null;
}
public static TimeSegment? AppendSnippet(
WaveFileWriter writer,
WaveFormat targetFormat,
byte[] snippet,
TimeSpan start)
{
if (snippet.Length == 0)
{
return null;
}
using var reader = new WaveFileReader(new MemoryStream(snippet));
if (!WaveFormatsMatch(reader.WaveFormat, targetFormat))
{
throw new InvalidDataException("Speaker identity snippets must use the same WAV format.");
}
reader.CopyTo(writer);
return new TimeSegment(start, start + reader.TotalTime);
}
public static void WriteSilence(
WaveFileWriter writer,
WaveFormat format,
TimeSpan duration)
{
if (duration <= TimeSpan.Zero)
{
return;
}
var bytes = new byte[(int)(format.AverageBytesPerSecond * duration.TotalSeconds)];
writer.Write(bytes, 0, bytes.Length);
}
public static string? FindBestSpeaker(
TimeSegment segment,
IReadOnlyList<TranscriptionSegment> segments)
{
var bestOverlap = 0d;
string? bestSpeaker = null;
foreach (var transcriptSegment in segments)
{
var overlap = Math.Min(segment.End.TotalSeconds, transcriptSegment.End.TotalSeconds)
- Math.Max(segment.Start.TotalSeconds, transcriptSegment.Start.TotalSeconds);
if (overlap > bestOverlap)
{
bestOverlap = overlap;
bestSpeaker = transcriptSegment.Speaker;
}
}
return bestSpeaker;
}
public static void TryDelete(string path)
{
try
{
if (File.Exists(path))
{
File.Delete(path);
}
}
catch (IOException)
{
}
}
private static bool WaveFormatsMatch(WaveFormat left, WaveFormat right)
{
return left.Encoding == right.Encoding
&& left.SampleRate == right.SampleRate
&& left.Channels == right.Channels
&& left.BitsPerSample == right.BitsPerSample;
}
}
internal readonly record struct TimeSegment(TimeSpan Start, TimeSpan End);