Files
meeting-assistant/MeetingAssistant/Recording/NaudioCaptureSource.cs
T
2026-05-20 02:06:16 +02:00

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);
}
}