|
Custom Decoder
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.
#include "../miniaudio.c"
#include "../extras/decoders/libvorbis/miniaudio_libvorbis.c"
#include "../extras/decoders/libopus/miniaudio_libopus.c"
#include <stdio.h>
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[] =
{
ma_decoding_backend_libvorbis,
ma_decoding_backend_libopus
};
if (argc < 2) {
printf("No input file.\n");
return -1;
}
/* Initialize the decoder. */
decoderConfig = ma_decoder_config_init_default();
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]);
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;
}
|