mirror of
https://github.com/spice2x/spice2x.github.io.git
synced 2026-08-01 22:30:42 -07:00
audio: retain WASAPI clients only after initialization (#807)
## Link to GitHub Issue or related Pull Request, if one exists Fixes #782 ## Description of change This addresses an issue only seen on Linux + WINE. Defer retaining WASAPI clients until `Initialize` succeeds. Before this PR, temporary clients created during DirectShow device capability probing were retained as active audio clients, causing Wine/Linux crashes in DDR and popn. Also covers IAudioClient3, dummy, ASIO, WaveOut, and null-device initialization paths, for consistency. ## Testing WIP
This commit is contained in:
@@ -53,9 +53,39 @@ namespace hooks::audio {
|
|||||||
std::string ASIO_DRIVER_NAME = "";
|
std::string ASIO_DRIVER_NAME = "";
|
||||||
bool ASIO_FORCE_UNLOAD_ON_STOP = false;
|
bool ASIO_FORCE_UNLOAD_ON_STOP = false;
|
||||||
|
|
||||||
// private globals
|
|
||||||
IAudioClient *CLIENT = nullptr;
|
|
||||||
std::mutex INITIALIZE_LOCK; // for asio
|
std::mutex INITIALIZE_LOCK; // for asio
|
||||||
|
|
||||||
|
IAudioClient *CLIENT = nullptr;
|
||||||
|
static std::mutex CLIENT_LOCK;
|
||||||
|
static bool CLIENT_STOPPING = false;
|
||||||
|
|
||||||
|
void set_active_client(IAudioClient *client, const char *source) {
|
||||||
|
IAudioClient *previous_client = nullptr;
|
||||||
|
{
|
||||||
|
std::lock_guard lock(CLIENT_LOCK);
|
||||||
|
|
||||||
|
if (CLIENT_STOPPING || CLIENT == client) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (client) {
|
||||||
|
client->AddRef();
|
||||||
|
}
|
||||||
|
|
||||||
|
previous_client = CLIENT;
|
||||||
|
CLIENT = client;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (previous_client) {
|
||||||
|
previous_client->Release();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (client) {
|
||||||
|
log_info(
|
||||||
|
"audio",
|
||||||
|
"active client selected by {} after successful initialization",
|
||||||
|
source);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static HRESULT STDAPICALLTYPE CoCreateInstance_hook(
|
static HRESULT STDAPICALLTYPE CoCreateInstance_hook(
|
||||||
@@ -133,11 +163,17 @@ namespace hooks::audio {
|
|||||||
|
|
||||||
void stop() {
|
void stop() {
|
||||||
log_info("audio", "stopping");
|
log_info("audio", "stopping");
|
||||||
if (CLIENT) {
|
IAudioClient *client = nullptr;
|
||||||
CLIENT->Stop();
|
{
|
||||||
CLIENT->Release();
|
std::lock_guard lock(CLIENT_LOCK);
|
||||||
|
CLIENT_STOPPING = true;
|
||||||
|
client = CLIENT;
|
||||||
CLIENT = nullptr;
|
CLIENT = nullptr;
|
||||||
}
|
}
|
||||||
|
if (client) {
|
||||||
|
client->Stop();
|
||||||
|
client->Release();
|
||||||
|
}
|
||||||
stop_low_latency();
|
stop_low_latency();
|
||||||
|
|
||||||
// release the ASIO drivers we kept pinned for the process lifetime now that we are
|
// release the ASIO drivers we kept pinned for the process lifetime now that we are
|
||||||
|
|||||||
@@ -14,4 +14,6 @@ namespace hooks::audio {
|
|||||||
extern std::mutex INITIALIZE_LOCK;
|
extern std::mutex INITIALIZE_LOCK;
|
||||||
extern bool VOLUME_HOOK_ENABLED;
|
extern bool VOLUME_HOOK_ENABLED;
|
||||||
extern LowLatencyAudioClient *LOW_LATENCY_CLIENT;
|
extern LowLatencyAudioClient *LOW_LATENCY_CLIENT;
|
||||||
|
|
||||||
|
void set_active_client(IAudioClient *client, const char *source);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -95,11 +95,6 @@ HRESULT STDMETHODCALLTYPE WrappedIMMDevice::Activate(
|
|||||||
}
|
}
|
||||||
std::lock_guard initialize_guard(hooks::audio::INITIALIZE_LOCK, std::adopt_lock);
|
std::lock_guard initialize_guard(hooks::audio::INITIALIZE_LOCK, std::adopt_lock);
|
||||||
|
|
||||||
// release old audio client if initialized
|
|
||||||
if (hooks::audio::CLIENT) {
|
|
||||||
hooks::audio::CLIENT->Release();
|
|
||||||
}
|
|
||||||
|
|
||||||
IAudioClient *client = nullptr;
|
IAudioClient *client = nullptr;
|
||||||
if (iid == IID_IAudioClient) {
|
if (iid == IID_IAudioClient) {
|
||||||
client = wrap_audio_client(reinterpret_cast<IAudioClient *>(*ppInterface));
|
client = wrap_audio_client(reinterpret_cast<IAudioClient *>(*ppInterface));
|
||||||
@@ -107,9 +102,6 @@ HRESULT STDMETHODCALLTYPE WrappedIMMDevice::Activate(
|
|||||||
client = wrap_audio_client3(reinterpret_cast<IAudioClient3 *>(*ppInterface));
|
client = wrap_audio_client3(reinterpret_cast<IAudioClient3 *>(*ppInterface));
|
||||||
}
|
}
|
||||||
*ppInterface = client;
|
*ppInterface = client;
|
||||||
// persist the audio client
|
|
||||||
hooks::audio::CLIENT = client;
|
|
||||||
hooks::audio::CLIENT->AddRef();
|
|
||||||
|
|
||||||
} else if (iid == __uuidof(IAudioEndpointVolume) && hooks::audio::VOLUME_HOOK_ENABLED) {
|
} else if (iid == __uuidof(IAudioEndpointVolume) && hooks::audio::VOLUME_HOOK_ENABLED) {
|
||||||
*ppInterface = new WrappedIAudioEndpointVolume(reinterpret_cast<IAudioEndpointVolume *>(*ppInterface));
|
*ppInterface = new WrappedIAudioEndpointVolume(reinterpret_cast<IAudioEndpointVolume *>(*ppInterface));
|
||||||
|
|||||||
@@ -153,19 +153,9 @@ HRESULT STDMETHODCALLTYPE NullMMDevice::Activate(
|
|||||||
log_info("audio::null", "NullMMDevice::Activate {}", guid2s(iid));
|
log_info("audio::null", "NullMMDevice::Activate {}", guid2s(iid));
|
||||||
|
|
||||||
if (iid == IID_IAudioClient) {
|
if (iid == IID_IAudioClient) {
|
||||||
|
|
||||||
// release any previously persisted client
|
|
||||||
if (hooks::audio::CLIENT != nullptr) {
|
|
||||||
hooks::audio::CLIENT->Release();
|
|
||||||
}
|
|
||||||
|
|
||||||
auto *client = static_cast<IAudioClient *>(new DummyIAudioClient(new NullDiscardBackend()));
|
auto *client = static_cast<IAudioClient *>(new DummyIAudioClient(new NullDiscardBackend()));
|
||||||
*ppInterface = client;
|
*ppInterface = client;
|
||||||
|
|
||||||
// persist the audio client
|
|
||||||
hooks::audio::CLIENT = client;
|
|
||||||
hooks::audio::CLIENT->AddRef();
|
|
||||||
|
|
||||||
return S_OK;
|
return S_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -302,6 +302,7 @@ HRESULT STDMETHODCALLTYPE WrappedIAudioClient::Initialize(
|
|||||||
device_format->nChannels * (device_format->wBitsPerSample / 8));
|
device_format->nChannels * (device_format->wBitsPerSample / 8));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
hooks::audio::set_active_client(this, "WrappedIAudioClient::Initialize");
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
HRESULT STDMETHODCALLTYPE WrappedIAudioClient::GetBufferSize(UINT32 *pNumBufferFrames) {
|
HRESULT STDMETHODCALLTYPE WrappedIAudioClient::GetBufferSize(UINT32 *pNumBufferFrames) {
|
||||||
@@ -651,5 +652,6 @@ HRESULT STDMETHODCALLTYPE WrappedIAudioClient::InitializeSharedAudioStream(
|
|||||||
log_info("audio::wasapi", "IAudioClient3::InitializeSharedAudioStream success, hr={}", FMT_HRESULT(ret));
|
log_info("audio::wasapi", "IAudioClient3::InitializeSharedAudioStream success, hr={}", FMT_HRESULT(ret));
|
||||||
copy_wave_format(&hooks::audio::FORMAT, pFormat);
|
copy_wave_format(&hooks::audio::FORMAT, pFormat);
|
||||||
copy_wave_format(&this->device_format, pFormat);
|
copy_wave_format(&this->device_format, pFormat);
|
||||||
|
hooks::audio::set_active_client(this, "WrappedIAudioClient::InitializeSharedAudioStream");
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -75,13 +75,17 @@ HRESULT STDMETHODCALLTYPE DummyIAudioClient::Initialize(
|
|||||||
log_info("audio::wasapi", "IAudioClient::Initialize hook hit");
|
log_info("audio::wasapi", "IAudioClient::Initialize hook hit");
|
||||||
print_format(ShareMode, StreamFlags, hnsBufferDuration, hnsPeriodicity, pFormat);
|
print_format(ShareMode, StreamFlags, hnsBufferDuration, hnsPeriodicity, pFormat);
|
||||||
|
|
||||||
CHECK_RESULT(this->backend->on_initialize(
|
HRESULT ret = this->backend->on_initialize(
|
||||||
&ShareMode,
|
&ShareMode,
|
||||||
&StreamFlags,
|
&StreamFlags,
|
||||||
&hnsBufferDuration,
|
&hnsBufferDuration,
|
||||||
&hnsPeriodicity,
|
&hnsPeriodicity,
|
||||||
pFormat,
|
pFormat,
|
||||||
AudioSessionGuid));
|
AudioSessionGuid);
|
||||||
|
if (SUCCEEDED(ret)) {
|
||||||
|
hooks::audio::set_active_client(this, "DummyIAudioClient::Initialize");
|
||||||
|
}
|
||||||
|
CHECK_RESULT(ret);
|
||||||
}
|
}
|
||||||
HRESULT STDMETHODCALLTYPE DummyIAudioClient::GetBufferSize(UINT32 *pNumBufferFrames) {
|
HRESULT STDMETHODCALLTYPE DummyIAudioClient::GetBufferSize(UINT32 *pNumBufferFrames) {
|
||||||
static std::once_flag printed;
|
static std::once_flag printed;
|
||||||
|
|||||||
Reference in New Issue
Block a user