Public Access
101 lines
3.2 KiB
C#
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);
|
|
}
|