/* Copyright(C) 2007-2017 VoIPobjects (voipobjects.com) * This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ #if defined(TARGET_WIN) && defined(_MSC_VER) #include "Audio_DirectSound.h" #include "Audio_Helper.h" #include "../Helper/HL_Exception.h" #include "../Helper/HL_Log.h" #include #include #include #pragma comment(lib, "dsound.lib") #pragma comment(lib, "dxguid.lib") #define DRVM_MAPPER_CONSOLEVOICECOM_GET (0x2000 + 23) #define DRVM_MAPPER_PREFERRED_GET (0x2000 + 21) #define DRV_QUERYFUNCTIONINSTANCEID (DRV_RESERVED + 17) #define DRV_QUERYFUNCTIONINSTANCEIDSIZE (DRV_RESERVED + 18) #define LOG_SUBSYSTEM "DirectSound" using namespace Audio; class DSoundInit { public: DSoundInit(); virtual ~DSoundInit(); void load(); void unload(); struct EntryPoints { HINSTANCE mInstance; HRESULT (WINAPI *DirectSoundCreate8)(LPGUID, LPDIRECTSOUND8 *, LPUNKNOWN); HRESULT (WINAPI *DirectSoundEnumerateW)(LPDSENUMCALLBACKW, LPVOID); HRESULT (WINAPI *DirectSoundEnumerateA)(LPDSENUMCALLBACKA, LPVOID); HRESULT (WINAPI *DirectSoundCaptureCreate8)(LPGUID, LPDIRECTSOUNDCAPTURE8* , LPUNKNOWN); HRESULT (WINAPI *DirectSoundCaptureEnumerateW)(LPDSENUMCALLBACKW, LPVOID); HRESULT (WINAPI *DirectSoundCaptureEnumerateA)(LPDSENUMCALLBACKA, LPVOID); HRESULT (WINAPI *GetDeviceID)(LPCGUID src, LPGUID dst); } mRoutines; protected: LPDIRECTSOUND mDirectSound; Mutex mGuard; unsigned int mRefCount; }; DSoundInit gDSoundInit; DSoundInit::DSoundInit() :mRefCount(0) { } DSoundInit::~DSoundInit() { //Unload(); } void DSoundInit::load() { Lock l(mGuard); if (++mRefCount == 1) { HRESULT hr = E_FAIL; hr = ::CoInitialize(NULL); //load the DirectSound DLL mRoutines.mInstance = ::LoadLibraryW(L"dsound.dll"); if (!mRoutines.mInstance) throw std::logic_error("Cannot load dsound.dll"); mRoutines.DirectSoundCaptureCreate8 = (HRESULT (WINAPI *)(LPGUID, LPDIRECTSOUNDCAPTURE8 *, LPUNKNOWN))::GetProcAddress(mRoutines.mInstance, "DirectSoundCaptureCreate8"); mRoutines.DirectSoundCaptureEnumerateW = (HRESULT (WINAPI *)(LPDSENUMCALLBACKW, LPVOID))::GetProcAddress(mRoutines.mInstance, "DirectSoundCaptureEnumerateW"); mRoutines.DirectSoundCreate8 = (HRESULT (WINAPI *)(LPGUID, LPDIRECTSOUND8 *, LPUNKNOWN))::GetProcAddress(mRoutines.mInstance, "DirectSoundCreate8"); mRoutines.DirectSoundEnumerateW = (HRESULT (WINAPI *)(LPDSENUMCALLBACKW, LPVOID))::GetProcAddress(mRoutines.mInstance, "DirectSoundEnumerateW"); mRoutines.GetDeviceID = (HRESULT (WINAPI*) (LPCGUID, LPGUID)) GetProcAddress(mRoutines.mInstance, "GetDeviceID"); } } void DSoundInit::unload() { Lock l(mGuard); if (--mRefCount == 0) { if (mRoutines.mInstance) { ::FreeLibrary(mRoutines.mInstance); mRoutines.mInstance = NULL; } CoUninitialize(); } } // --------------- VistaEnumerator --------------------- VistaEnumerator::VistaEnumerator() :mCollection(NULL), mDefaultDevice(NULL), mEnumerator(NULL), mDirection(eCapture) { } VistaEnumerator::~VistaEnumerator() { close(); } void VistaEnumerator::open(int direction) { const CLSID CLSID_MMDeviceEnumerator = __uuidof(MMDeviceEnumerator); const IID IID_IMMDeviceEnumerator = __uuidof(IMMDeviceEnumerator); mDirection = (direction == myMicrophone) ? eCapture : eRender; HRESULT hr = CoCreateInstance( CLSID_MMDeviceEnumerator, NULL, CLSCTX_ALL, IID_IMMDeviceEnumerator, (void**)&mEnumerator); if (!mEnumerator) return; hr = mEnumerator->EnumAudioEndpoints(mDirection, DEVICE_STATE_ACTIVE, &mCollection); if (!mCollection) return; hr = mEnumerator->GetDefaultAudioEndpoint(mDirection, eCommunications, &mDefaultDevice); if (!mDefaultDevice) return; enumerate(); } void VistaEnumerator::close() { try { if (mCollection) { mCollection->Release(); mCollection = NULL; } if (mDefaultDevice) { //mDefaultDevice->Release(); mDefaultDevice = NULL; } if (mEnumerator) { mEnumerator->Release(); mEnumerator = NULL; } } catch(...) { } } IMMDevice* VistaEnumerator::mapIndexToInterface(int index) { if (!mCollection) return NULL; if (index == -1) return mDefaultDevice; size_t idSize = 0; MMRESULT mmres = 0; WCHAR* id = NULL; if (mDirection == eCapture) { mmres = waveInMessage((HWAVEIN)index, DRV_QUERYFUNCTIONINSTANCEIDSIZE, (DWORD_PTR)&idSize, NULL); if (mmres != MMSYSERR_NOERROR) return NULL; id = (WCHAR*)_alloca(idSize*sizeof(WCHAR)); mmres = waveInMessage((HWAVEIN)index, DRV_QUERYFUNCTIONINSTANCEID, (DWORD_PTR)id, idSize); } else { mmres = waveOutMessage((HWAVEOUT)index, DRV_QUERYFUNCTIONINSTANCEIDSIZE, (DWORD_PTR)&idSize, NULL); if (mmres != MMSYSERR_NOERROR) return NULL; id = (WCHAR*)_alloca(idSize*sizeof(WCHAR)); mmres = waveOutMessage((HWAVEOUT)index, DRV_QUERYFUNCTIONINSTANCEID, (DWORD_PTR)id, idSize); } if (mmres != MMSYSERR_NOERROR) return NULL; IMMDevice* pDevice = NULL; mEnumerator->GetDevice(id, &pDevice); return pDevice; } void VistaEnumerator::enumerate() { mNameList.clear(); int res = (int)count(); for (int i=0; iOpenPropertyStore(STGM_READ, &store); if (store) { PROPVARIANT varName; PropVariantInit(&varName); if (store->GetValue(PKEY_Device_FriendlyName, &varName) == S_OK) mNameList.push_back(varName.pwszVal); PropVariantClear(&varName); store->Release(); } dev->Release(); } } } std::tstring VistaEnumerator::nameAt(int index) { return mNameList[index]; } int VistaEnumerator::idAt(int index) { return index; } int VistaEnumerator::count() { if (mDirection == eCapture) return waveInGetNumDevs(); else return waveOutGetNumDevs(); } int VistaEnumerator::indexOfDefaultDevice() { DWORD devID = -1, status = 0; if (mDirection == mySpeaker) { if (waveOutMessage((HWAVEOUT)WAVE_MAPPER, DRVM_MAPPER_CONSOLEVOICECOM_GET, (DWORD_PTR)&devID, (DWORD_PTR)&status) != MMSYSERR_NOERROR) waveOutMessage((HWAVEOUT)WAVE_MAPPER, DRVM_MAPPER_PREFERRED_GET, (DWORD_PTR)&devID, (DWORD_PTR)&status); } else { if (waveInMessage((HWAVEIN)WAVE_MAPPER, DRVM_MAPPER_CONSOLEVOICECOM_GET, (DWORD_PTR)&devID, (DWORD_PTR)&status) != MMSYSERR_NOERROR) waveInMessage((HWAVEIN)WAVE_MAPPER, DRVM_MAPPER_PREFERRED_GET, (DWORD_PTR)&devID, (DWORD_PTR)&status); } return devID; } // -------------- XpEnumerator --------------- XpEnumerator::XpEnumerator() :mDirection(-1) { } XpEnumerator::~XpEnumerator() { } void XpEnumerator::open(int direction) { mNameList.clear(); if (direction == myMicrophone) { int count = waveInGetNumDevs(); for (int i=0; iCreateCaptureBuffer(&dsbd, &dscb, NULL)); DSoundHelper::checkComResult(dscb->QueryInterface(IID_IDirectSoundCaptureBuffer8, (void**)&mBuffer)); DSoundHelper::checkComResult(dscb->QueryInterface(IID_IDirectSoundNotify, (void**)&mNotifications)); DSoundHelper::checkComResult(mNotifications->SetNotificationPositions(AUDIO_MIC_BUFFER_COUNT, mEventArray)); DSoundHelper::checkComResult(mBuffer->Start(DSCBSTART_LOOPING)); dscb->Release(); setSimulate( false ); } bool DSoundInputDevice::open() { ICELogInfo(<< "Request to DirectSound audio input"); Lock lock(mGuard); mRefCount++; if (mRefCount == 1) { ICELogInfo(<< "Schedule DirectSound audio input thread"); mThreadHandle = (HANDLE)_beginthread(&threadProc, 0, this); } return true; } void DSoundInputDevice::closeDevice() { ICELogInfo(<<"Close DirectSound audio input"); Lock l(mGuard); #ifdef AUDIO_DUMPINPUT mDump.close(); #endif if (mBuffer) { mBuffer->Stop(); mBuffer->Release(); mBuffer = NULL; } if (mNotifications) { mNotifications->Release(); mNotifications = NULL; } if (mDevice) { mDevice->Release(); mDevice = NULL; } else return; ::CoUninitialize(); } void DSoundInputDevice::close() { { Lock l(mGuard); mRefCount--; if (mRefCount != 0) return; // Set shutdown signal if (!mThreadHandle) return; ::SetEvent(mShutdownSignal); } ::WaitForSingleObject(mThreadHandle, INFINITE); mThreadHandle = 0; } bool DSoundInputDevice::tryReadBuffer(void* buffer) { // Ensure device exists if (!mDevice) { setSimulate( true ); return false; } if (mQueue.size() >= AUDIO_MIC_BUFFER_SIZE) { memcpy(buffer, mQueue.data(), AUDIO_MIC_BUFFER_SIZE); if (mEnableDenoiser && AUDIO_CHANNELS == 1) mDenoiser.fromMic(buffer, AUDIO_MIC_BUFFER_LENGTH); #ifdef AUDIO_DUMPINPUT mDump.write(buffer, AUDIO_MIC_BUFFER_SIZE); #endif mQueue.erase(0, AUDIO_MIC_BUFFER_SIZE); return true; } try { if (::WaitForSingleObject(mEventArray[mNextBuffer].hEventNotify, AUDIO_MIC_BUFFER_COUNT * AUDIO_MIC_BUFFER_LENGTH * 4) != WAIT_OBJECT_0) { setSimulate( true ); return false; } // See if all other buffers are signaled if (::WaitForMultipleObjects(AUDIO_MIC_BUFFER_COUNT, mEventSignals, TRUE, 0) != WAIT_TIMEOUT) { // Possible overflow. Consider current buffer resulting. Reset ALL events. for (int i = 0; iLock(mReadOffset, AUDIO_MIC_BUFFER_SIZE, &ptr1, &len1, &ptr2, &len2, 0)); // Copy&Enqueue captured data to mQueue if (ptr1 && len1) mQueue.appendBuffer(ptr1, len1); if (ptr2 && len2) mQueue.appendBuffer(ptr2, len2); DSoundHelper::checkComResult(mBuffer->Unlock(ptr1, len1, ptr2, len2)); if (mQueue.size() >= AUDIO_MIC_BUFFER_SIZE) { memcpy(buffer, mQueue.data(), AUDIO_MIC_BUFFER_SIZE); if (mEnableDenoiser && AUDIO_CHANNELS == 1) mDenoiser.fromMic(buffer, AUDIO_MIC_BUFFER_LENGTH); #ifdef AUDIO_DUMPINPUT mDump.write(buffer, AUDIO_MIC_BUFFER_SIZE); #endif mQueue.erase(0, AUDIO_MIC_BUFFER_SIZE); } else return false; return true; } catch(...) { setSimulate( true ); } return false; } void DSoundInputDevice::setSimulate(bool s) { if (!mSimulate && s) mNullAudio.start(); else if (mSimulate && !s) mNullAudio.stop(); mSimulate = s; } Format DSoundInputDevice::getFormat() { return Format(); } int DSoundInputDevice::readBuffer(void* buffer) { //Lock lock(mGuard); if (mRefCount <= 0 || isSimulate()) return 0; // Check for finished buffer if (!tryReadBuffer(buffer)) return 0; return AUDIO_MIC_BUFFER_SIZE; } void DSoundInputDevice::threadProc(void* arg) { DSoundInputDevice* impl = (DSoundInputDevice*)arg; impl->openDevice(); while (true) { // Poll for shutdown signal if (::WaitForSingleObject(impl->mShutdownSignal, 0) == WAIT_OBJECT_0) break; // Preset buffer with silence memset(impl->mTempBuffer, 0, AUDIO_MIC_BUFFER_SIZE); // Try to read buffer if (!impl->readBuffer(impl->mTempBuffer)) { // Introduce delay here to simulate true audio impl->mNullAudio.waitForBuffer(); } // Distribute the captured buffer if (impl->connection()) impl->connection()->onMicData(impl->getFormat(), impl->mTempBuffer, AUDIO_MIC_BUFFER_SIZE); } impl->closeDevice(); } DSoundOutputDevice::DSoundOutputDevice(GUID deviceId) :mDevice(NULL), mPrimaryBuffer(NULL), mBuffer(NULL), mWriteOffset(0), mPlayedSamples(0), mTotalPlayed(0), mTail(0), mThreadHandle(0), mSimulate(false), mGUID(deviceId), mNullAudio(AUDIO_SPK_BUFFER_LENGTH, AUDIO_SPK_BUFFER_COUNT) { gDSoundInit.load(); mShutdownSignal = ::CreateEvent(NULL, FALSE, FALSE, NULL); mBufferSignal = ::CreateEvent(NULL, FALSE, FALSE, NULL); mRefCount = 0; } DSoundOutputDevice::~DSoundOutputDevice() { close(); // Destroy used signals ::CloseHandle(mShutdownSignal); ::CloseHandle(mBufferSignal); gDSoundInit.unload(); } bool DSoundOutputDevice::open() { ICELogInfo(<< "Request to DirectSound audio output"); // Start thread mRefCount++; if (mRefCount == 1) { ICELogInfo(<< "Schedule DirectSound audio output thread"); mThreadHandle = (HANDLE)_beginthread(&threadProc, 0, this); ::SetThreadPriority(mThreadHandle, THREAD_PRIORITY_TIME_CRITICAL); } return true; } void DSoundOutputDevice::close() { if (mRefCount == 0) return; mRefCount--; if (mRefCount > 0) return; // Tell the thread to exit SetEvent(mShutdownSignal); // Wait for thread if (mThreadHandle) WaitForSingleObject(mThreadHandle, INFINITE); mThreadHandle = 0; } void DSoundOutputDevice::openDevice() { ICELogInfo(<< "Open DirectSound audio output"); if (IsEqualGUID(mGUID, GUID_NULL)) { setSimulate( true ); return; } mWriteOffset = 0; mPlayedSamples = 0; mSentBytes = 0; mPlayCursor = 0; mBufferSize = AUDIO_SPK_BUFFER_COUNT * AUDIO_SPK_BUFFER_SIZE; DSoundHelper::checkComResult(gDSoundInit.mRoutines.DirectSoundCreate8(&mGUID, &mDevice, NULL)); DSoundHelper::checkComResult(mDevice->SetCooperativeLevel(::GetDesktopWindow(), DSSCL_PRIORITY)); WAVEFORMATEX wfx; memset(&wfx, 0, sizeof(wfx)); wfx.cbSize = sizeof(wfx); wfx.nChannels = AUDIO_CHANNELS; wfx.nSamplesPerSec = AUDIO_SAMPLERATE; wfx.wBitsPerSample = 16; wfx.nBlockAlign = wfx.nChannels * wfx.wBitsPerSample / 8; wfx.nAvgBytesPerSec = wfx.nSamplesPerSec * wfx.nBlockAlign; wfx.wFormatTag = WAVE_FORMAT_PCM; DSBUFFERDESC dsbd; ZeroMemory(&dsbd, sizeof(dsbd)); dsbd.dwSize = sizeof(DSBUFFERDESC); dsbd.dwFlags = DSBCAPS_PRIMARYBUFFER; dsbd.dwBufferBytes = 0; dsbd.lpwfxFormat = NULL;//&wfx; dsbd.guid3DAlgorithm = DS3DALG_DEFAULT; DSoundHelper::checkComResult(mDevice->CreateSoundBuffer(&dsbd, &mPrimaryBuffer, NULL )); DSBCAPS caps; caps.dwSize = sizeof(caps); caps.dwFlags = 0; caps.dwBufferBytes = 0; caps.dwPlayCpuOverhead = 0; caps.dwUnlockTransferRate = 0; DSoundHelper::checkComResult(mPrimaryBuffer->GetCaps(&caps)); dsbd.dwSize = sizeof(caps); dsbd.dwFlags = DSBCAPS_GLOBALFOCUS; dsbd.lpwfxFormat = &wfx; dsbd.guid3DAlgorithm = DS3DALG_DEFAULT; dsbd.dwBufferBytes = mBufferSize; DSoundHelper::checkComResult(mDevice->CreateSoundBuffer(&dsbd, &mBuffer, NULL)); // Fill the buffer with silence LPVOID ptr1 = NULL, ptr2 = NULL; DWORD len1 = 0, len2 = 0; DSoundHelper::checkComResult(mBuffer->Lock(0, AUDIO_SPK_BUFFER_SIZE * AUDIO_SPK_BUFFER_COUNT, &ptr1, &len1, &ptr2, &len2, 0)); if (len1 && ptr1) memset(ptr1, 0, len1); if (len2 && ptr2) memset(ptr2, 0, len2); DSoundHelper::checkComResult(mBuffer->Unlock(ptr1, len1, ptr2, len2)); DSoundHelper::checkComResult(mBuffer->Play(0,0,DSBPLAY_LOOPING)); mBuffer->GetCurrentPosition(NULL, &mWriteCursor); } void DSoundOutputDevice::closeDevice() { if (mBuffer) { mBuffer->Stop(); mBuffer->Release(); mBuffer = NULL; } if (mPrimaryBuffer) { mPrimaryBuffer->Stop(); mPrimaryBuffer->Release(); mPrimaryBuffer = NULL; } if (mDevice) { mDevice->Release(); mDevice = NULL; } } void DSoundOutputDevice::restoreBuffer() { if (mSimulate) return; DWORD status = 0; DSoundHelper::checkComResult(mBuffer->GetStatus(&status)); if (DSBSTATUS_BUFFERLOST == status) DSoundHelper::checkComResult(mBuffer->Restore()); } bool DSoundOutputDevice::getMediaFrame() { try { memset(mMediaFrame, 0, sizeof mMediaFrame); if (mConnection) mConnection->onSpkData(getFormat(), mMediaFrame, sizeof mMediaFrame); } catch(...) {} return true; } bool DSoundOutputDevice::process() { if (mSimulate) return false; // Find amount of written data from last call DWORD cursor = 0; DSoundHelper::checkComResult(mBuffer->GetCurrentPosition(NULL, &cursor)); unsigned written; if (cursor < mWriteCursor) written = mBufferSize - mWriteCursor + cursor; else written = cursor - mWriteCursor; mWriteCursor += (written / AUDIO_SPK_BUFFER_SIZE) * AUDIO_SPK_BUFFER_SIZE; mWriteCursor %= mBufferSize; bool finished = false; for (unsigned frameIndex = 0; frameIndex < written / AUDIO_SPK_BUFFER_SIZE && !finished; frameIndex++) { unsigned offset = mWriteOffset + frameIndex * AUDIO_SPK_BUFFER_SIZE; offset %= mBufferSize; // See what we can write LPVOID ptr1 = NULL, ptr2 = NULL; DWORD len1 = 0, len2 = 0; DSoundHelper::checkComResult(mBuffer->Lock(offset, AUDIO_SPK_BUFFER_SIZE, &ptr1, &len1, &ptr2, &len2, 0)); assert(ptr2 == NULL); assert(len1 >= AUDIO_SPK_BUFFER_SIZE); if (getMediaFrame()) finished = true; memmove(ptr1, mMediaFrame, AUDIO_SPK_BUFFER_SIZE); DSoundHelper::checkComResult(mBuffer->Unlock(ptr1, AUDIO_SPK_BUFFER_SIZE, ptr2, 0)); } // Increase write offset mWriteOffset += (written / AUDIO_SPK_BUFFER_SIZE) * AUDIO_SPK_BUFFER_SIZE; mWriteOffset %= mBufferSize; return true; } void DSoundOutputDevice::threadProc(void* arg) { DSoundOutputDevice* impl = (DSoundOutputDevice*)arg; impl->openDevice(); DWORD waitResult = 0; HANDLE waitArray[2] = {impl->mBufferSignal, impl->mShutdownSignal}; unsigned exitCount = 0; bool exitSignal = false; while (true) { // Poll for shutdown signal if (WAIT_OBJECT_0 == ::WaitForSingleObject(impl->mShutdownSignal, 0)) break; if (impl->isSimulate()) { impl->mNullAudio.waitForBuffer(); impl->getMediaFrame(); } else { // Poll events waitResult = ::WaitForMultipleObjects(2, waitArray, FALSE, 5); if (waitResult == WAIT_OBJECT_0 + 1) break; try { impl->restoreBuffer(); impl->process(); } catch(const Exception& e) { ICELogError(<< "DirectSound output failed with code = " << e.code() << ", subcode = " << e.subcode()); impl->setSimulate(true); } catch(...) { ICELogError(<< "DirectSound output failed due to unexpected exception."); impl->setSimulate(true); } } } impl->closeDevice(); } unsigned DSoundOutputDevice::playedTime() const { return 0; } void DSoundOutputDevice::setSimulate(bool s) { mSimulate = s; } bool DSoundOutputDevice::isSimulate() const { return mSimulate; } Format DSoundOutputDevice::getFormat() { return Format(); } bool DSoundOutputDevice::closing() { return false; } typedef WINUSERAPI HRESULT (WINAPI *LPFNDLLGETCLASSOBJECT) (const CLSID &, const IID &, void **); HRESULT DirectSoundPrivateCreate (OUT LPKSPROPERTYSET * ppKsPropertySet) { HMODULE hLibDsound = NULL; LPFNDLLGETCLASSOBJECT pfnDllGetClassObject = NULL; LPCLASSFACTORY pClassFactory = NULL; LPKSPROPERTYSET pKsPropertySet = NULL; HRESULT hr = DS_OK; // Load dsound.dll hLibDsound = LoadLibrary(TEXT("dsound.dll")); if(!hLibDsound) { hr = DSERR_GENERIC; } // Find DllGetClassObject if(SUCCEEDED(hr)) { pfnDllGetClassObject = (LPFNDLLGETCLASSOBJECT)GetProcAddress ( hLibDsound, "DllGetClassObject" ); if(!pfnDllGetClassObject) { hr = DSERR_GENERIC; } } // Create a class factory object if(SUCCEEDED(hr)) { hr = pfnDllGetClassObject (CLSID_DirectSoundPrivate, IID_IClassFactory, (LPVOID *)&pClassFactory ); } // Create the DirectSoundPrivate object and query for an IKsPropertySet // interface if(SUCCEEDED(hr)) { hr = pClassFactory->CreateInstance ( NULL, IID_IKsPropertySet, (LPVOID *)&pKsPropertySet ); } // Release the class factory if(pClassFactory) { pClassFactory->Release(); } // Handle final success or failure if(SUCCEEDED(hr)) { *ppKsPropertySet = pKsPropertySet; } else if(pKsPropertySet) { pKsPropertySet->Release(); } FreeLibrary(hLibDsound); return hr; } BOOL GetInfoFromDSoundGUID( GUID i_sGUID, int &dwWaveID) { LPKSPROPERTYSET pKsPropertySet = NULL; HRESULT hr; BOOL retval = FALSE; PDSPROPERTY_DIRECTSOUNDDEVICE_DESCRIPTION_DATA psDirectSoundDeviceDescription = NULL; DSPROPERTY_DIRECTSOUNDDEVICE_DESCRIPTION_DATA sDirectSoundDeviceDescription; memset(&sDirectSoundDeviceDescription,0,sizeof(sDirectSoundDeviceDescription)); hr = DirectSoundPrivateCreate( &pKsPropertySet ); if(SUCCEEDED(hr)) { ULONG ulBytesReturned = 0; sDirectSoundDeviceDescription.DeviceId = i_sGUID; // On the first call the final size is unknown so pass the size of the struct in order to receive // "Type" and "DataFlow" values, ulBytesReturned will be populated with bytes required for struct+strings. hr = pKsPropertySet->Get(DSPROPSETID_DirectSoundDevice, DSPROPERTY_DIRECTSOUNDDEVICE_DESCRIPTION, NULL, 0, &sDirectSoundDeviceDescription, sizeof(sDirectSoundDeviceDescription), &ulBytesReturned ); if (ulBytesReturned) { // On the first call it notifies us of the required amount of memory in order to receive the strings. // Allocate the required memory, the strings will be pointed to the memory space directly after the struct. psDirectSoundDeviceDescription = (PDSPROPERTY_DIRECTSOUNDDEVICE_DESCRIPTION_DATA)new BYTE[ulBytesReturned]; *psDirectSoundDeviceDescription = sDirectSoundDeviceDescription; hr = pKsPropertySet->Get(DSPROPSETID_DirectSoundDevice, DSPROPERTY_DIRECTSOUNDDEVICE_DESCRIPTION, NULL, 0, psDirectSoundDeviceDescription, ulBytesReturned, &ulBytesReturned ); dwWaveID = psDirectSoundDeviceDescription->WaveDeviceId; /*Description = psDirectSoundDeviceDescription->Description; Module = psDirectSoundDeviceDescription->Module; Interface = psDirectSoundDeviceDescription->Interface;*/ delete [] psDirectSoundDeviceDescription; retval = TRUE; } pKsPropertySet->Release(); } return retval; } struct EnumResult { int mDeviceId; GUID mGuid; }; BOOL CALLBACK DSEnumCallback( LPGUID lpGuid, LPCTSTR lpcstrDescription, LPCTSTR lpcstrModule, LPVOID lpContext ) { if (lpGuid) { int devId = -1; GetInfoFromDSoundGUID(*lpGuid, devId); EnumResult* er = (EnumResult*)lpContext; if (er->mDeviceId == devId) { er->mGuid = *lpGuid; return FALSE; } else return TRUE; } else return TRUE; } GUID DSoundHelper::deviceId2Guid(int deviceId, bool captureDevice) { EnumResult er; er.mDeviceId = deviceId; er.mGuid = GUID_NULL; memset(&er.mGuid, 0, sizeof er.mGuid); if (captureDevice) DirectSoundCaptureEnumerate(DSEnumCallback, &er); else DirectSoundEnumerate(DSEnumCallback, &er); return er.mGuid; } void DSoundHelper::checkComResult(HRESULT code) { if (FAILED(code)) throw Exception(ERR_DSOUND); } #endif