Update docs and add support for backend-specific userdata for decoders.

This commit is contained in:
David Reid
2024-03-01 07:06:54 +10:00
parent 2eb6f4eda2
commit 61ec4b17c6
2 changed files with 81 additions and 41 deletions
-1
View File
@@ -83,7 +83,6 @@ int main(int argc, char** argv)
/* Initialize the decoder. */ /* Initialize the decoder. */
decoderConfig = ma_decoder_config_init_default(); 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.ppBackendVTables = pBackendVTables;
decoderConfig.backendCount = sizeof(pBackendVTables) / sizeof(pBackendVTables[0]); decoderConfig.backendCount = sizeof(pBackendVTables) / sizeof(pBackendVTables[0]);
+81 -40
View File
@@ -1564,22 +1564,28 @@ formats through the use of custom decoders. To do so, pass in your `ma_decoding_
vtables into the resource manager config: vtables into the resource manager config:
```c ```c
ma_decoding_backend_vtable* pCustomBackendVTables[] = ma_decoding_backend_vtable* pBackendVTables[] =
{ {
&g_ma_decoding_backend_vtable_libvorbis, ma_decoding_backend_libvorbis, // Custom
&g_ma_decoding_backend_vtable_libopus ma_decoding_backend_libopus, // Custom
ma_decoding_backend_wav, // Stock
ma_decoding_backend_flac, // Stock
ma_decoding_backend_mp3 // Stock
}; };
... ...
resourceManagerConfig.ppCustomDecodingBackendVTables = pCustomBackendVTables; resourceManagerConfig.ppDecodingBackendVTables = pBackendVTables;
resourceManagerConfig.customDecodingBackendCount = sizeof(pCustomBackendVTables) / sizeof(pCustomBackendVTables[0]); resourceManagerConfig.decodingBackendCount = sizeof(pBackendVTables) / sizeof(pBackendVTables[0]);
resourceManagerConfig.pCustomDecodingBackendUserData = NULL;
``` ```
This system can allow you to support any kind of file format. See the "Decoding" section for Note that when specifying a decoding backend, you need to specify every backend you want to
details on how to implement custom decoders. The miniaudio repository includes examples for Opus support, including stock backends. If `resourceManagerConfig.ppDecodingBackendVTables` is set to
via libopus and libopusfile and Vorbis via libvorbis and libvorbisfile. null, only stock backends will be used, and will be in a fixed order of priority. The order in
which you list the backends in `resourceManagerConfig.ppDecodingBackendVTables` is the priority
order. This system can allow you to support any kind of file format in any priority you like. See
the "Decoding" section for details on how to implement custom decoders. The miniaudio repository
includes examples for Opus via libopus and libopusfile and Vorbis via libvorbis and libvorbisfile.
Asynchronicity is achieved via a job system. When an operation needs to be performed, such as the Asynchronicity is achieved via a job system. When an operation needs to be performed, such as the
decoding of a page, a job will be posted to a queue which will then be processed by a job thread. decoding of a page, a job will be posted to a queue which will then be processed by a job thread.
@@ -2586,20 +2592,25 @@ A custom decoder must implement a data source. A vtable called `ma_decoding_back
to be implemented which is then passed into the decoder config: to be implemented which is then passed into the decoder config:
```c ```c
ma_decoding_backend_vtable* pCustomBackendVTables[] = ma_decoding_backend_vtable* pBackendVTables[] =
{ {
&g_ma_decoding_backend_vtable_libvorbis, ma_decoding_backend_libvorbis,
&g_ma_decoding_backend_vtable_libopus ma_decoding_backend_libopus,
ma_decoding_backend_wav,
ma_decoding_backend_flac,
ma_decoding_backend_mp3
}; };
... ...
decoderConfig = ma_decoder_config_init_default(); decoderConfig = ma_decoder_config_init_default();
decoderConfig.pCustomBackendUserData = NULL; decoderConfig.ppBackendVTables = pBackendVTables;
decoderConfig.ppCustomBackendVTables = pCustomBackendVTables; decoderConfig.backendCount = sizeof(pCustomBackendVTables) / sizeof(pCustomBackendVTables[0]);
decoderConfig.customBackendCount = sizeof(pCustomBackendVTables) / sizeof(pCustomBackendVTables[0]);
``` ```
Note that when specify your own decoding backends, you must explicitly include any stock backends
you want to use, and in the order in which you want them to be tried.
The `ma_decoding_backend_vtable` vtable has the following functions: The `ma_decoding_backend_vtable` vtable has the following functions:
``` ```
@@ -2607,7 +2618,8 @@ The `ma_decoding_backend_vtable` vtable has the following functions:
onInitFile onInitFile
onInitFileW onInitFileW
onInitMemory onInitMemory
onUninit onUninit,
onGetEncodingFormat
``` ```
There are only two functions that must be implemented - `onInit` and `onUninit`. The other There are only two functions that must be implemented - `onInit` and `onUninit`. The other
@@ -2640,6 +2652,14 @@ initialization routine is clean.
When a decoder is uninitialized, the `onUninit` callback will be fired which will give you an When a decoder is uninitialized, the `onUninit` callback will be fired which will give you an
opportunity to clean up and internal data. opportunity to clean up and internal data.
The `onGetEncodingFormat` function is used to retrieve the encoding format of the data source. This
is used as a hint to help miniaudio determine which decoding backend to use. If you don't know the
encoding format, you can return `ma_encoding_format_unknown` and miniaudio will deal with it for
you through trial and error.
The miniaudio repository includes some examples of custom decoders in the "extras" folder. The
"custom_decoder" example demonstrates how to use a custom decoder with miniaudio.
9. Encoding 9. Encoding
@@ -10022,9 +10042,6 @@ typedef struct
ma_encoding_format encodingFormat; /* This is the encoding format that the caller wants to use. If set to ma_encoding_format_unknown, the decoding backend should try initializing from any of it's supported formats. */ ma_encoding_format encodingFormat; /* This is the encoding format that the caller wants to use. If set to ma_encoding_format_unknown, the decoding backend should try initializing from any of it's supported formats. */
} ma_decoding_backend_config; } ma_decoding_backend_config;
MA_API ma_decoding_backend_config ma_decoding_backend_config_init(ma_format preferredFormat, ma_uint32 seekPointCount, ma_encoding_format encodingFormat);
typedef struct typedef struct
{ {
ma_result (* onInit )(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 (* onInit )(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);
@@ -10053,8 +10070,8 @@ typedef struct
ma_encoding_format encodingFormat; ma_encoding_format encodingFormat;
ma_uint32 seekPointCount; /* When set to > 0, specifies the number of seek points to use for the generation of a seek table. Not all decoding backends support this. */ ma_uint32 seekPointCount; /* When set to > 0, specifies the number of seek points to use for the generation of a seek table. Not all decoding backends support this. */
ma_decoding_backend_vtable** ppBackendVTables; ma_decoding_backend_vtable** ppBackendVTables;
void** ppBackendUserData;
ma_uint32 backendCount; ma_uint32 backendCount;
void* pBackendUserData;
} ma_decoder_config; } ma_decoder_config;
struct ma_decoder struct ma_decoder
@@ -10566,7 +10583,7 @@ typedef struct
ma_vfs* pVFS; /* Can be NULL in which case defaults will be used. */ ma_vfs* pVFS; /* Can be NULL in which case defaults will be used. */
ma_decoding_backend_vtable** ppDecodingBackendVTables; ma_decoding_backend_vtable** ppDecodingBackendVTables;
ma_uint32 decodingBackendCount; ma_uint32 decodingBackendCount;
void* pDecodingBackendUserData; void** ppDecodingBackendUserData;
} ma_resource_manager_config; } ma_resource_manager_config;
MA_API ma_resource_manager_config ma_resource_manager_config_init(void); MA_API ma_resource_manager_config ma_resource_manager_config_init(void);
@@ -63847,7 +63864,7 @@ MA_API ma_encoding_format ma_encoding_format_from_path_w(const wchar_t* pFilePat
} }
MA_API ma_decoding_backend_config ma_decoding_backend_config_init(ma_format preferredFormat, ma_uint32 seekPointCount, ma_encoding_format encodingFormat) static ma_decoding_backend_config ma_decoding_backend_config_init(ma_format preferredFormat, ma_uint32 seekPointCount, ma_encoding_format encodingFormat)
{ {
ma_decoding_backend_config config; ma_decoding_backend_config config;
@@ -64111,7 +64128,7 @@ static ma_result ma_decoder_init_from_file__internal(const ma_decoding_backend_v
/* Getting here means we were able to initialize the backend so we can now initialize the decoder. */ /* Getting here means we were able to initialize the backend so we can now initialize the decoder. */
pDecoder->pBackend = pBackend; pDecoder->pBackend = pBackend;
pDecoder->pBackendVTable = pVTable; pDecoder->pBackendVTable = pVTable;
pDecoder->pBackendUserData = pConfig->pBackendUserData; pDecoder->pBackendUserData = pVTableUserData;
return MA_SUCCESS; return MA_SUCCESS;
} }
@@ -64140,7 +64157,7 @@ static ma_result ma_decoder_init_from_file_w__internal(const ma_decoding_backend
/* Getting here means we were able to initialize the backend so we can now initialize the decoder. */ /* Getting here means we were able to initialize the backend so we can now initialize the decoder. */
pDecoder->pBackend = pBackend; pDecoder->pBackend = pBackend;
pDecoder->pBackendVTable = pVTable; pDecoder->pBackendVTable = pVTable;
pDecoder->pBackendUserData = pConfig->pBackendUserData; pDecoder->pBackendUserData = pVTableUserData;
return MA_SUCCESS; return MA_SUCCESS;
} }
@@ -64169,7 +64186,7 @@ static ma_result ma_decoder_init_from_memory__internal(const ma_decoding_backend
/* Getting here means we were able to initialize the backend so we can now initialize the decoder. */ /* Getting here means we were able to initialize the backend so we can now initialize the decoder. */
pDecoder->pBackend = pBackend; pDecoder->pBackend = pBackend;
pDecoder->pBackendVTable = pVTable; pDecoder->pBackendVTable = pVTable;
pDecoder->pBackendUserData = pConfig->pBackendUserData; pDecoder->pBackendUserData = pVTableUserData;
return MA_SUCCESS; return MA_SUCCESS;
} }
@@ -64318,6 +64335,15 @@ static ma_bool32 ma_can_decoding_backend_possibly_handle_encoding_format(const m
return backendEncodingFormat == encodingFormat; return backendEncodingFormat == encodingFormat;
} }
static void* ma_decoder_config_get_backend_user_data(const ma_decoder_config* pConfig, ma_uint32 iBackend)
{
if (pConfig->ppBackendUserData == NULL) {
return NULL;
}
return pConfig->ppBackendUserData[iBackend];
}
static ma_result ma_decoder_init__internal(const ma_decoder_config* pConfig, ma_decoder* pDecoder) static ma_result ma_decoder_init__internal(const ma_decoder_config* pConfig, ma_decoder* pDecoder)
{ {
@@ -64331,9 +64357,9 @@ static ma_result ma_decoder_init__internal(const ma_decoder_config* pConfig, ma_
MA_ASSERT(pConfig->ppBackendVTables != NULL); MA_ASSERT(pConfig->ppBackendVTables != NULL);
for (iBackend = 0; iBackend < pConfig->backendCount; iBackend += 1) { for (iBackend = 0; iBackend < pConfig->backendCount; iBackend += 1) {
if (ma_can_decoding_backend_possibly_handle_encoding_format(pConfig->ppBackendVTables[iBackend], pConfig->pBackendUserData, pConfig->encodingFormat)) { if (ma_can_decoding_backend_possibly_handle_encoding_format(pConfig->ppBackendVTables[iBackend], ma_decoder_config_get_backend_user_data(pConfig, iBackend), pConfig->encodingFormat)) {
/* Getting here means the backend may support the encoding format. */ /* Getting here means the backend may support the encoding format. */
result = ma_decoder_init_from_vtable__internal(pConfig->ppBackendVTables[iBackend], pConfig->pBackendUserData, pConfig, pDecoder); result = ma_decoder_init_from_vtable__internal(pConfig->ppBackendVTables[iBackend], ma_decoder_config_get_backend_user_data(pConfig, iBackend), pConfig, pDecoder);
if (result == MA_SUCCESS) { if (result == MA_SUCCESS) {
return ma_decoder__postinit_or_uninit(pConfig, pDecoder); return ma_decoder__postinit_or_uninit(pConfig, pDecoder);
} else { } else {
@@ -64489,9 +64515,9 @@ MA_API ma_result ma_decoder_init_memory(const void* pData, size_t dataSize, cons
} }
for (iBackend = 0; iBackend < config.backendCount; iBackend += 1) { for (iBackend = 0; iBackend < config.backendCount; iBackend += 1) {
if (ma_can_decoding_backend_possibly_handle_encoding_format(config.ppBackendVTables[iBackend], config.pBackendUserData, config.encodingFormat)) { if (ma_can_decoding_backend_possibly_handle_encoding_format(config.ppBackendVTables[iBackend], ma_decoder_config_get_backend_user_data(&config, iBackend), config.encodingFormat)) {
/* Getting here means the backend may support the encoding format. */ /* Getting here means the backend may support the encoding format. */
result = ma_decoder_init_from_memory__internal(config.ppBackendVTables[iBackend], config.pBackendUserData, pData, dataSize, &config, pDecoder); result = ma_decoder_init_from_memory__internal(config.ppBackendVTables[iBackend], ma_decoder_config_get_backend_user_data(&config, iBackend), pData, dataSize, &config, pDecoder);
if (result == MA_SUCCESS) { if (result == MA_SUCCESS) {
return ma_decoder__postinit_or_uninit(&config, pDecoder); return ma_decoder__postinit_or_uninit(&config, pDecoder);
} else { } else {
@@ -64687,9 +64713,9 @@ MA_API ma_result ma_decoder_init_file(const char* pFilePath, const ma_decoder_co
} }
for (iBackend = 0; iBackend < config.backendCount; iBackend += 1) { for (iBackend = 0; iBackend < config.backendCount; iBackend += 1) {
if (ma_can_decoding_backend_possibly_handle_encoding_format(config.ppBackendVTables[iBackend], config.pBackendUserData, config.encodingFormat)) { if (ma_can_decoding_backend_possibly_handle_encoding_format(config.ppBackendVTables[iBackend], ma_decoder_config_get_backend_user_data(&config, iBackend), config.encodingFormat)) {
/* Getting here means the backend may support the encoding format. */ /* Getting here means the backend may support the encoding format. */
result = ma_decoder_init_from_file__internal(config.ppBackendVTables[iBackend], config.pBackendUserData, pFilePath, &config, pDecoder); result = ma_decoder_init_from_file__internal(config.ppBackendVTables[iBackend], ma_decoder_config_get_backend_user_data(&config, iBackend), pFilePath, &config, pDecoder);
if (result == MA_SUCCESS) { if (result == MA_SUCCESS) {
return ma_decoder__postinit_or_uninit(&config, pDecoder); return ma_decoder__postinit_or_uninit(&config, pDecoder);
} else { } else {
@@ -64748,9 +64774,9 @@ MA_API ma_result ma_decoder_init_file_w(const wchar_t* pFilePath, const ma_decod
} }
for (iBackend = 0; iBackend < config.backendCount; iBackend += 1) { for (iBackend = 0; iBackend < config.backendCount; iBackend += 1) {
if (ma_can_decoding_backend_possibly_handle_encoding_format(config.ppBackendVTables[iBackend], config.pBackendUserData, config.encodingFormat)) { if (ma_can_decoding_backend_possibly_handle_encoding_format(config.ppBackendVTables[iBackend], ma_decoder_config_get_backend_user_data(&config, iBackend), config.encodingFormat)) {
/* Getting here means the backend may support the encoding format. */ /* Getting here means the backend may support the encoding format. */
result = ma_decoder_init_from_file_w__internal(config.ppBackendVTables[iBackend], config.pBackendUserData, pFilePath, &config, pDecoder); result = ma_decoder_init_from_file_w__internal(config.ppBackendVTables[iBackend], ma_decoder_config_get_backend_user_data(&config, iBackend), pFilePath, &config, pDecoder);
if (result == MA_SUCCESS) { if (result == MA_SUCCESS) {
return ma_decoder__postinit_or_uninit(&config, pDecoder); return ma_decoder__postinit_or_uninit(&config, pDecoder);
} else { } else {
@@ -67471,18 +67497,33 @@ MA_API ma_result ma_resource_manager_init(const ma_resource_manager_config* pCon
/* Custom decoding backends. */ /* Custom decoding backends. */
if (pConfig->ppDecodingBackendVTables != NULL && pConfig->decodingBackendCount > 0) { if (pConfig->ppDecodingBackendVTables != NULL && pConfig->decodingBackendCount > 0) {
size_t sizeInBytes = sizeof(*pResourceManager->config.ppDecodingBackendVTables) * pConfig->decodingBackendCount; size_t vtableSizeInBytes;
size_t vtableUserDataSizeInBytes;
pResourceManager->config.ppDecodingBackendVTables = (ma_decoding_backend_vtable**)ma_malloc(sizeInBytes, &pResourceManager->config.allocationCallbacks); vtableSizeInBytes = sizeof(*pResourceManager->config.ppDecodingBackendVTables) * pConfig->decodingBackendCount;
if (pConfig->ppDecodingBackendUserData != NULL) {
vtableUserDataSizeInBytes = sizeof(*pResourceManager->config.ppDecodingBackendUserData) * pConfig->decodingBackendCount;
} else {
vtableUserDataSizeInBytes = 0; /* No vtable user data present. No need for an allocation. */
}
pResourceManager->config.ppDecodingBackendVTables = (ma_decoding_backend_vtable**)ma_malloc(vtableSizeInBytes + vtableUserDataSizeInBytes, &pResourceManager->config.allocationCallbacks);
if (pResourceManager->config.ppDecodingBackendVTables == NULL) { if (pResourceManager->config.ppDecodingBackendVTables == NULL) {
ma_job_queue_uninit(&pResourceManager->jobQueue, &pResourceManager->config.allocationCallbacks); ma_job_queue_uninit(&pResourceManager->jobQueue, &pResourceManager->config.allocationCallbacks);
return MA_OUT_OF_MEMORY; return MA_OUT_OF_MEMORY;
} }
MA_COPY_MEMORY(pResourceManager->config.ppDecodingBackendVTables, pConfig->ppDecodingBackendVTables, sizeInBytes); MA_COPY_MEMORY(pResourceManager->config.ppDecodingBackendVTables, pConfig->ppDecodingBackendVTables, vtableSizeInBytes);
pResourceManager->config.decodingBackendCount = pConfig->decodingBackendCount; if (pConfig->ppDecodingBackendUserData != NULL) {
pResourceManager->config.pDecodingBackendUserData = pConfig->pDecodingBackendUserData; pResourceManager->config.ppDecodingBackendUserData = (void**)ma_offset_ptr(pResourceManager->config.ppDecodingBackendVTables, vtableSizeInBytes);
MA_COPY_MEMORY(pResourceManager->config.ppDecodingBackendUserData, pConfig->ppDecodingBackendUserData, vtableUserDataSizeInBytes);
} else {
pResourceManager->config.ppDecodingBackendUserData = NULL;
}
pResourceManager->config.decodingBackendCount = pConfig->decodingBackendCount;
} }
@@ -67584,7 +67625,7 @@ MA_API void ma_resource_manager_uninit(ma_resource_manager* pResourceManager)
#endif #endif
} }
ma_free(pResourceManager->config.ppDecodingBackendVTables, &pResourceManager->config.allocationCallbacks); ma_free(pResourceManager->config.ppDecodingBackendVTables, &pResourceManager->config.allocationCallbacks); /* <-- This also frees pResourceManager->config.ppDecodingBackendUserData because it's all in one allocation. */
if (pResourceManager->config.pLog == &pResourceManager->log) { if (pResourceManager->config.pLog == &pResourceManager->log) {
ma_log_uninit(&pResourceManager->log); ma_log_uninit(&pResourceManager->log);
@@ -67625,7 +67666,7 @@ static ma_decoder_config ma_resource_manager__init_decoder_config(ma_resource_ma
config.allocationCallbacks = pResourceManager->config.allocationCallbacks; config.allocationCallbacks = pResourceManager->config.allocationCallbacks;
config.ppBackendVTables = pResourceManager->config.ppDecodingBackendVTables; config.ppBackendVTables = pResourceManager->config.ppDecodingBackendVTables;
config.backendCount = pResourceManager->config.decodingBackendCount; config.backendCount = pResourceManager->config.decodingBackendCount;
config.pBackendUserData = pResourceManager->config.pDecodingBackendUserData; config.ppBackendUserData = pResourceManager->config.ppDecodingBackendUserData;
return config; return config;
} }