BlankAudioPlayback(CTSTR lpDevice) { const CLSID CLSID_MMDeviceEnumerator = __uuidof(MMDeviceEnumerator); const IID IID_IMMDeviceEnumerator = __uuidof(IMMDeviceEnumerator); const IID IID_IAudioClient = __uuidof(IAudioClient); const IID IID_IAudioRenderClient = __uuidof(IAudioRenderClient); HRESULT err; err = CoCreateInstance(CLSID_MMDeviceEnumerator, NULL, CLSCTX_ALL, IID_IMMDeviceEnumerator, (void**)&mmEnumerator); if(FAILED(err)) CrashError(TEXT("Could not create IMMDeviceEnumerator: 0x%08lx"), err); if (scmpi(lpDevice, TEXT("Default")) == 0) err = mmEnumerator->GetDefaultAudioEndpoint(eRender, eConsole, &mmDevice); else err = mmEnumerator->GetDevice(lpDevice, &mmDevice); if(FAILED(err)) CrashError(TEXT("Could not create IMMDevice")); err = mmDevice->Activate(IID_IAudioClient, CLSCTX_ALL, NULL, (void**)&mmClient); if(FAILED(err)) CrashError(TEXT("Could not create IAudioClient")); WAVEFORMATEX *pwfx; err = mmClient->GetMixFormat(&pwfx); if(FAILED(err)) CrashError(TEXT("Could not get mix format from audio client")); UINT inputBlockSize = pwfx->nBlockAlign; err = mmClient->Initialize(AUDCLNT_SHAREMODE_SHARED, 0, ConvertMSTo100NanoSec(1000), 0, pwfx, NULL); if(FAILED(err)) CrashError(TEXT("Could not initialize audio client, error = %08lX"), err); err = mmClient->GetService(IID_IAudioRenderClient, (void**)&mmRender); if(FAILED(err)) CrashError(TEXT("Could not get audio render client")); //---------------------------------------------------------------- UINT bufferFrameCount; err = mmClient->GetBufferSize(&bufferFrameCount); if(FAILED(err)) CrashError(TEXT("Could not get audio buffer size")); BYTE *lpData; err = mmRender->GetBuffer(bufferFrameCount, &lpData); if(FAILED(err)) CrashError(TEXT("Could not get audio buffer")); zero(lpData, bufferFrameCount*inputBlockSize); mmRender->ReleaseBuffer(bufferFrameCount, 0);//AUDCLNT_BUFFERFLAGS_SILENT); //probably better if it doesn't know if(FAILED(mmClient->Start())) CrashError(TEXT("Could not start audio source")); }
bool MMDeviceAudioSource::Initialize(bool bMic, CTSTR lpID) { const CLSID CLSID_MMDeviceEnumerator = __uuidof(MMDeviceEnumerator); const IID IID_IMMDeviceEnumerator = __uuidof(IMMDeviceEnumerator); const IID IID_IAudioClient = __uuidof(IAudioClient); const IID IID_IAudioCaptureClient = __uuidof(IAudioCaptureClient); HRESULT err; err = CoCreateInstance(CLSID_MMDeviceEnumerator, NULL, CLSCTX_ALL, IID_IMMDeviceEnumerator, (void**)&mmEnumerator); if(FAILED(err)) { AppWarning(TEXT("MMDeviceAudioSource::Initialize(%d): Could not create IMMDeviceEnumerator = %08lX"), (BOOL)bMic, err); return false; } bIsMic = bMic; if (bIsMic) { BOOL bMicSyncFixHack = GlobalConfig->GetInt(TEXT("Audio"), TEXT("UseMicSyncFixHack")); angerThreshold = bMicSyncFixHack ? 40 : 1000; } if (scmpi(lpID, TEXT("Default")) == 0) err = mmEnumerator->GetDefaultAudioEndpoint(bMic ? eCapture : eRender, bMic ? eCommunications : eConsole, &mmDevice); else err = mmEnumerator->GetDevice(lpID, &mmDevice); if(FAILED(err)) { AppWarning(TEXT("MMDeviceAudioSource::Initialize(%d): Could not create IMMDevice = %08lX"), (BOOL)bMic, err); return false; } err = mmDevice->Activate(IID_IAudioClient, CLSCTX_ALL, NULL, (void**)&mmClient); if(FAILED(err)) { AppWarning(TEXT("MMDeviceAudioSource::Initialize(%d): Could not create IAudioClient = %08lX"), (BOOL)bMic, err); return false; } //----------------------------------------------------------------- // get name IPropertyStore *store; if(SUCCEEDED(mmDevice->OpenPropertyStore(STGM_READ, &store))) { PROPVARIANT varName; PropVariantInit(&varName); if(SUCCEEDED(store->GetValue(PKEY_Device_FriendlyName, &varName))) { CWSTR wstrName = varName.pwszVal; strDeviceName = wstrName; } store->Release(); } if(bMic) { Log(TEXT("------------------------------------------")); Log(TEXT("Using auxilary audio input: %s"), GetDeviceName()); bUseQPC = GlobalConfig->GetInt(TEXT("Audio"), TEXT("UseMicQPC")) != 0; if (bUseQPC) Log(TEXT("Using Mic QPC timestamps")); } else { Log(TEXT("------------------------------------------")); Log(TEXT("Using desktop audio input: %s"), GetDeviceName()); bUseVideoTime = AppConfig->GetInt(TEXT("Audio"), TEXT("SyncToVideoTime")) != 0; SetTimeOffset(GlobalConfig->GetInt(TEXT("Audio"), TEXT("GlobalAudioTimeAdjust"))); } //----------------------------------------------------------------- // get format WAVEFORMATEX *pwfx; err = mmClient->GetMixFormat(&pwfx); if(FAILED(err)) { AppWarning(TEXT("MMDeviceAudioSource::Initialize(%d): Could not get mix format from audio client = %08lX"), (BOOL)bMic, err); return false; } bool bFloat; UINT inputChannels; UINT inputSamplesPerSec; UINT inputBitsPerSample; UINT inputBlockSize; DWORD inputChannelMask = 0; WAVEFORMATEXTENSIBLE *wfext = NULL; //the internal audio engine should always use floats (or so I read), but I suppose just to be safe better check if(pwfx->wFormatTag == WAVE_FORMAT_EXTENSIBLE) { wfext = (WAVEFORMATEXTENSIBLE*)pwfx; inputChannelMask = wfext->dwChannelMask; if(wfext->SubFormat != KSDATAFORMAT_SUBTYPE_IEEE_FLOAT) { AppWarning(TEXT("MMDeviceAudioSource::Initialize(%d): Unsupported wave format"), (BOOL)bMic); return false; } } else if(pwfx->wFormatTag != WAVE_FORMAT_IEEE_FLOAT) { AppWarning(TEXT("MMDeviceAudioSource::Initialize(%d): Unsupported wave format"), (BOOL)bMic); return false; } bFloat = true; inputChannels = pwfx->nChannels; inputBitsPerSample = 32; inputBlockSize = pwfx->nBlockAlign; inputSamplesPerSec = pwfx->nSamplesPerSec; sampleWindowSize = (inputSamplesPerSec/100); DWORD flags = bMic ? 0 : AUDCLNT_STREAMFLAGS_LOOPBACK; err = mmClient->Initialize(AUDCLNT_SHAREMODE_SHARED, flags, ConvertMSTo100NanoSec(5000), 0, pwfx, NULL); //err = AUDCLNT_E_UNSUPPORTED_FORMAT; if (err == AUDCLNT_E_UNSUPPORTED_FORMAT) { //workaround for razer kraken headset (bad drivers) pwfx->nBlockAlign = 2*pwfx->nChannels; pwfx->nAvgBytesPerSec = inputSamplesPerSec*pwfx->nBlockAlign; pwfx->wBitsPerSample = 16; wfext->SubFormat = KSDATAFORMAT_SUBTYPE_PCM; wfext->Samples.wValidBitsPerSample = 16; bConvert = true; err = mmClient->Initialize(AUDCLNT_SHAREMODE_SHARED, flags, ConvertMSTo100NanoSec(5000), 0, pwfx, NULL); } if(FAILED(err)) { AppWarning(TEXT("MMDeviceAudioSource::Initialize(%d): Could not initialize audio client, result = %08lX"), (BOOL)bMic, err); return false; } //----------------------------------------------------------------- // acquire services err = mmClient->GetService(IID_IAudioCaptureClient, (void**)&mmCapture); if(FAILED(err)) { AppWarning(TEXT("MMDeviceAudioSource::Initialize(%d): Could not get audio capture client, result = %08lX"), (BOOL)bMic, err); return false; } err = mmClient->GetService(__uuidof(IAudioClock), (void**)&mmClock); if(FAILED(err)) { AppWarning(TEXT("MMDeviceAudioSource::Initialize(%d): Could not get audio capture clock, result = %08lX"), (BOOL)bMic, err); return false; } CoTaskMemFree(pwfx); //----------------------------------------------------------------- InitAudioData(bFloat, inputChannels, inputSamplesPerSec, inputBitsPerSample, inputBlockSize, inputChannelMask); return true; }
// // Based on the input switches, pick the specified device to use. // bool PickDevice(IMMDevice **DeviceToUse, bool *IsDefaultDevice, ERole *DefaultDeviceRole) { HRESULT hr; bool retValue = true; IMMDeviceEnumerator *deviceEnumerator = NULL; IMMDeviceCollection *deviceCollection = NULL; *IsDefaultDevice = false; // Assume we're not using the default device. hr = CoCreateInstance(__uuidof(MMDeviceEnumerator), NULL, CLSCTX_INPROC_SERVER, IID_PPV_ARGS(&deviceEnumerator)); if (FAILED(hr)) { printf("Unable to instantiate device enumerator: %x\n", hr); retValue = false; goto Exit; } IMMDevice *device = NULL; // // First off, if none of the console switches was specified, use the console device. // if (!UseConsoleDevice && !UseCommunicationsDevice && !UseMultimediaDevice && OutputEndpoint == NULL) { // // The user didn't specify an output device, prompt the user for a device and use that. // hr = deviceEnumerator->EnumAudioEndpoints(eRender, DEVICE_STATE_ACTIVE, &deviceCollection); if (FAILED(hr)) { printf("Unable to retrieve device collection: %x\n", hr); retValue = false; goto Exit; } printf("Select an output device:\n"); printf(" 0: Default Console Device\n"); printf(" 1: Default Communications Device\n"); printf(" 2: Default Multimedia Device\n"); UINT deviceCount; hr = deviceCollection->GetCount(&deviceCount); if (FAILED(hr)) { printf("Unable to get device collection length: %x\n", hr); retValue = false; goto Exit; } for (UINT i = 0 ; i < deviceCount ; i += 1) { LPWSTR deviceName; deviceName = GetDeviceName(deviceCollection, i); if (deviceName == NULL) { retValue = false; goto Exit; } printf(" %d: %S\n", i + 3, deviceName); free(deviceName); } wchar_t choice[10]; _getws_s(choice); // Note: Using the safe CRT version of _getws. long deviceIndex; wchar_t *endPointer; deviceIndex = wcstoul(choice, &endPointer, 0); if (deviceIndex == 0 && endPointer == choice) { printf("unrecognized device index: %S\n", choice); retValue = false; goto Exit; } switch (deviceIndex) { case 0: UseConsoleDevice = 1; break; case 1: UseCommunicationsDevice = 1; break; case 2: UseMultimediaDevice = 1; break; default: hr = deviceCollection->Item(deviceIndex - 3, &device); if (FAILED(hr)) { printf("Unable to retrieve device %d: %x\n", deviceIndex - 3, hr); retValue = false; goto Exit; } break; } } else if (OutputEndpoint != NULL) { hr = deviceEnumerator->GetDevice(OutputEndpoint, &device); if (FAILED(hr)) { printf("Unable to get endpoint for endpoint %S: %x\n", OutputEndpoint, hr); retValue = false; goto Exit; } } if (device == NULL) { ERole deviceRole = eConsole; // Assume we're using the console role. if (UseConsoleDevice) { deviceRole = eConsole; } else if (UseCommunicationsDevice) { deviceRole = eCommunications; } else if (UseMultimediaDevice) { deviceRole = eMultimedia; } hr = deviceEnumerator->GetDefaultAudioEndpoint(eRender, deviceRole, &device); if (FAILED(hr)) { printf("Unable to get default device for role %d: %x\n", deviceRole, hr); retValue = false; goto Exit; } *IsDefaultDevice = true; *DefaultDeviceRole = deviceRole; } *DeviceToUse = device; retValue = true; Exit: SafeRelease(&deviceCollection); SafeRelease(&deviceEnumerator); return retValue; }
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; }
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; }
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; }