using MeetingAssistant.Transcription; using NAudio.Wave; namespace MeetingAssistant.Speakers; internal static class SpeakerCompositeWav { public static WaveFileReader? OpenFirstReadableWave(IEnumerable 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 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);