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 SineAPI Reference |
Custom DecoderDemonstrates how to implement a custom decoder. This example implements two custom decoders: 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 <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) { ma_data_source* pDataSource = (ma_data_source*)pDevice->pUserData; if (pDataSource == NULL) { return; } ma_data_source_read_pcm_frames(pDataSource, pOutput, frameCount, NULL); (void)pInput; } int main(int argc, char** argv) { ma_result result; ma_decoder_config decoderConfig; ma_decoder decoder; ma_device_config deviceConfig; ma_device device; ma_format format; ma_uint32 channels; 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. */ 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; } /* 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.ppCustomBackendVTables = pCustomBackendVTables; decoderConfig.customBackendCount = sizeof(pCustomBackendVTables) / sizeof(pCustomBackendVTables[0]); result = ma_decoder_init_file(argv[1], &decoderConfig, &decoder); if (result != MA_SUCCESS) { printf("Failed to initialize decoder."); return -1; } ma_data_source_set_looping(&decoder, MA_TRUE); /* Initialize the device. */ result = ma_data_source_get_data_format(&decoder, &format, &channels, &sampleRate, NULL, 0); if (result != MA_SUCCESS) { printf("Failed to retrieve decoder data format."); ma_decoder_uninit(&decoder); return -1; } deviceConfig = ma_device_config_init(ma_device_type_playback); deviceConfig.playback.format = format; deviceConfig.playback.channels = channels; deviceConfig.sampleRate = sampleRate; deviceConfig.dataCallback = data_callback; deviceConfig.pUserData = &decoder; if (ma_device_init(NULL, &deviceConfig, &device) != MA_SUCCESS) { printf("Failed to open playback device.\n"); ma_decoder_uninit(&decoder); return -1; } if (ma_device_start(&device) != MA_SUCCESS) { printf("Failed to start playback device.\n"); ma_device_uninit(&device); ma_decoder_uninit(&decoder); return -1; } printf("Press Enter to quit..."); getchar(); ma_device_uninit(&device); ma_decoder_uninit(&decoder); return 0; } |