Merge pull request #4789 from PrintOrb/fix/audioout-shared-sdl-init

fix(audioout): serialize SDL audio initialization for Dark Forces
This commit is contained in:
Dean 2026-10-14 15:35:56 +13:00 committed by GitHub
commit 6d032559c3
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
6 changed files with 98 additions and 3 deletions

View File

@ -1,5 +1,6 @@
add_library(libSceAudioOut SHARED EXCLUDE_FROM_ALL
${CMAKE_CURRENT_SOURCE_DIR}/src/AudioOut.cpp
${CMAKE_CURRENT_SOURCE_DIR}/src/AudioOutSdl.cpp
${CMAKE_CURRENT_SOURCE_DIR}/src/AudioOut2Context.cpp
${CMAKE_CURRENT_SOURCE_DIR}/src/AudioOut2PadMix.cpp
${CMAKE_CURRENT_SOURCE_DIR}/src/AudioOut2Port.cpp
@ -17,6 +18,17 @@ target_link_libraries(libSceAudioOut PRIVATE libc libkernel SDL2-static)
configure_windows_unwind(libSceAudioOut)
if(BUILD_TESTING)
add_test_executable(audio_out_initialization_tests tests/AudioOutInitialization.cpp src/AudioOutSdl.cpp)
target_include_directories(audio_out_initialization_tests PRIVATE ${LIBS_INCLUDE_DIR} ${ANYPS5_SDL2_INCLUDE_DIR})
target_link_libraries(audio_out_initialization_tests PRIVATE libc)
configure_windows_unwind(audio_out_initialization_tests)
add_test(NAME audio_out_initialization COMMAND audio_out_initialization_tests)
set_tests_properties(audio_out_initialization PROPERTIES TIMEOUT 10)
if(WIN32)
set_tests_properties(audio_out_initialization PROPERTIES ENVIRONMENT_MODIFICATION
"PATH=path_list_prepend:$<TARGET_FILE_DIR:libc>")
endif()
add_test_executable(audio_out2_pad_mix_tests tests/AudioOut2PadMix.cpp src/AudioOut2PadMix.cpp)
target_include_directories(audio_out2_pad_mix_tests PRIVATE ${LIBS_INCLUDE_DIR})
target_link_libraries(audio_out2_pad_mix_tests PRIVATE libc)

View File

@ -0,0 +1,6 @@
#ifndef CORE_LIBS_PRX_LIBSCEAUDIOOUT_INCLUDE_AUDIOOUTSDL_HPP
#define CORE_LIBS_PRX_LIBSCEAUDIOOUT_INCLUDE_AUDIOOUTSDL_HPP
int InitializeSdlAudio_nid_no_patch();
#endif

View File

@ -16,6 +16,7 @@
#include "SceTypes.hpp"
#include "prx/libc/include/PreciseWait.hpp"
#include "prx/libkernel/Time/include/Time.hpp"
#include "prx/libSceAudioOut/include/AudioOutSdl.hpp"
static constexpr int PORT_TYPE_MAIN = 0;
static constexpr int PORT_TYPE_BGM = 1;
@ -108,7 +109,7 @@ static bool ensureSdlAudio() {
if (g_sdlInitialized) {
return true;
}
if (SDL_InitSubSystem(SDL_INIT_AUDIO) < 0) {
if (InitializeSdlAudio_nid_no_patch() < 0) {
return false;
}
g_sdlInitialized = true;

View File

@ -14,6 +14,7 @@
#include "prx/libc/include/General.hpp"
#include "prx/libc/include/PreciseWait.hpp"
#include "AudioOut2Internal.hpp"
#include "prx/libSceAudioOut/include/AudioOutSdl.hpp"
// An AudioOut2 context is the hardware output queue: every push appends a grain (num_grains samples)
// to a queue of queue_depth grains that plays in real time. Titles pace their mixer on that queue, so
@ -106,7 +107,7 @@ static std::uint32_t QueueLevel(AudioOut2Context& context, Clock::time_point now
static void DeviceCallback(void* userdata, Uint8* stream, int length);
static void OpenDevice(AudioOut2Context& context) {
if (SDL_InitSubSystem(SDL_INIT_AUDIO) < 0) {
if (InitializeSdlAudio_nid_no_patch() < 0) {
AUDIOOUT2_TRACE("SDL audio init failed: %s\n", SDL_GetError());
return;
}
@ -134,7 +135,7 @@ static void CloseDevice(SDL_AudioDeviceID device) {
}
static void OpenPadDevice(AudioOut2Context& context) {
if (SDL_WasInit(SDL_INIT_AUDIO) == 0 && SDL_InitSubSystem(SDL_INIT_AUDIO) < 0) return;
if (SDL_WasInit(SDL_INIT_AUDIO) == 0 && InitializeSdlAudio_nid_no_patch() < 0) return;
const int count = SDL_GetNumAudioDevices(0);
for (int index = 0; index < count; index++) {
const char* listed = SDL_GetAudioDeviceName(index, 0);

View File

@ -0,0 +1,10 @@
#include "prx/libSceAudioOut/include/AudioOutSdl.hpp"
#include "SDL.h"
#include <mutex>
int InitializeSdlAudio_nid_no_patch() {
static std::mutex initializationMutex;
std::lock_guard lock(initializationMutex);
return SDL_InitSubSystem(SDL_INIT_AUDIO);
}

View File

@ -0,0 +1,65 @@
#define SDL_MAIN_HANDLED
#include "prx/libSceAudioOut/include/AudioOutSdl.hpp"
#include "SDL.h"
#include <chrono>
#include <condition_variable>
#include <cstdio>
#include <cstdlib>
#include <mutex>
#include <thread>
namespace {
std::mutex stateMutex;
std::condition_variable stateChanged;
int calls = 0;
int active = 0;
int peak = 0;
bool secondEntered = false;
bool secondReady = false;
bool validFlags = true;
}
extern "C" int SDLCALL SDL_InitSubSystem(Uint32 flags) {
std::unique_lock lock(stateMutex);
validFlags = validFlags && flags == SDL_INIT_AUDIO;
const int call = ++calls;
++active;
if (active > peak) peak = active;
stateChanged.notify_all();
if (call == 1) {
stateChanged.wait(lock, [] { return secondReady; });
stateChanged.wait_for(lock, std::chrono::milliseconds(500), [] { return secondEntered; });
} else {
secondEntered = true;
stateChanged.notify_all();
}
--active;
return call == 1 ? -1 : 0;
}
int main() {
int firstResult = 99;
int secondResult = 99;
std::thread first([&] { firstResult = InitializeSdlAudio_nid_no_patch(); });
{
std::unique_lock lock(stateMutex);
stateChanged.wait(lock, [] { return calls == 1; });
}
std::thread second([&] {
{
std::lock_guard lock(stateMutex);
secondReady = true;
stateChanged.notify_all();
}
secondResult = InitializeSdlAudio_nid_no_patch();
});
first.join();
second.join();
if (peak != 1 || calls != 2 || active != 0 || !validFlags || firstResult != -1 || secondResult != 0) {
std::fprintf(stderr, "audio initialization overlap or return-value mismatch: peak=%d calls=%d results=%d,%d\n", peak, calls, firstResult, secondResult);
return 1;
}
std::puts("Serialized audio initialization: PASS");
return 0;
}