Files
miniaudio/examples/custom_decoder.c
T

272 lines
8.8 KiB
C

/*
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 <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. 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.
*/
ma_decoding_backend_vtable* pBackendVTables[] =
{
&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.pBackendUserData = NULL; /* None of our decoders require user data, so this can be set to null. */
decoderConfig.ppBackendVTables = pBackendVTables;
decoderConfig.backendCount = sizeof(pBackendVTables) / sizeof(pBackendVTables[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;
}