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})
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

View File

@ -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 <QtEvents>
#include <QTimer>
@ -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();

View File

@ -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<S9xSDLSoundDriver>();
sound_driver = std::make_unique<S9xSDL3SoundDriver>();
}
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)
{

View File

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

View File

@ -1,15 +1,11 @@
#include "SDLInputManager.hpp"
#include "SDL.h"
#include "SDL_events.h"
#include "SDL_gamecontroller.h"
#include "SDL_joystick.h"
#include <algorithm>
#include <optional>
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<SDL_Event> 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<std::pair<int, std::string>> SDLInputManager::getXInputControllers()
for (auto &d : devices)
{
if (!d.second.is_controller)
if (!d.second.is_gamepad)
continue;
list.push_back(std::pair<int, std::string>(d.first, SDL_JoystickName(d.second.joystick)));
auto bind = SDL_GameControllerGetBindForButton(d.second.controller, SDL_CONTROLLER_BUTTON_A);
list.push_back(std::pair<int, std::string>(d.first, SDL_GetJoystickName(d.second.joystick)));
auto bind = SDL_GetGamepadButton(d.second.gamepad, SDL_GAMEPAD_BUTTON_EAST);
}
return list;

View File

@ -1,6 +1,6 @@
#pragma once
#include "SDL.h"
#include "SDL3/SDL.h"
#include <map>
#include <vector>
#include <string>
@ -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;