/* 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) { 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, 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) { 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, 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 to the resource manager 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.customDecodingBackendCount = 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 play 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; }