From 9a765820ccb56c84b1454ca39b27b1642edf901e Mon Sep 17 00:00:00 2001 From: Lior Halphon Date: Fri, 24 Jun 2022 14:18:53 +0300 Subject: [PATCH] Runtime audio driver fallback --- Makefile | 6 ++-- SDL/audio.c | 71 +++++++++++++++++++++++++++++++++++++++++++ SDL/audio/audio.h | 27 +++++++++++++++- SDL/audio/sdl.c | 28 +++++++++-------- SDL/audio/xaudio2.c | 38 ++++++++++++----------- SDL/audio/xaudio2_7.c | 38 ++++++++++++----------- SDL/gui.c | 27 ++++++++++------ 7 files changed, 176 insertions(+), 59 deletions(-) create mode 100644 SDL/audio.c diff --git a/Makefile b/Makefile index 4ea66fe..e566773 100644 --- a/Makefile +++ b/Makefile @@ -17,11 +17,11 @@ ifeq ($(PLATFORM),windows32) _ := $(shell chcp 65001) EXESUFFIX:=.exe NATIVE_CC = clang -IWindows -Wno-deprecated-declarations --target=i386-pc-windows -SDL_AUDIO_DRIVER ?= xaudio2 +SDL_AUDIO_DRIVERS ?= xaudio2 xaudio2_7 sdl else EXESUFFIX:= NATIVE_CC := cc -SDL_AUDIO_DRIVER ?= sdl +SDL_AUDIO_DRIVERS ?= sdl endif PB12_COMPRESS := build/pb12$(EXESUFFIX) @@ -212,7 +212,7 @@ 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) SDL/audio/$(SDL_AUDIO_DRIVER).c +SDL_SOURCES := $(shell ls SDL/*.c) $(OPEN_DIALOG) $(patsubst %,SDL/audio/%.c,$(SDL_AUDIO_DRIVERS)) TESTER_SOURCES := $(shell ls Tester/*.c) ifeq ($(PLATFORM),Darwin) diff --git a/SDL/audio.c b/SDL/audio.c new file mode 100644 index 0000000..1627189 --- /dev/null +++ b/SDL/audio.c @@ -0,0 +1,71 @@ +#include +#include +#include +#include "audio/audio.h" + +#define unlikely(x) __builtin_expect((bool)(x), 0) + +static const GB_audio_driver_t *driver = NULL; + +bool GB_audio_init(void) +{ + const GB_audio_driver_t *drivers[] = { +#ifdef _WIN32 + GB_AUDIO_DRIVER_REF(XAudio2), + GB_AUDIO_DRIVER_REF(XAudio2_7), +#endif + GB_AUDIO_DRIVER_REF(SDL), + }; + + for (unsigned i = 0; i < sizeof(drivers) / sizeof(drivers[0]); i++) { + driver = drivers[i]; + if (driver->audio_init()) { + return true; + } + } + + driver = NULL; + return false; +} + +bool GB_audio_is_playing(void) +{ + if (unlikely(!driver)) return false; + return driver->audio_is_playing(); +} + +void GB_audio_set_paused(bool paused) +{ + if (unlikely(!driver)) return; + return driver->audio_set_paused(paused); +} + +void GB_audio_clear_queue(void) +{ + if (unlikely(!driver)) return; + return driver->audio_clear_queue(); +} + +unsigned GB_audio_get_frequency(void) +{ + if (unlikely(!driver)) return 0; + return driver->audio_get_frequency(); +} + +size_t GB_audio_get_queue_length(void) +{ + if (unlikely(!driver)) return 0; + return driver->audio_get_queue_length(); +} + +void GB_audio_queue_sample(GB_sample_t *sample) +{ + if (unlikely(!driver)) return; + return driver->audio_queue_sample(sample); +} + +const char *GB_audio_driver_name(void) +{ + if (unlikely(!driver)) return "None"; + return driver->name; +} diff --git a/SDL/audio/audio.h b/SDL/audio/audio.h index acaa011..d743c32 100644 --- a/SDL/audio/audio.h +++ b/SDL/audio/audio.h @@ -11,6 +11,31 @@ 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); +bool GB_audio_init(void); +const char *GB_audio_driver_name(void); + +typedef struct { + typeof(GB_audio_is_playing) *audio_is_playing; + typeof(GB_audio_set_paused) *audio_set_paused; + typeof(GB_audio_clear_queue) *audio_clear_queue; + typeof(GB_audio_get_frequency) *audio_get_frequency; + typeof(GB_audio_get_queue_length) *audio_get_queue_length; + typeof(GB_audio_queue_sample) *audio_queue_sample; + typeof(GB_audio_init) *audio_init; + const char *name; +} GB_audio_driver_t; + +#define GB_AUDIO_DRIVER(_name) const GB_audio_driver_t _name##driver = { \ + .audio_is_playing = _audio_is_playing, \ + .audio_set_paused = _audio_set_paused, \ + .audio_clear_queue = _audio_clear_queue, \ + .audio_get_frequency = _audio_get_frequency, \ + .audio_get_queue_length = _audio_get_queue_length, \ + .audio_queue_sample = _audio_queue_sample, \ + .audio_init = _audio_init, \ + .name = #_name, \ +} + +#define GB_AUDIO_DRIVER_REF(name) ({extern const GB_audio_driver_t name##driver; &name##driver;}) #endif /* sdl_audio_h */ diff --git a/SDL/audio/sdl.c b/SDL/audio/sdl.c index 12ee69a..6b987f1 100644 --- a/SDL/audio/sdl.c +++ b/SDL/audio/sdl.c @@ -29,33 +29,33 @@ static SDL_AudioSpec want_aspec, have_aspec; static unsigned buffer_pos = 0; static GB_sample_t audio_buffer[AUDIO_BUFFER_SIZE]; -bool GB_audio_is_playing(void) +static bool _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) +static void _audio_clear_queue(void) { SDL_ClearQueuedAudio(device_id); } -unsigned GB_audio_get_frequency(void) +static void _audio_set_paused(bool paused) +{ + _audio_clear_queue(); + SDL_PauseAudioDevice(device_id, paused); +} + +static unsigned _audio_get_frequency(void) { return have_aspec.freq; } -size_t GB_audio_get_queue_length(void) +static size_t _audio_get_queue_length(void) { return SDL_GetQueuedAudioSize(device_id); } -void GB_audio_queue_sample(GB_sample_t *sample) +static void _audio_queue_sample(GB_sample_t *sample) { audio_buffer[buffer_pos++] = *sample; @@ -65,7 +65,7 @@ void GB_audio_queue_sample(GB_sample_t *sample) } } -void GB_audio_init(void) +static bool _audio_init(void) { /* Configure Audio */ memset(&want_aspec, 0, sizeof(want_aspec)); @@ -93,4 +93,8 @@ void GB_audio_init(void) #endif device_id = SDL_OpenAudioDevice(0, 0, &want_aspec, &have_aspec, SDL_AUDIO_ALLOW_FREQUENCY_CHANGE | SDL_AUDIO_ALLOW_SAMPLES_CHANGE); + + return true; } + +GB_AUDIO_DRIVER(SDL); diff --git a/SDL/audio/xaudio2.c b/SDL/audio/xaudio2.c index 3eab780..f738a4e 100644 --- a/SDL/audio/xaudio2.c +++ b/SDL/audio/xaudio2.c @@ -22,17 +22,23 @@ static const WAVEFORMATEX wave_format = { .cbSize = 0 }; -bool GB_audio_is_playing(void) +static bool _audio_is_playing(void) { return playing; } -void GB_audio_set_paused(bool paused) +static void _audio_clear_queue(void) +{ + pos = 0; + IXAudio2SourceVoice_FlushSourceBuffers(source_voice); +} + +static void _audio_set_paused(bool paused) { if (paused) { playing = false; IXAudio2SourceVoice_Stop(source_voice, 0, XAUDIO2_COMMIT_NOW); - GB_audio_clear_queue(); + _audio_clear_queue(); } else { playing = true; @@ -41,18 +47,12 @@ void GB_audio_set_paused(bool paused) } -void GB_audio_clear_queue(void) -{ - pos = 0; - IXAudio2SourceVoice_FlushSourceBuffers(source_voice); -} - -unsigned GB_audio_get_frequency(void) +static unsigned _audio_get_frequency(void) { return AUDIO_FREQUENCY; } -size_t GB_audio_get_queue_length(void) +static size_t _audio_get_queue_length(void) { static XAUDIO2_VOICE_STATE state; IXAudio2SourceVoice_GetState(source_voice, &state, XAUDIO2_VOICE_NOSAMPLESPLAYED); @@ -60,7 +60,7 @@ size_t GB_audio_get_queue_length(void) return state.BuffersQueued * BATCH_SIZE + (pos & (BATCH_SIZE - 1)); } -void GB_audio_queue_sample(GB_sample_t *sample) +static void _audio_queue_sample(GB_sample_t *sample) { if (!playing) return; @@ -74,18 +74,18 @@ void GB_audio_queue_sample(GB_sample_t *sample) } } -void GB_audio_init(void) +static bool _audio_init(void) { HRESULT hr = CoInitializeEx(NULL, COINIT_APARTMENTTHREADED); if (FAILED(hr)) { fprintf(stderr, "CoInitializeEx failed: %lx\n", hr); - return; + return false; } hr = XAudio2Create(&xaudio2, 0, XAUDIO2_DEFAULT_PROCESSOR); if (FAILED(hr)) { fprintf(stderr, "XAudio2Create failed: %lx\n", hr); - return; + return false; } hr = IXAudio2_CreateMasteringVoice(xaudio2, &master_voice, @@ -98,13 +98,17 @@ void GB_audio_init(void) ); if (FAILED(hr)) { fprintf(stderr, "CreateMasteringVoice failed: %lx\n", hr); - return; + return false; } hr = IXAudio2_CreateSourceVoice(xaudio2, &source_voice, &wave_format, 0, XAUDIO2_DEFAULT_FREQ_RATIO, NULL, NULL, NULL); if (FAILED(hr)) { fprintf(stderr, "CreateSourceVoice failed: %lx\n", hr); - return; + return false; } + + return true; } + +GB_AUDIO_DRIVER(XAudio2); \ No newline at end of file diff --git a/SDL/audio/xaudio2_7.c b/SDL/audio/xaudio2_7.c index eb81905..fdce47f 100644 --- a/SDL/audio/xaudio2_7.c +++ b/SDL/audio/xaudio2_7.c @@ -44,17 +44,23 @@ static inline HRESULT XAudio2Create(IXAudio2 **ppXAudio2, return hr; } -bool GB_audio_is_playing(void) +static bool _audio_is_playing(void) { return playing; } -void GB_audio_set_paused(bool paused) +static void _audio_clear_queue(void) +{ + pos = 0; + IXAudio2SourceVoice_FlushSourceBuffers(source_voice); +} + +static void _audio_set_paused(bool paused) { if (paused) { playing = false; IXAudio2SourceVoice_Stop(source_voice, 0, XAUDIO2_COMMIT_NOW); - GB_audio_clear_queue(); + _audio_clear_queue(); } else { playing = true; @@ -63,18 +69,12 @@ void GB_audio_set_paused(bool paused) } -void GB_audio_clear_queue(void) -{ - pos = 0; - IXAudio2SourceVoice_FlushSourceBuffers(source_voice); -} - -unsigned GB_audio_get_frequency(void) +static unsigned _audio_get_frequency(void) { return AUDIO_FREQUENCY; } -size_t GB_audio_get_queue_length(void) +static size_t _audio_get_queue_length(void) { static XAUDIO2_VOICE_STATE state; IXAudio2SourceVoice_GetState(source_voice, &state); @@ -82,7 +82,7 @@ size_t GB_audio_get_queue_length(void) return state.BuffersQueued * BATCH_SIZE + (pos & (BATCH_SIZE - 1)); } -void GB_audio_queue_sample(GB_sample_t *sample) +static void _audio_queue_sample(GB_sample_t *sample) { if (!playing) return; @@ -96,18 +96,18 @@ void GB_audio_queue_sample(GB_sample_t *sample) } } -void GB_audio_init(void) +static bool _audio_init(void) { HRESULT hr = CoInitializeEx(NULL, COINIT_APARTMENTTHREADED); if (FAILED(hr)) { fprintf(stderr, "CoInitializeEx failed: %lx\n", hr); - return; + return false; } hr = XAudio2Create(&xaudio2, 0, XAUDIO2_DEFAULT_PROCESSOR); if (FAILED(hr)) { fprintf(stderr, "XAudio2Create failed: %lx\n", hr); - return; + return false; } hr = IXAudio2_CreateMasteringVoice(xaudio2, &master_voice, @@ -119,13 +119,17 @@ void GB_audio_init(void) ); if (FAILED(hr)) { fprintf(stderr, "CreateMasteringVoice failed: %lx\n", hr); - return; + return false; } hr = IXAudio2_CreateSourceVoice(xaudio2, &source_voice, &wave_format, 0, XAUDIO2_DEFAULT_FREQ_RATIO, NULL, NULL, NULL); if (FAILED(hr)) { fprintf(stderr, "CreateSourceVoice failed: %lx\n", hr); - return; + return false; } + + return true; } + +GB_AUDIO_DRIVER(XAudio2_7); \ No newline at end of file diff --git a/SDL/gui.c b/SDL/gui.c index 4b4ee7b..68940e7 100644 --- a/SDL/gui.c +++ b/SDL/gui.c @@ -8,6 +8,7 @@ #include "utils.h" #include "gui.h" #include "font.h" +#include "audio/audio.h" static const SDL_Color gui_palette[4] = {{8, 24, 16,}, {57, 97, 57,}, {132, 165, 99}, {198, 222, 140}}; static uint32_t gui_palette_native[4]; @@ -847,13 +848,13 @@ static void enter_graphics_menu(unsigned index) recalculate_menu_height(); } -const char *highpass_filter_string(unsigned index) +static const char *highpass_filter_string(unsigned index) { return (const char *[]){"None (Keep DC Offset)", "Accurate", "Preserve Waveform"} [configuration.highpass_mode]; } -void cycle_highpass_filter(unsigned index) +static void cycle_highpass_filter(unsigned index) { configuration.highpass_mode++; if (configuration.highpass_mode == GB_HIGHPASS_MAX) { @@ -861,7 +862,7 @@ void cycle_highpass_filter(unsigned index) } } -void cycle_highpass_filter_backwards(unsigned index) +static void cycle_highpass_filter_backwards(unsigned index) { if (configuration.highpass_mode == 0) { configuration.highpass_mode = GB_HIGHPASS_MAX - 1; @@ -871,14 +872,14 @@ void cycle_highpass_filter_backwards(unsigned index) } } -const char *volume_string(unsigned index) +static const char *volume_string(unsigned index) { static char ret[5]; sprintf(ret, "%d%%", configuration.volume); return ret; } -void increase_volume(unsigned index) +static void increase_volume(unsigned index) { configuration.volume += 5; if (configuration.volume > 100) { @@ -886,7 +887,7 @@ void increase_volume(unsigned index) } } -void decrease_volume(unsigned index) +static void decrease_volume(unsigned index) { configuration.volume -= 5; if (configuration.volume > 100) { @@ -894,14 +895,14 @@ void decrease_volume(unsigned index) } } -const char *interference_volume_string(unsigned index) +static const char *interference_volume_string(unsigned index) { static char ret[5]; sprintf(ret, "%d%%", configuration.interference_volume); return ret; } -void increase_interference_volume(unsigned index) +static void increase_interference_volume(unsigned index) { configuration.interference_volume += 5; if (configuration.interference_volume > 100) { @@ -909,7 +910,7 @@ void increase_interference_volume(unsigned index) } } -void decrease_interference_volume(unsigned index) +static void decrease_interference_volume(unsigned index) { configuration.interference_volume -= 5; if (configuration.interference_volume > 100) { @@ -917,10 +918,18 @@ void decrease_interference_volume(unsigned index) } } +static const char *audio_driver_string(unsigned index) +{ + return GB_audio_driver_name(); +} + +static void nop(index){} + static const struct menu_item audio_menu[] = { {"Highpass Filter:", cycle_highpass_filter, highpass_filter_string, cycle_highpass_filter_backwards}, {"Volume:", increase_volume, volume_string, decrease_volume}, {"Interference Volume:", increase_interference_volume, interference_volume_string, decrease_interference_volume}, + {"Audio Driver:", nop, audio_driver_string}, {"Back", return_to_root_menu}, {NULL,} };