Public Access
Implement meeting assistant v1
This commit is contained in:
@@ -0,0 +1,86 @@
|
||||
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);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user