mirror of https://github.com/snes9xgit/snes9x.git
Sound buffer tweaking.
This commit is contained in:
parent
bafea657e7
commit
62c4686fc6
|
@ -80,7 +80,6 @@ long S9xCubebSoundDriver::data_callback(cubeb_stream *stream, void const *input_
|
||||||
auto avail = buffer.avail();
|
auto avail = buffer.avail();
|
||||||
if (avail < nframes * 2)
|
if (avail < nframes * 2)
|
||||||
{
|
{
|
||||||
printf("Underrun\n");
|
|
||||||
auto zeroed_samples = nframes * 2 - avail;
|
auto zeroed_samples = nframes * 2 - avail;
|
||||||
memset(output_buffer, 0, zeroed_samples);
|
memset(output_buffer, 0, zeroed_samples);
|
||||||
buffer.read((int16_t *)output_buffer + zeroed_samples, nframes * 2 - 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)
|
if (min_latency > suggested_latency)
|
||||||
{
|
{
|
||||||
suggested_latency = min_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",
|
auto retval = cubeb_stream_init(context, &stream, "Snes9x",
|
||||||
|
@ -125,7 +124,7 @@ bool S9xCubebSoundDriver::open_device(int playback_rate, int buffer_size)
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
buffer.resize(2 * buffer_size * playback_rate / 1000);
|
buffer.resize(suggested_latency * 4);
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
|
@ -74,7 +74,6 @@ bool S9xPortAudioSoundDriver::tryHostAPI(int index)
|
||||||
printf("Host API #%d has no info\n", index);
|
printf("Host API #%d has no info\n", index);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
printf("Attempting API: %s\n", hostapi_info->name);
|
|
||||||
|
|
||||||
auto device_info = Pa_GetDeviceInfo(hostapi_info->defaultOutputDevice);
|
auto device_info = Pa_GetDeviceInfo(hostapi_info->defaultOutputDevice);
|
||||||
if (!device_info)
|
if (!device_info)
|
||||||
|
@ -104,8 +103,12 @@ bool S9xPortAudioSoundDriver::tryHostAPI(int index)
|
||||||
NULL,
|
NULL,
|
||||||
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);
|
printf("PortAudio set buffer size to %d frames.\n", frames);
|
||||||
output_buffer_size = frames;
|
output_buffer_size = frames;
|
||||||
|
|
||||||
|
@ -181,7 +184,7 @@ int S9xPortAudioSoundDriver::space_free()
|
||||||
|
|
||||||
std::pair<int, int> S9xPortAudioSoundDriver::buffer_level()
|
std::pair<int, int> 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)
|
bool S9xPortAudioSoundDriver::write_samples(int16_t *data, int samples)
|
||||||
|
|
|
@ -181,16 +181,22 @@ std::pair<int, int> S9xPulseSoundDriver::buffer_level()
|
||||||
return { bytes, buffer_size };
|
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();
|
lock();
|
||||||
size_t bytes = pa_stream_writable_size(stream);
|
size_t bytes = pa_stream_writable_size(stream);
|
||||||
unlock();
|
unlock();
|
||||||
|
|
||||||
bytes = MIN(bytes, samples * 2) & ~1;
|
if (samples * 2 > bytes)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (samples * 2 < bytes)
|
||||||
|
bytes = samples * 2;
|
||||||
|
|
||||||
if (bytes == 0)
|
if (bytes == 0)
|
||||||
return;
|
return false;
|
||||||
|
|
||||||
lock();
|
lock();
|
||||||
void *output_buffer;
|
void *output_buffer;
|
||||||
|
@ -198,16 +204,18 @@ void S9xPulseSoundDriver::write_samples(int16_t *data, int samples)
|
||||||
{
|
{
|
||||||
pa_stream_flush(stream, nullptr, nullptr);
|
pa_stream_flush(stream, nullptr, nullptr);
|
||||||
unlock();
|
unlock();
|
||||||
return;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (bytes <= 0 || !output_buffer)
|
if (bytes <= 0 || !output_buffer)
|
||||||
{
|
{
|
||||||
unlock();
|
unlock();
|
||||||
return;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::memcpy(output_buffer, data, bytes);
|
std::memcpy(output_buffer, data, bytes);
|
||||||
pa_stream_write(stream, output_buffer, bytes, nullptr, 0, PA_SEEK_RELATIVE);
|
pa_stream_write(stream, output_buffer, bytes, nullptr, 0, PA_SEEK_RELATIVE);
|
||||||
unlock();
|
unlock();
|
||||||
|
|
||||||
|
return retval;
|
||||||
}
|
}
|
||||||
|
|
|
@ -16,7 +16,7 @@ class S9xPulseSoundDriver : public S9xSoundDriver
|
||||||
S9xPulseSoundDriver();
|
S9xPulseSoundDriver();
|
||||||
void init() override;
|
void init() override;
|
||||||
void deinit() 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;
|
bool open_device(int playback_rate, int buffer_size) override;
|
||||||
void start() override;
|
void start() override;
|
||||||
void stop() override;
|
void stop() override;
|
||||||
|
|
|
@ -64,7 +64,7 @@ bool S9xSDLSoundDriver::open_device(int playback_rate, int buffer_size)
|
||||||
audiospec.freq = playback_rate;
|
audiospec.freq = playback_rate;
|
||||||
audiospec.channels = 2;
|
audiospec.channels = 2;
|
||||||
audiospec.format = AUDIO_S16SYS;
|
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) {
|
audiospec.callback = [](void *userdata, uint8_t *stream, int len) {
|
||||||
((S9xSDLSoundDriver *)userdata)->mix((unsigned char *)stream, 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");
|
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;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
|
@ -80,13 +80,19 @@ find_package(Qt6 REQUIRED COMPONENTS Widgets Gui)
|
||||||
find_package(PkgConfig REQUIRED)
|
find_package(PkgConfig REQUIRED)
|
||||||
pkg_check_modules(SDL REQUIRED sdl2)
|
pkg_check_modules(SDL REQUIRED sdl2)
|
||||||
pkg_check_modules(ZLIB REQUIRED zlib)
|
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})
|
pkg_check_modules(PULSEAUDIO REQUIRED libpulse)
|
||||||
list(APPEND INCLUDES ${SDL_INCLUDE_DIRS} ${ZLIB_INCLUDE_DIRS} ${PORTAUDIO_INCLUDE_DIRS} ${Qt6Gui_PRIVATE_INCLUDE_DIRS})
|
if(PULSEAUDIO_FOUND)
|
||||||
list(APPEND FLAGS ${SDL_COMPILE_FLAGS} ${ZLIB_COMPILE_FLAGS} ${PORTAUDIO_COMPILE_FLAGS})
|
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/main.cpp
|
||||||
src/EmuApplication.cpp
|
src/EmuApplication.cpp
|
||||||
src/EmuMainWindow.cpp
|
src/EmuMainWindow.cpp
|
||||||
|
@ -112,8 +118,6 @@ set(QT_GUI_SOURCES
|
||||||
../external/glad/src/gl.c
|
../external/glad/src/gl.c
|
||||||
../common/audio/s9x_sound_driver_sdl.cpp
|
../common/audio/s9x_sound_driver_sdl.cpp
|
||||||
../common/audio/s9x_sound_driver_sdl.hpp
|
../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.cpp
|
||||||
../common/audio/s9x_sound_driver_cubeb.hpp
|
../common/audio/s9x_sound_driver_cubeb.hpp
|
||||||
../filter/2xsai.cpp
|
../filter/2xsai.cpp
|
||||||
|
@ -137,7 +141,7 @@ if(NOT ${CMAKE_SYSTEM_NAME} STREQUAL "Windows")
|
||||||
list(APPEND LIBS ${WAYLAND_LIBRARIES} ${X11_LIBRARIES})
|
list(APPEND LIBS ${WAYLAND_LIBRARIES} ${X11_LIBRARIES})
|
||||||
list(APPEND FLAGS ${WAYLAND_CFLAGS})
|
list(APPEND FLAGS ${WAYLAND_CFLAGS})
|
||||||
|
|
||||||
set(PLATFORM_SOURCES
|
list(APPEND PLATFORM_SOURCES
|
||||||
../common/video/glx_context.cpp
|
../common/video/glx_context.cpp
|
||||||
../common/video/wayland_egl_context.cpp
|
../common/video/wayland_egl_context.cpp
|
||||||
../common/video/wayland_surface.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/glx.c
|
||||||
../external/glad/src/egl.c)
|
../external/glad/src/egl.c)
|
||||||
else()
|
else()
|
||||||
set(PLATFORM_SOURCES
|
list(APPEND PLATFORM_SOURCES
|
||||||
../common/video/wgl_context.cpp
|
../common/video/wgl_context.cpp
|
||||||
../external/glad/src/wgl.c)
|
../external/glad/src/wgl.c)
|
||||||
list(APPEND LIBS opengl32)
|
list(APPEND LIBS opengl32)
|
||||||
|
|
|
@ -1,12 +1,17 @@
|
||||||
#include "EmuApplication.hpp"
|
#include "EmuApplication.hpp"
|
||||||
#include "common/audio/s9x_sound_driver_sdl.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 "common/audio/s9x_sound_driver_cubeb.hpp"
|
||||||
|
#include <qlabel.h>
|
||||||
|
#ifdef USE_PULSEAUDIO
|
||||||
|
#include "common/audio/s9x_sound_driver_pulse.hpp"
|
||||||
|
#endif
|
||||||
#include <QTimer>
|
#include <QTimer>
|
||||||
#include <chrono>
|
#include <chrono>
|
||||||
#include <thread>
|
#include <thread>
|
||||||
using namespace std::chrono_literals;
|
using namespace std::chrono_literals;
|
||||||
|
|
||||||
|
#undef SOUND_BUFFER_WINDOW
|
||||||
|
|
||||||
EmuApplication::EmuApplication()
|
EmuApplication::EmuApplication()
|
||||||
{
|
{
|
||||||
core = Snes9xController::get();
|
core = Snes9xController::get();
|
||||||
|
@ -22,11 +27,14 @@ void EmuApplication::restartAudio()
|
||||||
sound_driver.reset();
|
sound_driver.reset();
|
||||||
core->sound_output_function = nullptr;
|
core->sound_output_function = nullptr;
|
||||||
|
|
||||||
if (config->sound_driver == "portaudio")
|
#ifdef USE_PULSEAUDIO
|
||||||
sound_driver = std::make_unique<S9xPortAudioSoundDriver>();
|
if (config->sound_driver == "pulseaudio")
|
||||||
else if (config->sound_driver == "cubeb")
|
sound_driver = std::make_unique<S9xPulseSoundDriver>();
|
||||||
|
#endif
|
||||||
|
if (config->sound_driver == "cubeb")
|
||||||
sound_driver = std::make_unique<S9xCubebSoundDriver>();
|
sound_driver = std::make_unique<S9xCubebSoundDriver>();
|
||||||
else
|
|
||||||
|
if (!sound_driver)
|
||||||
{
|
{
|
||||||
config->sound_driver = "sdl";
|
config->sound_driver = "sdl";
|
||||||
sound_driver = std::make_unique<S9xSDLSoundDriver>();
|
sound_driver = std::make_unique<S9xSDLSoundDriver>();
|
||||||
|
@ -47,11 +55,42 @@ void EmuApplication::restartAudio()
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#ifdef SOUND_BUFFER_WINDOW
|
||||||
|
#include <QProgressDialog>
|
||||||
|
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)
|
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())
|
if (config->speed_sync_method == EmuConfig::eSoundSync && !core->isAbnormalSpeed())
|
||||||
{
|
{
|
||||||
int iterations = 0;
|
int iterations = 0;
|
||||||
|
@ -66,34 +105,13 @@ void EmuApplication::writeSamples(int16_t *data, int samples)
|
||||||
|
|
||||||
if (!sound_driver->write_samples(data, samples))
|
if (!sound_driver->write_samples(data, samples))
|
||||||
{
|
{
|
||||||
printf("Overrun\n");
|
|
||||||
core->clearSoundBuffer();
|
core->clearSoundBuffer();
|
||||||
}
|
}
|
||||||
|
|
||||||
char sym = '*';
|
#ifdef SOUND_BUFFER_WINDOW
|
||||||
int percent = (buffer_level.second - buffer_level.first) * 100 / buffer_level.second;
|
int percent = (buffer_level.second - buffer_level.first) * 100 / buffer_level.second;
|
||||||
printf("\033[0;0H>");
|
trackBufferLevel(percent, window.get());
|
||||||
if (percent < 50)
|
#endif
|
||||||
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");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void EmuApplication::startGame()
|
void EmuApplication::startGame()
|
||||||
|
|
|
@ -89,14 +89,16 @@ void SoundPanel::showEvent(QShowEvent *event)
|
||||||
auto &config = app->config;
|
auto &config = app->config;
|
||||||
|
|
||||||
comboBox_driver->clear();
|
comboBox_driver->clear();
|
||||||
comboBox_driver->addItem("SDL");
|
|
||||||
comboBox_driver->addItem("PortAudio");
|
|
||||||
comboBox_driver->addItem("Cubeb");
|
|
||||||
|
|
||||||
driver_list.clear();
|
driver_list.clear();
|
||||||
driver_list.push_back("sdl");
|
|
||||||
driver_list.push_back("portaudio");
|
comboBox_driver->addItem("Cubeb");
|
||||||
driver_list.push_back("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++)
|
for (int i = 0; i < driver_list.size(); i++)
|
||||||
{
|
{
|
||||||
|
|
Loading…
Reference in New Issue