From bdbdd42bd0fc066db54846fdf91b44f5106cff3b Mon Sep 17 00:00:00 2001 From: "Skyth (Asilkan)" <19259897+blueskythlikesclouds@users.noreply.github.com> Date: Wed, 22 Jan 2025 18:51:51 +0300 Subject: [PATCH] Implement config option to toggle surround sound. (#144) --- UnleashedRecomp/CMakeLists.txt | 11 +- UnleashedRecomp/apu/audio.h | 1 + UnleashedRecomp/apu/driver/sdl2_driver.cpp | 55 +++++++-- UnleashedRecomp/apu/driver/sdl2_driver.h | 3 - UnleashedRecomp/apu/driver/xaudio_driver.cpp | 121 ------------------- UnleashedRecomp/apu/driver/xaudio_driver.h | 2 - UnleashedRecomp/locale/config_locale.cpp | 16 +++ UnleashedRecomp/ui/options_menu.cpp | 5 + UnleashedRecomp/user/config.cpp | 6 + UnleashedRecomp/user/config.h | 6 + UnleashedRecomp/user/config_def.h | 1 + 11 files changed, 81 insertions(+), 146 deletions(-) delete mode 100644 UnleashedRecomp/apu/driver/sdl2_driver.h delete mode 100644 UnleashedRecomp/apu/driver/xaudio_driver.cpp delete mode 100644 UnleashedRecomp/apu/driver/xaudio_driver.h diff --git a/UnleashedRecomp/CMakeLists.txt b/UnleashedRecomp/CMakeLists.txt index 5104155..011244b 100644 --- a/UnleashedRecomp/CMakeLists.txt +++ b/UnleashedRecomp/CMakeLists.txt @@ -8,8 +8,6 @@ if (CMAKE_SYSTEM_NAME MATCHES "Linux") option(UNLEASHED_RECOMP_FLATPAK "Configure the build for Flatpak compatibility." OFF) endif() -option(UNLEASHED_RECOMP_XAUDIO2 "Use XAudio2 for audio playback" OFF) - function(BIN2C) cmake_parse_arguments(BIN2C_ARGS "" "TARGET_OBJ;SOURCE_FILE;DEST_FILE;ARRAY_NAME;COMPRESSION_TYPE" "" ${ARGN}) @@ -123,15 +121,10 @@ endif() set(UNLEASHED_RECOMP_APU_CXX_SOURCES "apu/audio.cpp" - "apu/embedded_player.cpp" + "apu/embedded_player.cpp" + "apu/driver/sdl2_driver.cpp" ) -if (UNLEASHED_RECOMP_XAUDIO2) - list(APPEND UNLEASHED_RECOMP_APU_CXX_SOURCES "apu/driver/xaudio_driver.cpp") -else() - list(APPEND UNLEASHED_RECOMP_APU_CXX_SOURCES "apu/driver/sdl2_driver.cpp") -endif() - set(UNLEASHED_RECOMP_HID_CXX_SOURCES "hid/hid.cpp" "hid/driver/sdl_hid.cpp" diff --git a/UnleashedRecomp/apu/audio.h b/UnleashedRecomp/apu/audio.h index aaa0038..1dbe941 100644 --- a/UnleashedRecomp/apu/audio.h +++ b/UnleashedRecomp/apu/audio.h @@ -10,6 +10,7 @@ void XAudioInitializeSystem(); void XAudioRegisterClient(PPCFunc* callback, uint32_t param); void XAudioSubmitFrame(void* samples); +void XAudioConfigValueChangedCallback(class IConfigDef* configDef); uint32_t XAudioRegisterRenderDriverClient(be* callback, be* driver); uint32_t XAudioUnregisterRenderDriverClient(uint32_t driver); diff --git a/UnleashedRecomp/apu/driver/sdl2_driver.cpp b/UnleashedRecomp/apu/driver/sdl2_driver.cpp index de01543..7258bbc 100644 --- a/UnleashedRecomp/apu/driver/sdl2_driver.cpp +++ b/UnleashedRecomp/apu/driver/sdl2_driver.cpp @@ -1,26 +1,29 @@ -#include "sdl2_driver.h" +#include #include #include +#include static PPCFunc* g_clientCallback{}; static uint32_t g_clientCallbackParam{}; // pointer in guest memory static SDL_AudioDeviceID g_audioDevice{}; static bool g_downMixToStereo; -void XAudioInitializeSystem() +static void CreateAudioDevice() { - SDL_SetHint(SDL_HINT_AUDIO_CATEGORY, "playback"); - SDL_SetHint(SDL_HINT_AUDIO_DEVICE_APP_NAME, "Unleashed Recompiled"); - SDL_InitSubSystem(SDL_INIT_AUDIO); + if (g_audioDevice != NULL) + SDL_CloseAudioDevice(g_audioDevice); + + bool surround = Config::ChannelConfiguration == EChannelConfiguration::Surround; + int allowedChanges = surround ? SDL_AUDIO_ALLOW_CHANNELS_CHANGE : 0; SDL_AudioSpec desired{}, obtained{}; desired.freq = XAUDIO_SAMPLES_HZ; desired.format = AUDIO_F32SYS; - desired.channels = XAUDIO_NUM_CHANNELS; + desired.channels = surround ? XAUDIO_NUM_CHANNELS : 2; desired.samples = XAUDIO_NUM_SAMPLES; - g_audioDevice = SDL_OpenAudioDevice(nullptr, 0, &desired, &obtained, SDL_AUDIO_ALLOW_CHANNELS_CHANGE); + g_audioDevice = SDL_OpenAudioDevice(nullptr, 0, &desired, &obtained, allowedChanges); - if (obtained.channels != 2 && obtained.channels != XAUDIO_NUM_CHANNELS) + if (obtained.channels != 2 && obtained.channels != XAUDIO_NUM_CHANNELS) // This check may fail only when surround sound is enabled. { SDL_CloseAudioDevice(g_audioDevice); g_audioDevice = SDL_OpenAudioDevice(nullptr, 0, &desired, &obtained, 0); @@ -29,7 +32,16 @@ void XAudioInitializeSystem() g_downMixToStereo = (obtained.channels == 2); } +void XAudioInitializeSystem() +{ + SDL_SetHint(SDL_HINT_AUDIO_CATEGORY, "playback"); + SDL_SetHint(SDL_HINT_AUDIO_DEVICE_APP_NAME, "Unleashed Recompiled"); + SDL_InitSubSystem(SDL_INIT_AUDIO); + CreateAudioDevice(); +} + static std::unique_ptr g_audioThread; +static volatile bool g_audioThreadShouldExit; static void AudioThread() { @@ -39,7 +51,7 @@ static void AudioThread() size_t channels = g_downMixToStereo ? 2 : XAUDIO_NUM_CHANNELS; - while (true) + while (!g_audioThreadShouldExit) { uint32_t queuedAudioSize = SDL_GetQueuedAudioSize(g_audioDevice); constexpr size_t MAX_LATENCY = 10; @@ -62,6 +74,13 @@ static void AudioThread() } } +static void CreateAudioThread() +{ + SDL_PauseAudioDevice(g_audioDevice, 0); + g_audioThreadShouldExit = false; + g_audioThread = std::make_unique(AudioThread); +} + void XAudioRegisterClient(PPCFunc* callback, uint32_t param) { auto* pClientParam = static_cast(g_userHeap.Alloc(sizeof(param))); @@ -70,8 +89,7 @@ void XAudioRegisterClient(PPCFunc* callback, uint32_t param) g_clientCallbackParam = g_memory.MapVirtual(pClientParam); g_clientCallback = callback; - SDL_PauseAudioDevice(g_audioDevice, 0); - g_audioThread = std::make_unique(AudioThread); + CreateAudioThread(); } void XAudioSubmitFrame(void* samples) @@ -119,3 +137,18 @@ void XAudioSubmitFrame(void* samples) SDL_QueueAudio(g_audioDevice, &audioFrames, sizeof(audioFrames)); } } + +void XAudioConfigValueChangedCallback(IConfigDef* configDef) +{ + if (configDef == &Config::ChannelConfiguration) + { + if (g_audioThread->joinable()) + { + g_audioThreadShouldExit = true; + g_audioThread->join(); + } + + CreateAudioDevice(); + CreateAudioThread(); + } +} diff --git a/UnleashedRecomp/apu/driver/sdl2_driver.h b/UnleashedRecomp/apu/driver/sdl2_driver.h deleted file mode 100644 index d80c989..0000000 --- a/UnleashedRecomp/apu/driver/sdl2_driver.h +++ /dev/null @@ -1,3 +0,0 @@ -#pragma once - -#include diff --git a/UnleashedRecomp/apu/driver/xaudio_driver.cpp b/UnleashedRecomp/apu/driver/xaudio_driver.cpp deleted file mode 100644 index 97fc0b3..0000000 --- a/UnleashedRecomp/apu/driver/xaudio_driver.cpp +++ /dev/null @@ -1,121 +0,0 @@ -#include -#include "xaudio_driver.h" -#include -#include -#include -#include - -#define XAUDIO_DRIVER_KEY (uint32_t)('XAUD') - -PPCFunc* volatile g_clientCallback{}; -DWORD g_clientCallbackParam{}; // pointer in guest memory -DWORD g_driverThread{}; - -// TODO: Should use a counted ptr -IXAudio2* g_audio{}; -IXAudio2MasteringVoice* g_masteringVoice{}; -IXAudio2SourceVoice* g_sourceVoice{}; - -constexpr uint32_t g_semaphoreCount = 16; -constexpr uint32_t g_audioFrameSize = 256 * 6; -HANDLE g_audioSemaphore{ CreateSemaphoreA(nullptr, g_semaphoreCount, g_semaphoreCount, nullptr) }; -uint32_t g_audioFrames[g_audioFrameSize * g_semaphoreCount]; -uint32_t g_audioFrameIndex = 0; - -class VoiceCallback : public IXAudio2VoiceCallback -{ - STDMETHOD_(void, OnVoiceProcessingPassStart)(UINT32 BytesRequired) override {} - STDMETHOD_(void, OnVoiceProcessingPassEnd)() override {} - - STDMETHOD_(void, OnBufferStart)(void* pBufferContext) override {} - STDMETHOD_(void, OnBufferEnd)(void* pBufferContext) override - { - ReleaseSemaphore(g_audioSemaphore, 1, nullptr); - } - - STDMETHOD_(void, OnStreamEnd)() override {} - - STDMETHOD_(void, OnLoopEnd)(void* pBufferContext) override {} - STDMETHOD_(void, OnVoiceError)(void* pBufferContext, HRESULT Error) override {} -} gVoiceCallback; - -PPC_FUNC(DriverLoop) -{ - GuestThread::SetThreadName(GetCurrentThreadId(), "Audio Driver"); - - while (true) - { - if (!g_clientCallback) - { - continue; - } - - WaitForSingleObject(g_audioSemaphore, INFINITE); - - ctx.r3.u64 = g_clientCallbackParam; - g_clientCallback(ctx, g_memory.base); - } -} - -void XAudioInitializeSystem() -{ - if (g_audio) - { - return; - } - - //reinterpret_cast( - // GetProcAddress(LoadLibraryA("XAudio2_8.dll"), "XAudio2Create"))(&gAudio, 0, 1); - - XAudio2Create(&g_audio); - g_audio->CreateMasteringVoice(&g_masteringVoice); - - WAVEFORMATIEEEFLOATEX format{}; - format.Format.wFormatTag = WAVE_FORMAT_EXTENSIBLE; - format.Format.cbSize = sizeof(format) - sizeof(format.Format); - format.Format.nChannels = XAUDIO_NUM_CHANNELS; - format.Format.nSamplesPerSec = XAUDIO_SAMPLES_HZ; - format.Format.wBitsPerSample = XAUDIO_SAMPLE_BITS; - format.Format.nBlockAlign = (format.Format.nChannels * format.Format.wBitsPerSample) / 8; - format.Format.nAvgBytesPerSec = format.Format.nSamplesPerSec * format.Format.nBlockAlign; - - format.SubFormat = KSDATAFORMAT_SUBTYPE_IEEE_FLOAT; - format.Samples.wValidBitsPerSample = format.Format.wBitsPerSample; - format.dwChannelMask = SPEAKER_FRONT_LEFT | SPEAKER_FRONT_CENTER | SPEAKER_FRONT_RIGHT | - SPEAKER_LOW_FREQUENCY | SPEAKER_BACK_LEFT | SPEAKER_BACK_RIGHT; - - g_audio->CreateSourceVoice(&g_sourceVoice, &format.Format, 0, 1024, &gVoiceCallback); - g_sourceVoice->Start(); - - KeInsertHostFunction(XAUDIO_DRIVER_KEY, DriverLoop); - GuestThread::Start({ XAUDIO_DRIVER_KEY, 0, 0 }, nullptr); -} - -void XAudioRegisterClient(PPCFunc* callback, uint32_t param) -{ - auto* pClientParam = static_cast(g_userHeap.Alloc(sizeof(param))); - ByteSwapInplace(param); - *pClientParam = param; - g_clientCallbackParam = g_memory.MapVirtual(pClientParam); - - g_clientCallback = callback; -} - -void XAudioSubmitFrame(void* samples) -{ - uint32_t* audioFrame = &g_audioFrames[g_audioFrameSize * g_audioFrameIndex]; - g_audioFrameIndex = (g_audioFrameIndex + 1) % g_semaphoreCount; - - for (size_t i = 0; i < XAUDIO_NUM_SAMPLES; i++) - { - for (size_t j = 0; j < 6; j++) - audioFrame[i * XAUDIO_NUM_CHANNELS + j] = ByteSwap(((uint32_t*)samples)[j * XAUDIO_NUM_SAMPLES + i]); - } - - XAUDIO2_BUFFER buffer{}; - buffer.pAudioData = (BYTE*)audioFrame; - buffer.AudioBytes = XAUDIO_NUM_SAMPLES * XAUDIO_NUM_CHANNELS * sizeof(float); - buffer.PlayLength = XAUDIO_NUM_SAMPLES; - - g_sourceVoice->SubmitSourceBuffer(&buffer); -} diff --git a/UnleashedRecomp/apu/driver/xaudio_driver.h b/UnleashedRecomp/apu/driver/xaudio_driver.h deleted file mode 100644 index c8f99bf..0000000 --- a/UnleashedRecomp/apu/driver/xaudio_driver.h +++ /dev/null @@ -1,2 +0,0 @@ -#pragma once -#include diff --git a/UnleashedRecomp/locale/config_locale.cpp b/UnleashedRecomp/locale/config_locale.cpp index 36871dc..63038fd 100644 --- a/UnleashedRecomp/locale/config_locale.cpp +++ b/UnleashedRecomp/locale/config_locale.cpp @@ -137,6 +137,22 @@ CONFIG_DEFINE_LOCALE(MusicAttenuation) { ELanguage::English, { "Music Attenuation", "Fade out the game's music when external media is playing." } } }; +CONFIG_DEFINE_LOCALE(ChannelConfiguration) +{ + { ELanguage::English, { "Channel Configuration", "" } } +}; + +CONFIG_DEFINE_ENUM_LOCALE(EChannelConfiguration) +{ + { + ELanguage::English, + { + { EChannelConfiguration::Stereo, { "STEREO", "" } }, + { EChannelConfiguration::Surround, { "SURROUND", "" } } + } + } +}; + CONFIG_DEFINE_LOCALE(VoiceLanguage) { { ELanguage::English, { "Voice Language", "Change the language used for character voices." } } diff --git a/UnleashedRecomp/ui/options_menu.cpp b/UnleashedRecomp/ui/options_menu.cpp index 4d409dc..124dfbc 100644 --- a/UnleashedRecomp/ui/options_menu.cpp +++ b/UnleashedRecomp/ui/options_menu.cpp @@ -5,6 +5,7 @@ #include "exports.h" #include +#include #include #include #include @@ -507,6 +508,7 @@ static void DrawConfigOption(int32_t rowIndex, float yOffset, ConfigDef* conf config->Callback(config); VideoConfigValueChangedCallback(config); + XAudioConfigValueChangedCallback(config); Game_PlaySound("sys_worldmap_finaldecide"); } @@ -538,6 +540,7 @@ static void DrawConfigOption(int32_t rowIndex, float yOffset, ConfigDef* conf if (config->Value != s_oldValue) { VideoConfigValueChangedCallback(config); + XAudioConfigValueChangedCallback(config); if (config->ApplyCallback) config->ApplyCallback(config); @@ -564,6 +567,7 @@ static void DrawConfigOption(int32_t rowIndex, float yOffset, ConfigDef* conf config->MakeDefault(); VideoConfigValueChangedCallback(config); + XAudioConfigValueChangedCallback(config); if (config->Callback) config->Callback(config); @@ -901,6 +905,7 @@ static void DrawConfigOptions() DrawConfigOption(rowCount++, yOffset, &Config::MasterVolume, true); DrawConfigOption(rowCount++, yOffset, &Config::MusicVolume, true); DrawConfigOption(rowCount++, yOffset, &Config::EffectsVolume, true); + DrawConfigOption(rowCount++, yOffset, &Config::ChannelConfiguration, true); DrawConfigOption(rowCount++, yOffset, &Config::VoiceLanguage, OptionsMenu::s_pauseMenuType == SWA::eMenuType_WorldMap, cmnReason); DrawConfigOption(rowCount++, yOffset, &Config::Subtitles, true); DrawConfigOption(rowCount++, yOffset, &Config::MusicAttenuation, AudioPatches::CanAttenuate(), &Localise("Options_Desc_OSNotSupported")); diff --git a/UnleashedRecomp/user/config.cpp b/UnleashedRecomp/user/config.cpp index d952af9..726f7cc 100644 --- a/UnleashedRecomp/user/config.cpp +++ b/UnleashedRecomp/user/config.cpp @@ -37,6 +37,12 @@ CONFIG_DEFINE_ENUM_TEMPLATE(EControllerIcons) { "PlayStation", EControllerIcons::PlayStation } }; +CONFIG_DEFINE_ENUM_TEMPLATE(EChannelConfiguration) +{ + { "Stereo", EChannelConfiguration::Stereo }, + { "Surround", EChannelConfiguration::Surround } +}; + CONFIG_DEFINE_ENUM_TEMPLATE(EVoiceLanguage) { { "English", EVoiceLanguage::English }, diff --git a/UnleashedRecomp/user/config.h b/UnleashedRecomp/user/config.h index ad3654d..1ff6d53 100644 --- a/UnleashedRecomp/user/config.h +++ b/UnleashedRecomp/user/config.h @@ -48,6 +48,12 @@ enum class EControllerIcons : uint32_t PlayStation }; +enum class EChannelConfiguration : uint32_t +{ + Stereo, + Surround +}; + enum class EVoiceLanguage : uint32_t { English, diff --git a/UnleashedRecomp/user/config_def.h b/UnleashedRecomp/user/config_def.h index 90f72d9..9439559 100644 --- a/UnleashedRecomp/user/config_def.h +++ b/UnleashedRecomp/user/config_def.h @@ -16,6 +16,7 @@ CONFIG_DEFINE_ENUM_LOCALISED("Input", EControllerIcons, ControllerIcons, EContro CONFIG_DEFINE_LOCALISED("Audio", float, MasterVolume, 1.0f); CONFIG_DEFINE_LOCALISED("Audio", float, MusicVolume, 1.0f); CONFIG_DEFINE_LOCALISED("Audio", float, EffectsVolume, 1.0f); +CONFIG_DEFINE_ENUM_LOCALISED("Audio", EChannelConfiguration, ChannelConfiguration, EChannelConfiguration::Stereo); CONFIG_DEFINE_ENUM_LOCALISED("Audio", EVoiceLanguage, VoiceLanguage, EVoiceLanguage::English); CONFIG_DEFINE_LOCALISED("Audio", bool, Subtitles, true); CONFIG_DEFINE_LOCALISED("Audio", bool, MusicAttenuation, false);