From b573ff3e7a998592d705f5e81b7217ccae44f2a5 Mon Sep 17 00:00:00 2001 From: Jeffery Myers Date: Sun, 21 Mar 2021 14:07:55 -0700 Subject: [PATCH] [AUDIO] Use device native sample rates (#1660) * Init MinAudio to a sample rate of 0 to let the device pick the rate. Read the rate from the device after it starts up. Convert AUDIO_DEVICE_SAMPLE_RATE from a #def into an int, that is set from the device's rate Set all sample systems to use the AUDIO_DEVICE_SAMPLE_RATE as the target rate to minimize resampling. * use device sample rate instead of separate var let config define a device sample rate if it wants to, but let the default be 0 for native rate. * Don't use fixed sample rates for tracker files. Remove config default audio buffer size and replace with a function that computes one for a reasonable frame rate at the output sample rate. --- src/config.h | 3 +-- src/raudio.c | 40 +++++++++++++++++++++++++--------------- 2 files changed, 26 insertions(+), 17 deletions(-) diff --git a/src/config.h b/src/config.h index c29978542..748a53e1f 100644 --- a/src/config.h +++ b/src/config.h @@ -195,9 +195,8 @@ //------------------------------------------------------------------------------------ #define AUDIO_DEVICE_FORMAT ma_format_f32 // Device output format (miniaudio: float-32bit) #define AUDIO_DEVICE_CHANNELS 2 // Device output channels: stereo -#define AUDIO_DEVICE_SAMPLE_RATE 44100 // Device output sample rate +#define AUDIO_DEVICE_SAMPLE_RATE 0 // Device sample rate (device default) -#define DEFAULT_AUDIO_BUFFER_SIZE 4096 // Default audio buffer size for streaming #define MAX_AUDIO_BUFFER_POOL_CHANNELS 16 // Maximum number of audio pool channels //------------------------------------------------------------------------------------ diff --git a/src/raudio.c b/src/raudio.c index 2de3d1fba..1f7f3531f 100644 --- a/src/raudio.c +++ b/src/raudio.c @@ -256,8 +256,9 @@ typedef struct tagBITMAPINFOHEADER { #ifndef AUDIO_DEVICE_CHANNELS #define AUDIO_DEVICE_CHANNELS 2 // Device output channels: stereo #endif + #ifndef AUDIO_DEVICE_SAMPLE_RATE - #define AUDIO_DEVICE_SAMPLE_RATE 44100 // Device output sample rate + #define AUDIO_DEVICE_SAMPLE_RATE 0 // Device output channels: stereo #endif #ifndef MAX_AUDIO_BUFFER_POOL_CHANNELS #define MAX_AUDIO_BUFFER_POOL_CHANNELS 16 // Audio pool channels @@ -358,7 +359,7 @@ static AudioData AUDIO = { // Global AUDIO context // After some math, considering a sampleRate of 48000, a buffer refill rate of 1/60 seconds and a // standard double-buffering system, a 4096 samples buffer has been chosen, it should be enough // In case of music-stalls, just increase this number - .Buffer.defaultSize = DEFAULT_AUDIO_BUFFER_SIZE + .Buffer.defaultSize = 0 }; //---------------------------------------------------------------------------------- @@ -405,6 +406,7 @@ void SetAudioBufferVolume(AudioBuffer *buffer, float volume); void SetAudioBufferPitch(AudioBuffer *buffer, float pitch); void TrackAudioBuffer(AudioBuffer *buffer); void UntrackAudioBuffer(AudioBuffer *buffer); +int GetAudioStreamBufferSizeDefault(); //---------------------------------------------------------------------------------- // Module Functions Definition - Audio Device initialization and Closing @@ -472,7 +474,7 @@ void InitAudioDevice(void) { // WARNING: An empty audioBuffer is created (data = 0) // AudioBuffer data just points to loaded sound data - AUDIO.MultiChannel.pool[i] = LoadAudioBuffer(AUDIO_DEVICE_FORMAT, AUDIO_DEVICE_CHANNELS, AUDIO_DEVICE_SAMPLE_RATE, 0, AUDIO_BUFFER_USAGE_STATIC); + AUDIO.MultiChannel.pool[i] = LoadAudioBuffer(AUDIO_DEVICE_FORMAT, AUDIO_DEVICE_CHANNELS, AUDIO.System.device.sampleRate, 0, AUDIO_BUFFER_USAGE_STATIC); } TRACELOG(LOG_INFO, "AUDIO: Device initialized successfully"); @@ -545,7 +547,7 @@ AudioBuffer *LoadAudioBuffer(ma_format format, ma_uint32 channels, ma_uint32 sam if (sizeInFrames > 0) audioBuffer->data = RL_CALLOC(sizeInFrames*channels*ma_get_bytes_per_sample(format), 1); // Audio data runs through a format converter - ma_data_converter_config converterConfig = ma_data_converter_config_init(format, AUDIO_DEVICE_FORMAT, channels, AUDIO_DEVICE_CHANNELS, sampleRate, AUDIO_DEVICE_SAMPLE_RATE); + ma_data_converter_config converterConfig = ma_data_converter_config_init(format, AUDIO_DEVICE_FORMAT, channels, AUDIO_DEVICE_CHANNELS, sampleRate, AUDIO.System.device.sampleRate); converterConfig.resampling.allowDynamicSampleRate = true; // Required for pitch shifting ma_result result = ma_data_converter_init(&converterConfig, &audioBuffer->converter); @@ -781,21 +783,21 @@ Sound LoadSoundFromWave(Wave wave) ma_format formatIn = ((wave.sampleSize == 8)? ma_format_u8 : ((wave.sampleSize == 16)? ma_format_s16 : ma_format_f32)); ma_uint32 frameCountIn = wave.sampleCount/wave.channels; - ma_uint32 frameCount = (ma_uint32)ma_convert_frames(NULL, 0, AUDIO_DEVICE_FORMAT, AUDIO_DEVICE_CHANNELS, AUDIO_DEVICE_SAMPLE_RATE, NULL, frameCountIn, formatIn, wave.channels, wave.sampleRate); + ma_uint32 frameCount = (ma_uint32)ma_convert_frames(NULL, 0, AUDIO_DEVICE_FORMAT, AUDIO_DEVICE_CHANNELS, AUDIO.System.device.sampleRate, NULL, frameCountIn, formatIn, wave.channels, wave.sampleRate); if (frameCount == 0) TRACELOG(LOG_WARNING, "SOUND: Failed to get frame count for format conversion"); - AudioBuffer *audioBuffer = LoadAudioBuffer(AUDIO_DEVICE_FORMAT, AUDIO_DEVICE_CHANNELS, AUDIO_DEVICE_SAMPLE_RATE, frameCount, AUDIO_BUFFER_USAGE_STATIC); + AudioBuffer *audioBuffer = LoadAudioBuffer(AUDIO_DEVICE_FORMAT, AUDIO_DEVICE_CHANNELS, AUDIO.System.device.sampleRate, frameCount, AUDIO_BUFFER_USAGE_STATIC); if (audioBuffer == NULL) { TRACELOG(LOG_WARNING, "SOUND: Failed to create buffer"); return sound; // early return to avoid dereferencing the audioBuffer null pointer } - frameCount = (ma_uint32)ma_convert_frames(audioBuffer->data, frameCount, AUDIO_DEVICE_FORMAT, AUDIO_DEVICE_CHANNELS, AUDIO_DEVICE_SAMPLE_RATE, wave.data, frameCountIn, formatIn, wave.channels, wave.sampleRate); + frameCount = (ma_uint32)ma_convert_frames(audioBuffer->data, frameCount, AUDIO_DEVICE_FORMAT, AUDIO_DEVICE_CHANNELS, AUDIO.System.device.sampleRate, wave.data, frameCountIn, formatIn, wave.channels, wave.sampleRate); if (frameCount == 0) TRACELOG(LOG_WARNING, "SOUND: Failed format conversion"); sound.sampleCount = frameCount*AUDIO_DEVICE_CHANNELS; - sound.stream.sampleRate = AUDIO_DEVICE_SAMPLE_RATE; + sound.stream.sampleRate = AUDIO.System.device.sampleRate; sound.stream.sampleSize = 32; sound.stream.channels = AUDIO_DEVICE_CHANNELS; sound.stream.buffer = audioBuffer; @@ -1219,7 +1221,7 @@ Music LoadMusicStream(const char *fileName) else if (IsFileExtension(fileName, ".xm")) { jar_xm_context_t *ctxXm = NULL; - int result = jar_xm_create_context_from_file(&ctxXm, 48000, fileName); + int result = jar_xm_create_context_from_file(&ctxXm, AUDIO.System.device.sampleRate, fileName); music.ctxType = MUSIC_MODULE_XM; music.ctxData = ctxXm; @@ -1229,7 +1231,7 @@ Music LoadMusicStream(const char *fileName) jar_xm_set_max_loop_count(ctxXm, 0); // Set infinite number of loops // NOTE: Only stereo is supported for XM - music.stream = InitAudioStream(48000, 16, 2); + music.stream = InitAudioStream(AUDIO.System.device.sampleRate, 16, AUDIO_DEVICE_CHANNELS); music.sampleCount = (unsigned int)jar_xm_get_remaining_samples(ctxXm)*2; // 2 channels music.looping = true; // Looping enabled by default jar_xm_reset(ctxXm); // make sure we start at the beginning of the song @@ -1250,7 +1252,7 @@ Music LoadMusicStream(const char *fileName) if (result > 0) { // NOTE: Only stereo is supported for MOD - music.stream = InitAudioStream(48000, 16, 2); + music.stream = InitAudioStream(AUDIO.System.device.sampleRate, 16, AUDIO_DEVICE_CHANNELS); music.sampleCount = (unsigned int)jar_mod_max_samples(ctxMod)*2; // 2 channels music.looping = true; // Looping enabled by default musicLoaded = true; @@ -1390,14 +1392,14 @@ Music LoadMusicStreamFromMemory(const char *fileType, unsigned char* data, int d else if (TextIsEqual(fileExtLower, ".xm")) { jar_xm_context_t *ctxXm = NULL; - int result = jar_xm_create_context_safe(&ctxXm, (const char*)data, dataSize, 48000); + int result = jar_xm_create_context_safe(&ctxXm, (const char*)data, dataSize, AUDIO.System.device.sampleRate); if (result == 0) // XM AUDIO.System.context created successfully { music.ctxType = MUSIC_MODULE_XM; jar_xm_set_max_loop_count(ctxXm, 0); // Set infinite number of loops // NOTE: Only stereo is supported for XM - music.stream = InitAudioStream(48000, 16, 2); + music.stream = InitAudioStream(AUDIO.System.device.sampleRate, 16, 2); music.sampleCount = (unsigned int)jar_xm_get_remaining_samples(ctxXm)*2; // 2 channels music.looping = true; // Looping enabled by default jar_xm_reset(ctxXm); // make sure we start at the beginning of the song @@ -1435,7 +1437,7 @@ Music LoadMusicStreamFromMemory(const char *fileType, unsigned char* data, int d music.ctxType = MUSIC_MODULE_MOD; // NOTE: Only stereo is supported for MOD - music.stream = InitAudioStream(48000, 16, 2); + music.stream = InitAudioStream(AUDIO.System.device.sampleRate, 16, 2); music.sampleCount = (unsigned int)jar_mod_max_samples(ctxMod)*2; // 2 channels music.looping = true; // Looping enabled by default musicLoaded = true; @@ -1734,7 +1736,7 @@ AudioStream InitAudioStream(unsigned int sampleRate, unsigned int sampleSize, un // The size of a streaming buffer must be at least double the size of a period unsigned int periodSize = AUDIO.System.device.playback.internalPeriodSizeInFrames; - unsigned int subBufferSize = AUDIO.Buffer.defaultSize; // Default buffer size (audio stream) + unsigned int subBufferSize = GetAudioStreamBufferSizeDefault(); if (subBufferSize < periodSize) subBufferSize = periodSize; @@ -1869,6 +1871,14 @@ void SetAudioStreamBufferSizeDefault(int size) AUDIO.Buffer.defaultSize = size; } +int GetAudioStreamBufferSizeDefault() +{ + // if the buffer is not set, compute one that would give us a buffer good enough for a decent frame rate + if (AUDIO.Buffer.defaultSize == 0) + AUDIO.Buffer.defaultSize = AUDIO.System.device.sampleRate / 30; + + return AUDIO.Buffer.defaultSize; +} //---------------------------------------------------------------------------------- // Module specific Functions Definition //----------------------------------------------------------------------------------