diff --git a/workspace/all/common/api.c b/workspace/all/common/api.c index 1e1d179f9..003572f3d 100644 --- a/workspace/all/common/api.c +++ b/workspace/all/common/api.c @@ -2275,6 +2275,9 @@ SDL_Color GFX_mapColor(uint32_t c) #ifndef SAMPLES #define SAMPLES 512 // default #endif +#ifndef BUFFER_MULTIPLIER +#define BUFFER_MULTIPLIER 8 // default +#endif #define ms SDL_GetTicks @@ -2343,6 +2346,33 @@ void SND_setQuality(int quality) soundQuality = qualityLevels[quality]; resetSrcState = 1; } + +static int audio_buffer_samples = SAMPLES; +static int audio_buffer_multiplier = BUFFER_MULTIPLIER; + +void SND_setBufferSize(int samples) { + if (samples != audio_buffer_samples) { + audio_buffer_samples = samples; + LOG_info("Audio buffer size set to %d samples\n", samples); + if (snd.initialized) { + SND_resetAudio(snd.sample_rate_in, snd.frame_rate); + } + } +} + +void SND_setBufferMultiplier(int multiplier) { + if (multiplier != audio_buffer_multiplier) { + audio_buffer_multiplier = multiplier; + if (snd.initialized && snd.sample_rate_out > 0) { + snd.frame_count = ((float)snd.sample_rate_out / SCREEN_FPS) * audio_buffer_multiplier; + perf.buffer_size = snd.frame_count; + SND_resizeBuffer(); + SND_pauseAudio(true); + LOG_info("Audio buffer multiplier set to %d, new frame_count: %d\n", multiplier, snd.frame_count); + } + } +} + ResampledFrames resample_audio(const SND_Frame *input_frames, int input_frame_count, int input_sample_rate, int output_sample_rate, double ratio) @@ -2796,7 +2826,7 @@ void SND_init(double sample_rate, double frame_rate) spec_in.freq = PLAT_pickSampleRate(sample_rate, MAX_SAMPLE_RATE); spec_in.format = AUDIO_S16; spec_in.channels = 2; - spec_in.samples = SAMPLES; + spec_in.samples = audio_buffer_samples; spec_in.callback = SND_audioCallback; #if defined(USE_SDL2) @@ -2821,7 +2851,7 @@ void SND_init(double sample_rate, double frame_rate) LOG_info("We now have audio device #%d\n", snd.device_id); - snd.frame_count = ((float)spec_out.freq / SCREEN_FPS) * 8; // buffer size based on sample rate out (times 12 samples headroom) + snd.frame_count = ((float)spec_out.freq / SCREEN_FPS) * audio_buffer_multiplier; perf.buffer_size = snd.frame_count; snd.sample_rate_in = sample_rate; snd.sample_rate_out = spec_out.freq; @@ -2832,7 +2862,7 @@ void SND_init(double sample_rate, double frame_rate) // start with audiodevice paused so buffer can fill a little, snd_batchsamples will unpause it SND_pauseAudio(true); - LOG_info("sample rate: %i (req) %i (rec) [samples %i]\n", snd.sample_rate_in, snd.sample_rate_out, SAMPLES); + LOG_info("sample rate: %i (req) %i (rec) [samples %i]\n", snd.sample_rate_in, snd.sample_rate_out, audio_buffer_samples); snd.initialized = 1; } @@ -4388,4 +4418,4 @@ FALLBACK_IMPLEMENTATION void PLAT_bluetoothStreamBegin(int buffersize) {} FALLBACK_IMPLEMENTATION void PLAT_bluetoothStreamEnd() {} FALLBACK_IMPLEMENTATION void PLAT_bluetoothStreamQuit() {} FALLBACK_IMPLEMENTATION int PLAT_bluetoothVolume() { return 100; } -FALLBACK_IMPLEMENTATION void PLAT_bluetoothSetVolume(int vol) {} \ No newline at end of file +FALLBACK_IMPLEMENTATION void PLAT_bluetoothSetVolume(int vol) {} diff --git a/workspace/all/common/api.h b/workspace/all/common/api.h index 3d5bdf097..e1ac90cda 100644 --- a/workspace/all/common/api.h +++ b/workspace/all/common/api.h @@ -413,6 +413,8 @@ void SND_quit(void); void SND_resetAudio(double sample_rate, double frame_rate); void SND_pauseAudio(bool paused); void SND_setQuality(int quality); +void SND_setBufferSize(int samples); +void SND_setBufferMultiplier(int multiplier); // watch audio device changes typedef enum { diff --git a/workspace/all/minarch/minarch.c b/workspace/all/minarch/minarch.c index 81fc45db0..f0e6192f7 100644 --- a/workspace/all/minarch/minarch.c +++ b/workspace/all/minarch/minarch.c @@ -2027,6 +2027,14 @@ static char* resample_labels[] = { "Max", NULL }; +static char* buffer_size_labels[] = { + "128", "256", "512", "1024", + NULL +}; +static char* buffer_multiplier_labels[] = { + "4", "6", "8", "10", + NULL +}; static char* rewind_enable_labels[] = { "Off", "On", @@ -2319,6 +2327,8 @@ enum { FE_OPT_REWIND_COMPRESSION, FE_OPT_REWIND_COMPRESSION_ACCEL, FE_OPT_REWIND_AUDIO, + FE_OPT_AUDIO_BUFFER, + FE_OPT_AUDIO_LATENCY, FE_OPT_COUNT, }; @@ -2737,6 +2747,26 @@ static struct Config { .values = onoff_labels, .labels = onoff_labels, }, + [FE_OPT_AUDIO_BUFFER] = { + .key = "minarch__audio_buffer", + .name = "Audio Buffer Size", + .desc = "Size of audio chunks sent to the device.\nSmaller values lower latency but use more CPU.", + .default_value = 2, + .value = 2, + .count = 4, + .values = buffer_size_labels, + .labels = buffer_size_labels, + }, + [FE_OPT_AUDIO_LATENCY] = { + .key = "minarch__audio_latency", + .name = "Audio Buffer Headroom", + .desc = "How much audio is buffered ahead of time.\nSmaller values lower latency but may crackle.", + .default_value = 2, + .value = 2, + .count = 4, + .values = buffer_multiplier_labels, + .labels = buffer_multiplier_labels, + }, [FE_OPT_COUNT] = {NULL} } }, @@ -3109,6 +3139,16 @@ static void Config_syncFrontend(char* key, int value) { else if (exactMatch(key,config.frontend.options[FE_OPT_REWIND_COMPRESSION_ACCEL].key)) { i = FE_OPT_REWIND_COMPRESSION_ACCEL; } + else if (exactMatch(key, config.frontend.options[FE_OPT_AUDIO_BUFFER].key)) { + int buf_size = atoi(config.frontend.options[FE_OPT_AUDIO_BUFFER].values[value]); + SND_setBufferSize(buf_size); + i = FE_OPT_AUDIO_BUFFER; + } + else if (exactMatch(key, config.frontend.options[FE_OPT_AUDIO_LATENCY].key)) { + int multiplier = atoi(config.frontend.options[FE_OPT_AUDIO_LATENCY].values[value]); + SND_setBufferMultiplier(multiplier); + i = FE_OPT_AUDIO_LATENCY; + } if (i==-1) return; Option* option = &config.frontend.options[i]; option->value = value;