From 7dbb9f5e1abf51ff82ec394c545f4ce19a33d68b Mon Sep 17 00:00:00 2001 From: strager Date: Sun, 5 Jan 2025 17:49:57 -0500 Subject: [PATCH 01/13] Fix missing ma_resampler_init argument in docs ma_resampler_init has three parameters, not two. Add the missing pAllocationCallbacks argument in the example code. --- miniaudio.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/miniaudio.h b/miniaudio.h index f15bb2a4..e9c25402 100644 --- a/miniaudio.h +++ b/miniaudio.h @@ -2899,7 +2899,7 @@ like the following: ma_resample_algorithm_linear); ma_resampler resampler; - ma_result result = ma_resampler_init(&config, &resampler); + ma_result result = ma_resampler_init(&config, NULL, &resampler); if (result != MA_SUCCESS) { // An error occurred... } From da76932f6b6318b398dec3debce2de7c98baa1ef Mon Sep 17 00:00:00 2001 From: David Reid Date: Mon, 6 Jan 2025 10:52:12 +1000 Subject: [PATCH 02/13] Update change history. --- CHANGES.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/CHANGES.md b/CHANGES.md index 1b67da17..3aa1ac2f 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -9,8 +9,9 @@ v0.11.22 - TBD * Web: Fix ScriptProcessorNode path when compiling with `--closure=1`. Note that the Audio Worklets path is not currently working due to the callback specified in `emscripten_create_wasm_audio_worklet_processor_async` never getting fired. * Web: Fix an error with the unlocked notification when compiling as C++. * Web: Fix a JavaScript error when initializing and then uninitializing a context before any interactivity. +* AAudio: Fix an error where the device is silenced after rerouting. With this change, miniaudio will no longer give AAudio a hint to use your supplied period size which will therefore result in AAudio using its default latency configuration. If you want AAudio to try to use the period size you supply in the device config, which is the old behaviour, set `aaudio.allowSetBufferCapacity` to true in the device config. Note, however, if you do this you may end up with errors when rerouting between devices. * AAudio: The default minimum SDK version has been increased from 26 to 27 when enabling AAudio. If you need to support version 26, you can use `#define MA_AAUDIO_MIN_ANDROID_SDK_VERSION 26`. -* AAudio: Fix ma_device_get_info() implementation +* AAudio: Fix ma_device_get_info() implementation. * PulseAudio: Allow setting the channel map requested from PulseAudio in device configs From 970c3801d96ab5fd2ce00250d83cad740d839e49 Mon Sep 17 00:00:00 2001 From: Dmitry Atamanov Date: Thu, 9 Jan 2025 20:18:08 +0500 Subject: [PATCH 03/13] Small fixes in tools/audioconverter --- tools/audioconverter/audioconverter.c | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/tools/audioconverter/audioconverter.c b/tools/audioconverter/audioconverter.c index 83b43a0f..9e7d5ece 100644 --- a/tools/audioconverter/audioconverter.c +++ b/tools/audioconverter/audioconverter.c @@ -172,8 +172,6 @@ int main(int argc, char** argv) int iarg; const char* pOutputFilePath; - print_usage(); - /* Print help if requested. */ if (argc == 2) { if (strcmp(argv[1], "-h") == 0 || strcmp(argv[1], "--help") == 0) { @@ -236,19 +234,19 @@ int main(int argc, char** argv) result = ma_decoder_init_file(argv[1], &decoderConfig, &decoder); if (result != MA_SUCCESS) { printf("Failed to open input file. Check the file exists and the format is supported. Supported input formats:\n"); - #if defined(dr_opus_h) + #if defined(ma_dr_opus_h) printf(" Opus\n"); #endif - #if defined(dr_mp3_h) + #if defined(ma_dr_mp3_h) printf(" MP3\n"); #endif - #if defined(dr_flac_h) + #if defined(ma_dr_flac_h) printf(" FLAC\n"); #endif #if defined(STB_VORBIS_INCLUDE_STB_VORBIS_H) printf(" Vorbis\n"); #endif - #if defined(dr_wav_h) + #if defined(ma_dr_wav_h) printf(" WAV\n"); #endif return (int)result; From ad615af1a825d4bb8a0651ca45e8c211c23ff190 Mon Sep 17 00:00:00 2001 From: David Reid Date: Sat, 11 Jan 2025 08:47:29 +1000 Subject: [PATCH 04/13] Fix some warnings with GCC. --- miniaudio.h | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/miniaudio.h b/miniaudio.h index e9c25402..25604185 100644 --- a/miniaudio.h +++ b/miniaudio.h @@ -58015,6 +58015,13 @@ MA_API void ma_data_source_get_range_in_pcm_frames(const ma_data_source* pDataSo { const ma_data_source_base* pDataSourceBase = (const ma_data_source_base*)pDataSource; + if (pRangeBegInFrames != NULL) { + *pRangeBegInFrames = 0; + } + if (pRangeEndInFrames != NULL) { + *pRangeEndInFrames = 0; + } + if (pDataSource == NULL) { return; } @@ -58059,6 +58066,13 @@ MA_API void ma_data_source_get_loop_point_in_pcm_frames(const ma_data_source* pD { const ma_data_source_base* pDataSourceBase = (const ma_data_source_base*)pDataSource; + if (pLoopBegInFrames != NULL) { + *pLoopBegInFrames = 0; + } + if (pLoopEndInFrames != NULL) { + *pLoopEndInFrames = 0; + } + if (pDataSource == NULL) { return; } From 8d5bf8210c90d7c4c7f4c9d60fe0e6f41e39436f Mon Sep 17 00:00:00 2001 From: "francois@recisio.com" Date: Tue, 5 Nov 2024 15:32:39 +0100 Subject: [PATCH 05/13] WebAudio: Fix a noise sound before device was started --- miniaudio.h | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/miniaudio.h b/miniaudio.h index 25604185..cacd5d4d 100644 --- a/miniaudio.h +++ b/miniaudio.h @@ -40099,15 +40099,10 @@ typedef struct static EM_BOOL ma_audio_worklet_process_callback__webaudio(int inputCount, const AudioSampleFrame* pInputs, int outputCount, AudioSampleFrame* pOutputs, int paramCount, const AudioParamFrame* pParams, void* pUserData) { ma_device* pDevice = (ma_device*)pUserData; - ma_uint32 frameCount; (void)paramCount; (void)pParams; - if (ma_device_get_state(pDevice) != ma_device_state_started) { - return EM_TRUE; - } - /* The Emscripten documentation says that it'll always be 128 frames being passed in. Hard coding it like that feels like a very bad idea to me. Even if it's hard coded in the backend, the API and documentation should always refer @@ -40116,7 +40111,15 @@ static EM_BOOL ma_audio_worklet_process_callback__webaudio(int inputCount, const Unfortunately the audio data is not interleaved so we'll need to convert it before we give the data to miniaudio for further processing. */ - frameCount = 128; + ma_uint32 frameCount = 128; + + if (ma_device_get_state(pDevice) != ma_device_state_started) { + /* Fill the output buffer with zero to avoid a noise sound */ + if (outputCount > 0) { + MA_ZERO_MEMORY(pOutputs[0].data, frameCount * pDevice->playback.internalChannels * sizeof(float)); + } + return EM_TRUE; + } if (inputCount > 0) { /* Input data needs to be interleaved before we hand it to the client. */ From 82ae0138f348867984a941c11930cb2a97290ed6 Mon Sep 17 00:00:00 2001 From: "francois@recisio.com" Date: Thu, 14 Nov 2024 16:16:57 +0100 Subject: [PATCH 06/13] Improve fix, handle all outputs --- miniaudio.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/miniaudio.h b/miniaudio.h index cacd5d4d..af7e44ba 100644 --- a/miniaudio.h +++ b/miniaudio.h @@ -40115,8 +40115,8 @@ static EM_BOOL ma_audio_worklet_process_callback__webaudio(int inputCount, const if (ma_device_get_state(pDevice) != ma_device_state_started) { /* Fill the output buffer with zero to avoid a noise sound */ - if (outputCount > 0) { - MA_ZERO_MEMORY(pOutputs[0].data, frameCount * pDevice->playback.internalChannels * sizeof(float)); + for (int i = 0; i < outputCount; i += 1) { + MA_ZERO_MEMORY(pOutputs[i].data, pOutputs[i].numberOfChannels * pOutputs[i].samplesPerChannel * sizeof(float)); } return EM_TRUE; } From 1a7a9a7ed24a0174b55e6dfa7fbefac0a55feaaa Mon Sep 17 00:00:00 2001 From: "francois@recisio.com" Date: Thu, 14 Nov 2024 16:29:27 +0100 Subject: [PATCH 07/13] Fix build for emscripten before 3.1.70 --- miniaudio.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/miniaudio.h b/miniaudio.h index af7e44ba..bc69e578 100644 --- a/miniaudio.h +++ b/miniaudio.h @@ -40116,7 +40116,7 @@ static EM_BOOL ma_audio_worklet_process_callback__webaudio(int inputCount, const if (ma_device_get_state(pDevice) != ma_device_state_started) { /* Fill the output buffer with zero to avoid a noise sound */ for (int i = 0; i < outputCount; i += 1) { - MA_ZERO_MEMORY(pOutputs[i].data, pOutputs[i].numberOfChannels * pOutputs[i].samplesPerChannel * sizeof(float)); + MA_ZERO_MEMORY(pOutputs[i].data, pOutputs[i].numberOfChannels * /*pOutputs[i].samplesPerChannel*/frameCount * sizeof(float)); } return EM_TRUE; } From b53daca5542342e6869b384af12d45c5800bd695 Mon Sep 17 00:00:00 2001 From: David Reid Date: Sat, 11 Jan 2025 09:25:43 +1000 Subject: [PATCH 08/13] Clean up. --- miniaudio.h | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/miniaudio.h b/miniaudio.h index bc69e578..840e4292 100644 --- a/miniaudio.h +++ b/miniaudio.h @@ -40099,6 +40099,7 @@ typedef struct static EM_BOOL ma_audio_worklet_process_callback__webaudio(int inputCount, const AudioSampleFrame* pInputs, int outputCount, AudioSampleFrame* pOutputs, int paramCount, const AudioParamFrame* pParams, void* pUserData) { ma_device* pDevice = (ma_device*)pUserData; + ma_uint32 frameCount; (void)paramCount; (void)pParams; @@ -40111,13 +40112,14 @@ static EM_BOOL ma_audio_worklet_process_callback__webaudio(int inputCount, const Unfortunately the audio data is not interleaved so we'll need to convert it before we give the data to miniaudio for further processing. */ - ma_uint32 frameCount = 128; + frameCount = 128; if (ma_device_get_state(pDevice) != ma_device_state_started) { /* Fill the output buffer with zero to avoid a noise sound */ for (int i = 0; i < outputCount; i += 1) { - MA_ZERO_MEMORY(pOutputs[i].data, pOutputs[i].numberOfChannels * /*pOutputs[i].samplesPerChannel*/frameCount * sizeof(float)); + MA_ZERO_MEMORY(pOutputs[i].data, pOutputs[i].numberOfChannels * frameCount * sizeof(float)); } + return EM_TRUE; } From ae2cd4bea4059fce5d643e5f151bdf260bed9737 Mon Sep 17 00:00:00 2001 From: David Reid Date: Sat, 11 Jan 2025 10:17:20 +1000 Subject: [PATCH 09/13] Web Audio: Add support for variable buffer sizes to Audio Worklets. Support for this was added to Emscripten 3.1.70. Currently only querying the quantum size is supported. I believe support for configuring the quantum size is coming later in Web Audio 1.1. Compilation with versions of Emscripten earlier than 3.1.70 is still supported. --- miniaudio.h | 20 ++++++++++++++++++-- 1 file changed, 18 insertions(+), 2 deletions(-) diff --git a/miniaudio.h b/miniaudio.h index 840e4292..5b5d92d0 100644 --- a/miniaudio.h +++ b/miniaudio.h @@ -39857,6 +39857,10 @@ Web Audio Backend #if (__EMSCRIPTEN_major__ > 3) || (__EMSCRIPTEN_major__ == 3 && (__EMSCRIPTEN_minor__ > 1 || (__EMSCRIPTEN_minor__ == 1 && __EMSCRIPTEN_tiny__ >= 32))) #include #define MA_SUPPORT_AUDIO_WORKLETS + + #if (__EMSCRIPTEN_major__ > 3) || (__EMSCRIPTEN_major__ == 3 && (__EMSCRIPTEN_minor__ > 1 || (__EMSCRIPTEN_minor__ == 1 && __EMSCRIPTEN_tiny__ >= 70))) + #define MA_SUPPORT_AUDIO_WORKLETS_VARIABLE_BUFFER_SIZE + #endif #endif /* @@ -40112,7 +40116,11 @@ static EM_BOOL ma_audio_worklet_process_callback__webaudio(int inputCount, const Unfortunately the audio data is not interleaved so we'll need to convert it before we give the data to miniaudio for further processing. */ - frameCount = 128; + if (pDevice->type == ma_device_type_playback) { + frameCount = pDevice->playback.internalPeriodSizeInFrames; + } else { + frameCount = pDevice->capture.internalPeriodSizeInFrames; + } if (ma_device_get_state(pDevice) != ma_device_state_started) { /* Fill the output buffer with zero to avoid a noise sound */ @@ -40199,7 +40207,15 @@ static void ma_audio_worklet_processor_created__webaudio(EMSCRIPTEN_WEBAUDIO_T a Now that we know the channel count to use we can allocate the intermediary buffer. The intermediary buffer is used for interleaving and deinterleaving. */ - intermediaryBufferSizeInFrames = 128; + #if defined(MA_SUPPORT_AUDIO_WORKLETS_VARIABLE_BUFFER_SIZE) + { + intermediaryBufferSizeInFrames = (size_t)emscripten_audio_context_quantum_size(audioContext); + } + #else + { + intermediaryBufferSizeInFrames = 128; + } + #endif pParameters->pDevice->webaudio.pIntermediaryBuffer = (float*)ma_malloc(intermediaryBufferSizeInFrames * (ma_uint32)channels * sizeof(float), &pParameters->pDevice->pContext->allocationCallbacks); if (pParameters->pDevice->webaudio.pIntermediaryBuffer == NULL) { From 928ed8bd856c41b4fd06982887c35f50bbd79f82 Mon Sep 17 00:00:00 2001 From: David Reid Date: Sat, 11 Jan 2025 13:00:33 +1000 Subject: [PATCH 10/13] Web Audio: Enable threading in ma_engine if compiling with -pthread. With this commit, when targeting pthreads with the -pthread, the engine will allow threading with it's internal resource manager. Public issue https://github.com/mackron/miniaudio/issues/855 --- miniaudio.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/miniaudio.h b/miniaudio.h index 5b5d92d0..c270609d 100644 --- a/miniaudio.h +++ b/miniaudio.h @@ -75690,8 +75690,8 @@ MA_API ma_result ma_engine_init(const ma_engine_config* pConfig, ma_engine* pEng ma_allocation_callbacks_init_copy(&resourceManagerConfig.allocationCallbacks, &pEngine->allocationCallbacks); resourceManagerConfig.pVFS = engineConfig.pResourceManagerVFS; - /* The Emscripten build cannot use threads. */ - #if defined(MA_EMSCRIPTEN) + /* The Emscripten build cannot use threads unless it's targeting pthreads. */ + #if defined(MA_EMSCRIPTEN) && !defined(__EMSCRIPTEN_PTHREADS__) { resourceManagerConfig.jobThreadCount = 0; resourceManagerConfig.flags |= MA_RESOURCE_MANAGER_FLAG_NO_THREADING; From 547ef1c9b7241616eb10357844770a89abaf5321 Mon Sep 17 00:00:00 2001 From: David Reid Date: Sat, 11 Jan 2025 15:57:42 +1000 Subject: [PATCH 11/13] Don't return MA_AT_END from ring buffers. There is no notion of an "end" in a ring buffer. Also, this result is returned when the operation completed successfully which makes a result code other than MA_SUCCESS confusing. --- CHANGES.md | 1 + miniaudio.h | 12 ++---------- 2 files changed, 3 insertions(+), 10 deletions(-) diff --git a/CHANGES.md b/CHANGES.md index 3aa1ac2f..c27fe154 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -1,6 +1,7 @@ v0.11.22 - TBD ===================== * Add `MA_SOUND_FLAG_LOOPING` and `MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_LOOPING` flags. These can be used to initialize sounds and resource managed data sources to loop by default. This is the recommended way to enable looping for streams. The `isLooping` config option in `ma_sound_config` and `ma_resource_manager_data_source_config` has been deprecated. If you are using those, you should switch to the new flag or else you'll get compiler errors when upgrading to a future version. +* `ma_rb_commit_read()`, `ma_rb_commit_write()`, `ma_pcm_rb_commit_read()` and `ma_pcm_rb_commit_write()` no longer return `MA_AT_END`. The reason for this change is that there's no real notion of an "end" in a ring buffer which makes this result code confusing. In addition, it's possible for these functions to return something other than `MA_SUCCESS` when the operation completed successfully which adds to the confusion. The correct way to check if there is any more room in the ring buffer is to look at the frame count returned by `*rb_acquire_read/write()`. * Fix a bug relating to node detachment. * Fix a bug where amplification with `ma_device_set_master_volume()` does not work. * Fix a bug where sounds loaded with `MA_SOUND_FLAG_DECODE` do not loop. diff --git a/miniaudio.h b/miniaudio.h index c270609d..19acbe66 100644 --- a/miniaudio.h +++ b/miniaudio.h @@ -56592,11 +56592,7 @@ MA_API ma_result ma_rb_commit_read(ma_rb* pRB, size_t sizeInBytes) ma_atomic_exchange_32(&pRB->encodedReadOffset, ma_rb__construct_offset(newReadOffsetLoopFlag, newReadOffsetInBytes)); - if (ma_rb_pointer_distance(pRB) == 0) { - return MA_AT_END; - } else { - return MA_SUCCESS; - } + return MA_SUCCESS; } MA_API ma_result ma_rb_acquire_write(ma_rb* pRB, size_t* pSizeInBytes, void** ppBufferOut) @@ -56678,11 +56674,7 @@ MA_API ma_result ma_rb_commit_write(ma_rb* pRB, size_t sizeInBytes) ma_atomic_exchange_32(&pRB->encodedWriteOffset, ma_rb__construct_offset(newWriteOffsetLoopFlag, newWriteOffsetInBytes)); - if (ma_rb_pointer_distance(pRB) == 0) { - return MA_AT_END; - } else { - return MA_SUCCESS; - } + return MA_SUCCESS; } MA_API ma_result ma_rb_seek_read(ma_rb* pRB, size_t offsetInBytes) From fcddfe62047313d6a0d95514ca66826d1c1cecc0 Mon Sep 17 00:00:00 2001 From: David Reid Date: Sat, 11 Jan 2025 16:30:15 +1000 Subject: [PATCH 12/13] Update ma_pcm_rb data source implementation. The data source implementation of a ma_pcm_rb could possibly return a frame count of 0 which would in turn result in ma_data_source_read_pcm_frames() returning MA_AT_END which does not make sense for a ring buffer since it has no notion of an end. --- CHANGES.md | 1 + examples/hilo_interop.c | 3 --- miniaudio.h | 10 ++++++++++ 3 files changed, 11 insertions(+), 3 deletions(-) diff --git a/CHANGES.md b/CHANGES.md index c27fe154..07c49095 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -2,6 +2,7 @@ v0.11.22 - TBD ===================== * Add `MA_SOUND_FLAG_LOOPING` and `MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_LOOPING` flags. These can be used to initialize sounds and resource managed data sources to loop by default. This is the recommended way to enable looping for streams. The `isLooping` config option in `ma_sound_config` and `ma_resource_manager_data_source_config` has been deprecated. If you are using those, you should switch to the new flag or else you'll get compiler errors when upgrading to a future version. * `ma_rb_commit_read()`, `ma_rb_commit_write()`, `ma_pcm_rb_commit_read()` and `ma_pcm_rb_commit_write()` no longer return `MA_AT_END`. The reason for this change is that there's no real notion of an "end" in a ring buffer which makes this result code confusing. In addition, it's possible for these functions to return something other than `MA_SUCCESS` when the operation completed successfully which adds to the confusion. The correct way to check if there is any more room in the ring buffer is to look at the frame count returned by `*rb_acquire_read/write()`. +* The `ma_pcm_rb` data source implementation has been modified to pad output data with silence if there is not enough data in the ring buffer to fill the request. What this means is `ma_data_source_read_pcm_frames()` should no longer return a frame count of less than what you requested, and will therefore never return `MA_AT_END` which does not make sense for a ring buffer since it does not have the notion of an end. * Fix a bug relating to node detachment. * Fix a bug where amplification with `ma_device_set_master_volume()` does not work. * Fix a bug where sounds loaded with `MA_SOUND_FLAG_DECODE` do not loop. diff --git a/examples/hilo_interop.c b/examples/hilo_interop.c index 994e3ab9..1fde9293 100644 --- a/examples/hilo_interop.c +++ b/examples/hilo_interop.c @@ -125,9 +125,6 @@ int main(int argc, char** argv) return -1; } - /* Make sure the sound is set to looping or else it'll stop if the ring buffer runs out of data. */ - ma_sound_set_looping(&sound, MA_TRUE); - /* Link the starting of the device and sound together. */ ma_device_start(&device); ma_sound_start(&sound); diff --git a/miniaudio.h b/miniaudio.h index 19acbe66..8e69ace0 100644 --- a/miniaudio.h +++ b/miniaudio.h @@ -56897,6 +56897,16 @@ static ma_result ma_pcm_rb_data_source__on_read(ma_data_source* pDataSource, voi totalFramesRead += mappedFrameCount; } + /* + There is no notion of an "end" in a ring buffer. If we didn't have enough data to fill the requested frame + count we'll need to pad with silence. If we don't do this, totalFramesRead might equal 0 which will result + in the data source layer at a higher level translating this to MA_AT_END which is incorrect for a ring buffer. + */ + if (totalFramesRead < frameCount) { + ma_silence_pcm_frames(ma_offset_pcm_frames_ptr(pFramesOut, totalFramesRead, pRB->format, pRB->channels), (frameCount - totalFramesRead), pRB->format, pRB->channels); + totalFramesRead = frameCount; + } + *pFramesRead = totalFramesRead; return MA_SUCCESS; } From 3081e314b786d4bb50407bfe7d7c3ec3000321a2 Mon Sep 17 00:00:00 2001 From: David Reid Date: Sat, 11 Jan 2025 16:47:30 +1000 Subject: [PATCH 13/13] Update change history. --- CHANGES.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGES.md b/CHANGES.md index 07c49095..fe8d5fb6 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -2,7 +2,7 @@ v0.11.22 - TBD ===================== * Add `MA_SOUND_FLAG_LOOPING` and `MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_LOOPING` flags. These can be used to initialize sounds and resource managed data sources to loop by default. This is the recommended way to enable looping for streams. The `isLooping` config option in `ma_sound_config` and `ma_resource_manager_data_source_config` has been deprecated. If you are using those, you should switch to the new flag or else you'll get compiler errors when upgrading to a future version. * `ma_rb_commit_read()`, `ma_rb_commit_write()`, `ma_pcm_rb_commit_read()` and `ma_pcm_rb_commit_write()` no longer return `MA_AT_END`. The reason for this change is that there's no real notion of an "end" in a ring buffer which makes this result code confusing. In addition, it's possible for these functions to return something other than `MA_SUCCESS` when the operation completed successfully which adds to the confusion. The correct way to check if there is any more room in the ring buffer is to look at the frame count returned by `*rb_acquire_read/write()`. -* The `ma_pcm_rb` data source implementation has been modified to pad output data with silence if there is not enough data in the ring buffer to fill the request. What this means is `ma_data_source_read_pcm_frames()` should no longer return a frame count of less than what you requested, and will therefore never return `MA_AT_END` which does not make sense for a ring buffer since it does not have the notion of an end. +* The `ma_pcm_rb` data source implementation has been modified to pad output data with silence if there is not enough data in the ring buffer to fill the request. What this means is for an `ma_pcm_rb`, `ma_data_source_read_pcm_frames()` should no longer return a frame count of less than what you requested, and will therefore never return `MA_AT_END` which does not make sense for a ring buffer since it does not have the notion of an end. This change should make it much easier to use a ring buffer as the data source for a `ma_sound`. * Fix a bug relating to node detachment. * Fix a bug where amplification with `ma_device_set_master_volume()` does not work. * Fix a bug where sounds loaded with `MA_SOUND_FLAG_DECODE` do not loop.