mirror of
https://github.com/mackron/miniaudio.git
synced 2026-04-22 00:06:59 +02:00
Update the custom decoder example.
This commit is contained in:
+24
-162
@@ -1,11 +1,20 @@
|
||||
/*
|
||||
Demonstrates how to implement a custom decoder.
|
||||
Demonstrates how to plug in custom decoders.
|
||||
|
||||
This example implements two custom decoders:
|
||||
|
||||
* Vorbis via libvorbis
|
||||
* Opus via libopus
|
||||
|
||||
The files miniaudio_libvorbis.h and miniaudio_libopus.h are where the custom decoders are implemented.
|
||||
Refer to these files for an example of how you can implement your own custom decoders.
|
||||
|
||||
To wire up your custom decoders to the `ma_decoder` API, you need to set up a `ma_decoder_config`
|
||||
object and fill out the `ppBackendVTables` and `backendCount` members. The `ppBackendVTables` member
|
||||
is an array of pointers to `ma_decoding_backend_vtable` objects. The order of the array defines the
|
||||
order of priority, with the first being the highest priority. The `backendCount` member is the number
|
||||
of items in the `ppBackendVTables` array.
|
||||
|
||||
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.
|
||||
@@ -15,8 +24,6 @@ the decoder via the decoder config (`ma_decoder_config`). You need to implement
|
||||
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"
|
||||
@@ -24,157 +31,6 @@ The `onInitFile`, `onInitFileW` and `onInitMemory` functions are optional.
|
||||
|
||||
#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)
|
||||
{
|
||||
@@ -200,16 +56,22 @@ int main(int argc, char** argv)
|
||||
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.
|
||||
Add your custom backend vtables here. The order in the array defines the order of priority, with the
|
||||
first being the highest priority. The vtables are 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. You should list the backends in your preferred order of priority.
|
||||
|
||||
The list below shows how you would set up your array to prioritize the custom decoders over the stock
|
||||
decoders. If you want to prioritize the stock decoders over the custom decoders, you would simply
|
||||
change the order.
|
||||
*/
|
||||
ma_decoding_backend_vtable* pBackendVTables[] =
|
||||
const ma_decoding_backend_vtable* pBackendVTables[] =
|
||||
{
|
||||
&g_ma_decoding_backend_vtable_libvorbis,
|
||||
&g_ma_decoding_backend_vtable_libopus
|
||||
ma_decoding_backend_libvorbis,
|
||||
ma_decoding_backend_libopus,
|
||||
ma_decoding_backend_wav,
|
||||
ma_decoding_backend_flac,
|
||||
ma_decoding_backend_mp3
|
||||
};
|
||||
|
||||
|
||||
|
||||
@@ -1,8 +1,11 @@
|
||||
/*
|
||||
This implements a data source that decodes Opus streams via libopus + libopusfile
|
||||
|
||||
This object can be plugged into any `ma_data_source_*()` API and can also be used as a custom
|
||||
decoding backend. See the custom_decoder example.
|
||||
The `ma_libopus` object can be plugged into any `ma_data_source_*()` API.
|
||||
|
||||
This can also be used as a custom decoding backend for `ma_decoder`. Use `ma_decoding_backend_libopus`
|
||||
to wire up the custom decoding backend to `ma_decoder`. See the custom_decoder example for an example
|
||||
of how to do this.
|
||||
|
||||
You need to include this file after miniaudio.h.
|
||||
*/
|
||||
@@ -39,6 +42,9 @@ MA_API ma_result ma_libopus_get_data_format(ma_libopus* pOpus, ma_format* pForma
|
||||
MA_API ma_result ma_libopus_get_cursor_in_pcm_frames(ma_libopus* pOpus, ma_uint64* pCursor);
|
||||
MA_API ma_result ma_libopus_get_length_in_pcm_frames(ma_libopus* pOpus, ma_uint64* pLength);
|
||||
|
||||
/* Decoding backend vtable. This is what you'll plug into ma_decoder_config.pBackendVTables. No user data required. */
|
||||
extern const ma_decoding_backend_vtable* ma_decoding_backend_libopus;
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
@@ -493,4 +499,84 @@ MA_API ma_result ma_libopus_get_length_in_pcm_frames(ma_libopus* pOpus, ma_uint6
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*
|
||||
The code below defines the vtable that you'll plug into your `ma_decoder_config` object.
|
||||
*/
|
||||
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
|
||||
};
|
||||
const ma_decoding_backend_vtable* ma_decoding_backend_libopus = &g_ma_decoding_backend_vtable_libopus;
|
||||
|
||||
#endif
|
||||
|
||||
@@ -1,8 +1,11 @@
|
||||
/*
|
||||
This implements a data source that decodes Vorbis streams via libvorbis + libvorbisfile
|
||||
|
||||
This object can be plugged into any `ma_data_source_*()` API and can also be used as a custom
|
||||
decoding backend. See the custom_decoder example.
|
||||
The `ma_libvorbis` object can be plugged into any `ma_data_source_*()` API.
|
||||
|
||||
This can also be used as a custom decoding backend for `ma_decoder`. Use `ma_decoding_backend_libvorbis`
|
||||
to wire up the custom decoding backend to `ma_decoder`. See the custom_decoder example for an example
|
||||
of how to do this.
|
||||
|
||||
You need to include this file after miniaudio.h.
|
||||
*/
|
||||
@@ -42,6 +45,9 @@ MA_API ma_result ma_libvorbis_get_data_format(ma_libvorbis* pVorbis, ma_format*
|
||||
MA_API ma_result ma_libvorbis_get_cursor_in_pcm_frames(ma_libvorbis* pVorbis, ma_uint64* pCursor);
|
||||
MA_API ma_result ma_libvorbis_get_length_in_pcm_frames(ma_libvorbis* pVorbis, ma_uint64* pLength);
|
||||
|
||||
/* Decoding backend vtable. This is what you'll plug into ma_decoder_config.pBackendVTables. No user data required. */
|
||||
extern const ma_decoding_backend_vtable* ma_decoding_backend_libvorbis;
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
@@ -513,4 +519,84 @@ MA_API ma_result ma_libvorbis_get_length_in_pcm_frames(ma_libvorbis* pVorbis, ma
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*
|
||||
The code below defines the vtable that you'll plug into your `ma_decoder_config` object.
|
||||
*/
|
||||
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
|
||||
};
|
||||
const ma_decoding_backend_vtable* ma_decoding_backend_libvorbis = &g_ma_decoding_backend_vtable_libvorbis;
|
||||
|
||||
#endif
|
||||
|
||||
Reference in New Issue
Block a user