using NAudio.Wave;
namespace ytLive.Services.Audio;
///
/// Captures the user's desktop/game audio (TASK 4 ship step 4) via WASAPI
/// loopback on the default render device. Runs for the app's lifetime so the
/// game audio bar stays live in preview.
///
public sealed class WasapiLoopbackAudioSource : IAudioSource
{
private WasapiLoopbackCapture? _capture;
public event Action? Started;
public event Action? SampleReady;
public event Action? Failed;
public void Start()
{
if (_capture != null)
throw new InvalidOperationException("Already started.");
try
{
_capture = new WasapiLoopbackCapture();
_capture.DataAvailable += OnDataAvailable;
_capture.RecordingStopped += OnRecordingStopped;
_capture.StartRecording();
Started?.Invoke();
}
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 void OnDataAvailable(object? sender, WaveInEventArgs e)
{
if (e.BytesRecorded <= 0)
return;
var format = _capture?.WaveFormat ?? WaveFormat.CreateIeeeFloatWaveFormat(48000, 2);
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);
}
}