diff --git a/common/audio/s9x_sound_driver_cubeb.cpp b/common/audio/s9x_sound_driver_cubeb.cpp index 549882fe..bbbe31f0 100644 --- a/common/audio/s9x_sound_driver_cubeb.cpp +++ b/common/audio/s9x_sound_driver_cubeb.cpp @@ -80,7 +80,6 @@ long S9xCubebSoundDriver::data_callback(cubeb_stream *stream, void const *input_ auto avail = buffer.avail(); if (avail < nframes * 2) { - printf("Underrun\n"); auto zeroed_samples = nframes * 2 - avail; memset(output_buffer, 0, zeroed_samples); buffer.read((int16_t *)output_buffer + zeroed_samples, nframes * 2 - zeroed_samples); @@ -107,7 +106,7 @@ bool S9xCubebSoundDriver::open_device(int playback_rate, int buffer_size) if (min_latency > suggested_latency) { suggested_latency = min_latency; - printf("Using minimum latency: %d\n", min_latency); + printf("Requires a minimum latency: %d\n", min_latency); } auto retval = cubeb_stream_init(context, &stream, "Snes9x", @@ -125,7 +124,7 @@ bool S9xCubebSoundDriver::open_device(int playback_rate, int buffer_size) return false; } - buffer.resize(2 * buffer_size * playback_rate / 1000); + buffer.resize(suggested_latency * 4); return true; } diff --git a/common/audio/s9x_sound_driver_portaudio.cpp b/common/audio/s9x_sound_driver_portaudio.cpp index 7b90563e..70293d5e 100644 --- a/common/audio/s9x_sound_driver_portaudio.cpp +++ b/common/audio/s9x_sound_driver_portaudio.cpp @@ -74,7 +74,6 @@ bool S9xPortAudioSoundDriver::tryHostAPI(int index) printf("Host API #%d has no info\n", index); return false; } - printf("Attempting API: %s\n", hostapi_info->name); auto device_info = Pa_GetDeviceInfo(hostapi_info->defaultOutputDevice); if (!device_info) @@ -104,8 +103,12 @@ bool S9xPortAudioSoundDriver::tryHostAPI(int index) NULL, NULL); - int frames = playback_rate * buffer_size_ms / 1000; - //int frames = Pa_GetStreamWriteAvailable(audio_stream); + int frames = Pa_GetStreamWriteAvailable(audio_stream); + if (frames < 0) + { + Pa_Sleep(10); + frames = Pa_GetStreamWriteAvailable(audio_stream); + } printf("PortAudio set buffer size to %d frames.\n", frames); output_buffer_size = frames; @@ -181,7 +184,7 @@ int S9xPortAudioSoundDriver::space_free() std::pair S9xPortAudioSoundDriver::buffer_level() { - return { Pa_GetStreamWriteAvailable(audio_stream), output_buffer_size }; + return { Pa_GetStreamWriteAvailable(audio_stream), output_buffer_size }; } bool S9xPortAudioSoundDriver::write_samples(int16_t *data, int samples) diff --git a/common/audio/s9x_sound_driver_pulse.cpp b/common/audio/s9x_sound_driver_pulse.cpp index 120db531..5d26a289 100644 --- a/common/audio/s9x_sound_driver_pulse.cpp +++ b/common/audio/s9x_sound_driver_pulse.cpp @@ -181,16 +181,22 @@ std::pair S9xPulseSoundDriver::buffer_level() return { bytes, buffer_size }; } -void S9xPulseSoundDriver::write_samples(int16_t *data, int samples) +bool S9xPulseSoundDriver::write_samples(int16_t *data, int samples) { + bool retval = true; + lock(); size_t bytes = pa_stream_writable_size(stream); unlock(); - bytes = MIN(bytes, samples * 2) & ~1; + if (samples * 2 > bytes) + return false; + + if (samples * 2 < bytes) + bytes = samples * 2; if (bytes == 0) - return; + return false; lock(); void *output_buffer; @@ -198,16 +204,18 @@ void S9xPulseSoundDriver::write_samples(int16_t *data, int samples) { pa_stream_flush(stream, nullptr, nullptr); unlock(); - return; + return false; } if (bytes <= 0 || !output_buffer) { unlock(); - return; + return false; } std::memcpy(output_buffer, data, bytes); pa_stream_write(stream, output_buffer, bytes, nullptr, 0, PA_SEEK_RELATIVE); unlock(); + + return retval; } diff --git a/common/audio/s9x_sound_driver_pulse.hpp b/common/audio/s9x_sound_driver_pulse.hpp index e4bdae46..12ee60b4 100644 --- a/common/audio/s9x_sound_driver_pulse.hpp +++ b/common/audio/s9x_sound_driver_pulse.hpp @@ -16,7 +16,7 @@ class S9xPulseSoundDriver : public S9xSoundDriver S9xPulseSoundDriver(); void init() override; void deinit() override; - void write_samples(int16_t *data, int samples) override; + bool write_samples(int16_t *data, int samples) override; bool open_device(int playback_rate, int buffer_size) override; void start() override; void stop() override; diff --git a/common/audio/s9x_sound_driver_sdl.cpp b/common/audio/s9x_sound_driver_sdl.cpp index a18ad342..01e93335 100644 --- a/common/audio/s9x_sound_driver_sdl.cpp +++ b/common/audio/s9x_sound_driver_sdl.cpp @@ -64,7 +64,7 @@ bool S9xSDLSoundDriver::open_device(int playback_rate, int buffer_size) audiospec.freq = playback_rate; audiospec.channels = 2; audiospec.format = AUDIO_S16SYS; - audiospec.samples = audiospec.freq * 4 / 1000; // 4ms per sampling + audiospec.samples = audiospec.freq * buffer_size / 8 / 1000; // 1/8th buffer per callback audiospec.callback = [](void *userdata, uint8_t *stream, int len) { ((S9xSDLSoundDriver *)userdata)->mix((unsigned char *)stream, len); }; @@ -83,8 +83,10 @@ bool S9xSDLSoundDriver::open_device(int playback_rate, int buffer_size) } printf("OK\n"); + if (buffer_size < 32) + buffer_size = 32; - buffer.resize(buffer_size * audiospec.freq / 1000); + buffer.resize(buffer_size * 4 * audiospec.freq / 1000); return true; } diff --git a/qt/CMakeLists.txt b/qt/CMakeLists.txt index e0e6075f..acb7e17e 100644 --- a/qt/CMakeLists.txt +++ b/qt/CMakeLists.txt @@ -80,13 +80,19 @@ find_package(Qt6 REQUIRED COMPONENTS Widgets Gui) find_package(PkgConfig REQUIRED) pkg_check_modules(SDL REQUIRED sdl2) pkg_check_modules(ZLIB REQUIRED zlib) -pkg_check_modules(PORTAUDIO REQUIRED portaudio-2.0) +list(APPEND LIBS Qt6::Widgets Qt6::Gui ${SDL_LIBRARIES} ${ZLIB_LIBRARIES}) +list(APPEND INCLUDES ${SDL_INCLUDE_DIRS} ${ZLIB_INCLUDE_DIRS}${Qt6Gui_PRIVATE_INCLUDE_DIRS}) +list(APPEND FLAGS ${SDL_COMPILE_FLAGS} ${ZLIB_COMPILE_FLAGS}) -list(APPEND LIBS Qt6::Widgets Qt6::Gui ${SDL_LIBRARIES} ${ZLIB_LIBRARIES} ${PORTAUDIO_LIBRARIES}) -list(APPEND INCLUDES ${SDL_INCLUDE_DIRS} ${ZLIB_INCLUDE_DIRS} ${PORTAUDIO_INCLUDE_DIRS} ${Qt6Gui_PRIVATE_INCLUDE_DIRS}) -list(APPEND FLAGS ${SDL_COMPILE_FLAGS} ${ZLIB_COMPILE_FLAGS} ${PORTAUDIO_COMPILE_FLAGS}) +pkg_check_modules(PULSEAUDIO REQUIRED libpulse) +if(PULSEAUDIO_FOUND) + list(APPEND LIBS ${PULSEAUDIO_LIBRARIES}) + list(APPEND INCLUDES ${PULSEAUDIO_INCLUDE_DIRS}) + list(APPEND DEFINES "USE_PULSEAUDIO") + list(APPEND PLATFORM_SOURCES ../common/audio/s9x_sound_driver_pulse.cpp) +endif() -set(QT_GUI_SOURCES +list(APPEND QT_GUI_SOURCES src/main.cpp src/EmuApplication.cpp src/EmuMainWindow.cpp @@ -112,8 +118,6 @@ set(QT_GUI_SOURCES ../external/glad/src/gl.c ../common/audio/s9x_sound_driver_sdl.cpp ../common/audio/s9x_sound_driver_sdl.hpp - ../common/audio/s9x_sound_driver_portaudio.cpp - ../common/audio/s9x_sound_driver_portaudio.hpp ../common/audio/s9x_sound_driver_cubeb.cpp ../common/audio/s9x_sound_driver_cubeb.hpp ../filter/2xsai.cpp @@ -137,7 +141,7 @@ if(NOT ${CMAKE_SYSTEM_NAME} STREQUAL "Windows") list(APPEND LIBS ${WAYLAND_LIBRARIES} ${X11_LIBRARIES}) list(APPEND FLAGS ${WAYLAND_CFLAGS}) - set(PLATFORM_SOURCES + list(APPEND PLATFORM_SOURCES ../common/video/glx_context.cpp ../common/video/wayland_egl_context.cpp ../common/video/wayland_surface.cpp @@ -147,7 +151,7 @@ if(NOT ${CMAKE_SYSTEM_NAME} STREQUAL "Windows") ../external/glad/src/glx.c ../external/glad/src/egl.c) else() - set(PLATFORM_SOURCES + list(APPEND PLATFORM_SOURCES ../common/video/wgl_context.cpp ../external/glad/src/wgl.c) list(APPEND LIBS opengl32) diff --git a/qt/src/EmuApplication.cpp b/qt/src/EmuApplication.cpp index b1f72329..76662800 100644 --- a/qt/src/EmuApplication.cpp +++ b/qt/src/EmuApplication.cpp @@ -1,12 +1,17 @@ #include "EmuApplication.hpp" #include "common/audio/s9x_sound_driver_sdl.hpp" -#include "common/audio/s9x_sound_driver_portaudio.hpp" #include "common/audio/s9x_sound_driver_cubeb.hpp" +#include +#ifdef USE_PULSEAUDIO +#include "common/audio/s9x_sound_driver_pulse.hpp" +#endif #include #include #include using namespace std::chrono_literals; +#undef SOUND_BUFFER_WINDOW + EmuApplication::EmuApplication() { core = Snes9xController::get(); @@ -22,11 +27,14 @@ void EmuApplication::restartAudio() sound_driver.reset(); core->sound_output_function = nullptr; - if (config->sound_driver == "portaudio") - sound_driver = std::make_unique(); - else if (config->sound_driver == "cubeb") +#ifdef USE_PULSEAUDIO + if (config->sound_driver == "pulseaudio") + sound_driver = std::make_unique(); +#endif + if (config->sound_driver == "cubeb") sound_driver = std::make_unique(); - else + + if (!sound_driver) { config->sound_driver = "sdl"; sound_driver = std::make_unique(); @@ -47,11 +55,42 @@ void EmuApplication::restartAudio() }; } +#ifdef SOUND_BUFFER_WINDOW +#include +static void trackBufferLevel(int percent, QWidget *parent) +{ + static uint64_t total = 0; + static uint64_t ticks = 0; + static std::chrono::steady_clock::time_point then; + + static QProgressDialog *dialog = nullptr; + + if (!dialog) + { + dialog = new QProgressDialog(parent); + dialog->setRange(0, 100); + } + + ticks++; + total += percent; + + dialog->setValue(percent); + auto now = std::chrono::steady_clock::now(); + if (ticks > 0 && now - then >= std::chrono::seconds(1)) + { + dialog->setLabelText(QString("%1").arg(total / ticks)); + then = now; + total = 0; + ticks = 0; + } + + if (!dialog->isVisible()) + dialog->show(); +} +#endif + void EmuApplication::writeSamples(int16_t *data, int samples) { - static uint64_t total; - static uint64_t ticks; - static std::chrono::steady_clock::time_point then; if (config->speed_sync_method == EmuConfig::eSoundSync && !core->isAbnormalSpeed()) { int iterations = 0; @@ -66,34 +105,13 @@ void EmuApplication::writeSamples(int16_t *data, int samples) if (!sound_driver->write_samples(data, samples)) { - printf("Overrun\n"); core->clearSoundBuffer(); } - char sym = '*'; +#ifdef SOUND_BUFFER_WINDOW int percent = (buffer_level.second - buffer_level.first) * 100 / buffer_level.second; - printf("\033[0;0H>"); - if (percent < 50) - sym = '_'; - for (int i = 0; i < percent; i+=2) - putchar(sym); - for (int i = percent; i <= 100; i+=2) - putchar('-'); - - total += percent; - ticks++; - - auto now = std::chrono::steady_clock::now(); - if (now - then >= std::chrono::seconds(1)) - { - then = now; - printf(" %lu %%", total / ticks); - total = 0; - ticks = 0; - } - printf("\n"); - - printf(" 10 20 30 40 50 60 70 80 90 100"); + trackBufferLevel(percent, window.get()); +#endif } void EmuApplication::startGame() diff --git a/qt/src/SoundPanel.cpp b/qt/src/SoundPanel.cpp index 8bf7cf62..0b47f2fd 100644 --- a/qt/src/SoundPanel.cpp +++ b/qt/src/SoundPanel.cpp @@ -89,14 +89,16 @@ void SoundPanel::showEvent(QShowEvent *event) auto &config = app->config; comboBox_driver->clear(); - comboBox_driver->addItem("SDL"); - comboBox_driver->addItem("PortAudio"); - comboBox_driver->addItem("Cubeb"); - driver_list.clear(); - driver_list.push_back("sdl"); - driver_list.push_back("portaudio"); + + comboBox_driver->addItem("Cubeb"); driver_list.push_back("cubeb"); + comboBox_driver->addItem("SDL"); + driver_list.push_back("sdl"); +#ifdef USE_PULSEAUDIO + comboBox_driver->addItem("PulseAudio"); + driver_list.push_back("pulseaudio"); +#endif for (int i = 0; i < driver_list.size(); i++) {