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")); }
HRESULT __stdcall initialize_patch( IAudioClient* this_, AUDCLNT_SHAREMODE ShareMode, DWORD StreamFlags, REFERENCE_TIME hnsBufferDuration, REFERENCE_TIME hnsPeriodicity, const WAVEFORMATEX* pFormat, LPCGUID AudioSessionGuid) { // synchronize initializing so it doesn't happen while streams are being flushed HANDLE audio_router_mutex = OpenMutexW(SYNCHRONIZE, FALSE, L"Local\\audio-router-mutex"); assert(audio_router_mutex != NULL); if(audio_router_mutex) { DWORD res = WaitForSingleObject(audio_router_mutex, INFINITE); assert(res == WAIT_OBJECT_0); } IAudioClient* proxy = get_duplicate(this_)->proxy; LPCGUID guid = ((GUID***)this_)[0][17]; DWORD_PTR* old_vftptr = swap_vtable(this_); HRESULT hr = proxy->Initialize( ShareMode, StreamFlags | AUDCLNT_SESSIONFLAGS_EXPIREWHENUNOWNED | AUDCLNT_SESSIONFLAGS_DISPLAY_HIDEWHENEXPIRED, hnsBufferDuration, hnsPeriodicity, pFormat, guid); ((DWORD_PTR**)this_)[0] = old_vftptr; if(hr != S_OK) tell_error(hr); else *((WORD***)this_)[0][18] = pFormat->nBlockAlign; if(hr == S_OK) { for(iaudioclient_duplicate* next = get_duplicate(this_)->next; next != NULL; next = next->next) { HRESULT hr2 = next->proxy->Initialize( ShareMode, StreamFlags | AUDCLNT_STREAMFLAGS_AUTOCONVERTPCM | AUDCLNT_STREAMFLAGS_SRC_DEFAULT_QUALITY | AUDCLNT_SESSIONFLAGS_EXPIREWHENUNOWNED | AUDCLNT_SESSIONFLAGS_DISPLAY_HIDEWHENEXPIRED, hnsBufferDuration, hnsPeriodicity, pFormat, guid); if(hr2 != S_OK) tell_error(hr2); } } ReleaseMutex(audio_router_mutex); CloseHandle(audio_router_mutex); return hr; }
HRESULT __stdcall getmixformat_patch(IAudioClient* this_, WAVEFORMATEX** ppDeviceFormat) { // STATIC FUNCTION IAudioClient* proxy = get_duplicate(this_)->proxy; DWORD_PTR* old_vftptr = swap_vtable(this_); HRESULT hr = proxy->GetMixFormat(ppDeviceFormat); ((DWORD_PTR**)this_)[0] = old_vftptr; return hr; }
HRESULT __stdcall reset_patch(IAudioClient* this_) { IAudioClient* proxy = get_duplicate(this_)->proxy; DWORD_PTR* old_vftptr = swap_vtable(this_); HRESULT hr = proxy->Reset(); ((DWORD_PTR**)this_)[0] = old_vftptr; for(iaudioclient_duplicate* next = get_duplicate(this_)->next; next != NULL; next = next->next) next->proxy->Reset(); return hr; }
HRESULT __stdcall seteventhandle_patch(IAudioClient* this_, HANDLE eventHandle) { IAudioClient* proxy = get_duplicate(this_)->proxy; for(iaudioclient_duplicate* next = get_duplicate(this_)->next; next != NULL; next = next->next) next->proxy->SetEventHandle(eventHandle); DWORD_PTR* old_vftptr = swap_vtable(this_); HRESULT hr = proxy->SetEventHandle(eventHandle); ((DWORD_PTR**)this_)[0] = old_vftptr; return hr; }
HRESULT __stdcall isformatsupported_patch( IAudioClient* this_, AUDCLNT_SHAREMODE ShareMode, const WAVEFORMATEX* pFormat, WAVEFORMATEX** ppClosestMatch) { // STATIC FUNCTION IAudioClient* proxy = get_duplicate(this_)->proxy; DWORD_PTR* old_vftptr = swap_vtable(this_); HRESULT hr = proxy->IsFormatSupported(ShareMode, pFormat, ppClosestMatch); ((DWORD_PTR**)this_)[0] = old_vftptr; return hr; }
HRESULT __stdcall start_patch(IAudioClient* this_) { IAudioClient* proxy = get_duplicate(this_)->proxy; DWORD_PTR* old_vftptr = swap_vtable(this_); HRESULT hr = proxy->Start(), hr2; ((DWORD_PTR**)this_)[0] = old_vftptr; if(hr == S_OK) { for(iaudioclient_duplicate* next = get_duplicate(this_)->next; next != NULL; next = next->next) hr2 = next->proxy->Start(); } return hr; }
HRESULT __stdcall getbuffersize_patch(IAudioClient* this_, UINT32* pNumBufferFrames) { IAudioClient* proxy = get_duplicate(this_)->proxy; DWORD_PTR* old_vftptr = swap_vtable(this_); HRESULT hr = proxy->GetBufferSize(pNumBufferFrames); ((DWORD_PTR**)this_)[0] = old_vftptr; for(iaudioclient_duplicate* next = get_duplicate(this_)->next; next != NULL; next = next->next) { UINT32 buf; HRESULT hr = next->proxy->GetBufferSize(&buf); assert(buf >= *pNumBufferFrames); } return hr; }
HRESULT __stdcall getstreamlatency_patch(IAudioClient* this_, REFERENCE_TIME* phnsLatency) { IAudioClient* proxy = get_duplicate(this_)->proxy; DWORD_PTR* old_vftptr = swap_vtable(this_); HRESULT hr = proxy->GetStreamLatency(phnsLatency); ((DWORD_PTR**)this_)[0] = old_vftptr; for(iaudioclient_duplicate* next = get_duplicate(this_)->next; next != NULL; next = next->next) { REFERENCE_TIME t; next->proxy->GetStreamLatency(&t); assert(*phnsLatency == t); } return hr; }
HRESULT __stdcall getcurrentpadding_patch(IAudioClient* this_, UINT32* pNumPaddingFrames) { IAudioClient* proxy = get_duplicate(this_)->proxy; DWORD_PTR* old_vftptr = swap_vtable(this_); HRESULT hr = proxy->GetCurrentPadding(pNumPaddingFrames); ((DWORD_PTR**)this_)[0] = old_vftptr; for(iaudioclient_duplicate* next = get_duplicate(this_)->next; next != NULL; next = next->next) { UINT32 pad; next->proxy->GetCurrentPadding(&pad); //assert(pad == *pNumPaddingFrames); } return hr; }
// // Initialize WASAPI in event driven mode, associate the audio client with our samples ready event handle, retrieve // a capture client for the transport, create the capture thread and start the audio engine. // bool CWASAPICapture::InitializeAudioEngine() { HRESULT hr = _AudioClient->Initialize(AUDCLNT_SHAREMODE_SHARED, AUDCLNT_STREAMFLAGS_NOPERSIST, _EngineLatencyInMS*10000, 0, MixFormat(), NULL); PersistentAssert(SUCCEEDED(hr), "_AudioClient->Initialize failed"); // // Retrieve the buffer size for the audio client. // hr = _AudioClient->GetBufferSize(&_BufferSize); PersistentAssert(SUCCEEDED(hr), "_AudioClient->GetBufferSize failed"); hr = _AudioClient->GetService(IID_PPV_ARGS(&_CaptureClient)); PersistentAssert(SUCCEEDED(hr), "_AudioClient->GetService failed"); return true; }
// // Retrieve the format we'll use to capture samples. // // We use the Mix format since we're capturing in shared mode. // bool CWASAPICapture::LoadFormat() { HRESULT hr = _AudioClient->GetMixFormat((WAVEFORMATEX**)&_MixFormat); PersistentAssert(SUCCEEDED(hr), "_AudioClient->GetMixFormat failed"); //- SubFormat {00000003-0000-0010-8000-00AA00389B71} _GUID, KSDATAFORMAT_SUBTYPE_IEEE_FLOAT _FrameSize = (_MixFormat->Format.wBitsPerSample / 8) * _MixFormat->Format.nChannels; return true; }
~BlankAudioPlayback() { mmClient->Stop(); SafeRelease(mmRender); SafeRelease(mmClient); SafeRelease(mmDevice); SafeRelease(mmEnumerator); }
HRESULT __stdcall getdeviceperiod_patch( IAudioClient* this_, REFERENCE_TIME* phnsDefaultDevicePeriod, REFERENCE_TIME* phnsMinimumDevicePeriod) { // STATIC FUNCTION IAudioClient* proxy = get_duplicate(this_)->proxy; DWORD_PTR* old_vftptr = swap_vtable(this_); HRESULT hr = proxy->GetDevicePeriod(phnsDefaultDevicePeriod, phnsMinimumDevicePeriod); ((DWORD_PTR**)this_)[0] = old_vftptr; for(iaudioclient_duplicate* next = get_duplicate(this_)->next; next != NULL; next = next->next) { REFERENCE_TIME def, min; next->proxy->GetDevicePeriod(&def, &min); assert(def == *phnsDefaultDevicePeriod && min == *phnsMinimumDevicePeriod); } return hr; }
void MMDeviceAudioSource::StartCapture() { if(mmClient) { mmClient->Start(); UINT64 freq; mmClock->GetFrequency(&freq); Log(TEXT("MMDeviceAudioSource: Frequency for device '%s' is %llu, samples per sec is %u"), GetDeviceName(), freq, this->GetSamplesPerSec()); } }
HRESULT __stdcall getservice_patch(IAudioClient* this_, REFIID riid, void** ppv) { IAudioClient* proxy = get_duplicate(this_)->proxy; DWORD_PTR* old_vftptr = swap_vtable(this_); HRESULT hr = proxy->GetService(riid, ppv); ((DWORD_PTR**)this_)[0] = old_vftptr; // renderclient list has 1:1 mapping to audioclient if(hr == S_OK) { if(riid == __uuidof(IAudioRenderClient)) { IAudioRenderClient* host = *((IAudioRenderClient**)ppv); patch_iaudiorenderclient(host, *((WORD***)this_)[0][18]); for(iaudioclient_duplicate* next = get_duplicate(this_)->next; next != NULL; next = next->next) { IAudioRenderClient* renderclient = NULL; next->proxy->GetService(riid, (void**)&renderclient); get_duplicate(host)->add(renderclient); } } else if(riid == __uuidof(IAudioStreamVolume)) { IAudioStreamVolume* host = *((IAudioStreamVolume**)ppv); patch_iaudiostreamvolume(host); for(iaudioclient_duplicate* next = get_duplicate(this_)->next; next != NULL; next = next->next) { IAudioStreamVolume* streamvolume = NULL; next->proxy->GetService(riid, (void**)&streamvolume); if(streamvolume != NULL) get_duplicate(host)->add(streamvolume); } } } return hr; }
void propagateWithRawCurrentFormat(WAVEFORMATEX *toThis) { WAVEFORMATEX *pwfx; IMMDevice *pMMDevice; IAudioClient *pAudioClient; HANDLE hTask; DWORD nTaskIndex = 0; hTask = AvSetMmThreadCharacteristics(L"Capture", &nTaskIndex); HRESULT hr = get_default_device(&pMMDevice); if (FAILED(hr)) { assert(false); } // activate an (the default, for us, since we want loopback) IAudioClient hr = pMMDevice->Activate( __uuidof(IAudioClient), CLSCTX_ALL, NULL, (void**)&pAudioClient ); if (FAILED(hr)) { ShowOutput("IMMDevice::Activate(IAudioClient) failed: hr = 0x%08x", hr); assert(false); } hr = pAudioClient->GetMixFormat(&pwfx); if (FAILED(hr)) { ShowOutput("IAudioClient::GetMixFormat failed: hr = 0x%08x\n", hr); CoTaskMemFree(pwfx); pAudioClient->Release(); assert(false); } pAudioClient->Stop(); AvRevertMmThreadCharacteristics(hTask); pAudioClient->Release(); pMMDevice->Release(); memcpy(toThis, pwfx, sizeof(WAVEFORMATEX)); CoTaskMemFree(pwfx); }
// // Start capturing... // bool CWASAPICapture::Start(BYTE *CaptureBuffer, size_t CaptureBufferSize) { HRESULT hr; _CaptureBuffer = CaptureBuffer; _CaptureBufferSize = CaptureBufferSize; // // Now create the thread which is going to drive the capture. // _CaptureThread = CreateThread(NULL, 0, WASAPICaptureThread, this, 0, NULL); PersistentAssert(_CaptureThread != NULL, "CreateThread failed"); // // We're ready to go, start capturing! // hr = _AudioClient->Start(); PersistentAssert(SUCCEEDED(hr), "_AudioClient->Start failed"); return true; }
// // Stop the capturer. // void CWASAPICapture::Stop() { HRESULT hr; // // Tell the capture thread to shut down, wait for the thread to complete then clean up all the stuff we // allocated in Start(). // if (_ShutdownEvent) { SetEvent(_ShutdownEvent); } hr = _AudioClient->Stop(); PersistentAssert(SUCCEEDED(hr), "_AudioClient->Stop failed"); if (_CaptureThread) { WaitForSingleObject(_CaptureThread, INFINITE); CloseHandle(_CaptureThread); _CaptureThread = NULL; } }
//HRESULT LoopbackCapture( // IMMDevice *pMMDevice, // bool bInt16, // HANDLE hStartedEvent, // HANDLE hStopEvent, // PUINT32 pnFrames, // HMMIO hFile, // AudioBuffer *pBuffer //) HRESULT LoopbackCapture::Process() { 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 = 0x%08x", hr); return hr; } // get the default device periodicity REFERENCE_TIME hnsDefaultDevicePeriod; hr = pAudioClient->GetDevicePeriod(&hnsDefaultDevicePeriod, NULL); if (FAILED(hr)) { printf("IAudioClient::GetDevicePeriod failed: hr = 0x%08x\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 = 0x%08x\n", hr); CoTaskMemFree(pwfx); pAudioClient->Release(); return hr; } if (pwfx->wFormatTag == WAVE_FORMAT_EXTENSIBLE) { PWAVEFORMATEXTENSIBLE pEx = reinterpret_cast<PWAVEFORMATEXTENSIBLE>(pwfx); //pEx->SubFormat = KSDATAFORMAT_SUBTYPE_PCM; printf("WAVE_FORMAT_EXTENSIBLE\n"); if (IsEqualGUID(KSDATAFORMAT_SUBTYPE_IEEE_FLOAT, pEx->SubFormat)) { printf("float\n"); }// else if (IsEqualGUID(KSDATAFORMAT_SUBTYPE_PCM, pEx->SubFormat)) { printf("PCM\n"); }//KSDATAFORMAT_SUBTYPE_WAVEFORMATEX else if (IsEqualGUID(KSDATAFORMAT_SUBTYPE_WAVEFORMATEX, pEx->SubFormat)) { printf("WAVEFORMATEX\n"); } } 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<PWAVEFORMATEXTENSIBLE>(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 = 0x%08x to int-16\n", pwfx->wFormatTag); CoTaskMemFree(pwfx); pAudioClient->Release(); return E_UNEXPECTED; } } MMCKINFO ckRIFF = {0}; MMCKINFO ckData = {0}; if (hFile!=NULL) hr = WriteWaveHeader(hFile, pwfx, &ckRIFF, &ckData); if (pBuffer) { bool isFloat = false; switch (pwfx->wFormatTag) { case WAVE_FORMAT_IEEE_FLOAT: isFloat = true; 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)) { isFloat = true; } } break; default: break; } pBuffer->SetAudioInfo(pwfx->nSamplesPerSec,pwfx->nBlockAlign,pwfx->nChannels,pwfx->wBitsPerSample,isFloat); } 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; UINT32 nChannels = pwfx->nChannels; nFrames = 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 = 0x%08x\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 0x%08x\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 = 0x%08x\n", hr); AvRevertMmThreadCharacteristics(hTask); pAudioCaptureClient->Release(); CloseHandle(hWakeUp); pAudioClient->Release(); return hr; } SetEvent(hStartedEvent); // loopback capture loop HANDLE waitArray[2] = { hStopEvent, hWakeUp }; DWORD dwWaitResult; DWORD immdState; 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, nFrames); 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, nFrames); pAudioClient->Stop(); CancelWaitableTimer(hWakeUp); AvRevertMmThreadCharacteristics(hTask); pAudioCaptureClient->Release(); CloseHandle(hWakeUp); pAudioClient->Release(); return E_UNEXPECTED; } printf("'"); // got a "wake up" event - see if there's data UINT32 nNextPacketSize; hr = pAudioCaptureClient->GetNextPacketSize(&nNextPacketSize); if (FAILED(hr)) { if (hr == AUDCLNT_E_SERVICE_NOT_RUNNING) printf("AUDCLNT_E_SERVICE_NOT_RUNNING : \n"); else if (hr == AUDCLNT_E_DEVICE_INVALIDATED) printf("AUDCLNT_E_DEVICE_INVALIDATED : \n"); else printf("UNKNOWN ERROR!!! : \n"); printf("IAudioCaptureClient::GetNextPacketSize failed on pass %u after %u frames: hr = 0x%08x\n", nPasses, nFrames, 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 = 0x%08x\n", nPasses, nFrames, 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 (dwFlags & AUDCLNT_BUFFERFLAGS_SILENT) { printf("#"); } else if (dwFlags & AUDCLNT_BUFFERFLAGS_DATA_DISCONTINUITY) { printf("!"); } else if (0 != dwFlags) { printf("IAudioCaptureClient::GetBuffer set flags to 0x%08x on pass %u after %u frames\n", dwFlags, nPasses, nFrames); pAudioClient->Stop(); CancelWaitableTimer(hWakeUp); AvRevertMmThreadCharacteristics(hTask); pAudioCaptureClient->Release(); CloseHandle(hWakeUp); pAudioClient->Release(); return E_UNEXPECTED; } if (0 == nNumFramesToRead) { // no data yet continue; } //if (0 == nNumFramesToRead) { // printf("IAudioCaptureClient::GetBuffer said to read 0 frames on pass %u after %u frames\n", nPasses, nFrames); // 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") if (hFile!=NULL) { LONG lBytesWritten = mmioWrite(hFile, reinterpret_cast<PCHAR>(pData), lBytesToWrite); if (lBytesToWrite != lBytesWritten) { printf("mmioWrite wrote %u bytes on pass %u after %u frames: expected %u bytes\n", lBytesWritten, nPasses, nFrames, lBytesToWrite); pAudioClient->Stop(); CancelWaitableTimer(hWakeUp); AvRevertMmThreadCharacteristics(hTask); pAudioCaptureClient->Release(); CloseHandle(hWakeUp); pAudioClient->Release(); return E_UNEXPECTED; } } if (pBuffer) { //switch (nBlockAlign/nChannels) //{ //case 1: // ShowPCM((unsigned char*)pData,nNumFramesToRead,nChannels,1024,60,"SYS_Byte"); // break; //case 2: // ShowPCM((short*)pData,nNumFramesToRead,nChannels,1024,60,"SYS_Short"); // break; //case 4: // ShowPCM((int*)pData,nNumFramesToRead,nChannels,1024,60,"SYS_Int"); // //ShowPCM((float*)pData,nNumFramesToRead,nChannels,1024,60,"SYS_float"); // break; //} pBuffer->PushBuffer(pData,lBytesToWrite); } nFrames += nNumFramesToRead; hr = pAudioCaptureClient->ReleaseBuffer(nNumFramesToRead); if (FAILED(hr)) { printf("IAudioCaptureClient::ReleaseBuffer failed on pass %u after %u frames: hr = 0x%08x\n", nPasses, nFrames, hr); pAudioClient->Stop(); CancelWaitableTimer(hWakeUp); AvRevertMmThreadCharacteristics(hTask); pAudioCaptureClient->Release(); CloseHandle(hWakeUp); pAudioClient->Release(); return hr; } bFirstPacket = false; } // capture loop if (hFile!=NULL) 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; }
int main(int argc, char *argv[]) { CoInitialize(nullptr); listDevices(); IAudioClient *pAudioClient; IMMDevice *device; getDefaultDevice(&device); HRESULT hr = device->Activate(__uuidof(IAudioClient), CLSCTX_ALL, nullptr, (void**)&pAudioClient); if (FAILED(hr)) { printf("IMMDevice::Activate(IAudioClient) failed: hr = 0x%08x", hr); return hr; } REFERENCE_TIME hnsDefaultDevicePeriod; hr = pAudioClient->GetDevicePeriod(&hnsDefaultDevicePeriod, nullptr); if (FAILED(hr)) { printf("IAudioClient::GetDevicePeriod failed: hr = 0x%08x\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 = 0x%08x\n", hr); CoTaskMemFree(pwfx); pAudioClient->Release(); return hr; } DVAR(pwfx->wFormatTag); DVAR(pwfx->wBitsPerSample); DVAR(pwfx->nBlockAlign); DVAR(pwfx->nAvgBytesPerSec); 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<PWAVEFORMATEXTENSIBLE>(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 = 0x%08x to int-16\n", pwfx->wFormatTag); CoTaskMemFree(pwfx); pAudioClient->Release(); return E_UNEXPECTED; } DVAR(pwfx->wFormatTag); DVAR(pwfx->wBitsPerSample); DVAR(pwfx->nBlockAlign); DVAR(pwfx->nAvgBytesPerSec); hr = pAudioClient->Initialize(AUDCLNT_SHAREMODE_SHARED, AUDCLNT_STREAMFLAGS_LOOPBACK, 0, 0, pwfx, 0 ); if (FAILED(hr)) { printf("IAudioClient::Initialize failed: hr = 0x%08x\n", hr); pAudioClient->Release(); return hr; } IAudioCaptureClient *pAudioCaptureClient; hr = pAudioClient->GetService(__uuidof(IAudioCaptureClient), (void**)&pAudioCaptureClient); if (FAILED(hr)) { printf("IAudioClient::GetService(IAudioCaptureClient) failed: hr 0x%08x\n", hr); pAudioClient->Release(); return hr; } hr = pAudioClient->Start(); if (FAILED(hr)) { printf("IAudioClient::Start failed: hr = 0x%08x\n", hr); pAudioCaptureClient->Release(); pAudioClient->Release(); return hr; } for (int i = 0; i < 10; ++i) { UINT32 nNextPacketSize; hr = pAudioCaptureClient->GetNextPacketSize(&nNextPacketSize); if (FAILED(hr)) { printf("IAudioCaptureClient::GetNextPacketSize failed on pass %u after %u frames: hr = 0x%08x\n", 0, 0, hr); pAudioClient->Stop(); pAudioCaptureClient->Release(); pAudioClient->Release(); return hr; } // get the captured data BYTE *pData; UINT32 nNumFramesToRead; DWORD dwFlags; hr = pAudioCaptureClient->GetBuffer(&pData, &nNumFramesToRead, &dwFlags, nullptr, nullptr); if (FAILED(hr)) { printf("IAudioCaptureClient::GetBuffer failed on pass %u after %u frames: hr = 0x%08x\n", 0, 0, hr); pAudioClient->Stop(); pAudioCaptureClient->Release(); pAudioClient->Release(); return hr; } DVAR(nNumFramesToRead); // if (bFirstPacket && AUDCLNT_BUFFERFLAGS_DATA_DISCONTINUITY == dwFlags) { // printf("Probably spurious glitch reported on first packet\n"); if (0 != dwFlags && AUDCLNT_BUFFERFLAGS_DATA_DISCONTINUITY != dwFlags) { printf("IAudioCaptureClient::GetBuffer set flags to 0x%08x on pass %u after %u frames\n", dwFlags, 0, 0); // pAudioClient->Stop(); // pAudioCaptureClient->Release(); // pAudioClient->Release(); // return E_UNEXPECTED; } else DVAR((int)*pData); if (0 == nNumFramesToRead) { printf("IAudioCaptureClient::GetBuffer said to read 0 frames on pass %u after %u frames\n", 0, 0); pAudioClient->Stop(); pAudioCaptureClient->Release(); pAudioClient->Release(); return E_UNEXPECTED; } UINT32 nBlockAlign = pwfx->nBlockAlign; LONG lBytesToWrite = nNumFramesToRead * nBlockAlign; hr = pAudioCaptureClient->ReleaseBuffer(nNumFramesToRead); } pAudioClient->Stop(); pAudioCaptureClient->Release(); pAudioClient->Release(); CoUninitialize(); return 0; }
bool MMDeviceAudioSource::Initialize(bool bMic, CTSTR lpID) { const CLSID CLSID_MMDeviceEnumerator = __uuidof(MMDeviceEnumerator); const IID IID_IMMDeviceEnumerator = __uuidof(IMMDeviceEnumerator); const IID IID_IAudioClient = __uuidof(IAudioClient); const IID IID_IAudioCaptureClient = __uuidof(IAudioCaptureClient); HRESULT err; err = CoCreateInstance(CLSID_MMDeviceEnumerator, NULL, CLSCTX_ALL, IID_IMMDeviceEnumerator, (void**)&mmEnumerator); if(FAILED(err)) { AppWarning(TEXT("MMDeviceAudioSource::Initialize(%d): Could not create IMMDeviceEnumerator = %08lX"), (BOOL)bMic, err); return false; } if(bMic) err = mmEnumerator->GetDevice(lpID, &mmDevice); else err = mmEnumerator->GetDefaultAudioEndpoint(eRender, eConsole, &mmDevice); if(FAILED(err)) { AppWarning(TEXT("MMDeviceAudioSource::Initialize(%d): Could not create IMMDevice = %08lX"), (BOOL)bMic, err); return false; } err = mmDevice->Activate(IID_IAudioClient, CLSCTX_ALL, NULL, (void**)&mmClient); if(FAILED(err)) { AppWarning(TEXT("MMDeviceAudioSource::Initialize(%d): Could not create IAudioClient = %08lX"), (BOOL)bMic, err); return false; } WAVEFORMATEX *pwfx; err = mmClient->GetMixFormat(&pwfx); if(FAILED(err)) { AppWarning(TEXT("MMDeviceAudioSource::Initialize(%d): Could not get mix format from audio client = %08lX"), (BOOL)bMic, err); return false; } String strName = GetDeviceName(); if(bMic) { Log(TEXT("------------------------------------------")); Log(TEXT("Using auxilary audio input: %s"), strName.Array()); } //the internal audio engine should always use floats (or so I read), but I suppose just to be safe better check if(pwfx->wFormatTag == WAVE_FORMAT_EXTENSIBLE) { WAVEFORMATEXTENSIBLE *wfext = (WAVEFORMATEXTENSIBLE*)pwfx; inputChannelMask = wfext->dwChannelMask; if(wfext->SubFormat != KSDATAFORMAT_SUBTYPE_IEEE_FLOAT) { AppWarning(TEXT("MMDeviceAudioSource::Initialize(%d): Unsupported wave format"), (BOOL)bMic); return false; } } else if(pwfx->wFormatTag != WAVE_FORMAT_IEEE_FLOAT) { AppWarning(TEXT("MMDeviceAudioSource::Initialize(%d): Unsupported wave format"), (BOOL)bMic); return false; } inputChannels = pwfx->nChannels; inputBitsPerSample = 32; inputBlockSize = pwfx->nBlockAlign; inputSamplesPerSec = pwfx->nSamplesPerSec; DWORD flags = bMic ? 0 : AUDCLNT_STREAMFLAGS_LOOPBACK; err = mmClient->Initialize(AUDCLNT_SHAREMODE_SHARED, flags, ConvertMSTo100NanoSec(5000), 0, pwfx, NULL); if(FAILED(err)) { AppWarning(TEXT("MMDeviceAudioSource::Initialize(%d): Could not initialize audio client, result = %08lX"), (BOOL)bMic, err); return false; } err = mmClient->GetService(IID_IAudioCaptureClient, (void**)&mmCapture); if(FAILED(err)) { AppWarning(TEXT("MMDeviceAudioSource::Initialize(%d): Could not get audio capture client, result = %08lX"), (BOOL)bMic, err); return false; } err = mmClient->GetService(__uuidof(IAudioClock), (void**)&mmClock); if(FAILED(err)) { AppWarning(TEXT("MMDeviceAudioSource::Initialize(%d): Could not get audio capture clock, result = %08lX"), (BOOL)bMic, err); return false; } CoTaskMemFree(pwfx); //------------------------------------------------------------------------- if(inputSamplesPerSec != 44100) { int errVal; int converterType = AppConfig->GetInt(TEXT("Audio"), TEXT("UseHighQualityResampling"), FALSE) ? SRC_SINC_FASTEST : SRC_LINEAR; resampler = src_new(converterType, 2, &errVal);//SRC_SINC_FASTEST//SRC_ZERO_ORDER_HOLD if(!resampler) { CrashError(TEXT("MMDeviceAudioSource::Initialize(%d): Could not initiate resampler"), (BOOL)bMic); return false; } resampleRatio = 44100.0 / double(inputSamplesPerSec); bResample = true; //---------------------------------------------------- // hack to get rid of that weird first quirky resampled packet size // (always returns a non-441 sized packet on the first resample) SRC_DATA data; data.src_ratio = resampleRatio; List<float> blankBuffer; blankBuffer.SetSize(inputSamplesPerSec/100*2); data.data_in = blankBuffer.Array(); data.input_frames = inputSamplesPerSec/100; UINT frameAdjust = UINT((double(data.input_frames) * resampleRatio) + 1.0); UINT newFrameSize = frameAdjust*2; tempResampleBuffer.SetSize(newFrameSize); data.data_out = tempResampleBuffer.Array(); data.output_frames = frameAdjust; data.end_of_input = 0; int err = src_process(resampler, &data); nop(); } //------------------------------------------------------------------------- if(inputChannels > 2) { if(inputChannelMask == 0) { switch(inputChannels) { case 3: inputChannelMask = KSAUDIO_SPEAKER_2POINT1; break; case 4: inputChannelMask = KSAUDIO_SPEAKER_QUAD; break; case 5: inputChannelMask = KSAUDIO_SPEAKER_4POINT1; break; case 6: inputChannelMask = KSAUDIO_SPEAKER_5POINT1; break; case 8: inputChannelMask = KSAUDIO_SPEAKER_7POINT1; break; } } switch(inputChannelMask) { case KSAUDIO_SPEAKER_QUAD: Log(TEXT("Using quad speaker setup")); break; //ocd anyone? case KSAUDIO_SPEAKER_2POINT1: Log(TEXT("Using 2.1 speaker setup")); break; case KSAUDIO_SPEAKER_4POINT1: Log(TEXT("Using 4.1 speaker setup")); break; case KSAUDIO_SPEAKER_SURROUND: Log(TEXT("Using basic surround speaker setup")); break; case KSAUDIO_SPEAKER_5POINT1: Log(TEXT("Using 5.1 speaker setup")); break; case KSAUDIO_SPEAKER_5POINT1_SURROUND: Log(TEXT("Using 5.1 surround speaker setup")); break; case KSAUDIO_SPEAKER_7POINT1: Log(TEXT("Using 7.1 speaker setup (experimental)")); break; case KSAUDIO_SPEAKER_7POINT1_SURROUND: Log(TEXT("Using 7.1 surround speaker setup (experimental)")); break; default: Log(TEXT("Using unknown speaker setup: 0x%lX"), inputChannelMask); CrashError(TEXT("Speaker setup not yet implemented -- dear god of all the audio APIs, the one I -have- to use doesn't support resampling or downmixing. fabulous.")); break; } } return true; }
void LoopbackCaptureFor(IMMDevice* mmDevice, std::string filename, int secs) { // open new file MMIOINFO mi = { 0 }; // some flags cause mmioOpen write to this buffer // but not any that we're using std::wstring wsFilename(filename.begin(), filename.end()); // mmioOpen wants a wstring HMMIO file = mmioOpen(const_cast<LPWSTR>(wsFilename.c_str()), &mi, MMIO_WRITE | MMIO_CREATE); time_t startTime = time(nullptr); // activate an IAudioClient IAudioClient* audioClient; HRESULT hr = mmDevice->Activate(__uuidof(IAudioClient), CLSCTX_ALL, nullptr, (void**)&audioClient); if (FAILED(hr)) { fprintf(stderr, "IMMDevice::Activate(IAudioClient) failed: hr = 0x%08x", hr); return; } // get the default device periodicity REFERENCE_TIME hnsDefaultDevicePeriod; hr = audioClient->GetDevicePeriod(&hnsDefaultDevicePeriod, nullptr); if (FAILED(hr)) { fprintf(stderr, "IAudioClient::GetDevicePeriod failed: hr = 0x%08x\n", hr); audioClient->Release(); return; } // get the default device format WAVEFORMATEX* waveform; hr = audioClient->GetMixFormat(&waveform); if (FAILED(hr)) { fprintf(stderr, "IAudioClient::GetMixFormat failed: hr = 0x%08x\n", hr); CoTaskMemFree(waveform); audioClient->Release(); return; } // 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 (waveform->wFormatTag) { case WAVE_FORMAT_IEEE_FLOAT: waveform->wFormatTag = WAVE_FORMAT_PCM; waveform->wBitsPerSample = BITS_PER_SAMPLE; waveform->nBlockAlign = BLOCK_ALIGN; waveform->nAvgBytesPerSec = BYTE_RATE; break; case WAVE_FORMAT_EXTENSIBLE: { // naked scope for case-local variable PWAVEFORMATEXTENSIBLE pEx = reinterpret_cast<PWAVEFORMATEXTENSIBLE>(waveform); if (IsEqualGUID(KSDATAFORMAT_SUBTYPE_IEEE_FLOAT, pEx->SubFormat)) { pEx->SubFormat = KSDATAFORMAT_SUBTYPE_PCM; pEx->Samples.wValidBitsPerSample = BITS_PER_SAMPLE; waveform->wBitsPerSample = BITS_PER_SAMPLE; waveform->nBlockAlign = waveform->nChannels * BYTE_PER_SAMPLE; waveform->nAvgBytesPerSec = waveform->nBlockAlign * waveform->nSamplesPerSec; } break; } } MMCKINFO ckRIFF = { 0 }; MMCKINFO ckData = { 0 }; hr = WriteWaveHeader(file, waveform, &ckRIFF, &ckData); // create a periodic waitable timer HANDLE hWakeUp = CreateWaitableTimer(nullptr, FALSE, nullptr); UINT32 nBlockAlign = waveform->nBlockAlign; // 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 = audioClient->Initialize(AUDCLNT_SHAREMODE_SHARED, AUDCLNT_STREAMFLAGS_LOOPBACK, 0, 0, waveform, 0); if (FAILED(hr)) { fprintf(stderr, "IAudioClient::Initialize failed: hr = 0x%08x\n", hr); CloseHandle(hWakeUp); audioClient->Release(); return; } // free up waveform CoTaskMemFree(waveform); // activate an IAudioCaptureClient IAudioCaptureClient* audioCaptureClient; hr = audioClient->GetService(__uuidof(IAudioCaptureClient), (void**)&audioCaptureClient); // register with MMCSS DWORD nTaskIndex = 0; HANDLE hTask = AvSetMmThreadCharacteristics(L"Capture", &nTaskIndex); if (hTask == nullptr) { DWORD dwErr = GetLastError(); fprintf(stderr, "AvSetMmThreadCharacteristics failed: last error = %u\n", dwErr); audioCaptureClient->Release(); CloseHandle(hWakeUp); audioClient->Release(); return; } // 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 if (!SetWaitableTimer(hWakeUp, &liFirstFire, lTimeBetweenFires, nullptr, nullptr, FALSE)) { DWORD dwErr = GetLastError(); fprintf(stderr, "SetWaitableTimer failed: last error = %u\n", dwErr); AvRevertMmThreadCharacteristics(hTask); audioCaptureClient->Release(); CloseHandle(hWakeUp); audioClient->Release(); return; } // call IAudioClient::Start hr = audioClient->Start(); // loopback capture loop DWORD dwWaitResult; UINT32 frames = 0; for (UINT32 passes = 0; ; passes++) { // drain data while it is available UINT32 nextPacketSize; for (hr = audioCaptureClient->GetNextPacketSize(&nextPacketSize); SUCCEEDED(hr) && nextPacketSize > 0; hr = audioCaptureClient->GetNextPacketSize(&nextPacketSize)) { // get the captured data BYTE* data; UINT32 framesToRead; DWORD dwFlags; hr = audioCaptureClient->GetBuffer(&data, &framesToRead, &dwFlags, nullptr, nullptr); if (FAILED(hr)) { fprintf(stderr, "IAudioCaptureClient::GetBuffer failed on pass %u after %u frames: hr = 0x%08x\n", passes, frames, hr); audioClient->Stop(); CancelWaitableTimer(hWakeUp); AvRevertMmThreadCharacteristics(hTask); audioCaptureClient->Release(); CloseHandle(hWakeUp); audioClient->Release(); return; } // this type of error seems to happen often, ignore it if (dwFlags == AUDCLNT_BUFFERFLAGS_DATA_DISCONTINUITY) ; else if (dwFlags != 0) { fprintf(stderr, "IAudioCaptureClient::GetBuffer set flags to 0x%08x on pass %u after %u frames\n", dwFlags, passes, frames); audioClient->Stop(); CancelWaitableTimer(hWakeUp); AvRevertMmThreadCharacteristics(hTask); audioCaptureClient->Release(); CloseHandle(hWakeUp); audioClient->Release(); return; } if (framesToRead == 0) { fprintf(stderr, "IAudioCaptureClient::GetBuffer said to read 0 frames on pass %u after %u frames\n", passes, frames); audioClient->Stop(); CancelWaitableTimer(hWakeUp); AvRevertMmThreadCharacteristics(hTask); audioCaptureClient->Release(); CloseHandle(hWakeUp); audioClient->Release(); return; } LONG lBytesToWrite = framesToRead * nBlockAlign; #pragma prefast(suppress: __WARNING_INCORRECT_ANNOTATION, "IAudioCaptureClient::GetBuffer SAL annotation implies a 1-byte buffer") LONG lBytesWritten = mmioWrite(file, reinterpret_cast<PCHAR>(data), lBytesToWrite); if (lBytesToWrite != lBytesWritten) { fprintf(stderr, "mmioWrite wrote %u bytes on pass %u after %u frames: expected %u bytes\n", lBytesWritten, passes, frames, lBytesToWrite); audioClient->Stop(); CancelWaitableTimer(hWakeUp); AvRevertMmThreadCharacteristics(hTask); audioCaptureClient->Release(); CloseHandle(hWakeUp); audioClient->Release(); return; } frames += framesToRead; hr = audioCaptureClient->ReleaseBuffer(framesToRead); } dwWaitResult = WaitForSingleObject(hWakeUp, INFINITE); if (time(nullptr) - startTime > secs) break; } FinishWaveFile(file, &ckData, &ckRIFF); audioClient->Stop(); CancelWaitableTimer(hWakeUp); AvRevertMmThreadCharacteristics(hTask); audioCaptureClient->Release(); CloseHandle(hWakeUp); audioClient->Release(); // everything went well... fixup the fact chunk in the file MMRESULT result = mmioClose(file, 0); file = nullptr; if (result != MMSYSERR_NOERROR) { fprintf(stderr, "mmioClose failed: MMSYSERR = %u\n", result); return; } // reopen the file in read/write mode mi = { 0 }; file = mmioOpen(const_cast<LPWSTR>(wsFilename.c_str()), &mi, MMIO_READWRITE); if (file == nullptr) { fprintf(stderr, "mmioOpen(\"%ls\", ...) failed. wErrorRet == %u\n", filename, mi.wErrorRet); return; } // descend into the RIFF/WAVE chunk ckRIFF = { 0 }; ckRIFF.ckid = MAKEFOURCC('W', 'A', 'V', 'E'); // this is right for mmioDescend result = mmioDescend(file, &ckRIFF, nullptr, MMIO_FINDRIFF); if (result != MMSYSERR_NOERROR) { fprintf(stderr, "mmioDescend(\"WAVE\") failed: MMSYSERR = %u\n", result); return; } // descend into the fact chunk MMCKINFO ckFact = { 0 }; ckFact.ckid = MAKEFOURCC('f', 'a', 'c', 't'); result = mmioDescend(file, &ckFact, &ckRIFF, MMIO_FINDCHUNK); if (result != MMSYSERR_NOERROR) { fprintf(stderr, "mmioDescend(\"fact\") failed: MMSYSERR = %u\n", result); return; } // write the correct data to the fact chunk LONG lBytesWritten = mmioWrite(file, reinterpret_cast<PCHAR>(&frames), sizeof(frames)); if (lBytesWritten != sizeof(frames)) { fprintf(stderr, "Updating the fact chunk wrote %u bytes; expected %u\n", lBytesWritten, (UINT32)sizeof(frames)); return; } // ascend out of the fact chunk result = mmioAscend(file, &ckFact, 0); if (result != MMSYSERR_NOERROR) fprintf(stderr, "mmioAscend(\"fact\") failed: MMSYSERR = %u\n", result); }
void MMDeviceAudioSource::StopCapture() { if(mmClient) mmClient->Stop(); }
void CAESinkDirectSound::EnumerateDevicesEx(AEDeviceInfoList &deviceInfoList, bool force) { CAEDeviceInfo deviceInfo; IMMDeviceEnumerator* pEnumerator = NULL; IMMDeviceCollection* pEnumDevices = NULL; HRESULT hr; std::string strDD = GetDefaultDevice(); /* Windows Vista or later - supporting WASAPI device probing */ hr = CoCreateInstance(CLSID_MMDeviceEnumerator, NULL, CLSCTX_ALL, IID_IMMDeviceEnumerator, (void**)&pEnumerator); EXIT_ON_FAILURE(hr, __FUNCTION__": Could not allocate WASAPI device enumerator. CoCreateInstance error code: %li", hr) UINT uiCount = 0; hr = pEnumerator->EnumAudioEndpoints(eRender, DEVICE_STATE_ACTIVE, &pEnumDevices); EXIT_ON_FAILURE(hr, __FUNCTION__": Retrieval of audio endpoint enumeration failed.") hr = pEnumDevices->GetCount(&uiCount); EXIT_ON_FAILURE(hr, __FUNCTION__": Retrieval of audio endpoint count failed.") for (UINT i = 0; i < uiCount; i++) { IMMDevice *pDevice = NULL; IPropertyStore *pProperty = NULL; PROPVARIANT varName; PropVariantInit(&varName); deviceInfo.m_channels.Reset(); deviceInfo.m_dataFormats.clear(); deviceInfo.m_sampleRates.clear(); hr = pEnumDevices->Item(i, &pDevice); if (FAILED(hr)) { CLog::Log(LOGERROR, __FUNCTION__": Retrieval of DirectSound endpoint failed."); goto failed; } hr = pDevice->OpenPropertyStore(STGM_READ, &pProperty); if (FAILED(hr)) { CLog::Log(LOGERROR, __FUNCTION__": Retrieval of DirectSound endpoint properties failed."); SAFE_RELEASE(pDevice); goto failed; } hr = pProperty->GetValue(PKEY_Device_FriendlyName, &varName); if (FAILED(hr)) { CLog::Log(LOGERROR, __FUNCTION__": Retrieval of DirectSound endpoint device name failed."); SAFE_RELEASE(pDevice); SAFE_RELEASE(pProperty); goto failed; } std::string strFriendlyName = localWideToUtf(varName.pwszVal); PropVariantClear(&varName); hr = pProperty->GetValue(PKEY_AudioEndpoint_GUID, &varName); if (FAILED(hr)) { CLog::Log(LOGERROR, __FUNCTION__": Retrieval of DirectSound endpoint GUID failed."); SAFE_RELEASE(pDevice); SAFE_RELEASE(pProperty); goto failed; } std::string strDevName = localWideToUtf(varName.pwszVal); PropVariantClear(&varName); hr = pProperty->GetValue(PKEY_AudioEndpoint_FormFactor, &varName); if (FAILED(hr)) { CLog::Log(LOGERROR, __FUNCTION__": Retrieval of DirectSound endpoint form factor failed."); SAFE_RELEASE(pDevice); SAFE_RELEASE(pProperty); goto failed; } std::string strWinDevType = winEndpoints[(EndpointFormFactor)varName.uiVal].winEndpointType; AEDeviceType aeDeviceType = winEndpoints[(EndpointFormFactor)varName.uiVal].aeDeviceType; PropVariantClear(&varName); /* In shared mode Windows tells us what format the audio must be in. */ IAudioClient *pClient; hr = pDevice->Activate(IID_IAudioClient, CLSCTX_ALL, NULL, (void**)&pClient); if (FAILED(hr)) { CLog::Log(LOGERROR, __FUNCTION__": Activate device failed (%s)", WASAPIErrToStr(hr)); goto failed; } //hr = pClient->GetMixFormat(&pwfxex); hr = pProperty->GetValue(PKEY_AudioEngine_DeviceFormat, &varName); if (SUCCEEDED(hr) && varName.blob.cbSize > 0) { WAVEFORMATEX* smpwfxex = (WAVEFORMATEX*)varName.blob.pBlobData; deviceInfo.m_channels = layoutsByChCount[std::max(std::min(smpwfxex->nChannels, (WORD) DS_SPEAKER_COUNT), (WORD) 2)]; deviceInfo.m_dataFormats.push_back(AEDataFormat(AE_FMT_FLOAT)); deviceInfo.m_dataFormats.push_back(AEDataFormat(AE_FMT_AC3)); deviceInfo.m_sampleRates.push_back(std::min(smpwfxex->nSamplesPerSec, (DWORD) 192000)); } else { CLog::Log(LOGERROR, __FUNCTION__": Getting DeviceFormat failed (%s)", WASAPIErrToStr(hr)); } pClient->Release(); SAFE_RELEASE(pDevice); SAFE_RELEASE(pProperty); deviceInfo.m_deviceName = strDevName; deviceInfo.m_displayName = strWinDevType.append(strFriendlyName); deviceInfo.m_displayNameExtra = std::string("DirectSound: ").append(strFriendlyName); deviceInfo.m_deviceType = aeDeviceType; deviceInfoList.push_back(deviceInfo); // add the default device with m_deviceName = default if(strDD == strDevName) { deviceInfo.m_deviceName = std::string("default"); deviceInfo.m_displayName = std::string("default"); deviceInfo.m_displayNameExtra = std::string(""); deviceInfoList.push_back(deviceInfo); } } return; failed: if (FAILED(hr)) CLog::Log(LOGERROR, __FUNCTION__": Failed to enumerate WASAPI endpoint devices (%s).", WASAPIErrToStr(hr)); SAFE_RELEASE(pEnumDevices); SAFE_RELEASE(pEnumerator); }
void CAESinkDirectSound::EnumerateDevicesEx(AEDeviceInfoList &deviceInfoList) { CAEDeviceInfo deviceInfo; OSVERSIONINFO osvi; IMMDeviceEnumerator* pEnumerator = NULL; IMMDeviceCollection* pEnumDevices = NULL; WAVEFORMATEX* pwfxex = NULL; HRESULT hr; /* See if we are on Windows XP */ ZeroMemory(&osvi, sizeof(OSVERSIONINFO)); osvi.dwOSVersionInfoSize = sizeof(OSVERSIONINFO); GetVersionEx(&osvi); if (osvi.dwMajorVersion == 5) { /* We are on XP - WASAPI not supported - enumerate using DS devices */ LPGUID deviceGUID = NULL; RPC_CSTR cszGUID; std::string szGUID; std::list<DSDevice> DSDeviceList; DirectSoundEnumerate(DSEnumCallback, &DSDeviceList); for(std::list<DSDevice>::iterator itt = DSDeviceList.begin(); itt != DSDeviceList.end(); itt++) { if (UuidToString((*itt).lpGuid, &cszGUID) != RPC_S_OK) continue; /* could not convert GUID to string - skip device */ deviceInfo.m_channels.Reset(); deviceInfo.m_dataFormats.clear(); deviceInfo.m_sampleRates.clear(); szGUID = (LPSTR)cszGUID; deviceInfo.m_deviceName = "{" + szGUID + "}"; deviceInfo.m_displayName = (*itt).name; deviceInfo.m_displayNameExtra = std::string("DirectSound: ") + (*itt).name; deviceInfo.m_deviceType = AE_DEVTYPE_PCM; deviceInfo.m_channels = layoutsByChCount[2]; deviceInfo.m_dataFormats.push_back(AEDataFormat(AE_FMT_FLOAT)); deviceInfo.m_dataFormats.push_back(AEDataFormat(AE_FMT_AC3)); deviceInfo.m_sampleRates.push_back((DWORD) 96000); deviceInfoList.push_back(deviceInfo); } RpcStringFree(&cszGUID); return; } /* Windows Vista or later - supporting WASAPI device probing */ hr = CoCreateInstance(CLSID_MMDeviceEnumerator, NULL, CLSCTX_ALL, IID_IMMDeviceEnumerator, (void**)&pEnumerator); EXIT_ON_FAILURE(hr, __FUNCTION__": Could not allocate WASAPI device enumerator. CoCreateInstance error code: %li", hr) UINT uiCount = 0; hr = pEnumerator->EnumAudioEndpoints(eRender, DEVICE_STATE_ACTIVE, &pEnumDevices); EXIT_ON_FAILURE(hr, __FUNCTION__": Retrieval of audio endpoint enumeration failed.") hr = pEnumDevices->GetCount(&uiCount); EXIT_ON_FAILURE(hr, __FUNCTION__": Retrieval of audio endpoint count failed.") for (UINT i = 0; i < uiCount; i++) { IMMDevice *pDevice = NULL; IPropertyStore *pProperty = NULL; PROPVARIANT varName; PropVariantInit(&varName); deviceInfo.m_channels.Reset(); deviceInfo.m_dataFormats.clear(); deviceInfo.m_sampleRates.clear(); hr = pEnumDevices->Item(i, &pDevice); if (FAILED(hr)) { CLog::Log(LOGERROR, __FUNCTION__": Retrieval of DirectSound endpoint failed."); goto failed; } hr = pDevice->OpenPropertyStore(STGM_READ, &pProperty); if (FAILED(hr)) { CLog::Log(LOGERROR, __FUNCTION__": Retrieval of DirectSound endpoint properties failed."); SAFE_RELEASE(pDevice); goto failed; } hr = pProperty->GetValue(PKEY_Device_FriendlyName, &varName); if (FAILED(hr)) { CLog::Log(LOGERROR, __FUNCTION__": Retrieval of DirectSound endpoint device name failed."); SAFE_RELEASE(pDevice); SAFE_RELEASE(pProperty); goto failed; } std::wstring strRawFriendlyName(varName.pwszVal); std::string strFriendlyName = std::string(strRawFriendlyName.begin(), strRawFriendlyName.end()); PropVariantClear(&varName); hr = pProperty->GetValue(PKEY_AudioEndpoint_GUID, &varName); if (FAILED(hr)) { CLog::Log(LOGERROR, __FUNCTION__": Retrieval of DirectSound endpoint GUID failed."); SAFE_RELEASE(pDevice); SAFE_RELEASE(pProperty); goto failed; } std::wstring strRawDevName(varName.pwszVal); std::string strDevName = std::string(strRawDevName.begin(), strRawDevName.end()); PropVariantClear(&varName); hr = pProperty->GetValue(PKEY_AudioEndpoint_FormFactor, &varName); if (FAILED(hr)) { CLog::Log(LOGERROR, __FUNCTION__": Retrieval of DirectSound endpoint form factor failed."); SAFE_RELEASE(pDevice); SAFE_RELEASE(pProperty); goto failed; } std::string strWinDevType = winEndpoints[(EndpointFormFactor)varName.uiVal].winEndpointType; AEDeviceType aeDeviceType = winEndpoints[(EndpointFormFactor)varName.uiVal].aeDeviceType; PropVariantClear(&varName); /* In shared mode Windows tells us what format the audio must be in. */ IAudioClient *pClient; hr = pDevice->Activate(IID_IAudioClient, CLSCTX_ALL, NULL, (void**)&pClient); if (FAILED(hr)) { CLog::Log(LOGERROR, __FUNCTION__": Activate device failed (%s)", WASAPIErrToStr(hr)); } //hr = pClient->GetMixFormat(&pwfxex); hr = pProperty->GetValue(PKEY_AudioEngine_DeviceFormat, &varName); if (SUCCEEDED(hr)) { WAVEFORMATEX* smpwfxex = (WAVEFORMATEX*)varName.blob.pBlobData; deviceInfo.m_channels = layoutsByChCount[std::min(smpwfxex->nChannels, (WORD) 2)]; deviceInfo.m_dataFormats.push_back(AEDataFormat(AE_FMT_FLOAT)); deviceInfo.m_dataFormats.push_back(AEDataFormat(AE_FMT_AC3)); deviceInfo.m_sampleRates.push_back(std::min(smpwfxex->nSamplesPerSec, (DWORD) 96000)); } else { CLog::Log(LOGERROR, __FUNCTION__": GetMixFormat failed (%s)", WASAPIErrToStr(hr)); } pClient->Release(); SAFE_RELEASE(pDevice); SAFE_RELEASE(pProperty); deviceInfo.m_deviceName = strDevName; deviceInfo.m_displayName = strWinDevType.append(strFriendlyName); deviceInfo.m_displayNameExtra = std::string("DirectSound: ").append(strFriendlyName); deviceInfo.m_deviceType = aeDeviceType; /* Now logged by AESinkFactory on startup */ //CLog::Log(LOGDEBUG,"Audio Device %d: %s", i, ((std::string)deviceInfo).c_str()); deviceInfoList.push_back(deviceInfo); } return; failed: if (FAILED(hr)) CLog::Log(LOGERROR, __FUNCTION__": Failed to enumerate WASAPI endpoint devices (%s).", WASAPIErrToStr(hr)); SAFE_RELEASE(pEnumDevices); SAFE_RELEASE(pEnumerator); }
bool MMDeviceAudioSource::Initialize(bool bMic, CTSTR lpID) { const CLSID CLSID_MMDeviceEnumerator = __uuidof(MMDeviceEnumerator); const IID IID_IMMDeviceEnumerator = __uuidof(IMMDeviceEnumerator); const IID IID_IAudioClient = __uuidof(IAudioClient); const IID IID_IAudioCaptureClient = __uuidof(IAudioCaptureClient); HRESULT err; err = CoCreateInstance(CLSID_MMDeviceEnumerator, NULL, CLSCTX_ALL, IID_IMMDeviceEnumerator, (void**)&mmEnumerator); if(FAILED(err)) { AppWarning(TEXT("MMDeviceAudioSource::Initialize(%d): Could not create IMMDeviceEnumerator = %08lX"), (BOOL)bMic, err); return false; } bIsMic = bMic; if (bIsMic) { BOOL bMicSyncFixHack = GlobalConfig->GetInt(TEXT("Audio"), TEXT("UseMicSyncFixHack")); angerThreshold = bMicSyncFixHack ? 40 : 1000; } if (scmpi(lpID, TEXT("Default")) == 0) err = mmEnumerator->GetDefaultAudioEndpoint(bMic ? eCapture : eRender, bMic ? eCommunications : eConsole, &mmDevice); else err = mmEnumerator->GetDevice(lpID, &mmDevice); if(FAILED(err)) { AppWarning(TEXT("MMDeviceAudioSource::Initialize(%d): Could not create IMMDevice = %08lX"), (BOOL)bMic, err); return false; } err = mmDevice->Activate(IID_IAudioClient, CLSCTX_ALL, NULL, (void**)&mmClient); if(FAILED(err)) { AppWarning(TEXT("MMDeviceAudioSource::Initialize(%d): Could not create IAudioClient = %08lX"), (BOOL)bMic, err); return false; } //----------------------------------------------------------------- // get name IPropertyStore *store; if(SUCCEEDED(mmDevice->OpenPropertyStore(STGM_READ, &store))) { PROPVARIANT varName; PropVariantInit(&varName); if(SUCCEEDED(store->GetValue(PKEY_Device_FriendlyName, &varName))) { CWSTR wstrName = varName.pwszVal; strDeviceName = wstrName; } store->Release(); } if(bMic) { Log(TEXT("------------------------------------------")); Log(TEXT("Using auxilary audio input: %s"), GetDeviceName()); bUseQPC = GlobalConfig->GetInt(TEXT("Audio"), TEXT("UseMicQPC")) != 0; if (bUseQPC) Log(TEXT("Using Mic QPC timestamps")); } else { Log(TEXT("------------------------------------------")); Log(TEXT("Using desktop audio input: %s"), GetDeviceName()); bUseVideoTime = AppConfig->GetInt(TEXT("Audio"), TEXT("SyncToVideoTime")) != 0; SetTimeOffset(GlobalConfig->GetInt(TEXT("Audio"), TEXT("GlobalAudioTimeAdjust"))); } //----------------------------------------------------------------- // get format WAVEFORMATEX *pwfx; err = mmClient->GetMixFormat(&pwfx); if(FAILED(err)) { AppWarning(TEXT("MMDeviceAudioSource::Initialize(%d): Could not get mix format from audio client = %08lX"), (BOOL)bMic, err); return false; } bool bFloat; UINT inputChannels; UINT inputSamplesPerSec; UINT inputBitsPerSample; UINT inputBlockSize; DWORD inputChannelMask = 0; WAVEFORMATEXTENSIBLE *wfext = NULL; //the internal audio engine should always use floats (or so I read), but I suppose just to be safe better check if(pwfx->wFormatTag == WAVE_FORMAT_EXTENSIBLE) { wfext = (WAVEFORMATEXTENSIBLE*)pwfx; inputChannelMask = wfext->dwChannelMask; if(wfext->SubFormat != KSDATAFORMAT_SUBTYPE_IEEE_FLOAT) { AppWarning(TEXT("MMDeviceAudioSource::Initialize(%d): Unsupported wave format"), (BOOL)bMic); return false; } } else if(pwfx->wFormatTag != WAVE_FORMAT_IEEE_FLOAT) { AppWarning(TEXT("MMDeviceAudioSource::Initialize(%d): Unsupported wave format"), (BOOL)bMic); return false; } bFloat = true; inputChannels = pwfx->nChannels; inputBitsPerSample = 32; inputBlockSize = pwfx->nBlockAlign; inputSamplesPerSec = pwfx->nSamplesPerSec; sampleWindowSize = (inputSamplesPerSec/100); DWORD flags = bMic ? 0 : AUDCLNT_STREAMFLAGS_LOOPBACK; err = mmClient->Initialize(AUDCLNT_SHAREMODE_SHARED, flags, ConvertMSTo100NanoSec(5000), 0, pwfx, NULL); //err = AUDCLNT_E_UNSUPPORTED_FORMAT; if (err == AUDCLNT_E_UNSUPPORTED_FORMAT) { //workaround for razer kraken headset (bad drivers) pwfx->nBlockAlign = 2*pwfx->nChannels; pwfx->nAvgBytesPerSec = inputSamplesPerSec*pwfx->nBlockAlign; pwfx->wBitsPerSample = 16; wfext->SubFormat = KSDATAFORMAT_SUBTYPE_PCM; wfext->Samples.wValidBitsPerSample = 16; bConvert = true; err = mmClient->Initialize(AUDCLNT_SHAREMODE_SHARED, flags, ConvertMSTo100NanoSec(5000), 0, pwfx, NULL); } if(FAILED(err)) { AppWarning(TEXT("MMDeviceAudioSource::Initialize(%d): Could not initialize audio client, result = %08lX"), (BOOL)bMic, err); return false; } //----------------------------------------------------------------- // acquire services err = mmClient->GetService(IID_IAudioCaptureClient, (void**)&mmCapture); if(FAILED(err)) { AppWarning(TEXT("MMDeviceAudioSource::Initialize(%d): Could not get audio capture client, result = %08lX"), (BOOL)bMic, err); return false; } err = mmClient->GetService(__uuidof(IAudioClock), (void**)&mmClock); if(FAILED(err)) { AppWarning(TEXT("MMDeviceAudioSource::Initialize(%d): Could not get audio capture clock, result = %08lX"), (BOOL)bMic, err); return false; } CoTaskMemFree(pwfx); //----------------------------------------------------------------- InitAudioData(bFloat, inputChannels, inputSamplesPerSec, inputBitsPerSample, inputBlockSize, inputChannelMask); return true; }
void CAESinkDirectSound::EnumerateDevicesEx(AEDeviceInfoList &deviceInfoList, bool force) { CAEDeviceInfo deviceInfo; IMMDeviceEnumerator* pEnumerator = NULL; IMMDeviceCollection* pEnumDevices = NULL; HRESULT hr; /* See if we are on Windows XP */ if (!g_sysinfo.IsWindowsVersionAtLeast(CSysInfo::WindowsVersionVista)) { /* We are on XP - WASAPI not supported - enumerate using DS devices */ LPGUID deviceGUID = NULL; RPC_CSTR cszGUID; std::string szGUID; std::list<DSDevice> DSDeviceList; DirectSoundEnumerate(DSEnumCallback, &DSDeviceList); for(std::list<DSDevice>::iterator itt = DSDeviceList.begin(); itt != DSDeviceList.end(); ++itt) { if (UuidToString((*itt).lpGuid, &cszGUID) != RPC_S_OK) continue; /* could not convert GUID to string - skip device */ deviceInfo.m_channels.Reset(); deviceInfo.m_dataFormats.clear(); deviceInfo.m_sampleRates.clear(); szGUID = (LPSTR)cszGUID; deviceInfo.m_deviceName = "{" + szGUID + "}"; deviceInfo.m_displayName = (*itt).name; deviceInfo.m_displayNameExtra = std::string("DirectSound: ") + (*itt).name; deviceInfo.m_deviceType = AE_DEVTYPE_PCM; deviceInfo.m_channels = layoutsByChCount[2]; deviceInfo.m_dataFormats.push_back(AEDataFormat(AE_FMT_FLOAT)); deviceInfo.m_dataFormats.push_back(AEDataFormat(AE_FMT_AC3)); deviceInfo.m_sampleRates.push_back((DWORD) 96000); deviceInfoList.push_back(deviceInfo); } RpcStringFree(&cszGUID); return; } /* Windows Vista or later - supporting WASAPI device probing */ hr = CoCreateInstance(CLSID_MMDeviceEnumerator, NULL, CLSCTX_ALL, IID_IMMDeviceEnumerator, (void**)&pEnumerator); EXIT_ON_FAILURE(hr, __FUNCTION__": Could not allocate WASAPI device enumerator. CoCreateInstance error code: %li", hr) UINT uiCount = 0; hr = pEnumerator->EnumAudioEndpoints(eRender, DEVICE_STATE_ACTIVE, &pEnumDevices); EXIT_ON_FAILURE(hr, __FUNCTION__": Retrieval of audio endpoint enumeration failed.") hr = pEnumDevices->GetCount(&uiCount); EXIT_ON_FAILURE(hr, __FUNCTION__": Retrieval of audio endpoint count failed.") for (UINT i = 0; i < uiCount; i++) { IMMDevice *pDevice = NULL; IPropertyStore *pProperty = NULL; PROPVARIANT varName; PropVariantInit(&varName); deviceInfo.m_channels.Reset(); deviceInfo.m_dataFormats.clear(); deviceInfo.m_sampleRates.clear(); hr = pEnumDevices->Item(i, &pDevice); if (FAILED(hr)) { CLog::Log(LOGERROR, __FUNCTION__": Retrieval of DirectSound endpoint failed."); goto failed; } hr = pDevice->OpenPropertyStore(STGM_READ, &pProperty); if (FAILED(hr)) { CLog::Log(LOGERROR, __FUNCTION__": Retrieval of DirectSound endpoint properties failed."); SAFE_RELEASE(pDevice); goto failed; } hr = pProperty->GetValue(PKEY_Device_FriendlyName, &varName); if (FAILED(hr)) { CLog::Log(LOGERROR, __FUNCTION__": Retrieval of DirectSound endpoint device name failed."); SAFE_RELEASE(pDevice); SAFE_RELEASE(pProperty); goto failed; } std::string strFriendlyName = localWideToUtf(varName.pwszVal); PropVariantClear(&varName); hr = pProperty->GetValue(PKEY_AudioEndpoint_GUID, &varName); if (FAILED(hr)) { CLog::Log(LOGERROR, __FUNCTION__": Retrieval of DirectSound endpoint GUID failed."); SAFE_RELEASE(pDevice); SAFE_RELEASE(pProperty); goto failed; } std::string strDevName = localWideToUtf(varName.pwszVal); PropVariantClear(&varName); hr = pProperty->GetValue(PKEY_AudioEndpoint_FormFactor, &varName); if (FAILED(hr)) { CLog::Log(LOGERROR, __FUNCTION__": Retrieval of DirectSound endpoint form factor failed."); SAFE_RELEASE(pDevice); SAFE_RELEASE(pProperty); goto failed; } std::string strWinDevType = winEndpoints[(EndpointFormFactor)varName.uiVal].winEndpointType; AEDeviceType aeDeviceType = winEndpoints[(EndpointFormFactor)varName.uiVal].aeDeviceType; PropVariantClear(&varName); /* In shared mode Windows tells us what format the audio must be in. */ IAudioClient *pClient; hr = pDevice->Activate(IID_IAudioClient, CLSCTX_ALL, NULL, (void**)&pClient); if (FAILED(hr)) { CLog::Log(LOGERROR, __FUNCTION__": Activate device failed (%s)", WASAPIErrToStr(hr)); goto failed; } //hr = pClient->GetMixFormat(&pwfxex); hr = pProperty->GetValue(PKEY_AudioEngine_DeviceFormat, &varName); if (SUCCEEDED(hr) && varName.blob.cbSize > 0) { WAVEFORMATEX* smpwfxex = (WAVEFORMATEX*)varName.blob.pBlobData; deviceInfo.m_channels = layoutsByChCount[std::max(std::min(smpwfxex->nChannels, (WORD) DS_SPEAKER_COUNT), (WORD) 2)]; deviceInfo.m_dataFormats.push_back(AEDataFormat(AE_FMT_FLOAT)); deviceInfo.m_dataFormats.push_back(AEDataFormat(AE_FMT_AC3)); deviceInfo.m_sampleRates.push_back(std::min(smpwfxex->nSamplesPerSec, (DWORD) 192000)); } else { CLog::Log(LOGERROR, __FUNCTION__": Getting DeviceFormat failed (%s)", WASAPIErrToStr(hr)); } pClient->Release(); SAFE_RELEASE(pDevice); SAFE_RELEASE(pProperty); deviceInfo.m_deviceName = strDevName; deviceInfo.m_displayName = strWinDevType.append(strFriendlyName); deviceInfo.m_displayNameExtra = std::string("DirectSound: ").append(strFriendlyName); deviceInfo.m_deviceType = aeDeviceType; deviceInfoList.push_back(deviceInfo); } // since AE takes the first device in deviceInfoList as default audio device we need // to sort it in order to use the real default device if(deviceInfoList.size() > 1) { std::string strDD = GetDefaultDevice(); for (AEDeviceInfoList::iterator itt = deviceInfoList.begin(); itt != deviceInfoList.end(); ++itt) { CAEDeviceInfo devInfo = *itt; if(devInfo.m_deviceName == strDD) { deviceInfoList.erase(itt); deviceInfoList.insert(deviceInfoList.begin(), devInfo); break; } } } return; failed: if (FAILED(hr)) CLog::Log(LOGERROR, __FUNCTION__": Failed to enumerate WASAPI endpoint devices (%s).", WASAPIErrToStr(hr)); SAFE_RELEASE(pEnumDevices); SAFE_RELEASE(pEnumerator); }
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); }
void CAESinkWASAPI::EnumerateDevicesEx(AEDeviceInfoList &deviceInfoList, bool force) { IMMDeviceEnumerator* pEnumerator = NULL; IMMDeviceCollection* pEnumDevices = NULL; IMMDevice* pDefaultDevice = NULL; CAEDeviceInfo deviceInfo; CAEChannelInfo deviceChannels; LPWSTR pwszID = NULL; std::wstring wstrDDID; WAVEFORMATEXTENSIBLE wfxex = {0}; HRESULT hr; hr = CoCreateInstance(CLSID_MMDeviceEnumerator, NULL, CLSCTX_ALL, IID_IMMDeviceEnumerator, (void**)&pEnumerator); EXIT_ON_FAILURE(hr, __FUNCTION__": Could not allocate WASAPI device enumerator. CoCreateInstance error code: %li", hr) UINT uiCount = 0; // get the default audio endpoint if(pEnumerator->GetDefaultAudioEndpoint(eRender, eConsole, &pDefaultDevice) == S_OK) { if(pDefaultDevice->GetId(&pwszID) == S_OK) { wstrDDID = pwszID; CoTaskMemFree(pwszID); } SAFE_RELEASE(pDefaultDevice); } // enumerate over all audio endpoints hr = pEnumerator->EnumAudioEndpoints(eRender, DEVICE_STATE_ACTIVE, &pEnumDevices); EXIT_ON_FAILURE(hr, __FUNCTION__": Retrieval of audio endpoint enumeration failed.") hr = pEnumDevices->GetCount(&uiCount); EXIT_ON_FAILURE(hr, __FUNCTION__": Retrieval of audio endpoint count failed.") for (UINT i = 0; i < uiCount; i++) { IMMDevice *pDevice = NULL; IPropertyStore *pProperty = NULL; PROPVARIANT varName; PropVariantInit(&varName); deviceInfo.m_channels.Reset(); deviceInfo.m_dataFormats.clear(); deviceInfo.m_sampleRates.clear(); hr = pEnumDevices->Item(i, &pDevice); if (FAILED(hr)) { CLog::Log(LOGERROR, __FUNCTION__": Retrieval of WASAPI endpoint failed."); goto failed; } hr = pDevice->OpenPropertyStore(STGM_READ, &pProperty); if (FAILED(hr)) { CLog::Log(LOGERROR, __FUNCTION__": Retrieval of WASAPI endpoint properties failed."); SAFE_RELEASE(pDevice); goto failed; } hr = pProperty->GetValue(PKEY_Device_FriendlyName, &varName); if (FAILED(hr)) { CLog::Log(LOGERROR, __FUNCTION__": Retrieval of WASAPI endpoint device name failed."); SAFE_RELEASE(pDevice); SAFE_RELEASE(pProperty); goto failed; } std::string strFriendlyName = localWideToUtf(varName.pwszVal); PropVariantClear(&varName); hr = pProperty->GetValue(PKEY_AudioEndpoint_GUID, &varName); if(FAILED(hr)) { CLog::Log(LOGERROR, __FUNCTION__": Retrieval of WASAPI endpoint GUID failed."); SAFE_RELEASE(pDevice); SAFE_RELEASE(pProperty); goto failed; } std::string strDevName = localWideToUtf(varName.pwszVal); PropVariantClear(&varName); hr = pProperty->GetValue(PKEY_AudioEndpoint_FormFactor, &varName); if (FAILED(hr)) { CLog::Log(LOGERROR, __FUNCTION__": Retrieval of WASAPI endpoint form factor failed."); SAFE_RELEASE(pDevice); SAFE_RELEASE(pProperty); goto failed; } std::string strWinDevType = winEndpoints[(EndpointFormFactor)varName.uiVal].winEndpointType; AEDeviceType aeDeviceType = winEndpoints[(EndpointFormFactor)varName.uiVal].aeDeviceType; PropVariantClear(&varName); hr = pProperty->GetValue(PKEY_AudioEndpoint_PhysicalSpeakers, &varName); if (FAILED(hr)) { CLog::Log(LOGERROR, __FUNCTION__": Retrieval of WASAPI endpoint speaker layout failed."); SAFE_RELEASE(pDevice); SAFE_RELEASE(pProperty); goto failed; } unsigned int uiChannelMask = std::max(varName.uintVal, (unsigned int) KSAUDIO_SPEAKER_STEREO); deviceChannels.Reset(); for (unsigned int c = 0; c < WASAPI_SPEAKER_COUNT; c++) { if (uiChannelMask & WASAPIChannelOrder[c]) deviceChannels += AEChannelNames[c]; } PropVariantClear(&varName); IAudioClient *pClient; hr = pDevice->Activate(IID_IAudioClient, CLSCTX_ALL, NULL, (void**)&pClient); if (SUCCEEDED(hr)) { /* Test format DTS-HD */ wfxex.Format.cbSize = sizeof(WAVEFORMATEXTENSIBLE)-sizeof(WAVEFORMATEX); wfxex.Format.nSamplesPerSec = 192000; wfxex.dwChannelMask = KSAUDIO_SPEAKER_7POINT1_SURROUND; wfxex.Format.wFormatTag = WAVE_FORMAT_EXTENSIBLE; wfxex.SubFormat = KSDATAFORMAT_SUBTYPE_IEC61937_DTS_HD; wfxex.Format.wBitsPerSample = 16; wfxex.Samples.wValidBitsPerSample = 16; wfxex.Format.nChannels = 8; wfxex.Format.nBlockAlign = wfxex.Format.nChannels * (wfxex.Format.wBitsPerSample >> 3); wfxex.Format.nAvgBytesPerSec = wfxex.Format.nSamplesPerSec * wfxex.Format.nBlockAlign; hr = pClient->IsFormatSupported(AUDCLNT_SHAREMODE_EXCLUSIVE, &wfxex.Format, NULL); if (SUCCEEDED(hr)) deviceInfo.m_dataFormats.push_back(AEDataFormat(AE_FMT_DTSHD)); /* Test format Dolby TrueHD */ wfxex.SubFormat = KSDATAFORMAT_SUBTYPE_IEC61937_DOLBY_MLP; hr = pClient->IsFormatSupported(AUDCLNT_SHAREMODE_EXCLUSIVE, &wfxex.Format, NULL); if (SUCCEEDED(hr)) deviceInfo.m_dataFormats.push_back(AEDataFormat(AE_FMT_TRUEHD)); /* Test format Dolby EAC3 */ wfxex.SubFormat = KSDATAFORMAT_SUBTYPE_IEC61937_DOLBY_DIGITAL_PLUS; wfxex.Format.nChannels = 2; wfxex.Format.nBlockAlign = wfxex.Format.nChannels * (wfxex.Format.wBitsPerSample >> 3); wfxex.Format.nAvgBytesPerSec = wfxex.Format.nSamplesPerSec * wfxex.Format.nBlockAlign; hr = pClient->IsFormatSupported(AUDCLNT_SHAREMODE_EXCLUSIVE, &wfxex.Format, NULL); if (SUCCEEDED(hr)) deviceInfo.m_dataFormats.push_back(AEDataFormat(AE_FMT_EAC3)); /* Test format DTS */ wfxex.Format.nSamplesPerSec = 48000; wfxex.dwChannelMask = KSAUDIO_SPEAKER_5POINT1; wfxex.SubFormat = KSDATAFORMAT_SUBTYPE_IEC61937_DTS; wfxex.Format.nBlockAlign = wfxex.Format.nChannels * (wfxex.Format.wBitsPerSample >> 3); wfxex.Format.nAvgBytesPerSec = wfxex.Format.nSamplesPerSec * wfxex.Format.nBlockAlign; hr = pClient->IsFormatSupported(AUDCLNT_SHAREMODE_EXCLUSIVE, &wfxex.Format, NULL); if (SUCCEEDED(hr)) deviceInfo.m_dataFormats.push_back(AEDataFormat(AE_FMT_DTS)); /* Test format Dolby AC3 */ wfxex.SubFormat = KSDATAFORMAT_SUBTYPE_IEC61937_DOLBY_DIGITAL; hr = pClient->IsFormatSupported(AUDCLNT_SHAREMODE_EXCLUSIVE, &wfxex.Format, NULL); if (SUCCEEDED(hr)) deviceInfo.m_dataFormats.push_back(AEDataFormat(AE_FMT_AC3)); /* Test format AAC */ wfxex.SubFormat = KSDATAFORMAT_SUBTYPE_IEC61937_AAC; hr = pClient->IsFormatSupported(AUDCLNT_SHAREMODE_EXCLUSIVE, &wfxex.Format, NULL); if (SUCCEEDED(hr)) deviceInfo.m_dataFormats.push_back(AEDataFormat(AE_FMT_AAC)); /* Test format for PCM format iteration */ wfxex.Format.cbSize = sizeof(WAVEFORMATEXTENSIBLE)-sizeof(WAVEFORMATEX); wfxex.dwChannelMask = KSAUDIO_SPEAKER_STEREO; wfxex.Format.wFormatTag = WAVE_FORMAT_EXTENSIBLE; wfxex.SubFormat = KSDATAFORMAT_SUBTYPE_IEEE_FLOAT; for (int p = AE_FMT_FLOAT; p > AE_FMT_INVALID; p--) { if (p < AE_FMT_FLOAT) wfxex.SubFormat = KSDATAFORMAT_SUBTYPE_PCM; wfxex.Format.wBitsPerSample = CAEUtil::DataFormatToBits((AEDataFormat) p); wfxex.Format.nBlockAlign = wfxex.Format.nChannels * (wfxex.Format.wBitsPerSample >> 3); wfxex.Format.nAvgBytesPerSec = wfxex.Format.nSamplesPerSec * wfxex.Format.nBlockAlign; if (p <= AE_FMT_S24NE4 && p >= AE_FMT_S24BE4) { wfxex.Samples.wValidBitsPerSample = 24; } else { wfxex.Samples.wValidBitsPerSample = wfxex.Format.wBitsPerSample; } hr = pClient->IsFormatSupported(AUDCLNT_SHAREMODE_EXCLUSIVE, &wfxex.Format, NULL); if (SUCCEEDED(hr)) deviceInfo.m_dataFormats.push_back((AEDataFormat) p); } /* Test format for sample rate iteration */ wfxex.Format.cbSize = sizeof(WAVEFORMATEXTENSIBLE)-sizeof(WAVEFORMATEX); wfxex.dwChannelMask = KSAUDIO_SPEAKER_STEREO; wfxex.Format.wFormatTag = WAVE_FORMAT_EXTENSIBLE; wfxex.SubFormat = KSDATAFORMAT_SUBTYPE_PCM; wfxex.Format.wBitsPerSample = 16; wfxex.Samples.wValidBitsPerSample = 16; wfxex.Format.nChannels = 2; wfxex.Format.nBlockAlign = wfxex.Format.nChannels * (wfxex.Format.wBitsPerSample >> 3); wfxex.Format.nAvgBytesPerSec = wfxex.Format.nSamplesPerSec * wfxex.Format.nBlockAlign; for (int j = 0; j < WASAPISampleRateCount; j++) { wfxex.Format.nSamplesPerSec = WASAPISampleRates[j]; wfxex.Format.nAvgBytesPerSec = wfxex.Format.nSamplesPerSec * wfxex.Format.nBlockAlign; hr = pClient->IsFormatSupported(AUDCLNT_SHAREMODE_EXCLUSIVE, &wfxex.Format, NULL); if (SUCCEEDED(hr)) deviceInfo.m_sampleRates.push_back(WASAPISampleRates[j]); } /* Test format for channels iteration */ wfxex.Format.cbSize = sizeof(WAVEFORMATEXTENSIBLE)-sizeof(WAVEFORMATEX); wfxex.dwChannelMask = KSAUDIO_SPEAKER_STEREO; wfxex.Format.wFormatTag = WAVE_FORMAT_EXTENSIBLE; wfxex.SubFormat = KSDATAFORMAT_SUBTYPE_PCM; wfxex.Format.nSamplesPerSec = 48000; wfxex.Format.wBitsPerSample = 16; wfxex.Samples.wValidBitsPerSample = 16; wfxex.Format.nChannels = 2; wfxex.Format.nBlockAlign = wfxex.Format.nChannels * (wfxex.Format.wBitsPerSample >> 3); wfxex.Format.nAvgBytesPerSec = wfxex.Format.nSamplesPerSec * wfxex.Format.nBlockAlign; bool hasLpcm = false; // Try with KSAUDIO_SPEAKER_DIRECTOUT for (unsigned int k = WASAPI_SPEAKER_COUNT; k > 0; k--) { wfxex.dwChannelMask = KSAUDIO_SPEAKER_DIRECTOUT; wfxex.Format.nChannels = k; wfxex.Format.nBlockAlign = wfxex.Format.nChannels * (wfxex.Format.wBitsPerSample >> 3); wfxex.Format.nAvgBytesPerSec = wfxex.Format.nSamplesPerSec * wfxex.Format.nBlockAlign; hr = pClient->IsFormatSupported(AUDCLNT_SHAREMODE_EXCLUSIVE, &wfxex.Format, NULL); if (SUCCEEDED(hr)) { if (k > 3) // Add only multichannel LPCM { deviceInfo.m_dataFormats.push_back(AE_FMT_LPCM); hasLpcm = true; } break; } } /* Try with reported channel mask */ for (unsigned int k = WASAPI_SPEAKER_COUNT; k > 0; k--) { wfxex.dwChannelMask = uiChannelMask; wfxex.Format.nChannels = k; wfxex.Format.nBlockAlign = wfxex.Format.nChannels * (wfxex.Format.wBitsPerSample >> 3); wfxex.Format.nAvgBytesPerSec = wfxex.Format.nSamplesPerSec * wfxex.Format.nBlockAlign; hr = pClient->IsFormatSupported(AUDCLNT_SHAREMODE_EXCLUSIVE, &wfxex.Format, NULL); if (SUCCEEDED(hr)) { if ( !hasLpcm && k > 3) // Add only multichannel LPCM { deviceInfo.m_dataFormats.push_back(AE_FMT_LPCM); hasLpcm = true; } break; } } /* Try with specific speakers configurations */ for (unsigned int i = 0; i < ARRAYSIZE(layoutsList); i++) { unsigned int nmbOfCh; wfxex.dwChannelMask = ChLayoutToChMask(layoutsList[i], &nmbOfCh); wfxex.Format.nChannels = nmbOfCh; wfxex.Format.nBlockAlign = wfxex.Format.nChannels * (wfxex.Format.wBitsPerSample >> 3); wfxex.Format.nAvgBytesPerSec = wfxex.Format.nSamplesPerSec * wfxex.Format.nBlockAlign; hr = pClient->IsFormatSupported(AUDCLNT_SHAREMODE_EXCLUSIVE, &wfxex.Format, NULL); if (SUCCEEDED(hr)) { if ( deviceChannels.Count() < nmbOfCh) deviceChannels = layoutsList[i]; if ( !hasLpcm && nmbOfCh > 3) // Add only multichannel LPCM { deviceInfo.m_dataFormats.push_back(AE_FMT_LPCM); hasLpcm = true; } } } pClient->Release(); } else { CLog::Log(LOGDEBUG, __FUNCTION__": Failed to activate device for passthrough capability testing."); } deviceInfo.m_deviceName = strDevName; deviceInfo.m_displayName = strWinDevType.append(strFriendlyName); deviceInfo.m_displayNameExtra = std::string("WASAPI: ").append(strFriendlyName); deviceInfo.m_deviceType = aeDeviceType; deviceInfo.m_channels = deviceChannels; /* Store the device info */ deviceInfoList.push_back(deviceInfo); if(pDevice->GetId(&pwszID) == S_OK) { if(wstrDDID.compare(pwszID) == 0) { deviceInfo.m_deviceName = std::string("default"); deviceInfo.m_displayName = std::string("default"); deviceInfo.m_displayNameExtra = std::string(""); deviceInfoList.push_back(deviceInfo); } CoTaskMemFree(pwszID); } SAFE_RELEASE(pDevice); SAFE_RELEASE(pProperty); }