[GTK3] Port experimental PortAudio branch

This commit is contained in:
Maximilian Mader 2020-04-28 18:36:47 +02:00
parent 434c39767e
commit 82eaec96dd
Signed by: Max
GPG Key ID: F71D56A3151C4FB3
5 changed files with 232 additions and 69 deletions

View File

@ -38,6 +38,12 @@ VERSION := 0.12.3
export VERSION export VERSION
CONF ?= debug CONF ?= debug
ifeq ($(PLATFORM),windows32)
SDL_AUDIO_DRIVER ?= sdl
else
SDL_AUDIO_DRIVER ?= portaudio
endif
BIN := build/bin BIN := build/bin
OBJ := build/obj OBJ := build/obj
BOOTROMS_DIR ?= $(BIN)/BootROMs BOOTROMS_DIR ?= $(BIN)/BootROMs
@ -51,7 +57,7 @@ endif
# Use clang if it's available. # Use clang if it's available.
ifeq ($(origin CC),default) ifeq ($(origin CC),default)
ifneq (, $(shell which clang)) ifneq (, $(shell which clang))
CC := clang CC := clang
endif endif
endif endif
@ -125,6 +131,22 @@ LDFLAGS += -lc -lm -ldl
GTK3_LDFLAGS += -Wl -rdynamic GTK3_LDFLAGS += -Wl -rdynamic
endif 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) ifeq ($(PLATFORM),Darwin)
SYSROOT := $(shell xcodebuild -sdk macosx -version Path 2> $(NULL)) SYSROOT := $(shell xcodebuild -sdk macosx -version Path 2> $(NULL))
CFLAGS += -F/Library/Frameworks -mmacosx-version-min=10.9 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 # Get a list of our source files and their respective object file targets
CORE_SOURCES := $(shell ls Core/*.c) CORE_SOURCES := $(shell ls Core/*.c)
SDL_SOURCES := $(shell ls SDL/*.c) $(OPEN_DIALOG) 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_SOURCES := $(shell ls gtk3/*.c) gtk3/sameboy-gtk3-resources.c gtk3/audio/$(SDL_AUDIO_DRIVER).c
TESTER_SOURCES := $(shell ls Tester/*.c) TESTER_SOURCES := $(shell ls Tester/*.c)
ifeq ($(PLATFORM),Darwin) ifeq ($(PLATFORM),Darwin)

16
gtk3/audio/audio.h Normal file
View File

@ -0,0 +1,16 @@
#ifndef sdl_audio_h
#define sdl_audio_h
#include <stdbool.h>
#include <stddef.h>
#include <Core/gb.h>
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 */

79
gtk3/audio/portaudio.c Normal file
View File

@ -0,0 +1,79 @@
#include "audio.h"
#include <portaudio.h>
#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 <stdio.h>
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);
}

95
gtk3/audio/sdl.c Normal file
View File

@ -0,0 +1,95 @@
#include "audio.h"
#include <SDL.h>
#ifndef _WIN32
#define AUDIO_FREQUENCY 96000
#include <unistd.h>
#else
#include <Windows.h>
/* 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);
}

View File

@ -1,4 +1,5 @@
#include "main.h" #include "main.h"
#include "audio/audio.h"
#ifndef _WIN32 #ifndef _WIN32
#define DEFAULT_AUDIO_SAMPLE_RATE 96000 #define DEFAULT_AUDIO_SAMPLE_RATE 96000
@ -94,7 +95,7 @@ static const GActionEntry file_entries[] = {
}; };
static const GActionEntry edit_entries[] = { static const GActionEntry edit_entries[] = {
}; };
static const GActionEntry emulation_entries[] = { static const GActionEntry emulation_entries[] = {
@ -324,51 +325,6 @@ static gboolean init_controllers() {
return true; 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() { static GB_model_t get_model() {
if (gui_data.cli_options.model != -1) { if (gui_data.cli_options.model != -1) {
return gui_data.cli_options.model; 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) { if (turbo_down) {
static unsigned skip = 0; static unsigned skip = 0;
skip++; skip++;
if (skip == have_aspec.freq / 8) {
if (skip == GB_audio_get_frequency() / 8) {
skip = 0; skip = 0;
} }
if (skip > have_aspec.freq / 16) {
if (skip > GB_audio_get_frequency() / 16) {
return; 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; return;
} }
SDL_QueueAudio(device_id, sample, sizeof(*sample)); GB_audio_queue_sample(sample);
} }
// Console TODO: // Console TODO:
@ -963,7 +921,7 @@ static void activate(GApplication *app, gpointer gui_data_gptr) {
// initialize SameBoy core // initialize SameBoy core
init(gui_data); init(gui_data);
init_audio(); GB_audio_init();
init_controllers(); init_controllers();
connect_signal_handlers(app); 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) { static void activate_close(GSimpleAction *action, GVariant *parameter, gpointer app) {
stop(&gui_data); stop(&gui_data);
GB_free(&gb); GB_free(&gb);
// Clear the screen as side effect // Clear the screen as side effect
update_window_geometry(); update_window_geometry();
gtk_widget_queue_draw(fallback_canvas ? GTK_WIDGET(fallback_canvas) : GTK_WIDGET(gl_area)); 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); g_mutex_lock(&tileset_buffer_mutex);
memset(tileset_buffer, 0, sizeof tileset_buffer); memset(tileset_buffer, 0, sizeof tileset_buffer);
g_mutex_unlock(&tileset_buffer_mutex); g_mutex_unlock(&tileset_buffer_mutex);
g_mutex_lock(&tilemap_buffer_mutex); g_mutex_lock(&tilemap_buffer_mutex);
memset(tilemap_buffer, 0, sizeof tilemap_buffer); memset(tilemap_buffer, 0, sizeof tilemap_buffer);
g_mutex_unlock(&tilemap_buffer_mutex); 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) { static void on_mute_changed(GSimpleAction *action, GVariant *value, gpointer user_data) {
gboolean do_mute = g_variant_get_boolean(value); GB_audio_set_paused(g_variant_get_boolean(value));
if (do_mute) {
SDL_PauseAudioDevice(device_id, 1);
}
else {
SDL_ClearQueuedAudio(device_id);
SDL_PauseAudioDevice(device_id, 0);
}
g_simple_action_set_state(action, 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; 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) { 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) { static void stop(GuiData *gui_data) {
if (!running) return; if (!running) return;
SDL_PauseAudioDevice(device_id, 1); GB_audio_set_paused(true);
GB_debugger_set_disabled(&gb, true); GB_debugger_set_disabled(&gb, true);
if (GB_debugger_is_stopped(&gb)) { if (GB_debugger_is_stopped(&gb)) {
@ -2341,8 +2293,7 @@ static void start(GuiData *gui_data) {
running = true; running = true;
gui_data->stopped = false; gui_data->stopped = false;
SDL_ClearQueuedAudio(device_id); GB_audio_set_paused(false);
SDL_PauseAudioDevice(device_id, 0);
/* Run emulation */ /* Run emulation */
while (running) { while (running) {