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.
This commit is contained in:
BearOso 2024-12-13 17:14:34 -06:00
parent 2ffc66c33c
commit 56e58cdf99
8 changed files with 250 additions and 75 deletions

View File

@ -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 <vector>
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<int, int> S9xSDL3SoundDriver::buffer_level()
{
std::pair<int, int> level = { buffer.space_empty(), buffer.buffer_size };
return level;
}

View File

@ -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 <mutex>
#include <cstdint>
#include <vector>
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<int, int> buffer_level() override;
private:
void mix(int req, int total);
SDL_AudioStream *stream;
SDL_AudioSpec audiospec;
Resampler buffer;
std::mutex mutex;
std::vector<int16_t> tmp;
};
#endif /* __S9X_SOUND_DRIVER_SDL3_HPP */

View File

@ -98,11 +98,10 @@ list(APPEND LIBS Qt6::Widgets Qt6::Gui)
list(APPEND INCLUDES ${Qt6Gui_PRIVATE_INCLUDE_DIRS}) list(APPEND INCLUDES ${Qt6Gui_PRIVATE_INCLUDE_DIRS})
find_package(PkgConfig REQUIRED) find_package(PkgConfig REQUIRED)
pkg_check_modules(SDL REQUIRED sdl2)
pkg_check_modules(ZLIB REQUIRED zlib) pkg_check_modules(ZLIB REQUIRED zlib)
pkg_check_modules(PNG REQUIRED libpng) pkg_check_modules(PNG REQUIRED libpng)
list(APPEND INCLUDES ${SDL_INCLUDE_DIRS} ${ZLIB_INCLUDE_DIRS}) list(APPEND INCLUDES ${ZLIB_INCLUDE_DIRS})
list(APPEND FLAGS ${SDL_COMPILE_FLAGS} ${ZLIB_COMPILE_FLAGS}) list(APPEND FLAGS ${ZLIB_COMPILE_FLAGS})
if(${CMAKE_SYSTEM_NAME} STREQUAL "Windows") if(${CMAKE_SYSTEM_NAME} STREQUAL "Windows")
if (${CMAKE_CXX_COMPILER_ID} STREQUAL "Clang") if (${CMAKE_CXX_COMPILER_ID} STREQUAL "Clang")
@ -113,8 +112,7 @@ if(${CMAKE_SYSTEM_NAME} STREQUAL "Windows")
list(APPEND LIBS libstdc++.a) list(APPEND LIBS libstdc++.a)
endif() endif()
list(APPEND LIBS libSDL2.a libz.a libpng.a opengl32 gdi32 winmm imm32 ole32 oleaut32 version uuid advapi32 setupapi shell32 dinput8) list(APPEND LIBS libz.a libpng.a opengl32 gdi32 winmm imm32 ole32 oleaut32 version uuid advapi32 setupapi shell32 dinput8)
list(APPEND DEFINES SDL_MAIN_HANDLED)
list(APPEND PLATFORM_SOURCES list(APPEND PLATFORM_SOURCES
../common/video/opengl/wgl_context.cpp ../common/video/opengl/wgl_context.cpp
../external/glad/src/wgl.c ../external/glad/src/wgl.c
@ -128,7 +126,7 @@ else()
endif() endif()
list(APPEND INCLUDES ${WAYLAND_INCLUDE_DIRS} ${X11_INCLUDE_DIRS}) 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}) list(APPEND FLAGS ${WAYLAND_CFLAGS})
pkg_check_modules(PULSEAUDIO libpulse) pkg_check_modules(PULSEAUDIO libpulse)
@ -156,6 +154,17 @@ else()
../external/glad/src/egl.c) ../external/glad/src/egl.c)
endif() 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 PLATFORM_SOURCES ../external/fmt/src/format.cc)
list(APPEND INCLUDES ../external/fmt/include) list(APPEND INCLUDES ../external/fmt/include)
@ -184,8 +193,8 @@ list(APPEND QT_GUI_SOURCES
src/EmuCanvasOpenGL.cpp src/EmuCanvasOpenGL.cpp
src/EmuCanvasVulkan.cpp src/EmuCanvasVulkan.cpp
../external/glad/src/gl.c ../external/glad/src/gl.c
../common/audio/s9x_sound_driver_sdl.cpp ../common/audio/s9x_sound_driver_sdl3.cpp
../common/audio/s9x_sound_driver_sdl.hpp ../common/audio/s9x_sound_driver_sdl3.hpp
../common/audio/s9x_sound_driver_cubeb.cpp ../common/audio/s9x_sound_driver_cubeb.cpp
../common/audio/s9x_sound_driver_cubeb.hpp ../common/audio/s9x_sound_driver_cubeb.hpp
../filter/2xsai.cpp ../filter/2xsai.cpp

View File

@ -1,8 +1,8 @@
#include "ControllerPanel.hpp" #include "ControllerPanel.hpp"
#include "SDL3/SDL_gamepad.h"
#include "SDLInputManager.hpp" #include "SDLInputManager.hpp"
#include "EmuApplication.hpp" #include "EmuApplication.hpp"
#include "EmuConfig.hpp" #include "EmuConfig.hpp"
#include "SDL_gamecontroller.h"
#include <QtEvents> #include <QtEvents>
#include <QTimer> #include <QTimer>
@ -103,30 +103,42 @@ void ControllerPanel::autoPopulateWithKeyboard(int slot)
void ControllerPanel::autoPopulateWithJoystick(int joystick_id, int slot) void ControllerPanel::autoPopulateWithJoystick(int joystick_id, int slot)
{ {
auto &device = app->input_manager->devices[joystick_id]; 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; auto &buttons = app->config->binding.controller[controllerComboBox->currentIndex()].buttons;
const SDL_GameControllerButton list[] = { SDL_CONTROLLER_BUTTON_DPAD_UP, const SDL_GamepadButton list[] = { SDL_GAMEPAD_BUTTON_DPAD_UP,
SDL_CONTROLLER_BUTTON_DPAD_DOWN, SDL_GAMEPAD_BUTTON_DPAD_DOWN,
SDL_CONTROLLER_BUTTON_DPAD_LEFT, SDL_GAMEPAD_BUTTON_DPAD_LEFT,
SDL_CONTROLLER_BUTTON_DPAD_RIGHT, SDL_GAMEPAD_BUTTON_DPAD_RIGHT,
// B, A and X, Y are inverted on XInput vs SNES // B, A and X, Y are inverted on XInput vs SNES
SDL_CONTROLLER_BUTTON_B, SDL_GAMEPAD_BUTTON_SOUTH,
SDL_CONTROLLER_BUTTON_A, SDL_GAMEPAD_BUTTON_EAST,
SDL_CONTROLLER_BUTTON_Y, SDL_GAMEPAD_BUTTON_WEST,
SDL_CONTROLLER_BUTTON_X, SDL_GAMEPAD_BUTTON_NORTH,
SDL_CONTROLLER_BUTTON_LEFTSHOULDER, SDL_GAMEPAD_BUTTON_LEFT_SHOULDER,
SDL_CONTROLLER_BUTTON_RIGHTSHOULDER, SDL_GAMEPAD_BUTTON_RIGHT_SHOULDER,
SDL_CONTROLLER_BUTTON_START, SDL_GAMEPAD_BUTTON_START,
SDL_CONTROLLER_BUTTON_BACK }; 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++) for (auto i = 0; i < std::size(list); i++)
{ {
auto sdl_binding = SDL_GameControllerGetBindForButton(sdl_controller, list[i]); auto sdl_binding = get_binding_for_button(list[i]);
if (SDL_CONTROLLER_BINDTYPE_BUTTON == sdl_binding.bindType) if (SDL_GAMEPAD_BINDTYPE_BUTTON == sdl_binding.input_type)
buttons[4 * i + slot] = EmuBinding::joystick_button(device.index, sdl_binding.value.button); buttons[4 * i + slot] = EmuBinding::joystick_button(device.index, sdl_binding.input.button);
else if (SDL_CONTROLLER_BINDTYPE_HAT == sdl_binding.bindType) else if (SDL_GAMEPAD_BINDTYPE_HAT == sdl_binding.input_type)
buttons[4 * i + slot] = EmuBinding::joystick_hat(device.index, sdl_binding.value.hat.hat, sdl_binding.value.hat.hat_mask); buttons[4 * i + slot] = EmuBinding::joystick_hat(device.index, sdl_binding.input.hat.hat, sdl_binding.input.hat.hat_mask);
else if (SDL_CONTROLLER_BINDTYPE_AXIS == sdl_binding.bindType) else if (SDL_GAMEPAD_BINDTYPE_AXIS == sdl_binding.input_type)
buttons[4 * i + slot] = EmuBinding::joystick_axis(device.index, sdl_binding.value.axis, sdl_binding.value.axis); buttons[4 * i + slot] = EmuBinding::joystick_axis(device.index, sdl_binding.input.axis.axis, sdl_binding.input.axis.axis);
} }
fillTable(); fillTable();
app->updateBindings(); app->updateBindings();

View File

@ -2,7 +2,7 @@
#include "EmuMainWindow.hpp" #include "EmuMainWindow.hpp"
#include "SDLInputManager.hpp" #include "SDLInputManager.hpp"
#include "Snes9xController.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" #include "common/audio/s9x_sound_driver_cubeb.hpp"
#ifdef USE_PULSEAUDIO #ifdef USE_PULSEAUDIO
#include "common/audio/s9x_sound_driver_pulse.hpp" #include "common/audio/s9x_sound_driver_pulse.hpp"
@ -42,7 +42,7 @@ void EmuApplication::restartAudio()
if (!sound_driver) if (!sound_driver)
{ {
config->sound_driver = "sdl"; config->sound_driver = "sdl";
sound_driver = std::make_unique<S9xSDLSoundDriver>(); sound_driver = std::make_unique<S9xSDL3SoundDriver>();
} }
sound_driver->init(); sound_driver->init();
@ -432,12 +432,12 @@ void EmuApplication::pollJoysticks()
switch (event->type) switch (event->type)
{ {
case SDL_JOYDEVICEADDED: case SDL_EVENT_JOYSTICK_ADDED:
case SDL_JOYDEVICEREMOVED: case SDL_EVENT_JOYSTICK_REMOVED:
if (joypads_changed_callback) if (joypads_changed_callback)
joypads_changed_callback(); joypads_changed_callback();
break; break;
case SDL_JOYAXISMOTION: { case SDL_EVENT_JOYSTICK_AXIS_MOTION: {
auto axis_event = input_manager->DiscretizeJoyAxisEvent(event.value()); auto axis_event = input_manager->DiscretizeJoyAxisEvent(event.value());
if (axis_event) if (axis_event)
{ {
@ -450,13 +450,13 @@ void EmuApplication::pollJoysticks()
} }
break; break;
} }
case SDL_JOYBUTTONDOWN: case SDL_EVENT_JOYSTICK_BUTTON_DOWN:
case SDL_JOYBUTTONUP: case SDL_EVENT_JOYSTICK_BUTTON_UP:
reportBinding(EmuBinding::joystick_button( reportBinding(EmuBinding::joystick_button(
input_manager->devices[event->jbutton.which].index, input_manager->devices[event->jbutton.which].index,
event->jbutton.button), event->jbutton.state == 1); event->jbutton.button), event->jbutton.down == 1);
break; break;
case SDL_JOYHATMOTION: case SDL_EVENT_JOYSTICK_HAT_MOTION:
auto hat_event = input_manager->DiscretizeHatEvent(event.value()); auto hat_event = input_manager->DiscretizeHatEvent(event.value());
if (hat_event) if (hat_event)
{ {

View File

@ -1,7 +1,7 @@
#include "EmuBinding.hpp" #include "EmuBinding.hpp"
#include "SDL_joystick.h"
#include <QString> #include <QString>
#include <QKeySequence> #include <QKeySequence>
#include "SDL3/SDL.h"
// Hash format: // Hash format:
// //

View File

@ -1,15 +1,11 @@
#include "SDLInputManager.hpp" #include "SDLInputManager.hpp"
#include "SDL.h"
#include "SDL_events.h"
#include "SDL_gamecontroller.h"
#include "SDL_joystick.h"
#include <algorithm> #include <algorithm>
#include <optional> #include <optional>
SDLInputManager::SDLInputManager() SDLInputManager::SDLInputManager()
{ {
SDL_Init(SDL_INIT_GAMECONTROLLER | SDL_INIT_JOYSTICK); SDL_Init(SDL_INIT_GAMEPAD | SDL_INIT_JOYSTICK);
} }
SDLInputManager::~SDLInputManager() SDLInputManager::~SDLInputManager()
@ -23,8 +19,8 @@ void SDLInputManager::AddDevice(int device_index)
return; return;
d.index = FindFirstOpenIndex(); d.index = FindFirstOpenIndex();
printf("Slot %d: %s: ", d.index, SDL_JoystickName(d.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_controller ? "Controller" : "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 }); devices.insert({ d.instance_id, d });
} }
@ -37,10 +33,10 @@ void SDLInputManager::RemoveDevice(int instance_id)
auto &d = iter->second; auto &d = iter->second;
if (d.is_controller) if (d.is_gamepad)
SDL_GameControllerClose(d.controller); SDL_CloseGamepad(d.gamepad);
else else
SDL_JoystickClose(d.joystick); SDL_CloseJoystick(d.joystick);
devices.erase(iter); devices.erase(iter);
return; return;
@ -129,17 +125,17 @@ std::optional<SDL_Event> SDLInputManager::ProcessEvent()
{ {
switch (event.type) switch (event.type)
{ {
case SDL_JOYAXISMOTION: case SDL_EVENT_JOYSTICK_AXIS_MOTION:
return event; return event;
case SDL_JOYHATMOTION: case SDL_EVENT_JOYSTICK_HAT_MOTION:
return event; return event;
case SDL_JOYBUTTONUP: case SDL_EVENT_JOYSTICK_BUTTON_UP:
case SDL_JOYBUTTONDOWN: case SDL_EVENT_JOYSTICK_BUTTON_DOWN:
return event; return event;
case SDL_JOYDEVICEADDED: case SDL_EVENT_JOYSTICK_ADDED:
AddDevice(event.jdevice.which); AddDevice(event.jdevice.which);
return event; return event;
case SDL_JOYDEVICEREMOVED: case SDL_EVENT_JOYSTICK_REMOVED:
RemoveDevice(event.jdevice.which); RemoveDevice(event.jdevice.which);
return event; return event;
} }
@ -153,14 +149,14 @@ void SDLInputManager::PrintDevices()
for (auto &pair : devices) for (auto &pair : devices)
{ {
auto &d = pair.second; auto &d = pair.second;
printf("%s: \n", SDL_JoystickName(d.joystick)); printf("%s: \n", SDL_GetJoystickName(d.joystick));
printf(" Index: %d\n" printf(" Index: %d\n"
" Instance ID: %d\n" " Instance ID: %d\n"
" Controller %s\n" " Controller %s\n"
" SDL Joystick Number: %d\n", " SDL Joystick Number: %d\n",
d.index, d.index,
d.instance_id, d.instance_id,
d.is_controller ? "yes" : "no", d.is_gamepad ? "yes" : "no",
d.sdl_joystick_number); d.sdl_joystick_number);
} }
} }
@ -180,33 +176,33 @@ int SDLInputManager::FindFirstOpenIndex()
bool SDLInputDevice::open(int joystick_num) bool SDLInputDevice::open(int joystick_num)
{ {
sdl_joystick_number = 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); gamepad = SDL_OpenGamepad(joystick_num);
joystick = SDL_GameControllerGetJoystick(controller); joystick = SDL_GetGamepadJoystick(gamepad);
} }
else else
{ {
joystick = SDL_JoystickOpen(joystick_num); joystick = SDL_OpenJoystick(joystick_num);
controller = nullptr; gamepad = nullptr;
} }
if (!joystick) if (!joystick)
return false; return false;
auto num_axes = SDL_JoystickNumAxes(joystick); auto num_axes = SDL_GetNumJoystickAxes(joystick);
axes.resize(num_axes); axes.resize(num_axes);
for (int i = 0; i < num_axes; i++) 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; axes[i].last = axes[i].initial;
} }
buttons.resize(SDL_JoystickNumButtons(joystick)); buttons.resize(SDL_GetNumJoystickButtons(joystick));
hats.resize(SDL_JoystickNumHats(joystick)); hats.resize(SDL_GetNumJoystickHats(joystick));
instance_id = SDL_JoystickInstanceID(joystick); instance_id = SDL_GetJoystickID(joystick);
return true; return true;
} }
@ -217,11 +213,11 @@ std::vector<std::pair<int, std::string>> SDLInputManager::getXInputControllers()
for (auto &d : devices) for (auto &d : devices)
{ {
if (!d.second.is_controller) if (!d.second.is_gamepad)
continue; continue;
list.push_back(std::pair<int, std::string>(d.first, SDL_JoystickName(d.second.joystick))); list.push_back(std::pair<int, std::string>(d.first, SDL_GetJoystickName(d.second.joystick)));
auto bind = SDL_GameControllerGetBindForButton(d.second.controller, SDL_CONTROLLER_BUTTON_A); auto bind = SDL_GetGamepadButton(d.second.gamepad, SDL_GAMEPAD_BUTTON_EAST);
} }
return list; return list;

View File

@ -1,6 +1,6 @@
#pragma once #pragma once
#include "SDL.h" #include "SDL3/SDL.h"
#include <map> #include <map>
#include <vector> #include <vector>
#include <string> #include <string>
@ -12,8 +12,8 @@ struct SDLInputDevice
int index; int index;
int sdl_joystick_number; int sdl_joystick_number;
bool is_controller; bool is_gamepad;
SDL_GameController *controller = nullptr; SDL_Gamepad *gamepad = nullptr;
SDL_Joystick *joystick = nullptr; SDL_Joystick *joystick = nullptr;
SDL_JoystickID instance_id; SDL_JoystickID instance_id;