diff options
Diffstat (limited to 'drivers/wasapi/audio_driver_wasapi.cpp')
-rw-r--r-- | drivers/wasapi/audio_driver_wasapi.cpp | 210 |
1 files changed, 139 insertions, 71 deletions
diff --git a/drivers/wasapi/audio_driver_wasapi.cpp b/drivers/wasapi/audio_driver_wasapi.cpp index ab2976f02c..276fda2a8f 100644 --- a/drivers/wasapi/audio_driver_wasapi.cpp +++ b/drivers/wasapi/audio_driver_wasapi.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -32,11 +32,63 @@ #include "audio_driver_wasapi.h" +#include "core/config/project_settings.h" #include "core/os/os.h" -#include "core/project_settings.h" + +#include <stdint.h> // INT32_MAX #include <functiondiscoverykeys.h> +// Define IAudioClient3 if not already defined by MinGW headers +#if defined __MINGW32__ || defined __MINGW64__ + +#ifndef __IAudioClient3_FWD_DEFINED__ +#define __IAudioClient3_FWD_DEFINED__ + +typedef interface IAudioClient3 IAudioClient3; + +#endif // __IAudioClient3_FWD_DEFINED__ + +#ifndef __IAudioClient3_INTERFACE_DEFINED__ +#define __IAudioClient3_INTERFACE_DEFINED__ + +MIDL_INTERFACE("7ED4EE07-8E67-4CD4-8C1A-2B7A5987AD42") +IAudioClient3 : public IAudioClient2 { +public: + virtual HRESULT STDMETHODCALLTYPE GetSharedModeEnginePeriod( + /* [annotation][in] */ + _In_ const WAVEFORMATEX *pFormat, + /* [annotation][out] */ + _Out_ UINT32 *pDefaultPeriodInFrames, + /* [annotation][out] */ + _Out_ UINT32 *pFundamentalPeriodInFrames, + /* [annotation][out] */ + _Out_ UINT32 *pMinPeriodInFrames, + /* [annotation][out] */ + _Out_ UINT32 *pMaxPeriodInFrames) = 0; + + virtual HRESULT STDMETHODCALLTYPE GetCurrentSharedModeEnginePeriod( + /* [unique][annotation][out] */ + _Out_ WAVEFORMATEX * *ppFormat, + /* [annotation][out] */ + _Out_ UINT32 * pCurrentPeriodInFrames) = 0; + + virtual HRESULT STDMETHODCALLTYPE InitializeSharedAudioStream( + /* [annotation][in] */ + _In_ DWORD StreamFlags, + /* [annotation][in] */ + _In_ UINT32 PeriodInFrames, + /* [annotation][in] */ + _In_ const WAVEFORMATEX *pFormat, + /* [annotation][in] */ + _In_opt_ LPCGUID AudioSessionGuid) = 0; +}; +__CRT_UUID_DECL(IAudioClient3, 0x7ED4EE07, 0x8E67, 0x4CD4, 0x8C, 0x1A, 0x2B, 0x7A, 0x59, 0x87, 0xAD, 0x42) + +#endif // __IAudioClient3_INTERFACE_DEFINED__ + +#endif // __MINGW32__ || __MINGW64__ + #ifndef PKEY_Device_FriendlyName #undef DEFINE_PROPERTYKEY @@ -51,6 +103,7 @@ DEFINE_PROPERTYKEY(PKEY_Device_FriendlyName, 0xa45c254e, 0xdf1c, 0x4efd, 0x80, 0 const CLSID CLSID_MMDeviceEnumerator = __uuidof(MMDeviceEnumerator); const IID IID_IMMDeviceEnumerator = __uuidof(IMMDeviceEnumerator); const IID IID_IAudioClient = __uuidof(IAudioClient); +const IID IID_IAudioClient3 = __uuidof(IAudioClient3); const IID IID_IAudioRenderClient = __uuidof(IAudioRenderClient); const IID IID_IAudioCaptureClient = __uuidof(IAudioCaptureClient); @@ -69,13 +122,11 @@ static bool default_render_device_changed = false; static bool default_capture_device_changed = false; class CMMNotificationClient : public IMMNotificationClient { - LONG _cRef; - IMMDeviceEnumerator *_pEnumerator; + LONG _cRef = 1; + IMMDeviceEnumerator *_pEnumerator = nullptr; public: - CMMNotificationClient() : - _cRef(1), - _pEnumerator(nullptr) {} + CMMNotificationClient() {} virtual ~CMMNotificationClient() { if ((_pEnumerator) != nullptr) { (_pEnumerator)->Release(); @@ -141,7 +192,6 @@ public: static CMMNotificationClient notif_client; Error AudioDriverWASAPI::audio_device_init(AudioDeviceWASAPI *p_device, bool p_capture, bool reinit) { - WAVEFORMATEX *pwfex; IMMDeviceEnumerator *enumerator = nullptr; IMMDevice *device = nullptr; @@ -224,7 +274,22 @@ Error AudioDriverWASAPI::audio_device_init(AudioDeviceWASAPI *p_device, bool p_c ERR_PRINT("WASAPI: RegisterEndpointNotificationCallback error"); } - hr = device->Activate(IID_IAudioClient, CLSCTX_ALL, nullptr, (void **)&p_device->audio_client); + bool using_audio_client_3 = !p_capture; // IID_IAudioClient3 is only used for adjustable output latency (not input) + if (using_audio_client_3) { + hr = device->Activate(IID_IAudioClient3, CLSCTX_ALL, nullptr, (void **)&p_device->audio_client); + if (hr != S_OK) { + // IID_IAudioClient3 will never activate on OS versions before Windows 10. + // Older Windows versions should fall back gracefully. + using_audio_client_3 = false; + print_verbose("WASAPI: Couldn't activate device with IAudioClient3 interface, falling back to IAudioClient interface"); + } else { + print_verbose("WASAPI: Activated device using IAudioClient3 interface"); + } + } + if (!using_audio_client_3) { + hr = device->Activate(IID_IAudioClient, CLSCTX_ALL, nullptr, (void **)&p_device->audio_client); + } + SAFE_RELEASE(device) if (reinit) { @@ -235,6 +300,16 @@ Error AudioDriverWASAPI::audio_device_init(AudioDeviceWASAPI *p_device, bool p_c ERR_FAIL_COND_V(hr != S_OK, ERR_CANT_OPEN); } + if (using_audio_client_3) { + AudioClientProperties audioProps; + audioProps.cbSize = sizeof(AudioClientProperties); + audioProps.bIsOffload = FALSE; + audioProps.eCategory = AudioCategory_GameEffects; + + hr = ((IAudioClient3 *)p_device->audio_client)->SetClientProperties(&audioProps); + ERR_FAIL_COND_V_MSG(hr != S_OK, ERR_CANT_OPEN, "WASAPI: SetClientProperties failed with error 0x" + String::num_uint64(hr, 16) + "."); + } + hr = p_device->audio_client->GetMixFormat(&pwfex); ERR_FAIL_COND_V(hr != S_OK, ERR_CANT_OPEN); @@ -288,15 +363,55 @@ Error AudioDriverWASAPI::audio_device_init(AudioDeviceWASAPI *p_device, bool p_c } } - DWORD streamflags = 0; - if ((DWORD)mix_rate != pwfex->nSamplesPerSec) { - streamflags |= AUDCLNT_STREAMFLAGS_RATEADJUST; - pwfex->nSamplesPerSec = mix_rate; - pwfex->nAvgBytesPerSec = pwfex->nSamplesPerSec * pwfex->nChannels * (pwfex->wBitsPerSample / 8); - } + if (!using_audio_client_3) { + DWORD streamflags = 0; + if ((DWORD)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, nullptr); + ERR_FAIL_COND_V_MSG(hr != S_OK, ERR_CANT_OPEN, "WASAPI: Initialize failed with error 0x" + String::num_uint64(hr, 16) + "."); + UINT32 max_frames; + HRESULT hr = p_device->audio_client->GetBufferSize(&max_frames); + ERR_FAIL_COND_V(hr != S_OK, ERR_CANT_OPEN); - hr = p_device->audio_client->Initialize(AUDCLNT_SHAREMODE_SHARED, streamflags, p_capture ? REFTIMES_PER_SEC : 0, 0, pwfex, nullptr); - ERR_FAIL_COND_V_MSG(hr != S_OK, ERR_CANT_OPEN, "WASAPI: Initialize failed with error 0x" + String::num_uint64(hr, 16) + "."); + // Due to WASAPI Shared Mode we have no control of the buffer size + buffer_frames = max_frames; + } else { + IAudioClient3 *device_audio_client_3 = (IAudioClient3 *)p_device->audio_client; + + // AUDCLNT_STREAMFLAGS_RATEADJUST is an invalid flag with IAudioClient3, therefore we have to use + // the closest supported mix rate supported by the audio driver. + mix_rate = pwfex->nSamplesPerSec; + print_verbose("WASAPI: mix_rate = " + itos(mix_rate)); + + UINT32 default_period_frames, fundamental_period_frames, min_period_frames, max_period_frames; + hr = device_audio_client_3->GetSharedModeEnginePeriod( + pwfex, + &default_period_frames, + &fundamental_period_frames, + &min_period_frames, + &max_period_frames); + ERR_FAIL_COND_V_MSG(hr != S_OK, ERR_CANT_OPEN, "WASAPI: GetSharedModeEnginePeriod failed with error 0x" + String::num_uint64(hr, 16) + "."); + + // Period frames must be an integral multiple of fundamental_period_frames or IAudioClient3 initialization will fail, + // so we need to select the closest multiple to the user-specified latency. + UINT32 desired_period_frames = target_latency_ms * mix_rate / 1000; + UINT32 period_frames = (desired_period_frames / fundamental_period_frames) * fundamental_period_frames; + if (ABS((int64_t)period_frames - (int64_t)desired_period_frames) > ABS((int64_t)(period_frames + fundamental_period_frames) - (int64_t)desired_period_frames)) { + period_frames = period_frames + fundamental_period_frames; + } + period_frames = CLAMP(period_frames, min_period_frames, max_period_frames); + print_verbose("WASAPI: fundamental_period_frames = " + itos(fundamental_period_frames)); + print_verbose("WASAPI: min_period_frames = " + itos(min_period_frames)); + print_verbose("WASAPI: max_period_frames = " + itos(max_period_frames)); + print_verbose("WASAPI: selected a period frame size of " + itos(period_frames)); + buffer_frames = period_frames; + + hr = device_audio_client_3->InitializeSharedAudioStream(0, period_frames, pwfex, nullptr); + ERR_FAIL_COND_V_MSG(hr != S_OK, ERR_CANT_OPEN, "WASAPI: InitializeSharedAudioStream failed with error 0x" + String::num_uint64(hr, 16) + "."); + } if (p_capture) { hr = p_device->audio_client->GetService(IID_IAudioCaptureClient, (void **)&p_device->capture_client); @@ -313,7 +428,6 @@ Error AudioDriverWASAPI::audio_device_init(AudioDeviceWASAPI *p_device, bool p_c } Error AudioDriverWASAPI::init_render_device(bool reinit) { - Error err = audio_device_init(&audio_output, false, reinit); if (err != OK) return err; @@ -332,13 +446,6 @@ Error AudioDriverWASAPI::init_render_device(bool reinit) { break; } - UINT32 max_frames; - HRESULT hr = audio_output.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) samples_in.resize(buffer_frames * channels); @@ -352,7 +459,6 @@ Error AudioDriverWASAPI::init_render_device(bool reinit) { } Error AudioDriverWASAPI::init_capture_device(bool reinit) { - Error err = audio_device_init(&audio_input, true, reinit); if (err != OK) return err; @@ -368,12 +474,10 @@ Error AudioDriverWASAPI::init_capture_device(bool reinit) { } Error AudioDriverWASAPI::audio_device_finish(AudioDeviceWASAPI *p_device) { - if (p_device->active) { if (p_device->audio_client) { p_device->audio_client->Stop(); } - p_device->active = false; } @@ -385,18 +489,17 @@ Error AudioDriverWASAPI::audio_device_finish(AudioDeviceWASAPI *p_device) { } Error AudioDriverWASAPI::finish_render_device() { - return audio_device_finish(&audio_output); } Error AudioDriverWASAPI::finish_capture_device() { - return audio_device_finish(&audio_input); } Error AudioDriverWASAPI::init() { + mix_rate = GLOBAL_GET("audio/driver/mix_rate"); - mix_rate = GLOBAL_DEF_RST("audio/mix_rate", DEFAULT_MIX_RATE); + target_latency_ms = GLOBAL_GET("audio/driver/output_latency"); Error err = init_render_device(); if (err != OK) { @@ -406,23 +509,20 @@ Error AudioDriverWASAPI::init() { exit_thread = false; thread_exited = false; - thread = Thread::create(thread_func, this); + thread.start(thread_func, this); return OK; } int AudioDriverWASAPI::get_mix_rate() const { - return mix_rate; } AudioDriver::SpeakerMode AudioDriverWASAPI::get_speaker_mode() const { - return get_speaker_mode_by_total_channels(channels); } Array AudioDriverWASAPI::audio_device_get_list(bool p_capture) { - Array list; IMMDeviceCollection *devices = nullptr; IMMDeviceEnumerator *enumerator = nullptr; @@ -470,12 +570,10 @@ Array AudioDriverWASAPI::audio_device_get_list(bool p_capture) { } Array AudioDriverWASAPI::get_device_list() { - return audio_device_get_list(false); } String AudioDriverWASAPI::get_device() { - lock(); String name = audio_output.device_name; unlock(); @@ -484,7 +582,6 @@ String AudioDriverWASAPI::get_device() { } void AudioDriverWASAPI::set_device(String device) { - lock(); audio_output.new_device = device; unlock(); @@ -552,13 +649,11 @@ void AudioDriverWASAPI::write_sample(WORD format_tag, int bits_per_sample, BYTE } void AudioDriverWASAPI::thread_func(void *p_udata) { - AudioDriverWASAPI *ad = (AudioDriverWASAPI *)p_udata; uint32_t avail_frames = 0; uint32_t write_ofs = 0; while (!ad->exit_thread) { - uint32_t read_frames = 0; uint32_t written_frames = 0; @@ -585,19 +680,16 @@ void AudioDriverWASAPI::thread_func(void *p_udata) { ad->start_counting_ticks(); if (avail_frames > 0 && ad->audio_output.audio_client) { - UINT32 cur_frames; bool invalidated = false; HRESULT hr = ad->audio_output.audio_client->GetCurrentPadding(&cur_frames); if (hr == S_OK) { - // Check how much frames are available on the WASAPI buffer UINT32 write_frames = MIN(ad->buffer_frames - cur_frames, avail_frames); if (write_frames > 0) { BYTE *buffer = nullptr; hr = ad->audio_output.render_client->GetBuffer(write_frames, &buffer); if (hr == S_OK) { - // We're using WASAPI Shared Mode so we must convert the buffer if (ad->channels == ad->audio_output.channels) { for (unsigned int i = 0; i < write_frames * ad->channels; i++) { @@ -768,7 +860,6 @@ void AudioDriverWASAPI::thread_func(void *p_udata) { } void AudioDriverWASAPI::start() { - if (audio_output.audio_client) { HRESULT hr = audio_output.audio_client->Start(); if (hr != S_OK) { @@ -780,31 +871,22 @@ void AudioDriverWASAPI::start() { } void AudioDriverWASAPI::lock() { - mutex.lock(); } void AudioDriverWASAPI::unlock() { - mutex.unlock(); } void AudioDriverWASAPI::finish() { - - if (thread) { - exit_thread = true; - Thread::wait_to_finish(thread); - - memdelete(thread); - thread = nullptr; - } + exit_thread = true; + thread.wait_to_finish(); finish_capture_device(); finish_render_device(); } Error AudioDriverWASAPI::capture_start() { - Error err = init_capture_device(); if (err != OK) { ERR_PRINT("WASAPI: init_capture_device error"); @@ -821,7 +903,6 @@ Error AudioDriverWASAPI::capture_start() { } Error AudioDriverWASAPI::capture_stop() { - if (audio_input.active) { audio_input.audio_client->Stop(); audio_input.active = false; @@ -833,19 +914,16 @@ Error AudioDriverWASAPI::capture_stop() { } void AudioDriverWASAPI::capture_set_device(const String &p_name) { - lock(); audio_input.new_device = p_name; unlock(); } Array AudioDriverWASAPI::capture_get_device_list() { - return audio_device_get_list(true); } String AudioDriverWASAPI::capture_get_device() { - lock(); String name = audio_input.device_name; unlock(); @@ -854,17 +932,7 @@ String AudioDriverWASAPI::capture_get_device() { } AudioDriverWASAPI::AudioDriverWASAPI() { - - thread = nullptr; - samples_in.clear(); - - channels = 0; - mix_rate = 0; - buffer_frames = 0; - - thread_exited = false; - exit_thread = false; } -#endif +#endif // WASAPI_ENABLED |