Matthew van Eerde's web log
I am a Software Development Engineer in Test working for the Windows Sound team. You can contact me via email: mateer at microsoft dot com
Friend key: 28904932216450_59cd9d55374be03d8167d37c8ff4196b
In a previous post I showed how to play silence to a given audio device and hinted at a possible application.
Attached to this post is a sample WASAPI loopback capture app - amd64, x86 and source included. This allows you to record the sound that is coming out of your speakers:
>loopback-capture -?loopback-capture -?loopback-capture --list-devicesloopback-capture [--device "Device long name"] [--file "file name"] [--int-16] -? prints this message. --list-devices displays the long names of all active playback devices. --device captures from the specified device (default if omitted) --file saves the output to a file (loopback-capture.wav if omitted)) --int-16 attempts to coerce data to 16-bit integer format
There are a couple of oddities for WASAPI loopback capture. One is that "event mode" doesn't work for loopback capture; you can call pAudioClient->Initialize(... AUDCLNT_STREAMFLAGS_LOOPBACK | AUDCLNT_STREAMFLAGS_EVENTCALLBACK, ... ), you can call pAudioClient->SetEventHandle(...), and everything will succeed... but the "data is ready" event will never fire. So this app creates its own waitable timer.
Another oddity is that WASAPI will only push data down to the render endpoint when there are active streams. When nothing is playing, there is nothing to capture.
For example, play a song, and then run loopback-capture. While loopback-capture is running, stop the song, and then start it again. You'll get this output when you start it back up:
>loopback-capturePress Enter to quit...IAudioCaptureClient::GetBuffer set flags to 0x00000001 on pass 5381 after 1088829 framesThread HRESULT is 0x8000ffff
The flag in question is AUDCLNT_BUFFERFLAGS_DATA_DISCONTINUITY. When the song stopped, no more data was available to capture. Eventually the song started up again, and WASAPI dutifully reported that there was a glitch detected. This app stops on glitches.
There are a couple of other possible ways to handle this. One way is to ignore glitches; then if you stop a song, wait a few seconds, and start it again, then the recorded signal will omit the wait and abut the two "audio is playing" portions.
But my particular favorite way of handling this is to run silence.exe. That way there are never any "nothing is playing" glitches, because there's always something playing.
EDIT 11/23/2009: Updated loopback-capture.exe to ignore the glitch flag on the first packet, since Windows 7 sets it. Also improved the interaction between the capture thread bailing out and the user pressing Enter to finish.
Would you happen to know whether it's possible to record audio from a specific application using the loopback technique?
Not directly... loopback capture is the aggregate of all shared-mode streams to the device in question, post-mix.
This also won't work for applications that play in exclusive mode.
I have been looking for something similar to what Jay mentions as well. I would like to capture the audio stream for a single application to a file stream. So if loopback capture isn't the right answer, is there one?
I will provide your feedback to the audio team.
Thanks very much for the post.. I was desperately looking for this idea for my pet project. Thanks again.
Thanks for this. One of my systems (a Dell e521) is a model that has no ability to record stereo mix. Why I think stereo mix should've been included, and am at a loss as to why Dell or MS would remove it, this is a decent workaround.
First of all, thanks for an interesting post!
I'm trying to port the code to C# but have run into some trouble. Everything is up and running except the inner capturing loop. When I call iAudioCaptureClient.GetNextPacketSize it throws an AUDCLNT_E_OUT_OF_ORDER at me.
If I skip that and call iAudioCaptureClient.GetBuffer it actually returns a couple of frames (around 400) and that data seems good. But when I call ReleaseBuffer I get an AccessViolationException and the next call to GetBuffer will now also return an AUDCLNT_E_OUT_OF_ORDER error.
Any idea what I could be doing wrong?
It sounds like your C# iAudioCaptureClient methods aren't mapping to the IAudioCaptureClient methods correctly.; that is, the method you think you're calling is not the method you're actually calling.
AUDCLNT_E_OUT_OF_ORDER is expected if you call ReleaseBuffer() without calling GetBuffer() first, or if you call GetBuffer() twice in a row without a ReleaseBuffer() in between.
The Access Violation is harder to explain.
Can you post (or email me) the DllImport code you're using to define the iAudioCaptureClient methods?
I have looked into the attachments and had the loopback-capture.exe file. How should i use the exe file to record what i hear from my system speakers? can any one help me please?
What have you tried, and what happened when you tried it? Note that depending on your audio hardware and drivers there are likely other ways to record what comes from your speakers: e.g., using the analog mixer.
Thank you very much, Matthew!
I really want to on like this.
Again thank you!
How we can make it the loop back capture usefull for AEC, in the sense I need to record what are the samples rendering to play back device as well as the sound capturing from microphone. If it is possible in vista could i do it in XP also?. Plz reply me.......
I'd love to see a .NET port of this, too bad my C++ skills are too bad to wrap my head around this.
Thanks for sharing -- this is great. Will the AUDCLNT_STREAMFLAGS_EVENTCALLBACK and "data is ready" event be support for LOOPBACK in Win7? i.e., was this a bug that will be fixed in Win7?
No, this is a design decision. I'm lobbying for IAudioClient::Initialize(... _EVENTCALLBACK | _LOOPBACK, ...) to return an error code, but that won't happen for Win7 either.