Gtk: Simplify drivers by moving port code to gtk_sound.cpp.

This commit is contained in:
BearOso 2023-06-07 15:34:10 -05:00
parent 9a0712b258
commit 8c5b6d012e
12 changed files with 242 additions and 365 deletions

View File

@ -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<int16_t> 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 */

View File

@ -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 <cstdint>
#include <tuple>
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<int, int> 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;
};

View File

@ -5,9 +5,6 @@
\*****************************************************************************/
#include "gtk_sound_driver_alsa.h"
#include "gtk_s9x.h"
#include "snes9x.h"
#include "apu/apu.h"
#include <fcntl.h>
#include <sys/ioctl.h>
@ -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);
}
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<int, int> S9xAlsaSoundDriver::buffer_level()
{
return { snd_pcm_avail(pcm), output_buffer_size_bytes / 4 };
}

View File

@ -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<int, int> buffer_level() override;
private:
snd_pcm_t *pcm;
std::vector<uint8_t> sound_buffer;
int output_buffer_size;
int output_buffer_size_bytes;
};
#endif /* __GTK_SOUND_DRIVER_ALSA_H */

View File

@ -5,47 +5,30 @@
\*****************************************************************************/
#include "gtk_sound_driver_oss.h"
#include "gtk_s9x.h"
#include "snes9x.h"
#include "apu/apu.h"
#include <fcntl.h>
#include <sys/ioctl.h>
#include <sys/soundcard.h>
#include <sys/time.h>
static void oss_samples_available(void *data)
{
((S9xOSSSoundDriver *)data)->samples_available();
}
#include <unistd.h>
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<int, int> 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();
if (samples == 0)
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)
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)

View File

@ -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<int, int> 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 */

View File

@ -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 <cstring>
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,
&param,
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<int, int> 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<int16_t> 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);
}

View File

@ -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<int, int> 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;
};

View File

@ -5,10 +5,8 @@
\*****************************************************************************/
#include "gtk_sound_driver_pulse.h"
#include "gtk_s9x.h"
#include "snes9x.h"
#include "apu/apu.h"
#include <cstring>
#include <fcntl.h>
#include <sys/ioctl.h>
#include <sys/time.h>
@ -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()
{
bool clear_samples = false;
lock();
size_t bytes = pa_stream_writable_size(stream);
auto buffer_attr = pa_stream_get_buffer_attr(stream);
unlock();
return bytes / 2;
}
buffer_size = buffer_attr->tlength;
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)
{
std::pair<int, int> S9xPulseSoundDriver::buffer_level()
{
lock();
int i;
for (i = 0; i < 500; i++)
{
bytes = pa_stream_writable_size(stream);
if (bytes / 2 < samples)
{
size_t bytes = pa_stream_writable_size(stream);
unlock();
usleep(50);
lock();
}
else
{
unlock();
break;
}
}
if (i == 500)
printf("PulseAudio: Timed out waiting for sound sync.\n");
}
}
return { bytes, buffer_size };
}
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();
}

View File

@ -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<int, int> buffer_level() override;
pa_threaded_mainloop *mainloop;
pa_context *context;
pa_stream *stream;
private:
void lock();
void unlock();
void wait();
int buffer_size;
};

View File

@ -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();
if (samples > buffer.space_empty())
samples = buffer.space_empty();
mutex.unlock();
}
}
mutex.lock();
buffer.push(temp, snes_samples_available);
if (Settings.DynamicRateControl)
S9xUpdateDynamicRate(buffer.space_empty(), buffer.buffer_size);
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);
}
}
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<int, int> S9xSDLSoundDriver::buffer_level()
{
mutex.lock();
std::pair<int, int> level = { buffer.space_empty(), buffer.buffer_size };
mutex.unlock();
return level;
}

View File

@ -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<int, int> buffer_level() override;
private:
void mix(unsigned char *output, int bytes);
SDL_AudioSpec audiospec;
Resampler buffer;
std::mutex mutex;