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; }
void VolumeControl::init() { //initialize audio mixer interface #if defined (__APPLE__) #warning TODO: Not implemented for MacOS yet!!! #elif defined(__linux__) //try to open mixer device if (mixerHandle == nullptr) { snd_mixer_selem_id_alloca(&mixerSelemId); //sets simple-mixer index and name snd_mixer_selem_id_set_index(mixerSelemId, mixerIndex); snd_mixer_selem_id_set_name(mixerSelemId, mixerName); //open mixer if (snd_mixer_open(&mixerHandle, 0) >= 0) { LOG(LogDebug) << "VolumeControl::init() - Opened ALSA mixer"; //ok. attach to defualt card if (snd_mixer_attach(mixerHandle, mixerCard) >= 0) { LOG(LogDebug) << "VolumeControl::init() - Attached to default card"; //ok. register simple element class if (snd_mixer_selem_register(mixerHandle, NULL, NULL) >= 0) { LOG(LogDebug) << "VolumeControl::init() - Registered simple element class"; //ok. load registered elements if (snd_mixer_load(mixerHandle) >= 0) { LOG(LogDebug) << "VolumeControl::init() - Loaded mixer elements"; //ok. find elements now mixerElem = snd_mixer_find_selem(mixerHandle, mixerSelemId); if (mixerElem != nullptr) { //wohoo. good to go... LOG(LogDebug) << "VolumeControl::init() - Mixer initialized"; } else { LOG(LogError) << "VolumeControl::init() - Failed to find mixer elements!"; snd_mixer_close(mixerHandle); mixerHandle = nullptr; } } else { LOG(LogError) << "VolumeControl::init() - Failed to load mixer elements!"; snd_mixer_close(mixerHandle); mixerHandle = nullptr; } } else { LOG(LogError) << "VolumeControl::init() - Failed to register simple element class!"; snd_mixer_close(mixerHandle); mixerHandle = nullptr; } } else { LOG(LogError) << "VolumeControl::init() - Failed to attach to default card!"; snd_mixer_close(mixerHandle); mixerHandle = nullptr; } } else { LOG(LogError) << "VolumeControl::init() - Failed to open ALSA mixer!"; } } #elif defined(WIN32) || defined(_WIN32) //get windows version information OSVERSIONINFOEXA osVer = {sizeof(OSVERSIONINFO)}; ::GetVersionExA(reinterpret_cast<LPOSVERSIONINFOA>(&osVer)); //check windows version if(osVer.dwMajorVersion < 6) { //Windows older than Vista. use mixer API. open default mixer if (mixerHandle == nullptr) { if (mixerOpen(&mixerHandle, 0, NULL, 0, 0) == MMSYSERR_NOERROR) { //retrieve info on the volume slider control for the "Speaker Out" line MIXERLINECONTROLS mixerLineControls; mixerLineControls.cbStruct = sizeof(MIXERLINECONTROLS); mixerLineControls.dwLineID = 0xFFFF0000; //Id of "Speaker Out" line mixerLineControls.cControls = 1; //mixerLineControls.dwControlID = 0x00000000; //Id of "Speaker Out" line's volume slider mixerLineControls.dwControlType = MIXERCONTROL_CONTROLTYPE_VOLUME; //Get volume control mixerLineControls.pamxctrl = &mixerControl; mixerLineControls.cbmxctrl = sizeof(MIXERCONTROL); if (mixerGetLineControls((HMIXEROBJ)mixerHandle, &mixerLineControls, MIXER_GETLINECONTROLSF_ONEBYTYPE) != MMSYSERR_NOERROR) { LOG(LogError) << "VolumeControl::getVolume() - Failed to get mixer volume control!"; mixerClose(mixerHandle); mixerHandle = nullptr; } } else { LOG(LogError) << "VolumeControl::init() - Failed to open mixer!"; } } } else { //Windows Vista or above. use EndpointVolume API. get device enumerator if (endpointVolume == nullptr) { CoInitialize(nullptr); IMMDeviceEnumerator * deviceEnumerator = nullptr; CoCreateInstance(__uuidof(MMDeviceEnumerator), nullptr, CLSCTX_INPROC_SERVER, __uuidof(IMMDeviceEnumerator), (LPVOID *)&deviceEnumerator); if (deviceEnumerator != nullptr) { //get default endpoint IMMDevice * defaultDevice = nullptr; deviceEnumerator->GetDefaultAudioEndpoint(eRender, eConsole, &defaultDevice); if (defaultDevice != nullptr) { //retrieve endpoint volume defaultDevice->Activate(__uuidof(IAudioEndpointVolume), CLSCTX_INPROC_SERVER, nullptr, (LPVOID *)&endpointVolume); if (endpointVolume == nullptr) { LOG(LogError) << "VolumeControl::init() - Failed to get default audio endpoint volume!"; } //release default device. we don't need it anymore defaultDevice->Release(); } else { LOG(LogError) << "VolumeControl::init() - Failed to get default audio endpoint!"; } //release device enumerator. we don't need it anymore deviceEnumerator->Release(); } else { LOG(LogError) << "VolumeControl::init() - Failed to get audio endpoint enumerator!"; CoUninitialize(); } } } #endif }
bool EnumBuiltinDevices_w64(const uint32_t deviceType, STUDIO_LINK_DEVICE_LIST* devices) { PRECONDITION_RETURN(deviceType != INVALID_DEVICE_TYPE, false); PRECONDITION_RETURN(devices != 0, false); bool result = false; HRESULT hr = CoInitialize(0); if(SUCCEEDED(hr)) { IMMDeviceEnumerator* pDeviceEnumerator = 0; hr = CoCreateInstance(CLSID_MMDeviceEnumerator, 0, CLSCTX_INPROC_SERVER, IID_IMMDeviceEnumerator, (void**)&pDeviceEnumerator); if(SUCCEEDED(hr) && (pDeviceEnumerator != 0)) { EDataFlow dataFlow = eAll; if(deviceType == HEADPHONE) { dataFlow = eRender; } else if(deviceType == MICROPHONE) { dataFlow = eCapture; } IMMDeviceCollection* pDeviceCollection = 0; hr = pDeviceEnumerator->EnumAudioEndpoints(dataFlow, DEVICE_STATE_ACTIVE, &pDeviceCollection); if(SUCCEEDED(hr) && (pDeviceCollection != 0)) { UINT deviceCount = 0; hr = pDeviceCollection->GetCount(&deviceCount); if(SUCCEEDED(hr)) { size_t foundDevices = 0; for(UINT i = 0; (i < deviceCount) && (foundDevices < deviceCount); i++) { IMMDevice* pDevice = 0; hr = pDeviceCollection->Item(i, &pDevice); if(SUCCEEDED(hr) && (pDevice != 0)) { IPropertyStore* pProperties = 0; hr = pDevice->OpenPropertyStore(STGM_READ, &pProperties); if(SUCCEEDED(hr)) { PROPVARIANT deviceFormatProperty; PropVariantInit(&deviceFormatProperty); hr = pProperties->GetValue(PKEY_AudioEngine_DeviceFormat, &deviceFormatProperty); if(SUCCEEDED(hr) && (VT_BLOB == deviceFormatProperty.vt)) { WAVEFORMATEX* deviceFormat = (WAVEFORMATEX*)deviceFormatProperty.blob.pBlobData; devices->devices[foundDevices].channelCount = deviceFormat->nChannels; devices->devices[foundDevices].sampleRate = deviceFormat->nSamplesPerSec; if((WAVE_FORMAT_EXTENSIBLE == deviceFormat->wFormatTag) && (deviceFormat->cbSize >= 22)) { //WAVEFORMATEXTENSIBLE* extensibleDeviceFormat = (WAVEFORMATEXTENSIBLE*)deviceFormat; } PropVariantClear(&deviceFormatProperty); PROPVARIANT deviceNameProperty; PropVariantInit(&deviceNameProperty); hr = pProperties->GetValue(PKEY_Device_FriendlyName, &deviceNameProperty); if(SUCCEEDED(hr)) { PWSTR deviceName = 0; hr = PropVariantToStringAlloc(deviceNameProperty, &deviceName); if(SUCCEEDED(hr)) { memset(devices->devices[foundDevices].name, 0, STUDIO_LINK_DEVICE_NAME_LENGTH); size_t numOfCharsConverted = 0; const size_t deviceNameLength = wcslen(deviceName); wcstombs_s(&numOfCharsConverted, devices->devices[foundDevices].name, STUDIO_LINK_DEVICE_NAME_LENGTH, deviceName, deviceNameLength); foundDevices++; CoTaskMemFree(deviceName); deviceName = 0; } PropVariantClear(&deviceNameProperty); } } SafeRelease(pProperties); } SafeRelease(pDevice); } } devices->deviceCount = foundDevices; } SafeRelease(pDeviceCollection); } SafeRelease(pDeviceEnumerator); } CoUninitialize(); } if(devices->deviceCount > 0) { result = true; } return result; }
Error AudioDriverWASAPI::init_device() { WAVEFORMATEX *pwfex; IMMDeviceEnumerator *enumerator = NULL; IMMDevice *device = NULL; CoInitialize(NULL); HRESULT hr = CoCreateInstance(CLSID_MMDeviceEnumerator, NULL, CLSCTX_ALL, IID_IMMDeviceEnumerator, (void **)&enumerator); ERR_FAIL_COND_V(hr != S_OK, ERR_CANT_OPEN); hr = enumerator->GetDefaultAudioEndpoint(eRender, eConsole, &device); ERR_FAIL_COND_V(hr != S_OK, ERR_CANT_OPEN); hr = device->Activate(IID_IAudioClient, CLSCTX_ALL, NULL, (void **)&audio_client); ERR_FAIL_COND_V(hr != S_OK, ERR_CANT_OPEN); hr = audio_client->GetMixFormat(&pwfex); ERR_FAIL_COND_V(hr != S_OK, ERR_CANT_OPEN); // Since we're using WASAPI Shared Mode we can't control any of these, we just tag along channels = pwfex->nChannels; mix_rate = pwfex->nSamplesPerSec; format_tag = pwfex->wFormatTag; bits_per_sample = pwfex->wBitsPerSample; switch (channels) { case 2: // Stereo case 4: // Surround 3.1 case 6: // Surround 5.1 case 8: // Surround 7.1 break; default: ERR_PRINTS("WASAPI: Unsupported number of channels: " + itos(channels)); ERR_FAIL_V(ERR_CANT_OPEN); break; } if (format_tag == WAVE_FORMAT_EXTENSIBLE) { WAVEFORMATEXTENSIBLE *wfex = (WAVEFORMATEXTENSIBLE *)pwfex; if (wfex->SubFormat == KSDATAFORMAT_SUBTYPE_PCM) { format_tag = WAVE_FORMAT_PCM; } else if (wfex->SubFormat == KSDATAFORMAT_SUBTYPE_IEEE_FLOAT) { format_tag = WAVE_FORMAT_IEEE_FLOAT; } else { ERR_PRINT("WASAPI: Format not supported"); ERR_FAIL_V(ERR_CANT_OPEN); } } else { if (format_tag != WAVE_FORMAT_PCM && format_tag != WAVE_FORMAT_IEEE_FLOAT) { ERR_PRINT("WASAPI: Format not supported"); ERR_FAIL_V(ERR_CANT_OPEN); } } hr = audio_client->Initialize(AUDCLNT_SHAREMODE_SHARED, AUDCLNT_STREAMFLAGS_EVENTCALLBACK, 0, 0, pwfex, NULL); ERR_FAIL_COND_V(hr != S_OK, ERR_CANT_OPEN); event = CreateEvent(NULL, FALSE, FALSE, NULL); ERR_FAIL_COND_V(event == NULL, ERR_CANT_OPEN); hr = audio_client->SetEventHandle(event); ERR_FAIL_COND_V(hr != S_OK, ERR_CANT_OPEN); hr = audio_client->GetService(IID_IAudioRenderClient, (void **)&render_client); ERR_FAIL_COND_V(hr != S_OK, ERR_CANT_OPEN); UINT32 max_frames; hr = audio_client->GetBufferSize(&max_frames); ERR_FAIL_COND_V(hr != S_OK, ERR_CANT_OPEN); // Due to WASAPI Shared Mode we have no control of the buffer size buffer_frames = max_frames; // Sample rate is independent of channels (ref: https://stackoverflow.com/questions/11048825/audio-sample-frequency-rely-on-channels) buffer_size = buffer_frames * channels; samples_in.resize(buffer_size); if (OS::get_singleton()->is_stdout_verbose()) { print_line("WASAPI: detected " + itos(channels) + " channels"); print_line("WASAPI: audio buffer frames: " + itos(buffer_frames) + " calculated latency: " + itos(buffer_frames * 1000 / mix_rate) + "ms"); } return OK; }
void modifyVolume(char ch) { HRESULT hr; bool decibels = false; bool scalar = true; double newVolume = 10; // ------------------------- CoInitialize(NULL); IMMDeviceEnumerator *deviceEnumerator = NULL; hr = CoCreateInstance(__uuidof(MMDeviceEnumerator), NULL, CLSCTX_INPROC_SERVER, __uuidof(IMMDeviceEnumerator), (LPVOID *)&deviceEnumerator); IMMDevice *defaultDevice = NULL; hr = deviceEnumerator->GetDefaultAudioEndpoint(eRender, eMultimedia, &defaultDevice); deviceEnumerator->Release(); deviceEnumerator = NULL; IAudioEndpointVolume *endpointVolume = NULL; hr = defaultDevice->Activate(__uuidof(IAudioEndpointVolume), CLSCTX_INPROC_SERVER, NULL, (LPVOID *)&endpointVolume); defaultDevice->Release(); defaultDevice = NULL; // ------------------------- float currentVolume = 0; endpointVolume->GetMasterVolumeLevel(¤tVolume); //printf("Current volume in dB is: %f\n", currentVolume); hr = endpointVolume->GetMasterVolumeLevelScalar(¤tVolume); float min, max, inc; endpointVolume->GetVolumeRange(&min, &max, &inc); //printf("Current volume as a scalar is: %f\n", currentVolume); if (ch == 'u') newVolume = currentVolume + 0.1; else if (ch == 'd') newVolume = currentVolume - 0.1; if (ch == 'm') { endpointVolume->SetMute(TRUE, NULL); } else if (ch == 'n') { endpointVolume->SetMute(FALSE, NULL); } else { if (decibels) { hr = endpointVolume->SetMasterVolumeLevel((float)newVolume, NULL); //endpointVolume->VolumeStepUp(NULL); } else if (scalar) { hr = endpointVolume->SetMasterVolumeLevelScalar((float)newVolume, NULL); //endpointVolume->VolumeStepUp(NULL); } } endpointVolume->Release(); CoUninitialize(); }
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); }
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; }
int __cdecl wmain(void) { HRESULT hr = CoInitialize(NULL); if (FAILED(hr)) { LOG(L"Failed CoInitializeEx: hr = 0x%08x", hr); return __LINE__; } CoUninitializeOnExit cuoe; // get active devices IMMDeviceEnumerator *pEnum = NULL; hr = CoCreateInstance( __uuidof(MMDeviceEnumerator), NULL, CLSCTX_ALL, __uuidof(IMMDeviceEnumerator), (void**)&pEnum ); if (FAILED(hr)) { LOG(L"Couldn't get device enumerator: hr = 0x%08x", hr); return __LINE__; } ReleaseOnExit releaseEnum(pEnum); IMMDeviceCollection *pDeviceCollection = NULL; hr = pEnum->EnumAudioEndpoints(eAll, DEVICE_STATE_ACTIVE | DEVICE_STATE_UNPLUGGED, &pDeviceCollection); if (FAILED(hr)) { LOG(L"Couldn't get device collection: hr = 0x%08x", hr); return __LINE__; } ReleaseOnExit releaseDeviceCollection(pDeviceCollection); UINT nDevices = 0; hr = pDeviceCollection->GetCount(&nDevices); if (FAILED(hr)) { LOG(L"Couldn't get device collection count: hr = 0x%08x", hr); return __LINE__; } for (UINT i = 0; i < nDevices; i++) { IMMDevice *pDevice = NULL; hr = pDeviceCollection->Item(i, &pDevice); if (FAILED(hr)) { LOG(L"Couldn't get device: hr = 0x%08x", hr); return __LINE__; } ReleaseOnExit releaseDevice(pDevice); IMMEndpoint *pEndpoint = NULL; hr = pDevice->QueryInterface(IID_PPV_ARGS(&pEndpoint)); if (FAILED(hr)) { LOG(L"Couldn't get endpoint: hr = 0x%08x", hr); return __LINE__; } ReleaseOnExit releaseEndpoint(pEndpoint); EDataFlow eDirection = eAll; hr = pEndpoint->GetDataFlow(&eDirection); if (FAILED(hr)) { LOG(L"Couldn't get data flow: hr = 0x%08x", hr); return __LINE__; } LOG(L"%s endpoint", StringFromDataFlow(eDirection)); IPropertyStore *pPropertyStore = NULL; hr = pDevice->OpenPropertyStore(STGM_READ, &pPropertyStore); if (FAILED(hr)) { LOG(L"Couldn't get property store: hr = 0x%08x", hr); return __LINE__; } ReleaseOnExit releasePropertyStore(pPropertyStore); // get the long name property PROPVARIANT pv; PropVariantInit(&pv); hr = pPropertyStore->GetValue(PKEY_Device_FriendlyName, &pv); if (FAILED(hr)) { LOG(L"Couldn't get friendly name: hr = 0x%08x", hr); return hr; } PropVariantClearOnExit clearPropVariant(&pv); LOG(L"Name: %s", pv.pwszVal); // get the ID WCHAR *wszId = NULL; hr = pDevice->GetId(&wszId); if (FAILED(hr)) { LOG(L"Couldn't get device ID: hr = 0x%08x", hr); return __LINE__; } CoTaskMemFreeOnExit releaseId(wszId); LOG(L"Endpoint ID: %s", wszId); // get device topology object for that endpoint IDeviceTopology *pDT = NULL; hr = pDevice->Activate(__uuidof(IDeviceTopology), CLSCTX_ALL, NULL, (void**)&pDT); if (FAILED(hr)) { LOG(L"Couldn't get device topology object: hr = 0x%08x", hr); return __LINE__; } ReleaseOnExit releaseDT(pDT); // get the single connector for that endpoint IConnector *pConnector = NULL; hr = pDT->GetConnector(0, &pConnector); if (FAILED(hr)) { LOG(L"Couldn't get the connector on the endpoint: hr = 0x%08x", hr); return __LINE__; } ReleaseOnExit releaseConnector(pConnector); // QI on the device's connector for IPart IPart *pPart = NULL; hr = pConnector->QueryInterface(IID_PPV_ARGS(&pPart)); if (FAILED(hr)) { LOG(L"Couldn't get the part: hr = 0x%08x", hr); return __LINE__; } ReleaseOnExit releasePart(pPart); // all the real work is done in this function // follow the connector from this trivial endpoint topology // over to the rest of the topologies hr = WalkTreeFromPart(pPart, eDirection, true, 1); if (FAILED(hr)) { LOG(L"Couldn't walk the tree: hr = 0x%08x", hr); return __LINE__; } LOG(L""); } return 0; }
Error AudioDriverWASAPI::audio_device_init(AudioDeviceWASAPI *p_device, bool p_capture, bool reinit) { WAVEFORMATEX *pwfex; IMMDeviceEnumerator *enumerator = NULL; IMMDevice *device = NULL; CoInitialize(NULL); HRESULT hr = CoCreateInstance(CLSID_MMDeviceEnumerator, NULL, CLSCTX_ALL, IID_IMMDeviceEnumerator, (void **)&enumerator); ERR_FAIL_COND_V(hr != S_OK, ERR_CANT_OPEN); if (p_device->device_name == "Default") { hr = enumerator->GetDefaultAudioEndpoint(p_capture ? eCapture : eRender, eConsole, &device); } else { IMMDeviceCollection *devices = NULL; hr = enumerator->EnumAudioEndpoints(p_capture ? eCapture : eRender, DEVICE_STATE_ACTIVE, &devices); ERR_FAIL_COND_V(hr != S_OK, ERR_CANT_OPEN); LPWSTR strId = NULL; bool found = false; UINT count = 0; hr = devices->GetCount(&count); ERR_FAIL_COND_V(hr != S_OK, ERR_CANT_OPEN); for (ULONG i = 0; i < count && !found; i++) { IMMDevice *device = NULL; hr = devices->Item(i, &device); ERR_BREAK(hr != S_OK); IPropertyStore *props = NULL; hr = device->OpenPropertyStore(STGM_READ, &props); ERR_BREAK(hr != S_OK); PROPVARIANT propvar; PropVariantInit(&propvar); hr = props->GetValue(PKEY_Device_FriendlyName, &propvar); ERR_BREAK(hr != S_OK); if (p_device->device_name == String(propvar.pwszVal)) { hr = device->GetId(&strId); ERR_BREAK(hr != S_OK); found = true; } PropVariantClear(&propvar); props->Release(); device->Release(); } if (found) { hr = enumerator->GetDevice(strId, &device); } if (strId) { CoTaskMemFree(strId); } if (device == NULL) { hr = enumerator->GetDefaultAudioEndpoint(p_capture ? eCapture : eRender, eConsole, &device); } } if (reinit) { // In case we're trying to re-initialize the device prevent throwing this error on the console, // otherwise if there is currently no device available this will spam the console. if (hr != S_OK) { return ERR_CANT_OPEN; } } else { ERR_FAIL_COND_V(hr != S_OK, ERR_CANT_OPEN); } hr = enumerator->RegisterEndpointNotificationCallback(¬if_client); SAFE_RELEASE(enumerator) if (hr != S_OK) { ERR_PRINT("WASAPI: RegisterEndpointNotificationCallback error"); } hr = device->Activate(IID_IAudioClient, CLSCTX_ALL, NULL, (void **)&p_device->audio_client); SAFE_RELEASE(device) if (reinit) { if (hr != S_OK) { return ERR_CANT_OPEN; } } else { ERR_FAIL_COND_V(hr != S_OK, ERR_CANT_OPEN); } hr = p_device->audio_client->GetMixFormat(&pwfex); ERR_FAIL_COND_V(hr != S_OK, ERR_CANT_OPEN); // Since we're using WASAPI Shared Mode we can't control any of these, we just tag along p_device->channels = pwfex->nChannels; p_device->format_tag = pwfex->wFormatTag; p_device->bits_per_sample = pwfex->wBitsPerSample; p_device->frame_size = (p_device->bits_per_sample / 8) * p_device->channels; if (p_device->format_tag == WAVE_FORMAT_EXTENSIBLE) { WAVEFORMATEXTENSIBLE *wfex = (WAVEFORMATEXTENSIBLE *)pwfex; if (wfex->SubFormat == KSDATAFORMAT_SUBTYPE_PCM) { p_device->format_tag = WAVE_FORMAT_PCM; } else if (wfex->SubFormat == KSDATAFORMAT_SUBTYPE_IEEE_FLOAT) { p_device->format_tag = WAVE_FORMAT_IEEE_FLOAT; } else { ERR_PRINT("WASAPI: Format not supported"); ERR_FAIL_V(ERR_CANT_OPEN); } } else { if (p_device->format_tag != WAVE_FORMAT_PCM && p_device->format_tag != WAVE_FORMAT_IEEE_FLOAT) { ERR_PRINT("WASAPI: Format not supported"); ERR_FAIL_V(ERR_CANT_OPEN); } } DWORD streamflags = 0; if (mix_rate != pwfex->nSamplesPerSec) { streamflags |= AUDCLNT_STREAMFLAGS_RATEADJUST; pwfex->nSamplesPerSec = mix_rate; pwfex->nAvgBytesPerSec = pwfex->nSamplesPerSec * pwfex->nChannels * (pwfex->wBitsPerSample / 8); } hr = p_device->audio_client->Initialize(AUDCLNT_SHAREMODE_SHARED, streamflags, p_capture ? REFTIMES_PER_SEC : 0, 0, pwfex, NULL); ERR_FAIL_COND_V(hr != S_OK, ERR_CANT_OPEN); if (p_capture) { hr = p_device->audio_client->GetService(IID_IAudioCaptureClient, (void **)&p_device->capture_client); } else { hr = p_device->audio_client->GetService(IID_IAudioRenderClient, (void **)&p_device->render_client); } ERR_FAIL_COND_V(hr != S_OK, ERR_CANT_OPEN); // Free memory CoTaskMemFree(pwfex); SAFE_RELEASE(device) return OK; }
std::vector<IMMDevice*> AudioMasterMind::getActiveDevices() { CoInitialize(NULL); IMMDeviceEnumerator* deviceEnumeratorPtr = NULL; HRESULT hr = CoCreateInstance( __uuidof(MMDeviceEnumerator), NULL, CLSCTX_ALL, __uuidof(IMMDeviceEnumerator), (void**)&deviceEnumeratorPtr ); if (FAILED(hr)) { BOOST_LOG_TRIVIAL(error) << "Failed to create IMMDeviceEnumerator hr:" << hr; throw std::logic_error{ "Failed to create IMMDeviceEnumerator" }; } IMMDeviceCollection* deviceCollectionPtr = NULL; hr = deviceEnumeratorPtr->EnumAudioEndpoints(eAll, DEVICE_STATEMASK_ALL, &deviceCollectionPtr); if (FAILED(hr)) { BOOST_LOG_TRIVIAL(error) << "Failed to enumerate audio endpoints "; throw std::logic_error{ "Failed to enumerate audio endpoints" }; } UINT deviceCount = 0; hr = deviceCollectionPtr->GetCount(&deviceCount); if (FAILED(hr)) { BOOST_LOG_TRIVIAL(error) << "Failed to get the count of devices"; throw std::logic_error{ "Failed to get Device count" }; } BOOST_LOG_TRIVIAL(info) << "Device Count is: " << deviceCount; std::vector<IMMDevice*> activeDevices; for (unsigned int i = 0; i < deviceCount; ++i) { IMMDevice* devicePtr = NULL; hr = deviceCollectionPtr->Item(i, &devicePtr); if (FAILED(hr)) { BOOST_LOG_TRIVIAL(error) << "Failed to get device no. " << i; throw std::logic_error{ "Failed to get Device Ptr" }; } DWORD deviceState; hr = devicePtr->GetState(&deviceState); if (FAILED(hr)) { BOOST_LOG_TRIVIAL(error) << "Failed to get devices state: " << hr; throw std::logic_error{ "Failed to get Device State" }; } if (deviceState == DEVICE_STATE_ACTIVE) { activeDevices.push_back(devicePtr); } } return activeDevices; }
RVolumeControl::RVolumeControl(QWidget *parent) : QWidget(parent), _value(0), _min(0), _max(100), _isEnableSound(true), _isOn(true), _isDark(true), _isWinXP(false) { endpointVolume = NULL; volumeNotification = NULL; // ХАК ОТ ВАЛЕНТИНА setProperty("ignoreFilter", true); setToolTip(tr("Volume control")); BOOL mute = false; float currVolume = 0.; // Определяем версию ОС (xp или старше?) OSVERSIONINFO m_osinfo; ZeroMemory(&m_osinfo, sizeof(m_osinfo)); m_osinfo.dwOSVersionInfoSize = sizeof(OSVERSIONINFO); if (GetVersionEx((LPOSVERSIONINFO) &m_osinfo)) { if(m_osinfo.dwMajorVersion < 6) { _isWinXP = true; } } if(!_isWinXP) { CoInitialize(NULL); HRESULT hr; IMMDeviceEnumerator *deviceEnumerator = NULL; hr = CoCreateInstance(__uuidof(MMDeviceEnumerator), NULL, CLSCTX_INPROC_SERVER, __uuidof(IMMDeviceEnumerator), (LPVOID *)&deviceEnumerator); if(hr == S_OK) { IMMDevice *defaultDevice = NULL; hr = deviceEnumerator->GetDefaultAudioEndpoint(eRender, eConsole, &defaultDevice); if(hr == S_OK) { deviceEnumerator->Release(); deviceEnumerator = NULL; //endpointVolume = NULL; hr = defaultDevice->Activate(__uuidof(IAudioEndpointVolume), CLSCTX_INPROC_SERVER, NULL, (LPVOID *)&endpointVolume); if(hr == S_OK) { defaultDevice->Release(); defaultDevice = NULL; volumeNotification = new CVolumeNotification(); //connect(volumeNotification, SIGNAL(volumeChanged(double)), this, SLOT(onVolumeChange(double))); connect(volumeNotification, SIGNAL(volumeChanged(int)), this, SLOT(onVolumeChange(int))); connect(volumeNotification, SIGNAL(volumeMuted(bool)), this, SLOT(onVolumeMuted(bool))); hr = endpointVolume->RegisterControlChangeNotify(volumeNotification); endpointVolume->GetMute(&mute); endpointVolume->GetMasterVolumeLevelScalar(&currVolume); }
void changeFGWindowVolume() { if (soundOption == dncItemID) return; char titleBuff[128]; HWND activeHWND = GetForegroundWindow(); GetWindowText(activeHWND, titleBuff, 128); UINT count = sizeof(mediaCommands) / sizeof(mediaCommands[0]); for (UINT i = 0; i < count; i++) { if (strstr(titleBuff, mediaCommands[i].title) > 0) return; } IMMDevice *mmDevice; IMMDeviceEnumerator *mmDeviceEnum; IAudioSessionManager2 *sessionManager; IAudioSessionEnumerator *sessionEnum; IAudioSessionControl *sessionControl; IAudioSessionControl2 *sessionControl2; ISimpleAudioVolume *audioVolume; CoCreateInstance(__uuidof(MMDeviceEnumerator), 0, CLSCTX_ALL, __uuidof(IMMDeviceEnumerator), (void**)&mmDeviceEnum); mmDeviceEnum->GetDefaultAudioEndpoint(eRender, eMultimedia, &mmDevice); mmDevice->Activate(__uuidof(IAudioSessionManager2), CLSCTX_ALL, 0, (void**)&sessionManager); sessionManager->GetSessionEnumerator(&sessionEnum); DWORD activePid; GetWindowThreadProcessId(activeHWND, &activePid); int sessionCount; sessionEnum->GetCount(&sessionCount); for (int i = 0; i < sessionCount; i++) { sessionEnum->GetSession(i, &sessionControl); sessionControl->QueryInterface(__uuidof(IAudioSessionControl2), (void**)&sessionControl2); DWORD pid; sessionControl2->GetProcessId(&pid); if (activePid == pid) { sessionControl->QueryInterface(__uuidof(ISimpleAudioVolume), (void**)&audioVolume); BOOL muted; audioVolume->GetMute(&muted); float volumeLevel; audioVolume->GetMasterVolume(&volumeLevel); if (soundOption == muteItemID) { audioVolume->SetMute(!muted, 0); if (volumeLevel != 1.0f) { audioVolume->SetMasterVolume(1.0f, 0); } } else { float newVolumeLevel = (soundOption - sBaseItemID) / 100.0f; audioVolume->SetMasterVolume(volumeLevel == 1.0f ? newVolumeLevel : 1.0f, 0); if (muted) { audioVolume->SetMute(false, 0); } } audioVolume->Release(); } sessionControl->Release(); sessionControl2->Release(); } sessionEnum->Release(); sessionManager->Release(); mmDevice->Release(); mmDeviceEnum->Release(); }
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; }
/********************************************************************************** Function Name = LeapDesktopAppFull::volumeManipulation Descriptive Name = Increase/decrease/mute/unmute the volume Function = This functions is responsible for performing the actions of increasing/ decreasing/muting/unmuting the volume. Makes use of the available functions on windows to construct and initialize the interface of audio devices for communication. Dependencies = None Restrictions = Must have audio devices connected, running and capable of being accessed through the IMMDeviceEnumerator interface. Input = string controlOption - The volume action to be performed, supports the following options: VOLUME_STEP_UP - This option will increase the volume by 1 VOLUME_STEP_DOWN - This option will decrease the volume by 1 VOLUME_MUTE_UNMUTE - This option will mute or unmute based on what the state of the volume currently is. Output = Currently there are no outputs from this functions as it only performs the action. Future TODO is to make sure errors are handled and appropriate response is returned. Normal Return = N/A Error Return = N/A ******************************************************************************/ void LeapDesktopAppFull::volumeManipulation ( string controlOption ) { // Variable Declaration HRESULT volumeManipulationResults = NULL ; // Stores detailed information for the volume communication interface IMMDeviceEnumerator *deviceEnumerator = NULL ; // Stores the interface pointer of enumerating audio devices IMMDevice *defaultDevice = NULL ; // Stores all the default audio devices resources IAudioEndpointVolume *endpointVolume = NULL ; // Stores the volume controls on the audio streams BOOL pbMute = FALSE ; // // Initialize the COM library on the current thread CoInitialize ( NULL ) ; // Create an instance of a single uninitialized object of audio devices, a success instance creation will return "S_OK" volumeManipulationResults = CoCreateInstance ( __uuidof( MMDeviceEnumerator ), // The CLSID associated with the data and code that is used to create the object NULL, // Set to NULL to identify that object is not being created as an aggregate CLSCTX_INPROC_SERVER, // Identifies how the new object will run, in this case it is a DLL that runs in the same process __uuidof( IMMDeviceEnumerator ), // The identifier of the interface that will be used to communicate with the object ( LPVOID * ) &deviceEnumerator // Pointer that will store the interface pointer that was requested ) ; // Using the device enumerator interface get the default audio endpoint for the provided data-flow direction volumeManipulationResults = deviceEnumerator->GetDefaultAudioEndpoint ( eRender, // Set to eRender to represent an audio rendering stream eConsole, // Set to eConsole to represent the role that is assigned to the endpoint device &defaultDevice // Pointer to which the device address is stored of the endpoint object ) ; // No longer need the pointer for the device enumerator interface so release it to free up memory deviceEnumerator->Release () ; deviceEnumerator = NULL ; // Force un-initialization // Activate the device that was retrieved above, a success activation will return "S_OK" volumeManipulationResults = defaultDevice->Activate ( __uuidof( IAudioEndpointVolume ), // The interface identifier, in this case this is an audio endpoint device CLSCTX_INPROC_SERVER, // Identifies how the new object will run, in this case it is a DLL that runs in the same process NULL, // Set to NULL to activate an IAudioEndpointVolume interface on an audio endpoint device ( LPVOID * ) &endpointVolume // Pointer which stores the address of the IAudioEndpointVolume interface ) ; // No longer need the pointer for the device interface so release it to free up memory defaultDevice->Release () ; defaultDevice = NULL ; // Force un-initialization // From the endpointVolume interface get the mute status // GetMute returns TRUE for muted and FALSE for the stream not muted endpointVolume->GetMute ( &pbMute ) ; // Based on the mute status perform the action of muting or unmuting the stream if ( ( pbMute && controlOption == VOLUME_MUTE_UNMUTE ) ) { // Found that the stream is already currently muted so un-mute the stream volumeManipulationResults = endpointVolume->SetMute ( 0, NULL ) ; // Display user feed back image based on the action performed createUserFeedBackWindow ( loadResource ( RES_KEYTAP_IMAGE ), 150, 131 ) ; } else if ( !pbMute && controlOption == VOLUME_MUTE_UNMUTE ) { // Found that the stream is not mute so mute the stream volumeManipulationResults = endpointVolume->SetMute ( 1, NULL ) ; // Display user feed back image based on the action performed createUserFeedBackWindow ( loadResource ( RES_KEYTAPRELEASE_IMAGE ), 150, 131 ) ; } // Based on the volume increase/decrease option, perform the appropriate action if ( controlOption == VOLUME_STEP_UP ) { // Increase the volume on the stream by one, this also will update any connected system notifications volumeManipulationResults = endpointVolume->VolumeStepUp ( NULL ); volumeManipulationResults = endpointVolume->VolumeStepUp ( NULL ); volumeManipulationResults = endpointVolume->VolumeStepUp ( NULL ); volumeManipulationResults = endpointVolume->VolumeStepUp ( NULL ); } else if ( controlOption == VOLUME_STEP_DOWN ) { // Decrease the volume on the stream by one, this also will update any connected system notifications volumeManipulationResults = endpointVolume->VolumeStepDown ( NULL ); volumeManipulationResults = endpointVolume->VolumeStepDown ( NULL ); volumeManipulationResults = endpointVolume->VolumeStepDown ( NULL ); volumeManipulationResults = endpointVolume->VolumeStepDown ( NULL ); } // No longer need the endpointVolume interface so release it to free up memory endpointVolume->Release (); // All the actions that are to be performed on the end point device are done so un-initialize the instance CoUninitialize (); }
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(); } } } }
bool MMDeviceAudioSource::Reinitialize() { const IID IID_IAudioClient = __uuidof(IAudioClient); const IID IID_IAudioCaptureClient = __uuidof(IAudioCaptureClient); HRESULT err; bool useInputDevice = bIsMic || AppConfig->GetInt(L"Audio", L"UseInputDevices", false) != 0; if (bIsMic) { BOOL bMicSyncFixHack = GlobalConfig->GetInt(TEXT("Audio"), TEXT("UseMicSyncFixHack")); angerThreshold = bMicSyncFixHack ? 40 : 1000; } if (scmpi(deviceId, TEXT("Default")) == 0) err = mmEnumerator->GetDefaultAudioEndpoint(useInputDevice ? eCapture : eRender, useInputDevice ? eCommunications : eConsole, &mmDevice); else err = mmEnumerator->GetDevice(deviceId, &mmDevice); if(FAILED(err)) { if (!deviceLost) AppWarning(TEXT("MMDeviceAudioSource::Initialize(%d): Could not create IMMDevice = %08lX"), (BOOL)bIsMic, err); return false; } err = mmDevice->Activate(IID_IAudioClient, CLSCTX_ALL, NULL, (void**)&mmClient); if(FAILED(err)) { if (!deviceLost) AppWarning(TEXT("MMDeviceAudioSource::Initialize(%d): Could not create IAudioClient = %08lX"), (BOOL)bIsMic, 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(bIsMic) { if (!deviceLost) { 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 { if (!deviceLost) { 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)) { if (!deviceLost) AppWarning(TEXT("MMDeviceAudioSource::Initialize(%d): Could not get mix format from audio client = %08lX"), (BOOL)bIsMic, 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) { if (!deviceLost) AppWarning(TEXT("MMDeviceAudioSource::Initialize(%d): Unsupported wave format"), (BOOL)bIsMic); CoTaskMemFree(pwfx); return false; } } else if(pwfx->wFormatTag != WAVE_FORMAT_IEEE_FLOAT) { if (!deviceLost) AppWarning(TEXT("MMDeviceAudioSource::Initialize(%d): Unsupported wave format"), (BOOL)bIsMic); CoTaskMemFree(pwfx); return false; } bFloat = true; inputChannels = pwfx->nChannels; inputBitsPerSample = 32; inputBlockSize = pwfx->nBlockAlign; inputSamplesPerSec = pwfx->nSamplesPerSec; sampleWindowSize = (inputSamplesPerSec/100); DWORD flags = useInputDevice ? 0 : AUDCLNT_STREAMFLAGS_LOOPBACK; err = mmClient->Initialize(AUDCLNT_SHAREMODE_SHARED, flags, ConvertMSTo100NanoSec(5000), 0, pwfx, NULL); //err = AUDCLNT_E_UNSUPPORTED_FORMAT; if(FAILED(err)) { if (!deviceLost) AppWarning(TEXT("MMDeviceAudioSource::Initialize(%d): Could not initialize audio client, result = %08lX"), (BOOL)bIsMic, err); CoTaskMemFree(pwfx); return false; } //----------------------------------------------------------------- // acquire services err = mmClient->GetService(IID_IAudioCaptureClient, (void**)&mmCapture); if(FAILED(err)) { if (!deviceLost) AppWarning(TEXT("MMDeviceAudioSource::Initialize(%d): Could not get audio capture client, result = %08lX"), (BOOL)bIsMic, err); CoTaskMemFree(pwfx); return false; } err = mmClient->GetService(__uuidof(IAudioClock), (void**)&mmClock); if(FAILED(err)) { if (!deviceLost) AppWarning(TEXT("MMDeviceAudioSource::Initialize(%d): Could not get audio capture clock, result = %08lX"), (BOOL)bIsMic, err); CoTaskMemFree(pwfx); return false; } CoTaskMemFree(pwfx); //----------------------------------------------------------------- InitAudioData(bFloat, inputChannels, inputSamplesPerSec, inputBitsPerSample, inputBlockSize, inputChannelMask); deviceLost = false; return true; }
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); }
HRESULT CVistaVolume::SetMute(BOOL bMute) { if (m_bMute == bMute) return S_OK; HRESULT hr = E_FAIL; IMMDevice* pDevice; hr = m_pEnumerator->GetDefaultAudioEndpoint(eRender, eConsole, &pDevice); if (FAILED(hr)) return hr; IAudioSessionManager2* pasm = NULL; hr = pDevice->Activate(__uuidof(IAudioSessionManager2), CLSCTX_ALL, NULL, (void**)&pasm); if (FAILED(hr)) return hr; IAudioSessionEnumerator* audio_session_enumerator; if(SUCCEEDED(pasm->GetSessionEnumerator(&audio_session_enumerator))) { int count; if(SUCCEEDED(audio_session_enumerator->GetCount(&count))) { for (int i = 0; i < count; i++) { IAudioSessionControl* audio_session_control; IAudioSessionControl2* audio_session_control2; if(SUCCEEDED(audio_session_enumerator->GetSession(i, &audio_session_control))) { if(SUCCEEDED(audio_session_control->QueryInterface(__uuidof(IAudioSessionControl2), (void**)&audio_session_control2))) { DWORD processid; if(SUCCEEDED(audio_session_control2->GetProcessId(&processid))) { if (processid == GetCurrentProcessId()) { ISimpleAudioVolume* pSAV; hr = audio_session_control2->QueryInterface(__uuidof(ISimpleAudioVolume), (void**) &pSAV); if (SUCCEEDED(hr)) { hr = pSAV->SetMute(bMute, NULL); if (SUCCEEDED(hr)) { m_bMute = bMute; } pSAV->Release(); } } audio_session_control->Release(); audio_session_control2->Release(); } } } } audio_session_enumerator->Release(); } } pasm->Release(); return hr; }
long WasapiEngine::doInit(long rate, int const latency) { { IMMDevice *pDevice = 0; IMMDeviceEnumerator *pEnumerator = 0; HRESULT hr; if (FAILED(CoCreateInstance(CLSID_MMDeviceEnumerator, 0, CLSCTX_ALL, IID_IMMDeviceEnumerator, reinterpret_cast<void **>(&pEnumerator)))) { std::cerr << "CoCreateInstance failed" << std::endl; return -1; } if (deviceSelector->count() > 1) { hr = pEnumerator->GetDevice( deviceSelector->itemData(deviceIndex).value<std::wstring>().c_str(), &pDevice); } else hr = pEnumerator->GetDefaultAudioEndpoint(eRender, eConsole, &pDevice); pEnumerator->Release(); if (FAILED(hr)) { std::cerr << "pEnumerator->GetDefaultAudioEndpoint failed" << std::endl; return -1; } hr = pDevice->Activate(IID_IAudioClient, CLSCTX_ALL, 0, reinterpret_cast<void **>(&pAudioClient)); pDevice->Release(); if (FAILED(hr)) { std::cerr << "pDevice->Activate failed" << std::endl; return -1; } } { static GUID const ksdataformat_subtype_pcm = { WAVE_FORMAT_PCM, 0x0000, 0x0010, { 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71 } }; WAVEFORMATEXTENSIBLE wfext; std::memset(&wfext, 0, sizeof wfext); wfext.Format.cbSize = sizeof wfext - sizeof wfext.Format; wfext.Format.wFormatTag = WAVE_FORMAT_EXTENSIBLE; wfext.Format.nChannels = 2; wfext.Format.nSamplesPerSec = rate; wfext.Format.wBitsPerSample = 16; wfext.Samples.wValidBitsPerSample = 16; wfext.SubFormat = ksdataformat_subtype_pcm; wfext.dwChannelMask = SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT; if (!exclusive_.value()) { WAVEFORMATEX *mwfe = 0; if (SUCCEEDED(pAudioClient->GetMixFormat(&mwfe)) && mwfe) { wfext.Format.nChannels = std::max<int>(mwfe->nChannels, 2); wfext.Format.nSamplesPerSec = mwfe->nSamplesPerSec; if (mwfe->wFormatTag == WAVE_FORMAT_EXTENSIBLE) { wfext.dwChannelMask = reinterpret_cast<WAVEFORMATEXTENSIBLE *>(mwfe)->dwChannelMask; } CoTaskMemFree(mwfe); } else std::cerr << "pAudioClient->GetMixFormat failed\r\n"; if (!(eventHandle_ = CreateEventA(0, false, false, 0))) std::cerr << "CreateEvent failed\r\n"; } wfext.Format.nBlockAlign = wfext.Format.nChannels * wfext.Format.wBitsPerSample >> 3; wfext.Format.nAvgBytesPerSec = wfext.Format.nSamplesPerSec * wfext.Format.nBlockAlign; nchannels_ = wfext.Format.nChannels; rate = wfext.Format.nSamplesPerSec; HRESULT hr = pAudioClient->Initialize( exclusive_.value() ? AUDCLNT_SHAREMODE_EXCLUSIVE : AUDCLNT_SHAREMODE_SHARED, eventHandle_ ? AUDCLNT_STREAMFLAGS_EVENTCALLBACK : 0, latency * 10000, 0, &wfext.Format, 0); if (FAILED(hr)) { std::cerr << "pAudioClient->Initialize failed: " << hr << "\r\n"; return -1; } } if (eventHandle_ && FAILED(pAudioClient->SetEventHandle(eventHandle_))) { std::cerr << "pAudioClient->SetEventHandle failed\r\n"; return -1; } if (FAILED(pAudioClient->GetBufferSize(&bufferFrameCount))) { std::cerr << "pAudioClient->GetBufferSize failed" << std::endl; return -1; } if (FAILED(pAudioClient->GetService(IID_IAudioRenderClient, reinterpret_cast<void **>(&pRenderClient)))) { std::cerr << "pAudioClient->GetService failed" << std::endl; return -1; } if (FAILED(pAudioClient->GetService(IID_IAudioClock, reinterpret_cast<void **>(&pAudioClock)))) { std::cerr << "pAudioClient->GetService failed" << std::endl; return -1; } { UINT64 freq = 0; pAudioClock->GetFrequency(&freq); posFrames = freq / (rate ? rate : 1); } pos_ = 0; est = RateEst(rate, bufferFrameCount); return rate; }
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 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; }
// we only call this once...per hit of the play button :) HRESULT LoopbackCaptureSetup() { assert(shouldStop); // duplicate starts would be odd... shouldStop = false; // allow graphs to restart, if they so desire... pnFrames = 0; bool bInt16 = true; // makes it actually work, for some reason...LODO HRESULT hr; hr = get_default_device(&m_pMMDevice); // so it can re-place our pointer... if (FAILED(hr)) { return hr; } // tell it to not overflow one buffer's worth <sigh> not sure if this is right or not, and thus we don't "cache" or "buffer" more than that much currently... // but a buffer size is a buffer size...hmm...as long as we keep it small though... assert(expectedMaxBufferSize <= pBufOriginalSize); // activate an (the default, for us, since we want loopback) IAudioClient hr = m_pMMDevice->Activate( __uuidof(IAudioClient), CLSCTX_ALL, NULL, (void**)&pAudioClient ); if (FAILED(hr)) { ShowOutput("IMMDevice::Activate(IAudioClient) failed: hr = 0x%08x", hr); return hr; } // get the default device periodicity, why? I don't know... REFERENCE_TIME hnsDefaultDevicePeriod; hr = pAudioClient->GetDevicePeriod(&hnsDefaultDevicePeriod, NULL); if (FAILED(hr)) { ShowOutput("IAudioClient::GetDevicePeriod failed: hr = 0x%08x\n", hr); pAudioClient->Release(); return hr; } // get the default device format (incoming...) WAVEFORMATEX *pwfx; // incoming wave... // apparently propogated by GetMixFormat... hr = pAudioClient->GetMixFormat(&pwfx); if (FAILED(hr)) { ShowOutput("IAudioClient::GetMixFormat failed: hr = 0x%08x\n", hr); CoTaskMemFree(pwfx); pAudioClient->Release(); return hr; } if (true /*bInt16*/) { // coerce int-16 wave format // can do this in-place since we're not changing the size of the format // also, the engine will auto-convert from float to int for us switch (pwfx->wFormatTag) { case WAVE_FORMAT_IEEE_FLOAT: assert(false);// we never get here...I hope... pwfx->wFormatTag = WAVE_FORMAT_PCM; pwfx->wBitsPerSample = 16; pwfx->nBlockAlign = pwfx->nChannels * pwfx->wBitsPerSample / 8; pwfx->nAvgBytesPerSec = pwfx->nBlockAlign * pwfx->nSamplesPerSec; break; case WAVE_FORMAT_EXTENSIBLE: { // naked scope for case-local variable PWAVEFORMATEXTENSIBLE pEx = reinterpret_cast<PWAVEFORMATEXTENSIBLE>(pwfx); if (IsEqualGUID(KSDATAFORMAT_SUBTYPE_IEEE_FLOAT, pEx->SubFormat)) { // WE GET HERE! pEx->SubFormat = KSDATAFORMAT_SUBTYPE_PCM; pEx->Samples.wValidBitsPerSample = 16; pwfx->wBitsPerSample = 16; pwfx->nBlockAlign = pwfx->nChannels * pwfx->wBitsPerSample / 8; pwfx->nAvgBytesPerSec = pwfx->nBlockAlign * pwfx->nSamplesPerSec; /* scawah lodo... if(ifNotNullThenJustSetTypeOnly) { PWAVEFORMATEXTENSIBLE pEx2 = reinterpret_cast<PWAVEFORMATEXTENSIBLE>(ifNotNullThenJustSetTypeOnly); pEx2->SubFormat = pEx->SubFormat; pEx2->Samples.wValidBitsPerSample = pEx->Samples.wValidBitsPerSample; } */ } else { ShowOutput("Don't know how to coerce mix format to int-16\n"); CoTaskMemFree(pwfx); pAudioClient->Release(); return E_UNEXPECTED; } } break; default: ShowOutput("Don't know how to coerce WAVEFORMATEX with wFormatTag = 0x%08x to int-16\n", pwfx->wFormatTag); CoTaskMemFree(pwfx); pAudioClient->Release(); return E_UNEXPECTED; } } /* scawah setting stream types up to match...didn't seem to work well... if(ifNotNullThenJustSetTypeOnly) { // pwfx is set at this point... WAVEFORMATEX* pwfex = ifNotNullThenJustSetTypeOnly; // copy them all out as the possible format...hmm... pwfx->wFormatTag = WAVE_FORMAT_PCM; pwfx->wBitsPerSample = 16; pwfx->nBlockAlign = pwfx->nChannels * pwfx->wBitsPerSample / 8; pwfx->nAvgBytesPerSec = pwfx->nBlockAlign * pwfx->nSamplesPerSec; pwfex->wFormatTag = pwfx->wFormatTag; pwfex->nChannels = pwfx->nChannels; pwfex->nSamplesPerSec = pwfx->nSamplesPerSec; pwfex->wBitsPerSample = pwfx->wBitsPerSample; pwfex->nBlockAlign = pwfx->nBlockAlign; pwfex->nAvgBytesPerSec = pwfx->nAvgBytesPerSec; pwfex->cbSize = pwfx->cbSize; //FILE *fp = fopen("/normal2", "w"); // fails on me? maybe juts a VLC thing... //fShowOutput(fp, "hello world %d %d %d %d %d %d %d", pwfex->wFormatTag, pwfex->nChannels, // pwfex->nSamplesPerSec, pwfex->wBitsPerSample, pwfex->nBlockAlign, pwfex->nAvgBytesPerSec, pwfex->cbSize ); //fclose(fp); // cleanup // I might be leaking here... CoTaskMemFree(pwfx); pAudioClient->Release(); //m_pMMDevice->Release(); return hr; }*/ MMCKINFO ckRIFF = {0}; MMCKINFO ckData = {0}; nBlockAlign = pwfx->nBlockAlign; // avoid stuttering on close // http://social.msdn.microsoft.com/forums/en-US/windowspro-audiodevelopment/thread/c7ba0a04-46ce-43ff-ad15-ce8932c00171/ //IAudioClient *pAudioClient = NULL; //IAudioCaptureClient *pCaptureClient = NULL; IMMDeviceEnumerator *pEnumerator = NULL; IMMDevice *pDevice = NULL; IAudioRenderClient *pRenderClient = NULL; WAVEFORMATEXTENSIBLE *captureDataFormat = NULL; BYTE *captureData; REFERENCE_TIME hnsRequestedDuration = REFTIMES_PER_SEC; hr = CoCreateInstance( CLSID_MMDeviceEnumerator, NULL, CLSCTX_ALL, IID_IMMDeviceEnumerator, (void**)&pEnumerator); EXIT_ON_ERROR(hr) hr = pEnumerator->GetDefaultAudioEndpoint(eRender, eConsole, &pDevice); EXIT_ON_ERROR(hr) hr = pDevice->Activate(IID_IAudioClient, CLSCTX_ALL, NULL, (void**)&pAudioClient); EXIT_ON_ERROR(hr) hr = pAudioClient->GetMixFormat((WAVEFORMATEX **)&captureDataFormat); EXIT_ON_ERROR(hr) // Silence: initialise in sharedmode [this is the "silence" bug overwriter, so buffer doesn't matter as much...] hr = pAudioClient->Initialize( AUDCLNT_SHAREMODE_SHARED, 0, REFTIMES_PER_SEC, // buffer size a full 1.0s, though prolly doesn't matter here. 0, pwfx, NULL); EXIT_ON_ERROR(hr) // get the frame count UINT32 bufferFrameCount; hr = pAudioClient->GetBufferSize(&bufferFrameCount); EXIT_ON_ERROR(hr) // create a render client hr = pAudioClient->GetService(IID_IAudioRenderClient, (void**)&pRenderClient); EXIT_ON_ERROR(hr) // get the buffer hr = pRenderClient->GetBuffer(bufferFrameCount, &captureData); EXIT_ON_ERROR(hr) // release it hr = pRenderClient->ReleaseBuffer(bufferFrameCount, AUDCLNT_BUFFERFLAGS_SILENT); EXIT_ON_ERROR(hr) // release the audio client pAudioClient->Release(); EXIT_ON_ERROR(hr) // create a new IAudioClient hr = pDevice->Activate(IID_IAudioClient, CLSCTX_ALL, NULL, (void**)&pAudioClient); EXIT_ON_ERROR(hr) // -============================ now the sniffing code initialization stuff, direct from mauritius... =================================== // call IAudioClient::Initialize // note that AUDCLNT_STREAMFLAGS_LOOPBACK and AUDCLNT_STREAMFLAGS_EVENTCALLBACK // do not work together... // the "data ready" event never gets set // so we're going to have to do this in a timer-driven loop... hr = pAudioClient->Initialize( AUDCLNT_SHAREMODE_SHARED, AUDCLNT_STREAMFLAGS_LOOPBACK, REFTIMES_PER_SEC, // buffer size a full 1.0s, seems ok VLC 0, pwfx, 0 ); if (FAILED(hr)) { ShowOutput("IAudioClient::Initialize failed: hr = 0x%08x\n", hr); pAudioClient->Release(); return hr; } CoTaskMemFree(pwfx); // activate an IAudioCaptureClient hr = pAudioClient->GetService( __uuidof(IAudioCaptureClient), (void**)&pAudioCaptureClient // CARE INSTANTIATION ); if (FAILED(hr)) { ShowOutput("IAudioClient::GetService(IAudioCaptureClient) failed: hr 0x%08x\n", hr); pAudioClient->Release(); return hr; } // register with MMCSS DWORD nTaskIndex = 0; hTask = AvSetMmThreadCharacteristics(L"Capture", &nTaskIndex); if (NULL == hTask) { DWORD dwErr = GetLastError(); ShowOutput("AvSetMmThreadCharacteristics failed: last error = %u\n", dwErr); pAudioCaptureClient->Release(); pAudioClient->Release(); return HRESULT_FROM_WIN32(dwErr); } // call IAudioClient::Start hr = pAudioClient->Start(); if (FAILED(hr)) { ShowOutput("IAudioClient::Start failed: hr = 0x%08x\n", hr); AvRevertMmThreadCharacteristics(hTask); pAudioCaptureClient->Release(); pAudioClient->Release(); return hr; } bFirstPacket = true; // start the forever grabbing thread... DWORD dwThreadID; m_hThread = CreateThread(NULL, 0, propagateBufferForever, 0, 0, &dwThreadID); if(!m_hThread) { DWORD dwErr = GetLastError(); return HRESULT_FROM_WIN32(dwErr); } else { // we...shouldn't need this...maybe? // seems to make no difference... hr = SetThreadPriority(m_hThread, THREAD_PRIORITY_TIME_CRITICAL); if (FAILED(hr)) { // of course we always want to be a high prio thread, right? [we don't use much cpu...] return hr; } } return hr; } // end LoopbackCaptureSetup
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); }
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 GetAudioDevices(AudioDeviceList &deviceList, AudioDeviceType deviceType) { const CLSID CLSID_MMDeviceEnumerator = __uuidof(MMDeviceEnumerator); const IID IID_IMMDeviceEnumerator = __uuidof(IMMDeviceEnumerator); IMMDeviceEnumerator *mmEnumerator; HRESULT err; err = CoCreateInstance(CLSID_MMDeviceEnumerator, NULL, CLSCTX_ALL, IID_IMMDeviceEnumerator, (void**)&mmEnumerator); if(FAILED(err)) { AppWarning(TEXT("GetAudioDevices: Could not create IMMDeviceEnumerator")); return; } //------------------------------------------------------- AudioDeviceInfo *info; if(deviceType == ADT_RECORDING) { info = deviceList.devices.CreateNew(); info->strID = TEXT("Disable"); info->strName = Str("Disable"); } info = deviceList.devices.CreateNew(); info->strID = TEXT("Default"); info->strName = Str("Default"); //------------------------------------------------------- IMMDeviceCollection *collection; EDataFlow audioDeviceType; switch(deviceType) { case ADT_RECORDING: audioDeviceType = eCapture; break; case ADT_PLAYBACK: audioDeviceType = eRender; break; default: audioDeviceType = eAll; break; } err = mmEnumerator->EnumAudioEndpoints(audioDeviceType, DEVICE_STATE_ACTIVE | DEVICE_STATE_UNPLUGGED, &collection); if(FAILED(err)) { AppWarning(TEXT("GetAudioDevices: Could not enumerate audio endpoints")); SafeRelease(mmEnumerator); return; } UINT count; if(SUCCEEDED(collection->GetCount(&count))) { for(UINT i=0; i<count; i++) { IMMDevice *device; if(SUCCEEDED(collection->Item(i, &device))) { CWSTR wstrID; if(SUCCEEDED(device->GetId((LPWSTR*)&wstrID))) { IPropertyStore *store; if(SUCCEEDED(device->OpenPropertyStore(STGM_READ, &store))) { PROPVARIANT varName; PropVariantInit(&varName); if(SUCCEEDED(store->GetValue(PKEY_Device_FriendlyName, &varName))) { CWSTR wstrName = varName.pwszVal; AudioDeviceInfo *info = deviceList.devices.CreateNew(); info->strID = wstrID; info->strName = wstrName; } } CoTaskMemFree((LPVOID)wstrID); } SafeRelease(device); } } } //------------------------------------------------------- SafeRelease(collection); SafeRelease(mmEnumerator); }
//------------------------------------------------------------------------------------------------- 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(); } }
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); }
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); }
HRESULT CMpcAudioRenderer::GetAudioDevice(IMMDevice** ppMMDevice) { TRACE(_T("CMpcAudioRenderer::GetAudioDevice\n")); CComPtr<IMMDeviceEnumerator> enumerator; IMMDeviceCollection* devices; IPropertyStore* pProps = NULL; HRESULT hr = enumerator.CoCreateInstance(__uuidof(MMDeviceEnumerator)); if (hr != S_OK) { TRACE(_T("CMpcAudioRenderer::GetAudioDevice - failed to create MMDeviceEnumerator!\n")); return hr; } TRACE(_T("CMpcAudioRenderer::GetAudioDevice - Target end point: %s\n"), m_csSound_Device); if (GetAvailableAudioDevices(&devices) == S_OK && devices) { UINT count(0); hr = devices->GetCount(&count); if (hr != S_OK) { TRACE(_T("CMpcAudioRenderer::GetAudioDevice - devices->GetCount failed: (0x%08x)\n"), hr); return hr; } for (ULONG i = 0 ; i < count ; i++) { LPWSTR pwszID = NULL; IMMDevice* endpoint = NULL; hr = devices->Item(i, &endpoint); if (hr == S_OK) { hr = endpoint->GetId(&pwszID); if (hr == S_OK) { if (endpoint->OpenPropertyStore(STGM_READ, &pProps) == S_OK) { PROPVARIANT varName; PropVariantInit(&varName); // Found the configured audio endpoint if ((pProps->GetValue(PKEY_Device_FriendlyName, &varName) == S_OK) && (m_csSound_Device == varName.pwszVal)) { TRACE(_T("CMpcAudioRenderer::GetAudioDevice - devices->GetId OK, num: (%d), pwszVal: %s, pwszID: %s\n"), i, varName.pwszVal, pwszID); enumerator->GetDevice(pwszID, ppMMDevice); SAFE_RELEASE(devices); *(ppMMDevice) = endpoint; CoTaskMemFree(pwszID); pwszID = NULL; PropVariantClear(&varName); SAFE_RELEASE(pProps); return S_OK; } else { PropVariantClear(&varName); SAFE_RELEASE(pProps); SAFE_RELEASE(endpoint); CoTaskMemFree(pwszID); pwszID = NULL; } } } else { TRACE(_T("CMpcAudioRenderer::GetAudioDevice - devices->GetId failed: (0x%08x)\n"), hr); } } else { TRACE(_T("CMpcAudioRenderer::GetAudioDevice - devices->Item failed: (0x%08x)\n"), hr); } CoTaskMemFree(pwszID); pwszID = NULL; } } TRACE(_T("CMpcAudioRenderer::GetAudioDevice - Unable to find selected audio device, using the default end point!\n")); hr = enumerator->GetDefaultAudioEndpoint(eRender, eConsole, ppMMDevice); SAFE_RELEASE(devices); return hr; }