std::vector<AudioDevice> EnumerateAudioDevices(_Out_opt_ AudioDevice* defaultDevice = nullptr) { UINT32 deviceCount = 0; IMMDeviceEnumerator *immDevEnum = nullptr; IMMDeviceCollection *immDevCollection = nullptr; IMMDevice *immDev = nullptr; std::vector<AudioDevice> vAudioDevices; HRESULT hResult = CoCreateInstance( __uuidof(MMDeviceEnumerator), NULL, CLSCTX_ALL, __uuidof(IMMDeviceEnumerator), (void**) &immDevEnum); if (FAILED(hResult)) { idLib::Warning( "Failed to get audio enumerator" ); return std::move(vAudioDevices); } if (defaultDevice != nullptr) { ZeroMemory(defaultDevice, sizeof(AudioDevice)); IMMDevice *defaultImmDev = nullptr; // @pjb: get the default audio endpoint and make it the first one in the list if (SUCCEEDED(immDevEnum->GetDefaultAudioEndpoint(eRender, eConsole, &defaultImmDev))) { GetAudioDeviceDetails(defaultImmDev, defaultDevice); defaultImmDev->Release(); } } hResult = immDevEnum->EnumAudioEndpoints(eRender, DEVICE_STATE_ACTIVE, &immDevCollection); if (FAILED(hResult)) { idLib::Warning( "Failed to get audio endpoints" ); return std::move(vAudioDevices); } hResult = immDevCollection->GetCount(&deviceCount); if (FAILED(hResult)) { idLib::Warning( "No audio devices found" ); return std::move(vAudioDevices); } for (UINT i = 0; i < deviceCount; i++) { AudioDevice ad; hResult = immDevCollection->Item(i, &immDev); if (SUCCEEDED(hResult)) { hResult = GetAudioDeviceDetails(immDev, &ad); } if (SUCCEEDED(hResult)) { vAudioDevices.push_back(ad); } if (immDev != nullptr) { immDev->Release(); } } immDevCollection->Release(); immDevEnum->Release(); return std::move(vAudioDevices); }
void MFSound_InitWASAPI() { gDevices.Init(sizeof(MFAudioDevice), 8, 8); gCaptureDevices.Init(sizeof(MFAudioCaptureDevice), 8, 8); HRESULT hr = CoCreateInstance(__uuidof(MMDeviceEnumerator), NULL, CLSCTX_ALL, __uuidof(IMMDeviceEnumerator), (void**)&gpEnumerator); if(FAILED(hr)) { MFDebug_Assert(false, "Couldn't create multimedia device enumerator!"); return; } // enumerate audio devices... gpNotification = new MFAudioDeviceNotification; gpEnumerator->RegisterEndpointNotificationCallback(gpNotification); // enumerate render devices IMMDeviceCollection *pDevices; gpEnumerator->EnumAudioEndpoints(eRender, DEVICE_STATE_ACTIVE | DEVICE_STATE_UNPLUGGED, &pDevices); if(pDevices) { UINT count; pDevices->GetCount(&count); for(UINT i=0; i<count; ++i) { IMMDevice *pDevice; pDevices->Item(i, &pDevice); MFDevice *pDev = MFDevice_AllocDevice(MFDT_AudioRender, NULL); pDev->pInternal = gDevices.AllocAndZero(); MFAudioDevice &device = *(MFAudioDevice*)pDev->pInternal; GetDeviceInfo(pDevice, pDev); pDevice->Release(); MFDebug_Log(0, MFStr("Found audio device: %s, %s - state: %d (%s)", pDev->strings[MFDS_DeviceName], pDev->strings[MFDS_Manufacturer], device.state, pDev->strings[MFDS_ID])); } pDevices->Release(); } // enumerate capture devices gpEnumerator->EnumAudioEndpoints(eCapture, DEVICE_STATE_ACTIVE | DEVICE_STATE_UNPLUGGED, &pDevices); if(pDevices) { UINT count; pDevices->GetCount(&count); for(UINT i=0; i<count; ++i) { IMMDevice *pDevice; pDevices->Item(i, &pDevice); MFDevice *pDev = MFDevice_AllocDevice(MFDT_AudioCapture, NULL); pDev->pInternal = gCaptureDevices.AllocAndZero(); MFAudioCaptureDevice &device = *(MFAudioCaptureDevice*)pDev->pInternal; GetDeviceInfo(pDevice, pDev); pDevice->Release(); MFDebug_Log(0, MFStr("Found audio capture device: %s, %s - state: %d (%s)", pDev->strings[MFDS_DeviceName], pDev->strings[MFDS_Manufacturer], device.state, pDev->strings[MFDS_ID])); } pDevices->Release(); } // set defaults (this is some awkward windows code!) for(int i=0; i<2; ++i) { for(int j=0; j<3; ++j) { IMMDevice *pDevice; gpEnumerator->GetDefaultAudioEndpoint(gDirection[i], gRole[j], &pDevice); if(pDevice) { wchar_t *pDefaultId; pDevice->GetId(&pDefaultId); char temp[128]; MFString_CopyUTF16ToUTF8(temp, pDefaultId); MFDevice *pDev = MFDevice_GetDeviceById(temp); MFDevice_SetDefaultDevice(gDt[i], gDef[j], pDev); CoTaskMemFree(pDefaultId); pDevice->Release(); } } } }
static bool UpdateAudioDucking(bool bDuckingOptOutChecked) { HRESULT hr = S_OK; // Start with the default endpoint. IMMDeviceEnumerator* pDeviceEnumerator = NULL; hr = CoCreateInstance(__uuidof(MMDeviceEnumerator), NULL, CLSCTX_INPROC_SERVER, IID_PPV_ARGS(&pDeviceEnumerator)); if (SUCCEEDED(hr)) { { IMMDevice* pEndpoint = NULL; hr = pDeviceEnumerator->GetDefaultAudioEndpoint(eRender, eConsole, &pEndpoint); if (SUCCEEDED(hr)) { LPWSTR Desc; pEndpoint->GetId(&Desc); UE_LOG(LogVoiceCapture, Display, TEXT("%s ducking on audio device. Desc: %s"), bDuckingOptOutChecked ? TEXT("Disabling") : TEXT("Enabling"), Desc); CoTaskMemFree(Desc); FAudioDuckingWindows::EnableDuckingOptOut(pEndpoint, bDuckingOptOutChecked); pEndpoint->Release(); pEndpoint = NULL; } } if (0) // reference for enumerating all endpoints in case its necessary { IMMDeviceCollection* pDeviceCollection = NULL; IMMDevice* pCollEndpoint = NULL; hr = pDeviceEnumerator->EnumAudioEndpoints(eRender, DEVICE_STATE_ACTIVE, &pDeviceCollection); if (SUCCEEDED(hr)) { IPropertyStore *pProps = NULL; ::UINT DeviceCount = 0; pDeviceCollection->GetCount(&DeviceCount); for (::UINT i = 0; i < DeviceCount; ++i) { hr = pDeviceCollection->Item(i, &pCollEndpoint); if (SUCCEEDED(hr) && pCollEndpoint) { LPWSTR Desc; pCollEndpoint->GetId(&Desc); hr = pCollEndpoint->OpenPropertyStore(STGM_READ, &pProps); if (SUCCEEDED(hr)) { PROPVARIANT varName; // Initialize container for property value. PropVariantInit(&varName); // Get the endpoint's friendly-name property. hr = pProps->GetValue( PKEY_Device_FriendlyName, &varName); if (SUCCEEDED(hr)) { // Print endpoint friendly name and endpoint ID. UE_LOG(LogVoiceCapture, Display, TEXT("%s ducking on audio device [%d]: \"%s\" (%s)"), bDuckingOptOutChecked ? TEXT("Disabling") : TEXT("Enabling"), i, varName.pwszVal, Desc); CoTaskMemFree(Desc); Desc = NULL; PropVariantClear(&varName); pProps->Release(); pProps = NULL; } } FAudioDuckingWindows::EnableDuckingOptOut(pCollEndpoint, bDuckingOptOutChecked); pCollEndpoint->Release(); pCollEndpoint = NULL; } } pDeviceCollection->Release(); pDeviceCollection = NULL; } } pDeviceEnumerator->Release(); pDeviceEnumerator = NULL; } if (FAILED(hr)) { UE_LOG(LogVoiceCapture, Warning, TEXT("Failed to duck audio endpoint. Error: %0x08x"), hr); } return SUCCEEDED(hr); }
//------------------------------------------------------------------------------------------------- void FMain() { if (const HWND hWnd = FindWindowW(g_wGuidClass, nullptr)) PostMessageW(hWnd, WM_CLOSE, 0, 0); if (SUCCEEDED(CoInitializeEx(nullptr, COINIT_APARTMENTTHREADED))) { IMMDeviceEnumerator *immDeviceEnumerator; if (CoCreateInstance(__uuidof(MMDeviceEnumerator), nullptr, CLSCTX_ALL, __uuidof(IMMDeviceEnumerator), reinterpret_cast<LPVOID*>(&immDeviceEnumerator)) == S_OK) { IMMDevice *immDeviceDefault; if (immDeviceEnumerator->GetDefaultAudioEndpoint(eRender, eConsole, &immDeviceDefault) == S_OK) { wchar_t *wIdDefaultOld; HRESULT hr = immDeviceDefault->GetId(&wIdDefaultOld); immDeviceDefault->Release(); if (hr == S_OK) { IMMDeviceCollection *immDeviceCollection; hr = immDeviceEnumerator->EnumAudioEndpoints(eRender, DEVICE_STATE_ACTIVE, &immDeviceCollection); immDeviceEnumerator->Release(); if (hr == S_OK) { UINT iCount; if (immDeviceCollection->GetCount(&iCount) == S_OK) { bool bFail = true; for (UINT i = 0; i < iCount; ++i) { IMMDevice *immDevice; if (immDeviceCollection->Item(i, &immDevice) == S_OK) { wchar_t *wIdEnum; hr = immDevice->GetId(&wIdEnum); immDevice->Release(); if (hr == S_OK) { if (FCompareMemoryW(wIdDefaultOld, wIdEnum)) { bFail = false; if (++i >= iCount) i = 0; hr = immDeviceCollection->Item(i, &immDevice); immDeviceCollection->Release(); if (hr == S_OK) { wchar_t *wIdDefaultNew; if (immDevice->GetId(&wIdDefaultNew) == S_OK) { IPropertyStore *ipStore; hr = immDevice->OpenPropertyStore(STGM_READ, &ipStore); immDevice->Release(); if (hr == S_OK) { PROPVARIANT propFriendlyName; PropVariantInitFix(&propFriendlyName); PROPERTYKEY propKeyFriendlyName; propKeyFriendlyName.fmtid.Data1 = 0xA45C254E; propKeyFriendlyName.fmtid.Data2 = 0xDF1C; propKeyFriendlyName.fmtid.Data3 = 0x4EFD; FCopyMemory(propKeyFriendlyName.fmtid.Data4); propKeyFriendlyName.pid = 14; hr = ipStore->GetValue(propKeyFriendlyName, &propFriendlyName); ipStore->Release(); if (SUCCEEDED(hr)) { IPolicyConfig *pPolicyConfig; if (CoCreateInstance(__uuidof(CPolicyConfigClient), nullptr, CLSCTX_ALL, (GetVersion() & 0xFF) >= 10 ? __uuidof(IPolicyConfigWin10) : __uuidof(IPolicyConfig), reinterpret_cast<LPVOID*>(&pPolicyConfig)) == S_OK) { hr = pPolicyConfig->SetDefaultEndpoint(wIdDefaultNew, eConsole); if (hr == S_OK) { pPolicyConfig->SetDefaultEndpoint(wIdDefaultNew, eMultimedia); pPolicyConfig->SetDefaultEndpoint(wIdDefaultNew, eCommunications); } pPolicyConfig->Release(); if (hr == S_OK) { WNDCLASSEX wndCl; wndCl.cbSize = sizeof(WNDCLASSEX); wndCl.style = 0; wndCl.lpfnWndProc = WindowProc; wndCl.cbClsExtra = 0; wndCl.cbWndExtra = 0; wndCl.hInstance = GetModuleHandleW(nullptr); wndCl.hIcon = nullptr; wndCl.hCursor = nullptr; wndCl.hbrBackground = nullptr; wndCl.lpszMenuName = nullptr; wndCl.lpszClassName = g_wGuidClass; wndCl.hIconSm = nullptr; if (RegisterClassExW(&wndCl)) { if (CreateWindowExW(WS_EX_NOACTIVATE | WS_EX_LAYERED | WS_EX_TOOLWINDOW | WS_EX_TRANSPARENT | WS_EX_TOPMOST, g_wGuidClass, nullptr, WS_POPUP | WS_VISIBLE | WS_MAXIMIZE, 0, 0, 0, 0, nullptr, nullptr, wndCl.hInstance, propFriendlyName.pwszVal)) { MSG msg; while (GetMessageW(&msg, nullptr, 0, 0) > 0) DispatchMessageW(&msg); } UnregisterClassW(g_wGuidClass, wndCl.hInstance); } } } } PropVariantClear(&propFriendlyName); } CoTaskMemFree(wIdDefaultNew); } else immDevice->Release(); } break; } CoTaskMemFree(wIdEnum); } } } if (bFail) immDeviceCollection->Release(); } else immDeviceCollection->Release(); } CoTaskMemFree(wIdDefaultOld); } else immDeviceEnumerator->Release(); } else immDeviceEnumerator->Release(); } CoUninitialize(); } }
SexyAL_device *SexyALI_WASAPISH_Open(const char *id, SexyAL_format *format, SexyAL_buffering *buffering) { SexyAL_device *dev; WASWrap *w; IMMDeviceEnumerator *immdeven = NULL; WAVEFORMATEXTENSIBLE wfe; HRESULT hr; if(!(dev = (SexyAL_device *)calloc(1, sizeof(SexyAL_device)))) { printf("calloc failed.\n"); return(NULL); } timeBeginPeriod(1); w = (WASWrap *)calloc(1, sizeof(WASWrap)); dev->private_data = w; if(!w) { printf("calloc failed.\n"); Cleanup(dev); return(NULL); } // // // hr = CoInitializeEx(NULL, COINIT_MULTITHREADED); if(hr != S_OK && hr != S_FALSE) { printf("CoInitializeEx() failed: 0x%08x\n", (unsigned)hr); Cleanup(dev); return(NULL); } //printf("NOODLES: 0x%08x 0x%08x\n", LV_CLSID_MMDeviceEnumerator.Data1, LV_IID_IMMDeviceEnumerator.Data1); TRYHR(CoCreateInstance(LV_CLSID_MMDeviceEnumerator, NULL, CLSCTX_ALL, LV_IID_IMMDeviceEnumerator, (void**)&immdeven)); if(id == NULL) { TRYHR(immdeven->GetDefaultAudioEndpoint(eRender, eConsole, &w->immdev)); } else { IMMDeviceCollection *devcoll = NULL; UINT numdevs = 0; wchar_t *id16 = (wchar_t *)calloc(strlen(id) + 1, sizeof(wchar_t)); w->immdev = NULL; MultiByteToWideChar(CP_UTF8, 0, id, -1, id16, strlen(id) + 1); TRYHR(immdeven->EnumAudioEndpoints(eRender, DEVICE_STATE_ACTIVE, &devcoll)); TRYHR(devcoll->GetCount(&numdevs)); for(UINT i = 0; i < numdevs && w->immdev == NULL; i++) { IMMDevice *tmpdev = NULL; IPropertyStore *props = NULL; PROPVARIANT prop_fname; PropVariantInit(&prop_fname); TRYHR(devcoll->Item(i, &tmpdev)); TRYHR(tmpdev->OpenPropertyStore(STGM_READ, &props)); TRYHR(props->GetValue(LV_PKEY_Device_FriendlyName, &prop_fname)); printf("Device: %S\n", prop_fname.pwszVal); if(!wcscmp(id16, prop_fname.pwszVal)) w->immdev = tmpdev; else { tmpdev->Release(); tmpdev = NULL; } PropVariantClear(&prop_fname); if(props != NULL) { props->Release(); props = NULL; } } if(id16 != NULL) { free(id16); id16 = NULL; } if(devcoll != NULL) { devcoll->Release(); devcoll = NULL; } if(w->immdev == NULL) { puts("Device not found!"); Cleanup(dev); return(NULL); } } TRYHR(w->immdev->Activate(LV_IID_IAudioClient, CLSCTX_ALL, NULL, (void**)&w->ac)); { WAVEFORMATEX *mf = NULL; TRYHR(w->ac->GetMixFormat(&mf)); memcpy(&wfe, mf, std::min<unsigned>(sizeof(WAVEFORMATEXTENSIBLE), sizeof(WAVEFORMATEX) + mf->cbSize)); if(wfe.Format.cbSize > 22) wfe.Format.cbSize = 22; wfe.Format.wBitsPerSample = 16; wfe.Format.nBlockAlign = (wfe.Format.nChannels * wfe.Format.wBitsPerSample) / 8; wfe.Format.nAvgBytesPerSec = wfe.Format.nSamplesPerSec * wfe.Format.nBlockAlign; if(wfe.Format.wFormatTag == WAVE_FORMAT_EXTENSIBLE) { wfe.Samples.wValidBitsPerSample = wfe.Format.wBitsPerSample; wfe.SubFormat = LV_KSDATAFORMAT_SUBTYPE_PCM; } else { wfe.Format.wFormatTag = WAVE_FORMAT_PCM; } CoTaskMemFree(mf); } format->rate = wfe.Format.nSamplesPerSec; format->sampformat = ((wfe.Format.wBitsPerSample >> 3) << 4) | 1; if(wfe.Format.wBitsPerSample == 32) format->sampformat |= 2; format->channels = wfe.Format.nChannels; format->revbyteorder = false; format->noninterleaved = false; // // // { REFERENCE_TIME raw_ac_latency; int32_t des_effbuftime = buffering->ms ? buffering->ms : 52; int32_t des_effbufsize = (int64_t)des_effbuftime * wfe.Format.nSamplesPerSec / 1000; int32_t des_realbuftime = des_effbuftime + 40; //printf("%u\n", wfe.Format.wFormatTag); //printf("%u\n", wfe.Format.nChannels); //printf("%u\n", wfe.Format.nSamplesPerSec); //printf("%u\n", wfe.Format.wBitsPerSample); //printf("%u\n", wfe.Format.nBlockAlign); //printf("%u\n", wfe.Format.nAvgBytesPerSec); TRYHR(w->ac->Initialize(AUDCLNT_SHAREMODE_SHARED, AUDCLNT_STREAMFLAGS_EVENTCALLBACK, (REFERENCE_TIME)des_realbuftime * 10000, 0, (WAVEFORMATEX*)&wfe, NULL)); TRYHR(w->ac->GetBufferSize(&w->bfc)); TRYHR(w->ac->GetStreamLatency(&raw_ac_latency)); w->BufferBPF = wfe.Format.wBitsPerSample / 8 * wfe.Format.nChannels; buffering->buffer_size = std::min<int32_t>(des_effbufsize, w->bfc); buffering->period_size = 0; buffering->latency = buffering->buffer_size + (((int64_t)raw_ac_latency * format->rate + 5000000) / 10000000); buffering->bt_gran = 0; } if(!(w->evt = CreateEvent(NULL, FALSE, FALSE, NULL))) { printf("Error creating event.\n"); Cleanup(dev); return(NULL); } if((w->avrt_dll = LoadLibrary("avrt.dll")) != NULL) { if(!(w->p_AvSetMmThreadCharacteristicsA = (HANDLE WINAPI (*)(LPCSTR, LPDWORD))(GetProcAddress(w->avrt_dll, "AvSetMmThreadCharacteristicsA")))) { printf("Error getting pointer to AvSetMmThreadCharacteristicsA.\n"); Cleanup(dev); return(NULL); } if(!(w->p_AvRevertMmThreadCharacteristics = (BOOL WINAPI (*)(HANDLE))(GetProcAddress(w->avrt_dll, "AvRevertMmThreadCharacteristics")))) { printf("Error getting pointer to AvRevertMmThreadCharacteristics.\n"); Cleanup(dev); return(NULL); } } TRYHR(w->ac->SetEventHandle(w->evt)); TRYHR(w->ac->GetService(LV_IID_IAudioRenderClient, (void**)&w->arc)); memcpy(&dev->buffering, buffering, sizeof(SexyAL_buffering)); memcpy(&dev->format, format, sizeof(SexyAL_format)); dev->RawWrite = RawWrite; dev->RawCanWrite = RawCanWrite; dev->RawClose = Close; dev->Pause = Pause; // // Clear buffer. // { BYTE *bd; TRYHR(w->arc->GetBuffer(w->bfc, &bd)); memset(bd, 0, w->bfc * w->BufferBPF); w->arc->ReleaseBuffer(w->bfc, 0); } #if 0 { UINT32 paddie = 0; printf("buffer_size=%u\n", buffering->buffer_size); printf("bfc=%u\n", w->bfc); w->ac->GetCurrentPadding(&paddie); printf("paddie=%u\n", paddie); printf("snoo=%d\n", (int)buffering->buffer_size - paddie); } #endif TRYHR(w->ac->Start()); if(QueryPerformanceFrequency(&w->qpc_freq) == 0) { printf("QueryPerformanceFrequency() failed.\n"); Cleanup(dev); return(NULL); } //printf("qpc_freq=%f\n", (double)w->qpc_freq.QuadPart); w->AThreadRunning = true; if(!(w->AThread = CreateThread(NULL, 0, AThreadMain, dev, CREATE_SUSPENDED, NULL))) { printf("Error creating thread.\n"); Cleanup(dev); return(NULL); } InitializeCriticalSection(&w->crit); ResumeThread(w->AThread); return(dev); }
HRESULT list_devices() { HRESULT hr = S_OK; // get an enumerator IMMDeviceEnumerator *pMMDeviceEnumerator; hr = CoCreateInstance( __uuidof(MMDeviceEnumerator), NULL, CLSCTX_ALL, __uuidof(IMMDeviceEnumerator), (void**)&pMMDeviceEnumerator ); if (FAILED(hr)) { printf("CoCreateInstance(IMMDeviceEnumerator) failed: hr = 0x%08x\n", hr); return hr; } IMMDeviceCollection *pMMDeviceCollection; // get all the active render endpoints hr = pMMDeviceEnumerator->EnumAudioEndpoints( eRender, DEVICE_STATE_ACTIVE, &pMMDeviceCollection ); pMMDeviceEnumerator->Release(); if (FAILED(hr)) { printf("IMMDeviceEnumerator::EnumAudioEndpoints failed: hr = 0x%08x\n", hr); return hr; } UINT count; hr = pMMDeviceCollection->GetCount(&count); if (FAILED(hr)) { pMMDeviceCollection->Release(); printf("IMMDeviceCollection::GetCount failed: hr = 0x%08x\n", hr); return hr; } printf("Active render endpoints found: %u\n", count); for (UINT i = 0; i < count; i++) { IMMDevice *pMMDevice; // get the "n"th device hr = pMMDeviceCollection->Item(i, &pMMDevice); if (FAILED(hr)) { pMMDeviceCollection->Release(); printf("IMMDeviceCollection::Item failed: hr = 0x%08x\n", hr); return hr; } // open the property store on that device IPropertyStore *pPropertyStore; hr = pMMDevice->OpenPropertyStore(STGM_READ, &pPropertyStore); pMMDevice->Release(); if (FAILED(hr)) { pMMDeviceCollection->Release(); printf("IMMDevice::OpenPropertyStore failed: hr = 0x%08x\n", hr); return hr; } // get the long name property PROPVARIANT pv; PropVariantInit(&pv); hr = pPropertyStore->GetValue(PKEY_Device_FriendlyName, &pv); pPropertyStore->Release(); if (FAILED(hr)) { pMMDeviceCollection->Release(); printf("IPropertyStore::GetValue failed: hr = 0x%08x\n", hr); return hr; } if (VT_LPWSTR != pv.vt) { printf("PKEY_Device_FriendlyName variant type is %u - expected VT_LPWSTR", pv.vt); PropVariantClear(&pv); pMMDeviceCollection->Release(); return E_UNEXPECTED; } printf(" %ls\n", pv.pwszVal); PropVariantClear(&pv); } pMMDeviceCollection->Release(); return S_OK; }
HRESULT get_specific_device(LPCWSTR szLongName, IMMDevice **ppMMDevice) { HRESULT hr = S_OK; *ppMMDevice = NULL; // get an enumerator IMMDeviceEnumerator *pMMDeviceEnumerator; hr = CoCreateInstance( __uuidof(MMDeviceEnumerator), NULL, CLSCTX_ALL, __uuidof(IMMDeviceEnumerator), (void**)&pMMDeviceEnumerator ); if (FAILED(hr)) { printf("CoCreateInstance(IMMDeviceEnumerator) failed: hr = 0x%08x\n", hr); return hr; } IMMDeviceCollection *pMMDeviceCollection; // get all the active render endpoints hr = pMMDeviceEnumerator->EnumAudioEndpoints( eRender, DEVICE_STATE_ACTIVE, &pMMDeviceCollection ); pMMDeviceEnumerator->Release(); if (FAILED(hr)) { printf("IMMDeviceEnumerator::EnumAudioEndpoints failed: hr = 0x%08x\n", hr); return hr; } UINT count; hr = pMMDeviceCollection->GetCount(&count); if (FAILED(hr)) { pMMDeviceCollection->Release(); printf("IMMDeviceCollection::GetCount failed: hr = 0x%08x\n", hr); return hr; } for (UINT i = 0; i < count; i++) { IMMDevice *pMMDevice; // get the "n"th device hr = pMMDeviceCollection->Item(i, &pMMDevice); if (FAILED(hr)) { pMMDeviceCollection->Release(); printf("IMMDeviceCollection::Item failed: hr = 0x%08x\n", hr); return hr; } // open the property store on that device IPropertyStore *pPropertyStore; hr = pMMDevice->OpenPropertyStore(STGM_READ, &pPropertyStore); if (FAILED(hr)) { pMMDevice->Release(); pMMDeviceCollection->Release(); printf("IMMDevice::OpenPropertyStore failed: hr = 0x%08x\n", hr); return hr; } // get the long name property PROPVARIANT pv; PropVariantInit(&pv); hr = pPropertyStore->GetValue(PKEY_Device_FriendlyName, &pv); pPropertyStore->Release(); if (FAILED(hr)) { pMMDevice->Release(); pMMDeviceCollection->Release(); printf("IPropertyStore::GetValue failed: hr = 0x%08x\n", hr); return hr; } if (VT_LPWSTR != pv.vt) { printf("PKEY_Device_FriendlyName variant type is %u - expected VT_LPWSTR", pv.vt); PropVariantClear(&pv); pMMDevice->Release(); pMMDeviceCollection->Release(); return E_UNEXPECTED; } // is it a match? if (0 == _wcsicmp(pv.pwszVal, szLongName)) { // did we already find it? if (NULL == *ppMMDevice) { *ppMMDevice = pMMDevice; pMMDevice->AddRef(); } else { printf("Found (at least) two devices named %ls\n", szLongName); PropVariantClear(&pv); pMMDevice->Release(); pMMDeviceCollection->Release(); return E_UNEXPECTED; } } pMMDevice->Release(); PropVariantClear(&pv); } pMMDeviceCollection->Release(); if (NULL == *ppMMDevice) { printf("Could not find a device named %ls\n", szLongName); return HRESULT_FROM_WIN32(ERROR_NOT_FOUND); } return S_OK; }
int _tmain(int argc, _TCHAR* argv[]) { // read the command line option, -1 indicates list devices. int option = -1; if (argc == 2) option = atoi((char*)argv[1]); std::wcout << "SoundSwitch Audio Interface Changer\r\n"; bool setOutput = false; HRESULT hr = CoInitialize(NULL); if (SUCCEEDED(hr)) { IMMDeviceEnumerator *pEnum = NULL; // Create a multimedia device enumerator. hr = CoCreateInstance(__uuidof(MMDeviceEnumerator), NULL, CLSCTX_ALL, __uuidof(IMMDeviceEnumerator), (void**)&pEnum); if (SUCCEEDED(hr)) { LPWSTR wstrDefaultID = NULL; hr = GetDefaultDeviceID(pEnum, &wstrDefaultID); if(FAILED(hr)) { wstrDefaultID = NULL; } IMMDeviceCollection *pDevices; // Get default device IMMDevice *pDevice = NULL; hr = pEnum->GetDefaultAudioEndpoint(eRender, eConsole, &pDevice); // Enumerate the output devices. hr = pEnum->EnumAudioEndpoints(eRender, DEVICE_STATE_ACTIVE | DEVICE_STATE_UNPLUGGED | DEVICE_STATE_DISABLED, &pDevices); if (SUCCEEDED(hr)) { UINT count; pDevices->GetCount(&count); if (SUCCEEDED(hr)) { for (int i = 0; i < count; i++) { IMMDevice *pDevice; hr = pDevices->Item(i, &pDevice); if (SUCCEEDED(hr)) { LPWSTR wstrID = NULL; hr = pDevice->GetId(&wstrID); if (SUCCEEDED(hr)) { IPropertyStore *pStore; hr = pDevice->OpenPropertyStore(STGM_READ, &pStore); if (SUCCEEDED(hr)) { bool isDefault = false; if(wstrDefaultID != NULL) { LPWSTR wstrID = NULL; HRESULT hr = pDevice->GetId(&wstrID); if(!_wcsicmp(wstrID, wstrDefaultID)) { isDefault = true; } CoTaskMemFree(wstrID); } PROPVARIANT friendlyName; PropVariantInit(&friendlyName); hr = pStore->GetValue(PKEY_Device_FriendlyName, &friendlyName); if (SUCCEEDED(hr)) { // if no options, print the device // otherwise, find the selected device and set it to be default if (option == -1) { //printf("Audio Device %d: %ws\n",i, friendlyName.pwszVal); std::wcout << i << ": " << friendlyName.pwszVal; if (isDefault == true) { std::wcout << " (default)"; } std::wcout << "\r\n"; } if (i == option) { std::wcout << "Setting output to: " << i << ": " << friendlyName.pwszVal << "\r\n"; SetDefaultAudioPlaybackDevice(wstrID); setOutput = true; } PropVariantClear(&friendlyName); } pStore->Release(); } } pDevice->Release(); } } } pDevices->Release(); } //WTF? If Release() gets called, then redirecting this .exe's output stops working. No idea why.. // pEnum->Release(); } } if (option >= 0 && !setOutput) { std::cout << "Failed to select device " << option << "\r\n"; return 1; } return hr; }