From d12121cec9a987dc5f135d4fc29d42ff14bc52d9 Mon Sep 17 00:00:00 2001 From: Brandon Wright Date: Wed, 22 Nov 2017 18:14:49 -0600 Subject: [PATCH] Update PulseAudio driver to full API. --- gtk/src/gtk_sound_driver_pulse.cpp | 214 +++++++++++++++++++++++------ gtk/src/gtk_sound_driver_pulse.h | 12 +- 2 files changed, 183 insertions(+), 43 deletions(-) diff --git a/gtk/src/gtk_sound_driver_pulse.cpp b/gtk/src/gtk_sound_driver_pulse.cpp index 4331a945..ff0bfe89 100644 --- a/gtk/src/gtk_sound_driver_pulse.cpp +++ b/gtk/src/gtk_sound_driver_pulse.cpp @@ -13,8 +13,9 @@ pulse_samples_available (void *data) S9xPulseSoundDriver::S9xPulseSoundDriver (void) { - pulse = NULL; - buffer = NULL; + mainloop = NULL; + context = NULL; + stream = NULL; buffer_size = 0; return; @@ -29,19 +30,24 @@ S9xPulseSoundDriver::init (void) void S9xPulseSoundDriver::terminate (void) { - stop (); - S9xSetSamplesAvailableCallback (NULL, NULL); - if (pulse) + if (stream) { - pa_simple_free (pulse); + pa_stream_disconnect (stream); + pa_stream_unref (stream); } - if (buffer) + if (context) { - delete[] buffer; - buffer = NULL; + pa_context_disconnect (context); + pa_context_unref (context); + } + + if (mainloop) + { + pa_threaded_mainloop_stop (mainloop); + pa_threaded_mainloop_free (mainloop); } return; @@ -59,12 +65,73 @@ S9xPulseSoundDriver::stop (void) return; } +void +S9xPulseSoundDriver::lock (void) +{ + pa_threaded_mainloop_lock (mainloop); + + return; +} + +void +S9xPulseSoundDriver::unlock (void) +{ + pa_threaded_mainloop_unlock (mainloop); + + return; +} + +void +S9xPulseSoundDriver::wait (void) +{ + pa_threaded_mainloop_wait (mainloop); + + return; +} + +static void +context_state_cb (pa_context *c, void *userdata) +{ + S9xPulseSoundDriver *driver = (S9xPulseSoundDriver *) userdata; + int state; + + state = pa_context_get_state (c); + + if (state == PA_CONTEXT_READY || + state == PA_CONTEXT_FAILED || + state == PA_CONTEXT_TERMINATED) + { + pa_threaded_mainloop_signal (driver->mainloop, 0); + } + + return; +} + +static void +stream_state_callback (pa_stream *p, void *userdata) +{ + S9xPulseSoundDriver *driver = (S9xPulseSoundDriver *) userdata; + int state; + + state = pa_stream_get_state (p); + + if (state == PA_STREAM_READY || + state == PA_STREAM_FAILED || + state == PA_STREAM_TERMINATED) + { + pa_threaded_mainloop_signal (driver->mainloop, 0); + } + + return; +} + bool8 S9xPulseSoundDriver::open_device (void) { - int err; + int err = PA_ERR_UNKNOWN; pa_sample_spec ss; pa_buffer_attr buffer_attr; + const pa_buffer_attr *actual_buffer_attr; ss.channels = Settings.Stereo ? 2 : 1; ss.format = Settings.SixteenBitSound ? PA_SAMPLE_S16NE : PA_SAMPLE_U8; @@ -78,33 +145,87 @@ S9xPulseSoundDriver::open_device (void) printf ("PulseAudio sound driver initializing...\n"); - printf (" --> (%dhz, %s %s, %dms)...", + printf (" --> (%dhz, %s %s, %dms)...\n", Settings.SoundPlaybackRate, Settings.SixteenBitSound ? "16-bit" : "8-bit", Settings.Stereo ? "Stereo" : "Mono", gui_config->sound_buffer_size); + fflush (stdout); - pulse = pa_simple_new (NULL, - "Snes9x", - PA_STREAM_PLAYBACK, - NULL, - "Game", - &ss, - NULL, - &buffer_attr, - &err); - - if (!pulse) + mainloop = pa_threaded_mainloop_new (); + if (!mainloop) { - printf ("Failed: %s\n", pa_strerror (err)); - return FALSE; + fprintf (stderr, "Failed to create mainloop.\n"); + goto error0; } + 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); + if ((err = pa_context_connect (context, NULL, PA_CONTEXT_NOFLAGS, NULL)) != 0) + goto error2; + + lock (); + + if ((err = pa_threaded_mainloop_start (mainloop)) != 0) + goto error2; + wait (); + + if ((err = pa_context_get_state (context)) != PA_CONTEXT_READY) + { + printf ("Coundn't create context: State: %d\n", err); + goto error2; + } + + stream = pa_stream_new (context, "Game", &ss, NULL); + + if (!stream) + goto error2; + + pa_stream_set_state_callback (stream, stream_state_callback, this); + + if (pa_stream_connect_playback (stream, + NULL, + &buffer_attr, + PA_STREAM_ADJUST_LATENCY, + NULL, + NULL) < 0) + goto error3; + wait (); + + if (pa_stream_get_state (stream) != PA_STREAM_READY) + { + goto error3; + } + + actual_buffer_attr = pa_stream_get_buffer_attr (stream); + + buffer_size = actual_buffer_attr->tlength; + printf ("size: %d\n", buffer_size); + printf ("OK\n"); S9xSetSamplesAvailableCallback (pulse_samples_available, this); + unlock (); + 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 @@ -116,30 +237,45 @@ S9xPulseSoundDriver::mix (void) void S9xPulseSoundDriver::samples_available (void) { - int bytes, err; + size_t bytes; + int samples; + const pa_buffer_attr *buffer_attr; + void *output_buffer = NULL; + + lock (); + bytes = pa_stream_writable_size (stream); + buffer_attr = pa_stream_get_buffer_attr (stream); + unlock (); + + buffer_size = buffer_attr->tlength; + + if (Settings.DynamicRateControl) + { + S9xUpdateDynamicRate (bytes, buffer_size); + } S9xFinalizeSamples (); - bytes = (S9xGetSampleCount () << (Settings.SixteenBitSound ? 1 : 0)); + samples = S9xGetSampleCount (); - if (bytes <= 128) - return; - - if (buffer_size < bytes || buffer == NULL) + if (Settings.DynamicRateControl) { - delete[] buffer; - buffer = new uint8[bytes]; - buffer_size = bytes; + if ((int) bytes < (samples << (Settings.SixteenBitSound ? 1 : 0))) + { + S9xClearSamples (); + return; + } } - S9xMixSamples (buffer, bytes >> (Settings.SixteenBitSound ? 1 : 0)); + bytes = MIN (bytes, (samples << (Settings.SixteenBitSound ? 1 : 0))); - /* PulseAudio-simple has no write indicator, so we don't know when the - buffer is full. So we just drop the audio when we're in Turbo Mode. */ - if (Settings.TurboMode) - return; + lock (); - pa_simple_write (pulse, buffer, bytes, &err); + pa_stream_begin_write (stream, &output_buffer, &bytes); + S9xMixSamples ((uint8 *) output_buffer, bytes >> (Settings.SixteenBitSound ? 1 : 0)); + pa_stream_write (stream, output_buffer, bytes, NULL, 0, PA_SEEK_RELATIVE); + + unlock (); return; } diff --git a/gtk/src/gtk_sound_driver_pulse.h b/gtk/src/gtk_sound_driver_pulse.h index 878716cf..417a4872 100644 --- a/gtk/src/gtk_sound_driver_pulse.h +++ b/gtk/src/gtk_sound_driver_pulse.h @@ -3,8 +3,7 @@ #include "gtk_sound.h" #include "gtk_sound_driver.h" -#include "pulse/simple.h" -#include "pulse/error.h" +#include "pulse/pulseaudio.h" class S9xPulseSoundDriver : public S9xSoundDriver { @@ -17,10 +16,15 @@ class S9xPulseSoundDriver : public S9xSoundDriver void stop (void); void mix (void); void samples_available (void); + void lock (void); + void unlock (void); + void wait (void); + + pa_threaded_mainloop *mainloop; + pa_context *context; + pa_stream *stream; private: - pa_simple *pulse; - uint8 *buffer; int buffer_size; };