This project has moved. For the latest updates, please go here.

How to record all sounds played on the PC

May 18, 2010 at 7:39 AM

Hi there,

Is there any way to change the source to monitor the main audio source (i.e. picking up windows sounds, music playing by other applications etc.)?

I've had a good look around the code, but even in the WPF application I can't see where the source is being set to "Microphone".

Thanks!
Josh 

May 19, 2010 at 7:09 PM

I've tried the tutorial below:

http://opensebj.blogspot.com/2009/04/naudio-tutorial-5-recording-audio.html

But when I try the "Record all sounds on my computer" button in the example program, I only get the microphone recorded.

Is it possible to record all sounds with NAudio? Are there any gotchas that might prevent me from recording everything?

Thanks for the project though, awesome effort! 

May 20, 2010 at 7:03 AM

By the way I'm using Windows 7, in case that makes a difference!

Coordinator
May 21, 2010 at 10:06 AM

I'm afraid that there is no easy way to do this with NAudio at the moment. Many soundcard drivers do allow you to configure their output as an input, so it is possible to record everything, but it can require the user to pull up the recording mixer (in XP). On Windows 7, things are a little different, so you might need to experiment a bit more. It is possible that the new CoreAudio can be used to do this, but I haven't tried it yet. Sorry I can't be more helpful, but this kind of thing tends to require a lot of trial and error in my experience.

Mark

May 21, 2010 at 10:53 AM

Hi Mark - really appreciate your reply!

I found this code in C++ from Matthew Van Eerde this morning:

http://blogs.msdn.com/matthew_van_eerde/archive/2008/12/16/sample-wasapi-loopback-capture-record-what-you-hear.aspx

The compiled executable he provides does exactly what I want using WASAPI - so by that margin it must somehow be possible in C# with the right PInvokes etc.

Only trouble is I'm not so hot on my C++ anymore... haven't touched it for about 6 years! In fairness reading the C++ isn't the problem; it's figuring out how to get the code to compile in VS that always stumps me!

Coordinator
May 21, 2010 at 11:00 AM

yes, there are WASAPI wrappers in NAudio. Have a go with this class, which someone contributed but I haven't got round to testing yet. It's basically a few modifications to the existing Wasapi capture

 

    /// 
    /// Audio Capture using Wasapi
    /// See http://msdn.microsoft.com/en-us/library/dd370800%28VS.85%29.aspx
    /// 
    public class WasapiLoopbackCapture : IWaveIn
    {
        private const long REFTIMES_PER_SEC = 10000000;
        private const long REFTIMES_PER_MILLISEC = 10000;
        private volatile bool stop;
        private byte[] recordBuffer;
        private Thread captureThread;
        private AudioClient audioClient;
        private int bytesPerFrame;

        /// 
        /// Indicates recorded data is available 
        /// 
        public event EventHandler DataAvailable;

        /// 
        /// Indicates that all recorded data has now been received.
        /// 
        public event EventHandler RecordingStopped;

        /// 
        /// Initialises a new instance of the WASAPI capture class
        /// 
        public WasapiLoopbackCapture() :
            this(GetDefaultCaptureDevice())
        {
        }

        /// 
        /// Initialises a new instance of the WASAPI capture class
        /// 
        /// <param name="captureDevice" />Capture device to use
        public WasapiLoopbackCapture(MMDevice captureDevice)
        {
            this.audioClient = captureDevice.AudioClient;
        }

        /// 
        /// Recording wave format
        /// 
        public WaveFormat WaveFormat
        {
            get
            {
                return audioClient.MixFormat;
            }
            set
            {
                throw new Exception("Setting of Wave Format not supported for loopback device !");
            }
        }

        /// 
        /// Gets the default audio capture device
        /// 
        /// The default audio capture device
        public static MMDevice GetDefaultCaptureDevice()
        {
            MMDeviceEnumerator devices = new MMDeviceEnumerator();
            return devices.GetDefaultAudioEndpoint(DataFlow.Render, Role.Multimedia);
        }

        private void InitializeCaptureDevice()
        {
            long requestedDuration = REFTIMES_PER_MILLISEC * 100;

            audioClient.Initialize(AudioClientShareMode.Shared,
                AudioClientStreamFlags.Loopback,
                requestedDuration,
                0,
                WaveFormat,
                Guid.Empty);

            int bufferFrameCount = audioClient.BufferSize;
            bytesPerFrame = WaveFormat.BlockAlign;
            recordBuffer = new byte[bufferFrameCount * bytesPerFrame];
            Debug.WriteLine(string.Format("record buffer size = {0}", recordBuffer.Length));
        }

        /// 
        /// Start Recording
        /// 
        public void StartRecording()
        {
            InitializeCaptureDevice();
            ThreadStart start = delegate { this.CaptureThread(this.audioClient); };
            this.captureThread = new Thread(start);

            Debug.WriteLine("Thread starting...");
            this.stop = false;
            this.captureThread.Start();
        }

        /// 
        /// Stop Recording
        /// 
        public void StopRecording()
        {
            if (this.captureThread != null)
            {
                this.stop = true;

                Debug.WriteLine("Thread ending...");

                // wait for thread to end
                this.captureThread.Join();
                this.captureThread = null;

                Debug.WriteLine("Done.");

                this.stop = false;
            }
        }

        private void CaptureThread(AudioClient client)
        {
            Debug.WriteLine(client.BufferSize);
            int bufferFrameCount = audioClient.BufferSize;

            // Calculate the actual duration of the allocated buffer.
            long actualDuration = (long)((double)REFTIMES_PER_SEC *
                             bufferFrameCount / WaveFormat.SampleRate);
            int sleepMilliseconds = (int)(actualDuration / REFTIMES_PER_MILLISEC / 2);

            AudioCaptureClient capture = client.AudioCaptureClient;
            client.Start();

            try
            {
                Debug.WriteLine(string.Format("sleep: {0} ms", sleepMilliseconds));
                while (!this.stop)
                {
                    Thread.Sleep(sleepMilliseconds);
                    ReadNextPacket(capture);
                }

                client.Stop();

                if (RecordingStopped != null)
                {
                    RecordingStopped(this, EventArgs.Empty);
                }
            }
            finally
            {
                if (capture != null)
                {
                    capture.Dispose();
                }
                if (client != null)
                {
                    client.Dispose();
                }

                client = null;
                capture = null;
            }

            System.Diagnostics.Debug.WriteLine("stop wasapi");
        }

        private void ReadNextPacket(AudioCaptureClient capture)
        {
            IntPtr buffer;
            int framesAvailable;
            AudioClientBufferFlags flags;
            int packetSize = capture.GetNextPacketSize();
            int recordBufferOffset = 0;
            //Debug.WriteLine(string.Format("packet size: {0} samples", packetSize / 4));

            while (packetSize != 0)
            {
                buffer = capture.GetBuffer(out framesAvailable, out flags);

                int bytesAvailable = framesAvailable * bytesPerFrame;

                //Debug.WriteLine(string.Format("got buffer: {0} frames", framesAvailable));

                // if not silence...
                if ((flags & AudioClientBufferFlags.Silent) != AudioClientBufferFlags.Silent)
                {
                    Marshal.Copy(buffer, recordBuffer, recordBufferOffset, bytesAvailable);
                }
                else
                {
                    Array.Clear(recordBuffer, recordBufferOffset, bytesAvailable);
                }
                recordBufferOffset += bytesAvailable;
                capture.ReleaseBuffer(framesAvailable);
                packetSize = capture.GetNextPacketSize();
            }
            if (DataAvailable != null)
            {
                DataAvailable(this, new WaveInEventArgs(recordBuffer, recordBufferOffset));
            }
        }

        /// 
        /// Dispose
        /// 
        public void Dispose()
        {
            StopRecording();
        }
    }

May 21, 2010 at 11:03 AM

Fantastic, thanks Mark! I'll give that a go and report back on here how I get on.

May 21, 2010 at 11:11 AM

p.s. Enjoy the sun this weekend in Southampton; I live in Brighton - gotta love those coastal cities in the Summer!

Coordinator
May 21, 2010 at 11:21 AM

yes, nice living on the south coast. good .net developer community here too, and from the sounds of it in Brighton too

 

Jul 21, 2010 at 6:59 PM

I tested the code when I wrote it so it should work ;)