diff --git a/gtk/src/gtk_sound.cpp b/gtk/src/gtk_sound.cpp index 1a6d2d34..1fbaaf50 100644 --- a/gtk/src/gtk_sound.cpp +++ b/gtk/src/gtk_sound.cpp @@ -150,7 +150,7 @@ void S9xPortSoundDeinit() S9xSoundStop(); if (driver) - driver->terminate(); + driver->deinit(); delete driver; } @@ -167,6 +167,55 @@ void S9xSoundStop() driver->stop(); } +static std::vector temp_buffer; +void S9xSamplesAvailable(void *userdata) +{ + bool clear_leftover_samples = false; + int samples = S9xGetSampleCount(); + int space_free = driver->space_free(); + + if (space_free < samples) + { + if (!Settings.SoundSync) + clear_leftover_samples = true; + + if (Settings.SoundSync && !Settings.TurboMode && !Settings.Mute) + { + for (int i = 0; i < 200; i++) // Wait for a max of 5ms + { + space_free = driver->space_free(); + if (space_free < samples) + usleep(50); + else + break; + } + } + } + + if (space_free < samples) + samples = space_free & ~1; + + if (samples == 0) + { + S9xClearSamples(); + return; + } + + if ((int)temp_buffer.size() < samples) + temp_buffer.resize(samples); + S9xMixSamples((uint8_t *)temp_buffer.data(), samples); + driver->write_samples(temp_buffer.data(), samples); + + if (clear_leftover_samples) + S9xClearSamples(); + + if (Settings.DynamicRateControl) + { + auto level = driver->buffer_level(); + S9xUpdateDynamicRate(level.first, level.second); + } +} + bool8 S9xOpenSoundDevice() { if (gui_config->mute_sound) @@ -174,7 +223,8 @@ bool8 S9xOpenSoundDevice() gui_config->sound_buffer_size = CLAMP(gui_config->sound_buffer_size, 2, 256); - return driver->open_device(); + S9xSetSamplesAvailableCallback(S9xSamplesAvailable, nullptr); + return driver->open_device(Settings.SoundPlaybackRate, gui_config->sound_buffer_size); } /* This really shouldn't be in the port layer */ diff --git a/gtk/src/gtk_sound_driver.h b/gtk/src/gtk_sound_driver.h index 8f5fe02f..52ea042e 100644 --- a/gtk/src/gtk_sound_driver.h +++ b/gtk/src/gtk_sound_driver.h @@ -4,10 +4,11 @@ For further information, consult the LICENSE file in the root directory. \*****************************************************************************/ -#ifndef __GTK_SOUND_DRIVER_H -#define __GTK_SOUND_DRIVER_H +#ifndef __S9X_SOUND_DRIVER_H +#define __S9X_SOUND_DRIVER_H -#include "gtk_s9x.h" +#include +#include class S9xSoundDriver { @@ -15,9 +16,12 @@ class S9xSoundDriver virtual ~S9xSoundDriver() { } + virtual void write_samples(int16_t *data, int samples) = 0; + virtual int space_free() = 0; + virtual std::pair buffer_level() = 0; virtual void init() = 0; - virtual void terminate() = 0; - virtual bool open_device() = 0; + virtual void deinit() = 0; + virtual bool open_device(int playback_rate, int buffer_size) = 0; virtual void start() = 0; virtual void stop() = 0; }; diff --git a/gtk/src/gtk_sound_driver_alsa.cpp b/gtk/src/gtk_sound_driver_alsa.cpp index 8e94ab39..275f2535 100644 --- a/gtk/src/gtk_sound_driver_alsa.cpp +++ b/gtk/src/gtk_sound_driver_alsa.cpp @@ -5,9 +5,6 @@ \*****************************************************************************/ #include "gtk_sound_driver_alsa.h" -#include "gtk_s9x.h" -#include "snes9x.h" -#include "apu/apu.h" #include #include @@ -16,27 +13,22 @@ S9xAlsaSoundDriver::S9xAlsaSoundDriver() { pcm = {}; - sound_buffer.clear(); } void S9xAlsaSoundDriver::init() { } -void S9xAlsaSoundDriver::terminate() +void S9xAlsaSoundDriver::deinit() { stop(); - S9xSetSamplesAvailableCallback(nullptr, nullptr); - if (pcm) { snd_pcm_drain(pcm); snd_pcm_close(pcm); pcm = nullptr; } - - sound_buffer.clear(); } void S9xAlsaSoundDriver::start() @@ -47,16 +39,17 @@ void S9xAlsaSoundDriver::stop() { } -bool S9xAlsaSoundDriver::open_device() +bool S9xAlsaSoundDriver::open_device(int playback_rate, int buffer_size_ms) { int err; unsigned int periods = 8; - unsigned int buffer_size = gui_config->sound_buffer_size * 1000; + unsigned int buffer_size = buffer_size_ms * 1000; snd_pcm_sw_params_t *sw_params; snd_pcm_hw_params_t *hw_params; snd_pcm_uframes_t alsa_buffer_size, alsa_period_size; unsigned int min = 0; unsigned int max = 0; + unsigned int playback_rate_param = 0; printf("ALSA sound driver initializing...\n"); printf(" --> (Device: default)...\n"); @@ -69,8 +62,8 @@ bool S9xAlsaSoundDriver::open_device() } printf(" --> (16-bit Stereo, %dhz, %d ms)...\n", - Settings.SoundPlaybackRate, - gui_config->sound_buffer_size); + playback_rate, + buffer_size_ms); snd_pcm_hw_params_alloca(&hw_params); snd_pcm_hw_params_any(pcm, hw_params); @@ -82,12 +75,14 @@ bool S9xAlsaSoundDriver::open_device() snd_pcm_hw_params_get_rate_min(hw_params, &min, nullptr); snd_pcm_hw_params_get_rate_max(hw_params, &max, nullptr); printf(" --> Available rates: %d to %d\n", min, max); - if (Settings.SoundPlaybackRate > max && Settings.SoundPlaybackRate < min) + if (playback_rate > (int)max || playback_rate < (int)min) { - printf(" Rate %d not available. Using %d instead.\n", Settings.SoundPlaybackRate, max); - Settings.SoundPlaybackRate = max; + printf(" Rate %d not available. Using %d instead.\n", playback_rate, max); + playback_rate = max; } - snd_pcm_hw_params_set_rate_near(pcm, hw_params, &Settings.SoundPlaybackRate, nullptr); + + playback_rate_param = playback_rate; + snd_pcm_hw_params_set_rate_near(pcm, hw_params, &playback_rate_param, nullptr); snd_pcm_hw_params_get_buffer_time_min(hw_params, &min, nullptr); snd_pcm_hw_params_get_buffer_time_max(hw_params, &max, nullptr); @@ -121,7 +116,7 @@ bool S9xAlsaSoundDriver::open_device() snd_pcm_sw_params_set_start_threshold(pcm, sw_params, (alsa_buffer_size / 2)); err = snd_pcm_sw_params(pcm, sw_params); - output_buffer_size = snd_pcm_frames_to_bytes(pcm, alsa_buffer_size); + output_buffer_size_bytes = snd_pcm_frames_to_bytes(pcm, alsa_buffer_size); if (err < 0) { @@ -131,10 +126,6 @@ bool S9xAlsaSoundDriver::open_device() printf("OK\n"); - S9xSetSamplesAvailableCallback([](void *userdata) { - ((decltype(this)) userdata)->samples_available();; - }, this); - return true; close_fail: @@ -148,7 +139,7 @@ fail: return false; } -void S9xAlsaSoundDriver::samples_available() +void S9xAlsaSoundDriver::write_samples(int16_t *data, int samples) { snd_pcm_sframes_t frames_written, frames; size_t bytes; @@ -161,47 +152,21 @@ void S9xAlsaSoundDriver::samples_available() return; } - int snes_frames_available = S9xGetSampleCount() >> 1; - - if (Settings.DynamicRateControl && !Settings.SoundSync) - { - // Using rate control, we should always keep the emulator's sound buffers empty to - // maintain an accurate measurement. - if (frames < snes_frames_available) - { - S9xClearSamples(); - return; - } - } - - if (Settings.SoundSync && !Settings.TurboMode && !Settings.Mute) - { - snd_pcm_nonblock(pcm, 0); - frames = snes_frames_available; - } - else - { - snd_pcm_nonblock(pcm, 1); - frames = MIN(frames, snes_frames_available); - } + snd_pcm_nonblock(pcm, 0); + if (frames > samples / 2) + frames = samples / 2; bytes = snd_pcm_frames_to_bytes(pcm, frames); if (bytes <= 0) return; - if (sound_buffer.size() < bytes) - sound_buffer.resize(bytes); - - S9xMixSamples(sound_buffer.data(), frames * 2); - frames_written = 0; - while (frames_written < frames) { int result; result = snd_pcm_writei(pcm, - &sound_buffer[snd_pcm_frames_to_bytes(pcm, frames_written)], + &data[snd_pcm_frames_to_bytes(pcm, frames_written) / 2], frames - frames_written); if (result < 0) @@ -218,11 +183,14 @@ void S9xAlsaSoundDriver::samples_available() frames_written += result; } } - - if (Settings.DynamicRateControl) - { - frames = snd_pcm_avail(pcm); - S9xUpdateDynamicRate(snd_pcm_frames_to_bytes(pcm, frames), - output_buffer_size); - } +} + +int S9xAlsaSoundDriver::space_free() +{ + return snd_pcm_avail(pcm) * 2; +} + +std::pair S9xAlsaSoundDriver::buffer_level() +{ + return { snd_pcm_avail(pcm), output_buffer_size_bytes / 4 }; } diff --git a/gtk/src/gtk_sound_driver_alsa.h b/gtk/src/gtk_sound_driver_alsa.h index 09882ed2..53d9aadb 100644 --- a/gtk/src/gtk_sound_driver_alsa.h +++ b/gtk/src/gtk_sound_driver_alsa.h @@ -15,17 +15,18 @@ class S9xAlsaSoundDriver : public S9xSoundDriver { public: S9xAlsaSoundDriver(); - void init(); - void terminate(); - bool open_device(); - void start(); - void stop(); - void samples_available(); + void init() override; + void deinit() override; + bool open_device(int playback_rate, int buffer_size_ms) override; + void start() override; + void stop() override; + void write_samples(int16_t *data, int samples) override; + int space_free() override; + std::pair buffer_level() override; private: snd_pcm_t *pcm; - std::vector sound_buffer; - int output_buffer_size; + int output_buffer_size_bytes; }; #endif /* __GTK_SOUND_DRIVER_ALSA_H */ diff --git a/gtk/src/gtk_sound_driver_oss.cpp b/gtk/src/gtk_sound_driver_oss.cpp index 7f5412a5..41494aae 100644 --- a/gtk/src/gtk_sound_driver_oss.cpp +++ b/gtk/src/gtk_sound_driver_oss.cpp @@ -5,47 +5,30 @@ \*****************************************************************************/ #include "gtk_sound_driver_oss.h" -#include "gtk_s9x.h" -#include "snes9x.h" -#include "apu/apu.h" #include #include #include #include - -static void oss_samples_available(void *data) -{ - ((S9xOSSSoundDriver *)data)->samples_available(); -} +#include S9xOSSSoundDriver::S9xOSSSoundDriver() { filedes = -1; - sound_buffer = NULL; - sound_buffer_size = 0; } void S9xOSSSoundDriver::init() { } -void S9xOSSSoundDriver::terminate() +void S9xOSSSoundDriver::deinit() { stop(); - S9xSetSamplesAvailableCallback(NULL, NULL); - if (filedes >= 0) { close(filedes); } - - if (sound_buffer) - { - free(sound_buffer); - sound_buffer = NULL; - } } void S9xOSSSoundDriver::start() @@ -56,16 +39,16 @@ void S9xOSSSoundDriver::stop() { } -bool S9xOSSSoundDriver::open_device() +bool S9xOSSSoundDriver::open_device(int playback_rate, int buffer_size_ms) { int temp; audio_buf_info info; - output_buffer_size = (gui_config->sound_buffer_size * Settings.SoundPlaybackRate) / 1000; + output_buffer_size_bytes = (buffer_size_ms * playback_rate) / 1000; - output_buffer_size *= 4; - if (output_buffer_size < 256) - output_buffer_size = 256; + output_buffer_size_bytes *= 4; + if (output_buffer_size_bytes < 256) + output_buffer_size_bytes = 256; printf("OSS sound driver initializing...\n"); @@ -115,31 +98,29 @@ bool S9xOSSSoundDriver::open_device() printf("OK\n"); - printf(" --> (Frequency: %d)...", Settings.SoundPlaybackRate); - if (ioctl(filedes, SNDCTL_DSP_SPEED, &Settings.SoundPlaybackRate) < 0) + printf(" --> (Frequency: %d)...", playback_rate); + if (ioctl(filedes, SNDCTL_DSP_SPEED, &playback_rate) < 0) goto close_fail; printf("OK\n"); /* OSS requires a power-of-two buffer size, first 16 bits are the number * of fragments to generate, second 16 are the respective power-of-two. */ - temp = (4 << 16) | (S9xSoundBase2log(output_buffer_size / 4)); + temp = (4 << 16) | (S9xSoundBase2log(output_buffer_size_bytes / 4)); if (ioctl(filedes, SNDCTL_DSP_SETFRAGMENT, &temp) < 0) goto close_fail; ioctl(filedes, SNDCTL_DSP_GETOSPACE, &info); - output_buffer_size = info.fragsize * info.fragstotal; + output_buffer_size_bytes = info.fragsize * info.fragstotal; printf(" --> (Buffer size: %d bytes, %dms latency)...", - output_buffer_size, - (output_buffer_size * 250) / Settings.SoundPlaybackRate); + output_buffer_size_bytes, + (output_buffer_size_bytes * 250) / playback_rate); printf("OK\n"); - S9xSetSamplesAvailableCallback(oss_samples_available, this); - return true; close_fail: @@ -152,68 +133,41 @@ fail: return false; } -void S9xOSSSoundDriver::samples_available() +int S9xOSSSoundDriver::space_free() +{ + audio_buf_info info; + ioctl(filedes, SNDCTL_DSP_GETOSPACE, &info); + return info.bytes / 2; +} + +std::pair S9xOSSSoundDriver::buffer_level() +{ + return { space_free(), output_buffer_size_bytes / 2}; +} + +void S9xOSSSoundDriver::write_samples(int16_t *data, int samples) { audio_buf_info info; - int samples_to_write; int bytes_to_write; int bytes_written; ioctl(filedes, SNDCTL_DSP_GETOSPACE, &info); - if (Settings.DynamicRateControl) - { - S9xUpdateDynamicRate(info.bytes, output_buffer_size); - } + if (samples > info.bytes / 2) + samples = info.bytes / 2; - samples_to_write = S9xGetSampleCount(); - - if (Settings.DynamicRateControl && !Settings.SoundSync) - { - // Using rate control, we should always keep the emulator's sound buffers empty to - // maintain an accurate measurement. - if (samples_to_write > (info.bytes >> 1)) - { - S9xClearSamples(); - return; - } - } - - if (Settings.SoundSync && !Settings.TurboMode && !Settings.Mute) - { - while (info.bytes >> 1 < samples_to_write) - { - int usec_to_sleep = ((samples_to_write >> 1) - (info.bytes >> 2)) * 10000 / - (Settings.SoundPlaybackRate / 100); - usleep(usec_to_sleep > 0 ? usec_to_sleep : 0); - ioctl(filedes, SNDCTL_DSP_GETOSPACE, &info); - } - } - else - { - samples_to_write = MIN(info.bytes >> 1, samples_to_write) & ~1; - } - - if (samples_to_write < 0) + if (samples == 0) return; - if (sound_buffer_size < samples_to_write * 2) - { - sound_buffer = (uint8 *)realloc(sound_buffer, samples_to_write * 2); - sound_buffer_size = samples_to_write * 2; - } - - S9xMixSamples(sound_buffer, samples_to_write); - bytes_written = 0; - bytes_to_write = samples_to_write * 2; + bytes_to_write = samples * 2; while (bytes_to_write > bytes_written) { int result; result = write(filedes, - ((char *)sound_buffer) + bytes_written, + ((char *)data) + bytes_written, bytes_to_write - bytes_written); if (result < 0) diff --git a/gtk/src/gtk_sound_driver_oss.h b/gtk/src/gtk_sound_driver_oss.h index d2fcd30d..6fd10b77 100644 --- a/gtk/src/gtk_sound_driver_oss.h +++ b/gtk/src/gtk_sound_driver_oss.h @@ -14,18 +14,18 @@ class S9xOSSSoundDriver : public S9xSoundDriver { public: S9xOSSSoundDriver(); - void init(); - void terminate(); - bool open_device(); - void start(); - void stop(); - void samples_available(); + void init() override; + void deinit() override; + bool open_device(int playback_rate, int buffer_size_ms) override; + void start() override; + void stop() override; + void write_samples(int16_t *data, int samples) override; + int space_free() override; + std::pair buffer_level() override; private: int filedes; - uint8 *sound_buffer; - int sound_buffer_size; - int output_buffer_size; + int output_buffer_size_bytes; }; #endif /* __GTK_SOUND_DRIVER_OSS_H */ diff --git a/gtk/src/gtk_sound_driver_portaudio.cpp b/gtk/src/gtk_sound_driver_portaudio.cpp index a33d0159..dc513345 100644 --- a/gtk/src/gtk_sound_driver_portaudio.cpp +++ b/gtk/src/gtk_sound_driver_portaudio.cpp @@ -5,20 +5,11 @@ \*****************************************************************************/ #include "gtk_sound_driver_portaudio.h" -#include "gtk_s9x.h" -#include "apu/apu.h" -#include "snes9x.h" - -static void port_audio_samples_available_callback(void *data) -{ - ((S9xPortAudioSoundDriver *)data)->samples_available(); -} +#include S9xPortAudioSoundDriver::S9xPortAudioSoundDriver() { audio_stream = NULL; - sound_buffer = NULL; - sound_buffer_size = 0; } void S9xPortAudioSoundDriver::init() @@ -33,26 +24,18 @@ void S9xPortAudioSoundDriver::init() Pa_GetErrorText(err)); } -void S9xPortAudioSoundDriver::terminate() +void S9xPortAudioSoundDriver::deinit() { stop(); - S9xSetSamplesAvailableCallback(NULL, NULL); - Pa_Terminate(); - - if (sound_buffer) - { - free(sound_buffer); - sound_buffer = NULL; - } } void S9xPortAudioSoundDriver::start() { PaError err; - if (audio_stream != NULL && !(gui_config->mute_sound)) + if (audio_stream != NULL) { if ((Pa_IsStreamActive(audio_stream))) return; @@ -74,16 +57,7 @@ void S9xPortAudioSoundDriver::stop() } } -void S9xPortAudioSoundDriver::set_buffer_min(int frames) -{ - if ((sound_buffer_size < frames) || sound_buffer == NULL) - { - sound_buffer = (uint8 *)realloc(sound_buffer, frames << 2); - sound_buffer_size = frames; - } -} - -bool S9xPortAudioSoundDriver::open_device() +bool S9xPortAudioSoundDriver::open_device(int playback_rate, int buffer_size_ms) { PaStreamParameters param; const PaDeviceInfo *device_info; @@ -133,7 +107,7 @@ bool S9xPortAudioSoundDriver::open_device() } param.device = hostapi_info->defaultOutputDevice; - param.suggestedLatency = gui_config->sound_buffer_size * 0.001; + param.suggestedLatency = buffer_size_ms * 0.001; printf("(%s : %s, latency %dms)...\n", hostapi_info->name, @@ -145,7 +119,7 @@ bool S9xPortAudioSoundDriver::open_device() err = Pa_OpenStream(&audio_stream, NULL, ¶m, - Settings.SoundPlaybackRate, + playback_rate, 0, paNoFlag, NULL, @@ -174,15 +148,20 @@ bool S9xPortAudioSoundDriver::open_device() return false; } - S9xSetSamplesAvailableCallback(port_audio_samples_available_callback, this); - - fflush(stdout); - fflush(stderr); - return true; } -void S9xPortAudioSoundDriver::samples_available() +int S9xPortAudioSoundDriver::space_free() +{ + return Pa_GetStreamWriteAvailable(audio_stream) * 2; +} + +std::pair S9xPortAudioSoundDriver::buffer_level() +{ + return { Pa_GetStreamWriteAvailable(audio_stream), output_buffer_size }; +} + +void S9xPortAudioSoundDriver::write_samples(int16_t *data, int samples) { int frames; @@ -191,38 +170,14 @@ void S9xPortAudioSoundDriver::samples_available() if (frames == output_buffer_size) { // Prime the stream - set_buffer_min(output_buffer_size >> 1); - memset(sound_buffer, 0, output_buffer_size << 1); - Pa_WriteStream(audio_stream, sound_buffer, output_buffer_size >> 1); + std::vector tmp(output_buffer_size); + memset(tmp.data(), 0, output_buffer_size << 1); + Pa_WriteStream(audio_stream, tmp.data(), output_buffer_size >> 1); frames -= output_buffer_size >> 1; } - int snes_frames_available = S9xGetSampleCount() >> 1; + if (frames > samples / 2) + frames = samples / 2; - if (Settings.DynamicRateControl && !Settings.SoundSync) - { - // Using rate control, we should always keep the emulator's sound buffers empty to - // maintain an accurate measurement. - if (frames < snes_frames_available) - { - S9xClearSamples(); - return; - } - } - - // Rely on PortAudio's blocking behavior to sync - if (Settings.SoundSync && !Settings.TurboMode && !Settings.Mute) - frames = snes_frames_available; - - frames = MIN(frames, snes_frames_available); - - set_buffer_min(frames); - S9xMixSamples(sound_buffer, frames << 1); - Pa_WriteStream(audio_stream, sound_buffer, frames); - - if (Settings.DynamicRateControl) - { - frames = Pa_GetStreamWriteAvailable(audio_stream); - S9xUpdateDynamicRate(frames, output_buffer_size); - } + Pa_WriteStream(audio_stream, data, frames); } diff --git a/gtk/src/gtk_sound_driver_portaudio.h b/gtk/src/gtk_sound_driver_portaudio.h index 2c88ee8e..f3eea923 100644 --- a/gtk/src/gtk_sound_driver_portaudio.h +++ b/gtk/src/gtk_sound_driver_portaudio.h @@ -17,18 +17,18 @@ class S9xPortAudioSoundDriver : public S9xSoundDriver { public: S9xPortAudioSoundDriver(); - void init(); - void terminate(); - bool open_device(); - void start(); - void stop(); + void init() override; + void deinit() override; + bool open_device(int playback_rate, int buffer_size) override; + void start() override; + void stop() override; + void write_samples(int16_t *data, int samples) override; + int space_free() override; + std::pair buffer_level() override; void samples_available(); private: - void set_buffer_min(int frames); PaStream *audio_stream; - int sound_buffer_size; - uint8 *sound_buffer; int output_buffer_size; }; diff --git a/gtk/src/gtk_sound_driver_pulse.cpp b/gtk/src/gtk_sound_driver_pulse.cpp index 4be8927d..e7eab8b5 100644 --- a/gtk/src/gtk_sound_driver_pulse.cpp +++ b/gtk/src/gtk_sound_driver_pulse.cpp @@ -5,10 +5,8 @@ \*****************************************************************************/ #include "gtk_sound_driver_pulse.h" -#include "gtk_s9x.h" -#include "snes9x.h" -#include "apu/apu.h" +#include #include #include #include @@ -26,10 +24,8 @@ void S9xPulseSoundDriver::init() buffer_size = {}; } -void S9xPulseSoundDriver::terminate() +void S9xPulseSoundDriver::deinit() { - S9xSetSamplesAvailableCallback(nullptr, nullptr); - if (mainloop) pa_threaded_mainloop_stop(mainloop); @@ -104,17 +100,17 @@ static void stream_state_callback(pa_stream *p, void *userdata) } } -bool S9xPulseSoundDriver::open_device() +bool S9xPulseSoundDriver::open_device(int playback_rate, int buffer_size_ms) { init(); pa_sample_spec ss; ss.channels = 2; ss.format = PA_SAMPLE_S16NE; - ss.rate = Settings.SoundPlaybackRate; + ss.rate = playback_rate; pa_buffer_attr buffer_attr; - buffer_attr.tlength = pa_usec_to_bytes(gui_config->sound_buffer_size * 1000, &ss); + buffer_attr.tlength = pa_usec_to_bytes(buffer_size_ms * 1000, &ss); buffer_attr.maxlength = buffer_attr.tlength * 2; buffer_attr.minreq = pa_usec_to_bytes(3000, &ss); buffer_attr.prebuf = -1; @@ -122,8 +118,8 @@ bool S9xPulseSoundDriver::open_device() printf("PulseAudio sound driver initializing...\n"); printf(" --> (%dhz, 16-bit Stereo, %dms)...", - Settings.SoundPlaybackRate, - gui_config->sound_buffer_size); + playback_rate, + buffer_size_ms); int err = PA_ERR_UNKNOWN; mainloop = pa_threaded_mainloop_new(); @@ -164,73 +160,39 @@ bool S9xPulseSoundDriver::open_device() printf("OK\n"); - S9xSetSamplesAvailableCallback([](void *userdata) { - ((decltype(this)) userdata)->samples_available();; - }, this); - - return true; } -void S9xPulseSoundDriver::samples_available() +int S9xPulseSoundDriver::space_free() +{ + lock(); + size_t bytes = pa_stream_writable_size(stream); + unlock(); + return bytes / 2; +} + +std::pair S9xPulseSoundDriver::buffer_level() { - bool clear_samples = false; - lock(); size_t bytes = pa_stream_writable_size(stream); - auto buffer_attr = pa_stream_get_buffer_attr(stream); unlock(); - buffer_size = buffer_attr->tlength; + return { bytes, buffer_size }; +} - size_t samples = S9xGetSampleCount(); - - int frames_available = samples / 2; - int frames_writable = bytes / 4; - - if (frames_writable < frames_available) - { - if (!Settings.SoundSync) - { - clear_samples = true; - } - - if (Settings.SoundSync && !Settings.TurboMode && !Settings.Mute) - { - lock(); - int i; - for (i = 0; i < 500; i++) - { - bytes = pa_stream_writable_size(stream); - if (bytes / 2 < samples) - { - unlock(); - usleep(50); - lock(); - } - else - { - unlock(); - break; - } - } - - if (i == 500) - printf("PulseAudio: Timed out waiting for sound sync.\n"); - } - } +void S9xPulseSoundDriver::write_samples(int16_t *data, int samples) +{ + lock(); + size_t bytes = pa_stream_writable_size(stream); + unlock(); bytes = MIN(bytes, samples * 2) & ~1; - if (!bytes) - { - S9xClearSamples(); + if (bytes == 0) return; - } lock(); - - void *output_buffer;; + void *output_buffer; if (pa_stream_begin_write(stream, &output_buffer, &bytes) != 0) { pa_stream_flush(stream, nullptr, nullptr); @@ -244,17 +206,7 @@ void S9xPulseSoundDriver::samples_available() return; } - S9xMixSamples((uint8_t *)output_buffer, bytes >> 1); + std::memcpy(output_buffer, data, bytes); pa_stream_write(stream, output_buffer, bytes, nullptr, 0, PA_SEEK_RELATIVE); - - if (Settings.DynamicRateControl) - { - bytes = pa_stream_writable_size(stream); - S9xUpdateDynamicRate(bytes, buffer_size); - } - unlock(); - - if (clear_samples) - S9xClearSamples(); } diff --git a/gtk/src/gtk_sound_driver_pulse.h b/gtk/src/gtk_sound_driver_pulse.h index 108a6227..06e5b177 100644 --- a/gtk/src/gtk_sound_driver_pulse.h +++ b/gtk/src/gtk_sound_driver_pulse.h @@ -15,21 +15,23 @@ class S9xPulseSoundDriver : public S9xSoundDriver { public: S9xPulseSoundDriver(); - void init(); - void terminate(); - bool open_device(); - void start(); - void stop(); - void samples_available(); - void lock(); - void unlock(); - void wait(); - + void init() override; + void deinit() override; + void write_samples(int16_t *data, int samples) override; + bool open_device(int playback_rate, int buffer_size) override; + void start() override; + void stop() override; + int space_free() override; + std::pair buffer_level() override; pa_threaded_mainloop *mainloop; pa_context *context; pa_stream *stream; private: + void lock(); + void unlock(); + void wait(); + int buffer_size; }; diff --git a/gtk/src/gtk_sound_driver_sdl.cpp b/gtk/src/gtk_sound_driver_sdl.cpp index 4e4dc356..22e52e7e 100644 --- a/gtk/src/gtk_sound_driver_sdl.cpp +++ b/gtk/src/gtk_sound_driver_sdl.cpp @@ -6,34 +6,13 @@ #include "gtk_sound_driver_sdl.h" #include "SDL_audio.h" -#include "gtk_s9x.h" -#include "apu/apu.h" -#include "snes9x.h" -void S9xSDLSoundDriver::samples_available() +void S9xSDLSoundDriver::write_samples(int16_t *data, int samples) { - int snes_samples_available = S9xGetSampleCount(); - S9xMixSamples((uint8 *)temp, snes_samples_available); - - if (Settings.SoundSync && !Settings.TurboMode && !Settings.Mute) - { - mutex.lock(); - int samples = buffer.space_empty(); - mutex.unlock(); - - while (samples < snes_samples_available) - { - usleep(100); - mutex.lock(); - samples = buffer.space_empty(); - mutex.unlock(); - } - } - mutex.lock(); - buffer.push(temp, snes_samples_available); - if (Settings.DynamicRateControl) - S9xUpdateDynamicRate(buffer.space_empty(), buffer.buffer_size); + if (samples > buffer.space_empty()) + samples = buffer.space_empty(); + buffer.push(data, samples); mutex.unlock(); } @@ -55,7 +34,7 @@ void S9xSDLSoundDriver::init() stop(); } -void S9xSDLSoundDriver::terminate() +void S9xSDLSoundDriver::deinit() { stop(); SDL_CloseAudio(); @@ -64,10 +43,7 @@ void S9xSDLSoundDriver::terminate() void S9xSDLSoundDriver::start() { - if (!gui_config->mute_sound) - { - SDL_PauseAudio(0); - } + SDL_PauseAudio(0); } void S9xSDLSoundDriver::stop() @@ -75,10 +51,10 @@ void S9xSDLSoundDriver::stop() SDL_PauseAudio(1); } -bool S9xSDLSoundDriver::open_device() +bool S9xSDLSoundDriver::open_device(int playback_rate, int buffer_size) { audiospec = {}; - audiospec.freq = Settings.SoundPlaybackRate; + audiospec.freq = playback_rate; audiospec.channels = 2; audiospec.format = AUDIO_S16SYS; audiospec.samples = audiospec.freq * 4 / 1000; // 4ms per sampling @@ -101,11 +77,23 @@ bool S9xSDLSoundDriver::open_device() printf("OK\n"); - buffer.resize(gui_config->sound_buffer_size * audiospec.freq / 500); - - S9xSetSamplesAvailableCallback([](void *userdata) { - ((decltype(this)) userdata)->samples_available();; - }, this); + buffer.resize(buffer_size * audiospec.freq / 1000); return true; } + +int S9xSDLSoundDriver::space_free() +{ + mutex.lock(); + auto space_empty = buffer.space_empty(); + mutex.unlock(); + return space_empty; +} + +std::pair S9xSDLSoundDriver::buffer_level() +{ + mutex.lock(); + std::pair level = { buffer.space_empty(), buffer.buffer_size }; + mutex.unlock(); + return level; +} \ No newline at end of file diff --git a/gtk/src/gtk_sound_driver_sdl.h b/gtk/src/gtk_sound_driver_sdl.h index 18083a15..61890e0c 100644 --- a/gtk/src/gtk_sound_driver_sdl.h +++ b/gtk/src/gtk_sound_driver_sdl.h @@ -23,15 +23,18 @@ class S9xSDLSoundDriver : public S9xSoundDriver { public: S9xSDLSoundDriver(); - void init(); - void terminate(); - bool open_device(); - void start(); - void stop(); - void mix(unsigned char *output, int bytes); - void samples_available(); + void init() override; + void deinit() override; + bool open_device(int playback_rate, int buffer_size) override; + void start() override; + void stop() override; + void write_samples(int16_t *data, int samples) override; + int space_free() override; + std::pair buffer_level() override; private: + void mix(unsigned char *output, int bytes); + SDL_AudioSpec audiospec; Resampler buffer; std::mutex mutex;