NAudio Loopback Record (record what you hear through the speaker)



Update: I've cleaned this code up and created a Nuget package that supports both .NET Framework and .NET (Core). The source code for that library and the link to the Nuget are:

There have been times when I have needed to record what is coming through the speaker. In the past I had used a project I created called basic audio that is hosted over at codeplex. basic audio uses the windows api to record from a given device. the main requirement though is that the audio drivers are capable of recording what you hear. as windows versions increment this functionality seems to be disappearing from a lot of drivers. as i moved to windows 8 basic audio was no longer able to record for me because my audio drivers don't support what you hear on windows 8. enter NAaudio.

NAudio is a project that is hosted at CodePlex and has been actively developed for over a decade now (at least it appears that way from some of the comment files included). In one of the recent versions the added the ability to record from the loopback and the cool thing is that it appears to work whether the sound card supports recording what you hear or not. The code was fairly straightforward but I decided to create a wrapper that would make my re-use of it only a few lines of code when I needed it in my hobby projects. I typically code in Visual Basic but I went ahead and wrote this as a C# class and compiled it into the NAudio assembly. Hope this helps or saves someone time. I should note also, portions of this are based off of one of the samples that was included with NAudio. I basically turned it into a reusable class (for how I use it).

C#

/// <summary>
/// A wrapper for the WasapiLoopbackCapture that will implement basic recording to a file that is overwrite only.
/// </summary>
public class LoopbackRecorder
{
    private IWaveIn _waveIn;
    private WaveFileWriter _writer;
    private bool _isRecording = false;

    /// <summary>
    /// Constructor
    /// </summary>
    public LoopbackRecorder()
    {
    } 

    /// <summary>
    /// Starts the recording.
    /// </summary>
    /// <param name="fileName"></param>
    public void StartRecording(string fileName)
    {   
        // If we are currently record then go ahead and exit out.
        if (_isRecording == true)
        {
            return;
        }
        _fileName = fileName;
        _waveIn = new WasapiLoopbackCapture();
        _writer = new WaveFileWriter(fileName, _waveIn.WaveFormat);
        _waveIn.DataAvailable += OnDataAvailable;
        _waveIn.RecordingStopped += OnRecordingStopped;
        _waveIn.StartRecording();
        _isRecording = true;
    }

    /// <summary>
    /// Stops the recording
    /// </summary>
    public void StopRecording()
    {
        if (_waveIn == null)
        {
            return;
        }
        _waveIn.StopRecording();
    }

    /// <summary>
    /// Event handled when recording is stopped.  We will clean up open objects here that are required to be 
    /// closed and/or disposed of.
    /// </summary>
    /// <param name="sender"></param>
    /// <param name="e"></param>
    void OnRecordingStopped(object sender, StoppedEventArgs e)
    {
        // Writer Close() needs to come first otherwise NAudio will lock up.
        if (_writer != null)
        {
            _writer.Close();
            _writer = null;
        }
        if (_waveIn != null) 
        {
            _waveIn.Dispose();
            _waveIn = null;
        }
        _isRecording = false;
        if (e.Exception != null)
        {
            throw e.Exception;
        }
    } 
    
    /// <summary>
    /// Event handled when data becomes available.  The data will be written out to disk at this point.
    /// </summary>
    /// <param name="sender"></param>
    /// <param name="e"></param>
    void OnDataAvailable(object sender, WaveInEventArgs e)
    {
        _writer.Write(e.Buffer, 0, e.BytesRecorded);
        //int secondsRecorded = (int)(_writer.Length / _writer.WaveFormat.AverageBytesPerSecond);
    } 

    private string _fileName = "";

    /// <summary>
    /// The name of the file that was set when StartRecording was called.  E.g. the current file being written to.
    /// </summary>
    public string FileName
    {
        get
        {
            return _fileName;
        }
    }
} 

To test this, I created a project (just happened to be in Visual Basic) where I added a WinForm with two buttons on it. 3 lines of code are required with the wrapper. A class wide variable declaring the recorder. A start recording line and a stop recording line. Basically it looked something like this (The C# obviously will be almost identical):

VB.Net

Private _recorder As New NAudio.LoopbackRecorder

Private Sub btnStartRecording_Click(sender As Object, e As EventArgs) Handles btnStartRecording.Click
    _recorder.StartRecording("c:tempnaudio.wav")
End Sub

Private Sub btnStopRecording_Click(sender As Object, e As EventArgs) Handles btnStopRecording.Click
    _recorder.StopRecording()
End Sub

Leave a comment

Please note that we won't show your email to others, or use it for sending unwanted emails. We will only use it to render your Gravatar image and to validate you as a real person.