From 82eaec96dd7401761f97f05bf6bbbe8be31c8a3b Mon Sep 17 00:00:00 2001 From: Maximilian Mader Date: Tue, 28 Apr 2020 18:36:47 +0200 Subject: [PATCH] [GTK3] Port experimental PortAudio branch --- Makefile | 28 +++++++++++-- gtk3/audio/audio.h | 16 +++++++ gtk3/audio/portaudio.c | 79 +++++++++++++++++++++++++++++++++++ gtk3/audio/sdl.c | 95 ++++++++++++++++++++++++++++++++++++++++++ gtk3/main.c | 83 ++++++++---------------------------- 5 files changed, 232 insertions(+), 69 deletions(-) create mode 100644 gtk3/audio/audio.h create mode 100644 gtk3/audio/portaudio.c create mode 100644 gtk3/audio/sdl.c diff --git a/Makefile b/Makefile index c45bb0a..b1d2eda 100644 --- a/Makefile +++ b/Makefile @@ -38,6 +38,12 @@ VERSION := 0.12.3 export VERSION CONF ?= debug +ifeq ($(PLATFORM),windows32) +SDL_AUDIO_DRIVER ?= sdl +else +SDL_AUDIO_DRIVER ?= portaudio +endif + BIN := build/bin OBJ := build/obj BOOTROMS_DIR ?= $(BIN)/BootROMs @@ -51,7 +57,7 @@ endif # Use clang if it's available. ifeq ($(origin CC),default) ifneq (, $(shell which clang)) -CC := clang +CC := clang endif endif @@ -125,6 +131,22 @@ LDFLAGS += -lc -lm -ldl GTK3_LDFLAGS += -Wl -rdynamic endif +ifeq ($(SDL_AUDIO_DRIVER),portaudio) +ifeq (,$(PKG_CONFIG)) +SDL_CFLAGS += -I/usr/local/include +SDL_LDFLAGS += -lportaudio + +GTK3_CFLAGS += -I/usr/local/include +GTK3_LDFLAGS += -lportaudio +else +SDL_CFLAGS += $(shell $(PKG_CONFIG) --cflags portaudio-2.0) +SDL_LDFLAGS += $(shell $(PKG_CONFIG) --libs portaudio-2.0) + +GTK3_CFLAGS += $(shell $(PKG_CONFIG) --cflags portaudio-2.0) +GTK3_LDFLAGS += $(shell $(PKG_CONFIG) --libs portaudio-2.0) +endif +endif + ifeq ($(PLATFORM),Darwin) SYSROOT := $(shell xcodebuild -sdk macosx -version Path 2> $(NULL)) CFLAGS += -F/Library/Frameworks -mmacosx-version-min=10.9 @@ -178,8 +200,8 @@ all: cocoa sdl tester libretro # Get a list of our source files and their respective object file targets CORE_SOURCES := $(shell ls Core/*.c) -SDL_SOURCES := $(shell ls SDL/*.c) $(OPEN_DIALOG) -GTK3_SOURCES := $(shell ls gtk3/*.c) gtk3/sameboy-gtk3-resources.c +SDL_SOURCES := $(shell ls SDL/*.c) $(OPEN_DIALOG) SDL/audio/$(SDL_AUDIO_DRIVER).c +GTK3_SOURCES := $(shell ls gtk3/*.c) gtk3/sameboy-gtk3-resources.c gtk3/audio/$(SDL_AUDIO_DRIVER).c TESTER_SOURCES := $(shell ls Tester/*.c) ifeq ($(PLATFORM),Darwin) diff --git a/gtk3/audio/audio.h b/gtk3/audio/audio.h new file mode 100644 index 0000000..acaa011 --- /dev/null +++ b/gtk3/audio/audio.h @@ -0,0 +1,16 @@ +#ifndef sdl_audio_h +#define sdl_audio_h + +#include +#include +#include + +bool GB_audio_is_playing(void); +void GB_audio_set_paused(bool paused); +void GB_audio_clear_queue(void); +unsigned GB_audio_get_frequency(void); +size_t GB_audio_get_queue_length(void); +void GB_audio_queue_sample(GB_sample_t *sample); +void GB_audio_init(void); + +#endif /* sdl_audio_h */ diff --git a/gtk3/audio/portaudio.c b/gtk3/audio/portaudio.c new file mode 100644 index 0000000..5e524ae --- /dev/null +++ b/gtk3/audio/portaudio.c @@ -0,0 +1,79 @@ +#include "audio.h" +#include + + +#define AUDIO_FREQUENCY 96000 +static PaStream *stream; +static size_t buffer_position = 0; +static GB_sample_t buffer[8192]; + + +bool GB_audio_is_playing(void) +{ + return stream && Pa_IsStreamActive(stream); +} + +void GB_audio_set_paused(bool paused) +{ + if (!stream) return; + + if (paused) { + Pa_StopStream(stream); + } + else { + Pa_StartStream(stream); + } +} + +void GB_audio_clear_queue(void) +{ + buffer_position = 0; +} + +unsigned GB_audio_get_frequency(void) +{ + return stream? Pa_GetStreamInfo(stream)->sampleRate : 0; +} + +size_t GB_audio_get_queue_length(void) +{ + return buffer_position; +} + +#include +void GB_audio_queue_sample(GB_sample_t *sample) +{ + if (!stream) return; + + if (buffer_position < sizeof(buffer) / sizeof(buffer[0])) { + buffer[buffer_position++] = *sample; + } + else { + printf("Overflow\n"); + } + + size_t write_legnth = Pa_GetStreamWriteAvailable(stream); + if (write_legnth > buffer_position) { + return; + } + if (write_legnth) { + printf("Pushing %zu out of %zu (Distance: %zu)\n", write_legnth, buffer_position, buffer_position - write_legnth); + Pa_WriteStream(stream, buffer, write_legnth); + memmove(buffer, buffer + write_legnth, sizeof(buffer[0]) * (buffer_position - write_legnth)); + buffer_position -= write_legnth; + } +} + +void GB_audio_init(void) +{ + Pa_Initialize(); + Pa_OpenDefaultStream(&stream, + 0, + 2, + paInt16, + AUDIO_FREQUENCY, + paFramesPerBufferUnspecified, + NULL, + NULL); + +} diff --git a/gtk3/audio/sdl.c b/gtk3/audio/sdl.c new file mode 100644 index 0000000..361a4f8 --- /dev/null +++ b/gtk3/audio/sdl.c @@ -0,0 +1,95 @@ +#include "audio.h" +#include + +#ifndef _WIN32 +#define AUDIO_FREQUENCY 96000 +#include +#else +#include +/* Windows (well, at least my VM) can't handle 96KHz sound well :( */ + +/* felsqualle says: For SDL 2.0.6+ using the WASAPI driver, the highest freq. + we can get is 48000. 96000 also works, but always has some faint crackling in + the audio, no matter how high or low I set the buffer length... + Not quite satisfied with that solution, because acc. to SDL2 docs, + 96k + WASAPI *should* work. */ + +#define AUDIO_FREQUENCY 48000 +#endif + +/* Compatibility with older SDL versions */ +#ifndef SDL_AUDIO_ALLOW_SAMPLES_CHANGE +#define SDL_AUDIO_ALLOW_SAMPLES_CHANGE 0 +#endif + +#undef AUDIO_FREQUENCY +#define AUDIO_FREQUENCY 96000 + +static SDL_AudioDeviceID device_id; +static SDL_AudioSpec want_aspec, have_aspec; + +bool GB_audio_is_playing(void) +{ + return SDL_GetAudioDeviceStatus(device_id) == SDL_AUDIO_PLAYING; +} + +void GB_audio_set_paused(bool paused) +{ + GB_audio_clear_queue(); + SDL_PauseAudioDevice(device_id, paused); +} + +void GB_audio_clear_queue(void) +{ + SDL_ClearQueuedAudio(device_id); +} + +unsigned GB_audio_get_frequency(void) +{ + return have_aspec.freq; +} + +size_t GB_audio_get_queue_length(void) +{ + return SDL_GetQueuedAudioSize(device_id); +} + +void GB_audio_queue_sample(GB_sample_t *sample) +{ + SDL_QueueAudio(device_id, sample, sizeof(*sample)); +} + +void GB_audio_init(void) +{ + if (SDL_InitSubSystem(SDL_INIT_AUDIO) < 0) { + fprintf(stderr, "Failed to initialize audio: %s", SDL_GetError()); + return; + } + + /* Configure Audio */ + memset(&want_aspec, 0, sizeof(want_aspec)); + want_aspec.freq = AUDIO_FREQUENCY; + want_aspec.format = AUDIO_S16SYS; + want_aspec.channels = 2; + want_aspec.samples = 512; + + SDL_version _sdl_version; + SDL_GetVersion(&_sdl_version); + unsigned sdl_version = _sdl_version.major * 1000 + _sdl_version.minor * 100 + _sdl_version.patch; + +#ifndef _WIN32 + /* SDL 2.0.5 on macOS and Linux introduced a bug where certain combinations of buffer lengths and frequencies + fail to produce audio correctly. */ + if (sdl_version >= 2005) { + want_aspec.samples = 2048; + } +#else + if (sdl_version < 2006) { + /* Since WASAPI audio was introduced in SDL 2.0.6, we have to lower the audio frequency + to 44100 because otherwise we would get garbled audio output.*/ + want_aspec.freq = 44100; + } +#endif + + device_id = SDL_OpenAudioDevice(0, 0, &want_aspec, &have_aspec, SDL_AUDIO_ALLOW_FREQUENCY_CHANGE | SDL_AUDIO_ALLOW_SAMPLES_CHANGE); +} diff --git a/gtk3/main.c b/gtk3/main.c index 8e4619f..994cb1e 100644 --- a/gtk3/main.c +++ b/gtk3/main.c @@ -1,4 +1,5 @@ #include "main.h" +#include "audio/audio.h" #ifndef _WIN32 #define DEFAULT_AUDIO_SAMPLE_RATE 96000 @@ -94,7 +95,7 @@ static const GActionEntry file_entries[] = { }; static const GActionEntry edit_entries[] = { - + }; static const GActionEntry emulation_entries[] = { @@ -324,51 +325,6 @@ static gboolean init_controllers() { return true; } -static gboolean init_audio() { - bool audio_playing = SDL_GetAudioDeviceStatus(device_id) == SDL_AUDIO_PLAYING; - SDL_PauseAudioDevice(device_id, 1); - SDL_ClearQueuedAudio(device_id); - SDL_QuitSubSystem(SDL_INIT_AUDIO); - - if (SDL_InitSubSystem(SDL_INIT_AUDIO) < 0) { - g_warning("Failed to initialize audio: %s", SDL_GetError()); - return false; - } - - memset(&want_aspec, 0, sizeof(want_aspec)); - want_aspec.freq = gui_data.sample_rate; - want_aspec.format = AUDIO_S16SYS; - want_aspec.channels = 2; - want_aspec.samples = 512; - - SDL_version _sdl_version; - SDL_GetVersion(&_sdl_version); - unsigned sdl_version = _sdl_version.major * 1000 + _sdl_version.minor * 100 + _sdl_version.patch; - -#ifndef _WIN32 - /* SDL 2.0.5 on macOS and Linux introduced a bug where certain combinations of buffer lengths and frequencies - fail to produce audio correctly. */ - if (sdl_version >= 2005) { - want_aspec.samples = 2048; - } -#else - if (sdl_version < 2006) { - /* Since WASAPI audio was introduced in SDL 2.0.6, we have to lower the audio frequency - to 44100 because otherwise we would get garbled audio output.*/ - want_aspec.freq = 44100; - } -#endif - - device_id = SDL_OpenAudioDevice(0, 0, &want_aspec, &have_aspec, SDL_AUDIO_ALLOW_FREQUENCY_CHANGE | SDL_AUDIO_ALLOW_SAMPLES_CHANGE); - - g_debug("Requested Sample Rate: %d Hz\nUsed Sample Rate: %d Hz", want_aspec.freq, have_aspec.freq); - - SDL_PauseAudioDevice(device_id, audio_playing? 0 : 1); - GB_set_sample_rate(&gb, have_aspec.freq); - - return true; -} - static GB_model_t get_model() { if (gui_data.cli_options.model != -1) { return gui_data.cli_options.model; @@ -395,19 +351,21 @@ static void gb_audio_callback(GB_gameboy_t *gb, GB_sample_t *sample) { if (turbo_down) { static unsigned skip = 0; skip++; - if (skip == have_aspec.freq / 8) { + + if (skip == GB_audio_get_frequency() / 8) { skip = 0; } - if (skip > have_aspec.freq / 16) { + + if (skip > GB_audio_get_frequency() / 16) { return; } } - if (SDL_GetQueuedAudioSize(device_id) / sizeof(*sample) > have_aspec.freq / 4) { + if (GB_audio_get_queue_length() / sizeof(*sample) > GB_audio_get_frequency() / 4) { return; } - SDL_QueueAudio(device_id, sample, sizeof(*sample)); + GB_audio_queue_sample(sample); } // Console TODO: @@ -963,7 +921,7 @@ static void activate(GApplication *app, gpointer gui_data_gptr) { // initialize SameBoy core init(gui_data); - init_audio(); + GB_audio_init(); init_controllers(); connect_signal_handlers(app); @@ -1158,7 +1116,7 @@ static void activate_open(GSimpleAction *action, GVariant *parameter, gpointer a static void activate_close(GSimpleAction *action, GVariant *parameter, gpointer app) { stop(&gui_data); GB_free(&gb); - + // Clear the screen as side effect update_window_geometry(); gtk_widget_queue_draw(fallback_canvas ? GTK_WIDGET(fallback_canvas) : GTK_WIDGET(gl_area)); @@ -1167,7 +1125,7 @@ static void activate_close(GSimpleAction *action, GVariant *parameter, gpointer g_mutex_lock(&tileset_buffer_mutex); memset(tileset_buffer, 0, sizeof tileset_buffer); g_mutex_unlock(&tileset_buffer_mutex); - + g_mutex_lock(&tilemap_buffer_mutex); memset(tilemap_buffer, 0, sizeof tilemap_buffer); g_mutex_unlock(&tilemap_buffer_mutex); @@ -1257,15 +1215,7 @@ static void on_model_changed(GSimpleAction *action, GVariant *value, gpointer us } static void on_mute_changed(GSimpleAction *action, GVariant *value, gpointer user_data) { - gboolean do_mute = g_variant_get_boolean(value); - - if (do_mute) { - SDL_PauseAudioDevice(device_id, 1); - } - else { - SDL_ClearQueuedAudio(device_id); - SDL_PauseAudioDevice(device_id, 0); - } + GB_audio_set_paused(g_variant_get_boolean(value)); g_simple_action_set_state(action, value); } @@ -1711,7 +1661,9 @@ G_MODULE_EXPORT void on_sample_rate_changed(GtkWidget *w, gpointer user_data_gpt gui_data.sample_rate = config.sample_rate; } - init_audio(); + GB_audio_set_paused(true); + GB_audio_init(); + GB_audio_set_paused(false); } G_MODULE_EXPORT void on_sgb_model_changed(GtkWidget *w, gpointer user_data_gptr) { @@ -2280,7 +2232,7 @@ static void load_boot_rom(GB_gameboy_t *gb, GB_boot_rom_t type) { static void stop(GuiData *gui_data) { if (!running) return; - SDL_PauseAudioDevice(device_id, 1); + GB_audio_set_paused(true); GB_debugger_set_disabled(&gb, true); if (GB_debugger_is_stopped(&gb)) { @@ -2341,8 +2293,7 @@ static void start(GuiData *gui_data) { running = true; gui_data->stopped = false; - SDL_ClearQueuedAudio(device_id); - SDL_PauseAudioDevice(device_id, 0); + GB_audio_set_paused(false); /* Run emulation */ while (running) {