Gtk: Clean up a couple sound drivers.

Make them more idiomatic C++.
This commit is contained in:
BearOso 2022-03-12 11:19:39 -06:00
parent 0a94eaa6dc
commit a8fafcd94c
4 changed files with 74 additions and 134 deletions

View File

@ -13,16 +13,10 @@
#include <sys/ioctl.h> #include <sys/ioctl.h>
#include <sys/time.h> #include <sys/time.h>
static void alsa_samples_available(void *data)
{
((S9xAlsaSoundDriver *)data)->samples_available();
}
S9xAlsaSoundDriver::S9xAlsaSoundDriver() S9xAlsaSoundDriver::S9xAlsaSoundDriver()
{ {
pcm = NULL; pcm = {};
sound_buffer = NULL; sound_buffer.clear();
sound_buffer_size = 0;
} }
void S9xAlsaSoundDriver::init() void S9xAlsaSoundDriver::init()
@ -33,20 +27,16 @@ void S9xAlsaSoundDriver::terminate()
{ {
stop(); stop();
S9xSetSamplesAvailableCallback(NULL, NULL); S9xSetSamplesAvailableCallback(nullptr, nullptr);
if (pcm) if (pcm)
{ {
snd_pcm_drain(pcm); snd_pcm_drain(pcm);
snd_pcm_close(pcm); snd_pcm_close(pcm);
pcm = NULL; pcm = nullptr;
} }
if (sound_buffer) sound_buffer.clear();
{
free(sound_buffer);
sound_buffer = NULL;
}
} }
void S9xAlsaSoundDriver::start() void S9xAlsaSoundDriver::start()
@ -71,10 +61,7 @@ bool S9xAlsaSoundDriver::open_device()
printf("ALSA sound driver initializing...\n"); printf("ALSA sound driver initializing...\n");
printf(" --> (Device: default)...\n"); printf(" --> (Device: default)...\n");
err = snd_pcm_open(&pcm, err = snd_pcm_open(&pcm, "default", SND_PCM_STREAM_PLAYBACK, SND_PCM_NONBLOCK);
"default",
SND_PCM_STREAM_PLAYBACK,
SND_PCM_NONBLOCK);
if (err < 0) if (err < 0)
{ {
@ -92,34 +79,34 @@ bool S9xAlsaSoundDriver::open_device()
snd_pcm_hw_params_set_rate_resample(pcm, hw_params, 0); snd_pcm_hw_params_set_rate_resample(pcm, hw_params, 0);
snd_pcm_hw_params_set_channels(pcm, hw_params, 2); snd_pcm_hw_params_set_channels(pcm, hw_params, 2);
snd_pcm_hw_params_get_rate_min(hw_params, &min, NULL); snd_pcm_hw_params_get_rate_min(hw_params, &min, nullptr);
snd_pcm_hw_params_get_rate_max(hw_params, &max, NULL); snd_pcm_hw_params_get_rate_max(hw_params, &max, nullptr);
printf(" --> Available rates: %d to %d\n", min, max); printf(" --> Available rates: %d to %d\n", min, max);
if (Settings.SoundPlaybackRate > max && Settings.SoundPlaybackRate < min) if (Settings.SoundPlaybackRate > max && Settings.SoundPlaybackRate < min)
{ {
printf(" Rate %d not available. Using %d instead.\n", Settings.SoundPlaybackRate, max); printf(" Rate %d not available. Using %d instead.\n", Settings.SoundPlaybackRate, max);
Settings.SoundPlaybackRate = max; Settings.SoundPlaybackRate = max;
} }
snd_pcm_hw_params_set_rate_near(pcm, hw_params, &Settings.SoundPlaybackRate, NULL); snd_pcm_hw_params_set_rate_near(pcm, hw_params, &Settings.SoundPlaybackRate, nullptr);
snd_pcm_hw_params_get_buffer_time_min(hw_params, &min, NULL); snd_pcm_hw_params_get_buffer_time_min(hw_params, &min, nullptr);
snd_pcm_hw_params_get_buffer_time_max(hw_params, &max, NULL); snd_pcm_hw_params_get_buffer_time_max(hw_params, &max, nullptr);
printf(" --> Available buffer sizes: %dms to %dms\n", min / 1000, max / 1000); printf(" --> Available buffer sizes: %dms to %dms\n", min / 1000, max / 1000);
if (buffer_size < min && buffer_size > max) if (buffer_size < min && buffer_size > max)
{ {
printf(" Buffer size %dms not available. Using %d instead.\n", buffer_size / 1000, (min + max) / 2000); printf(" Buffer size %dms not available. Using %d instead.\n", buffer_size / 1000, (min + max) / 2000);
buffer_size = (min + max) / 2; buffer_size = (min + max) / 2;
} }
snd_pcm_hw_params_set_buffer_time_near(pcm, hw_params, &buffer_size, NULL); snd_pcm_hw_params_set_buffer_time_near(pcm, hw_params, &buffer_size, nullptr);
snd_pcm_hw_params_get_periods_min(hw_params, &min, NULL); snd_pcm_hw_params_get_periods_min(hw_params, &min, nullptr);
snd_pcm_hw_params_get_periods_max(hw_params, &max, NULL); snd_pcm_hw_params_get_periods_max(hw_params, &max, nullptr);
printf(" --> Period ranges: %d to %d blocks\n", min, max); printf(" --> Period ranges: %d to %d blocks\n", min, max);
if (periods > max) if (periods > max)
{ {
periods = max; periods = max;
} }
snd_pcm_hw_params_set_periods_near(pcm, hw_params, &periods, NULL); snd_pcm_hw_params_set_periods_near(pcm, hw_params, &periods, nullptr);
if ((err = snd_pcm_hw_params(pcm, hw_params)) < 0) if ((err = snd_pcm_hw_params(pcm, hw_params)) < 0)
{ {
@ -144,14 +131,16 @@ bool S9xAlsaSoundDriver::open_device()
printf("OK\n"); printf("OK\n");
S9xSetSamplesAvailableCallback(alsa_samples_available, this); S9xSetSamplesAvailableCallback([](void *userdata) {
((decltype(this)) userdata)->samples_available();;
}, this);
return true; return true;
close_fail: close_fail:
snd_pcm_drain(pcm); snd_pcm_drain(pcm);
snd_pcm_close(pcm); snd_pcm_close(pcm);
pcm = NULL; pcm = nullptr;
fail: fail:
printf("Failed: %s\n", snd_strerror(err)); printf("Failed: %s\n", snd_strerror(err));
@ -162,7 +151,7 @@ fail:
void S9xAlsaSoundDriver::samples_available() void S9xAlsaSoundDriver::samples_available()
{ {
snd_pcm_sframes_t frames_written, frames; snd_pcm_sframes_t frames_written, frames;
int bytes; size_t bytes;
frames = snd_pcm_avail(pcm); frames = snd_pcm_avail(pcm);
@ -206,13 +195,10 @@ void S9xAlsaSoundDriver::samples_available()
if (bytes <= 0) if (bytes <= 0)
return; return;
if (sound_buffer_size < bytes || sound_buffer == NULL) if (sound_buffer.size() < bytes)
{ sound_buffer.resize(bytes);
sound_buffer = (uint8 *)realloc(sound_buffer, bytes);
sound_buffer_size = bytes;
}
S9xMixSamples(sound_buffer, frames * 2); S9xMixSamples(sound_buffer.data(), frames * 2);
frames_written = 0; frames_written = 0;
@ -221,8 +207,7 @@ void S9xAlsaSoundDriver::samples_available()
int result; int result;
result = snd_pcm_writei(pcm, result = snd_pcm_writei(pcm,
sound_buffer + &sound_buffer[snd_pcm_frames_to_bytes(pcm, frames_written)],
snd_pcm_frames_to_bytes(pcm, frames_written),
frames - frames_written); frames - frames_written);
if (result < 0) if (result < 0)

View File

@ -24,8 +24,7 @@ class S9xAlsaSoundDriver : public S9xSoundDriver
private: private:
snd_pcm_t *pcm; snd_pcm_t *pcm;
int sound_buffer_size; std::vector<uint8_t> sound_buffer;
uint8 *sound_buffer;
int output_buffer_size; int output_buffer_size;
}; };

View File

@ -13,26 +13,22 @@
#include <sys/ioctl.h> #include <sys/ioctl.h>
#include <sys/time.h> #include <sys/time.h>
static void pulse_samples_available(void *data)
{
((S9xPulseSoundDriver *)data)->samples_available();
}
S9xPulseSoundDriver::S9xPulseSoundDriver() S9xPulseSoundDriver::S9xPulseSoundDriver()
{ {
mainloop = NULL; init();
context = NULL;
stream = NULL;
buffer_size = 0;
} }
void S9xPulseSoundDriver::init() void S9xPulseSoundDriver::init()
{ {
mainloop = {};
context = {};
stream = {};
buffer_size = {};
} }
void S9xPulseSoundDriver::terminate() void S9xPulseSoundDriver::terminate()
{ {
S9xSetSamplesAvailableCallback(NULL, NULL); S9xSetSamplesAvailableCallback(nullptr, nullptr);
if (mainloop) if (mainloop)
pa_threaded_mainloop_stop(mainloop); pa_threaded_mainloop_stop(mainloop);
@ -110,15 +106,14 @@ static void stream_state_callback(pa_stream *p, void *userdata)
bool S9xPulseSoundDriver::open_device() bool S9xPulseSoundDriver::open_device()
{ {
int err = PA_ERR_UNKNOWN; init();
pa_sample_spec ss;
pa_buffer_attr buffer_attr;
const pa_buffer_attr *actual_buffer_attr;
pa_sample_spec ss;
ss.channels = 2; ss.channels = 2;
ss.format = PA_SAMPLE_S16NE; ss.format = PA_SAMPLE_S16NE;
ss.rate = Settings.SoundPlaybackRate; ss.rate = Settings.SoundPlaybackRate;
pa_buffer_attr buffer_attr;
buffer_attr.fragsize = -1; buffer_attr.fragsize = -1;
buffer_attr.tlength = pa_usec_to_bytes(gui_config->sound_buffer_size * 1000, &ss); buffer_attr.tlength = pa_usec_to_bytes(gui_config->sound_buffer_size * 1000, &ss);
buffer_attr.maxlength = buffer_attr.tlength * 2; buffer_attr.maxlength = buffer_attr.tlength * 2;
@ -130,93 +125,59 @@ bool S9xPulseSoundDriver::open_device()
printf(" --> (%dhz, 16-bit Stereo, %dms)...", printf(" --> (%dhz, 16-bit Stereo, %dms)...",
Settings.SoundPlaybackRate, Settings.SoundPlaybackRate,
gui_config->sound_buffer_size); gui_config->sound_buffer_size);
fflush(stdout);
int err = PA_ERR_UNKNOWN;
mainloop = pa_threaded_mainloop_new(); mainloop = pa_threaded_mainloop_new();
if (!mainloop)
{
fprintf(stderr, "Failed to create mainloop.\n");
goto error0;
}
context = pa_context_new(pa_threaded_mainloop_get_api(mainloop), "Snes9x"); context = pa_context_new(pa_threaded_mainloop_get_api(mainloop), "Snes9x");
if (!context)
goto error1;
pa_context_set_state_callback(context, context_state_cb, this); pa_context_set_state_callback(context, context_state_cb, this);
if ((err = pa_context_connect(context, NULL, PA_CONTEXT_NOFLAGS, NULL)) != 0) pa_context_connect(context, nullptr, PA_CONTEXT_NOFLAGS, nullptr);
goto error2;
lock(); lock();
pa_threaded_mainloop_start(mainloop);
if ((err = pa_threaded_mainloop_start(mainloop)) != 0)
goto error2;
wait(); wait();
if ((err = pa_context_get_state(context)) != PA_CONTEXT_READY) if ((err = pa_context_get_state(context)) != PA_CONTEXT_READY)
{ return false;
printf("Coundn't create context: State: %d\n", err);
goto error2;
}
stream = pa_stream_new(context, "Game", &ss, NULL); stream = pa_stream_new(context, "Game", &ss, nullptr);
if (!stream)
goto error2;
pa_stream_set_state_callback(stream, stream_state_callback, this); pa_stream_set_state_callback(stream, stream_state_callback, this);
if (pa_stream_connect_playback(stream, if (pa_stream_connect_playback(stream,
NULL, nullptr,
&buffer_attr, &buffer_attr,
PA_STREAM_ADJUST_LATENCY, PA_STREAM_ADJUST_LATENCY,
NULL, nullptr,
NULL) < 0) nullptr) < 0)
goto error3; {
return false;
}
wait(); wait();
if (pa_stream_get_state(stream) != PA_STREAM_READY) if (pa_stream_get_state(stream) != PA_STREAM_READY)
{ return false;
goto error3;
}
actual_buffer_attr = pa_stream_get_buffer_attr(stream); auto actual_buffer_attr = pa_stream_get_buffer_attr(stream);
unlock();
buffer_size = actual_buffer_attr->tlength; buffer_size = actual_buffer_attr->tlength;
printf("OK\n"); printf("OK\n");
S9xSetSamplesAvailableCallback(pulse_samples_available, this); S9xSetSamplesAvailableCallback([](void *userdata) {
((decltype(this)) userdata)->samples_available();;
}, this);
unlock();
return true; return true;
error3:
pa_stream_disconnect(stream);
pa_stream_unref(stream);
error2:
pa_context_disconnect(context);
pa_context_unref(context);
unlock();
error1:
pa_threaded_mainloop_free(mainloop);
error0:
printf("Failed: %s\n", pa_strerror(err));
return false;
} }
void S9xPulseSoundDriver::samples_available() void S9xPulseSoundDriver::samples_available()
{ {
size_t bytes;
int samples;
const pa_buffer_attr *buffer_attr;
void *output_buffer = NULL;
lock(); lock();
bytes = pa_stream_writable_size(stream); size_t bytes = pa_stream_writable_size(stream);
buffer_attr = pa_stream_get_buffer_attr(stream); auto buffer_attr = pa_stream_get_buffer_attr(stream);
unlock(); unlock();
buffer_size = buffer_attr->tlength; buffer_size = buffer_attr->tlength;
@ -226,22 +187,22 @@ void S9xPulseSoundDriver::samples_available()
S9xUpdateDynamicRate(bytes, buffer_size); S9xUpdateDynamicRate(bytes, buffer_size);
} }
samples = S9xGetSampleCount(); size_t samples = S9xGetSampleCount();
if (Settings.DynamicRateControl && !Settings.SoundSync) int frames_available = samples / 2;
int frames_writable = bytes / 4;
if (frames_writable < frames_available)
{ {
if ((int)bytes < (samples * 2)) if (Settings.DynamicRateControl && !Settings.SoundSync)
{ {
S9xClearSamples(); S9xClearSamples();
return; return;
} }
}
if (Settings.SoundSync && !Settings.TurboMode && !Settings.Mute) if (Settings.SoundSync && !Settings.TurboMode && !Settings.Mute)
{
if ((int)bytes < samples * 2)
{ {
int usec_to_sleep = ((samples >> 1) - (bytes >> 2)) * 10000 / int usec_to_sleep = (frames_available - frames_writable) * 10000 /
(Settings.SoundPlaybackRate / 100); (Settings.SoundPlaybackRate / 100);
usleep(usec_to_sleep > 0 ? usec_to_sleep : 0); usleep(usec_to_sleep > 0 ? usec_to_sleep : 0);
lock(); lock();
@ -250,16 +211,17 @@ void S9xPulseSoundDriver::samples_available()
} }
} }
bytes = MIN((int)bytes, samples * 2) & ~1; bytes = MIN(bytes, samples * 2) & ~1;
if (!bytes) if (!bytes)
return; return;
lock(); lock();
void *output_buffer;;
if (pa_stream_begin_write(stream, &output_buffer, &bytes) != 0) if (pa_stream_begin_write(stream, &output_buffer, &bytes) != 0)
{ {
pa_stream_flush(stream, NULL, NULL); pa_stream_flush(stream, nullptr, nullptr);
unlock(); unlock();
return; return;
} }
@ -270,8 +232,8 @@ void S9xPulseSoundDriver::samples_available()
return; return;
} }
S9xMixSamples((uint8 *)output_buffer, bytes >> 1); S9xMixSamples((uint8_t *)output_buffer, bytes >> 1);
pa_stream_write(stream, output_buffer, bytes, NULL, 0, PA_SEEK_RELATIVE); pa_stream_write(stream, output_buffer, bytes, nullptr, 0, PA_SEEK_RELATIVE);
unlock(); unlock();
} }

View File

@ -9,16 +9,6 @@
#include "apu/apu.h" #include "apu/apu.h"
#include "snes9x.h" #include "snes9x.h"
static void sdl_audio_callback(void *userdata, Uint8 *stream, int len)
{
((S9xSDLSoundDriver *)userdata)->mix((unsigned char *)stream, len);
}
static void c_samples_available(void *data)
{
((S9xSDLSoundDriver *)data)->samples_available();
}
void S9xSDLSoundDriver::samples_available() void S9xSDLSoundDriver::samples_available()
{ {
int snes_samples_available = S9xGetSampleCount(); int snes_samples_available = S9xGetSampleCount();
@ -89,7 +79,10 @@ bool S9xSDLSoundDriver::open_device()
audiospec.channels = 2; audiospec.channels = 2;
audiospec.format = AUDIO_S16SYS; audiospec.format = AUDIO_S16SYS;
audiospec.samples = (gui_config->sound_buffer_size * audiospec.freq / 1000) >> 2; audiospec.samples = (gui_config->sound_buffer_size * audiospec.freq / 1000) >> 2;
audiospec.callback = sdl_audio_callback; audiospec.callback = [](void *userdata, uint8_t *stream, int len) {
((S9xSDLSoundDriver *)userdata)->mix((unsigned char *)stream, len);
};
audiospec.userdata = this; audiospec.userdata = this;
printf("SDL sound driver initializing...\n"); printf("SDL sound driver initializing...\n");
@ -106,9 +99,10 @@ bool S9xSDLSoundDriver::open_device()
printf("OK\n"); printf("OK\n");
buffer.resize(gui_config->sound_buffer_size * audiospec.freq / 500); buffer.resize(gui_config->sound_buffer_size * audiospec.freq / 500);
buffer.time_ratio(1.0);
S9xSetSamplesAvailableCallback(c_samples_available, this); S9xSetSamplesAvailableCallback([](void *userdata) {
((decltype(this)) userdata)->samples_available();;
}, this);
return true; return true;
} }