Runtime audio driver fallback
This commit is contained in:
parent
fd6b734fd0
commit
9a765820cc
6
Makefile
6
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)
|
||||
|
71
SDL/audio.c
Normal file
71
SDL/audio.c
Normal 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;
|
||||
}
|
@ -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 */
|
||||
|
@ -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);
|
||||
|
@ -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);
|
@ -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);
|
27
SDL/gui.c
27
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,}
|
||||
};
|
||||
|
Loading…
Reference in New Issue
Block a user