Audaspace: port bugfixes from upstream.

Windows audio backend (WASAPI) now automatically switches to
the selected audio device in windows.
This commit is contained in:
Jörg Müller 2022-01-14 22:51:35 +01:00
parent 9fe704800e
commit 2a095d8bfe
4 changed files with 109 additions and 19 deletions

@ -1092,12 +1092,12 @@ if(WITH_PYTHON)
configure_file(${PYTHON_SOURCE_DIRECTORY}/setup.py.in ${CMAKE_CURRENT_BINARY_DIR}/setup.py ESCAPE_QUOTES @ONLY) configure_file(${PYTHON_SOURCE_DIRECTORY}/setup.py.in ${CMAKE_CURRENT_BINARY_DIR}/setup.py ESCAPE_QUOTES @ONLY)
if(APPLE) 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) elseif(WIN32)
set(ENV{VS100COMNTOOLS} $ENV{VS120COMNTOOLS}) 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() 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() endif()
add_custom_target(pythonmodule ALL DEPENDS build SOURCES ${PYTHON_SOURCE_DIRECTORY}/setup.py.in ${PYTHON_SRC} ${PYTHON_HDR}) add_custom_target(pythonmodule ALL DEPENDS build SOURCES ${PYTHON_SOURCE_DIRECTORY}/setup.py.in ${PYTHON_SRC} ${PYTHON_HDR})
add_dependencies(pythonmodule audaspace) add_dependencies(pythonmodule audaspace)

@ -8,20 +8,20 @@ import numpy
from distutils.core import setup, Extension from distutils.core import setup, Extension
if len(sys.argv) > 2 and sys.argv[1] == '--build-docs': if len(sys.argv) > 2 and sys.argv[1] == '--build-docs':
import subprocess import subprocess
from distutils.core import Distribution from distutils.core import Distribution
from distutils.command.build import build from distutils.command.build import build
dist = Distribution() dist = Distribution()
cmd = build(dist) cmd = build(dist)
cmd.finalize_options() cmd.finalize_options()
#print(cmd.build_platlib) #print(cmd.build_platlib)
os.environ['PYTHONPATH'] = os.path.join(os.getcwd(), cmd.build_platlib) os.environ['PYTHONPATH'] = os.path.join(os.getcwd(), cmd.build_platlib)
os.environ['LD_LIBRARY_PATH'] = os.getcwd() os.environ['LD_LIBRARY_PATH'] = os.getcwd()
ret = subprocess.call(sys.argv[2:]) ret = subprocess.call(sys.argv[2:])
sys.exit(ret) sys.exit(ret)
# the following line is not working due to https://bugs.python.org/issue9023 # the following line is not working due to https://bugs.python.org/issue9023
@ -43,7 +43,8 @@ audaspace = Extension(
library_dirs = ['.', 'Release', 'Debug'], library_dirs = ['.', 'Release', 'Debug'],
language = 'c++', language = 'c++',
extra_compile_args = extra_args, 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( setup(
@ -56,6 +57,6 @@ setup(
license = 'Apache License 2.0', license = 'Apache License 2.0',
long_description = codecs.open(os.path.join(source_directory, '../../README.md'), 'r', 'utf-8').read(), long_description = codecs.open(os.path.join(source_directory, '../../README.md'), 'r', 'utf-8').read(),
ext_modules = [audaspace], 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']
) )

@ -95,6 +95,13 @@ void WASAPIDevice::runMixingThread()
sleep_duration = std::chrono::milliseconds(buffer_size * 1000 / int(m_specs.rate) / 2); 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))) if(FAILED(result = m_audio_client->GetCurrentPadding(&padding)))
goto stop_thread; goto stop_thread;
@ -296,13 +303,78 @@ bool WASAPIDevice::setupDevice(DeviceSpecs &specs)
return true; 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<IMMNotificationClient*>(this);
AddRef();
}
else if(riid == IID_IUnknown)
{
*ppvObject = reinterpret_cast<IUnknown*>(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) : WASAPIDevice::WASAPIDevice(DeviceSpecs specs, int buffersize) :
m_buffersize(buffersize), m_buffersize(buffersize),
m_imm_device_enumerator(nullptr), m_imm_device_enumerator(nullptr),
m_imm_device(nullptr), m_imm_device(nullptr),
m_audio_client(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 // initialize COM if it hasn't happened yet
CoInitializeEx(nullptr, COINIT_MULTITHREADED); CoInitializeEx(nullptr, COINIT_MULTITHREADED);
@ -327,6 +399,8 @@ WASAPIDevice::WASAPIDevice(DeviceSpecs specs, int buffersize) :
create(); create();
m_imm_device_enumerator->RegisterEndpointNotificationCallback(this);
return; return;
error: error:
@ -340,6 +414,8 @@ WASAPIDevice::~WASAPIDevice()
{ {
stopMixingThread(); stopMixingThread();
m_imm_device_enumerator->UnregisterEndpointNotificationCallback(this);
SafeRelease(&m_audio_client); SafeRelease(&m_audio_client);
SafeRelease(&m_imm_device); SafeRelease(&m_imm_device);
SafeRelease(&m_imm_device_enumerator); SafeRelease(&m_imm_device_enumerator);

@ -40,7 +40,7 @@ AUD_NAMESPACE_BEGIN
/** /**
* This device plays back through WASAPI, the Windows audio API. * 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: private:
int m_buffersize; int m_buffersize;
@ -48,6 +48,8 @@ private:
IMMDevice* m_imm_device; IMMDevice* m_imm_device;
IAudioClient* m_audio_client; IAudioClient* m_audio_client;
WAVEFORMATEXTENSIBLE m_wave_format_extensible; WAVEFORMATEXTENSIBLE m_wave_format_extensible;
bool m_default_device_changed;
LONG m_reference_count;
AUD_LOCAL HRESULT setupRenderClient(IAudioRenderClient*& render_client, UINT32& buffer_size); AUD_LOCAL HRESULT setupRenderClient(IAudioRenderClient*& render_client, UINT32& buffer_size);
@ -58,6 +60,17 @@ private:
AUD_LOCAL bool setupDevice(DeviceSpecs& specs); 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= // delete copy constructor and operator=
WASAPIDevice(const WASAPIDevice&) = delete; WASAPIDevice(const WASAPIDevice&) = delete;
WASAPIDevice& operator=(const WASAPIDevice&) = delete; WASAPIDevice& operator=(const WASAPIDevice&) = delete;