From 8c2398b1efeb5a22aed8b9248263055fd614d33b Mon Sep 17 00:00:00 2001 From: David Reid Date: Thu, 29 Feb 2024 16:01:27 +1000 Subject: [PATCH] Update the custom decoder example. --- examples/custom_decoder.c | 186 +++++------------------------------ extras/miniaudio_libopus.h | 90 ++++++++++++++++- extras/miniaudio_libvorbis.h | 90 ++++++++++++++++- 3 files changed, 200 insertions(+), 166 deletions(-) diff --git a/examples/custom_decoder.c b/examples/custom_decoder.c index 8e919af5..b21bd31e 100644 --- a/examples/custom_decoder.c +++ b/examples/custom_decoder.c @@ -1,11 +1,20 @@ /* -Demonstrates how to implement a custom decoder. +Demonstrates how to plug in custom decoders. This example implements two custom decoders: * Vorbis via libvorbis * Opus via libopus +The files miniaudio_libvorbis.h and miniaudio_libopus.h are where the custom decoders are implemented. +Refer to these files for an example of how you can implement your own custom decoders. + +To wire up your custom decoders to the `ma_decoder` API, you need to set up a `ma_decoder_config` +object and fill out the `ppBackendVTables` and `backendCount` members. The `ppBackendVTables` member +is an array of pointers to `ma_decoding_backend_vtable` objects. The order of the array defines the +order of priority, with the first being the highest priority. The `backendCount` member is the number +of items in the `ppBackendVTables` array. + A custom decoder must implement a data source. In this example, the libvorbis data source is called `ma_libvorbis` and the Opus data source is called `ma_libopus`. These two objects are compatible with the `ma_data_source` APIs and can be taken straight from this example and used in real code. @@ -15,8 +24,6 @@ the decoder via the decoder config (`ma_decoder_config`). You need to implement 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" @@ -24,157 +31,6 @@ The `onInitFile`, `onInitFileW` and `onInitMemory` functions are optional. #include -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) { @@ -200,16 +56,22 @@ int main(int argc, char** argv) ma_uint32 sampleRate; /* - Add your custom backend vtables here. The order in the array defines the order of priority. The - vtables will be passed in via the decoder config. If you want to support stock backends in addition - to custom backends, you must add the stock backend vtables here as well. The stock backend vtables - are defined in miniaudio.h and are named `ma_decoding_backend_wav`, `ma_decoding_backend_flac`, etc. - You should list the backends in your preferred order of priority. + Add your custom backend vtables here. The order in the array defines the order of priority, with the + first being the highest priority. The vtables are be passed in via the decoder config. If you want to + support stock backends in addition to custom backends, you must add the stock backend vtables here as + well. You should list the backends in your preferred order of priority. + + The list below shows how you would set up your array to prioritize the custom decoders over the stock + decoders. If you want to prioritize the stock decoders over the custom decoders, you would simply + change the order. */ - ma_decoding_backend_vtable* pBackendVTables[] = + const ma_decoding_backend_vtable* pBackendVTables[] = { - &g_ma_decoding_backend_vtable_libvorbis, - &g_ma_decoding_backend_vtable_libopus + ma_decoding_backend_libvorbis, + ma_decoding_backend_libopus, + ma_decoding_backend_wav, + ma_decoding_backend_flac, + ma_decoding_backend_mp3 }; diff --git a/extras/miniaudio_libopus.h b/extras/miniaudio_libopus.h index d2c0ee4d..aade618f 100644 --- a/extras/miniaudio_libopus.h +++ b/extras/miniaudio_libopus.h @@ -1,8 +1,11 @@ /* This implements a data source that decodes Opus streams via libopus + libopusfile -This object can be plugged into any `ma_data_source_*()` API and can also be used as a custom -decoding backend. See the custom_decoder example. +The `ma_libopus` object can be plugged into any `ma_data_source_*()` API. + +This can also be used as a custom decoding backend for `ma_decoder`. Use `ma_decoding_backend_libopus` +to wire up the custom decoding backend to `ma_decoder`. See the custom_decoder example for an example +of how to do this. You need to include this file after miniaudio.h. */ @@ -39,6 +42,9 @@ MA_API ma_result ma_libopus_get_data_format(ma_libopus* pOpus, ma_format* pForma MA_API ma_result ma_libopus_get_cursor_in_pcm_frames(ma_libopus* pOpus, ma_uint64* pCursor); MA_API ma_result ma_libopus_get_length_in_pcm_frames(ma_libopus* pOpus, ma_uint64* pLength); +/* Decoding backend vtable. This is what you'll plug into ma_decoder_config.pBackendVTables. No user data required. */ +extern const ma_decoding_backend_vtable* ma_decoding_backend_libopus; + #ifdef __cplusplus } #endif @@ -493,4 +499,84 @@ MA_API ma_result ma_libopus_get_length_in_pcm_frames(ma_libopus* pOpus, ma_uint6 #endif } + + +/* +The code below defines the vtable that you'll plug into your `ma_decoder_config` object. +*/ +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 +}; +const ma_decoding_backend_vtable* ma_decoding_backend_libopus = &g_ma_decoding_backend_vtable_libopus; + #endif diff --git a/extras/miniaudio_libvorbis.h b/extras/miniaudio_libvorbis.h index 9a623586..6db34138 100644 --- a/extras/miniaudio_libvorbis.h +++ b/extras/miniaudio_libvorbis.h @@ -1,8 +1,11 @@ /* This implements a data source that decodes Vorbis streams via libvorbis + libvorbisfile -This object can be plugged into any `ma_data_source_*()` API and can also be used as a custom -decoding backend. See the custom_decoder example. +The `ma_libvorbis` object can be plugged into any `ma_data_source_*()` API. + +This can also be used as a custom decoding backend for `ma_decoder`. Use `ma_decoding_backend_libvorbis` +to wire up the custom decoding backend to `ma_decoder`. See the custom_decoder example for an example +of how to do this. You need to include this file after miniaudio.h. */ @@ -42,6 +45,9 @@ MA_API ma_result ma_libvorbis_get_data_format(ma_libvorbis* pVorbis, ma_format* MA_API ma_result ma_libvorbis_get_cursor_in_pcm_frames(ma_libvorbis* pVorbis, ma_uint64* pCursor); MA_API ma_result ma_libvorbis_get_length_in_pcm_frames(ma_libvorbis* pVorbis, ma_uint64* pLength); +/* Decoding backend vtable. This is what you'll plug into ma_decoder_config.pBackendVTables. No user data required. */ +extern const ma_decoding_backend_vtable* ma_decoding_backend_libvorbis; + #ifdef __cplusplus } #endif @@ -513,4 +519,84 @@ MA_API ma_result ma_libvorbis_get_length_in_pcm_frames(ma_libvorbis* pVorbis, ma #endif } + + +/* +The code below defines the vtable that you'll plug into your `ma_decoder_config` object. +*/ +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 +}; +const ma_decoding_backend_vtable* ma_decoding_backend_libvorbis = &g_ma_decoding_backend_vtable_libvorbis; + #endif