From 56e58cdf99bbaa354275cac158200c12a88955ab Mon Sep 17 00:00:00 2001 From: BearOso Date: Fri, 13 Dec 2024 17:14:34 -0600 Subject: [PATCH] Qt: Switch to SDL3 for audio and input. SDL3 is not a submodule in the git tree yet, so as to not cause an extra checkout for the other ports. It will eventually go in external/SDL. --- common/audio/s9x_sound_driver_sdl3.cpp | 112 +++++++++++++++++++++++++ common/audio/s9x_sound_driver_sdl3.hpp | 46 ++++++++++ qt/CMakeLists.txt | 25 ++++-- qt/src/ControllerPanel.cpp | 56 ++++++++----- qt/src/EmuApplication.cpp | 18 ++-- qt/src/EmuBinding.cpp | 2 +- qt/src/SDLInputManager.cpp | 60 +++++++------ qt/src/SDLInputManager.hpp | 6 +- 8 files changed, 250 insertions(+), 75 deletions(-) create mode 100644 common/audio/s9x_sound_driver_sdl3.cpp create mode 100644 common/audio/s9x_sound_driver_sdl3.hpp diff --git a/common/audio/s9x_sound_driver_sdl3.cpp b/common/audio/s9x_sound_driver_sdl3.cpp new file mode 100644 index 00000000..05ffa9e3 --- /dev/null +++ b/common/audio/s9x_sound_driver_sdl3.cpp @@ -0,0 +1,112 @@ +/*****************************************************************************\ + Snes9x - Portable Super Nintendo Entertainment System (TM) emulator. + This file is licensed under the Snes9x License. + For further information, consult the LICENSE file in the root directory. +\*****************************************************************************/ + +#include "s9x_sound_driver_sdl3.hpp" +#include "SDL3/SDL_audio.h" +#include + +bool S9xSDL3SoundDriver::write_samples(int16_t *data, int samples) +{ + bool retval = true; + auto empty = buffer.space_empty(); + if (samples > empty) + { + retval = false; + buffer.dump(buffer.buffer_size / 2 - empty); + } + buffer.push(data, samples); + + return retval; +} + +void S9xSDL3SoundDriver::mix(int req, int total) +{ + if (tmp.size() < req / 2) + tmp.resize(req / 2); + + if (buffer.avail() >= req / 2) + { + + buffer.read((int16_t *)(tmp.data()), req / 2); + SDL_PutAudioStreamData(stream, tmp.data(), req); + } + else + { + auto avail = buffer.avail(); + buffer.read((int16_t *)(tmp.data()), avail); + SDL_PutAudioStreamData(stream, tmp.data(), avail * 2); + buffer.add_silence(buffer.buffer_size / 2); + } +} + +S9xSDL3SoundDriver::S9xSDL3SoundDriver() +{ + stream = nullptr; +} + +S9xSDL3SoundDriver::~S9xSDL3SoundDriver() +{ + deinit(); +} + +void S9xSDL3SoundDriver::init() +{ + SDL_InitSubSystem(SDL_INIT_AUDIO); + stop(); +} + +void S9xSDL3SoundDriver::deinit() +{ + stop(); + SDL_DestroyAudioStream(stream); + SDL_QuitSubSystem(SDL_INIT_AUDIO); +} + +void S9xSDL3SoundDriver::start() +{ + SDL_ResumeAudioStreamDevice(stream); +} + +void S9xSDL3SoundDriver::stop() +{ + SDL_PauseAudioStreamDevice(stream); +} + +bool S9xSDL3SoundDriver::open_device(int playback_rate, int buffer_size) +{ + audiospec = {}; + audiospec.freq = playback_rate; + audiospec.channels = 2; + audiospec.format = SDL_AUDIO_S16; + + printf("SDL sound driver initializing...\n"); + + stream = SDL_OpenAudioDeviceStream(SDL_AUDIO_DEVICE_DEFAULT_PLAYBACK, + &audiospec, + [](void *userdata, SDL_AudioStream *stream, int addl, int total) { + ((S9xSDL3SoundDriver *)userdata)->mix(addl, total); + }, this); + + printf("OK\n"); + if (buffer_size < 32) + buffer_size = 32; + + buffer.resize(buffer_size * 4 * audiospec.freq / 1000); + + return true; +} + +int S9xSDL3SoundDriver::space_free() +{ + auto space_empty = buffer.space_empty(); + return space_empty; +} + +std::pair S9xSDL3SoundDriver::buffer_level() +{ + std::pair level = { buffer.space_empty(), buffer.buffer_size }; + return level; +} \ No newline at end of file diff --git a/common/audio/s9x_sound_driver_sdl3.hpp b/common/audio/s9x_sound_driver_sdl3.hpp new file mode 100644 index 00000000..5c0ef931 --- /dev/null +++ b/common/audio/s9x_sound_driver_sdl3.hpp @@ -0,0 +1,46 @@ +/*****************************************************************************\ + Snes9x - Portable Super Nintendo Entertainment System (TM) emulator. + This file is licensed under the Snes9x License. + For further information, consult the LICENSE file in the root directory. +\*****************************************************************************/ + +#ifndef __S9X_SOUND_DRIVER_SDL3_HPP +#define __S9X_SOUND_DRIVER_SDL3_HPP + +#include "SDL3/SDL.h" +// SDL.h may include altivec.h which redefines vector and bool +#undef vector +#undef bool + +#include "s9x_sound_driver.hpp" +#include "../../apu/resampler.h" + +#include +#include +#include + +class S9xSDL3SoundDriver : public S9xSoundDriver +{ + public: + S9xSDL3SoundDriver(); + ~S9xSDL3SoundDriver(); + void init() override; + void deinit() override; + bool open_device(int playback_rate, int buffer_size) override; + void start() override; + void stop() override; + bool write_samples(int16_t *data, int samples) override; + int space_free() override; + std::pair buffer_level() override; + + private: + void mix(int req, int total); + + SDL_AudioStream *stream; + SDL_AudioSpec audiospec; + Resampler buffer; + std::mutex mutex; + std::vector tmp; +}; + +#endif /* __S9X_SOUND_DRIVER_SDL3_HPP */ diff --git a/qt/CMakeLists.txt b/qt/CMakeLists.txt index bb83be04..e632f606 100644 --- a/qt/CMakeLists.txt +++ b/qt/CMakeLists.txt @@ -98,11 +98,10 @@ list(APPEND LIBS Qt6::Widgets Qt6::Gui) list(APPEND INCLUDES ${Qt6Gui_PRIVATE_INCLUDE_DIRS}) find_package(PkgConfig REQUIRED) -pkg_check_modules(SDL REQUIRED sdl2) pkg_check_modules(ZLIB REQUIRED zlib) pkg_check_modules(PNG REQUIRED libpng) -list(APPEND INCLUDES ${SDL_INCLUDE_DIRS} ${ZLIB_INCLUDE_DIRS}) -list(APPEND FLAGS ${SDL_COMPILE_FLAGS} ${ZLIB_COMPILE_FLAGS}) +list(APPEND INCLUDES ${ZLIB_INCLUDE_DIRS}) +list(APPEND FLAGS ${ZLIB_COMPILE_FLAGS}) if(${CMAKE_SYSTEM_NAME} STREQUAL "Windows") if (${CMAKE_CXX_COMPILER_ID} STREQUAL "Clang") @@ -113,8 +112,7 @@ if(${CMAKE_SYSTEM_NAME} STREQUAL "Windows") list(APPEND LIBS libstdc++.a) endif() - list(APPEND LIBS libSDL2.a libz.a libpng.a opengl32 gdi32 winmm imm32 ole32 oleaut32 version uuid advapi32 setupapi shell32 dinput8) - list(APPEND DEFINES SDL_MAIN_HANDLED) + list(APPEND LIBS libz.a libpng.a opengl32 gdi32 winmm imm32 ole32 oleaut32 version uuid advapi32 setupapi shell32 dinput8) list(APPEND PLATFORM_SOURCES ../common/video/opengl/wgl_context.cpp ../external/glad/src/wgl.c @@ -128,7 +126,7 @@ else() endif() list(APPEND INCLUDES ${WAYLAND_INCLUDE_DIRS} ${X11_INCLUDE_DIRS}) - list(APPEND LIBS ${WAYLAND_LIBRARIES} ${PNG_LIBRARIES} ${X11_LIBRARIES} ${ZLIB_LIBRARIES} ${SDL_LIBRARIES}) + list(APPEND LIBS ${WAYLAND_LIBRARIES} ${PNG_LIBRARIES} ${X11_LIBRARIES} ${ZLIB_LIBRARIES}) list(APPEND FLAGS ${WAYLAND_CFLAGS}) pkg_check_modules(PULSEAUDIO libpulse) @@ -156,6 +154,17 @@ else() ../external/glad/src/egl.c) endif() +set(SDL_GPU CACHE BOOL OFF FORCE) +set(SDL_VIDEO CACHE BOOL OFF FORCE) +set(SDL_RENDER CACHE BOOL OFF FORCE) +set(SDL_SHARED BOOL OFF FORCE) +set(SDL_STATIC BOOL ON FORCE) +add_subdirectory("../external/SDL" "SDL" EXCLUDE_FROM_ALL) +list(APPEND LIBS SDL3-static) +list(APPEND INCLUDES ../external/SDL/include) + +list(APPEND DEFINES SDL_MAIN_HANDLED) + list(APPEND PLATFORM_SOURCES ../external/fmt/src/format.cc) list(APPEND INCLUDES ../external/fmt/include) @@ -184,8 +193,8 @@ list(APPEND QT_GUI_SOURCES src/EmuCanvasOpenGL.cpp src/EmuCanvasVulkan.cpp ../external/glad/src/gl.c - ../common/audio/s9x_sound_driver_sdl.cpp - ../common/audio/s9x_sound_driver_sdl.hpp + ../common/audio/s9x_sound_driver_sdl3.cpp + ../common/audio/s9x_sound_driver_sdl3.hpp ../common/audio/s9x_sound_driver_cubeb.cpp ../common/audio/s9x_sound_driver_cubeb.hpp ../filter/2xsai.cpp diff --git a/qt/src/ControllerPanel.cpp b/qt/src/ControllerPanel.cpp index 46f623e6..fc0c23ec 100644 --- a/qt/src/ControllerPanel.cpp +++ b/qt/src/ControllerPanel.cpp @@ -1,8 +1,8 @@ #include "ControllerPanel.hpp" +#include "SDL3/SDL_gamepad.h" #include "SDLInputManager.hpp" #include "EmuApplication.hpp" #include "EmuConfig.hpp" -#include "SDL_gamecontroller.h" #include #include @@ -103,30 +103,42 @@ void ControllerPanel::autoPopulateWithKeyboard(int slot) void ControllerPanel::autoPopulateWithJoystick(int joystick_id, int slot) { auto &device = app->input_manager->devices[joystick_id]; - auto sdl_controller = device.controller; + auto sdl_controller = device.gamepad; auto &buttons = app->config->binding.controller[controllerComboBox->currentIndex()].buttons; - const SDL_GameControllerButton list[] = { SDL_CONTROLLER_BUTTON_DPAD_UP, - SDL_CONTROLLER_BUTTON_DPAD_DOWN, - SDL_CONTROLLER_BUTTON_DPAD_LEFT, - SDL_CONTROLLER_BUTTON_DPAD_RIGHT, - // B, A and X, Y are inverted on XInput vs SNES - SDL_CONTROLLER_BUTTON_B, - SDL_CONTROLLER_BUTTON_A, - SDL_CONTROLLER_BUTTON_Y, - SDL_CONTROLLER_BUTTON_X, - SDL_CONTROLLER_BUTTON_LEFTSHOULDER, - SDL_CONTROLLER_BUTTON_RIGHTSHOULDER, - SDL_CONTROLLER_BUTTON_START, - SDL_CONTROLLER_BUTTON_BACK }; + const SDL_GamepadButton list[] = { SDL_GAMEPAD_BUTTON_DPAD_UP, + SDL_GAMEPAD_BUTTON_DPAD_DOWN, + SDL_GAMEPAD_BUTTON_DPAD_LEFT, + SDL_GAMEPAD_BUTTON_DPAD_RIGHT, + // B, A and X, Y are inverted on XInput vs SNES + SDL_GAMEPAD_BUTTON_SOUTH, + SDL_GAMEPAD_BUTTON_EAST, + SDL_GAMEPAD_BUTTON_WEST, + SDL_GAMEPAD_BUTTON_NORTH, + SDL_GAMEPAD_BUTTON_LEFT_SHOULDER, + SDL_GAMEPAD_BUTTON_RIGHT_SHOULDER, + SDL_GAMEPAD_BUTTON_START, + SDL_GAMEPAD_BUTTON_BACK }; + + int num_bindings; + auto bindings = SDL_GetGamepadBindings(sdl_controller, &num_bindings); + + auto get_binding_for_button = [&](SDL_GamepadButton button) -> SDL_GamepadBinding + { + for (int i = 0; i < num_bindings; i++) + if (bindings[i]->output.button == button) + return *bindings[i]; + return SDL_GamepadBinding{}; + }; + for (auto i = 0; i < std::size(list); i++) { - auto sdl_binding = SDL_GameControllerGetBindForButton(sdl_controller, list[i]); - if (SDL_CONTROLLER_BINDTYPE_BUTTON == sdl_binding.bindType) - buttons[4 * i + slot] = EmuBinding::joystick_button(device.index, sdl_binding.value.button); - else if (SDL_CONTROLLER_BINDTYPE_HAT == sdl_binding.bindType) - buttons[4 * i + slot] = EmuBinding::joystick_hat(device.index, sdl_binding.value.hat.hat, sdl_binding.value.hat.hat_mask); - else if (SDL_CONTROLLER_BINDTYPE_AXIS == sdl_binding.bindType) - buttons[4 * i + slot] = EmuBinding::joystick_axis(device.index, sdl_binding.value.axis, sdl_binding.value.axis); + auto sdl_binding = get_binding_for_button(list[i]); + if (SDL_GAMEPAD_BINDTYPE_BUTTON == sdl_binding.input_type) + buttons[4 * i + slot] = EmuBinding::joystick_button(device.index, sdl_binding.input.button); + else if (SDL_GAMEPAD_BINDTYPE_HAT == sdl_binding.input_type) + buttons[4 * i + slot] = EmuBinding::joystick_hat(device.index, sdl_binding.input.hat.hat, sdl_binding.input.hat.hat_mask); + else if (SDL_GAMEPAD_BINDTYPE_AXIS == sdl_binding.input_type) + buttons[4 * i + slot] = EmuBinding::joystick_axis(device.index, sdl_binding.input.axis.axis, sdl_binding.input.axis.axis); } fillTable(); app->updateBindings(); diff --git a/qt/src/EmuApplication.cpp b/qt/src/EmuApplication.cpp index 1a2bb8fb..6787a78c 100644 --- a/qt/src/EmuApplication.cpp +++ b/qt/src/EmuApplication.cpp @@ -2,7 +2,7 @@ #include "EmuMainWindow.hpp" #include "SDLInputManager.hpp" #include "Snes9xController.hpp" -#include "common/audio/s9x_sound_driver_sdl.hpp" +#include "common/audio/s9x_sound_driver_sdl3.hpp" #include "common/audio/s9x_sound_driver_cubeb.hpp" #ifdef USE_PULSEAUDIO #include "common/audio/s9x_sound_driver_pulse.hpp" @@ -42,7 +42,7 @@ void EmuApplication::restartAudio() if (!sound_driver) { config->sound_driver = "sdl"; - sound_driver = std::make_unique(); + sound_driver = std::make_unique(); } sound_driver->init(); @@ -432,12 +432,12 @@ void EmuApplication::pollJoysticks() switch (event->type) { - case SDL_JOYDEVICEADDED: - case SDL_JOYDEVICEREMOVED: + case SDL_EVENT_JOYSTICK_ADDED: + case SDL_EVENT_JOYSTICK_REMOVED: if (joypads_changed_callback) joypads_changed_callback(); break; - case SDL_JOYAXISMOTION: { + case SDL_EVENT_JOYSTICK_AXIS_MOTION: { auto axis_event = input_manager->DiscretizeJoyAxisEvent(event.value()); if (axis_event) { @@ -450,13 +450,13 @@ void EmuApplication::pollJoysticks() } break; } - case SDL_JOYBUTTONDOWN: - case SDL_JOYBUTTONUP: + case SDL_EVENT_JOYSTICK_BUTTON_DOWN: + case SDL_EVENT_JOYSTICK_BUTTON_UP: reportBinding(EmuBinding::joystick_button( input_manager->devices[event->jbutton.which].index, - event->jbutton.button), event->jbutton.state == 1); + event->jbutton.button), event->jbutton.down == 1); break; - case SDL_JOYHATMOTION: + case SDL_EVENT_JOYSTICK_HAT_MOTION: auto hat_event = input_manager->DiscretizeHatEvent(event.value()); if (hat_event) { diff --git a/qt/src/EmuBinding.cpp b/qt/src/EmuBinding.cpp index d221ed1e..d3c4ef10 100644 --- a/qt/src/EmuBinding.cpp +++ b/qt/src/EmuBinding.cpp @@ -1,7 +1,7 @@ #include "EmuBinding.hpp" -#include "SDL_joystick.h" #include #include +#include "SDL3/SDL.h" // Hash format: // diff --git a/qt/src/SDLInputManager.cpp b/qt/src/SDLInputManager.cpp index 066bd0c2..5d43359b 100644 --- a/qt/src/SDLInputManager.cpp +++ b/qt/src/SDLInputManager.cpp @@ -1,15 +1,11 @@ #include "SDLInputManager.hpp" -#include "SDL.h" -#include "SDL_events.h" -#include "SDL_gamecontroller.h" -#include "SDL_joystick.h" #include #include SDLInputManager::SDLInputManager() { - SDL_Init(SDL_INIT_GAMECONTROLLER | SDL_INIT_JOYSTICK); + SDL_Init(SDL_INIT_GAMEPAD | SDL_INIT_JOYSTICK); } SDLInputManager::~SDLInputManager() @@ -23,8 +19,8 @@ void SDLInputManager::AddDevice(int device_index) return; d.index = FindFirstOpenIndex(); - printf("Slot %d: %s: ", d.index, SDL_JoystickName(d.joystick)); - printf("%zu axes, %zu buttons, %zu hats, %s API\n", d.axes.size(), d.buttons.size(), d.hats.size(), d.is_controller ? "Controller" : "Joystick"); + printf("Slot %d: %s: ", d.index, SDL_GetJoystickName(d.joystick)); + printf("%zu axes, %zu buttons, %zu hats, %s API\n", d.axes.size(), d.buttons.size(), d.hats.size(), d.is_gamepad ? "Controller" : "Joystick"); devices.insert({ d.instance_id, d }); } @@ -37,10 +33,10 @@ void SDLInputManager::RemoveDevice(int instance_id) auto &d = iter->second; - if (d.is_controller) - SDL_GameControllerClose(d.controller); + if (d.is_gamepad) + SDL_CloseGamepad(d.gamepad); else - SDL_JoystickClose(d.joystick); + SDL_CloseJoystick(d.joystick); devices.erase(iter); return; @@ -129,17 +125,17 @@ std::optional SDLInputManager::ProcessEvent() { switch (event.type) { - case SDL_JOYAXISMOTION: + case SDL_EVENT_JOYSTICK_AXIS_MOTION: return event; - case SDL_JOYHATMOTION: + case SDL_EVENT_JOYSTICK_HAT_MOTION: return event; - case SDL_JOYBUTTONUP: - case SDL_JOYBUTTONDOWN: + case SDL_EVENT_JOYSTICK_BUTTON_UP: + case SDL_EVENT_JOYSTICK_BUTTON_DOWN: return event; - case SDL_JOYDEVICEADDED: + case SDL_EVENT_JOYSTICK_ADDED: AddDevice(event.jdevice.which); return event; - case SDL_JOYDEVICEREMOVED: + case SDL_EVENT_JOYSTICK_REMOVED: RemoveDevice(event.jdevice.which); return event; } @@ -153,14 +149,14 @@ void SDLInputManager::PrintDevices() for (auto &pair : devices) { auto &d = pair.second; - printf("%s: \n", SDL_JoystickName(d.joystick)); + printf("%s: \n", SDL_GetJoystickName(d.joystick)); printf(" Index: %d\n" " Instance ID: %d\n" " Controller %s\n" " SDL Joystick Number: %d\n", d.index, d.instance_id, - d.is_controller ? "yes" : "no", + d.is_gamepad ? "yes" : "no", d.sdl_joystick_number); } } @@ -180,33 +176,33 @@ int SDLInputManager::FindFirstOpenIndex() bool SDLInputDevice::open(int joystick_num) { sdl_joystick_number = joystick_num; - is_controller = SDL_IsGameController(joystick_num); + is_gamepad = SDL_IsGamepad(joystick_num); - if (is_controller) + if (is_gamepad) { - controller = SDL_GameControllerOpen(joystick_num); - joystick = SDL_GameControllerGetJoystick(controller); + gamepad = SDL_OpenGamepad(joystick_num); + joystick = SDL_GetGamepadJoystick(gamepad); } else { - joystick = SDL_JoystickOpen(joystick_num); - controller = nullptr; + joystick = SDL_OpenJoystick(joystick_num); + gamepad = nullptr; } if (!joystick) return false; - auto num_axes = SDL_JoystickNumAxes(joystick); + auto num_axes = SDL_GetNumJoystickAxes(joystick); axes.resize(num_axes); for (int i = 0; i < num_axes; i++) { - SDL_JoystickGetAxisInitialState(joystick, i, &axes[i].initial); + SDL_GetJoystickAxisInitialState(joystick, i, &axes[i].initial); axes[i].last = axes[i].initial; } - buttons.resize(SDL_JoystickNumButtons(joystick)); - hats.resize(SDL_JoystickNumHats(joystick)); - instance_id = SDL_JoystickInstanceID(joystick); + buttons.resize(SDL_GetNumJoystickButtons(joystick)); + hats.resize(SDL_GetNumJoystickHats(joystick)); + instance_id = SDL_GetJoystickID(joystick); return true; } @@ -217,11 +213,11 @@ std::vector> SDLInputManager::getXInputControllers() for (auto &d : devices) { - if (!d.second.is_controller) + if (!d.second.is_gamepad) continue; - list.push_back(std::pair(d.first, SDL_JoystickName(d.second.joystick))); - auto bind = SDL_GameControllerGetBindForButton(d.second.controller, SDL_CONTROLLER_BUTTON_A); + list.push_back(std::pair(d.first, SDL_GetJoystickName(d.second.joystick))); + auto bind = SDL_GetGamepadButton(d.second.gamepad, SDL_GAMEPAD_BUTTON_EAST); } return list; diff --git a/qt/src/SDLInputManager.hpp b/qt/src/SDLInputManager.hpp index 8e473ffd..3f9fe551 100644 --- a/qt/src/SDLInputManager.hpp +++ b/qt/src/SDLInputManager.hpp @@ -1,6 +1,6 @@ #pragma once -#include "SDL.h" +#include "SDL3/SDL.h" #include #include #include @@ -12,8 +12,8 @@ struct SDLInputDevice int index; int sdl_joystick_number; - bool is_controller; - SDL_GameController *controller = nullptr; + bool is_gamepad; + SDL_Gamepad *gamepad = nullptr; SDL_Joystick *joystick = nullptr; SDL_JoystickID instance_id;