Files
meeting-assistant/MeetingAssistant/Workflow/WorkflowRulesEditorSamplePlaybackQueue.cs
codex 693f52afee
PR and Push Build/Test / build-and-test (push) Successful in 7m0s
Add tray rules and identities editor
2026-05-28 01:28:16 +02:00

101 lines
3.2 KiB
C#

using System.Threading.Channels;
using NAudio.Wave;
namespace MeetingAssistant.Workflow;
public interface IWorkflowRulesEditorSamplePlaybackQueue
{
Task<string> QueueAsync(int sampleId, byte[] wavBytes, CancellationToken cancellationToken = default);
}
public sealed class WorkflowRulesEditorSamplePlaybackQueue : IWorkflowRulesEditorSamplePlaybackQueue, IDisposable
{
private readonly Channel<QueuedSample> channel = Channel.CreateUnbounded<QueuedSample>();
private readonly ILogger<WorkflowRulesEditorSamplePlaybackQueue> logger;
private readonly CancellationTokenSource cancellation = new();
private readonly Task playbackTask;
public WorkflowRulesEditorSamplePlaybackQueue(ILogger<WorkflowRulesEditorSamplePlaybackQueue> logger)
{
this.logger = logger;
playbackTask = Task.Run(PlayQueuedSamplesAsync);
}
public async Task<string> QueueAsync(
int sampleId,
byte[] wavBytes,
CancellationToken cancellationToken = default)
{
if (wavBytes.Length == 0)
{
return $"Sample {sampleId} is empty and was not queued.";
}
await channel.Writer.WriteAsync(new QueuedSample(sampleId, wavBytes), cancellationToken);
return $"Queued sample {sampleId} for playback.";
}
public void Dispose()
{
cancellation.Cancel();
channel.Writer.TryComplete();
try
{
playbackTask.Wait(TimeSpan.FromSeconds(2));
}
catch
{
}
cancellation.Dispose();
}
private async Task PlayQueuedSamplesAsync()
{
try
{
await foreach (var sample in channel.Reader.ReadAllAsync(cancellation.Token))
{
try
{
await PlayAsync(sample, cancellation.Token);
}
catch (OperationCanceledException) when (cancellation.IsCancellationRequested)
{
break;
}
catch (Exception exception)
{
logger.LogWarning(
exception,
"Could not play queued speaker identity sample {SampleId}",
sample.SampleId);
}
}
}
catch (OperationCanceledException) when (cancellation.IsCancellationRequested)
{
}
}
private static async Task PlayAsync(QueuedSample sample, CancellationToken cancellationToken)
{
await using var stream = new MemoryStream(sample.WavBytes, writable: false);
using var reader = new WaveFileReader(stream);
using var output = new WaveOutEvent();
var completed = new TaskCompletionSource(TaskCreationOptions.RunContinuationsAsynchronously);
output.PlaybackStopped += (_, _) => completed.TrySetResult();
output.Init(reader);
output.Play();
await using var registration = cancellationToken.Register(() =>
{
output.Stop();
completed.TrySetCanceled(cancellationToken);
});
await completed.Task;
}
private sealed record QueuedSample(int SampleId, byte[] WavBytes);
}