70 lines
1.7 KiB
C#
70 lines
1.7 KiB
C#
using NAudio.Wave;
|
|
|
|
namespace ytLive.Services.Audio;
|
|
|
|
/// <summary>
|
|
/// Captures the user's desktop/game audio (TASK 4 ship step 4) via WASAPI
|
|
/// loopback on the default render device. Starts/stops with go-live only.
|
|
/// </summary>
|
|
public sealed class WasapiLoopbackAudioSource : IAudioSource
|
|
{
|
|
private WasapiLoopbackCapture? _capture;
|
|
|
|
public event Action<AudioSample>? SampleReady;
|
|
public event Action<Exception>? Failed;
|
|
|
|
public void Start()
|
|
{
|
|
if (_capture != null)
|
|
throw new InvalidOperationException("Already started.");
|
|
|
|
try
|
|
{
|
|
_capture = new WasapiLoopbackCapture();
|
|
_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 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);
|
|
}
|
|
}
|