From 2a095d8bfee39cb6f7f4f108d7febf9780bb8241 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B6rg=20M=C3=BCller?= Date: Fri, 14 Jan 2022 22:51:35 +0100 Subject: Audaspace: port bugfixes from upstream. Windows audio backend (WASAPI) now automatically switches to the selected audio device in windows. --- extern/audaspace/CMakeLists.txt | 6 +- extern/audaspace/bindings/python/setup.py.in | 27 ++++---- extern/audaspace/plugins/wasapi/WASAPIDevice.cpp | 80 +++++++++++++++++++++++- extern/audaspace/plugins/wasapi/WASAPIDevice.h | 15 ++++- 4 files changed, 109 insertions(+), 19 deletions(-) (limited to 'extern') diff --git a/extern/audaspace/CMakeLists.txt b/extern/audaspace/CMakeLists.txt index 8493fe3e67d..9877c8f7768 100644 --- a/extern/audaspace/CMakeLists.txt +++ b/extern/audaspace/CMakeLists.txt @@ -1092,12 +1092,12 @@ if(WITH_PYTHON) configure_file(${PYTHON_SOURCE_DIRECTORY}/setup.py.in ${CMAKE_CURRENT_BINARY_DIR}/setup.py ESCAPE_QUOTES @ONLY) if(APPLE) - add_custom_command(OUTPUT build COMMAND MACOSX_DEPLOYMENT_TARGET=${CMAKE_OSX_DEPLOYMENT_TARGET} ${PYTHON_EXECUTABLE} setup.py build DEPENDS ${PYTHON_SRC} ${PYTHON_HDR}) + add_custom_command(OUTPUT build COMMAND MACOSX_DEPLOYMENT_TARGET=${CMAKE_OSX_DEPLOYMENT_TARGET} ${PYTHON_EXECUTABLE} setup.py build DEPENDS ${PYTHON_SRC} ${PYTHON_HDR} setup.py) elseif(WIN32) set(ENV{VS100COMNTOOLS} $ENV{VS120COMNTOOLS}) - add_custom_command(OUTPUT build COMMAND ${PYTHON_EXECUTABLE} setup.py build DEPENDS ${PYTHON_SRC} ${PYTHON_HDR}) + add_custom_command(OUTPUT build COMMAND ${PYTHON_EXECUTABLE} setup.py build DEPENDS ${PYTHON_SRC} ${PYTHON_HDR} setup.py) else() - add_custom_command(OUTPUT build COMMAND ${PYTHON_EXECUTABLE} setup.py build DEPENDS ${PYTHON_SRC} ${PYTHON_HDR}) + add_custom_command(OUTPUT build COMMAND ${PYTHON_EXECUTABLE} setup.py build DEPENDS ${PYTHON_SRC} ${PYTHON_HDR} setup.py) endif() add_custom_target(pythonmodule ALL DEPENDS build SOURCES ${PYTHON_SOURCE_DIRECTORY}/setup.py.in ${PYTHON_SRC} ${PYTHON_HDR}) add_dependencies(pythonmodule audaspace) diff --git a/extern/audaspace/bindings/python/setup.py.in b/extern/audaspace/bindings/python/setup.py.in index 5ad1a37db3a..0e6666e06a0 100644 --- a/extern/audaspace/bindings/python/setup.py.in +++ b/extern/audaspace/bindings/python/setup.py.in @@ -8,20 +8,20 @@ import numpy from distutils.core import setup, Extension if len(sys.argv) > 2 and sys.argv[1] == '--build-docs': - import subprocess - from distutils.core import Distribution - from distutils.command.build import build + import subprocess + from distutils.core import Distribution + from distutils.command.build import build - dist = Distribution() - cmd = build(dist) - cmd.finalize_options() - #print(cmd.build_platlib) + dist = Distribution() + cmd = build(dist) + cmd.finalize_options() + #print(cmd.build_platlib) - os.environ['PYTHONPATH'] = os.path.join(os.getcwd(), cmd.build_platlib) - os.environ['LD_LIBRARY_PATH'] = os.getcwd() + os.environ['PYTHONPATH'] = os.path.join(os.getcwd(), cmd.build_platlib) + os.environ['LD_LIBRARY_PATH'] = os.getcwd() - ret = subprocess.call(sys.argv[2:]) - sys.exit(ret) + ret = subprocess.call(sys.argv[2:]) + sys.exit(ret) # the following line is not working due to https://bugs.python.org/issue9023 @@ -43,7 +43,8 @@ audaspace = Extension( library_dirs = ['.', 'Release', 'Debug'], language = 'c++', extra_compile_args = extra_args, - sources = [os.path.join(source_directory, file) for file in ['PyAPI.cpp', 'PyDevice.cpp', 'PyHandle.cpp', 'PySound.cpp', 'PySequenceEntry.cpp', 'PySequence.cpp', 'PyPlaybackManager.cpp', 'PyDynamicMusic.cpp', 'PyThreadPool.cpp', 'PySource.cpp'] + (['PyImpulseResponse.cpp', 'PyHRTF.cpp'] if '@WITH_FFTW@' == 'ON' else [])] + define_macros = [('WITH_CONVOLUTION', None)] if '@WITH_FFTW@' == 'ON' else [], + sources = [os.path.join(source_directory, file) for file in ['PyAPI.cpp', 'PyDevice.cpp', 'PyHandle.cpp', 'PySound.cpp', 'PySequenceEntry.cpp', 'PySequence.cpp', 'PyPlaybackManager.cpp', 'PyDynamicMusic.cpp', 'PyThreadPool.cpp', 'PySource.cpp'] + (['PyImpulseResponse.cpp', 'PyHRTF.cpp'] if '@WITH_FFTW@' == 'ON' else [])] ) setup( @@ -56,6 +57,6 @@ setup( license = 'Apache License 2.0', long_description = codecs.open(os.path.join(source_directory, '../../README.md'), 'r', 'utf-8').read(), ext_modules = [audaspace], - headers = [os.path.join(source_directory, file) for file in ['PyAPI.h', 'PyDevice.h', 'PyHandle.h', 'PySound.h', 'PySequenceEntry.h', 'PySequence.h', 'PyPlaybackManager.h', 'PyDynamicMusic.h', 'PyThreadPool.h', 'PySource.h'] + (['PyImpulseResponse.h', 'PyHRTF.h'] if '@WITH_FFTW@' == 'ON' else [])] + ['Audaspace.h'] + headers = [os.path.join(source_directory, file) for file in ['PyAPI.h', 'PyDevice.h', 'PyHandle.h', 'PySound.h', 'PySequenceEntry.h', 'PySequence.h', 'PyPlaybackManager.h', 'PyDynamicMusic.h', 'PyThreadPool.h', 'PySource.h'] + (['PyImpulseResponse.h', 'PyHRTF.h'] if '@WITH_FFTW@' == 'ON' else [])] + ['Audaspace.h'] ) diff --git a/extern/audaspace/plugins/wasapi/WASAPIDevice.cpp b/extern/audaspace/plugins/wasapi/WASAPIDevice.cpp index a5382bb9692..a8387dd9489 100644 --- a/extern/audaspace/plugins/wasapi/WASAPIDevice.cpp +++ b/extern/audaspace/plugins/wasapi/WASAPIDevice.cpp @@ -95,6 +95,13 @@ void WASAPIDevice::runMixingThread() sleep_duration = std::chrono::milliseconds(buffer_size * 1000 / int(m_specs.rate) / 2); } + if(m_default_device_changed) + { + m_default_device_changed = false; + result = AUDCLNT_E_DEVICE_INVALIDATED; + goto stop_thread; + } + if(FAILED(result = m_audio_client->GetCurrentPadding(&padding))) goto stop_thread; @@ -296,13 +303,78 @@ bool WASAPIDevice::setupDevice(DeviceSpecs &specs) return true; } +ULONG WASAPIDevice::AddRef() +{ + return InterlockedIncrement(&m_reference_count); +} + +ULONG WASAPIDevice::Release() +{ + ULONG reference_count = InterlockedDecrement(&m_reference_count); + + if(0 == reference_count) + delete this; + + return reference_count; +} + +HRESULT WASAPIDevice::QueryInterface(REFIID riid, void **ppvObject) +{ + if(riid == __uuidof(IMMNotificationClient)) + { + *ppvObject = reinterpret_cast(this); + AddRef(); + } + else if(riid == IID_IUnknown) + { + *ppvObject = reinterpret_cast(this); + AddRef(); + } + else + { + *ppvObject = nullptr; + return E_NOINTERFACE; + } + + return S_OK; +} + +HRESULT WASAPIDevice::OnDeviceStateChanged(LPCWSTR pwstrDeviceId, DWORD dwNewState) +{ + return S_OK; +} + +HRESULT WASAPIDevice::OnDeviceAdded(LPCWSTR pwstrDeviceId) +{ + return S_OK; +} + +HRESULT WASAPIDevice::OnDeviceRemoved(LPCWSTR pwstrDeviceId) +{ + return S_OK; +} + +HRESULT WASAPIDevice::OnDefaultDeviceChanged(EDataFlow flow, ERole role, LPCWSTR pwstrDeviceId) +{ + if(flow != EDataFlow::eCapture) + m_default_device_changed = true; + + return S_OK; +} + +HRESULT WASAPIDevice::OnPropertyValueChanged(LPCWSTR pwstrDeviceId, const PROPERTYKEY key) +{ + return S_OK; +} + 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({}) + m_wave_format_extensible({}), + m_default_device_changed(false), + m_reference_count(1) { // initialize COM if it hasn't happened yet CoInitializeEx(nullptr, COINIT_MULTITHREADED); @@ -327,6 +399,8 @@ WASAPIDevice::WASAPIDevice(DeviceSpecs specs, int buffersize) : create(); + m_imm_device_enumerator->RegisterEndpointNotificationCallback(this); + return; error: @@ -340,6 +414,8 @@ WASAPIDevice::~WASAPIDevice() { stopMixingThread(); + m_imm_device_enumerator->UnregisterEndpointNotificationCallback(this); + SafeRelease(&m_audio_client); SafeRelease(&m_imm_device); SafeRelease(&m_imm_device_enumerator); diff --git a/extern/audaspace/plugins/wasapi/WASAPIDevice.h b/extern/audaspace/plugins/wasapi/WASAPIDevice.h index 3b11adc98ef..24d0fedc191 100644 --- a/extern/audaspace/plugins/wasapi/WASAPIDevice.h +++ b/extern/audaspace/plugins/wasapi/WASAPIDevice.h @@ -40,7 +40,7 @@ AUD_NAMESPACE_BEGIN /** * This device plays back through WASAPI, the Windows audio API. */ -class AUD_PLUGIN_API WASAPIDevice : public ThreadedDevice +class AUD_PLUGIN_API WASAPIDevice : IMMNotificationClient, public ThreadedDevice { private: int m_buffersize; @@ -48,6 +48,8 @@ private: IMMDevice* m_imm_device; IAudioClient* m_audio_client; WAVEFORMATEXTENSIBLE m_wave_format_extensible; + bool m_default_device_changed; + LONG m_reference_count; AUD_LOCAL HRESULT setupRenderClient(IAudioRenderClient*& render_client, UINT32& buffer_size); @@ -58,6 +60,17 @@ private: AUD_LOCAL bool setupDevice(DeviceSpecs& specs); + // IUnknown implementation + ULONG STDMETHODCALLTYPE AddRef(); + ULONG STDMETHODCALLTYPE Release(); + HRESULT STDMETHODCALLTYPE QueryInterface(REFIID riid, void **ppvObject); + // IMMNotificationClient implementation + HRESULT STDMETHODCALLTYPE OnDeviceStateChanged(LPCWSTR pwstrDeviceId, DWORD dwNewState); + HRESULT STDMETHODCALLTYPE OnDeviceAdded(LPCWSTR pwstrDeviceId); + HRESULT STDMETHODCALLTYPE OnDeviceRemoved(LPCWSTR pwstrDeviceId); + HRESULT STDMETHODCALLTYPE OnDefaultDeviceChanged(EDataFlow flow, ERole role, LPCWSTR pwstrDeviceId); + HRESULT STDMETHODCALLTYPE OnPropertyValueChanged(LPCWSTR pwstrDeviceId, const PROPERTYKEY key); + // delete copy constructor and operator= WASAPIDevice(const WASAPIDevice&) = delete; WASAPIDevice& operator=(const WASAPIDevice&) = delete; -- cgit v1.2.3