From 2d623e179bdc2769f3305b12ca6268a174193d1a Mon Sep 17 00:00:00 2001 From: bicarus <202771338+bicarus-dev@users.noreply.github.com> Date: Sun, 1 Mar 2026 13:06:18 -0800 Subject: [PATCH] audio: simplify ASIO backend conversion (#560) ## Link to GitHub Issue or related Pull Request, if one exists ## Description of change Deprecate backend conversion options and replace with one unified option for enabling WASAPI exclusive conversion to ASIO. The new option takes in a string (ASIO driver name) and when filled out, it automatically enables the ASIO backend. This is to avoid confusion that was caused by the previous option - the fact that it was split into two (one to enable the conversion, another to optionally pick which ASIO driver to use) and that user needed to specify an integer as opposed to the driver name. ## Testing Tested on IIDX33. Old options should continue to work. --- src/spice2x/hooks/audio/audio.cpp | 3 +- src/spice2x/hooks/audio/audio.h | 4 +- .../hooks/audio/implementations/asio.cpp | 37 ++++++++++++++++--- src/spice2x/launcher/launcher.cpp | 10 ++++- src/spice2x/launcher/options.cpp | 19 +++++++++- src/spice2x/launcher/options.h | 1 + 6 files changed, 64 insertions(+), 10 deletions(-) diff --git a/src/spice2x/hooks/audio/audio.cpp b/src/spice2x/hooks/audio/audio.cpp index 649d6a3..b4e028e 100644 --- a/src/spice2x/hooks/audio/audio.cpp +++ b/src/spice2x/hooks/audio/audio.cpp @@ -42,7 +42,8 @@ namespace hooks::audio { bool USE_DUMMY = false; WAVEFORMATEXTENSIBLE FORMAT {}; std::optional BACKEND = std::nullopt; - size_t ASIO_DRIVER_ID = 0; + std::optional ASIO_DRIVER_ID = std::nullopt; + std::string ASIO_DRIVER_NAME = ""; bool ASIO_FORCE_UNLOAD_ON_STOP = false; // private globals diff --git a/src/spice2x/hooks/audio/audio.h b/src/spice2x/hooks/audio/audio.h index 7ea4517..9f55426 100644 --- a/src/spice2x/hooks/audio/audio.h +++ b/src/spice2x/hooks/audio/audio.h @@ -1,6 +1,7 @@ #pragma once #include +#include #include #include @@ -20,7 +21,8 @@ namespace hooks::audio { extern bool USE_DUMMY; extern WAVEFORMATEXTENSIBLE FORMAT; extern std::optional BACKEND; - extern size_t ASIO_DRIVER_ID; + extern std::optional ASIO_DRIVER_ID; + extern std::string ASIO_DRIVER_NAME; extern bool ASIO_FORCE_UNLOAD_ON_STOP; extern bool LOW_LATENCY_SHARED_WASAPI; diff --git a/src/spice2x/hooks/audio/implementations/asio.cpp b/src/spice2x/hooks/audio/implementations/asio.cpp index f91c4b2..0fea64d 100644 --- a/src/spice2x/hooks/audio/implementations/asio.cpp +++ b/src/spice2x/hooks/audio/implementations/asio.cpp @@ -218,13 +218,40 @@ void AsioBackend::set_thread_state(AsioThreadState state) { bool AsioBackend::load_driver() { AsioDriverList asio_driver_list; - for (const auto &driver : asio_driver_list.driver_list) { - log_info("audio::asio", "Driver {}", driver.id); - log_info("audio::asio", "... Name : {}", driver.name); - log_info("audio::asio", "... Path : {}", driver.dll_path); + size_t driver_id = 0; + std::string driver_name = ""; + if (hooks::audio::ASIO_DRIVER_ID.has_value()) { + driver_id = hooks::audio::ASIO_DRIVER_ID.value(); } - const auto driver_id = hooks::audio::ASIO_DRIVER_ID; + for (const auto &driver : asio_driver_list.driver_list) { + log_info("audio::asio", " Driver ID : {}", driver.id); + log_info("audio::asio", " ... Name : {}", driver.name); + log_info("audio::asio", " ... Path : {}", driver.dll_path); + + if (!hooks::audio::ASIO_DRIVER_NAME.empty()) { + // match on driver name + if (driver.name == hooks::audio::ASIO_DRIVER_NAME) { + driver_id = driver.id; + driver_name = driver.name; + } + } else if (driver.id == driver_id) { + // match on driver index (0 for default or user-specified) + driver_name = driver.name; + } + } + + if (driver_name.empty()) { + if (!hooks::audio::ASIO_DRIVER_NAME.empty()) { + log_warning( + "audio::asio", + "tried to look for ASIO driver but failed: {}", hooks::audio::ASIO_DRIVER_NAME); + } + log_fatal( + "audio::asio", + "could not find ASIO driver to use for conversion, fix your audio options and try again"); + } + log_info("audio::asio", "will attempt to use the following ASIO driver: ID = {}, Name = {}", driver_id, driver_name); IAsio *driver = nullptr; auto ret = asio_driver_list.open_driver(driver_id, reinterpret_cast(&driver)); diff --git a/src/spice2x/launcher/launcher.cpp b/src/spice2x/launcher/launcher.cpp index 409a866..a506689 100644 --- a/src/spice2x/launcher/launcher.cpp +++ b/src/spice2x/launcher/launcher.cpp @@ -1011,18 +1011,26 @@ int main_implementation(int argc, char *argv[]) { if (options[launcher::Options::spice2x_DisableVolumeHook].value_bool()) { hooks::audio::VOLUME_HOOK_ENABLED = false; } + if (options[launcher::Options::AudioBackend].is_active()) { auto &name = options[launcher::Options::AudioBackend].value_text(); auto backend = hooks::audio::name_to_backend(name.c_str()); if (!backend.has_value() && !cfg::CONFIGURATOR_STANDALONE) { log_fatal("launcher", "invalid audio backend: {}", name); } - hooks::audio::BACKEND = backend; } + if (options[launcher::Options::AsioDriverId].is_active()) { hooks::audio::ASIO_DRIVER_ID = options[launcher::Options::AsioDriverId].value_uint32(); } + if (options[launcher::Options::AsioDriverName].is_active()) { + hooks::audio::BACKEND = hooks::audio::name_to_backend("asio"); + hooks::audio::ASIO_DRIVER_NAME = options[launcher::Options::AsioDriverName].value_text(); + // this new option overrides the old one + hooks::audio::ASIO_DRIVER_ID.reset(); + } + if (options[launcher::Options::AudioDummy].value_bool()) { hooks::audio::USE_DUMMY = true; } diff --git a/src/spice2x/launcher/options.cpp b/src/spice2x/launcher/options.cpp index 157fae9..47fa204 100644 --- a/src/spice2x/launcher/options.cpp +++ b/src/spice2x/launcher/options.cpp @@ -1742,11 +1742,13 @@ static const std::vector OPTION_DEFINITIONS = { .category = "Audio (Hacks)", }, { - .title = "Spice Audio Hook Backend", + // AudioBackend + .title = "Spice Audio Hook Backend (DEPRECATED - use -asioconvert instead)", .name = "audiobackend", .desc = "Selects the audio backend to use when spice audio hook is enabled, overriding exclusive WASAPI. " "Does nothing for games that do not output to exclusive WASAPI.", .type = OptionType::Enum, + .hidden = true, .category = "Audio", .elements = { {"asio", "ASIO"}, @@ -1754,10 +1756,23 @@ static const std::vector OPTION_DEFINITIONS = { }, }, { - .title = "Spice Audio Hook ASIO Driver ID", + // AsioDriverId + .title = "Spice Audio Hook ASIO Driver ID (DEPRECATED - use -asioconvert instead)", .name = "asiodriverid", .desc = "Selects the ASIO driver id to use when Spice Audio Backend is set to ASIO.", .type = OptionType::Integer, + .hidden = true, + .category = "Audio", + }, + { + // AsioDriverName + .title = "WASAPI Exclusive to ASIO Conversion", + .name = "asioconvert", + .desc = "Converts WASAPI Exclusive audio output to ASIO. Value here should match registry key under HKLM\\SOFTWARE\\ASIO\\\n\n" + "Use this if the game is configured to use WASAPI Exclusive but you want to use your ASIO driver instead.\n\n" + "This should only be used as last resort if your audio device does not support WASAPI Exclusive.\n\n" + "Does nothing for games that do not output to exclusive WASAPI.", + .type = OptionType::Text, .category = "Audio", }, { diff --git a/src/spice2x/launcher/options.h b/src/spice2x/launcher/options.h index d95d580..7b6f59e 100644 --- a/src/spice2x/launcher/options.h +++ b/src/spice2x/launcher/options.h @@ -187,6 +187,7 @@ namespace launcher { spice2x_DisableVolumeHook, AudioBackend, AsioDriverId, + AsioDriverName, AudioDummy, DelayBy5Seconds, spice2x_DelayByNSeconds,