From 9b0c9bdb59494bb44730b854a899035976fe97bc Mon Sep 17 00:00:00 2001 From: David Reid Date: Fri, 21 Feb 2025 10:49:14 +1000 Subject: [PATCH] Update website. --- docs/api/index.html | 2 +- docs/examples/custom_backend.html | 42 +-- docs/examples/custom_decoder.html | 180 +-------- docs/examples/custom_decoder_engine.html | 177 +-------- docs/examples/data_source_chaining.html | 21 +- docs/examples/duplex_effect.html | 15 +- docs/examples/engine_advanced.html | 4 +- docs/examples/engine_effects.html | 7 +- docs/examples/engine_hello_world.html | 4 +- docs/examples/engine_sdl.html | 8 +- docs/examples/engine_steamaudio.html | 48 ++- docs/examples/hilo_interop.html | 11 +- docs/examples/index.html | 6 +- docs/examples/node_graph.html | 7 +- docs/examples/resource_manager.html | 5 +- docs/examples/resource_manager_advanced.html | 23 +- docs/examples/simple_capture.html | 9 +- docs/examples/simple_duplex.html | 10 +- docs/examples/simple_enumeration.html | 6 +- docs/examples/simple_loopback.html | 9 +- docs/examples/simple_looping.html | 4 +- docs/examples/simple_mixing.html | 12 +- docs/examples/simple_playback.html | 4 +- docs/examples/simple_playback_sine.html | 4 +- docs/examples/simple_spatialization.html | 363 +++++++++++++++++++ docs/index.html | 2 +- docs/manual/index.html | 165 ++++++--- index.html | 4 +- 28 files changed, 639 insertions(+), 513 deletions(-) create mode 100644 docs/examples/simple_spatialization.html diff --git a/docs/api/index.html b/docs/api/index.html index 16bb5116..86e2002e 100644 --- a/docs/api/index.html +++ b/docs/api/index.html @@ -263,7 +263,7 @@ Coming soon...
- Copyright © 2023 David Reid
+ Copyright © 2025 David Reid
Developed by David Reid - mackron@gmail.com
diff --git a/docs/examples/custom_backend.html b/docs/examples/custom_backend.html index 849d0221..3944e324 100644 --- a/docs/examples/custom_backend.html +++ b/docs/examples/custom_backend.html @@ -248,7 +248,7 @@ a.doc-navigation-l4 {
+} +
+Documentation HomeProgramming ManualExamplesCustom BackendCustom DecoderCustom Decoder EngineData Source ChainingDuplex EffectEngine AdvancedEngine EffectsEngine Hello WorldEngine SdlEngine SteamaudioHilo InteropNode GraphResource ManagerResource Manager AdvancedSimple CaptureSimple DuplexSimple EnumerationSimple LoopbackSimple LoopingSimple MixingSimple PlaybackSimple Playback SineSimple Playback SineSimple SpatializationAPI Reference

Custom Backend

This example show how a custom backend can be implemented.

@@ -439,9 +439,6 @@ ma_format ma_format_from_sdl(MA_SDL_AudioFormat format) ma_bool32 cbResult; int iDevice; - MA_ASSERT(pContext != NULL); - MA_ASSERT(callback != NULL); - /* Playback */ if (!isTerminated) { int deviceCount = ((MA_PFN_SDL_GetNumAudioDevices)pContextEx->sdl.SDL_GetNumAudioDevices)(0); @@ -500,8 +497,6 @@ ma_format ma_format_from_sdl(MA_SDL_AudioFormat format) const char* pDeviceName; #endif - MA_ASSERT(pContext != NULL); - if (pDeviceID == NULL) { if (deviceType == ma_device_type_playback) { pDeviceInfo->id.custom.i = 0; @@ -525,7 +520,7 @@ ma_format ma_format_from_sdl(MA_SDL_AudioFormat format) of the device's _actual_ ideal format. Note: With Emscripten, it looks like non-zero values need to be specified for desiredSpec. Whatever is specified in - desiredSpec will be used by SDL since it uses it just does it's own format conversion internally. Therefore, from what + desiredSpec will be used by SDL since it uses it just does its own format conversion internally. Therefore, from what I can tell, there's no real way to know the device's actual format which means I'm just going to fall back to the full range of channels and sample rates on Emscripten builds. */ @@ -581,8 +576,6 @@ ma_format ma_format_from_sdl(MA_SDL_AudioFormat format) { ma_device_ex* pDeviceEx = (ma_device_ex*)pUserData; - MA_ASSERT(pDeviceEx != NULL); - ma_device_handle_backend_data_callback((ma_device*)pDeviceEx, NULL, pBuffer, (ma_uint32)bufferSizeInBytes / ma_get_bytes_per_frame(pDeviceEx->device.capture.internalFormat, pDeviceEx->device.capture.internalChannels)); } @@ -590,8 +583,6 @@ ma_format ma_format_from_sdl(MA_SDL_AudioFormat format) { ma_device_ex* pDeviceEx = (ma_device_ex*)pUserData; - MA_ASSERT(pDeviceEx != NULL); - ma_device_handle_backend_data_callback((ma_device*)pDeviceEx, pBuffer, NULL, (ma_uint32)bufferSizeInBytes / ma_get_bytes_per_frame(pDeviceEx->device.playback.internalFormat, pDeviceEx->device.playback.internalChannels)); } @@ -603,9 +594,6 @@ ma_format ma_format_from_sdl(MA_SDL_AudioFormat format) const char* pDeviceName; int deviceID; - MA_ASSERT(pDeviceEx != NULL); - MA_ASSERT(pDescriptor != NULL); - /* SDL is a little bit awkward with specifying the buffer size, You need to specify the size of the buffer in frames, but since we may have requested a period size in milliseconds we'll need to convert, which depends on the sample rate. But there's a possibility that @@ -689,8 +677,6 @@ ma_format ma_format_from_sdl(MA_SDL_AudioFormat format) ma_context_ex* pContextEx = (ma_context_ex*)pDevice->pContext; ma_result result; - MA_ASSERT(pDevice != NULL); - /* SDL does not support loopback mode, so must return MA_DEVICE_TYPE_NOT_SUPPORTED if it's requested. */ if (pConfig->deviceType == ma_device_type_loopback) { return MA_DEVICE_TYPE_NOT_SUPPORTED; @@ -722,8 +708,6 @@ ma_format ma_format_from_sdl(MA_SDL_AudioFormat format) ma_device_ex* pDeviceEx = (ma_device_ex*)pDevice; ma_context_ex* pContextEx = (ma_context_ex*)pDevice->pContext; - MA_ASSERT(pDevice != NULL); - if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) { ((MA_PFN_SDL_CloseAudioDevice)pContextEx->sdl.SDL_CloseAudioDevice)(pDeviceEx->sdl.deviceIDCapture); } @@ -740,8 +724,6 @@ ma_format ma_format_from_sdl(MA_SDL_AudioFormat format) ma_device_ex* pDeviceEx = (ma_device_ex*)pDevice; ma_context_ex* pContextEx = (ma_context_ex*)pDevice->pContext; - MA_ASSERT(pDevice != NULL); - if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) { ((MA_PFN_SDL_PauseAudioDevice)pContextEx->sdl.SDL_PauseAudioDevice)(pDeviceEx->sdl.deviceIDCapture, 0); } @@ -758,8 +740,6 @@ ma_format ma_format_from_sdl(MA_SDL_AudioFormat format) ma_device_ex* pDeviceEx = (ma_device_ex*)pDevice; ma_context_ex* pContextEx = (ma_context_ex*)pDevice->pContext; - MA_ASSERT(pDevice != NULL); - if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) { ((MA_PFN_SDL_PauseAudioDevice)pContextEx->sdl.SDL_PauseAudioDevice)(pDeviceEx->sdl.deviceIDCapture, 1); } @@ -775,8 +755,6 @@ ma_format ma_format_from_sdl(MA_SDL_AudioFormat format) { ma_context_ex* pContextEx = (ma_context_ex*)pContext; - MA_ASSERT(pContext != NULL); - ((MA_PFN_SDL_QuitSubSystem)pContextEx->sdl.SDL_QuitSubSystem)(MA_SDL_INIT_AUDIO); /* Close the handle to the SDL shared object last. */ @@ -804,8 +782,6 @@ ma_format ma_format_from_sdl(MA_SDL_AudioFormat format) #endif }; - MA_ASSERT(pContext != NULL); - (void)pConfig; /* Check if we have SDL2 installed somewhere. If not it's not usable and we need to abort. */ @@ -900,15 +876,8 @@ Main program starts here. void data_callback(ma_device* pDevice, void* pOutput, const void* pInput, ma_uint32 frameCount) { - MA_ASSERT(pDevice->playback.channels == DEVICE_CHANNELS); - if (pDevice->type == ma_device_type_playback) { - ma_waveform* pSineWave; - - pSineWave = (ma_waveform*)pDevice->pUserData; - MA_ASSERT(pSineWave != NULL); - - ma_waveform_read_pcm_frames(pSineWave, pOutput, frameCount, NULL); + ma_waveform_read_pcm_frames((ma_waveform*)pDevice->pUserData, pOutput, frameCount, NULL); } if (pDevice->type == ma_device_type_duplex) { @@ -996,7 +965,8 @@ Main program starts here. (void)argv; return 0; -}
@@ -1009,7 +979,7 @@ Main program starts here.
- Copyright © 2023 David Reid
+ Copyright © 2025 David Reid
Developed by David Reid - mackron@gmail.com
diff --git a/docs/examples/custom_decoder.html b/docs/examples/custom_decoder.html index 037dddc8..6efafaaa 100644 --- a/docs/examples/custom_decoder.html +++ b/docs/examples/custom_decoder.html @@ -248,7 +248,7 @@ a.doc-navigation-l4 {
+} +
+Documentation HomeProgramming ManualExamplesCustom BackendCustom DecoderCustom Decoder EngineData Source ChainingDuplex EffectEngine AdvancedEngine EffectsEngine Hello WorldEngine SdlEngine SteamaudioHilo InteropNode GraphResource ManagerResource Manager AdvancedSimple CaptureSimple DuplexSimple EnumerationSimple LoopbackSimple LoopingSimple MixingSimple PlaybackSimple Playback SineSimple Playback SineSimple SpatializationAPI Reference

Custom Decoder

Demonstrates how to implement a custom decoder.

@@ -275,167 +275,16 @@ the decoder via the decoder config (ma_deco of your custom decoders. See ma_decoding_backend_vtable for the functions you need to implement. The onInitFile, onInitFileW and onInitMemory functions are optional.

-#define MA_NO_VORBIS    /* Disable the built-in Vorbis decoder to ensure the libvorbis decoder is picked. */
-#define MA_NO_OPUS      /* Disable the (not yet implemented) built-in Opus decoder to ensure the libopus decoder is picked. */
-#define MINIAUDIO_IMPLEMENTATION
-#include "../miniaudio.h"
-#include "../extras/miniaudio_libvorbis.h"
-#include "../extras/miniaudio_libopus.h"
+/*
+For now these need to be declared before miniaudio.c due to some compatibility issues with the old
+MINIAUDIO_IMPLEMENTATION system. This will change from version 0.12.
+*/
+#include "../extras/decoders/libvorbis/miniaudio_libvorbis.c"
+#include "../extras/decoders/libopus/miniaudio_libopus.c"
+#include "../miniaudio.c"
 
 #include <stdio.h>
 
-static ma_result ma_decoding_backend_init__libvorbis(void* pUserData, ma_read_proc onRead, ma_seek_proc onSeek, ma_tell_proc onTell, void* pReadSeekTellUserData, const ma_decoding_backend_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_data_source** ppBackend)
-{
-    ma_result result;
-    ma_libvorbis* pVorbis;
-
-    (void)pUserData;
-
-    pVorbis = (ma_libvorbis*)ma_malloc(sizeof(*pVorbis), pAllocationCallbacks);
-    if (pVorbis == NULL) {
-        return MA_OUT_OF_MEMORY;
-    }
-
-    result = ma_libvorbis_init(onRead, onSeek, onTell, pReadSeekTellUserData, pConfig, pAllocationCallbacks, pVorbis);
-    if (result != MA_SUCCESS) {
-        ma_free(pVorbis, pAllocationCallbacks);
-        return result;
-    }
-
-    *ppBackend = pVorbis;
-
-    return MA_SUCCESS;
-}
-
-static ma_result ma_decoding_backend_init_file__libvorbis(void* pUserData, const char* pFilePath, const ma_decoding_backend_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_data_source** ppBackend)
-{
-    ma_result result;
-    ma_libvorbis* pVorbis;
-
-    (void)pUserData;
-
-    pVorbis = (ma_libvorbis*)ma_malloc(sizeof(*pVorbis), pAllocationCallbacks);
-    if (pVorbis == NULL) {
-        return MA_OUT_OF_MEMORY;
-    }
-
-    result = ma_libvorbis_init_file(pFilePath, pConfig, pAllocationCallbacks, pVorbis);
-    if (result != MA_SUCCESS) {
-        ma_free(pVorbis, pAllocationCallbacks);
-        return result;
-    }
-
-    *ppBackend = pVorbis;
-
-    return MA_SUCCESS;
-}
-
-static void ma_decoding_backend_uninit__libvorbis(void* pUserData, ma_data_source* pBackend, const ma_allocation_callbacks* pAllocationCallbacks)
-{
-    ma_libvorbis* pVorbis = (ma_libvorbis*)pBackend;
-
-    (void)pUserData;
-
-    ma_libvorbis_uninit(pVorbis, pAllocationCallbacks);
-    ma_free(pVorbis, pAllocationCallbacks);
-}
-
-static ma_result ma_decoding_backend_get_channel_map__libvorbis(void* pUserData, ma_data_source* pBackend, ma_channel* pChannelMap, size_t channelMapCap)
-{
-    ma_libvorbis* pVorbis = (ma_libvorbis*)pBackend;
-
-    (void)pUserData;
-
-    return ma_libvorbis_get_data_format(pVorbis, NULL, NULL, NULL, pChannelMap, channelMapCap);
-}
-
-static ma_decoding_backend_vtable g_ma_decoding_backend_vtable_libvorbis =
-{
-    ma_decoding_backend_init__libvorbis,
-    ma_decoding_backend_init_file__libvorbis,
-    NULL, /* onInitFileW() */
-    NULL, /* onInitMemory() */
-    ma_decoding_backend_uninit__libvorbis
-};
-
-
-
-static ma_result ma_decoding_backend_init__libopus(void* pUserData, ma_read_proc onRead, ma_seek_proc onSeek, ma_tell_proc onTell, void* pReadSeekTellUserData, const ma_decoding_backend_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_data_source** ppBackend)
-{
-    ma_result result;
-    ma_libopus* pOpus;
-
-    (void)pUserData;
-
-    pOpus = (ma_libopus*)ma_malloc(sizeof(*pOpus), pAllocationCallbacks);
-    if (pOpus == NULL) {
-        return MA_OUT_OF_MEMORY;
-    }
-
-    result = ma_libopus_init(onRead, onSeek, onTell, pReadSeekTellUserData, pConfig, pAllocationCallbacks, pOpus);
-    if (result != MA_SUCCESS) {
-        ma_free(pOpus, pAllocationCallbacks);
-        return result;
-    }
-
-    *ppBackend = pOpus;
-
-    return MA_SUCCESS;
-}
-
-static ma_result ma_decoding_backend_init_file__libopus(void* pUserData, const char* pFilePath, const ma_decoding_backend_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_data_source** ppBackend)
-{
-    ma_result result;
-    ma_libopus* pOpus;
-
-    (void)pUserData;
-
-    pOpus = (ma_libopus*)ma_malloc(sizeof(*pOpus), pAllocationCallbacks);
-    if (pOpus == NULL) {
-        return MA_OUT_OF_MEMORY;
-    }
-
-    result = ma_libopus_init_file(pFilePath, pConfig, pAllocationCallbacks, pOpus);
-    if (result != MA_SUCCESS) {
-        ma_free(pOpus, pAllocationCallbacks);
-        return result;
-    }
-
-    *ppBackend = pOpus;
-
-    return MA_SUCCESS;
-}
-
-static void ma_decoding_backend_uninit__libopus(void* pUserData, ma_data_source* pBackend, const ma_allocation_callbacks* pAllocationCallbacks)
-{
-    ma_libopus* pOpus = (ma_libopus*)pBackend;
-
-    (void)pUserData;
-
-    ma_libopus_uninit(pOpus, pAllocationCallbacks);
-    ma_free(pOpus, pAllocationCallbacks);
-}
-
-static ma_result ma_decoding_backend_get_channel_map__libopus(void* pUserData, ma_data_source* pBackend, ma_channel* pChannelMap, size_t channelMapCap)
-{
-    ma_libopus* pOpus = (ma_libopus*)pBackend;
-
-    (void)pUserData;
-
-    return ma_libopus_get_data_format(pOpus, NULL, NULL, NULL, pChannelMap, channelMapCap);
-}
-
-static ma_decoding_backend_vtable g_ma_decoding_backend_vtable_libopus =
-{
-    ma_decoding_backend_init__libopus,
-    ma_decoding_backend_init_file__libopus,
-    NULL, /* onInitFileW() */
-    NULL, /* onInitMemory() */
-    ma_decoding_backend_uninit__libopus
-};
-
-
-
 
 void data_callback(ma_device* pDevice, void* pOutput, const void* pInput, ma_uint32 frameCount)
 {
@@ -464,10 +313,10 @@ The onInitFile, const ma_decoding_backend_vtable* pCustomBackendVTables[] =
     {
-        &g_ma_decoding_backend_vtable_libvorbis,
-        &g_ma_decoding_backend_vtable_libopus
+        ma_decoding_backend_libvorbis,
+        ma_decoding_backend_libopus
     };
 
 
@@ -479,7 +328,7 @@ The onInitFile, /* Initialize the decoder. */
     decoderConfig = ma_decoder_config_init_default();
-    decoderConfig.pCustomBackendUserData = NULL;  /* In this example our backend objects are contained within a ma_decoder_ex object to avoid a malloc. Our vtables need to know about this. */
+    decoderConfig.pCustomBackendUserData = NULL;  /* None of our decoders require user data, so this can be set to null. */
     decoderConfig.ppCustomBackendVTables = pCustomBackendVTables;
     decoderConfig.customBackendCount     = sizeof(pCustomBackendVTables) / sizeof(pCustomBackendVTables[0]);
     
@@ -527,7 +376,8 @@ The onInitFile, return 0;
-}
@@ -540,7 +390,7 @@ The onInitFile, - Copyright © 2023 David Reid
+ Copyright © 2025 David Reid
Developed by David Reid - mackron@gmail.com diff --git a/docs/examples/custom_decoder_engine.html b/docs/examples/custom_decoder_engine.html index b80f69c7..a0062ec3 100644 --- a/docs/examples/custom_decoder_engine.html +++ b/docs/examples/custom_decoder_engine.html @@ -248,7 +248,7 @@ a.doc-navigation-l4 {
+} +
+Documentation HomeProgramming ManualExamplesCustom BackendCustom DecoderCustom Decoder EngineData Source ChainingDuplex EffectEngine AdvancedEngine EffectsEngine Hello WorldEngine SdlEngine SteamaudioHilo InteropNode GraphResource ManagerResource Manager AdvancedSimple CaptureSimple DuplexSimple EnumerationSimple LoopbackSimple LoopingSimple MixingSimple PlaybackSimple Playback SineSimple Playback SineSimple SpatializationAPI Reference

Custom Decoder Engine

Demonstrates how to implement a custom decoder and use it with the high level API.

@@ -258,166 +258,16 @@ This is the same as the custom_decoder example, only it's used with the high rather than the low level decoding API. You can use this to add support for Opus to your games, for example (via libopus).

-#define MA_NO_VORBIS    /* Disable the built-in Vorbis decoder to ensure the libvorbis decoder is picked. */
-#define MA_NO_OPUS      /* Disable the (not yet implemented) built-in Opus decoder to ensure the libopus decoder is picked. */
-#define MINIAUDIO_IMPLEMENTATION
-#include "../miniaudio.h"
-#include "../extras/miniaudio_libvorbis.h"
-#include "../extras/miniaudio_libopus.h"
+/*
+For now these need to be declared before miniaudio.c due to some compatibility issues with the old
+MINIAUDIO_IMPLEMENTATION system. This will change from version 0.12.
+*/
+#include "../extras/decoders/libvorbis/miniaudio_libvorbis.c"
+#include "../extras/decoders/libopus/miniaudio_libopus.c"
+#include "../miniaudio.c"
 
 #include <stdio.h>
 
-static ma_result ma_decoding_backend_init__libvorbis(void* pUserData, ma_read_proc onRead, ma_seek_proc onSeek, ma_tell_proc onTell, void* pReadSeekTellUserData, const ma_decoding_backend_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_data_source** ppBackend)
-{
-    ma_result result;
-    ma_libvorbis* pVorbis;
-
-    (void)pUserData;
-
-    pVorbis = (ma_libvorbis*)ma_malloc(sizeof(*pVorbis), pAllocationCallbacks);
-    if (pVorbis == NULL) {
-        return MA_OUT_OF_MEMORY;
-    }
-
-    result = ma_libvorbis_init(onRead, onSeek, onTell, pReadSeekTellUserData, pConfig, pAllocationCallbacks, pVorbis);
-    if (result != MA_SUCCESS) {
-        ma_free(pVorbis, pAllocationCallbacks);
-        return result;
-    }
-
-    *ppBackend = pVorbis;
-
-    return MA_SUCCESS;
-}
-
-static ma_result ma_decoding_backend_init_file__libvorbis(void* pUserData, const char* pFilePath, const ma_decoding_backend_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_data_source** ppBackend)
-{
-    ma_result result;
-    ma_libvorbis* pVorbis;
-
-    (void)pUserData;
-
-    pVorbis = (ma_libvorbis*)ma_malloc(sizeof(*pVorbis), pAllocationCallbacks);
-    if (pVorbis == NULL) {
-        return MA_OUT_OF_MEMORY;
-    }
-
-    result = ma_libvorbis_init_file(pFilePath, pConfig, pAllocationCallbacks, pVorbis);
-    if (result != MA_SUCCESS) {
-        ma_free(pVorbis, pAllocationCallbacks);
-        return result;
-    }
-
-    *ppBackend = pVorbis;
-
-    return MA_SUCCESS;
-}
-
-static void ma_decoding_backend_uninit__libvorbis(void* pUserData, ma_data_source* pBackend, const ma_allocation_callbacks* pAllocationCallbacks)
-{
-    ma_libvorbis* pVorbis = (ma_libvorbis*)pBackend;
-
-    (void)pUserData;
-
-    ma_libvorbis_uninit(pVorbis, pAllocationCallbacks);
-    ma_free(pVorbis, pAllocationCallbacks);
-}
-
-static ma_result ma_decoding_backend_get_channel_map__libvorbis(void* pUserData, ma_data_source* pBackend, ma_channel* pChannelMap, size_t channelMapCap)
-{
-    ma_libvorbis* pVorbis = (ma_libvorbis*)pBackend;
-
-    (void)pUserData;
-
-    return ma_libvorbis_get_data_format(pVorbis, NULL, NULL, NULL, pChannelMap, channelMapCap);
-}
-
-static ma_decoding_backend_vtable g_ma_decoding_backend_vtable_libvorbis =
-{
-    ma_decoding_backend_init__libvorbis,
-    ma_decoding_backend_init_file__libvorbis,
-    NULL, /* onInitFileW() */
-    NULL, /* onInitMemory() */
-    ma_decoding_backend_uninit__libvorbis
-};
-
-
-
-static ma_result ma_decoding_backend_init__libopus(void* pUserData, ma_read_proc onRead, ma_seek_proc onSeek, ma_tell_proc onTell, void* pReadSeekTellUserData, const ma_decoding_backend_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_data_source** ppBackend)
-{
-    ma_result result;
-    ma_libopus* pOpus;
-
-    (void)pUserData;
-
-    pOpus = (ma_libopus*)ma_malloc(sizeof(*pOpus), pAllocationCallbacks);
-    if (pOpus == NULL) {
-        return MA_OUT_OF_MEMORY;
-    }
-
-    result = ma_libopus_init(onRead, onSeek, onTell, pReadSeekTellUserData, pConfig, pAllocationCallbacks, pOpus);
-    if (result != MA_SUCCESS) {
-        ma_free(pOpus, pAllocationCallbacks);
-        return result;
-    }
-
-    *ppBackend = pOpus;
-
-    return MA_SUCCESS;
-}
-
-static ma_result ma_decoding_backend_init_file__libopus(void* pUserData, const char* pFilePath, const ma_decoding_backend_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_data_source** ppBackend)
-{
-    ma_result result;
-    ma_libopus* pOpus;
-
-    (void)pUserData;
-
-    pOpus = (ma_libopus*)ma_malloc(sizeof(*pOpus), pAllocationCallbacks);
-    if (pOpus == NULL) {
-        return MA_OUT_OF_MEMORY;
-    }
-
-    result = ma_libopus_init_file(pFilePath, pConfig, pAllocationCallbacks, pOpus);
-    if (result != MA_SUCCESS) {
-        ma_free(pOpus, pAllocationCallbacks);
-        return result;
-    }
-
-    *ppBackend = pOpus;
-
-    return MA_SUCCESS;
-}
-
-static void ma_decoding_backend_uninit__libopus(void* pUserData, ma_data_source* pBackend, const ma_allocation_callbacks* pAllocationCallbacks)
-{
-    ma_libopus* pOpus = (ma_libopus*)pBackend;
-
-    (void)pUserData;
-
-    ma_libopus_uninit(pOpus, pAllocationCallbacks);
-    ma_free(pOpus, pAllocationCallbacks);
-}
-
-static ma_result ma_decoding_backend_get_channel_map__libopus(void* pUserData, ma_data_source* pBackend, ma_channel* pChannelMap, size_t channelMapCap)
-{
-    ma_libopus* pOpus = (ma_libopus*)pBackend;
-
-    (void)pUserData;
-
-    return ma_libopus_get_data_format(pOpus, NULL, NULL, NULL, pChannelMap, channelMapCap);
-}
-
-static ma_decoding_backend_vtable g_ma_decoding_backend_vtable_libopus =
-{
-    ma_decoding_backend_init__libopus,
-    ma_decoding_backend_init_file__libopus,
-    NULL, /* onInitFileW() */
-    NULL, /* onInitMemory() */
-    ma_decoding_backend_uninit__libopus
-};
-
-
 
 int main(int argc, char** argv)
 {
@@ -431,10 +281,10 @@ example (via libopus).

Add your custom backend vtables here. The order in the array defines the order of priority. The vtables will be passed in to the resource manager config. */ - ma_decoding_backend_vtable* pCustomBackendVTables[] = + const ma_decoding_backend_vtable* pCustomBackendVTables[] = { - &g_ma_decoding_backend_vtable_libvorbis, - &g_ma_decoding_backend_vtable_libopus + ma_decoding_backend_libvorbis, + ma_decoding_backend_libopus }; @@ -480,7 +330,8 @@ example (via libopus).

getchar(); return 0; -}
@@ -493,7 +344,7 @@ example (via libopus).

- Copyright © 2023 David Reid
+ Copyright © 2025 David Reid
Developed by David Reid - mackron@gmail.com
diff --git a/docs/examples/data_source_chaining.html b/docs/examples/data_source_chaining.html index d6602115..fc6deba8 100644 --- a/docs/examples/data_source_chaining.html +++ b/docs/examples/data_source_chaining.html @@ -248,7 +248,7 @@ a.doc-navigation-l4 {
+} +
+Documentation HomeProgramming ManualExamplesCustom BackendCustom DecoderCustom Decoder EngineData Source ChainingDuplex EffectEngine AdvancedEngine EffectsEngine Hello WorldEngine SdlEngine SteamaudioHilo InteropNode GraphResource ManagerResource Manager AdvancedSimple CaptureSimple DuplexSimple EnumerationSimple LoopbackSimple LoopingSimple MixingSimple PlaybackSimple Playback SineSimple Playback SineSimple SpatializationAPI Reference

Data Source Chaining

Demonstrates one way to chain together a number of data sources so they play back seamlessly without gaps. @@ -314,7 +314,11 @@ For simplicity, this example requires the device to use floating point samples. static ma_data_source* next_callback_tail(ma_data_source* pDataSource) { - MA_ASSERT(g_decoderCount > 0); /* <-- We check for this in main() so should never happen. */ + (void)pDataSource; /* Unused. */ + + if (g_decoderCount > 0) { /* <-- We check for this in main() so should never happen. */ + return NULL; + } /* This will be fired when the last item in the chain has reached the end. In this example we want @@ -396,15 +400,15 @@ For simplicity, this example requires the device to use floating point samples. deviceConfig.dataCallback = data_callback; deviceConfig.pUserData = NULL; - if (ma_device_init(NULL, &deviceConfig, &device) != MA_SUCCESS) { + result = ma_device_init(NULL, &deviceConfig, &device); + if (result != MA_SUCCESS) { printf("Failed to open playback device.\n"); - result = -1; goto done_decoders; } - if (ma_device_start(&device) != MA_SUCCESS) { + result = ma_device_start(&device); + if (result != MA_SUCCESS) { printf("Failed to start playback device.\n"); - result = -1; goto done; } @@ -421,7 +425,8 @@ done_decoders: free(g_pDecoders); return 0; -}

@@ -434,7 +439,7 @@ done_decoders:
- Copyright © 2023 David Reid
+ Copyright © 2025 David Reid
Developed by David Reid - mackron@gmail.com
diff --git a/docs/examples/duplex_effect.html b/docs/examples/duplex_effect.html index 46b29a1d..979fb4b7 100644 --- a/docs/examples/duplex_effect.html +++ b/docs/examples/duplex_effect.html @@ -248,7 +248,7 @@ a.doc-navigation-l4 {
+Documentation HomeProgramming ManualExamplesCustom BackendCustom DecoderCustom Decoder EngineData Source ChainingDuplex EffectEngine AdvancedEngine EffectsEngine Hello WorldEngine SdlEngine SteamaudioHilo InteropNode GraphResource ManagerResource Manager AdvancedSimple CaptureSimple DuplexSimple EnumerationSimple LoopbackSimple LoopingSimple MixingSimple PlaybackSimple Playback SineSimple Playback SineSimple SpatializationAPI Reference

Duplex Effect

Demonstrates how to apply an effect to a duplex stream using the node graph system.

@@ -265,7 +265,7 @@ effect.

#include <stdio.h> -#define DEVICE_FORMAT ma_format_f32; /* Must always be f32 for this example because the node graph system only works with this. */ +#define DEVICE_FORMAT ma_format_f32 /* Must always be f32 for this example because the node graph system only works with this. */ #define DEVICE_CHANNELS 1 /* For this example, always set to 1. */ static ma_waveform g_sourceData; /* The underlying data source of the source node. */ @@ -277,8 +277,13 @@ effect.

void data_callback(ma_device* pDevice, void* pOutput, const void* pInput, ma_uint32 frameCount) { - MA_ASSERT(pDevice->capture.format == pDevice->playback.format); - MA_ASSERT(pDevice->capture.channels == pDevice->playback.channels); + /* + This example assumes the playback and capture sides use the same format and channel count. The + format must be f32. + */ + if (pDevice->capture.format != DEVICE_FORMAT || pDevice->playback.format != DEVICE_FORMAT || pDevice->capture.channels != pDevice->playback.channels) { + return; + } /* The node graph system is a pulling style of API. At the lowest level of the chain will be a @@ -412,7 +417,7 @@ done0: ma_device_uninit(&device);
- Copyright © 2023 David Reid
+ Copyright © 2025 David Reid
Developed by David Reid - mackron@gmail.com
diff --git a/docs/examples/engine_advanced.html b/docs/examples/engine_advanced.html index 9bb6a47b..479bf2c8 100644 --- a/docs/examples/engine_advanced.html +++ b/docs/examples/engine_advanced.html @@ -248,7 +248,7 @@ a.doc-navigation-l4 {
+Documentation HomeProgramming ManualExamplesCustom BackendCustom DecoderCustom Decoder EngineData Source ChainingDuplex EffectEngine AdvancedEngine EffectsEngine Hello WorldEngine SdlEngine SteamaudioHilo InteropNode GraphResource ManagerResource Manager AdvancedSimple CaptureSimple DuplexSimple EnumerationSimple LoopbackSimple LoopingSimple MixingSimple PlaybackSimple Playback SineSimple Playback SineSimple SpatializationAPI Reference

Engine Advanced

This example demonstrates some of the advanced features of the high level engine API.

@@ -524,7 +524,7 @@ might be a local co-op multiplayer game where each player has their own headphon
- Copyright © 2023 David Reid
+ Copyright © 2025 David Reid
Developed by David Reid - mackron@gmail.com
diff --git a/docs/examples/engine_effects.html b/docs/examples/engine_effects.html index 08a71d90..cf787fad 100644 --- a/docs/examples/engine_effects.html +++ b/docs/examples/engine_effects.html @@ -248,7 +248,7 @@ a.doc-navigation-l4 {
+} +
+Documentation HomeProgramming ManualExamplesCustom BackendCustom DecoderCustom Decoder EngineData Source ChainingDuplex EffectEngine AdvancedEngine EffectsEngine Hello WorldEngine SdlEngine SteamaudioHilo InteropNode GraphResource ManagerResource Manager AdvancedSimple CaptureSimple DuplexSimple EnumerationSimple LoopbackSimple LoopingSimple MixingSimple PlaybackSimple Playback SineSimple Playback SineSimple SpatializationAPI Reference

Engine Effects

Demonstrates how to apply an effect to sounds using the high level engine API.

@@ -358,7 +358,8 @@ sound file, you need multiple ma_soundreturn 0; -}
@@ -371,7 +372,7 @@ sound file, you need multiple ma_sound
- Copyright © 2023 David Reid
+ Copyright © 2025 David Reid
Developed by David Reid - mackron@gmail.com
diff --git a/docs/examples/engine_hello_world.html b/docs/examples/engine_hello_world.html index d7f1b35b..d356a72a 100644 --- a/docs/examples/engine_hello_world.html +++ b/docs/examples/engine_hello_world.html @@ -248,7 +248,7 @@ a.doc-navigation-l4 {
+Documentation HomeProgramming ManualExamplesCustom BackendCustom DecoderCustom Decoder EngineData Source ChainingDuplex EffectEngine AdvancedEngine EffectsEngine Hello WorldEngine SdlEngine SteamaudioHilo InteropNode GraphResource ManagerResource Manager AdvancedSimple CaptureSimple DuplexSimple EnumerationSimple LoopbackSimple LoopingSimple MixingSimple PlaybackSimple Playback SineSimple Playback SineSimple SpatializationAPI Reference

Engine Hello World

This example demonstrates how to initialize an audio engine and play a sound.

@@ -299,7 +299,7 @@ This will play the sound specified on the command line.

- Copyright © 2023 David Reid
+ Copyright © 2025 David Reid
Developed by David Reid - mackron@gmail.com
diff --git a/docs/examples/engine_sdl.html b/docs/examples/engine_sdl.html index c381af14..0acf1c36 100644 --- a/docs/examples/engine_sdl.html +++ b/docs/examples/engine_sdl.html @@ -248,7 +248,7 @@ a.doc-navigation-l4 {
+Documentation HomeProgramming ManualExamplesCustom BackendCustom DecoderCustom Decoder EngineData Source ChainingDuplex EffectEngine AdvancedEngine EffectsEngine Hello WorldEngine SdlEngine SteamaudioHilo InteropNode GraphResource ManagerResource Manager AdvancedSimple CaptureSimple DuplexSimple EnumerationSimple LoopbackSimple LoopingSimple MixingSimple PlaybackSimple Playback SineSimple Playback SineSimple SpatializationAPI Reference

Engine Sdl

Shows how to use the high level engine API with SDL.

@@ -268,7 +268,7 @@ head.

#include "../miniaudio.h" #define SDL_MAIN_HANDLED -#include <SDL.h> /* Change this to your include location. Might be <SDL2/SDL.h>. */ +#include <SDL2/SDL.h> /* Change this to your include location. Might be <SDL.h>. */ #define CHANNELS 2 /* Must be stereo for this example. */ #define SAMPLE_RATE 48000 @@ -278,6 +278,8 @@ head.

void data_callback(void* pUserData, ma_uint8* pBuffer, int bufferSizeInBytes) { + (void)pUserData; + /* Reading is just a matter of reading straight from the engine. */ ma_uint32 bufferSizeInFrames = (ma_uint32)bufferSizeInBytes / ma_get_bytes_per_frame(ma_format_f32, ma_engine_get_channels(&g_engine)); ma_engine_read_pcm_frames(&g_engine, pBuffer, bufferSizeInFrames, NULL); @@ -399,7 +401,7 @@ head.

- Copyright © 2023 David Reid
+ Copyright © 2025 David Reid
Developed by David Reid - mackron@gmail.com
diff --git a/docs/examples/engine_steamaudio.html b/docs/examples/engine_steamaudio.html index 43f9183a..1c8f46ac 100644 --- a/docs/examples/engine_steamaudio.html +++ b/docs/examples/engine_steamaudio.html @@ -248,7 +248,7 @@ a.doc-navigation-l4 {
+} +
+Documentation HomeProgramming ManualExamplesCustom BackendCustom DecoderCustom Decoder EngineData Source ChainingDuplex EffectEngine AdvancedEngine EffectsEngine Hello WorldEngine SdlEngine SteamaudioHilo InteropNode GraphResource ManagerResource Manager AdvancedSimple CaptureSimple DuplexSimple EnumerationSimple LoopbackSimple LoopingSimple MixingSimple PlaybackSimple Playback SineSimple Playback SineSimple SpatializationAPI Reference

Engine Steamaudio

Demonstrates integration of Steam Audio with miniaudio's engine API.

@@ -265,23 +265,17 @@ channel count of this node is always stereo.

Steam Audio requires fixed sized processing, the size of which must be specified at initialization -time of the IPLBinauralEffect and IPLHRTF objects. This creates a problem because the node graph -will at times need to break down processing into smaller chunks for it's internal processing. The -node graph internally will read into a temporary buffer which is then mixed into the final output -buffer. This temporary buffer is allocated on the stack and is a fixed size. However, variability -comes into play because the channel count of the node is variable. It's not safe to just blindly -process the effect with the frame count specified in miniaudio's node processing callback. Doing so -results in glitching. To work around this, this example is just setting the update size to a known -value that works (256). If it's set to something too big it'll exceed miniaudio's processing size -used by the node graph. Alternatively you could use some kind of intermediary cache which -accumulates input data until enough is available and then do the processing. Ideally, Steam Audio -would support variable sized updates which would avoid this whole mess entirely.

+time of the IPLBinauralEffect and IPLHRTF objects. To ensure miniaudio and Steam Audio are +consistent, you must set the period size in the engine config to be consistent with the frame size +you specify in your IPLAudioSettings object. If for some reason you want the period size of the +engine to be different to that of your Steam Audio configuration, you'll need to implement a sort +of buffering solution to your node.

 #define MINIAUDIO_IMPLEMENTATION
 #include "../miniaudio.h"
 
+#include <stdint.h> /* Required for uint32_t which is used by STEAMAUDIO_VERSION, and a random use of uint8_t. If there's a Steam Audio maintainer reading this, that needs to be fixed to use IPLuint32 and IPLuint8. */
 #include <phonon.h> /* Steam Audio */
-#include <stdint.h> /* Required for uint32_t which is used by STEAMAUDIO_VERSION. That dependency needs to be removed from Steam Audio - use IPLuint32 or "unsigned int" instead! */
 
 #define FORMAT      ma_format_f32   /* Must be floating point. */
 #define CHANNELS    2               /* Must be stereo for this example. */
@@ -355,6 +349,7 @@ MA_API ma_steamaudio_binaural_node_config ma_steamaudio_binaural_node_config_ini
     ma_uint32 totalFramesToProcess = *pFrameCountOut;
     ma_uint32 totalFramesProcessed = 0;
 
+    MA_ZERO_OBJECT(&binauralParams);
     binauralParams.direction.x   = pBinauralNode->direction.x;
     binauralParams.direction.y   = pBinauralNode->direction.y;
     binauralParams.direction.z   = pBinauralNode->direction.z;
@@ -380,7 +375,7 @@ MA_API ma_steamaudio_binaural_node_config ma_steamaudio_binaural_node_config_ini
             pBinauralNode->ppBuffersIn[0] = (float*)ma_offset_pcm_frames_const_ptr_f32(ppFramesIn[0], totalFramesProcessed, 1);
         } else {
             /* Slow path. Need to deinterleave the input data. */
-            ma_deinterleave_pcm_frames(ma_format_f32, inputBufferDesc.numChannels, framesToProcessThisIteration, ma_offset_pcm_frames_const_ptr_f32(ppFramesIn[0], totalFramesProcessed, inputBufferDesc.numChannels), pBinauralNode->ppBuffersIn);
+            ma_deinterleave_pcm_frames(ma_format_f32, inputBufferDesc.numChannels, framesToProcessThisIteration, ma_offset_pcm_frames_const_ptr_f32(ppFramesIn[0], totalFramesProcessed, inputBufferDesc.numChannels), (void**)&pBinauralNode->ppBuffersIn[0]);
         }
 
         inputBufferDesc.data       = pBinauralNode->ppBuffersIn;
@@ -390,7 +385,7 @@ MA_API ma_steamaudio_binaural_node_config ma_steamaudio_binaural_node_config_ini
         iplBinauralEffectApply(pBinauralNode->iplEffect, &binauralParams, &inputBufferDesc, &outputBufferDesc);
 
         /* Interleave straight into the output buffer. */
-        ma_interleave_pcm_frames(ma_format_f32, 2, framesToProcessThisIteration, pBinauralNode->ppBuffersOut, ma_offset_pcm_frames_ptr_f32(ppFramesOut[0], totalFramesProcessed, 2));
+        ma_interleave_pcm_frames(ma_format_f32, 2, framesToProcessThisIteration, (const void**)&pBinauralNode->ppBuffersOut[0], ma_offset_pcm_frames_ptr_f32(ppFramesOut[0], totalFramesProcessed, 2));
 
         /* Advance. */
         totalFramesProcessed += framesToProcessThisIteration;
@@ -541,8 +536,16 @@ MA_API ma_result ma_steamaudio_binaural_node_
 
     /* The engine needs to be initialized first. */
     engineConfig = ma_engine_config_init();
-    engineConfig.channels           = CHANNELS;
-    engineConfig.sampleRate         = SAMPLE_RATE;
+    engineConfig.channels   = CHANNELS;
+    engineConfig.sampleRate = SAMPLE_RATE;
+
+    /*
+    Steam Audio requires processing in fixed sized chunks. Setting the period size in the engine config will
+    ensure our updates happen in predicably sized chunks as required by Steam Audio.
+
+    Note that the configuration of Steam Audio below (IPLAudioSettings) will use this variable to specify the
+    update size to ensure it remains consistent.
+    */
     engineConfig.periodSizeInFrames = 256;
 
     result = ma_engine_init(&engineConfig, &g_engine);
@@ -562,6 +565,9 @@ MA_API ma_result ma_steamaudio_binaural_node_
     be documented. If this is for some kind of buffer management with FFT or something, then this
     need not be exposed to the public API. There should be no need for the public API to require a
     fixed sized update.
+
+    It's important that this be set to the periodSizeInFrames specified in the engine config above.
+    This ensures updates on both the miniaudio side and the Steam Audio side are consistent.
     */
     iplAudioSettings.frameSize = engineConfig.periodSizeInFrames;
 
@@ -579,7 +585,8 @@ MA_API ma_result ma_steamaudio_binaural_node_
 
     /* IPLHRTF */
     MA_ZERO_OBJECT(&iplHRTFSettings);
-    iplHRTFSettings.type = IPL_HRTFTYPE_DEFAULT;
+    iplHRTFSettings.type   = IPL_HRTFTYPE_DEFAULT;
+    iplHRTFSettings.volume = 1;
 
     result = ma_result_from_IPLerror(iplHRTFCreate(iplContext, &iplAudioSettings, &iplHRTFSettings, &iplHRTF));
     if (result != MA_SUCCESS) {
@@ -686,7 +693,8 @@ MA_API ma_result ma_steamaudio_binaural_node_
     ma_engine_uninit(&g_engine);
 
     return 0;
-}
@@ -699,7 +707,7 @@ MA_API ma_result ma_steamaudio_binaural_node_
- Copyright © 2023 David Reid
+ Copyright © 2025 David Reid
Developed by David Reid - mackron@gmail.com
diff --git a/docs/examples/hilo_interop.html b/docs/examples/hilo_interop.html index b13ffa2b..19c3ad61 100644 --- a/docs/examples/hilo_interop.html +++ b/docs/examples/hilo_interop.html @@ -248,7 +248,7 @@ a.doc-navigation-l4 {
+Documentation HomeProgramming ManualExamplesCustom BackendCustom DecoderCustom Decoder EngineData Source ChainingDuplex EffectEngine AdvancedEngine EffectsEngine Hello WorldEngine SdlEngine SteamaudioHilo InteropNode GraphResource ManagerResource Manager AdvancedSimple CaptureSimple DuplexSimple EnumerationSimple LoopbackSimple LoopingSimple MixingSimple PlaybackSimple Playback SineSimple Playback SineSimple SpatializationAPI Reference

Hilo Interop

Demonstrates interop between the high-level and the low-level API.

@@ -285,6 +285,8 @@ may be updated to make use of a more advanced data source that handles all of th ma_result result; ma_uint32 framesWritten; + (void)pFramesOut; + /* We need to write to the ring buffer. Need to do this in a loop. */ framesWritten = 0; while (framesWritten < frameCount) { @@ -301,7 +303,7 @@ may be updated to make use of a more advanced data source that handles all of th } /* Copy the data from the capture buffer to the ring buffer. */ - ma_copy_pcm_frames(pMappedBuffer, ma_offset_pcm_frames_const_ptr_f32(pFramesIn, framesWritten, pDevice->capture.channels), framesToWrite, pDevice->capture.format, pDevice->capture.channels); + ma_copy_pcm_frames(pMappedBuffer, ma_offset_pcm_frames_const_ptr_f32((const float*)pFramesIn, framesWritten, pDevice->capture.channels), framesToWrite, pDevice->capture.format, pDevice->capture.channels); result = ma_pcm_rb_commit_write(&rb, framesToWrite); if (result != MA_SUCCESS) { @@ -382,9 +384,6 @@ may be updated to make use of a more advanced data source that handles all of th 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); @@ -416,7 +415,7 @@ may be updated to make use of a more advanced data source that handles all of th
- Copyright © 2023 David Reid
+ Copyright © 2025 David Reid
Developed by David Reid - mackron@gmail.com
diff --git a/docs/examples/index.html b/docs/examples/index.html index 3fc12ac5..5a1169c3 100644 --- a/docs/examples/index.html +++ b/docs/examples/index.html @@ -248,10 +248,10 @@ a.doc-navigation-l4 {
+without gaps.
+Documentation HomeProgramming ManualExamplesCustom BackendCustom DecoderCustom Decoder EngineData Source ChainingDuplex EffectEngine AdvancedEngine EffectsEngine Hello WorldEngine SdlEngine SteamaudioHilo InteropNode GraphResource ManagerResource Manager AdvancedSimple CaptureSimple DuplexSimple EnumerationSimple LoopbackSimple LoopingSimple MixingSimple PlaybackSimple Playback SineSimple Playback SineSimple SpatializationAPI Reference
Custom BackendThis example show how a custom backend can be implemented.
Custom DecoderDemonstrates how to implement a custom decoder.
Custom Decoder EngineDemonstrates how to implement a custom decoder and use it with the high level API.
Data Source ChainingDemonstrates one way to chain together a number of data sources so they play back seamlessly -without gaps.
Duplex EffectDemonstrates how to apply an effect to a duplex stream using the node graph system.
Engine AdvancedThis example demonstrates some of the advanced features of the high level engine API.
Engine EffectsDemonstrates how to apply an effect to sounds using the high level engine API.
Engine Hello WorldThis example demonstrates how to initialize an audio engine and play a sound.
Engine SdlShows how to use the high level engine API with SDL.
Engine SteamaudioDemonstrates integration of Steam Audio with miniaudio's engine API.
Hilo InteropDemonstrates interop between the high-level and the low-level API.
Node GraphThis example shows how to use the node graph system.
Resource ManagerDemonstrates how you can use the resource manager to manage loaded sounds.
Resource Manager AdvancedDemonstrates how you can use the resource manager to manage loaded sounds.
Simple CaptureDemonstrates how to capture data from a microphone using the low-level API.
Simple DuplexDemonstrates duplex mode which is where data is captured from a microphone and then output to a speaker device.
Simple EnumerationDemonstrates how to enumerate over devices.
Simple LoopbackDemonstrates how to implement loopback recording.
Simple LoopingShows one way to handle looping of a sound.
Simple MixingDemonstrates one way to load multiple files and play them all back at the same time.
Simple PlaybackDemonstrates how to load a sound file and play it back using the low-level API.
Simple Playback SineDemonstrates playback of a sine wave.
Simple Playback SineDemonstrates playback of a sine wave.
Duplex EffectDemonstrates how to apply an effect to a duplex stream using the node graph system.
Engine AdvancedThis example demonstrates some of the advanced features of the high level engine API.
Engine EffectsDemonstrates how to apply an effect to sounds using the high level engine API.
Engine Hello WorldThis example demonstrates how to initialize an audio engine and play a sound.
Engine SdlShows how to use the high level engine API with SDL.
Engine SteamaudioDemonstrates integration of Steam Audio with miniaudio's engine API.
Hilo InteropDemonstrates interop between the high-level and the low-level API.
Node GraphThis example shows how to use the node graph system.
Resource ManagerDemonstrates how you can use the resource manager to manage loaded sounds.
Resource Manager AdvancedDemonstrates how you can use the resource manager to manage loaded sounds.
Simple CaptureDemonstrates how to capture data from a microphone using the low-level API.
Simple DuplexDemonstrates duplex mode which is where data is captured from a microphone and then output to a speaker device.
Simple EnumerationDemonstrates how to enumerate over devices.
Simple LoopbackDemonstrates how to implement loopback recording.
Simple LoopingShows one way to handle looping of a sound.
Simple MixingDemonstrates one way to load multiple files and play them all back at the same time.
Simple PlaybackDemonstrates how to load a sound file and play it back using the low-level API.
Simple Playback SineDemonstrates playback of a sine wave.
Simple Playback SineDemonstrates playback of a sine wave.
Simple SpatializationDemonstrates how to do basic spatialization via the high level API.
@@ -264,7 +264,7 @@ without gaps.
- Copyright © 2023 David Reid
+ Copyright © 2025 David Reid
Developed by David Reid -
mackron@gmail.com diff --git a/docs/examples/node_graph.html b/docs/examples/node_graph.html index 5c32c561..d62ef4de 100644 --- a/docs/examples/node_graph.html +++ b/docs/examples/node_graph.html @@ -248,7 +248,7 @@ a.doc-navigation-l4 {
+Documentation HomeProgramming ManualExamplesCustom BackendCustom DecoderCustom Decoder EngineData Source ChainingDuplex EffectEngine AdvancedEngine EffectsEngine Hello WorldEngine SdlEngine SteamaudioHilo InteropNode GraphResource ManagerResource Manager AdvancedSimple CaptureSimple DuplexSimple EnumerationSimple LoopbackSimple LoopingSimple MixingSimple PlaybackSimple Playback SineSimple Playback SineSimple SpatializationAPI Reference

Node Graph

This example shows how to use the node graph system.

@@ -351,8 +351,6 @@ When you want to read from the graph, you simply call void data_callback(ma_device* pDevice, void* pOutput, const void* pInput, ma_uint32 frameCount) { - MA_ASSERT(pDevice->playback.channels == CHANNELS); - /* Hearing the output of the node graph is as easy as reading straight into the output buffer. You just need to make sure you use a consistent data format or else you'll need to do your own conversion. @@ -360,6 +358,7 @@ When you want to read from the graph, you simply call void)pInput; /* Unused. */ + (void)pDevice; /* Unused. */ } int main(int argc, char** argv) @@ -531,7 +530,7 @@ cleanup_graph:
- Copyright © 2023 David Reid
+ Copyright © 2025 David Reid
Developed by David Reid - mackron@gmail.com
diff --git a/docs/examples/resource_manager.html b/docs/examples/resource_manager.html index adedcb92..d6b7d499 100644 --- a/docs/examples/resource_manager.html +++ b/docs/examples/resource_manager.html @@ -248,7 +248,7 @@ a.doc-navigation-l4 {
+Documentation HomeProgramming ManualExamplesCustom BackendCustom DecoderCustom Decoder EngineData Source ChainingDuplex EffectEngine AdvancedEngine EffectsEngine Hello WorldEngine SdlEngine SteamaudioHilo InteropNode GraphResource ManagerResource Manager AdvancedSimple CaptureSimple DuplexSimple EnumerationSimple LoopbackSimple LoopingSimple MixingSimple PlaybackSimple Playback SineSimple Playback SineSimple SpatializationAPI Reference

Resource Manager

Demonstrates how you can use the resource manager to manage loaded sounds.

@@ -298,7 +298,6 @@ set, each sound will have their own formats and you'll need to do the necess void main_loop__em(void* pUserData) { ma_resource_manager* pResourceManager = (ma_resource_manager*)pUserData; - MA_ASSERT(pResourceManager != NULL); /* The Emscripten build does not support threading which means we need to process jobs manually. If @@ -427,7 +426,7 @@ set, each sound will have their own formats and you'll need to do the necess
- Copyright © 2023 David Reid
+ Copyright © 2025 David Reid
Developed by David Reid - mackron@gmail.com
diff --git a/docs/examples/resource_manager_advanced.html b/docs/examples/resource_manager_advanced.html index 06bc0f85..09a28bab 100644 --- a/docs/examples/resource_manager_advanced.html +++ b/docs/examples/resource_manager_advanced.html @@ -248,7 +248,7 @@ a.doc-navigation-l4 {
+Documentation HomeProgramming ManualExamplesCustom BackendCustom DecoderCustom Decoder EngineData Source ChainingDuplex EffectEngine AdvancedEngine EffectsEngine Hello WorldEngine SdlEngine SteamaudioHilo InteropNode GraphResource ManagerResource Manager AdvancedSimple CaptureSimple DuplexSimple EnumerationSimple LoopbackSimple LoopingSimple MixingSimple PlaybackSimple Playback SineSimple Playback SineSimple SpatializationAPI Reference

Resource Manager Advanced

Demonstrates how you can use the resource manager to manage loaded sounds.

@@ -291,8 +291,6 @@ being merged into miniaudio.h (it's currently in miniaudio_engine.h). This function is intended to be used when the format and channel count of the data source is known beforehand. The idea is to avoid overhead due to redundant calls to ma_data_source_get_data_format(). */ - MA_ASSERT(pDataSource != NULL); - if (dataSourceFormat == ma_format_f32) { /* Fast path. No conversion necessary. */ return ma_data_source_read_pcm_frames(pDataSource, pFramesOut, frameCount, pFramesRead); @@ -371,7 +369,7 @@ MA_API ma_result ma_data_source_read_pcm_fram result = ma_data_source_read_pcm_frames_f32_ex(pDataSource, temp, framesToRead, &framesJustRead, format, channels); - ma_mix_pcm_frames_f32(ma_offset_pcm_frames_ptr(pFramesOut, totalFramesRead, ma_format_f32, channels), temp, framesJustRead, channels, volume); + ma_mix_pcm_frames_f32(ma_offset_pcm_frames_ptr_f32(pFramesOut, totalFramesRead, channels), temp, framesJustRead, channels, volume); totalFramesRead += framesJustRead; if (result != MA_SUCCESS) { @@ -395,10 +393,6 @@ MA_API ma_result ma_data_source_read_pcm_fram */ ma_uint32 iDataSource; - MA_ASSERT(pDevice->playback.format == ma_format_f32); - - (void)pInput; /* Unused. */ - /* If the device was configured with noPreSilencedOutputBuffer then you would need to silence the buffer here, or make sure the first data source to be mixed is copied rather than mixed. @@ -409,12 +403,15 @@ MA_API ma_result ma_data_source_read_pcm_fram for (iDataSource = 0; iDataSource < g_dataSourceCount; iDataSource += 1) { ma_data_source_read_pcm_frames_and_mix_f32(&g_dataSources[iDataSource], (float*)pOutput, frameCount, NULL, /* volume = */1); } + + /* Unused. */ + (void)pInput; + (void)pDevice; } static ma_thread_result MA_THREADCALL custom_job_thread(void* pUserData) { ma_resource_manager* pResourceManager = (ma_resource_manager*)pUserData; - MA_ASSERT(pResourceManager != NULL); for (;;) { ma_result result; @@ -450,8 +447,8 @@ MA_API ma_result ma_data_source_read_pcm_fram event is received which means the result != MA_SUCCESS logic above will catch it. If you do not check the return value of ma_resource_manager_next_job() you will want to check for MA_RESOURCE_MANAGER_JOB_QUIT like the code below. */ - if (job.toc.breakup.code == MA_RESOURCE_MANAGER_JOB_QUIT) { - printf("CUSTOM JOB THREAD TERMINATING VIA MA_RESOURCE_MANAGER_JOB_QUIT... "); + if (job.toc.breakup.code == MA_JOB_TYPE_QUIT) { + printf("CUSTOM JOB THREAD TERMINATING VIA MA_JOB_TYPE_QUIT... "); break; } @@ -528,7 +525,7 @@ MA_API ma_result ma_data_source_read_pcm_fram ma_thread_create(&jobThread, ma_thread_priority_default, 0, custom_job_thread, &resourceManager, NULL); /* Create each data source from the resource manager. Note that the caller is the owner. */ - for (iFile = 0; iFile < ma_countof(g_dataSources) && iFile < argc-1; iFile += 1) { + for (iFile = 0; iFile < (int)ma_countof(g_dataSources) && iFile < argc-1; iFile += 1) { result = ma_resource_manager_data_source_init( &resourceManager, argv[iFile+1], @@ -599,7 +596,7 @@ MA_API ma_result ma_data_source_read_pcm_fram
- Copyright © 2023 David Reid
+ Copyright © 2025 David Reid
Developed by David Reid - mackron@gmail.com
diff --git a/docs/examples/simple_capture.html b/docs/examples/simple_capture.html index 3b6c7653..a1a1bfca 100644 --- a/docs/examples/simple_capture.html +++ b/docs/examples/simple_capture.html @@ -248,7 +248,7 @@ a.doc-navigation-l4 {
+Documentation HomeProgramming ManualExamplesCustom BackendCustom DecoderCustom Decoder EngineData Source ChainingDuplex EffectEngine AdvancedEngine EffectsEngine Hello WorldEngine SdlEngine SteamaudioHilo InteropNode GraphResource ManagerResource Manager AdvancedSimple CaptureSimple DuplexSimple EnumerationSimple LoopbackSimple LoopingSimple MixingSimple PlaybackSimple Playback SineSimple Playback SineSimple SpatializationAPI Reference

Simple Capture

Demonstrates how to capture data from a microphone using the low-level API.

@@ -271,10 +271,7 @@ data received by the microphone straight to a WAV file.

void data_callback(ma_device* pDevice, void* pOutput, const void* pInput, ma_uint32 frameCount) { - ma_encoder* pEncoder = (ma_encoder*)pDevice->pUserData; - MA_ASSERT(pEncoder != NULL); - - ma_encoder_write_pcm_frames(pEncoder, pInput, frameCount, NULL); + ma_encoder_write_pcm_frames((ma_encoder*)pDevice->pUserData, pInput, frameCount, NULL); (void)pOutput; } @@ -340,7 +337,7 @@ data received by the microphone straight to a WAV file.

- Copyright © 2023 David Reid
+ Copyright © 2025 David Reid
Developed by David Reid - mackron@gmail.com
diff --git a/docs/examples/simple_duplex.html b/docs/examples/simple_duplex.html index 5d5d1804..fb622da1 100644 --- a/docs/examples/simple_duplex.html +++ b/docs/examples/simple_duplex.html @@ -248,7 +248,7 @@ a.doc-navigation-l4 {
+Documentation HomeProgramming ManualExamplesCustom BackendCustom DecoderCustom Decoder EngineData Source ChainingDuplex EffectEngine AdvancedEngine EffectsEngine Hello WorldEngine SdlEngine SteamaudioHilo InteropNode GraphResource ManagerResource Manager AdvancedSimple CaptureSimple DuplexSimple EnumerationSimple LoopbackSimple LoopingSimple MixingSimple PlaybackSimple Playback SineSimple Playback SineSimple SpatializationAPI Reference

Simple Duplex

Demonstrates duplex mode which is where data is captured from a microphone and then output to a speaker device.

@@ -278,8 +278,10 @@ sample rate conversion for you automatically.

void data_callback(ma_device* pDevice, void* pOutput, const void* pInput, ma_uint32 frameCount) { - MA_ASSERT(pDevice->capture.format == pDevice->playback.format); - MA_ASSERT(pDevice->capture.channels == pDevice->playback.channels); + /* This example assumes the playback and capture sides use the same format and channel count. */ + if (pDevice->capture.format != pDevice->playback.format || pDevice->capture.channels != pDevice->playback.channels) { + return; + } /* In this example the format and channel count are the same for both input and output which means we can just memcpy(). */ MA_COPY_MEMORY(pOutput, pInput, frameCount * ma_get_bytes_per_frame(pDevice->capture.format, pDevice->capture.channels)); @@ -337,7 +339,7 @@ sample rate conversion for you automatically.

- Copyright © 2023 David Reid
+ Copyright © 2025 David Reid
Developed by David Reid - mackron@gmail.com
diff --git a/docs/examples/simple_enumeration.html b/docs/examples/simple_enumeration.html index 1ba2b756..73972d42 100644 --- a/docs/examples/simple_enumeration.html +++ b/docs/examples/simple_enumeration.html @@ -248,13 +248,13 @@ a.doc-navigation-l4 {
+Documentation HomeProgramming ManualExamplesCustom BackendCustom DecoderCustom Decoder EngineData Source ChainingDuplex EffectEngine AdvancedEngine EffectsEngine Hello WorldEngine SdlEngine SteamaudioHilo InteropNode GraphResource ManagerResource Manager AdvancedSimple CaptureSimple DuplexSimple EnumerationSimple LoopbackSimple LoopingSimple MixingSimple PlaybackSimple Playback SineSimple Playback SineSimple SpatializationAPI Reference

Simple Enumeration

Demonstrates how to enumerate over devices.

-Device enumaration requires a ma_context object which is initialized with ma_context_init(). Conceptually, the +Device enumeration requires a ma_context object which is initialized with ma_context_init(). Conceptually, the context sits above a device. You can have many devices to one context.

@@ -320,7 +320,7 @@ If you use device enumeration, you should explicitly specify the same context yo

- Copyright © 2023 David Reid
+ Copyright © 2025 David Reid
Developed by David Reid - mackron@gmail.com
diff --git a/docs/examples/simple_loopback.html b/docs/examples/simple_loopback.html index 06ecbbc4..24143567 100644 --- a/docs/examples/simple_loopback.html +++ b/docs/examples/simple_loopback.html @@ -248,7 +248,7 @@ a.doc-navigation-l4 {
+Documentation HomeProgramming ManualExamplesCustom BackendCustom DecoderCustom Decoder EngineData Source ChainingDuplex EffectEngine AdvancedEngine EffectsEngine Hello WorldEngine SdlEngine SteamaudioHilo InteropNode GraphResource ManagerResource Manager AdvancedSimple CaptureSimple DuplexSimple EnumerationSimple LoopbackSimple LoopingSimple MixingSimple PlaybackSimple Playback SineSimple Playback SineSimple SpatializationAPI Reference

Simple Loopback

Demonstrates how to implement loopback recording.

@@ -275,10 +275,7 @@ properties. The output buffer in the callback will be null whereas the input buf void data_callback(ma_device* pDevice, void* pOutput, const void* pInput, ma_uint32 frameCount) { - ma_encoder* pEncoder = (ma_encoder*)pDevice->pUserData; - MA_ASSERT(pEncoder != NULL); - - ma_encoder_write_pcm_frames(pEncoder, pInput, frameCount, NULL); + ma_encoder_write_pcm_frames((ma_encoder*)pDevice->pUserData, pInput, frameCount, NULL); (void)pOutput; } @@ -350,7 +347,7 @@ properties. The output buffer in the callback will be null whereas the input buf
- Copyright © 2023 David Reid
+ Copyright © 2025 David Reid
Developed by David Reid - mackron@gmail.com
diff --git a/docs/examples/simple_looping.html b/docs/examples/simple_looping.html index f86d8363..f39ee009 100644 --- a/docs/examples/simple_looping.html +++ b/docs/examples/simple_looping.html @@ -248,7 +248,7 @@ a.doc-navigation-l4 {
+Documentation HomeProgramming ManualExamplesCustom BackendCustom DecoderCustom Decoder EngineData Source ChainingDuplex EffectEngine AdvancedEngine EffectsEngine Hello WorldEngine SdlEngine SteamaudioHilo InteropNode GraphResource ManagerResource Manager AdvancedSimple CaptureSimple DuplexSimple EnumerationSimple LoopbackSimple LoopingSimple MixingSimple PlaybackSimple Playback SineSimple Playback SineSimple SpatializationAPI Reference

Simple Looping

Shows one way to handle looping of a sound.

@@ -340,7 +340,7 @@ decoder straight into ma_data_source_read_p
- Copyright © 2023 David Reid
+ Copyright © 2025 David Reid
Developed by David Reid - mackron@gmail.com
diff --git a/docs/examples/simple_mixing.html b/docs/examples/simple_mixing.html index 8758e368..30b3d623 100644 --- a/docs/examples/simple_mixing.html +++ b/docs/examples/simple_mixing.html @@ -248,7 +248,7 @@ a.doc-navigation-l4 {
+Documentation HomeProgramming ManualExamplesCustom BackendCustom DecoderCustom Decoder EngineData Source ChainingDuplex EffectEngine AdvancedEngine EffectsEngine Hello WorldEngine SdlEngine SteamaudioHilo InteropNode GraphResource ManagerResource Manager AdvancedSimple CaptureSimple DuplexSimple EnumerationSimple LoopbackSimple LoopingSimple MixingSimple PlaybackSimple Playback SineSimple Playback SineSimple SpatializationAPI Reference

Simple Mixing

Demonstrates one way to load multiple files and play them all back at the same time.

@@ -257,7 +257,7 @@ Demonstrates one way to load multiple files and play them all back at the same t When mixing multiple sounds together, you should not create multiple devices. Instead you should create only a single device and then mix your sounds together which you can do by simply summing their samples together. The simplest way to do this is to use floating point samples and use miniaudio's built-in clipper to handling clipping for you. (Clipping -is when sample are clampled to their minimum and maximum range, which for floating point is -1..1.) +is when sample are clamped to their minimum and maximum range, which for floating point is -1..1.)

@@ -284,7 +284,7 @@ For simplicity, this example requires the device to use floating point samples. ma_event g_stopEvent; /* <-- Signaled by the audio thread, waited on by the main thread. */ -ma_bool32 are_all_decoders_at_end() +ma_bool32 are_all_decoders_at_end(void) { ma_uint32 iDecoder; for (iDecoder = 0; iDecoder < g_decoderCount; ++iDecoder) { @@ -342,8 +342,7 @@ For simplicity, this example requires the device to use floating point samples. float* pOutputF32 = (float*)pOutput; ma_uint32 iDecoder; - MA_ASSERT(pDevice->playback.format == SAMPLE_FORMAT); /* <-- Important for this example. */ - + /* This example assumes the device was configured to use ma_format_f32. */ for (iDecoder = 0; iDecoder < g_decoderCount; ++iDecoder) { if (!g_pDecodersAtEnd[iDecoder]) { ma_uint32 framesRead = read_and_mix_pcm_frames_f32(&g_pDecoders[iDecoder], pOutputF32, frameCount); @@ -362,6 +361,7 @@ For simplicity, this example requires the device to use floating point samples. } (void)pInput; + (void)pDevice; } int main(int argc, char** argv) @@ -465,7 +465,7 @@ For simplicity, this example requires the device to use floating point samples.

- Copyright © 2023 David Reid
+ Copyright © 2025 David Reid
Developed by David Reid - mackron@gmail.com
diff --git a/docs/examples/simple_playback.html b/docs/examples/simple_playback.html index ff825935..9837730e 100644 --- a/docs/examples/simple_playback.html +++ b/docs/examples/simple_playback.html @@ -248,7 +248,7 @@ a.doc-navigation-l4 {
+Documentation HomeProgramming ManualExamplesCustom BackendCustom DecoderCustom Decoder EngineData Source ChainingDuplex EffectEngine AdvancedEngine EffectsEngine Hello WorldEngine SdlEngine SteamaudioHilo InteropNode GraphResource ManagerResource Manager AdvancedSimple CaptureSimple DuplexSimple EnumerationSimple LoopbackSimple LoopingSimple MixingSimple PlaybackSimple Playback SineSimple Playback SineSimple SpatializationAPI Reference

Simple Playback

Demonstrates how to load a sound file and play it back using the low-level API.

@@ -341,7 +341,7 @@ the simple_mixing example for how best to do this.

- Copyright © 2023 David Reid
+ Copyright © 2025 David Reid
Developed by David Reid - mackron@gmail.com
diff --git a/docs/examples/simple_playback_sine.html b/docs/examples/simple_playback_sine.html index 9edcb8c5..64c08c05 100644 --- a/docs/examples/simple_playback_sine.html +++ b/docs/examples/simple_playback_sine.html @@ -248,7 +248,7 @@ a.doc-navigation-l4 {
+Documentation HomeProgramming ManualExamplesCustom BackendCustom DecoderCustom Decoder EngineData Source ChainingDuplex EffectEngine AdvancedEngine EffectsEngine Hello WorldEngine SdlEngine SteamaudioHilo InteropNode GraphResource ManagerResource Manager AdvancedSimple CaptureSimple DuplexSimple EnumerationSimple LoopbackSimple LoopingSimple MixingSimple PlaybackSimple Playback SineSimple Playback SineSimple SpatializationAPI Reference

Simple Playback Sine

Demonstrates playback of a sine wave.

@@ -360,7 +360,7 @@ This example works with Emscripten.

- Copyright © 2023 David Reid
+ Copyright © 2025 David Reid
Developed by David Reid - mackron@gmail.com
diff --git a/docs/examples/simple_spatialization.html b/docs/examples/simple_spatialization.html new file mode 100644 index 00000000..1ddda063 --- /dev/null +++ b/docs/examples/simple_spatialization.html @@ -0,0 +1,363 @@ + + + + miniaudio - A single file audio playback and capture library. + + + + + + + + + + + + +
+
+ + + + + + + + + + +
+
+
+ + +
+

Simple Spatialization

+Demonstrates how to do basic spatialization via the high level API. +

+

+ +You can position and orientate sounds to create a simple spatialization effect. This example shows +how to do this. +

+

+ +In addition to positioning sounds, there is the concept of a listener. This can also be positioned +and orientated to help with spatialization. +

+

+ +This example only covers the basics to get your started. See the documentation for more detailed +information on the available features. +

+

+ +To use this example, pass in the path of a sound as the first argument. The sound will be +positioned in front of the listener, while the listener rotates on the the spot to create an +orbiting effect. Terminate the program with Ctrl+C.

+
+#define MINIAUDIO_IMPLEMENTATION
+#include "../miniaudio.h"
+
+#include <stdio.h>
+#include <math.h>   /* For sinf() and cosf() */
+
+/* Silence warning about unreachable code for MSVC. */
+#ifdef _MSC_VER
+    #pragma warning(push)
+    #pragma warning(disable: 4702)
+#endif
+
+int main(int argc, char** argv)
+{
+    ma_result result;
+    ma_engine engine;
+    ma_sound sound;
+    float listenerAngle = 0;
+
+    if (argc < 2) {
+        printf("No input file.\n");
+        return -1;
+    }
+
+    result = ma_engine_init(NULL, &engine);
+    if (result != MA_SUCCESS) {
+        printf("Failed to initialize engine.\n");
+        return -1;
+    }
+
+    result = ma_sound_init_from_file(&engine, argv[1], 0, NULL, NULL, &sound);
+    if (result != MA_SUCCESS) {
+        printf("Failed to load sound: %s\n", argv[1]);
+        ma_engine_uninit(&engine);
+        return -1;
+    }
+
+    /* This sets the position of the sound. miniaudio follows the same coordinate system as OpenGL, where -Z is forward. */
+    ma_sound_set_position(&sound, 0, 0, -1);
+
+    /*
+    This sets the position of the listener. The second parameter is the listener index. If you have only a single listener, which is
+    most likely, just use 0. The position defaults to (0,0,0).
+    */
+    ma_engine_listener_set_position(&engine, 0, 0, 0, 0);
+
+
+    /* Sounds are stopped by default. We'll start it once initial parameters have been setup. */
+    ma_sound_start(&sound);
+
+
+    /* Rotate the listener on the spot to create an orbiting effect. */
+    for (;;) {
+        listenerAngle += 0.01f;
+        ma_engine_listener_set_direction(&engine, 0, sinf(listenerAngle), 0, cosf(listenerAngle));
+
+        ma_sleep(1);
+    }
+
+
+    /* Won't actually get here, but do this to tear down. */
+    ma_sound_uninit(&sound);
+    ma_engine_uninit(&engine);
+
+    return 0;
+}
+
+#ifdef _MSC_VER
+    #pragma warning(pop)
+#endif
+
+
+ + + + + + + +
+ +
+ Copyright © 2025 David Reid
+ Developed by David Reid - mackron@gmail.com +
+ + diff --git a/docs/index.html b/docs/index.html index b0363b84..9cad8e0d 100644 --- a/docs/index.html +++ b/docs/index.html @@ -262,7 +262,7 @@ a.doc-navigation-l4 {
- Copyright © 2023 David Reid
+ Copyright © 2025 David Reid
Developed by David Reid - mackron@gmail.com
diff --git a/docs/manual/index.html b/docs/manual/index.html index fa34bcfb..a767842c 100644 --- a/docs/manual/index.html +++ b/docs/manual/index.html @@ -255,21 +255,25 @@ a.doc-navigation-l4 {

1. Introduction

-miniaudio is a single file library for audio playback and capture. To use it, do the following in -one .c file: +To use miniaudio, include "miniaudio.h":

-#define MINIAUDIO_IMPLEMENTATION
 #include "miniaudio.h"
 

-You can do #include "miniaudio.h" in other parts of the program just like any other header. +The implementation is contained in "miniaudio.c". Just compile this like any other source file. You +can include miniaudio.c if you want to compile your project as a single translation unit:

+

+
+#include "miniaudio.c"
+

+ miniaudio includes both low level and high level APIs. The low level API is good for those who want to do all of their mixing themselves and only require a light weight interface to the underlying audio device. The high level API is good for those who have complex mixing and effect requirements. @@ -699,7 +703,7 @@ avoids the same sound being loaded multiple times. The node graph is used for mixing and effect processing. The idea is that you connect a number of nodes into the graph by connecting each node's outputs to another node's inputs. Each node can -implement it's own effect. By chaining nodes together, advanced mixing and effect processing can +implement its own effect. By chaining nodes together, advanced mixing and effect processing can be achieved.

@@ -836,7 +840,7 @@ ma_sound_set_stop_time_in_milliseconds()

The start/stop time needs to be specified based on the absolute timer which is controlled by the -engine. The current global time time in PCM frames can be retrieved with +engine. The current global time in PCM frames can be retrieved with ma_engine_get_time_in_pcm_frames(). The engine's global time can be changed with ma_engine_set_time_in_pcm_frames() for synchronization purposes if required. Note that scheduling a start time still requires an explicit call to ma_sound_start() before anything will play: @@ -879,13 +883,13 @@ effect chains.

-A sound can have it's volume changed with ma_sound_set_volume(). If you prefer decibel volume +A sound can have its volume changed with ma_sound_set_volume(). If you prefer decibel volume control you can use ma_volume_db_to_linear() to convert from decibel representation to linear.

Panning and pitching is supported with ma_sound_set_pan() and ma_sound_set_pitch(). If you know -a sound will never have it's pitch changed with ma_sound_set_pitch() or via the doppler effect, +a sound will never have its pitch changed with ma_sound_set_pitch() or via the doppler effect, you can specify the MA_SOUND_FLAG_NO_PITCH flag when initializing the sound for an optimization.

@@ -969,24 +973,12 @@ through the command line requires linking to MA_NO_RUNTIME_LINKING option, like so: -

-

- -

-
-#ifdef __APPLE__
-    #define MA_NO_RUNTIME_LINKING
-#endif
-#define MINIAUDIO_IMPLEMENTATION
-#include "miniaudio.h"
-

- -This will require linking with -framework CoreFoundation -framework CoreAudio -framework AudioToolbox. -If you get errors about AudioToolbox, try with -framework AudioUnit instead. You may get this when -using older versions of iOS. Alternatively, if you would rather keep using runtime linking you can -add the following to your entitlements.xcent file: +notarization process. To fix this there are two options. The first is to compile with +-DMA_NO_RUNTIME_LINKING which in turn will require linking with +-framework CoreFoundation -framework CoreAudio -framework AudioToolbox. If you get errors about +AudioToolbox, try with -framework AudioUnit instead. You may get this when using older versions +of iOS. Alternatively, if you would rather keep using runtime linking you can add the following to +your entitlements.xcent file:

@@ -1088,7 +1080,7 @@ To run locally, you'll need to use emrun:

2.7. Build Options

-#define these options before including miniaudio.h. +#define these options before including miniaudio.c, or pass them as compiler flags:

@@ -1207,6 +1199,14 @@ Disables the Web Audio backend.

+MA_NO_CUSTOM

+ +

+Disables support for custom backends.

+ + + +

MA_NO_NULL

@@ -1355,6 +1355,16 @@ enable the Web Audio backend.

+MA_ENABLE_CUSTOM +

+ +

+Used in conjunction with MA_ENABLE_ONLY_SPECIFIC_BACKENDS to +enable custom backends.

+ + + +

MA_ENABLE_NULL

@@ -1567,6 +1577,16 @@ runtime linking via dlopen().

+MA_USE_STDINT +

+ +

+(Pass this in as a compiler flag. Do not #define this before +miniaudio.c) Forces the use of stdint.h for sized types.

+ + + +

MA_DEBUG_OUTPUT

@@ -1585,6 +1605,56 @@ Windows only. The value to pass to internal calls to

+MA_FORCE_UWP + +

+

+ +

+ +

+Windows only. Affects only the WASAPI backend. Will force the +WASAPI backend to use the UWP code path instead of the regular +desktop path. This is normally auto-detected and should rarely be +needed to be used explicitly, but can be useful for debugging.

+ + + +

+MA_ON_THREAD_ENTRY + +

+ +

+Defines some code that will be executed as soon as an internal +miniaudio-managed thread is created. This will be the first thing +to be executed by the thread entry point.

+ + + +

+MA_ON_THREAD_EXIT + +

+ +

+Defines some code that will be executed from the entry point of an +internal miniaudio-managed thread upon exit. This will be the last +thing to be executed before the thread's entry point exits.

+ + + +

+MA_THREAD_DEFAULT_STACK_SIZE +

+ +

+If set, specifies the default stack size used by miniaudio-managed +threads.

+ + + +

MA_API

@@ -2437,7 +2507,7 @@ only works for sounds that were initialized with MA_SOUND_FLAG_NO_DEFAULT_ATTACHMENT flag. This is useful if you want to set up a complex node graph.

@@ -2955,6 +3025,7 @@ MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_STREAM MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_DECODE MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_ASYNC MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_WAIT_INIT +MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_LOOPING

When no flags are specified (set to 0), the sound will be fully loaded into memory, but not @@ -2979,6 +3050,16 @@ subsequently processed in a job thread.

+The MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_LOOPING flag can be used so that the sound will loop +when it reaches the end by default. It's recommended you use this flag when you want to have a +looping streaming sound. If you try loading a very short sound as a stream, you will get a glitch. +This is because the resource manager needs to pre-fill the initial buffer at initialization time, +and if you don't specify the MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_LOOPING flag, the resource +manager will assume the sound is not looping and will stop filling the buffer when it reaches the +end, therefore resulting in a discontinuous buffer. +

+

+ For in-memory sounds, reference counting is used to ensure the data is loaded only once. This means multiple calls to ma_resource_manager_data_source_init() with the same file path will result in the file data only being loaded once. Each call to ma_resource_manager_data_source_init() must be @@ -2993,7 +3074,7 @@ actual file paths. When ma_resource_manager MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_STREAM flag), the resource manager will look for these explicitly registered data buffers and, if found, will use it as the backing data for the data source. Note that the resource manager does *not* make a copy of this data so it is up to the -caller to ensure the pointer stays valid for it's lifetime. Use +caller to ensure the pointer stays valid for its lifetime. Use ma_resource_manager_unregister_data() to unregister the self-managed data. You can also use ma_resource_manager_register_file() and ma_resource_manager_unregister_file() to register and unregister a file. It does not make sense to use the MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_STREAM @@ -3400,7 +3481,7 @@ In the above graph, it starts with two data sources whose outputs are attached t splitter node. It's at this point that the two data sources are mixed. After mixing, the splitter performs it's processing routine and produces two outputs which is simply a duplication of the input stream. One output is attached to a low pass filter, whereas the other output is attached to -a echo/delay. The outputs of the the low pass filter and the echo are attached to the endpoint, and +a echo/delay. The outputs of the low pass filter and the echo are attached to the endpoint, and since they're both connected to the same input bus, they'll be mixed.

@@ -3451,7 +3532,7 @@ result = ma_node_graph_read_pcm_frames(&nodeGraph, pFramesOut, frameCount, &

When you read audio data, miniaudio starts at the node graph's endpoint node which then pulls in -data from it's input attachments, which in turn recursively pull in data from their inputs, and so +data from its input attachments, which in turn recursively pull in data from their inputs, and so on. At the start of the graph there will be some kind of data source node which will have zero inputs and will instead read directly from a data source. The base nodes don't literally need to read from a ma_data_source object, but they will always have some kind of underlying object that @@ -3842,7 +3923,7 @@ ma_node_set_state(&splitterNode, ma_node_state_stopped); By default the node is in a started state, but since it won't be connected to anything won't actually be invoked by the node graph until it's connected. When you stop a node, data will not be -read from any of it's input connections. You can use this property to stop a group of sounds +read from any of its input connections. You can use this property to stop a group of sounds atomically.

@@ -3976,14 +4057,14 @@ chance to free the node's memory.

When the audio thread is processing a node, it does so by reading from each of the output buses of -the node. In order for a node to process data for one of it's output buses, it needs to read from -each of it's input buses, and so on an so forth. It follows that once all output buses of a node +the node. In order for a node to process data for one of its output buses, it needs to read from +each of its input buses, and so on an so forth. It follows that once all output buses of a node are detached, the node as a whole will be disconnected and no further processing will occur unless it's output buses are reattached, which won't be happening when the node is being uninitialized. By having ma_node_detach_output_bus() wait until the audio thread is finished with it, we can simplify a few things, at the expense of making ma_node_detach_output_bus() a bit slower. By doing this, the implementation of ma_node_uninit() becomes trivial - just detach all output -nodes, followed by each of the attachments to each of it's input nodes, and then do any final clean +nodes, followed by each of the attachments to each of its input nodes, and then do any final clean up.

@@ -3992,15 +4073,15 @@ With the above design, the worst-case scenario is ma_node_detach_output_bus() will stall until the audio thread is finished, which -includes the cost of recursively processing it's inputs. This is the biggest compromise made with -the approach taken by miniaudio for it's lock-free processing system. The cost of detaching nodes +includes the cost of recursively processing its inputs. This is the biggest compromise made with +the approach taken by miniaudio for its lock-free processing system. The cost of detaching nodes earlier in the pipeline (data sources, for example) will be cheaper than the cost of detaching higher level nodes, such as some kind of final post-processing endpoint. If you need to do mass detachments, detach starting from the lowest level nodes and work your way towards the final endpoint node (but don't try detaching the node graph's endpoint). If the audio thread is not running, detachment will be fast and detachment in any order will be the same. The reason nodes need to wait for their input attachments to complete is due to the potential for desyncs between -data sources. If the node was to terminate processing mid way through processing it's inputs, +data sources. If the node was to terminate processing mid way through processing its inputs, there's a chance that some of the underlying data sources will have been read, but then others not. That will then result in a potential desynchronization when detaching and reattaching higher-level nodes. A possible solution to this is to have an option when detaching to terminate processing @@ -4564,7 +4645,7 @@ weights. Custom weights can be passed in as the last parameter of

Predefined channel maps can be retrieved with ma_channel_map_init_standard(). This takes a -ma_standard_channel_map enum as it's first parameter, which can be one of the following: +ma_standard_channel_map enum as its first parameter, which can be one of the following:

@@ -4836,7 +4917,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... } @@ -5193,7 +5274,7 @@ Biquad filtering is achieved with the ma_bi

 ma_biquad_config config = ma_biquad_config_init(ma_format_f32, channels, b0, b1, b2, a0, a1, a2);
-ma_result result = ma_biquad_init(&config, &biquad);
+ma_result result = ma_biquad_init(&config, NULL, &biquad);
 if (result != MA_SUCCESS) {
     // Error.
 }
@@ -6312,7 +6393,7 @@ When compiling with VC6 and earlier, decoding is restricted to files less than 2
     
 
     
- Copyright © 2023 David Reid
+ Copyright © 2025 David Reid
Developed by David Reid - mackron@gmail.com
diff --git a/index.html b/index.html index 1b27de3c..cc59fb3c 100644 --- a/index.html +++ b/index.html @@ -351,7 +351,7 @@ a.doc-navigation-l4 { The resource manager is used for simplifying the hassle of dealing with the loading and management of your audio resources. It will reference count files so they're only loaded once and handles streaming of large audio sources to save on memory. It can even load files asynchronously and - exposes it's job system so you can handle resource management jobs from your existing job + exposes its job system so you can handle resource management jobs from your existing job infrastructure.
@@ -506,7 +506,7 @@ a.doc-navigation-l4 {
- Copyright © 2023 David Reid
+ Copyright © 2025 David Reid
Developed by David Reid - mackron@gmail.com