From 19d19970e4fa6a738010dfeb2b08886f4a3dd0d5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B6rg=20M=C3=BCller?= Date: Thu, 1 Jul 2021 14:24:06 +0200 Subject: Fix T88887: Audio causes issues with Playback when PC put to Sleep, Hibernate or when Screensaver appears Porting WASAPI device reinitialization from upstream. --- extern/audaspace/include/devices/SoftwareDevice.h | 6 + extern/audaspace/include/respec/Mixer.h | 6 + extern/audaspace/plugins/wasapi/WASAPIDevice.cpp | 197 ++++++++++++++-------- extern/audaspace/plugins/wasapi/WASAPIDevice.h | 5 + extern/audaspace/src/devices/SoftwareDevice.cpp | 22 +++ extern/audaspace/src/respec/Mixer.cpp | 30 ++-- 6 files changed, 182 insertions(+), 84 deletions(-) diff --git a/extern/audaspace/include/devices/SoftwareDevice.h b/extern/audaspace/include/devices/SoftwareDevice.h index 209be9941b1..c3af5cfd902 100644 --- a/extern/audaspace/include/devices/SoftwareDevice.h +++ b/extern/audaspace/include/devices/SoftwareDevice.h @@ -265,6 +265,12 @@ protected: */ void setSpecs(Specs specs); + /** + * Sets the audio output specification of the device. + * \param specs The output specification. + */ + void setSpecs(DeviceSpecs specs); + /** * Empty default constructor. To setup the device call the function create() * and to uninitialize call destroy(). diff --git a/extern/audaspace/include/respec/Mixer.h b/extern/audaspace/include/respec/Mixer.h index 600467826cd..9880d5fdcae 100644 --- a/extern/audaspace/include/respec/Mixer.h +++ b/extern/audaspace/include/respec/Mixer.h @@ -87,6 +87,12 @@ public: */ void setSpecs(Specs specs); + /** + * Sets the target specification for superposing. + * \param specs The target specification. + */ + void setSpecs(DeviceSpecs specs); + /** * Mixes a buffer. * \param buffer The buffer to superpose. diff --git a/extern/audaspace/plugins/wasapi/WASAPIDevice.cpp b/extern/audaspace/plugins/wasapi/WASAPIDevice.cpp index b4632ebb83e..a5382bb9692 100644 --- a/extern/audaspace/plugins/wasapi/WASAPIDevice.cpp +++ b/extern/audaspace/plugins/wasapi/WASAPIDevice.cpp @@ -31,65 +31,81 @@ template void SafeRelease(T **ppT) } } -void WASAPIDevice::runMixingThread() +HRESULT WASAPIDevice::setupRenderClient(IAudioRenderClient*& render_client, UINT32& buffer_size) { - UINT32 buffer_size; + const IID IID_IAudioRenderClient = __uuidof(IAudioRenderClient); + UINT32 padding; UINT32 length; data_t* buffer; - IAudioRenderClient* render_client = nullptr; + HRESULT result; - { - std::lock_guard lock(*this); + if(FAILED(result = m_audio_client->GetBufferSize(&buffer_size))) + return result; - const IID IID_IAudioRenderClient = __uuidof(IAudioRenderClient); + if(FAILED(result = m_audio_client->GetService(IID_IAudioRenderClient, reinterpret_cast(&render_client)))) + return result; - if(FAILED(m_audio_client->GetBufferSize(&buffer_size))) - goto init_error; + if(FAILED(result = m_audio_client->GetCurrentPadding(&padding))) + return result; - if(FAILED(m_audio_client->GetService(IID_IAudioRenderClient, reinterpret_cast(&render_client)))) - goto init_error; + length = buffer_size - padding; - if(FAILED(m_audio_client->GetCurrentPadding(&padding))) - goto init_error; + if(FAILED(result = render_client->GetBuffer(length, &buffer))) + return result; - length = buffer_size - padding; + mix((data_t*)buffer, length); - if(FAILED(render_client->GetBuffer(length, &buffer))) - goto init_error; + if(FAILED(result = render_client->ReleaseBuffer(length, 0))) + return result; - mix((data_t*)buffer, length); + m_audio_client->Start(); - if(FAILED(render_client->ReleaseBuffer(length, 0))) - { - init_error: - SafeRelease(&render_client); - doStop(); - return; - } - } + return result; +} - m_audio_client->Start(); +void WASAPIDevice::runMixingThread() +{ + UINT32 buffer_size; + + IAudioRenderClient* render_client = nullptr; + + std::chrono::milliseconds sleep_duration; - auto sleepDuration = std::chrono::milliseconds(buffer_size * 1000 / int(m_specs.rate) / 2); + bool run_init = true; for(;;) { + HRESULT result = S_OK; + { + UINT32 padding; + UINT32 length; + data_t* buffer; std::lock_guard lock(*this); - if(FAILED(m_audio_client->GetCurrentPadding(&padding))) + if(run_init) + { + result = setupRenderClient(render_client, buffer_size); + + if(FAILED(result)) + goto stop_thread; + + sleep_duration = std::chrono::milliseconds(buffer_size * 1000 / int(m_specs.rate) / 2); + } + + if(FAILED(result = m_audio_client->GetCurrentPadding(&padding))) goto stop_thread; length = buffer_size - padding; - if(FAILED(render_client->GetBuffer(length, &buffer))) + if(FAILED(result = render_client->GetBuffer(length, &buffer))) goto stop_thread; mix((data_t*)buffer, length); - if(FAILED(render_client->ReleaseBuffer(length, 0))) + if(FAILED(result = render_client->ReleaseBuffer(length, 0))) goto stop_thread; // stop thread @@ -98,53 +114,51 @@ void WASAPIDevice::runMixingThread() stop_thread: m_audio_client->Stop(); SafeRelease(&render_client); - doStop(); - return; + + if(result == AUDCLNT_E_DEVICE_INVALIDATED) + { + DeviceSpecs specs = m_specs; + if(!setupDevice(specs)) + result = S_FALSE; + else + { + setSpecs(specs); + + run_init = true; + } + } + + if(result != AUDCLNT_E_DEVICE_INVALIDATED) + { + doStop(); + return; + } } } - std::this_thread::sleep_for(sleepDuration); + std::this_thread::sleep_for(sleep_duration); } } -WASAPIDevice::WASAPIDevice(DeviceSpecs specs, int buffersize) : - m_imm_device_enumerator(nullptr), - m_imm_device(nullptr), - m_audio_client(nullptr), - - m_wave_format_extensible({}) +bool WASAPIDevice::setupDevice(DeviceSpecs &specs) { - // initialize COM if it hasn't happened yet - CoInitializeEx(nullptr, COINIT_MULTITHREADED); + SafeRelease(&m_audio_client); + SafeRelease(&m_imm_device); - const CLSID CLSID_MMDeviceEnumerator = __uuidof(MMDeviceEnumerator); - const IID IID_IMMDeviceEnumerator = __uuidof(IMMDeviceEnumerator); const IID IID_IAudioClient = __uuidof(IAudioClient); + if(FAILED(m_imm_device_enumerator->GetDefaultAudioEndpoint(eRender, eMultimedia, &m_imm_device))) + return false; + + if(FAILED(m_imm_device->Activate(IID_IAudioClient, CLSCTX_ALL, nullptr, reinterpret_cast(&m_audio_client)))) + return false; + WAVEFORMATEXTENSIBLE wave_format_extensible_closest_match; WAVEFORMATEXTENSIBLE* closest_match_pointer = &wave_format_extensible_closest_match; - HRESULT result; - REFERENCE_TIME minimum_time = 0; REFERENCE_TIME buffer_duration; - if(FAILED(CoCreateInstance(CLSID_MMDeviceEnumerator, nullptr, CLSCTX_ALL, IID_IMMDeviceEnumerator, reinterpret_cast(&m_imm_device_enumerator)))) - goto error; - - if(FAILED(m_imm_device_enumerator->GetDefaultAudioEndpoint(eRender, eMultimedia, &m_imm_device))) - goto error; - - if(FAILED(m_imm_device->Activate(IID_IAudioClient, CLSCTX_ALL, nullptr, reinterpret_cast(&m_audio_client)))) - goto error; - - if(specs.channels == CHANNELS_INVALID) - specs.channels = CHANNELS_STEREO; - if(specs.format == FORMAT_INVALID) - specs.format = FORMAT_FLOAT32; - if(specs.rate == RATE_INVALID) - specs.rate = RATE_48000; - switch(specs.format) { case FORMAT_U8: @@ -203,12 +217,14 @@ WASAPIDevice::WASAPIDevice(DeviceSpecs specs, int buffersize) : m_wave_format_extensible.Format.cbSize = 22; m_wave_format_extensible.Samples.wValidBitsPerSample = m_wave_format_extensible.Format.wBitsPerSample; - result = m_audio_client->IsFormatSupported(AUDCLNT_SHAREMODE_SHARED, reinterpret_cast(&m_wave_format_extensible), reinterpret_cast(&closest_match_pointer)); + HRESULT result = m_audio_client->IsFormatSupported(AUDCLNT_SHAREMODE_SHARED, reinterpret_cast(&m_wave_format_extensible), reinterpret_cast(&closest_match_pointer)); if(result == S_FALSE) { + bool errored = false; + if(closest_match_pointer->Format.wFormatTag != WAVE_FORMAT_EXTENSIBLE) - goto error; + goto closest_match_error; specs.channels = Channels(closest_match_pointer->Format.nChannels); specs.rate = closest_match_pointer->Format.nSamplesPerSec; @@ -220,7 +236,7 @@ WASAPIDevice::WASAPIDevice(DeviceSpecs specs, int buffersize) : else if(closest_match_pointer->Format.wBitsPerSample == 64) specs.format = FORMAT_FLOAT64; else - goto error; + goto closest_match_error; } else if(closest_match_pointer->SubFormat == KSDATAFORMAT_SUBTYPE_PCM) { @@ -239,44 +255,81 @@ WASAPIDevice::WASAPIDevice(DeviceSpecs specs, int buffersize) : specs.format = FORMAT_S32; break; default: - goto error; + goto closest_match_error; break; } } else - goto error; + goto closest_match_error; m_wave_format_extensible = *closest_match_pointer; + if(false) + { + closest_match_error: + errored = true; + } + if(closest_match_pointer != &wave_format_extensible_closest_match) { CoTaskMemFree(closest_match_pointer); closest_match_pointer = &wave_format_extensible_closest_match; } + + if(errored) + return false; } else if(FAILED(result)) - goto error; + return false; if(FAILED(m_audio_client->GetDevicePeriod(nullptr, &minimum_time))) - goto error; + return false; - buffer_duration = REFERENCE_TIME(buffersize) * REFERENCE_TIME(10000000) / REFERENCE_TIME(specs.rate); + buffer_duration = REFERENCE_TIME(m_buffersize) * REFERENCE_TIME(10000000) / REFERENCE_TIME(specs.rate); if(minimum_time > buffer_duration) buffer_duration = minimum_time; - m_specs = specs; - if(FAILED(m_audio_client->Initialize(AUDCLNT_SHAREMODE_SHARED, 0, buffer_duration, 0, reinterpret_cast(&m_wave_format_extensible), nullptr))) + return false; + + return true; +} + +WASAPIDevice::WASAPIDevice(DeviceSpecs specs, int buffersize) : + m_buffersize(buffersize), + m_imm_device_enumerator(nullptr), + m_imm_device(nullptr), + m_audio_client(nullptr), + + m_wave_format_extensible({}) +{ + // initialize COM if it hasn't happened yet + CoInitializeEx(nullptr, COINIT_MULTITHREADED); + + const CLSID CLSID_MMDeviceEnumerator = __uuidof(MMDeviceEnumerator); + const IID IID_IMMDeviceEnumerator = __uuidof(IMMDeviceEnumerator); + + if(specs.channels == CHANNELS_INVALID) + specs.channels = CHANNELS_STEREO; + if(specs.format == FORMAT_INVALID) + specs.format = FORMAT_FLOAT32; + if(specs.rate == RATE_INVALID) + specs.rate = RATE_48000; + + if(FAILED(CoCreateInstance(CLSID_MMDeviceEnumerator, nullptr, CLSCTX_ALL, IID_IMMDeviceEnumerator, reinterpret_cast(&m_imm_device_enumerator)))) + goto error; + + if(!setupDevice(specs)) goto error; + m_specs = specs; + create(); return; error: - if(closest_match_pointer != &wave_format_extensible_closest_match) - CoTaskMemFree(closest_match_pointer); SafeRelease(&m_imm_device); SafeRelease(&m_imm_device_enumerator); SafeRelease(&m_audio_client); diff --git a/extern/audaspace/plugins/wasapi/WASAPIDevice.h b/extern/audaspace/plugins/wasapi/WASAPIDevice.h index 375f03bd255..3b11adc98ef 100644 --- a/extern/audaspace/plugins/wasapi/WASAPIDevice.h +++ b/extern/audaspace/plugins/wasapi/WASAPIDevice.h @@ -43,16 +43,21 @@ AUD_NAMESPACE_BEGIN class AUD_PLUGIN_API WASAPIDevice : public ThreadedDevice { private: + int m_buffersize; IMMDeviceEnumerator* m_imm_device_enumerator; IMMDevice* m_imm_device; IAudioClient* m_audio_client; WAVEFORMATEXTENSIBLE m_wave_format_extensible; + AUD_LOCAL HRESULT setupRenderClient(IAudioRenderClient*& render_client, UINT32& buffer_size); + /** * Streaming thread main function. */ AUD_LOCAL void runMixingThread(); + AUD_LOCAL bool setupDevice(DeviceSpecs& specs); + // delete copy constructor and operator= WASAPIDevice(const WASAPIDevice&) = delete; WASAPIDevice& operator=(const WASAPIDevice&) = delete; diff --git a/extern/audaspace/src/devices/SoftwareDevice.cpp b/extern/audaspace/src/devices/SoftwareDevice.cpp index 7a2561515f4..e11b49a0967 100644 --- a/extern/audaspace/src/devices/SoftwareDevice.cpp +++ b/extern/audaspace/src/devices/SoftwareDevice.cpp @@ -756,6 +756,7 @@ void SoftwareDevice::mix(data_t* buffer, int length) // get the buffer from the source pos = 0; len = length; + eos = false; // update 3D Info sound->update(); @@ -842,6 +843,27 @@ void SoftwareDevice::setSpecs(Specs specs) { sound->setSpecs(specs); } + + for(auto& sound : m_pausedSounds) + { + sound->setSpecs(specs); + } +} + +void SoftwareDevice::setSpecs(DeviceSpecs specs) +{ + m_specs = specs; + m_mixer->setSpecs(specs); + + for(auto& sound : m_playingSounds) + { + sound->setSpecs(specs.specs); + } + + for(auto& sound : m_pausedSounds) + { + sound->setSpecs(specs.specs); + } } SoftwareDevice::SoftwareDevice() diff --git a/extern/audaspace/src/respec/Mixer.cpp b/extern/audaspace/src/respec/Mixer.cpp index ad8d885df4e..15872fbcff2 100644 --- a/extern/audaspace/src/respec/Mixer.cpp +++ b/extern/audaspace/src/respec/Mixer.cpp @@ -21,9 +21,25 @@ AUD_NAMESPACE_BEGIN -Mixer::Mixer(DeviceSpecs specs) : - m_specs(specs) +Mixer::Mixer(DeviceSpecs specs) { + setSpecs(specs); +} + +DeviceSpecs Mixer::getSpecs() const +{ + return m_specs; +} + +void Mixer::setSpecs(Specs specs) +{ + m_specs.specs = specs; +} + +void Mixer::setSpecs(DeviceSpecs specs) +{ + m_specs = specs; + switch(m_specs.format) { case FORMAT_U8: @@ -54,16 +70,6 @@ Mixer::Mixer(DeviceSpecs specs) : } } -DeviceSpecs Mixer::getSpecs() const -{ - return m_specs; -} - -void Mixer::setSpecs(Specs specs) -{ - m_specs.specs = specs; -} - void Mixer::clear(int length) { m_buffer.assureSize(length * AUD_SAMPLE_SIZE(m_specs)); -- cgit v1.2.3