// silence.cpp
#include
#include
#include
#include
#include
#include
#include "loopback-capture.h"
HRESULT LoopbackCapture(
IMMDevice *pMMDevice,
HMMIO hFile,
bool bInt16,
HANDLE hStartedEvent,
HANDLE hStopEvent,
PUINT32 pnFrames
);
HRESULT WriteWaveHeader(HMMIO hFile, LPCWAVEFORMATEX pwfx, MMCKINFO *pckRIFF, MMCKINFO *pckData);
HRESULT FinishWaveFile(HMMIO hFile, MMCKINFO *pckRIFF, MMCKINFO *pckData);
DWORD WINAPI LoopbackCaptureThreadFunction(LPVOID pContext) {
LoopbackCaptureThreadFunctionArguments *pArgs =
(LoopbackCaptureThreadFunctionArguments*)pContext;
pArgs->hr = CoInitialize(NULL);
if (FAILED(pArgs->hr)) {
printf("CoInitialize failed: hr = 0xx\n", pArgs->hr);
return 0;
}
pArgs->hr = LoopbackCapture(
pArgs->pMMDevice,
pArgs->hFile,
pArgs->bInt16,
pArgs->hStartedEvent,
pArgs->hStopEvent,
&pArgs->nFrames
);
CoUninitialize();
return 0;
}
HRESULT LoopbackCapture(
IMMDevice *pMMDevice,
HMMIO hFile,
bool bInt16,
HANDLE hStartedEvent,
HANDLE hStopEvent,
PUINT32 pnFrames
) {
HRESULT hr;
// activate an IAudioClient
IAudioClient *pAudioClient;
hr = pMMDevice->Activate(
__uuidof(IAudioClient),
CLSCTX_ALL, NULL,
(void**)&pAudioClient
);
if (FAILED(hr)) {
printf("IMMDevice::Activate(IAudioClient) failed: hr = 0xx", hr);
return hr;
}
// get the default device periodicity
REFERENCE_TIME hnsDefaultDevicePeriod;
hr = pAudioClient->GetDevicePeriod(&hnsDefaultDevicePeriod, NULL);
if (FAILED(hr)) {
printf("IAudioClient::GetDevicePeriod failed: hr = 0xx\n", hr);
pAudioClient->Release();
return hr;
}
// get the default device format
WAVEFORMATEX *pwfx;
hr = pAudioClient->GetMixFormat(&pwfx);
if (FAILED(hr)) {
printf("IAudioClient::GetMixFormat failed: hr = 0xx\n", hr);
CoTaskMemFree(pwfx);
pAudioClient->Release();
return hr;
}
if (bInt16) {
// coerce int-16 wave format
// can do this in-place since we're not changing the size of the format
// also, the engine will auto-convert from float to int for us
switch (pwfx->wFormatTag) {
case WAVE_FORMAT_IEEE_FLOAT:
pwfx->wFormatTag = WAVE_FORMAT_PCM;
pwfx->wBitsPerSample = 16;
pwfx->nBlockAlign = pwfx->nChannels * pwfx->wBitsPerSample / 8;
pwfx->nAvgBytesPerSec = pwfx->nBlockAlign * pwfx->nSamplesPerSec;
break;
case WAVE_FORMAT_EXTENSIBLE:
{
// naked scope for case-local variable
PWAVEFORMATEXTENSIBLE pEx = reinterpret_cast(pwfx);
if (IsEqualGUID(KSDATAFORMAT_SUBTYPE_IEEE_FLOAT, pEx->SubFormat)) {
pEx->SubFormat = KSDATAFORMAT_SUBTYPE_PCM;
pEx->Samples.wValidBitsPerSample = 16;
pwfx->wBitsPerSample = 16;
pwfx->nBlockAlign = pwfx->nChannels * pwfx->wBitsPerSample / 8;
pwfx->nAvgBytesPerSec = pwfx->nBlockAlign * pwfx->nSamplesPerSec;
} else {
printf("Don't know how to coerce mix format to int-16\n");
CoTaskMemFree(pwfx);
pAudioClient->Release();
return E_UNEXPECTED;
}
}
break;
default:
printf("Don't know how to coerce WAVEFORMATEX with wFormatTag = 0xx to int-16\n", pwfx->wFormatTag);
CoTaskMemFree(pwfx);
pAudioClient->Release();
return E_UNEXPECTED;
}
}
MMCKINFO ckRIFF = {0};
MMCKINFO ckData = {0};
hr = WriteWaveHeader(hFile, pwfx, &ckRIFF, &ckData);
if (FAILED(hr)) {
// WriteWaveHeader does its own logging
CoTaskMemFree(pwfx);
pAudioClient->Release();
return hr;
}
// create a periodic waitable timer
HANDLE hWakeUp = CreateWaitableTimer(NULL, FALSE, NULL);
if (NULL == hWakeUp) {
DWORD dwErr = GetLastError();
printf("CreateWaitableTimer failed: last error = %u\n", dwErr);
CoTaskMemFree(pwfx);
pAudioClient->Release();
return HRESULT_FROM_WIN32(dwErr);
}
UINT32 nBlockAlign = pwfx->nBlockAlign;
*pnFrames = 0;
// call IAudioClient::Initialize
// note that AUDCLNT_STREAMFLAGS_LOOPBACK and AUDCLNT_STREAMFLAGS_EVENTCALLBACK
// do not work together...
// the "data ready" event never gets set
// so we're going to do a timer-driven loop
hr = pAudioClient->Initialize(
AUDCLNT_SHAREMODE_SHARED,
AUDCLNT_STREAMFLAGS_LOOPBACK,
0, 0, pwfx, 0
);
if (FAILED(hr)) {
printf("IAudioClient::Initialize failed: hr = 0xx\n", hr);
CloseHandle(hWakeUp);
pAudioClient->Release();
return hr;
}
CoTaskMemFree(pwfx);
// activate an IAudioCaptureClient
IAudioCaptureClient *pAudioCaptureClient;
hr = pAudioClient->GetService(
__uuidof(IAudioCaptureClient),
(void**)&pAudioCaptureClient
);
if (FAILED(hr)) {
printf("IAudioClient::GetService(IAudioCaptureClient) failed: hr 0xx\n", hr);
CloseHandle(hWakeUp);
pAudioClient->Release();
return hr;
}
// register with MMCSS
DWORD nTaskIndex = 0;
HANDLE hTask = AvSetMmThreadCharacteristics(L"Capture", &nTaskIndex);
if (NULL == hTask) {
DWORD dwErr = GetLastError();
printf("AvSetMmThreadCharacteristics failed: last error = %u\n", dwErr);
pAudioCaptureClient->Release();
CloseHandle(hWakeUp);
pAudioClient->Release();
return HRESULT_FROM_WIN32(dwErr);
}
// set the waitable timer
LARGE_INTEGER liFirstFire;
liFirstFire.QuadPart = -hnsDefaultDevicePeriod / 2; // negative means relative time
LONG lTimeBetweenFires = (LONG)hnsDefaultDevicePeriod / 2 / (10 * 1000); // convert to milliseconds
BOOL bOK = SetWaitableTimer(
hWakeUp,
&liFirstFire,
lTimeBetweenFires,
NULL, NULL, FALSE
);
if (!bOK) {
DWORD dwErr = GetLastError();
printf("SetWaitableTimer failed: last error = %u\n", dwErr);
AvRevertMmThreadCharacteristics(hTask);
pAudioCaptureClient->Release();
CloseHandle(hWakeUp);
pAudioClient->Release();
return HRESULT_FROM_WIN32(dwErr);
}
// call IAudioClient::Start
hr = pAudioClient->Start();
if (FAILED(hr)) {
printf("IAudioClient::Start failed: hr = 0xx\n", hr);
AvRevertMmThreadCharacteristics(hTask);
pAudioCaptureClient->Release();
CloseHandle(hWakeUp);
pAudioClient->Release();
return hr;
}
SetEvent(hStartedEvent);
// loopback capture loop
HANDLE waitArray[2] = { hStopEvent, hWakeUp };
DWORD dwWaitResult;
bool bDone = false;
bool bFirstPacket = true;
for (UINT32 nPasses = 0; !bDone; nPasses++) {
dwWaitResult = WaitForMultipleObjects(
ARRAYSIZE(waitArray), waitArray,
FALSE, INFINITE
);
if (WAIT_OBJECT_0 == dwWaitResult) {
printf("Received stop event after %u passes and %u frames\n", nPasses, *pnFrames);
bDone = true;
continue; // exits loop
}
if (WAIT_OBJECT_0 + 1 != dwWaitResult) {
printf("Unexpected WaitForMultipleObjects return value %u on pass %u after %u frames\n", dwWaitResult, nPasses, *pnFrames);
pAudioClient->Stop();
CancelWaitableTimer(hWakeUp);
AvRevertMmThreadCharacteristics(hTask);
pAudioCaptureClient->Release();
CloseHandle(hWakeUp);
pAudioClient->Release();
return E_UNEXPECTED;
}
// got a "wake up" event - see if there's data
UINT32 nNextPacketSize;
hr = pAudioCaptureClient->GetNextPacketSize(&nNextPacketSize);
if (FAILED(hr)) {
printf("IAudioCaptureClient::GetNextPacketSize failed on pass %u after %u frames: hr = 0xx\n", nPasses, *pnFrames, hr);
pAudioClient->Stop();
CancelWaitableTimer(hWakeUp);
AvRevertMmThreadCharacteristics(hTask);
pAudioCaptureClient->Release();
CloseHandle(hWakeUp);
pAudioClient->Release();
return hr;
}
if (0 == nNextPacketSize) {
// no data yet
continue;
}
// get the captured data
BYTE *pData;
UINT32 nNumFramesToRead;
DWORD dwFlags;
hr = pAudioCaptureClient->GetBuffer(
&pData,
&nNumFramesToRead,
&dwFlags,
NULL,
NULL
);
if (FAILED(hr)) {
printf("IAudioCaptureClient::GetBuffer failed on pass %u after %u frames: hr = 0xx\n", nPasses, *pnFrames, hr);
pAudioClient->Stop();
CancelWaitableTimer(hWakeUp);
AvRevertMmThreadCharacteristics(hTask);
pAudioCaptureClient->Release();
CloseHandle(hWakeUp);
pAudioClient->Release();
return hr;
}
if (bFirstPacket && AUDCLNT_BUFFERFLAGS_DATA_DISCONTINUITY == dwFlags) {
printf("Probably spurious glitch reported on first packet\n");
} else if (0 != dwFlags) {
printf("IAudioCaptureClient::GetBuffer set flags to 0xx on pass %u after %u frames\n", dwFlags, nPasses, *pnFrames);
pAudioClient->Stop();
CancelWaitableTimer(hWakeUp);
AvRevertMmThreadCharacteristics(hTask);
pAudioCaptureClient->Release();
CloseHandle(hWakeUp);
pAudioClient->Release();
return E_UNEXPECTED;
}
if (0 == nNumFramesToRead) {
printf("IAudioCaptureClient::GetBuffer said to read 0 frames on pass %u after %u frames\n", nPasses, *pnFrames);
pAudioClient->Stop();
CancelWaitableTimer(hWakeUp);
AvRevertMmThreadCharacteristics(hTask);
pAudioCaptureClient->Release();
CloseHandle(hWakeUp);
pAudioClient->Release();
return E_UNEXPECTED;
}
LONG lBytesToWrite = nNumFramesToRead * nBlockAlign;
#pragma prefast(suppress: __WARNING_INCORRECT_ANNOTATION, "IAudioCaptureClient::GetBuffer SAL annotation implies a 1-byte buffer")
LONG lBytesWritten = mmioWrite(hFile, reinterpret_cast(pData),
lBytesToWrite
);
if (lBytesToWrite != lBytesWritten) {
printf("mmioWrite wrote %u bytes on pass %u after %u frames: expected %u bytes\n", lBytesWritten, nPasses, *pnFrames, lBytesToWrite);
pAudioClient->Stop();
CancelWaitableTimer(hWakeUp);
AvRevertMmThreadCharacteristics(hTask);
pAudioCaptureClient->Release();
CloseHandle(hWakeUp);
pAudioClient->Release();
return E_UNEXPECTED;
}
*pnFrames += nNumFramesToRead;
hr = pAudioCaptureClient->ReleaseBuffer(nNumFramesToRead);
if (FAILED(hr)) {
printf("IAudioCaptureClient::ReleaseBuffer failed on pass %u after %u frames: hr = 0xx\n", nPasses, *pnFrames, hr);
pAudioClient->Stop();
CancelWaitableTimer(hWakeUp);
AvRevertMmThreadCharacteristics(hTask);
pAudioCaptureClient->Release();
CloseHandle(hWakeUp);
pAudioClient->Release();
return hr;
}
bFirstPacket = false;
} // capture loop
hr = FinishWaveFile(hFile, &ckData, &ckRIFF);
if (FAILED(hr)) {
// FinishWaveFile does it's own logging
pAudioClient->Stop();
CancelWaitableTimer(hWakeUp);
AvRevertMmThreadCharacteristics(hTask);
pAudioCaptureClient->Release();
CloseHandle(hWakeUp);
pAudioClient->Release();
return hr;
}
pAudioClient->Stop();
CancelWaitableTimer(hWakeUp);
AvRevertMmThreadCharacteristics(hTask);
pAudioCaptureClient->Release();
CloseHandle(hWakeUp);
pAudioClient->Release();
return hr;
}
HRESULT WriteWaveHeader(HMMIO hFile, LPCWAVEFORMATEX pwfx, MMCKINFO *pckRIFF, MMCKINFO *pckData) {
MMRESULT result;
// make a RIFF/WAVE chunk
pckRIFF->ckid = MAKEFOURCC('R', 'I', 'F', 'F');
pckRIFF->fccType = MAKEFOURCC('W', 'A', 'V', 'E');
result = mmioCreateChunk(hFile, pckRIFF, MMIO_CREATERIFF);
if (MMSYSERR_NOERROR != result) {
printf("mmioCreateChunk("RIFF/WAVE") failed: MMRESULT = 0xx\n", result);
return E_FAIL;
}
// make a 'fmt ' chunk (within the RIFF/WAVE chunk)
MMCKINFO chunk;
chunk.ckid = MAKEFOURCC('f', 'm', 't', ' ');
result = mmioCreateChunk(hFile, &chunk, 0);
if (MMSYSERR_NOERROR != result) {
printf("mmioCreateChunk("fmt ") failed: MMRESULT = 0xx\n", result);
return E_FAIL;
}
// write the WAVEFORMATEX data to it
LONG lBytesInWfx = sizeof(WAVEFORMATEX) + pwfx->cbSize;
LONG lBytesWritten =
mmioWrite(
hFile,
reinterpret_cast(const_cast(pwfx)),
lBytesInWfx
);
if (lBytesWritten != lBytesInWfx) {
printf("mmioWrite(fmt data) wrote %u bytes; expected %u bytes\n", lBytesWritten, lBytesInWfx);
return E_FAIL;
}
// ascend from the 'fmt ' chunk
result = mmioAscend(hFile, &chunk, 0);
if (MMSYSERR_NOERROR != result) {
printf("mmioAscend("fmt " failed: MMRESULT = 0xx\n", result);
return E_FAIL;
}
// make a 'fact' chunk whose data is (DWORD)0
chunk.ckid = MAKEFOURCC('f', 'a', 'c', 't');
result = mmioCreateChunk(hFile, &chunk, 0);
if (MMSYSERR_NOERROR != result) {
printf("mmioCreateChunk("fmt ") failed: MMRESULT = 0xx\n", result);
return E_FAIL;
}
// write (DWORD)0 to it
// this is cleaned up later
DWORD frames = 0;
lBytesWritten = mmioWrite(hFile, reinterpret_cast(&frames), sizeof(frames));
if (lBytesWritten != sizeof(frames)) {
printf("mmioWrite(fact data) wrote %u bytes; expected %u bytes\n", lBytesWritten, (UINT32)sizeof(frames));
return E_FAIL;
}
// ascend from the 'fact' chunk
result = mmioAscend(hFile, &chunk, 0);
if (MMSYSERR_NOERROR != result) {
printf("mmioAscend("fact" failed: MMRESULT = 0xx\n", result);
return E_FAIL;
}
// make a 'data' chunk and leave the data pointer there
pckData->ckid = MAKEFOURCC('d', 'a', 't', 'a');
result = mmioCreateChunk(hFile, pckData, 0);
if (MMSYSERR_NOERROR != result) {
printf("mmioCreateChunk("data") failed: MMRESULT = 0xx\n", result);
return E_FAIL;
}
return S_OK;
}
HRESULT FinishWaveFile(HMMIO hFile, MMCKINFO *pckRIFF, MMCKINFO *pckData) {
MMRESULT result;
result = mmioAscend(hFile, pckData, 0);
if (MMSYSERR_NOERROR != result) {
printf("mmioAscend("data" failed: MMRESULT = 0xx\n", result);
return E_FAIL;
}
result = mmioAscend(hFile, pckRIFF, 0);
if (MMSYSERR_NOERROR != result) {
printf("mmioAscend("RIFF/WAVE" failed: MMRESULT = 0xx\n", result);
return E_FAIL;
}
return S_OK;
}
http://blog.sina.com.cn/s/blog_dc8dab590101cnob.html