diff --git a/research/_examples/custom_decoder_engine.c b/research/_examples/custom_decoder_engine.c new file mode 100644 index 00000000..f9a560a8 --- /dev/null +++ b/research/_examples/custom_decoder_engine.c @@ -0,0 +1,252 @@ +/* +Demonstrates how to implement a custom decoder. + +This example implements two custom decoders: + + * Vorbis via libvorbis + * Opus via libopus + +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. + +The custom decoding data sources (`ma_libvorbis` and `ma_libopus` in this example) are connected to +the decoder via the decoder config (`ma_decoder_config`). You need to implement a vtable for each +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" +#include "../miniaudio_engine.h" + +#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) { + 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) { + 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, + ma_decoding_backend_get_channel_map__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) { + 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) { + 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, + ma_decoding_backend_get_channel_map__libopus +}; + + + + +void data_callback(ma_device* pDevice, void* pOutput, const void* pInput, ma_uint32 frameCount) +{ + ma_data_source* pDataSource = (ma_data_source*)pDevice->pUserData; + if (pDataSource == NULL) { + return; + } + + ma_data_source_read_pcm_frames(pDataSource, pOutput, frameCount, NULL, MA_TRUE); + + (void)pInput; +} + +int main(int argc, char** argv) +{ + ma_result result; + ma_resource_manager_config resourceManagerConfig; + ma_resource_manager resourceManager; + ma_engine_config engineConfig; + ma_engine engine; + + /* + 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. + */ + ma_decoding_backend_vtable* pCustomBackendVTables[] = + { + &g_ma_decoding_backend_vtable_libvorbis, + &g_ma_decoding_backend_vtable_libopus + }; + + + if (argc < 2) { + printf("No input file.\n"); + return -1; + } + + + /* Using custom decoding backends requires a resource manager. */ + resourceManagerConfig = ma_resource_manager_config_init(); + resourceManagerConfig.ppCustomDecodingBackendVTables = pCustomBackendVTables; + resourceManagerConfig.customDecodingBackendVTableCount = sizeof(pCustomBackendVTables) / sizeof(pCustomBackendVTables[0]); + resourceManagerConfig.pCustomDecodingBackendUserData = NULL; /* <-- This will be passed in to the pUserData parameter of each function in the decoding backend vtables. */ + + result = ma_resource_manager_init(&resourceManagerConfig, &resourceManager); + if (result != MA_SUCCESS) { + printf("Failed to initialize resource manager."); + return -1; + } + + + /* Once we have a resource manager we can create the engine. */ + engineConfig = ma_engine_config_init(); + engineConfig.pResourceManager = &resourceManager; + + result = ma_engine_init(&engineConfig, &engine); + if (result != MA_SUCCESS) { + printf("Failed to initialize engine."); + return -1; + } + + + /* Now we can start our sound. */ + result = ma_engine_play_sound(&engine, argv[1], NULL); + if (result != MA_SUCCESS) { + printf("Failed to play sound."); + return -1; + } + + + printf("Press Enter to quit..."); + getchar(); + + return 0; +} \ No newline at end of file