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 [--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:
Press Enter to quit...
IAudioCaptureClient::GetBuffer set flags to 0x00000001 on pass 5381 after 1088829 frames
Thread 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.
EDIT 11/5/2014: Go read this new post which has updated source and binaries, as well as links to better samples.