Public Access
87 lines
2.6 KiB
C#
87 lines
2.6 KiB
C#
using System.Threading.Channels;
|
|
using Microsoft.Extensions.Options;
|
|
using NAudio.CoreAudioApi;
|
|
using NAudio.Wave;
|
|
|
|
namespace MeetingAssistant.Recording;
|
|
|
|
public sealed class MicrophoneAudioSource : IMeetingAudioSource
|
|
{
|
|
private readonly MeetingAssistantOptions options;
|
|
|
|
public MicrophoneAudioSource(IOptions<MeetingAssistantOptions> options)
|
|
{
|
|
this.options = options.Value;
|
|
}
|
|
|
|
public IAsyncEnumerable<AudioChunk> CaptureAsync(CancellationToken cancellationToken)
|
|
{
|
|
return CaptureAsync(new WasapiCapture(), cancellationToken);
|
|
}
|
|
|
|
private IAsyncEnumerable<AudioChunk> CaptureAsync(IWaveIn capture, CancellationToken cancellationToken)
|
|
{
|
|
capture.WaveFormat = new WaveFormat(options.Recording.SampleRate, 16, options.Recording.Channels);
|
|
return CaptureWith(capture, cancellationToken);
|
|
}
|
|
|
|
internal static async IAsyncEnumerable<AudioChunk> CaptureWith(
|
|
IWaveIn capture,
|
|
[System.Runtime.CompilerServices.EnumeratorCancellation] CancellationToken cancellationToken)
|
|
{
|
|
var channel = Channel.CreateUnbounded<AudioChunk>();
|
|
|
|
capture.DataAvailable += (_, args) =>
|
|
{
|
|
var buffer = new byte[args.BytesRecorded];
|
|
Buffer.BlockCopy(args.Buffer, 0, buffer, 0, args.BytesRecorded);
|
|
channel.Writer.TryWrite(new AudioChunk(buffer, capture.WaveFormat.SampleRate, capture.WaveFormat.Channels));
|
|
};
|
|
capture.RecordingStopped += (_, args) =>
|
|
{
|
|
if (args.Exception is not null)
|
|
{
|
|
channel.Writer.TryComplete(args.Exception);
|
|
return;
|
|
}
|
|
|
|
channel.Writer.TryComplete();
|
|
};
|
|
|
|
try
|
|
{
|
|
using var registration = cancellationToken.Register(capture.StopRecording);
|
|
capture.StartRecording();
|
|
|
|
await foreach (var chunk in channel.Reader.ReadAllAsync(cancellationToken))
|
|
{
|
|
yield return chunk;
|
|
}
|
|
}
|
|
finally
|
|
{
|
|
capture.Dispose();
|
|
}
|
|
}
|
|
}
|
|
|
|
public sealed class SystemAudioSource : IMeetingAudioSource
|
|
{
|
|
private readonly MeetingAssistantOptions options;
|
|
|
|
public SystemAudioSource(IOptions<MeetingAssistantOptions> options)
|
|
{
|
|
this.options = options.Value;
|
|
}
|
|
|
|
public IAsyncEnumerable<AudioChunk> CaptureAsync(CancellationToken cancellationToken)
|
|
{
|
|
var capture = new WasapiLoopbackCapture
|
|
{
|
|
WaveFormat = new WaveFormat(options.Recording.SampleRate, 16, options.Recording.Channels)
|
|
};
|
|
|
|
return MicrophoneAudioSource.CaptureWith(capture, cancellationToken);
|
|
}
|
|
}
|