BlankAudioPlayback(CTSTR lpDevice) { const CLSID CLSID_MMDeviceEnumerator = __uuidof(MMDeviceEnumerator); const IID IID_IMMDeviceEnumerator = __uuidof(IMMDeviceEnumerator); const IID IID_IAudioClient = __uuidof(IAudioClient); const IID IID_IAudioRenderClient = __uuidof(IAudioRenderClient); HRESULT err; err = CoCreateInstance(CLSID_MMDeviceEnumerator, NULL, CLSCTX_ALL, IID_IMMDeviceEnumerator, (void**)&mmEnumerator); if(FAILED(err)) CrashError(TEXT("Could not create IMMDeviceEnumerator: 0x%08lx"), err); if (scmpi(lpDevice, TEXT("Default")) == 0) err = mmEnumerator->GetDefaultAudioEndpoint(eRender, eConsole, &mmDevice); else err = mmEnumerator->GetDevice(lpDevice, &mmDevice); if(FAILED(err)) CrashError(TEXT("Could not create IMMDevice")); err = mmDevice->Activate(IID_IAudioClient, CLSCTX_ALL, NULL, (void**)&mmClient); if(FAILED(err)) CrashError(TEXT("Could not create IAudioClient")); WAVEFORMATEX *pwfx; err = mmClient->GetMixFormat(&pwfx); if(FAILED(err)) CrashError(TEXT("Could not get mix format from audio client")); UINT inputBlockSize = pwfx->nBlockAlign; err = mmClient->Initialize(AUDCLNT_SHAREMODE_SHARED, 0, ConvertMSTo100NanoSec(1000), 0, pwfx, NULL); if(FAILED(err)) CrashError(TEXT("Could not initialize audio client, error = %08lX"), err); err = mmClient->GetService(IID_IAudioRenderClient, (void**)&mmRender); if(FAILED(err)) CrashError(TEXT("Could not get audio render client")); //---------------------------------------------------------------- UINT bufferFrameCount; err = mmClient->GetBufferSize(&bufferFrameCount); if(FAILED(err)) CrashError(TEXT("Could not get audio buffer size")); BYTE *lpData; err = mmRender->GetBuffer(bufferFrameCount, &lpData); if(FAILED(err)) CrashError(TEXT("Could not get audio buffer")); zero(lpData, bufferFrameCount*inputBlockSize); mmRender->ReleaseBuffer(bufferFrameCount, 0);//AUDCLNT_BUFFERFLAGS_SILENT); //probably better if it doesn't know if(FAILED(mmClient->Start())) CrashError(TEXT("Could not start audio source")); }
void PlayAudio() { REFERENCE_TIME hnsRequestedDuration = REFTIMES_PER_SEC; // microseconds, so this is 1 seconds REFERENCE_TIME hnsActualDuration; HRESULT hr; IMMDeviceEnumerator *pEnumerator = NULL; IMMDevice *pDevice = NULL; IAudioClient *pAudioClient = NULL; IAudioRenderClient *pRenderClient = NULL; WAVEFORMATEX *pwfx = NULL; UINT32 bufferFrameCount; UINT32 numFramesAvailable; UINT32 numFramesPadding; BYTE *pData; DWORD flags = 0; hr = CoCreateInstance(CLSID_MMDeviceEnumerator, NULL, CLSCTX_ALL, IID_IMMDeviceEnumerator, (void**)&pEnumerator); EXIT_ON_ERROR(hr); hr = pEnumerator->GetDefaultAudioEndpoint( eRender, eConsole, &pDevice); EXIT_ON_ERROR(hr); hr = pDevice->Activate( IID_IAudioClient, CLSCTX_ALL, NULL, (void**)&pAudioClient); EXIT_ON_ERROR(hr); hr = pAudioClient->GetMixFormat(&pwfx); EXIT_ON_ERROR(hr); hr = pAudioClient->Initialize( AUDCLNT_SHAREMODE_SHARED, 0, hnsRequestedDuration, 0, pwfx, NULL); EXIT_ON_ERROR(hr); // Get the actual size of the allocated buffer. hr = pAudioClient->GetBufferSize(&bufferFrameCount); EXIT_ON_ERROR(hr); hr = pAudioClient->GetService( IID_IAudioRenderClient, (void**)&pRenderClient); EXIT_ON_ERROR(hr); // Grab the entire buffer for the initial fill operation. hr = pRenderClient->GetBuffer(bufferFrameCount, &pData); EXIT_ON_ERROR(hr); // load initial data hr = LoadAudioBuffer(bufferFrameCount, pData, pwfx, &flags); EXIT_ON_ERROR(hr); hr = pRenderClient->ReleaseBuffer(bufferFrameCount, flags); EXIT_ON_ERROR(hr); // Calculate the actual duration of the allocated buffer. hnsActualDuration = (REFERENCE_TIME)((double)REFTIMES_PER_SEC * bufferFrameCount / pwfx->nSamplesPerSec); hr = pAudioClient->Start(); // Start playing. EXIT_ON_ERROR(hr); // Each loop fills about half of the shared buffer. while (flags != AUDCLNT_BUFFERFLAGS_SILENT) { // Sleep for half the buffer duration. Sleep((DWORD)(hnsActualDuration/REFTIMES_PER_MILLISEC/2)); // See how much buffer space is available. hr = pAudioClient->GetCurrentPadding(&numFramesPadding); EXIT_ON_ERROR(hr) numFramesAvailable = bufferFrameCount - numFramesPadding; // Grab all the available space in the shared buffer. hr = pRenderClient->GetBuffer(numFramesAvailable, &pData); EXIT_ON_ERROR(hr) // Get next 1/2-second of data from the audio source. hr = LoadAudioBuffer(numFramesAvailable, pData, pwfx, &flags); EXIT_ON_ERROR(hr) hr = pRenderClient->ReleaseBuffer(numFramesAvailable, flags); EXIT_ON_ERROR(hr) } // Wait for last data in buffer to play before stopping. Sleep((DWORD)(hnsActualDuration/REFTIMES_PER_MILLISEC/2)); hr = pAudioClient->Stop(); // Stop playing. EXIT_ON_ERROR(hr); Exit: CoTaskMemFree(pwfx); SAFE_RELEASE(pEnumerator); SAFE_RELEASE(pDevice); SAFE_RELEASE(pAudioClient); SAFE_RELEASE(pRenderClient); }
// we only call this once...per hit of the play button :) HRESULT LoopbackCaptureSetup() { assert(shouldStop); // duplicate starts would be odd... shouldStop = false; // allow graphs to restart, if they so desire... pnFrames = 0; bool bInt16 = true; // makes it actually work, for some reason...LODO HRESULT hr; hr = get_default_device(&m_pMMDevice); // so it can re-place our pointer... if (FAILED(hr)) { return hr; } // tell it to not overflow one buffer's worth <sigh> not sure if this is right or not, and thus we don't "cache" or "buffer" more than that much currently... // but a buffer size is a buffer size...hmm...as long as we keep it small though... assert(expectedMaxBufferSize <= pBufOriginalSize); // activate an (the default, for us, since we want loopback) IAudioClient hr = m_pMMDevice->Activate( __uuidof(IAudioClient), CLSCTX_ALL, NULL, (void**)&pAudioClient ); if (FAILED(hr)) { ShowOutput("IMMDevice::Activate(IAudioClient) failed: hr = 0x%08x", hr); return hr; } // get the default device periodicity, why? I don't know... REFERENCE_TIME hnsDefaultDevicePeriod; hr = pAudioClient->GetDevicePeriod(&hnsDefaultDevicePeriod, NULL); if (FAILED(hr)) { ShowOutput("IAudioClient::GetDevicePeriod failed: hr = 0x%08x\n", hr); pAudioClient->Release(); return hr; } // get the default device format (incoming...) WAVEFORMATEX *pwfx; // incoming wave... // apparently propogated by GetMixFormat... hr = pAudioClient->GetMixFormat(&pwfx); if (FAILED(hr)) { ShowOutput("IAudioClient::GetMixFormat failed: hr = 0x%08x\n", hr); CoTaskMemFree(pwfx); pAudioClient->Release(); return hr; } if (true /*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: assert(false);// we never get here...I hope... 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<PWAVEFORMATEXTENSIBLE>(pwfx); if (IsEqualGUID(KSDATAFORMAT_SUBTYPE_IEEE_FLOAT, pEx->SubFormat)) { // WE GET HERE! 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; /* scawah lodo... if(ifNotNullThenJustSetTypeOnly) { PWAVEFORMATEXTENSIBLE pEx2 = reinterpret_cast<PWAVEFORMATEXTENSIBLE>(ifNotNullThenJustSetTypeOnly); pEx2->SubFormat = pEx->SubFormat; pEx2->Samples.wValidBitsPerSample = pEx->Samples.wValidBitsPerSample; } */ } else { ShowOutput("Don't know how to coerce mix format to int-16\n"); CoTaskMemFree(pwfx); pAudioClient->Release(); return E_UNEXPECTED; } } break; default: ShowOutput("Don't know how to coerce WAVEFORMATEX with wFormatTag = 0x%08x to int-16\n", pwfx->wFormatTag); CoTaskMemFree(pwfx); pAudioClient->Release(); return E_UNEXPECTED; } } /* scawah setting stream types up to match...didn't seem to work well... if(ifNotNullThenJustSetTypeOnly) { // pwfx is set at this point... WAVEFORMATEX* pwfex = ifNotNullThenJustSetTypeOnly; // copy them all out as the possible format...hmm... pwfx->wFormatTag = WAVE_FORMAT_PCM; pwfx->wBitsPerSample = 16; pwfx->nBlockAlign = pwfx->nChannels * pwfx->wBitsPerSample / 8; pwfx->nAvgBytesPerSec = pwfx->nBlockAlign * pwfx->nSamplesPerSec; pwfex->wFormatTag = pwfx->wFormatTag; pwfex->nChannels = pwfx->nChannels; pwfex->nSamplesPerSec = pwfx->nSamplesPerSec; pwfex->wBitsPerSample = pwfx->wBitsPerSample; pwfex->nBlockAlign = pwfx->nBlockAlign; pwfex->nAvgBytesPerSec = pwfx->nAvgBytesPerSec; pwfex->cbSize = pwfx->cbSize; //FILE *fp = fopen("/normal2", "w"); // fails on me? maybe juts a VLC thing... //fShowOutput(fp, "hello world %d %d %d %d %d %d %d", pwfex->wFormatTag, pwfex->nChannels, // pwfex->nSamplesPerSec, pwfex->wBitsPerSample, pwfex->nBlockAlign, pwfex->nAvgBytesPerSec, pwfex->cbSize ); //fclose(fp); // cleanup // I might be leaking here... CoTaskMemFree(pwfx); pAudioClient->Release(); //m_pMMDevice->Release(); return hr; }*/ MMCKINFO ckRIFF = {0}; MMCKINFO ckData = {0}; nBlockAlign = pwfx->nBlockAlign; // avoid stuttering on close // http://social.msdn.microsoft.com/forums/en-US/windowspro-audiodevelopment/thread/c7ba0a04-46ce-43ff-ad15-ce8932c00171/ //IAudioClient *pAudioClient = NULL; //IAudioCaptureClient *pCaptureClient = NULL; IMMDeviceEnumerator *pEnumerator = NULL; IMMDevice *pDevice = NULL; IAudioRenderClient *pRenderClient = NULL; WAVEFORMATEXTENSIBLE *captureDataFormat = NULL; BYTE *captureData; REFERENCE_TIME hnsRequestedDuration = REFTIMES_PER_SEC; hr = CoCreateInstance( CLSID_MMDeviceEnumerator, NULL, CLSCTX_ALL, IID_IMMDeviceEnumerator, (void**)&pEnumerator); EXIT_ON_ERROR(hr) hr = pEnumerator->GetDefaultAudioEndpoint(eRender, eConsole, &pDevice); EXIT_ON_ERROR(hr) hr = pDevice->Activate(IID_IAudioClient, CLSCTX_ALL, NULL, (void**)&pAudioClient); EXIT_ON_ERROR(hr) hr = pAudioClient->GetMixFormat((WAVEFORMATEX **)&captureDataFormat); EXIT_ON_ERROR(hr) // Silence: initialise in sharedmode [this is the "silence" bug overwriter, so buffer doesn't matter as much...] hr = pAudioClient->Initialize( AUDCLNT_SHAREMODE_SHARED, 0, REFTIMES_PER_SEC, // buffer size a full 1.0s, though prolly doesn't matter here. 0, pwfx, NULL); EXIT_ON_ERROR(hr) // get the frame count UINT32 bufferFrameCount; hr = pAudioClient->GetBufferSize(&bufferFrameCount); EXIT_ON_ERROR(hr) // create a render client hr = pAudioClient->GetService(IID_IAudioRenderClient, (void**)&pRenderClient); EXIT_ON_ERROR(hr) // get the buffer hr = pRenderClient->GetBuffer(bufferFrameCount, &captureData); EXIT_ON_ERROR(hr) // release it hr = pRenderClient->ReleaseBuffer(bufferFrameCount, AUDCLNT_BUFFERFLAGS_SILENT); EXIT_ON_ERROR(hr) // release the audio client pAudioClient->Release(); EXIT_ON_ERROR(hr) // create a new IAudioClient hr = pDevice->Activate(IID_IAudioClient, CLSCTX_ALL, NULL, (void**)&pAudioClient); EXIT_ON_ERROR(hr) // -============================ now the sniffing code initialization stuff, direct from mauritius... =================================== // 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 have to do this in a timer-driven loop... hr = pAudioClient->Initialize( AUDCLNT_SHAREMODE_SHARED, AUDCLNT_STREAMFLAGS_LOOPBACK, REFTIMES_PER_SEC, // buffer size a full 1.0s, seems ok VLC 0, pwfx, 0 ); if (FAILED(hr)) { ShowOutput("IAudioClient::Initialize failed: hr = 0x%08x\n", hr); pAudioClient->Release(); return hr; } CoTaskMemFree(pwfx); // activate an IAudioCaptureClient hr = pAudioClient->GetService( __uuidof(IAudioCaptureClient), (void**)&pAudioCaptureClient // CARE INSTANTIATION ); if (FAILED(hr)) { ShowOutput("IAudioClient::GetService(IAudioCaptureClient) failed: hr 0x%08x\n", hr); pAudioClient->Release(); return hr; } // register with MMCSS DWORD nTaskIndex = 0; hTask = AvSetMmThreadCharacteristics(L"Capture", &nTaskIndex); if (NULL == hTask) { DWORD dwErr = GetLastError(); ShowOutput("AvSetMmThreadCharacteristics failed: last error = %u\n", dwErr); pAudioCaptureClient->Release(); pAudioClient->Release(); return HRESULT_FROM_WIN32(dwErr); } // call IAudioClient::Start hr = pAudioClient->Start(); if (FAILED(hr)) { ShowOutput("IAudioClient::Start failed: hr = 0x%08x\n", hr); AvRevertMmThreadCharacteristics(hTask); pAudioCaptureClient->Release(); pAudioClient->Release(); return hr; } bFirstPacket = true; // start the forever grabbing thread... DWORD dwThreadID; m_hThread = CreateThread(NULL, 0, propagateBufferForever, 0, 0, &dwThreadID); if(!m_hThread) { DWORD dwErr = GetLastError(); return HRESULT_FROM_WIN32(dwErr); } else { // we...shouldn't need this...maybe? // seems to make no difference... hr = SetThreadPriority(m_hThread, THREAD_PRIORITY_TIME_CRITICAL); if (FAILED(hr)) { // of course we always want to be a high prio thread, right? [we don't use much cpu...] return hr; } } return hr; } // end LoopbackCaptureSetup