Runtime audio driver fallback

This commit is contained in:
Lior Halphon 2022-06-24 14:18:53 +03:00
parent fd6b734fd0
commit 9a765820cc
7 changed files with 176 additions and 59 deletions

View File

@ -17,11 +17,11 @@ ifeq ($(PLATFORM),windows32)
_ := $(shell chcp 65001) _ := $(shell chcp 65001)
EXESUFFIX:=.exe EXESUFFIX:=.exe
NATIVE_CC = clang -IWindows -Wno-deprecated-declarations --target=i386-pc-windows NATIVE_CC = clang -IWindows -Wno-deprecated-declarations --target=i386-pc-windows
SDL_AUDIO_DRIVER ?= xaudio2 SDL_AUDIO_DRIVERS ?= xaudio2 xaudio2_7 sdl
else else
EXESUFFIX:= EXESUFFIX:=
NATIVE_CC := cc NATIVE_CC := cc
SDL_AUDIO_DRIVER ?= sdl SDL_AUDIO_DRIVERS ?= sdl
endif endif
PB12_COMPRESS := build/pb12$(EXESUFFIX) 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 # 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/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) TESTER_SOURCES := $(shell ls Tester/*.c)
ifeq ($(PLATFORM),Darwin) ifeq ($(PLATFORM),Darwin)

71
SDL/audio.c Normal file
View File

@ -0,0 +1,71 @@
#include <stdbool.h>
#include <stddef.h>
#include <Core/gb.h>
#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;
}

View File

@ -11,6 +11,31 @@ void GB_audio_clear_queue(void);
unsigned GB_audio_get_frequency(void); unsigned GB_audio_get_frequency(void);
size_t GB_audio_get_queue_length(void); size_t GB_audio_get_queue_length(void);
void GB_audio_queue_sample(GB_sample_t *sample); 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 */ #endif /* sdl_audio_h */

View File

@ -29,33 +29,33 @@ static SDL_AudioSpec want_aspec, have_aspec;
static unsigned buffer_pos = 0; static unsigned buffer_pos = 0;
static GB_sample_t audio_buffer[AUDIO_BUFFER_SIZE]; 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; return SDL_GetAudioDeviceStatus(device_id) == SDL_AUDIO_PLAYING;
} }
void GB_audio_set_paused(bool paused) static void _audio_clear_queue(void)
{
GB_audio_clear_queue();
SDL_PauseAudioDevice(device_id, paused);
}
void GB_audio_clear_queue(void)
{ {
SDL_ClearQueuedAudio(device_id); 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; 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); 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; 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 */ /* Configure Audio */
memset(&want_aspec, 0, sizeof(want_aspec)); memset(&want_aspec, 0, sizeof(want_aspec));
@ -93,4 +93,8 @@ void GB_audio_init(void)
#endif #endif
device_id = SDL_OpenAudioDevice(0, 0, &want_aspec, &have_aspec, SDL_AUDIO_ALLOW_FREQUENCY_CHANGE | SDL_AUDIO_ALLOW_SAMPLES_CHANGE); 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);

View File

@ -22,17 +22,23 @@ static const WAVEFORMATEX wave_format = {
.cbSize = 0 .cbSize = 0
}; };
bool GB_audio_is_playing(void) static bool _audio_is_playing(void)
{ {
return playing; 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) { if (paused) {
playing = false; playing = false;
IXAudio2SourceVoice_Stop(source_voice, 0, XAUDIO2_COMMIT_NOW); IXAudio2SourceVoice_Stop(source_voice, 0, XAUDIO2_COMMIT_NOW);
GB_audio_clear_queue(); _audio_clear_queue();
} }
else { else {
playing = true; playing = true;
@ -41,18 +47,12 @@ void GB_audio_set_paused(bool paused)
} }
void GB_audio_clear_queue(void) static unsigned _audio_get_frequency(void)
{
pos = 0;
IXAudio2SourceVoice_FlushSourceBuffers(source_voice);
}
unsigned GB_audio_get_frequency(void)
{ {
return AUDIO_FREQUENCY; return AUDIO_FREQUENCY;
} }
size_t GB_audio_get_queue_length(void) static size_t _audio_get_queue_length(void)
{ {
static XAUDIO2_VOICE_STATE state; static XAUDIO2_VOICE_STATE state;
IXAudio2SourceVoice_GetState(source_voice, &state, XAUDIO2_VOICE_NOSAMPLESPLAYED); 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)); 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; 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); HRESULT hr = CoInitializeEx(NULL, COINIT_APARTMENTTHREADED);
if (FAILED(hr)) { if (FAILED(hr)) {
fprintf(stderr, "CoInitializeEx failed: %lx\n", hr); fprintf(stderr, "CoInitializeEx failed: %lx\n", hr);
return; return false;
} }
hr = XAudio2Create(&xaudio2, 0, XAUDIO2_DEFAULT_PROCESSOR); hr = XAudio2Create(&xaudio2, 0, XAUDIO2_DEFAULT_PROCESSOR);
if (FAILED(hr)) { if (FAILED(hr)) {
fprintf(stderr, "XAudio2Create failed: %lx\n", hr); fprintf(stderr, "XAudio2Create failed: %lx\n", hr);
return; return false;
} }
hr = IXAudio2_CreateMasteringVoice(xaudio2, &master_voice, hr = IXAudio2_CreateMasteringVoice(xaudio2, &master_voice,
@ -98,13 +98,17 @@ void GB_audio_init(void)
); );
if (FAILED(hr)) { if (FAILED(hr)) {
fprintf(stderr, "CreateMasteringVoice failed: %lx\n", 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); hr = IXAudio2_CreateSourceVoice(xaudio2, &source_voice, &wave_format, 0, XAUDIO2_DEFAULT_FREQ_RATIO, NULL, NULL, NULL);
if (FAILED(hr)) { if (FAILED(hr)) {
fprintf(stderr, "CreateSourceVoice failed: %lx\n", hr); fprintf(stderr, "CreateSourceVoice failed: %lx\n", hr);
return; return false;
} }
return true;
} }
GB_AUDIO_DRIVER(XAudio2);

View File

@ -44,17 +44,23 @@ static inline HRESULT XAudio2Create(IXAudio2 **ppXAudio2,
return hr; return hr;
} }
bool GB_audio_is_playing(void) static bool _audio_is_playing(void)
{ {
return playing; 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) { if (paused) {
playing = false; playing = false;
IXAudio2SourceVoice_Stop(source_voice, 0, XAUDIO2_COMMIT_NOW); IXAudio2SourceVoice_Stop(source_voice, 0, XAUDIO2_COMMIT_NOW);
GB_audio_clear_queue(); _audio_clear_queue();
} }
else { else {
playing = true; playing = true;
@ -63,18 +69,12 @@ void GB_audio_set_paused(bool paused)
} }
void GB_audio_clear_queue(void) static unsigned _audio_get_frequency(void)
{
pos = 0;
IXAudio2SourceVoice_FlushSourceBuffers(source_voice);
}
unsigned GB_audio_get_frequency(void)
{ {
return AUDIO_FREQUENCY; return AUDIO_FREQUENCY;
} }
size_t GB_audio_get_queue_length(void) static size_t _audio_get_queue_length(void)
{ {
static XAUDIO2_VOICE_STATE state; static XAUDIO2_VOICE_STATE state;
IXAudio2SourceVoice_GetState(source_voice, &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)); 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; 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); HRESULT hr = CoInitializeEx(NULL, COINIT_APARTMENTTHREADED);
if (FAILED(hr)) { if (FAILED(hr)) {
fprintf(stderr, "CoInitializeEx failed: %lx\n", hr); fprintf(stderr, "CoInitializeEx failed: %lx\n", hr);
return; return false;
} }
hr = XAudio2Create(&xaudio2, 0, XAUDIO2_DEFAULT_PROCESSOR); hr = XAudio2Create(&xaudio2, 0, XAUDIO2_DEFAULT_PROCESSOR);
if (FAILED(hr)) { if (FAILED(hr)) {
fprintf(stderr, "XAudio2Create failed: %lx\n", hr); fprintf(stderr, "XAudio2Create failed: %lx\n", hr);
return; return false;
} }
hr = IXAudio2_CreateMasteringVoice(xaudio2, &master_voice, hr = IXAudio2_CreateMasteringVoice(xaudio2, &master_voice,
@ -119,13 +119,17 @@ void GB_audio_init(void)
); );
if (FAILED(hr)) { if (FAILED(hr)) {
fprintf(stderr, "CreateMasteringVoice failed: %lx\n", 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); hr = IXAudio2_CreateSourceVoice(xaudio2, &source_voice, &wave_format, 0, XAUDIO2_DEFAULT_FREQ_RATIO, NULL, NULL, NULL);
if (FAILED(hr)) { if (FAILED(hr)) {
fprintf(stderr, "CreateSourceVoice failed: %lx\n", hr); fprintf(stderr, "CreateSourceVoice failed: %lx\n", hr);
return; return false;
} }
return true;
} }
GB_AUDIO_DRIVER(XAudio2_7);

View File

@ -8,6 +8,7 @@
#include "utils.h" #include "utils.h"
#include "gui.h" #include "gui.h"
#include "font.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 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]; static uint32_t gui_palette_native[4];
@ -847,13 +848,13 @@ static void enter_graphics_menu(unsigned index)
recalculate_menu_height(); 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"} return (const char *[]){"None (Keep DC Offset)", "Accurate", "Preserve Waveform"}
[configuration.highpass_mode]; [configuration.highpass_mode];
} }
void cycle_highpass_filter(unsigned index) static void cycle_highpass_filter(unsigned index)
{ {
configuration.highpass_mode++; configuration.highpass_mode++;
if (configuration.highpass_mode == GB_HIGHPASS_MAX) { 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) { if (configuration.highpass_mode == 0) {
configuration.highpass_mode = GB_HIGHPASS_MAX - 1; 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]; static char ret[5];
sprintf(ret, "%d%%", configuration.volume); sprintf(ret, "%d%%", configuration.volume);
return ret; return ret;
} }
void increase_volume(unsigned index) static void increase_volume(unsigned index)
{ {
configuration.volume += 5; configuration.volume += 5;
if (configuration.volume > 100) { 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; configuration.volume -= 5;
if (configuration.volume > 100) { 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]; static char ret[5];
sprintf(ret, "%d%%", configuration.interference_volume); sprintf(ret, "%d%%", configuration.interference_volume);
return ret; return ret;
} }
void increase_interference_volume(unsigned index) static void increase_interference_volume(unsigned index)
{ {
configuration.interference_volume += 5; configuration.interference_volume += 5;
if (configuration.interference_volume > 100) { 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; configuration.interference_volume -= 5;
if (configuration.interference_volume > 100) { 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[] = { static const struct menu_item audio_menu[] = {
{"Highpass Filter:", cycle_highpass_filter, highpass_filter_string, cycle_highpass_filter_backwards}, {"Highpass Filter:", cycle_highpass_filter, highpass_filter_string, cycle_highpass_filter_backwards},
{"Volume:", increase_volume, volume_string, decrease_volume}, {"Volume:", increase_volume, volume_string, decrease_volume},
{"Interference Volume:", increase_interference_volume, interference_volume_string, decrease_interference_volume}, {"Interference Volume:", increase_interference_volume, interference_volume_string, decrease_interference_volume},
{"Audio Driver:", nop, audio_driver_string},
{"Back", return_to_root_menu}, {"Back", return_to_root_menu},
{NULL,} {NULL,}
}; };