Files
ytLlive/Services/Audio/WasapiMicAudioSource.cs
T

103 lines
2.8 KiB
C#

using NAudio.CoreAudioApi;
using NAudio.Wave;
namespace ytLive.Services.Audio;
/// <summary>
/// Captures the selected microphone (TASK 4 ship step 4) via WASAPI. Resolves
/// the NAudio device by FriendlyName matching <c>MicSourceName</c> (the app only
/// persists DisplayName), falling back to the default capture endpoint.
/// </summary>
public sealed class WasapiMicAudioSource : IAudioSource
{
private readonly Func<string?> _micNameProvider;
private WasapiCapture? _capture;
/// <param name="micNameProvider">Returns the current mic DisplayName; read
/// at each Start so a device picked mid-session takes effect next go-live.</param>
public WasapiMicAudioSource(Func<string?> micNameProvider)
{
_micNameProvider = micNameProvider;
}
public event Action<AudioSample>? SampleReady;
public event Action<Exception>? Failed;
public void Start()
{
if (_capture != null)
throw new InvalidOperationException("Already started.");
try
{
var device = ResolveDevice();
_capture = device != null ? new WasapiCapture(device) : new WasapiCapture();
_capture.DataAvailable += OnDataAvailable;
_capture.RecordingStopped += OnRecordingStopped;
_capture.StartRecording();
}
catch (Exception ex)
{
Stop();
Failed?.Invoke(ex);
}
}
public void Stop()
{
if (_capture == null)
return;
try
{
_capture.StopRecording();
}
catch
{
}
_capture.Dispose();
_capture = null;
}
public void Dispose() => Stop();
private MMDevice? ResolveDevice()
{
var micName = _micNameProvider();
if (string.IsNullOrWhiteSpace(micName))
return null;
try
{
using var enumerator = new MMDeviceEnumerator();
foreach (var endpoint in enumerator.EnumerateAudioEndPoints(DataFlow.Capture, DeviceState.Active))
{
if (string.Equals(endpoint.FriendlyName, micName, StringComparison.OrdinalIgnoreCase))
return endpoint;
}
}
catch
{
}
return null;
}
private void OnDataAvailable(object? sender, WaveInEventArgs e)
{
if (e.BytesRecorded <= 0)
return;
var format = _capture?.WaveFormat ?? WaveFormat.CreateIeeeFloatWaveFormat(48000, 1);
var samples = WaveToFloat.Convert(e.Buffer, e.BytesRecorded, format);
if (samples.Length > 0)
SampleReady?.Invoke(new AudioSample(samples, format.SampleRate, format.Channels));
}
private void OnRecordingStopped(object? sender, StoppedEventArgs e)
{
if (e.Exception != null)
Failed?.Invoke(e.Exception);
}
}