Commit dad754b7 authored by David Reid's avatar David Reid

Add support for custom decoding backends.

Public issue https://github.com/mackron/miniaudio/issues/311
parent fca4d5c1
/*
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 <stdio.h>
#if !defined(MA_NO_LIBVORBIS)
#define OV_EXCLUDE_STATIC_CALLBACKS
#include <vorbis/vorbisfile.h>
#endif
typedef struct
{
ma_data_source_base ds; /* The libvorbis decoder can be used independently as a data source. */
ma_read_proc onRead;
ma_seek_proc onSeek;
ma_tell_proc onTell;
void* pReadSeekTellUserData;
ma_format format; /* Will be either f32 or s16. */
#if !defined(MA_NO_LIBVORBIS)
OggVorbis_File vf;
#endif
} ma_libvorbis;
MA_API ma_result ma_libvorbis_init(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_libvorbis* pVorbis);
MA_API ma_result ma_libvorbis_init_file(const char* pFilePath, const ma_decoding_backend_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_libvorbis* pVorbis);
MA_API void ma_libvorbis_uninit(ma_libvorbis* pVorbis, const ma_allocation_callbacks* pAllocationCallbacks);
MA_API ma_result ma_libvorbis_read_pcm_frames(ma_libvorbis* pVorbis, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead);
MA_API ma_result ma_libvorbis_seek_to_pcm_frame(ma_libvorbis* pVorbis, ma_uint64 frameIndex);
MA_API ma_result ma_libvorbis_get_data_format(ma_libvorbis* pVorbis, ma_format* pFormat, ma_uint32* pChannels, ma_uint32* pSampleRate, ma_channel* pChannelMap, size_t channelMapCap);
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);
static ma_result ma_libvorbis_ds_read(ma_data_source* pDataSource, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead)
{
return ma_libvorbis_read_pcm_frames((ma_libvorbis*)pDataSource, pFramesOut, frameCount, pFramesRead);
}
static ma_result ma_libvorbis_ds_seek(ma_data_source* pDataSource, ma_uint64 frameIndex)
{
return ma_libvorbis_seek_to_pcm_frame((ma_libvorbis*)pDataSource, frameIndex);
}
static ma_result ma_libvorbis_ds_get_data_format(ma_data_source* pDataSource, ma_format* pFormat, ma_uint32* pChannels, ma_uint32* pSampleRate)
{
return ma_libvorbis_get_data_format((ma_libvorbis*)pDataSource, pFormat, pChannels, pSampleRate, NULL, 0);
}
static ma_result ma_libvorbis_ds_get_cursor(ma_data_source* pDataSource, ma_uint64* pCursor)
{
return ma_libvorbis_get_cursor_in_pcm_frames((ma_libvorbis*)pDataSource, pCursor);
}
static ma_result ma_libvorbis_ds_get_length(ma_data_source* pDataSource, ma_uint64* pLength)
{
return ma_libvorbis_get_length_in_pcm_frames((ma_libvorbis*)pDataSource, pLength);
}
static ma_data_source_vtable g_ma_libvorbis_ds_vtable =
{
ma_libvorbis_ds_read,
ma_libvorbis_ds_seek,
NULL, /* onMap() */
NULL, /* onUnmap() */
ma_libvorbis_ds_get_data_format,
ma_libvorbis_ds_get_cursor,
ma_libvorbis_ds_get_length
};
#if !defined(MA_NO_LIBVORBIS)
static size_t ma_libvorbis_vf_callback__read(void* pBufferOut, size_t size, size_t count, void* pUserData)
{
ma_libvorbis* pVorbis = (ma_libvorbis*)pUserData;
ma_result result;
size_t bytesToRead;
size_t bytesRead;
bytesToRead = size * count;
result = pVorbis->onRead(pVorbis->pReadSeekTellUserData, pBufferOut, bytesToRead, &bytesRead);
return bytesRead / size;
}
static int ma_libvorbis_vf_callback__seek(void* pUserData, ogg_int64_t offset, int whence)
{
ma_libvorbis* pVorbis = (ma_libvorbis*)pUserData;
ma_result result;
ma_seek_origin origin;
if (whence == SEEK_SET) {
origin = ma_seek_origin_start;
} else if (whence == SEEK_END) {
origin = ma_seek_origin_end;
} else {
origin = ma_seek_origin_current;
}
result = pVorbis->onSeek(pVorbis->pReadSeekTellUserData, offset, origin);
if (result != MA_SUCCESS) {
return -1;
}
return 0;
}
static long ma_libvorbis_vf_callback__tell(void* pUserData)
{
ma_libvorbis* pVorbis = (ma_libvorbis*)pUserData;
ma_result result;
ma_int64 cursor;
result = pVorbis->onTell(pVorbis->pReadSeekTellUserData, &cursor);
if (result != MA_SUCCESS) {
return -1;
}
return (long)cursor;
}
#endif
static ma_result ma_libvorbis_init_internal(const ma_decoding_backend_config* pConfig, ma_libvorbis* pVorbis)
{
ma_result result;
ma_data_source_config dataSourceConfig;
if (pVorbis == NULL) {
return MA_INVALID_ARGS;
}
MA_ZERO_OBJECT(pVorbis);
pVorbis->format = ma_format_f32; /* f32 by default. */
if (pConfig != NULL && (pConfig->preferredFormat == ma_format_f32 || pConfig->preferredFormat == ma_format_s16)) {
pVorbis->format = pConfig->preferredFormat;
} else {
/* Getting here means something other than f32 and s16 was specified. Just leave this unset to use the default format. */
}
dataSourceConfig = ma_data_source_config_init();
dataSourceConfig.vtable = &g_ma_libvorbis_ds_vtable;
result = ma_data_source_init(&dataSourceConfig, &pVorbis->ds);
if (result != MA_SUCCESS) {
return result; /* Failed to initialize the base data source. */
}
return MA_SUCCESS;
}
MA_API ma_result ma_libvorbis_init(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_libvorbis* pVorbis)
{
ma_result result;
(void)pAllocationCallbacks; /* Can't seem to find a way to configure memory allocations in libvorbis. */
result = ma_libvorbis_init_internal(pConfig, pVorbis);
if (result != MA_SUCCESS) {
return result;
}
if (onRead == NULL || onSeek == NULL) {
return MA_INVALID_ARGS; /* onRead and onSeek are mandatory. */
}
pVorbis->onRead = onRead;
pVorbis->onSeek = onSeek;
pVorbis->onTell = onTell;
pVorbis->pReadSeekTellUserData = pReadSeekTellUserData;
#if !defined(MA_NO_LIBVORBIS)
{
int libvorbisResult;
ov_callbacks libvorbisCallbacks;
/* We can now initialize the vorbis decoder. This must be done after we've set up the callbacks. */
libvorbisCallbacks.read_func = ma_libvorbis_vf_callback__read;
libvorbisCallbacks.seek_func = ma_libvorbis_vf_callback__seek;
libvorbisCallbacks.close_func = NULL;
libvorbisCallbacks.tell_func = ma_libvorbis_vf_callback__tell;
libvorbisResult = ov_open_callbacks(pVorbis, &pVorbis->vf, NULL, 0, libvorbisCallbacks);
if (libvorbisResult < 0) {
return MA_INVALID_FILE;
}
return MA_SUCCESS;
}
#else
{
/* libvorbis is disabled. */
return MA_NOT_IMPLEMENTED;
}
#endif
}
MA_API ma_result ma_libvorbis_init_file(const char* pFilePath, const ma_decoding_backend_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_libvorbis* pVorbis)
{
ma_result result;
(void)pAllocationCallbacks; /* Can't seem to find a way to configure memory allocations in libvorbis. */
result = ma_libvorbis_init_internal(pConfig, pVorbis);
if (result != MA_SUCCESS) {
return result;
}
#if !defined(MA_NO_LIBVORBIS)
{
int libvorbisResult;
libvorbisResult = ov_fopen(pFilePath, &pVorbis->vf);
if (libvorbisResult < 0) {
return MA_INVALID_FILE;
}
return MA_SUCCESS;
}
#else
{
/* libvorbis is disabled. */
(void)pFilePath;
return MA_NOT_IMPLEMENTED;
}
#endif
}
MA_API void ma_libvorbis_uninit(ma_libvorbis* pVorbis, const ma_allocation_callbacks* pAllocationCallbacks)
{
if (pVorbis == NULL) {
return;
}
(void)pAllocationCallbacks;
#if !defined(MA_NO_LIBVORBIS)
{
ov_clear(&pVorbis->vf);
}
#else
{
/* libvorbis is disabled. Should never hit this since initialization would have failed. */
MA_ASSERT(MA_FALSE);
}
#endif
ma_data_source_uninit(&pVorbis->ds);
}
MA_API ma_result ma_libvorbis_read_pcm_frames(ma_libvorbis* pVorbis, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead)
{
if (pVorbis == NULL) {
return MA_INVALID_ARGS;
}
#if !defined(MA_NO_LIBVORBIS)
{
/* We always use floating point format. */
ma_result result = MA_SUCCESS; /* Must be initialized to MA_SUCCESS. */
ma_uint64 totalFramesRead;
ma_format format;
ma_uint32 channels;
ma_libvorbis_get_data_format(pVorbis, &format, &channels, NULL, NULL, 0);
totalFramesRead = 0;
while (totalFramesRead < frameCount) {
long libvorbisResult;
int framesToRead;
ma_uint64 framesRemaining;
framesRemaining = (frameCount - totalFramesRead);
framesToRead = 1024;
if (framesToRead > framesRemaining) {
framesToRead = (int)framesRemaining;
}
if (format == ma_format_f32) {
float** ppFramesF32;
libvorbisResult = ov_read_float(&pVorbis->vf, &ppFramesF32, framesToRead, NULL);
if (libvorbisResult < 0) {
result = MA_ERROR; /* Error while decoding. */
break;
} else {
/* Frames need to be interleaved. */
ma_interleave_pcm_frames(format, channels, libvorbisResult, ppFramesF32, ma_offset_pcm_frames_ptr(pFramesOut, totalFramesRead, format, channels));
totalFramesRead += libvorbisResult;
if (libvorbisResult == 0) {
result = MA_AT_END;
break;
}
}
} else {
libvorbisResult = ov_read(&pVorbis->vf, ma_offset_pcm_frames_ptr(pFramesOut, totalFramesRead, format, channels), framesToRead * ma_get_bytes_per_frame(format, channels), 0, 2, 1, NULL);
if (libvorbisResult < 0) {
result = MA_ERROR; /* Error while decoding. */
break;
} else {
/* Conveniently, there's no need to interleaving when using ov_read(). I'm not sure why ov_read_float() is different in that regard... */
totalFramesRead += libvorbisResult / ma_get_bytes_per_frame(format, channels);
if (libvorbisResult == 0) {
result = MA_AT_END;
break;
}
}
}
}
if (pFramesRead != NULL) {
*pFramesRead = totalFramesRead;
}
return result;
}
#else
{
/* libvorbis is disabled. Should never hit this since initialization would have failed. */
MA_ASSERT(MA_FALSE);
(void)pFramesOut;
(void)frameCount;
(void)pFramesRead;
return MA_NOT_IMPLEMENTED;
}
#endif
}
MA_API ma_result ma_libvorbis_seek_to_pcm_frame(ma_libvorbis* pVorbis, ma_uint64 frameIndex)
{
if (pVorbis == NULL) {
return MA_INVALID_ARGS;
}
#if !defined(MA_NO_LIBVORBIS)
{
int libvorbisResult = ov_pcm_seek(&pVorbis->vf, (ogg_int64_t)frameIndex);
if (libvorbisResult != 0) {
if (libvorbisResult == OV_ENOSEEK) {
return MA_INVALID_OPERATION; /* Not seekable. */
} else if (libvorbisResult == OV_EINVAL) {
return MA_INVALID_ARGS;
} else {
return MA_ERROR;
}
}
return MA_SUCCESS;
}
#else
{
/* libvorbis is disabled. Should never hit this since initialization would have failed. */
MA_ASSERT(MA_FALSE);
(void)frameIndex;
return MA_NOT_IMPLEMENTED;
}
#endif
}
MA_API ma_result ma_libvorbis_get_data_format(ma_libvorbis* pVorbis, ma_format* pFormat, ma_uint32* pChannels, ma_uint32* pSampleRate, ma_channel* pChannelMap, size_t channelMapCap)
{
/* Defaults for safety. */
if (pFormat != NULL) {
*pFormat = ma_format_unknown;
}
if (pChannels != NULL) {
*pChannels = 0;
}
if (pSampleRate != NULL) {
*pSampleRate = 0;
}
if (pChannelMap != NULL) {
MA_ZERO_MEMORY(pChannelMap, sizeof(*pChannelMap) * channelMapCap);
}
if (pVorbis == NULL) {
return MA_INVALID_OPERATION;
}
if (pFormat != NULL) {
*pFormat = pVorbis->format;
}
#if !defined(MA_NO_LIBVORBIS)
{
vorbis_info* pInfo = ov_info(&pVorbis->vf, 0);
if (pInfo == NULL) {
return MA_INVALID_OPERATION;
}
if (pChannels != NULL) {
*pChannels = pInfo->channels;
}
if (pSampleRate != NULL) {
*pSampleRate = pInfo->rate;
}
if (pChannelMap != NULL) {
ma_get_standard_channel_map(ma_standard_channel_map_vorbis, (ma_uint32)ma_min(pInfo->channels, channelMapCap), pChannelMap);
}
return MA_SUCCESS;
}
#else
{
/* libvorbis is disabled. Should never hit this since initialization would have failed. */
MA_ASSERT(MA_FALSE);
return MA_NOT_IMPLEMENTED;
}
#endif
}
MA_API ma_result ma_libvorbis_get_cursor_in_pcm_frames(ma_libvorbis* pVorbis, ma_uint64* pCursor)
{
if (pCursor == NULL) {
return MA_INVALID_ARGS;
}
*pCursor = 0; /* Safety. */
if (pVorbis == NULL) {
return MA_INVALID_ARGS;
}
#if !defined(MA_NO_LIBVORBIS)
{
ogg_int64_t offset = ov_pcm_tell(&pVorbis->vf);
if (offset < 0) {
return MA_INVALID_FILE;
}
*pCursor = (ma_uint64)offset;
return MA_SUCCESS;
}
#else
{
/* libvorbis is disabled. Should never hit this since initialization would have failed. */
MA_ASSERT(MA_FALSE);
return MA_NOT_IMPLEMENTED;
}
#endif
}
MA_API ma_result ma_libvorbis_get_length_in_pcm_frames(ma_libvorbis* pVorbis, ma_uint64* pLength)
{
if (pLength == NULL) {
return MA_INVALID_ARGS;
}
*pLength = 0; /* Safety. */
if (pVorbis == NULL) {
return MA_INVALID_ARGS;
}
#if !defined(MA_NO_LIBVORBIS)
{
/* I don't know how to reliably retrieve the length in frames using libvorbis, so returning 0 for now. */
*pLength = 0;
return MA_SUCCESS;
}
#else
{
/* libvorbis is disabled. Should never hit this since initialization would have failed. */
MA_ASSERT(MA_FALSE);
return MA_NOT_IMPLEMENTED;
}
#endif
}
#if !defined(MA_NO_LIBOPUS)
#include <opusfile.h>
#endif
typedef struct
{
ma_data_source_base ds; /* The libopus decoder can be used independently as a data source. */
ma_read_proc onRead;
ma_seek_proc onSeek;
ma_tell_proc onTell;
void* pReadSeekTellUserData;
ma_format format; /* Will be either f32 or s16. */
#if !defined(MA_NO_LIBOPUS)
OggOpusFile* of;
#endif
} ma_libopus;
MA_API ma_result ma_libopus_init(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_libopus* pVorbis);
MA_API ma_result ma_libopus_init_file(const char* pFilePath, const ma_decoding_backend_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_libopus* pVorbis);
MA_API void ma_libopus_uninit(ma_libopus* pVorbis, const ma_allocation_callbacks* pAllocationCallbacks);
MA_API ma_result ma_libopus_read_pcm_frames(ma_libopus* pVorbis, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead);
MA_API ma_result ma_libopus_seek_to_pcm_frame(ma_libopus* pVorbis, ma_uint64 frameIndex);
MA_API ma_result ma_libopus_get_data_format(ma_libopus* pVorbis, ma_format* pFormat, ma_uint32* pChannels, ma_uint32* pSampleRate, ma_channel* pChannelMap, size_t channelMapCap);
MA_API ma_result ma_libopus_get_cursor_in_pcm_frames(ma_libopus* pVorbis, ma_uint64* pCursor);
MA_API ma_result ma_libopus_get_length_in_pcm_frames(ma_libopus* pVorbis, ma_uint64* pLength);
static ma_result ma_libopus_ds_read(ma_data_source* pDataSource, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead)
{
return ma_libopus_read_pcm_frames((ma_libopus*)pDataSource, pFramesOut, frameCount, pFramesRead);
}
static ma_result ma_libopus_ds_seek(ma_data_source* pDataSource, ma_uint64 frameIndex)
{
return ma_libopus_seek_to_pcm_frame((ma_libopus*)pDataSource, frameIndex);
}
static ma_result ma_libopus_ds_get_data_format(ma_data_source* pDataSource, ma_format* pFormat, ma_uint32* pChannels, ma_uint32* pSampleRate)
{
return ma_libopus_get_data_format((ma_libopus*)pDataSource, pFormat, pChannels, pSampleRate, NULL, 0);
}
static ma_result ma_libopus_ds_get_cursor(ma_data_source* pDataSource, ma_uint64* pCursor)
{
return ma_libopus_get_cursor_in_pcm_frames((ma_libopus*)pDataSource, pCursor);
}
static ma_result ma_libopus_ds_get_length(ma_data_source* pDataSource, ma_uint64* pLength)
{
return ma_libopus_get_length_in_pcm_frames((ma_libopus*)pDataSource, pLength);
}
static ma_data_source_vtable g_ma_libopus_ds_vtable =
{
ma_libopus_ds_read,
ma_libopus_ds_seek,
NULL, /* onMap() */
NULL, /* onUnmap() */
ma_libopus_ds_get_data_format,
ma_libopus_ds_get_cursor,
ma_libopus_ds_get_length
};
#if !defined(MA_NO_LIBOPUS)
static int ma_libopus_of_callback__read(void* pUserData, void* pBufferOut, int bytesToRead)
{
ma_libopus* pOpus = (ma_libopus*)pUserData;
ma_result result;
size_t bytesRead;
result = pOpus->onRead(pOpus->pReadSeekTellUserData, pBufferOut, bytesToRead, &bytesRead);
if (result != MA_SUCCESS) {
return -1;
}
return (int)bytesRead;
}
static int ma_libopus_of_callback__seek(void* pUserData, ogg_int64_t offset, int whence)
{
ma_libopus* pOpus = (ma_libopus*)pUserData;
ma_result result;
ma_seek_origin origin;
if (whence == SEEK_SET) {
origin = ma_seek_origin_start;
} else if (whence == SEEK_END) {
origin = ma_seek_origin_end;
} else {
origin = ma_seek_origin_current;
}
result = pOpus->onSeek(pOpus->pReadSeekTellUserData, offset, origin);
if (result != MA_SUCCESS) {
return -1;
}
return 0;
}
static opus_int64 ma_libopus_of_callback__tell(void* pUserData)
{
ma_libopus* pOpus = (ma_libopus*)pUserData;
ma_result result;
ma_int64 cursor;
if (pOpus->onTell == NULL) {
return -1;
}
result = pOpus->onTell(pOpus->pReadSeekTellUserData, &cursor);
if (result != MA_SUCCESS) {
return -1;
}
return cursor;
}
#endif
static ma_result ma_libopus_init_internal(const ma_decoding_backend_config* pConfig, ma_libopus* pOpus)
{
ma_result result;
ma_data_source_config dataSourceConfig;
if (pOpus == NULL) {
return MA_INVALID_ARGS;
}
MA_ZERO_OBJECT(pOpus);
pOpus->format = ma_format_f32; /* f32 by default. */
if (pConfig != NULL && (pConfig->preferredFormat == ma_format_f32 || pConfig->preferredFormat == ma_format_s16)) {
pOpus->format = pConfig->preferredFormat;
} else {
/* Getting here means something other than f32 and s16 was specified. Just leave this unset to use the default format. */
}
dataSourceConfig = ma_data_source_config_init();
dataSourceConfig.vtable = &g_ma_libopus_ds_vtable;
result = ma_data_source_init(&dataSourceConfig, &pOpus->ds);
if (result != MA_SUCCESS) {
return result; /* Failed to initialize the base data source. */
}
return MA_SUCCESS;
}
MA_API ma_result ma_libopus_init(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_libopus* pOpus)
{
ma_result result;
(void)pAllocationCallbacks; /* Can't seem to find a way to configure memory allocations in libopus. */
result = ma_libopus_init_internal(pConfig, pOpus);
if (result != MA_SUCCESS) {
return result;
}
if (onRead == NULL || onSeek == NULL) {
return MA_INVALID_ARGS; /* onRead and onSeek are mandatory. */
}
pOpus->onRead = onRead;
pOpus->onSeek = onSeek;
pOpus->onTell = onTell;
pOpus->pReadSeekTellUserData = pReadSeekTellUserData;
#if !defined(MA_NO_LIBOPUS)
{
int libopusResult;
OpusFileCallbacks libopusCallbacks;
/* We can now initialize the vorbis decoder. This must be done after we've set up the callbacks. */
libopusCallbacks.read = ma_libopus_of_callback__read;
libopusCallbacks.seek = ma_libopus_of_callback__seek;
libopusCallbacks.close = NULL;
libopusCallbacks.tell = ma_libopus_of_callback__tell;
pOpus->of = op_open_callbacks(pOpus, &libopusCallbacks, NULL, 0, &libopusResult);
if (pOpus->of == NULL) {
return MA_INVALID_FILE;
}
return MA_SUCCESS;
}
#else
{
/* libopus is disabled. */
return MA_NOT_IMPLEMENTED;
}
#endif
}
MA_API ma_result ma_libopus_init_file(const char* pFilePath, const ma_decoding_backend_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_libopus* pOpus)
{
ma_result result;
(void)pAllocationCallbacks; /* Can't seem to find a way to configure memory allocations in libopus. */
result = ma_libopus_init_internal(pConfig, pOpus);
if (result != MA_SUCCESS) {
return result;
}
#if !defined(MA_NO_LIBOPUS)
{
int libopusResult;
pOpus->of = op_open_file(pFilePath, &libopusResult);
if (pOpus->of == NULL) {
return MA_INVALID_FILE;
}
return MA_SUCCESS;
}
#else
{
/* libopus is disabled. */
(void)pFilePath;
return MA_NOT_IMPLEMENTED;
}
#endif
}
MA_API void ma_libopus_uninit(ma_libopus* pOpus, const ma_allocation_callbacks* pAllocationCallbacks)
{
if (pOpus == NULL) {
return;
}
(void)pAllocationCallbacks;
#if !defined(MA_NO_LIBOPUS)
{
op_free(pOpus->of);
}
#else
{
/* libopus is disabled. Should never hit this since initialization would have failed. */
MA_ASSERT(MA_FALSE);
}
#endif
ma_data_source_uninit(&pOpus->ds);
}
MA_API ma_result ma_libopus_read_pcm_frames(ma_libopus* pOpus, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead)
{
if (pOpus == NULL) {
return MA_INVALID_ARGS;
}
#if !defined(MA_NO_LIBOPUS)
{
/* We always use floating point format. */
ma_result result = MA_SUCCESS; /* Must be initialized to MA_SUCCESS. */
ma_uint64 totalFramesRead;
ma_format format;
ma_uint32 channels;
ma_libopus_get_data_format(pOpus, &format, &channels, NULL, NULL, 0);
totalFramesRead = 0;
while (totalFramesRead < frameCount) {
long libopusResult;
int framesToRead;
ma_uint64 framesRemaining;
framesRemaining = (frameCount - totalFramesRead);
framesToRead = 1024;
if (framesToRead > framesRemaining) {
framesToRead = (int)framesRemaining;
}
if (format == ma_format_f32) {
libopusResult = op_read_float(pOpus->of, ma_offset_pcm_frames_ptr(pFramesOut, totalFramesRead, format, channels), framesToRead * channels, NULL);
} else {
libopusResult = op_read (pOpus->of, ma_offset_pcm_frames_ptr(pFramesOut, totalFramesRead, format, channels), framesToRead * channels, NULL);
}
if (libopusResult < 0) {
result = MA_ERROR; /* Error while decoding. */
break;
} else {
totalFramesRead += libopusResult;
if (libopusResult == 0) {
result = MA_AT_END;
break;
}
}
}
if (pFramesRead != NULL) {
*pFramesRead = totalFramesRead;
}
return result;
}
#else
{
/* libopus is disabled. Should never hit this since initialization would have failed. */
MA_ASSERT(MA_FALSE);
(void)pFramesOut;
(void)frameCount;
(void)pFramesRead;
return MA_NOT_IMPLEMENTED;
}
#endif
}
MA_API ma_result ma_libopus_seek_to_pcm_frame(ma_libopus* pOpus, ma_uint64 frameIndex)
{
if (pOpus == NULL) {
return MA_INVALID_ARGS;
}
#if !defined(MA_NO_LIBOPUS)
{
int libopusResult = op_pcm_seek(pOpus->of, (ogg_int64_t)frameIndex);
if (libopusResult != 0) {
if (libopusResult == OP_ENOSEEK) {
return MA_INVALID_OPERATION; /* Not seekable. */
} else if (libopusResult == OP_EINVAL) {
return MA_INVALID_ARGS;
} else {
return MA_ERROR;
}
}
return MA_SUCCESS;
}
#else
{
/* libopus is disabled. Should never hit this since initialization would have failed. */
MA_ASSERT(MA_FALSE);
(void)frameIndex;
return MA_NOT_IMPLEMENTED;
}
#endif
}
MA_API ma_result ma_libopus_get_data_format(ma_libopus* pOpus, ma_format* pFormat, ma_uint32* pChannels, ma_uint32* pSampleRate, ma_channel* pChannelMap, size_t channelMapCap)
{
/* Defaults for safety. */
if (pFormat != NULL) {
*pFormat = ma_format_unknown;
}
if (pChannels != NULL) {
*pChannels = 0;
}
if (pSampleRate != NULL) {
*pSampleRate = 0;
}
if (pChannelMap != NULL) {
MA_ZERO_MEMORY(pChannelMap, sizeof(*pChannelMap) * channelMapCap);
}
if (pOpus == NULL) {
return MA_INVALID_OPERATION;
}
if (pFormat != NULL) {
*pFormat = pOpus->format;
}
#if !defined(MA_NO_LIBOPUS)
{
ma_uint32 channels = op_channel_count(pOpus->of, -1);
if (pChannels != NULL) {
*pChannels = channels;
}
if (pSampleRate != NULL) {
*pSampleRate = 48000;
}
if (pChannelMap != NULL) {
ma_get_standard_channel_map(ma_standard_channel_map_vorbis, (ma_uint32)ma_min(channels, channelMapCap), pChannelMap);
}
return MA_SUCCESS;
}
#else
{
/* libopus is disabled. Should never hit this since initialization would have failed. */
MA_ASSERT(MA_FALSE);
return MA_NOT_IMPLEMENTED;
}
#endif
}
MA_API ma_result ma_libopus_get_cursor_in_pcm_frames(ma_libopus* pOpus, ma_uint64* pCursor)
{
if (pCursor == NULL) {
return MA_INVALID_ARGS;
}
*pCursor = 0; /* Safety. */
if (pOpus == NULL) {
return MA_INVALID_ARGS;
}
#if !defined(MA_NO_LIBOPUS)
{
ogg_int64_t offset = op_pcm_tell(pOpus->of);
if (offset < 0) {
return MA_INVALID_FILE;
}
*pCursor = (ma_uint64)offset;
return MA_SUCCESS;
}
#else
{
/* libopus is disabled. Should never hit this since initialization would have failed. */
MA_ASSERT(MA_FALSE);
return MA_NOT_IMPLEMENTED;
}
#endif
}
MA_API ma_result ma_libopus_get_length_in_pcm_frames(ma_libopus* pOpus, ma_uint64* pLength)
{
if (pLength == NULL) {
return MA_INVALID_ARGS;
}
*pLength = 0; /* Safety. */
if (pOpus == NULL) {
return MA_INVALID_ARGS;
}
#if !defined(MA_NO_LIBOPUS)
{
ogg_int64_t length = op_pcm_total(pOpus->of, -1);
if (length < 0) {
return MA_ERROR;
}
*pLength = (ma_uint64)length;
return MA_SUCCESS;
}
#else
{
/* libopus is disabled. Should never hit this since initialization would have failed. */
MA_ASSERT(MA_FALSE);
return MA_NOT_IMPLEMENTED;
}
#endif
}
/*
In this example we're going to be implementing our custom decoders as an extension to the ma_decoder
object. We declare our decoding backends after the ma_decoder object which allows us to avoid a
memory allocation. There are many ways to manage the backend objects so use whatever works best for
your particular scenario.
*/
typedef struct
{
ma_decoder base; /* Must be the first member so we can cast between ma_decoder_ex and ma_decoder. */
union
{
ma_libvorbis libvorbis;
ma_libopus libopus;
} backends;
} ma_decoder_ex;
static ma_result ma_decoder_ex_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_decoder_ex* pDecoderEx = (ma_decoder_ex*)pUserData;
ma_result result;
/*
NOTE: We don't need to allocate any memory for the libvorbis object here because our backend
data is just extended off the ma_decoder object (ma_decode_ex) which is passed in as a
parameter to this function. We therefore need only cast to ma_decoder_ex and reference data
directly from that structure.
*/
*ppBackend = (ma_data_source*)&pDecoderEx->backends.libvorbis;
result = ma_libvorbis_init(onRead, onSeek, onTell, pReadSeekTellUserData, pConfig, pAllocationCallbacks, &pDecoderEx->backends.libvorbis);
if (result != MA_SUCCESS) {
return result;
}
return MA_SUCCESS;
}
static ma_result ma_decoder_ex_init_file__libvorbis(void* pUserData, const char* pFilePath, const ma_decoding_backend_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_data_source** ppBackend)
{
ma_decoder_ex* pDecoderEx = (ma_decoder_ex*)pUserData;
ma_result result;
*ppBackend = (ma_data_source*)&pDecoderEx->backends.libvorbis;
result = ma_libvorbis_init_file(pFilePath, pConfig, pAllocationCallbacks, &pDecoderEx->backends.libvorbis);
if (result != MA_SUCCESS) {
return result;
}
return MA_SUCCESS;
}
static void ma_decoder_ex_uninit__libvorbis(void* pUserData, ma_data_source* pBackend, const ma_allocation_callbacks* pAllocationCallbacks)
{
ma_libvorbis* pVorbis = (ma_libvorbis*)pBackend;
ma_libvorbis_uninit(pVorbis, pAllocationCallbacks);
/* No need to free the pVorbis object because it is sitting in the containing ma_decoder_ex object. */
(void)pUserData;
}
static ma_result ma_decoder_ex_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_decoder_ex_init__libvorbis,
ma_decoder_ex_init_file__libvorbis,
NULL, /* onInitFileW() */
NULL, /* onInitMemory() */
ma_decoder_ex_uninit__libvorbis,
ma_decoder_ex_get_channel_map__libvorbis
};
static ma_result ma_decoder_ex_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_decoder_ex* pDecoderEx = (ma_decoder_ex*)pUserData;
ma_result result;
/*
NOTE: We don't need to allocate any memory for the libopus object here because our backend
data is just extended off the ma_decoder object (ma_decode_ex) which is passed in as a
parameter to this function. We therefore need only cast to ma_decoder_ex and reference data
directly from that structure.
*/
*ppBackend = (ma_data_source*)&pDecoderEx->backends.libopus;
result = ma_libopus_init(onRead, onSeek, onTell, pReadSeekTellUserData, pConfig, pAllocationCallbacks, &pDecoderEx->backends.libopus);
if (result != MA_SUCCESS) {
return result;
}
return MA_SUCCESS;
}
static ma_result ma_decoder_ex_init_file__libopus(void* pUserData, const char* pFilePath, const ma_decoding_backend_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_data_source** ppBackend)
{
ma_decoder_ex* pDecoderEx = (ma_decoder_ex*)pUserData;
ma_result result;
*ppBackend = (ma_data_source*)&pDecoderEx->backends.libopus;
result = ma_libopus_init_file(pFilePath, pConfig, pAllocationCallbacks, &pDecoderEx->backends.libopus);
if (result != MA_SUCCESS) {
return result;
}
return MA_SUCCESS;
}
static void ma_decoder_ex_uninit__libopus(void* pUserData, ma_data_source* pBackend, const ma_allocation_callbacks* pAllocationCallbacks)
{
ma_libopus* pOpus = (ma_libopus*)pBackend;
ma_libopus_uninit(pOpus, pAllocationCallbacks);
/* No need to free the pOpus object because it is sitting in the containing ma_decoder_ex object. */
(void)pUserData;
}
static ma_result ma_decoder_ex_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_decoder_ex_init__libopus,
ma_decoder_ex_init_file__libopus,
NULL, /* onInitFileW() */
NULL, /* onInitMemory() */
ma_decoder_ex_uninit__libopus,
ma_decoder_ex_get_channel_map__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, MA_TRUE);
(void)pInput;
}
int main(int argc, char** argv)
{
ma_result result;
ma_decoder_config decoderConfig;
ma_decoder_ex 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 = &decoder; /* 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.customBackendVTableCount = sizeof(pCustomBackendVTables) / sizeof(pCustomBackendVTables[0]);
result = ma_decoder_init_file(argv[1], &decoderConfig, &decoder.base);
if (result != MA_SUCCESS) {
printf("Failed to initialize decoder.");
return -1;
}
/* Initialize the device. */
result = ma_data_source_get_data_format(&decoder, &format, &channels, &sampleRate);
if (result != MA_SUCCESS) {
printf("Failed to retrieve decoder data format.");
ma_decoder_uninit(&decoder.base);
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.base);
return -1;
}
if (ma_device_start(&device) != MA_SUCCESS) {
printf("Failed to start playback device.\n");
ma_device_uninit(&device);
ma_decoder_uninit(&decoder.base);
return -1;
}
printf("Press Enter to quit...");
getchar();
ma_device_uninit(&device);
ma_decoder_uninit(&decoder.base);
return 0;
}
\ No newline at end of file
...@@ -6064,6 +6064,11 @@ MA_API ma_result ma_default_vfs_init(ma_default_vfs* pVFS, const ma_allocation_c ...@@ -6064,6 +6064,11 @@ MA_API ma_result ma_default_vfs_init(ma_default_vfs* pVFS, const ma_allocation_c
typedef ma_result (* ma_read_proc)(void* pUserData, void* pBufferOut, size_t bytesToRead, size_t* pBytesRead);
typedef ma_result (* ma_seek_proc)(void* pUserData, ma_int64 offset, ma_seek_origin origin);
typedef ma_result (* ma_tell_proc)(void* pUserData, ma_int64* pCursor);
#if !defined(MA_NO_DECODING) || !defined(MA_NO_ENCODING) #if !defined(MA_NO_DECODING) || !defined(MA_NO_ENCODING)
typedef enum typedef enum
...@@ -6084,9 +6089,33 @@ you do your own synchronization. ...@@ -6084,9 +6089,33 @@ you do your own synchronization.
#ifndef MA_NO_DECODING #ifndef MA_NO_DECODING
typedef struct ma_decoder ma_decoder; typedef struct ma_decoder ma_decoder;
typedef size_t (* ma_decoder_read_proc) (ma_decoder* pDecoder, void* pBufferOut, size_t bytesToRead); /* Returns the number of bytes read. */
typedef ma_bool32 (* ma_decoder_seek_proc) (ma_decoder* pDecoder, int byteOffset, ma_seek_origin origin); /* Origin will never be ma_seek_origin_end. */ typedef struct
typedef ma_uint64 (* ma_decoder_read_pcm_frames_proc) (ma_decoder* pDecoder, void* pFramesOut, ma_uint64 frameCount); /* Returns the number of frames read. Output data is in internal format. */ {
ma_format preferredFormat;
} ma_decoding_backend_config;
MA_API ma_decoding_backend_config ma_decoding_backend_config_init(ma_format preferredFormat);
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 (* onInitFile )(void* pUserData, const char* pFilePath, const ma_decoding_backend_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_data_source** ppBackend); /* Optional. */
ma_result (* onInitFileW )(void* pUserData, const wchar_t* pFilePath, const ma_decoding_backend_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_data_source** ppBackend); /* Optional. */
ma_result (* onInitMemory )(void* pUserData, const void* pData, size_t dataSize, const ma_decoding_backend_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_data_source** ppBackend); /* Optional. */
void (* onUninit )(void* pUserData, ma_data_source* pBackend, const ma_allocation_callbacks* pAllocationCallbacks);
ma_result (* onGetChannelMap)(void* pUserData, ma_data_source* pBackend, ma_channel* pChannelMap, size_t channelMapCap);
} ma_decoding_backend_vtable;
/* TODO: Convert read and seek to be consistent with the VFS API (ma_result return value, bytes read moved to an output parameter). */
typedef size_t (* ma_decoder_read_proc)(ma_decoder* pDecoder, void* pBufferOut, size_t bytesToRead); /* Returns the number of bytes read. */
typedef ma_bool32 (* ma_decoder_seek_proc)(ma_decoder* pDecoder, ma_int64 byteOffset, ma_seek_origin origin);
typedef ma_result (* ma_decoder_tell_proc)(ma_decoder* pDecoder, ma_int64* pCursor);
/* TODO: Remove these when internal decoders are transferred over to the new backend system. */
typedef ma_uint64 (* ma_decoder_read_pcm_frames_proc) (ma_decoder* pDecoder, void* pFramesOut, ma_uint64 frameCount); /* Returns the number of frames read. Output data is in internal format. */
typedef ma_result (* ma_decoder_seek_to_pcm_frame_proc) (ma_decoder* pDecoder, ma_uint64 frameIndex); typedef ma_result (* ma_decoder_seek_to_pcm_frame_proc) (ma_decoder* pDecoder, ma_uint64 frameIndex);
typedef ma_result (* ma_decoder_uninit_proc) (ma_decoder* pDecoder); typedef ma_result (* ma_decoder_uninit_proc) (ma_decoder* pDecoder);
typedef ma_uint64 (* ma_decoder_get_length_in_pcm_frames_proc)(ma_decoder* pDecoder); typedef ma_uint64 (* ma_decoder_get_length_in_pcm_frames_proc)(ma_decoder* pDecoder);
...@@ -6112,15 +6141,21 @@ typedef struct ...@@ -6112,15 +6141,21 @@ typedef struct
} speex; } speex;
} resampling; } resampling;
ma_allocation_callbacks allocationCallbacks; ma_allocation_callbacks allocationCallbacks;
ma_decoding_backend_vtable** ppCustomBackendVTables;
ma_uint32 customBackendVTableCount;
void* pCustomBackendUserData;
} ma_decoder_config; } ma_decoder_config;
struct ma_decoder struct ma_decoder
{ {
ma_data_source_base ds; ma_data_source_base ds;
ma_data_source* pBackend; /* The decoding backend we'll be pulling data from. */
const ma_decoding_backend_vtable* pBackendVTable; /* The vtable for the decoding backend. This needs to be stored so we can access the onUninit() callback. */
void* pBackendUserData;
ma_decoder_read_proc onRead; ma_decoder_read_proc onRead;
ma_decoder_seek_proc onSeek; ma_decoder_seek_proc onSeek;
ma_decoder_tell_proc onTell;
void* pUserData; void* pUserData;
ma_uint64 readPointerInBytes; /* In internal encoded data. */
ma_uint64 readPointerInPCMFrames; /* In output sample rate. Used for keeping track of how many frames are available for decoding. */ ma_uint64 readPointerInPCMFrames; /* In output sample rate. Used for keeping track of how many frames are available for decoding. */
ma_format internalFormat; ma_format internalFormat;
ma_uint32 internalChannels; ma_uint32 internalChannels;
...@@ -6150,10 +6185,11 @@ struct ma_decoder ...@@ -6150,10 +6185,11 @@ struct ma_decoder
size_t dataSize; size_t dataSize;
size_t currentReadPos; size_t currentReadPos;
} memory; /* Only used for decoders that were opened against a block of memory. */ } memory; /* Only used for decoders that were opened against a block of memory. */
} backend; } data;
}; };
MA_API ma_decoder_config ma_decoder_config_init(ma_format outputFormat, ma_uint32 outputChannels, ma_uint32 outputSampleRate); MA_API ma_decoder_config ma_decoder_config_init(ma_format outputFormat, ma_uint32 outputChannels, ma_uint32 outputSampleRate);
MA_API ma_decoder_config ma_decoder_config_init_default();
MA_API ma_result ma_decoder_init(ma_decoder_read_proc onRead, ma_decoder_seek_proc onSeek, void* pUserData, const ma_decoder_config* pConfig, ma_decoder* pDecoder); MA_API ma_result ma_decoder_init(ma_decoder_read_proc onRead, ma_decoder_seek_proc onSeek, void* pUserData, const ma_decoder_config* pConfig, ma_decoder* pDecoder);
MA_API ma_result ma_decoder_init_wav(ma_decoder_read_proc onRead, ma_decoder_seek_proc onSeek, void* pUserData, const ma_decoder_config* pConfig, ma_decoder* pDecoder); MA_API ma_result ma_decoder_init_wav(ma_decoder_read_proc onRead, ma_decoder_seek_proc onSeek, void* pUserData, const ma_decoder_config* pConfig, ma_decoder* pDecoder);
...@@ -44776,7 +44812,7 @@ MA_API ma_result ma_vfs_open_and_read_file_w(ma_vfs* pVFS, const wchar_t* pFileP ...@@ -44776,7 +44812,7 @@ MA_API ma_result ma_vfs_open_and_read_file_w(ma_vfs* pVFS, const wchar_t* pFileP
} }
#if defined(MA_WIN32) && defined(MA_WIN32_DESKTOP) #if defined(MA_WIN32) && defined(MA_WIN32_DESKTOP) && !defined(MA_NO_WIN32_FILEIO)
static void ma_default_vfs__get_open_settings_win32(ma_uint32 openMode, DWORD* pDesiredAccess, DWORD* pShareMode, DWORD* pCreationDisposition) static void ma_default_vfs__get_open_settings_win32(ma_uint32 openMode, DWORD* pDesiredAccess, DWORD* pShareMode, DWORD* pCreationDisposition)
{ {
*pDesiredAccess = 0; *pDesiredAccess = 0;
...@@ -45142,24 +45178,33 @@ static ma_result ma_default_vfs_write__stdio(ma_vfs* pVFS, ma_vfs_file file, con ...@@ -45142,24 +45178,33 @@ static ma_result ma_default_vfs_write__stdio(ma_vfs* pVFS, ma_vfs_file file, con
static ma_result ma_default_vfs_seek__stdio(ma_vfs* pVFS, ma_vfs_file file, ma_int64 offset, ma_seek_origin origin) static ma_result ma_default_vfs_seek__stdio(ma_vfs* pVFS, ma_vfs_file file, ma_int64 offset, ma_seek_origin origin)
{ {
int result; int result;
int whence;
MA_ASSERT(file != NULL); MA_ASSERT(file != NULL);
(void)pVFS; (void)pVFS;
if (origin == ma_seek_origin_start) {
whence = SEEK_SET;
} else if (origin == ma_seek_origin_end) {
whence = SEEK_END;
} else {
whence = SEEK_CUR;
}
#if defined(_WIN32) #if defined(_WIN32)
#if defined(_MSC_VER) && _MSC_VER > 1200 #if defined(_MSC_VER) && _MSC_VER > 1200
result = _fseeki64((FILE*)file, offset, origin); result = _fseeki64((FILE*)file, offset, whence);
#else #else
/* No _fseeki64() so restrict to 31 bits. */ /* No _fseeki64() so restrict to 31 bits. */
if (origin > 0x7FFFFFFF) { if (origin > 0x7FFFFFFF) {
return MA_OUT_OF_RANGE; return MA_OUT_OF_RANGE;
} }
result = fseek((FILE*)file, (int)offset, origin); result = fseek((FILE*)file, (int)offset, whence);
#endif #endif
#else #else
result = fseek((FILE*)file, (long int)offset, origin); result = fseek((FILE*)file, (long int)offset, whence);
#endif #endif
if (result != 0) { if (result != 0) {
return MA_ERROR; return MA_ERROR;
...@@ -45235,7 +45280,7 @@ static ma_result ma_default_vfs_open(ma_vfs* pVFS, const char* pFilePath, ma_uin ...@@ -45235,7 +45280,7 @@ static ma_result ma_default_vfs_open(ma_vfs* pVFS, const char* pFilePath, ma_uin
return MA_INVALID_ARGS; return MA_INVALID_ARGS;
} }
#if defined(MA_WIN32) && defined(MA_WIN32_DESKTOP) #if defined(MA_WIN32) && defined(MA_WIN32_DESKTOP) && !defined(MA_NO_WIN32_FILEIO)
return ma_default_vfs_open__win32(pVFS, pFilePath, openMode, pFile); return ma_default_vfs_open__win32(pVFS, pFilePath, openMode, pFile);
#else #else
return ma_default_vfs_open__stdio(pVFS, pFilePath, openMode, pFile); return ma_default_vfs_open__stdio(pVFS, pFilePath, openMode, pFile);
...@@ -45254,7 +45299,7 @@ static ma_result ma_default_vfs_open_w(ma_vfs* pVFS, const wchar_t* pFilePath, m ...@@ -45254,7 +45299,7 @@ static ma_result ma_default_vfs_open_w(ma_vfs* pVFS, const wchar_t* pFilePath, m
return MA_INVALID_ARGS; return MA_INVALID_ARGS;
} }
#if defined(MA_WIN32) && defined(MA_WIN32_DESKTOP) #if defined(MA_WIN32) && defined(MA_WIN32_DESKTOP) && !defined(MA_NO_WIN32_FILEIO)
return ma_default_vfs_open_w__win32(pVFS, pFilePath, openMode, pFile); return ma_default_vfs_open_w__win32(pVFS, pFilePath, openMode, pFile);
#else #else
return ma_default_vfs_open_w__stdio(pVFS, pFilePath, openMode, pFile); return ma_default_vfs_open_w__stdio(pVFS, pFilePath, openMode, pFile);
...@@ -45267,7 +45312,7 @@ static ma_result ma_default_vfs_close(ma_vfs* pVFS, ma_vfs_file file) ...@@ -45267,7 +45312,7 @@ static ma_result ma_default_vfs_close(ma_vfs* pVFS, ma_vfs_file file)
return MA_INVALID_ARGS; return MA_INVALID_ARGS;
} }
#if defined(MA_WIN32) && defined(MA_WIN32_DESKTOP) #if defined(MA_WIN32) && defined(MA_WIN32_DESKTOP) && !defined(MA_NO_WIN32_FILEIO)
return ma_default_vfs_close__win32(pVFS, file); return ma_default_vfs_close__win32(pVFS, file);
#else #else
return ma_default_vfs_close__stdio(pVFS, file); return ma_default_vfs_close__stdio(pVFS, file);
...@@ -45284,7 +45329,7 @@ static ma_result ma_default_vfs_read(ma_vfs* pVFS, ma_vfs_file file, void* pDst, ...@@ -45284,7 +45329,7 @@ static ma_result ma_default_vfs_read(ma_vfs* pVFS, ma_vfs_file file, void* pDst,
return MA_INVALID_ARGS; return MA_INVALID_ARGS;
} }
#if defined(MA_WIN32) && defined(MA_WIN32_DESKTOP) #if defined(MA_WIN32) && defined(MA_WIN32_DESKTOP) && !defined(MA_NO_WIN32_FILEIO)
return ma_default_vfs_read__win32(pVFS, file, pDst, sizeInBytes, pBytesRead); return ma_default_vfs_read__win32(pVFS, file, pDst, sizeInBytes, pBytesRead);
#else #else
return ma_default_vfs_read__stdio(pVFS, file, pDst, sizeInBytes, pBytesRead); return ma_default_vfs_read__stdio(pVFS, file, pDst, sizeInBytes, pBytesRead);
...@@ -45301,7 +45346,7 @@ static ma_result ma_default_vfs_write(ma_vfs* pVFS, ma_vfs_file file, const void ...@@ -45301,7 +45346,7 @@ static ma_result ma_default_vfs_write(ma_vfs* pVFS, ma_vfs_file file, const void
return MA_INVALID_ARGS; return MA_INVALID_ARGS;
} }
#if defined(MA_WIN32) && defined(MA_WIN32_DESKTOP) #if defined(MA_WIN32) && defined(MA_WIN32_DESKTOP) && !defined(MA_NO_WIN32_FILEIO)
return ma_default_vfs_write__win32(pVFS, file, pSrc, sizeInBytes, pBytesWritten); return ma_default_vfs_write__win32(pVFS, file, pSrc, sizeInBytes, pBytesWritten);
#else #else
return ma_default_vfs_write__stdio(pVFS, file, pSrc, sizeInBytes, pBytesWritten); return ma_default_vfs_write__stdio(pVFS, file, pSrc, sizeInBytes, pBytesWritten);
...@@ -45314,7 +45359,7 @@ static ma_result ma_default_vfs_seek(ma_vfs* pVFS, ma_vfs_file file, ma_int64 of ...@@ -45314,7 +45359,7 @@ static ma_result ma_default_vfs_seek(ma_vfs* pVFS, ma_vfs_file file, ma_int64 of
return MA_INVALID_ARGS; return MA_INVALID_ARGS;
} }
#if defined(MA_WIN32) && defined(MA_WIN32_DESKTOP) #if defined(MA_WIN32) && defined(MA_WIN32_DESKTOP) && !defined(MA_NO_WIN32_FILEIO)
return ma_default_vfs_seek__win32(pVFS, file, offset, origin); return ma_default_vfs_seek__win32(pVFS, file, offset, origin);
#else #else
return ma_default_vfs_seek__stdio(pVFS, file, offset, origin); return ma_default_vfs_seek__stdio(pVFS, file, offset, origin);
...@@ -45333,7 +45378,7 @@ static ma_result ma_default_vfs_tell(ma_vfs* pVFS, ma_vfs_file file, ma_int64* p ...@@ -45333,7 +45378,7 @@ static ma_result ma_default_vfs_tell(ma_vfs* pVFS, ma_vfs_file file, ma_int64* p
return MA_INVALID_ARGS; return MA_INVALID_ARGS;
} }
#if defined(MA_WIN32) && defined(MA_WIN32_DESKTOP) #if defined(MA_WIN32) && defined(MA_WIN32_DESKTOP) && !defined(MA_NO_WIN32_FILEIO)
return ma_default_vfs_tell__win32(pVFS, file, pCursor); return ma_default_vfs_tell__win32(pVFS, file, pCursor);
#else #else
return ma_default_vfs_tell__stdio(pVFS, file, pCursor); return ma_default_vfs_tell__stdio(pVFS, file, pCursor);
...@@ -45352,7 +45397,7 @@ static ma_result ma_default_vfs_info(ma_vfs* pVFS, ma_vfs_file file, ma_file_inf ...@@ -45352,7 +45397,7 @@ static ma_result ma_default_vfs_info(ma_vfs* pVFS, ma_vfs_file file, ma_file_inf
return MA_INVALID_ARGS; return MA_INVALID_ARGS;
} }
#if defined(MA_WIN32) && defined(MA_WIN32_DESKTOP) #if defined(MA_WIN32) && defined(MA_WIN32_DESKTOP) && !defined(MA_NO_WIN32_FILEIO)
return ma_default_vfs_info__win32(pVFS, file, pInfo); return ma_default_vfs_info__win32(pVFS, file, pInfo);
#else #else
return ma_default_vfs_info__stdio(pVFS, file, pInfo); return ma_default_vfs_info__stdio(pVFS, file, pInfo);
...@@ -46449,20 +46494,23 @@ Decoding ...@@ -46449,20 +46494,23 @@ Decoding
**************************************************************************************************************************************************************/ **************************************************************************************************************************************************************/
#ifndef MA_NO_DECODING #ifndef MA_NO_DECODING
static size_t ma_decoder_read_bytes(ma_decoder* pDecoder, void* pBufferOut, size_t bytesToRead) static ma_result ma_decoder_read_bytes(ma_decoder* pDecoder, void* pBufferOut, size_t bytesToRead, size_t* pBytesRead)
{ {
size_t bytesRead; size_t bytesRead;
MA_ASSERT(pDecoder != NULL); MA_ASSERT(pDecoder != NULL);
MA_ASSERT(pBufferOut != NULL); MA_ASSERT(pBufferOut != NULL);
bytesRead = pDecoder->onRead(pDecoder, pBufferOut, bytesToRead); bytesRead = pDecoder->onRead(pDecoder, pBufferOut, bytesToRead);
pDecoder->readPointerInBytes += bytesRead;
return bytesRead; if (pBytesRead != NULL) {
*pBytesRead = bytesRead;
}
return MA_SUCCESS;
} }
static ma_bool32 ma_decoder_seek_bytes(ma_decoder* pDecoder, int byteOffset, ma_seek_origin origin) static ma_result ma_decoder_seek_bytes(ma_decoder* pDecoder, ma_int64 byteOffset, ma_seek_origin origin)
{ {
ma_bool32 wasSuccessful; ma_bool32 wasSuccessful;
...@@ -46470,14 +46518,32 @@ static ma_bool32 ma_decoder_seek_bytes(ma_decoder* pDecoder, int byteOffset, ma_ ...@@ -46470,14 +46518,32 @@ static ma_bool32 ma_decoder_seek_bytes(ma_decoder* pDecoder, int byteOffset, ma_
wasSuccessful = pDecoder->onSeek(pDecoder, byteOffset, origin); wasSuccessful = pDecoder->onSeek(pDecoder, byteOffset, origin);
if (wasSuccessful) { if (wasSuccessful) {
if (origin == ma_seek_origin_start) { return MA_SUCCESS;
pDecoder->readPointerInBytes = (ma_uint64)byteOffset; } else {
} else { return MA_ERROR;
pDecoder->readPointerInBytes += byteOffset;
}
} }
}
return wasSuccessful; static ma_result ma_decoder_tell_bytes(ma_decoder* pDecoder, ma_int64* pCursor)
{
MA_ASSERT(pDecoder != NULL);
if (pDecoder->onTell == NULL) {
return MA_NOT_IMPLEMENTED;
}
return pDecoder->onTell(pDecoder, pCursor);
}
MA_API ma_decoding_backend_config ma_decoding_backend_config_init(ma_format preferredFormat)
{
ma_decoding_backend_config config;
MA_ZERO_OBJECT(&config);
config.preferredFormat = preferredFormat;
return config;
} }
...@@ -46497,6 +46563,11 @@ MA_API ma_decoder_config ma_decoder_config_init(ma_format outputFormat, ma_uint3 ...@@ -46497,6 +46563,11 @@ MA_API ma_decoder_config ma_decoder_config_init(ma_format outputFormat, ma_uint3
return config; return config;
} }
MA_API ma_decoder_config ma_decoder_config_init_default()
{
return ma_decoder_config_init(ma_format_unknown, 0, 0);
}
MA_API ma_decoder_config ma_decoder_config_init_copy(const ma_decoder_config* pConfig) MA_API ma_decoder_config ma_decoder_config_init_copy(const ma_decoder_config* pConfig)
{ {
ma_decoder_config config; ma_decoder_config config;
...@@ -46570,6 +46641,88 @@ static ma_result ma_decoder__init_data_converter(ma_decoder* pDecoder, const ma_ ...@@ -46570,6 +46641,88 @@ static ma_result ma_decoder__init_data_converter(ma_decoder* pDecoder, const ma_
return ma_data_converter_init(&converterConfig, &pDecoder->converter); return ma_data_converter_init(&converterConfig, &pDecoder->converter);
} }
/* Custom decoders. */
static ma_result ma_decoder_internal_on_read__custom(void* pUserData, void* pBufferOut, size_t bytesToRead, size_t* pBytesRead)
{
ma_decoder* pDecoder = (ma_decoder*)pUserData;
MA_ASSERT(pDecoder != NULL);
return ma_decoder_read_bytes(pDecoder, pBufferOut, bytesToRead, pBytesRead);
}
static ma_result ma_decoder_internal_on_seek__custom(void* pUserData, ma_int64 offset, ma_seek_origin origin)
{
ma_decoder* pDecoder = (ma_decoder*)pUserData;
MA_ASSERT(pDecoder != NULL);
return ma_decoder_seek_bytes(pDecoder, offset, origin);
}
static ma_result ma_decoder_internal_on_tell__custom(void* pUserData, ma_int64* pCursor)
{
ma_decoder* pDecoder = (ma_decoder*)pUserData;
MA_ASSERT(pDecoder != NULL);
return ma_decoder_tell_bytes(pDecoder, pCursor);
}
static ma_result ma_decoder_init_custom__internal(const ma_decoder_config* pConfig, ma_decoder* pDecoder)
{
ma_result result = MA_NO_BACKEND;
ma_data_source* pBackend;
ma_decoding_backend_config backendConfig;
size_t ivtable;
MA_ASSERT(pConfig != NULL);
MA_ASSERT(pDecoder != NULL);
(void)pConfig;
if (pConfig->ppCustomBackendVTables == NULL) {
return MA_NO_BACKEND;
}
backendConfig = ma_decoding_backend_config_init(pConfig->format);
/* The order each backend is listed is what defines the priority. */
for (ivtable = 0; ivtable < pConfig->customBackendVTableCount; ivtable += 1) {
const ma_decoding_backend_vtable* pVTable = pConfig->ppCustomBackendVTables[ivtable];
if (pVTable != NULL && pVTable->onInit != NULL) {
result = pVTable->onInit(pConfig->pCustomBackendUserData, ma_decoder_internal_on_read__custom, ma_decoder_internal_on_seek__custom, ma_decoder_internal_on_tell__custom, pDecoder, &backendConfig, &pDecoder->allocationCallbacks, &pBackend);
if (result == MA_SUCCESS) {
/* We found our decoding backend. Now we just need to set up some stuff in the decoder. */
pDecoder->pBackend = pBackend;
pDecoder->pBackendVTable = pVTable;
pDecoder->pBackendUserData = pConfig->pCustomBackendUserData;
/* Internal format/channels/rate. */
ma_data_source_get_data_format(pDecoder->pBackend, &pDecoder->internalFormat, &pDecoder->internalChannels, &pDecoder->internalSampleRate);
/* Internal channel map. For now we need to use a separate vtable API for this, but later on we'll add this to ma_data_source_get_data_format(). */
if (pVTable->onGetChannelMap == NULL || pVTable->onGetChannelMap(pDecoder->pBackendUserData, pDecoder->pBackend, pDecoder->internalChannelMap, ma_countof(pDecoder->internalChannelMap)) != MA_SUCCESS) {
/* Failed to retrieve the channel map. Assume default. */
ma_get_standard_channel_map(ma_standard_channel_map_default, ma_min(pDecoder->internalChannels, ma_countof(pDecoder->internalChannelMap)), pDecoder->internalChannelMap);
}
return MA_SUCCESS;
} else {
/* Initialization failed. Move on to the next one, but seek back to the start. */
result = ma_decoder_seek_bytes(pDecoder, 0, ma_seek_origin_start);
if (result != MA_SUCCESS) {
return result; /* Failed to seek back to the start. */
}
}
} else {
/* No onInit callback. */
}
}
/* Getting here means we couldn't find a backend. */
return MA_NO_BACKEND;
}
/* WAV */ /* WAV */
#ifdef dr_wav_h #ifdef dr_wav_h
#define MA_HAS_WAV #define MA_HAS_WAV
...@@ -46577,17 +46730,30 @@ static ma_result ma_decoder__init_data_converter(ma_decoder* pDecoder, const ma_ ...@@ -46577,17 +46730,30 @@ static ma_result ma_decoder__init_data_converter(ma_decoder* pDecoder, const ma_
static size_t ma_decoder_internal_on_read__wav(void* pUserData, void* pBufferOut, size_t bytesToRead) static size_t ma_decoder_internal_on_read__wav(void* pUserData, void* pBufferOut, size_t bytesToRead)
{ {
ma_decoder* pDecoder = (ma_decoder*)pUserData; ma_decoder* pDecoder = (ma_decoder*)pUserData;
ma_result result;
size_t bytesRead;
MA_ASSERT(pDecoder != NULL); MA_ASSERT(pDecoder != NULL);
return ma_decoder_read_bytes(pDecoder, pBufferOut, bytesToRead); result = ma_decoder_read_bytes(pDecoder, pBufferOut, bytesToRead, &bytesRead);
(void)result;
return bytesRead;
} }
static drwav_bool32 ma_decoder_internal_on_seek__wav(void* pUserData, int offset, drwav_seek_origin origin) static drwav_bool32 ma_decoder_internal_on_seek__wav(void* pUserData, int offset, drwav_seek_origin origin)
{ {
ma_decoder* pDecoder = (ma_decoder*)pUserData; ma_decoder* pDecoder = (ma_decoder*)pUserData;
ma_result result;
MA_ASSERT(pDecoder != NULL); MA_ASSERT(pDecoder != NULL);
return ma_decoder_seek_bytes(pDecoder, offset, (origin == drwav_seek_origin_start) ? ma_seek_origin_start : ma_seek_origin_current); result = ma_decoder_seek_bytes(pDecoder, offset, (origin == drwav_seek_origin_start) ? ma_seek_origin_start : ma_seek_origin_current);
if (result != MA_SUCCESS) {
return MA_FALSE;
}
return MA_TRUE;
} }
static ma_uint64 ma_decoder_internal_on_read_pcm_frames__wav(ma_decoder* pDecoder, void* pFramesOut, ma_uint64 frameCount) static ma_uint64 ma_decoder_internal_on_read_pcm_frames__wav(ma_decoder* pDecoder, void* pFramesOut, ma_uint64 frameCount)
...@@ -46722,17 +46888,30 @@ static ma_result ma_decoder_init_wav__internal(const ma_decoder_config* pConfig, ...@@ -46722,17 +46888,30 @@ static ma_result ma_decoder_init_wav__internal(const ma_decoder_config* pConfig,
static size_t ma_decoder_internal_on_read__flac(void* pUserData, void* pBufferOut, size_t bytesToRead) static size_t ma_decoder_internal_on_read__flac(void* pUserData, void* pBufferOut, size_t bytesToRead)
{ {
ma_decoder* pDecoder = (ma_decoder*)pUserData; ma_decoder* pDecoder = (ma_decoder*)pUserData;
ma_result result;
size_t bytesRead;
MA_ASSERT(pDecoder != NULL); MA_ASSERT(pDecoder != NULL);
return ma_decoder_read_bytes(pDecoder, pBufferOut, bytesToRead); result = ma_decoder_read_bytes(pDecoder, pBufferOut, bytesToRead, &bytesRead);
(void)result;
return bytesRead;
} }
static drflac_bool32 ma_decoder_internal_on_seek__flac(void* pUserData, int offset, drflac_seek_origin origin) static drflac_bool32 ma_decoder_internal_on_seek__flac(void* pUserData, int offset, drflac_seek_origin origin)
{ {
ma_decoder* pDecoder = (ma_decoder*)pUserData; ma_decoder* pDecoder = (ma_decoder*)pUserData;
ma_result result;
MA_ASSERT(pDecoder != NULL); MA_ASSERT(pDecoder != NULL);
return ma_decoder_seek_bytes(pDecoder, offset, (origin == drflac_seek_origin_start) ? ma_seek_origin_start : ma_seek_origin_current); result = ma_decoder_seek_bytes(pDecoder, offset, (origin == drflac_seek_origin_start) ? ma_seek_origin_start : ma_seek_origin_current);
if (result != MA_SUCCESS) {
return MA_FALSE;
}
return MA_TRUE;
} }
static ma_uint64 ma_decoder_internal_on_read_pcm_frames__flac(ma_decoder* pDecoder, void* pFramesOut, ma_uint64 frameCount) static ma_uint64 ma_decoder_internal_on_read_pcm_frames__flac(ma_decoder* pDecoder, void* pFramesOut, ma_uint64 frameCount)
...@@ -46843,17 +47022,30 @@ static ma_result ma_decoder_init_flac__internal(const ma_decoder_config* pConfig ...@@ -46843,17 +47022,30 @@ static ma_result ma_decoder_init_flac__internal(const ma_decoder_config* pConfig
static size_t ma_decoder_internal_on_read__mp3(void* pUserData, void* pBufferOut, size_t bytesToRead) static size_t ma_decoder_internal_on_read__mp3(void* pUserData, void* pBufferOut, size_t bytesToRead)
{ {
ma_decoder* pDecoder = (ma_decoder*)pUserData; ma_decoder* pDecoder = (ma_decoder*)pUserData;
ma_result result;
size_t bytesRead;
MA_ASSERT(pDecoder != NULL); MA_ASSERT(pDecoder != NULL);
return ma_decoder_read_bytes(pDecoder, pBufferOut, bytesToRead); result = ma_decoder_read_bytes(pDecoder, pBufferOut, bytesToRead, &bytesRead);
(void)result;
return bytesRead;
} }
static drmp3_bool32 ma_decoder_internal_on_seek__mp3(void* pUserData, int offset, drmp3_seek_origin origin) static drmp3_bool32 ma_decoder_internal_on_seek__mp3(void* pUserData, int offset, drmp3_seek_origin origin)
{ {
ma_decoder* pDecoder = (ma_decoder*)pUserData; ma_decoder* pDecoder = (ma_decoder*)pUserData;
ma_result result;
MA_ASSERT(pDecoder != NULL); MA_ASSERT(pDecoder != NULL);
return ma_decoder_seek_bytes(pDecoder, offset, (origin == drmp3_seek_origin_start) ? ma_seek_origin_start : ma_seek_origin_current); result = ma_decoder_seek_bytes(pDecoder, offset, (origin == drmp3_seek_origin_start) ? ma_seek_origin_start : ma_seek_origin_current);
if (result != MA_SUCCESS) {
return MA_FALSE;
}
return MA_TRUE;
} }
static ma_uint64 ma_decoder_internal_on_read_pcm_frames__mp3(ma_decoder* pDecoder, void* pFramesOut, ma_uint64 frameCount) static ma_uint64 ma_decoder_internal_on_read_pcm_frames__mp3(ma_decoder* pDecoder, void* pFramesOut, ma_uint64 frameCount)
...@@ -46975,6 +47167,7 @@ static ma_uint64 ma_vorbis_decoder_read_pcm_frames(ma_vorbis_decoder* pVorbis, m ...@@ -46975,6 +47167,7 @@ static ma_uint64 ma_vorbis_decoder_read_pcm_frames(ma_vorbis_decoder* pVorbis, m
{ {
float* pFramesOutF; float* pFramesOutF;
ma_uint64 totalFramesRead; ma_uint64 totalFramesRead;
ma_result result;
MA_ASSERT(pVorbis != NULL); MA_ASSERT(pVorbis != NULL);
MA_ASSERT(pDecoder != NULL); MA_ASSERT(pDecoder != NULL);
...@@ -47050,12 +47243,12 @@ static ma_uint64 ma_vorbis_decoder_read_pcm_frames(ma_vorbis_decoder* pVorbis, m ...@@ -47050,12 +47243,12 @@ static ma_uint64 ma_vorbis_decoder_read_pcm_frames(ma_vorbis_decoder* pVorbis, m
} }
/* Fill in a chunk. */ /* Fill in a chunk. */
bytesRead = ma_decoder_read_bytes(pDecoder, pVorbis->pData + pVorbis->dataSize, (pVorbis->dataCapacity - pVorbis->dataSize)); result = ma_decoder_read_bytes(pDecoder, pVorbis->pData + pVorbis->dataSize, (pVorbis->dataCapacity - pVorbis->dataSize), &bytesRead);
if (bytesRead == 0) { pVorbis->dataSize += bytesRead;
if (result != MA_SUCCESS) {
return totalFramesRead; /* Error reading more data. */ return totalFramesRead; /* Error reading more data. */
} }
pVorbis->dataSize += bytesRead;
} }
} while (MA_TRUE); } while (MA_TRUE);
} }
...@@ -47066,6 +47259,7 @@ static ma_uint64 ma_vorbis_decoder_read_pcm_frames(ma_vorbis_decoder* pVorbis, m ...@@ -47066,6 +47259,7 @@ static ma_uint64 ma_vorbis_decoder_read_pcm_frames(ma_vorbis_decoder* pVorbis, m
static ma_result ma_vorbis_decoder_seek_to_pcm_frame(ma_vorbis_decoder* pVorbis, ma_decoder* pDecoder, ma_uint64 frameIndex) static ma_result ma_vorbis_decoder_seek_to_pcm_frame(ma_vorbis_decoder* pVorbis, ma_decoder* pDecoder, ma_uint64 frameIndex)
{ {
float buffer[4096]; float buffer[4096];
ma_result result;
MA_ASSERT(pVorbis != NULL); MA_ASSERT(pVorbis != NULL);
MA_ASSERT(pDecoder != NULL); MA_ASSERT(pDecoder != NULL);
...@@ -47077,8 +47271,9 @@ static ma_result ma_vorbis_decoder_seek_to_pcm_frame(ma_vorbis_decoder* pVorbis, ...@@ -47077,8 +47271,9 @@ static ma_result ma_vorbis_decoder_seek_to_pcm_frame(ma_vorbis_decoder* pVorbis,
TODO: Use seeking logic documented for stb_vorbis_flush_pushdata(). TODO: Use seeking logic documented for stb_vorbis_flush_pushdata().
*/ */
if (!ma_decoder_seek_bytes(pDecoder, 0, ma_seek_origin_start)) { result = ma_decoder_seek_bytes(pDecoder, 0, ma_seek_origin_start);
return MA_ERROR; if (result != MA_SUCCESS) {
return result;
} }
stb_vorbis_flush_pushdata(pVorbis->pInternalVorbis); stb_vorbis_flush_pushdata(pVorbis->pInternalVorbis);
...@@ -47148,6 +47343,7 @@ static ma_uint64 ma_decoder_internal_on_get_length_in_pcm_frames__vorbis(ma_deco ...@@ -47148,6 +47343,7 @@ static ma_uint64 ma_decoder_internal_on_get_length_in_pcm_frames__vorbis(ma_deco
static ma_result ma_decoder_init_vorbis__internal(const ma_decoder_config* pConfig, ma_decoder* pDecoder) static ma_result ma_decoder_init_vorbis__internal(const ma_decoder_config* pConfig, ma_decoder* pDecoder)
{ {
ma_result result;
stb_vorbis* pInternalVorbis = NULL; stb_vorbis* pInternalVorbis = NULL;
size_t dataSize = 0; size_t dataSize = 0;
size_t dataCapacity = 0; size_t dataCapacity = 0;
...@@ -47179,12 +47375,13 @@ static ma_result ma_decoder_init_vorbis__internal(const ma_decoder_config* pConf ...@@ -47179,12 +47375,13 @@ static ma_result ma_decoder_init_vorbis__internal(const ma_decoder_config* pConf
pData = pNewData; pData = pNewData;
/* Fill in a chunk. */ /* Fill in a chunk. */
bytesRead = ma_decoder_read_bytes(pDecoder, pData + dataSize, (dataCapacity - dataSize)); result = ma_decoder_read_bytes(pDecoder, pData + dataSize, (dataCapacity - dataSize), &bytesRead);
if (bytesRead == 0) { dataSize += bytesRead;
return MA_ERROR;
if (result != MA_SUCCESS) {
return result;
} }
dataSize += bytesRead;
if (dataSize > INT_MAX) { if (dataSize > INT_MAX) {
return MA_ERROR; /* Too big. */ return MA_ERROR; /* Too big. */
} }
...@@ -47256,6 +47453,7 @@ static ma_result ma_decoder_init_vorbis__internal(const ma_decoder_config* pConf ...@@ -47256,6 +47453,7 @@ static ma_result ma_decoder_init_vorbis__internal(const ma_decoder_config* pConf
/* Raw */ /* Raw */
static ma_uint64 ma_decoder_internal_on_read_pcm_frames__raw(ma_decoder* pDecoder, void* pFramesOut, ma_uint64 frameCount) static ma_uint64 ma_decoder_internal_on_read_pcm_frames__raw(ma_decoder* pDecoder, void* pFramesOut, ma_uint64 frameCount)
{ {
ma_result result;
ma_uint32 bpf; ma_uint32 bpf;
ma_uint64 totalFramesRead; ma_uint64 totalFramesRead;
void* pRunningFramesOut; void* pRunningFramesOut;
...@@ -47276,11 +47474,12 @@ static ma_uint64 ma_decoder_internal_on_read_pcm_frames__raw(ma_decoder* pDecode ...@@ -47276,11 +47474,12 @@ static ma_uint64 ma_decoder_internal_on_read_pcm_frames__raw(ma_decoder* pDecode
} }
if (pFramesOut != NULL) { if (pFramesOut != NULL) {
framesReadThisIteration = ma_decoder_read_bytes(pDecoder, pRunningFramesOut, (size_t)framesToReadThisIteration * bpf) / bpf; /* Safe cast to size_t. */ result = ma_decoder_read_bytes(pDecoder, pRunningFramesOut, (size_t)framesToReadThisIteration * bpf, &framesReadThisIteration); /* Safe cast to size_t. */
framesReadThisIteration /= bpf;
pRunningFramesOut = ma_offset_ptr(pRunningFramesOut, framesReadThisIteration * bpf); pRunningFramesOut = ma_offset_ptr(pRunningFramesOut, framesReadThisIteration * bpf);
} else { } else {
/* We'll first try seeking. If this fails it means the end was reached and we'll to do a read-and-discard slow path to get the exact amount. */ /* We'll first try seeking. If this fails it means the end was reached and we'll to do a read-and-discard slow path to get the exact amount. */
if (ma_decoder_seek_bytes(pDecoder, (int)framesToReadThisIteration, ma_seek_origin_current)) { if (ma_decoder_seek_bytes(pDecoder, (int)framesToReadThisIteration, ma_seek_origin_current) == MA_SUCCESS) {
framesReadThisIteration = framesToReadThisIteration; framesReadThisIteration = framesToReadThisIteration;
} else { } else {
/* Slow path. Need to fall back to a read-and-discard. This is required so we can get the exact number of remaining. */ /* Slow path. Need to fall back to a read-and-discard. This is required so we can get the exact number of remaining. */
...@@ -47295,10 +47494,11 @@ static ma_uint64 ma_decoder_internal_on_read_pcm_frames__raw(ma_decoder* pDecode ...@@ -47295,10 +47494,11 @@ static ma_uint64 ma_decoder_internal_on_read_pcm_frames__raw(ma_decoder* pDecode
framesToReadNow = bufferCap; framesToReadNow = bufferCap;
} }
framesReadNow = ma_decoder_read_bytes(pDecoder, buffer, (size_t)(framesToReadNow * bpf)) / bpf; /* Safe cast. */ result = ma_decoder_read_bytes(pDecoder, buffer, (size_t)(framesToReadNow * bpf), &framesReadNow); /* Safe cast. */
framesReadNow /= bpf;
framesReadThisIteration += framesReadNow; framesReadThisIteration += framesReadNow;
if (framesReadNow < framesToReadNow) { if (result != MA_SUCCESS || framesReadNow < framesToReadNow) {
break; /* The end has been reached. */ break; /* The end has been reached. */
} }
} }
...@@ -47317,7 +47517,7 @@ static ma_uint64 ma_decoder_internal_on_read_pcm_frames__raw(ma_decoder* pDecode ...@@ -47317,7 +47517,7 @@ static ma_uint64 ma_decoder_internal_on_read_pcm_frames__raw(ma_decoder* pDecode
static ma_result ma_decoder_internal_on_seek_to_pcm_frame__raw(ma_decoder* pDecoder, ma_uint64 frameIndex) static ma_result ma_decoder_internal_on_seek_to_pcm_frame__raw(ma_decoder* pDecoder, ma_uint64 frameIndex)
{ {
ma_bool32 result = MA_FALSE; ma_result result = MA_ERROR;
ma_uint64 totalBytesToSeek; ma_uint64 totalBytesToSeek;
MA_ASSERT(pDecoder != NULL); MA_ASSERT(pDecoder != NULL);
...@@ -47334,7 +47534,7 @@ static ma_result ma_decoder_internal_on_seek_to_pcm_frame__raw(ma_decoder* pDeco ...@@ -47334,7 +47534,7 @@ static ma_result ma_decoder_internal_on_seek_to_pcm_frame__raw(ma_decoder* pDeco
} else { } else {
/* Complex case. Start by doing a seek relative to the start. Then keep looping using offset seeking. */ /* Complex case. Start by doing a seek relative to the start. Then keep looping using offset seeking. */
result = ma_decoder_seek_bytes(pDecoder, 0x7FFFFFFF, ma_seek_origin_start); result = ma_decoder_seek_bytes(pDecoder, 0x7FFFFFFF, ma_seek_origin_start);
if (result == MA_TRUE) { if (result == MA_SUCCESS) {
totalBytesToSeek -= 0x7FFFFFFF; totalBytesToSeek -= 0x7FFFFFFF;
while (totalBytesToSeek > 0) { while (totalBytesToSeek > 0) {
...@@ -47344,7 +47544,7 @@ static ma_result ma_decoder_internal_on_seek_to_pcm_frame__raw(ma_decoder* pDeco ...@@ -47344,7 +47544,7 @@ static ma_result ma_decoder_internal_on_seek_to_pcm_frame__raw(ma_decoder* pDeco
} }
result = ma_decoder_seek_bytes(pDecoder, (int)bytesToSeekThisIteration, ma_seek_origin_current); result = ma_decoder_seek_bytes(pDecoder, (int)bytesToSeekThisIteration, ma_seek_origin_current);
if (result != MA_TRUE) { if (result != MA_SUCCESS) {
break; break;
} }
...@@ -47353,11 +47553,7 @@ static ma_result ma_decoder_internal_on_seek_to_pcm_frame__raw(ma_decoder* pDeco ...@@ -47353,11 +47553,7 @@ static ma_result ma_decoder_internal_on_seek_to_pcm_frame__raw(ma_decoder* pDeco
} }
} }
if (result) { return result;
return MA_SUCCESS;
} else {
return MA_ERROR;
}
} }
static ma_result ma_decoder_internal_on_uninit__raw(ma_decoder* pDecoder) static ma_result ma_decoder_internal_on_uninit__raw(ma_decoder* pDecoder)
...@@ -47467,7 +47663,7 @@ static ma_data_source_vtable g_ma_decoder_data_source_vtable = ...@@ -47467,7 +47663,7 @@ static ma_data_source_vtable g_ma_decoder_data_source_vtable =
ma_decoder__data_source_on_get_length ma_decoder__data_source_on_get_length
}; };
static ma_result ma_decoder__preinit(ma_decoder_read_proc onRead, ma_decoder_seek_proc onSeek, void* pUserData, const ma_decoder_config* pConfig, ma_decoder* pDecoder) static ma_result ma_decoder__preinit(ma_decoder_read_proc onRead, ma_decoder_seek_proc onSeek, ma_decoder_tell_proc onTell, void* pUserData, const ma_decoder_config* pConfig, ma_decoder* pDecoder)
{ {
ma_result result; ma_result result;
ma_data_source_config dataSourceConfig; ma_data_source_config dataSourceConfig;
...@@ -47494,6 +47690,7 @@ static ma_result ma_decoder__preinit(ma_decoder_read_proc onRead, ma_decoder_see ...@@ -47494,6 +47690,7 @@ static ma_result ma_decoder__preinit(ma_decoder_read_proc onRead, ma_decoder_see
pDecoder->onRead = onRead; pDecoder->onRead = onRead;
pDecoder->onSeek = onSeek; pDecoder->onSeek = onSeek;
pDecoder->onTell = onTell;
pDecoder->pUserData = pUserData; pDecoder->pUserData = pUserData;
result = ma_decoder__init_allocation_callbacks(pConfig, pDecoder); result = ma_decoder__init_allocation_callbacks(pConfig, pDecoder);
...@@ -47535,7 +47732,7 @@ MA_API ma_result ma_decoder_init_wav(ma_decoder_read_proc onRead, ma_decoder_see ...@@ -47535,7 +47732,7 @@ MA_API ma_result ma_decoder_init_wav(ma_decoder_read_proc onRead, ma_decoder_see
config = ma_decoder_config_init_copy(pConfig); config = ma_decoder_config_init_copy(pConfig);
result = ma_decoder__preinit(onRead, onSeek, pUserData, &config, pDecoder); result = ma_decoder__preinit(onRead, onSeek, NULL, pUserData, &config, pDecoder);
if (result != MA_SUCCESS) { if (result != MA_SUCCESS) {
return result; return result;
} }
...@@ -47564,7 +47761,7 @@ MA_API ma_result ma_decoder_init_flac(ma_decoder_read_proc onRead, ma_decoder_se ...@@ -47564,7 +47761,7 @@ MA_API ma_result ma_decoder_init_flac(ma_decoder_read_proc onRead, ma_decoder_se
config = ma_decoder_config_init_copy(pConfig); config = ma_decoder_config_init_copy(pConfig);
result = ma_decoder__preinit(onRead, onSeek, pUserData, &config, pDecoder); result = ma_decoder__preinit(onRead, onSeek, NULL, pUserData, &config, pDecoder);
if (result != MA_SUCCESS) { if (result != MA_SUCCESS) {
return result; return result;
} }
...@@ -47593,7 +47790,7 @@ MA_API ma_result ma_decoder_init_mp3(ma_decoder_read_proc onRead, ma_decoder_see ...@@ -47593,7 +47790,7 @@ MA_API ma_result ma_decoder_init_mp3(ma_decoder_read_proc onRead, ma_decoder_see
config = ma_decoder_config_init_copy(pConfig); config = ma_decoder_config_init_copy(pConfig);
result = ma_decoder__preinit(onRead, onSeek, pUserData, &config, pDecoder); result = ma_decoder__preinit(onRead, onSeek, NULL, pUserData, &config, pDecoder);
if (result != MA_SUCCESS) { if (result != MA_SUCCESS) {
return result; return result;
} }
...@@ -47622,7 +47819,7 @@ MA_API ma_result ma_decoder_init_vorbis(ma_decoder_read_proc onRead, ma_decoder_ ...@@ -47622,7 +47819,7 @@ MA_API ma_result ma_decoder_init_vorbis(ma_decoder_read_proc onRead, ma_decoder_
config = ma_decoder_config_init_copy(pConfig); config = ma_decoder_config_init_copy(pConfig);
result = ma_decoder__preinit(onRead, onSeek, pUserData, &config, pDecoder); result = ma_decoder__preinit(onRead, onSeek, NULL, pUserData, &config, pDecoder);
if (result != MA_SUCCESS) { if (result != MA_SUCCESS) {
return result; return result;
} }
...@@ -47650,7 +47847,7 @@ MA_API ma_result ma_decoder_init_raw(ma_decoder_read_proc onRead, ma_decoder_see ...@@ -47650,7 +47847,7 @@ MA_API ma_result ma_decoder_init_raw(ma_decoder_read_proc onRead, ma_decoder_see
config = ma_decoder_config_init_copy(pConfigOut); config = ma_decoder_config_init_copy(pConfigOut);
result = ma_decoder__preinit(onRead, onSeek, pUserData, &config, pDecoder); result = ma_decoder__preinit(onRead, onSeek, NULL, pUserData, &config, pDecoder);
if (result != MA_SUCCESS) { if (result != MA_SUCCESS) {
return result; return result;
} }
...@@ -47674,10 +47871,17 @@ static ma_result ma_decoder_init__internal(ma_decoder_read_proc onRead, ma_decod ...@@ -47674,10 +47871,17 @@ static ma_result ma_decoder_init__internal(ma_decoder_read_proc onRead, ma_decod
(void)onRead; (void)onRead;
(void)onSeek; (void)onSeek;
(void)pUserData; (void)pUserData;
(void)pConfig;
(void)pDecoder;
/* We use trial and error to open a decoder. */ /*
We use trial and error to open a decoder. We prioritize custom decoders so that if they
implement the same encoding format they take priority over the built-in decoders.
*/
if (result != MA_SUCCESS) {
result = ma_decoder_init_custom__internal(pConfig, pDecoder);
if (result != MA_SUCCESS) {
onSeek(pDecoder, 0, ma_seek_origin_start);
}
}
#ifdef MA_HAS_WAV #ifdef MA_HAS_WAV
if (result != MA_SUCCESS) { if (result != MA_SUCCESS) {
...@@ -47726,7 +47930,7 @@ MA_API ma_result ma_decoder_init(ma_decoder_read_proc onRead, ma_decoder_seek_pr ...@@ -47726,7 +47930,7 @@ MA_API ma_result ma_decoder_init(ma_decoder_read_proc onRead, ma_decoder_seek_pr
config = ma_decoder_config_init_copy(pConfig); config = ma_decoder_config_init_copy(pConfig);
result = ma_decoder__preinit(onRead, onSeek, pUserData, &config, pDecoder); result = ma_decoder__preinit(onRead, onSeek, NULL, pUserData, &config, pDecoder);
if (result != MA_SUCCESS) { if (result != MA_SUCCESS) {
return result; return result;
} }
...@@ -47739,50 +47943,72 @@ static size_t ma_decoder__on_read_memory(ma_decoder* pDecoder, void* pBufferOut, ...@@ -47739,50 +47943,72 @@ static size_t ma_decoder__on_read_memory(ma_decoder* pDecoder, void* pBufferOut,
{ {
size_t bytesRemaining; size_t bytesRemaining;
MA_ASSERT(pDecoder->backend.memory.dataSize >= pDecoder->backend.memory.currentReadPos); MA_ASSERT(pDecoder->data.memory.dataSize >= pDecoder->data.memory.currentReadPos);
bytesRemaining = pDecoder->backend.memory.dataSize - pDecoder->backend.memory.currentReadPos; bytesRemaining = pDecoder->data.memory.dataSize - pDecoder->data.memory.currentReadPos;
if (bytesToRead > bytesRemaining) { if (bytesToRead > bytesRemaining) {
bytesToRead = bytesRemaining; bytesToRead = bytesRemaining;
} }
if (bytesToRead > 0) { if (bytesToRead > 0) {
MA_COPY_MEMORY(pBufferOut, pDecoder->backend.memory.pData + pDecoder->backend.memory.currentReadPos, bytesToRead); MA_COPY_MEMORY(pBufferOut, pDecoder->data.memory.pData + pDecoder->data.memory.currentReadPos, bytesToRead);
pDecoder->backend.memory.currentReadPos += bytesToRead; pDecoder->data.memory.currentReadPos += bytesToRead;
} }
return bytesToRead; return bytesToRead;
} }
static ma_bool32 ma_decoder__on_seek_memory(ma_decoder* pDecoder, int byteOffset, ma_seek_origin origin) static ma_bool32 ma_decoder__on_seek_memory(ma_decoder* pDecoder, ma_int64 byteOffset, ma_seek_origin origin)
{ {
if (origin == ma_seek_origin_current) { if (origin == ma_seek_origin_current) {
if (byteOffset > 0) { if (byteOffset > 0) {
if (pDecoder->backend.memory.currentReadPos + byteOffset > pDecoder->backend.memory.dataSize) { if (pDecoder->data.memory.currentReadPos + byteOffset > pDecoder->data.memory.dataSize) {
byteOffset = (int)(pDecoder->backend.memory.dataSize - pDecoder->backend.memory.currentReadPos); /* Trying to seek too far forward. */ byteOffset = (ma_int64)(pDecoder->data.memory.dataSize - pDecoder->data.memory.currentReadPos); /* Trying to seek too far forward. */
} }
} else { } else {
if (pDecoder->backend.memory.currentReadPos < (size_t)-byteOffset) { if (pDecoder->data.memory.currentReadPos < (size_t)-byteOffset) {
byteOffset = -(int)pDecoder->backend.memory.currentReadPos; /* Trying to seek too far backwards. */ byteOffset = -(ma_int64)pDecoder->data.memory.currentReadPos; /* Trying to seek too far backwards. */
} }
} }
/* This will never underflow thanks to the clamps above. */ /* This will never underflow thanks to the clamps above. */
pDecoder->backend.memory.currentReadPos += byteOffset; pDecoder->data.memory.currentReadPos += byteOffset;
} else { } else {
if ((ma_uint32)byteOffset <= pDecoder->backend.memory.dataSize) { if (origin == ma_seek_origin_end) {
pDecoder->backend.memory.currentReadPos = byteOffset; if (byteOffset < 0) {
byteOffset = -byteOffset;
}
if (byteOffset > (ma_int64)pDecoder->data.memory.dataSize) {
pDecoder->data.memory.currentReadPos = 0; /* Trying to seek too far back. */
} else {
pDecoder->data.memory.currentReadPos = pDecoder->data.memory.dataSize - byteOffset;
}
} else { } else {
pDecoder->backend.memory.currentReadPos = pDecoder->backend.memory.dataSize; /* Trying to seek too far forward. */ if ((size_t)byteOffset <= pDecoder->data.memory.dataSize) {
pDecoder->data.memory.currentReadPos = byteOffset;
} else {
pDecoder->data.memory.currentReadPos = pDecoder->data.memory.dataSize; /* Trying to seek too far forward. */
}
} }
} }
return MA_TRUE; return MA_TRUE;
} }
static ma_result ma_decoder__on_tell_memory(ma_decoder* pDecoder, ma_int64* pCursor)
{
MA_ASSERT(pDecoder != NULL);
MA_ASSERT(pCursor != NULL);
*pCursor = (ma_int64)pDecoder->data.memory.currentReadPos;
return MA_SUCCESS;
}
static ma_result ma_decoder__preinit_memory(const void* pData, size_t dataSize, const ma_decoder_config* pConfig, ma_decoder* pDecoder) static ma_result ma_decoder__preinit_memory(const void* pData, size_t dataSize, const ma_decoder_config* pConfig, ma_decoder* pDecoder)
{ {
ma_result result = ma_decoder__preinit(ma_decoder__on_read_memory, ma_decoder__on_seek_memory, NULL, pConfig, pDecoder); ma_result result = ma_decoder__preinit(ma_decoder__on_read_memory, ma_decoder__on_seek_memory, ma_decoder__on_tell_memory, NULL, pConfig, pDecoder);
if (result != MA_SUCCESS) { if (result != MA_SUCCESS) {
return result; return result;
} }
...@@ -47791,9 +48017,9 @@ static ma_result ma_decoder__preinit_memory(const void* pData, size_t dataSize, ...@@ -47791,9 +48017,9 @@ static ma_result ma_decoder__preinit_memory(const void* pData, size_t dataSize,
return MA_INVALID_ARGS; return MA_INVALID_ARGS;
} }
pDecoder->backend.memory.pData = (const ma_uint8*)pData; pDecoder->data.memory.pData = (const ma_uint8*)pData;
pDecoder->backend.memory.dataSize = dataSize; pDecoder->data.memory.dataSize = dataSize;
pDecoder->backend.memory.currentReadPos = 0; pDecoder->data.memory.currentReadPos = 0;
(void)pConfig; (void)pConfig;
return MA_SUCCESS; return MA_SUCCESS;
...@@ -48133,18 +48359,18 @@ static size_t ma_decoder__on_read_vfs(ma_decoder* pDecoder, void* pBufferOut, si ...@@ -48133,18 +48359,18 @@ static size_t ma_decoder__on_read_vfs(ma_decoder* pDecoder, void* pBufferOut, si
MA_ASSERT(pDecoder != NULL); MA_ASSERT(pDecoder != NULL);
MA_ASSERT(pBufferOut != NULL); MA_ASSERT(pBufferOut != NULL);
ma_vfs_or_default_read(pDecoder->backend.vfs.pVFS, pDecoder->backend.vfs.file, pBufferOut, bytesToRead, &bytesRead); ma_vfs_or_default_read(pDecoder->data.vfs.pVFS, pDecoder->data.vfs.file, pBufferOut, bytesToRead, &bytesRead);
return bytesRead; return bytesRead;
} }
static ma_bool32 ma_decoder__on_seek_vfs(ma_decoder* pDecoder, int offset, ma_seek_origin origin) static ma_bool32 ma_decoder__on_seek_vfs(ma_decoder* pDecoder, ma_int64 offset, ma_seek_origin origin)
{ {
ma_result result; ma_result result;
MA_ASSERT(pDecoder != NULL); MA_ASSERT(pDecoder != NULL);
result = ma_vfs_or_default_seek(pDecoder->backend.vfs.pVFS, pDecoder->backend.vfs.file, offset, origin); result = ma_vfs_or_default_seek(pDecoder->data.vfs.pVFS, pDecoder->data.vfs.file, offset, origin);
if (result != MA_SUCCESS) { if (result != MA_SUCCESS) {
return MA_FALSE; return MA_FALSE;
} }
...@@ -48152,12 +48378,19 @@ static ma_bool32 ma_decoder__on_seek_vfs(ma_decoder* pDecoder, int offset, ma_se ...@@ -48152,12 +48378,19 @@ static ma_bool32 ma_decoder__on_seek_vfs(ma_decoder* pDecoder, int offset, ma_se
return MA_TRUE; return MA_TRUE;
} }
static ma_result ma_decoder__on_tell_vfs(ma_decoder* pDecoder, ma_int64* pCursor)
{
MA_ASSERT(pDecoder != NULL);
return ma_vfs_or_default_tell(pDecoder->data.vfs.pVFS, pDecoder->data.vfs.file, pCursor);
}
static ma_result ma_decoder__preinit_vfs(ma_vfs* pVFS, const char* pFilePath, const ma_decoder_config* pConfig, ma_decoder* pDecoder) static ma_result ma_decoder__preinit_vfs(ma_vfs* pVFS, const char* pFilePath, const ma_decoder_config* pConfig, ma_decoder* pDecoder)
{ {
ma_result result; ma_result result;
ma_vfs_file file; ma_vfs_file file;
result = ma_decoder__preinit(ma_decoder__on_read_vfs, ma_decoder__on_seek_vfs, NULL, pConfig, pDecoder); result = ma_decoder__preinit(ma_decoder__on_read_vfs, ma_decoder__on_seek_vfs, ma_decoder__on_tell_vfs, NULL, pConfig, pDecoder);
if (result != MA_SUCCESS) { if (result != MA_SUCCESS) {
return result; return result;
} }
...@@ -48171,8 +48404,8 @@ static ma_result ma_decoder__preinit_vfs(ma_vfs* pVFS, const char* pFilePath, co ...@@ -48171,8 +48404,8 @@ static ma_result ma_decoder__preinit_vfs(ma_vfs* pVFS, const char* pFilePath, co
return result; return result;
} }
pDecoder->backend.vfs.pVFS = pVFS; pDecoder->data.vfs.pVFS = pVFS;
pDecoder->backend.vfs.file = file; pDecoder->data.vfs.file = file;
return MA_SUCCESS; return MA_SUCCESS;
} }
...@@ -48223,8 +48456,8 @@ MA_API ma_result ma_decoder_init_vfs(ma_vfs* pVFS, const char* pFilePath, const ...@@ -48223,8 +48456,8 @@ MA_API ma_result ma_decoder_init_vfs(ma_vfs* pVFS, const char* pFilePath, const
} }
if (result != MA_SUCCESS) { if (result != MA_SUCCESS) {
if (pDecoder->backend.vfs.file != NULL) { /* <-- Will be reset to NULL if ma_decoder_uninit() is called in one of the steps above which allows us to avoid a double close of the file. */ if (pDecoder->data.vfs.file != NULL) { /* <-- Will be reset to NULL if ma_decoder_uninit() is called in one of the steps above which allows us to avoid a double close of the file. */
ma_vfs_or_default_close(pVFS, pDecoder->backend.vfs.file); ma_vfs_or_default_close(pVFS, pDecoder->data.vfs.file);
} }
return result; return result;
...@@ -48251,7 +48484,7 @@ MA_API ma_result ma_decoder_init_vfs_wav(ma_vfs* pVFS, const char* pFilePath, co ...@@ -48251,7 +48484,7 @@ MA_API ma_result ma_decoder_init_vfs_wav(ma_vfs* pVFS, const char* pFilePath, co
} }
if (result != MA_SUCCESS) { if (result != MA_SUCCESS) {
ma_vfs_or_default_close(pVFS, pDecoder->backend.vfs.file); ma_vfs_or_default_close(pVFS, pDecoder->data.vfs.file);
} }
return result; return result;
...@@ -48282,7 +48515,7 @@ MA_API ma_result ma_decoder_init_vfs_flac(ma_vfs* pVFS, const char* pFilePath, c ...@@ -48282,7 +48515,7 @@ MA_API ma_result ma_decoder_init_vfs_flac(ma_vfs* pVFS, const char* pFilePath, c
} }
if (result != MA_SUCCESS) { if (result != MA_SUCCESS) {
ma_vfs_or_default_close(pVFS, pDecoder->backend.vfs.file); ma_vfs_or_default_close(pVFS, pDecoder->data.vfs.file);
} }
return result; return result;
...@@ -48313,7 +48546,7 @@ MA_API ma_result ma_decoder_init_vfs_mp3(ma_vfs* pVFS, const char* pFilePath, co ...@@ -48313,7 +48546,7 @@ MA_API ma_result ma_decoder_init_vfs_mp3(ma_vfs* pVFS, const char* pFilePath, co
} }
if (result != MA_SUCCESS) { if (result != MA_SUCCESS) {
ma_vfs_or_default_close(pVFS, pDecoder->backend.vfs.file); ma_vfs_or_default_close(pVFS, pDecoder->data.vfs.file);
} }
return result; return result;
...@@ -48344,7 +48577,7 @@ MA_API ma_result ma_decoder_init_vfs_vorbis(ma_vfs* pVFS, const char* pFilePath, ...@@ -48344,7 +48577,7 @@ MA_API ma_result ma_decoder_init_vfs_vorbis(ma_vfs* pVFS, const char* pFilePath,
} }
if (result != MA_SUCCESS) { if (result != MA_SUCCESS) {
ma_vfs_or_default_close(pVFS, pDecoder->backend.vfs.file); ma_vfs_or_default_close(pVFS, pDecoder->data.vfs.file);
} }
return result; return result;
...@@ -48364,7 +48597,7 @@ static ma_result ma_decoder__preinit_vfs_w(ma_vfs* pVFS, const wchar_t* pFilePat ...@@ -48364,7 +48597,7 @@ static ma_result ma_decoder__preinit_vfs_w(ma_vfs* pVFS, const wchar_t* pFilePat
ma_result result; ma_result result;
ma_vfs_file file; ma_vfs_file file;
result = ma_decoder__preinit(ma_decoder__on_read_vfs, ma_decoder__on_seek_vfs, NULL, pConfig, pDecoder); result = ma_decoder__preinit(ma_decoder__on_read_vfs, ma_decoder__on_seek_vfs, ma_decoder__on_tell_vfs, NULL, pConfig, pDecoder);
if (result != MA_SUCCESS) { if (result != MA_SUCCESS) {
return result; return result;
} }
...@@ -48378,8 +48611,8 @@ static ma_result ma_decoder__preinit_vfs_w(ma_vfs* pVFS, const wchar_t* pFilePat ...@@ -48378,8 +48611,8 @@ static ma_result ma_decoder__preinit_vfs_w(ma_vfs* pVFS, const wchar_t* pFilePat
return result; return result;
} }
pDecoder->backend.vfs.pVFS = pVFS; pDecoder->data.vfs.pVFS = pVFS;
pDecoder->backend.vfs.file = file; pDecoder->data.vfs.file = file;
return MA_SUCCESS; return MA_SUCCESS;
} }
...@@ -48430,7 +48663,7 @@ MA_API ma_result ma_decoder_init_vfs_w(ma_vfs* pVFS, const wchar_t* pFilePath, c ...@@ -48430,7 +48663,7 @@ MA_API ma_result ma_decoder_init_vfs_w(ma_vfs* pVFS, const wchar_t* pFilePath, c
} }
if (result != MA_SUCCESS) { if (result != MA_SUCCESS) {
ma_vfs_or_default_close(pVFS, pDecoder->backend.vfs.file); ma_vfs_or_default_close(pVFS, pDecoder->data.vfs.file);
return result; return result;
} }
...@@ -48455,7 +48688,7 @@ MA_API ma_result ma_decoder_init_vfs_wav_w(ma_vfs* pVFS, const wchar_t* pFilePat ...@@ -48455,7 +48688,7 @@ MA_API ma_result ma_decoder_init_vfs_wav_w(ma_vfs* pVFS, const wchar_t* pFilePat
} }
if (result != MA_SUCCESS) { if (result != MA_SUCCESS) {
ma_vfs_or_default_close(pVFS, pDecoder->backend.vfs.file); ma_vfs_or_default_close(pVFS, pDecoder->data.vfs.file);
} }
return result; return result;
...@@ -48486,7 +48719,7 @@ MA_API ma_result ma_decoder_init_vfs_flac_w(ma_vfs* pVFS, const wchar_t* pFilePa ...@@ -48486,7 +48719,7 @@ MA_API ma_result ma_decoder_init_vfs_flac_w(ma_vfs* pVFS, const wchar_t* pFilePa
} }
if (result != MA_SUCCESS) { if (result != MA_SUCCESS) {
ma_vfs_or_default_close(pVFS, pDecoder->backend.vfs.file); ma_vfs_or_default_close(pVFS, pDecoder->data.vfs.file);
} }
return result; return result;
...@@ -48517,7 +48750,7 @@ MA_API ma_result ma_decoder_init_vfs_mp3_w(ma_vfs* pVFS, const wchar_t* pFilePat ...@@ -48517,7 +48750,7 @@ MA_API ma_result ma_decoder_init_vfs_mp3_w(ma_vfs* pVFS, const wchar_t* pFilePat
} }
if (result != MA_SUCCESS) { if (result != MA_SUCCESS) {
ma_vfs_or_default_close(pVFS, pDecoder->backend.vfs.file); ma_vfs_or_default_close(pVFS, pDecoder->data.vfs.file);
} }
return result; return result;
...@@ -48548,7 +48781,7 @@ MA_API ma_result ma_decoder_init_vfs_vorbis_w(ma_vfs* pVFS, const wchar_t* pFile ...@@ -48548,7 +48781,7 @@ MA_API ma_result ma_decoder_init_vfs_vorbis_w(ma_vfs* pVFS, const wchar_t* pFile
} }
if (result != MA_SUCCESS) { if (result != MA_SUCCESS) {
ma_vfs_or_default_close(pVFS, pDecoder->backend.vfs.file); ma_vfs_or_default_close(pVFS, pDecoder->data.vfs.file);
} }
return result; return result;
...@@ -48621,13 +48854,20 @@ MA_API ma_result ma_decoder_uninit(ma_decoder* pDecoder) ...@@ -48621,13 +48854,20 @@ MA_API ma_result ma_decoder_uninit(ma_decoder* pDecoder)
return MA_INVALID_ARGS; return MA_INVALID_ARGS;
} }
if (pDecoder->pBackend != NULL) {
if (pDecoder->pBackendVTable != NULL && pDecoder->pBackendVTable->onUninit != NULL) {
pDecoder->pBackendVTable->onUninit(pDecoder->pBackendUserData, pDecoder->pBackend, &pDecoder->allocationCallbacks);
}
}
/* Legacy. */
if (pDecoder->onUninit) { if (pDecoder->onUninit) {
pDecoder->onUninit(pDecoder); pDecoder->onUninit(pDecoder);
} }
if (pDecoder->onRead == ma_decoder__on_read_vfs) { if (pDecoder->onRead == ma_decoder__on_read_vfs) {
ma_vfs_or_default_close(pDecoder->backend.vfs.pVFS, pDecoder->backend.vfs.file); ma_vfs_or_default_close(pDecoder->data.vfs.pVFS, pDecoder->data.vfs.file);
pDecoder->backend.vfs.file = NULL; pDecoder->data.vfs.file = NULL;
} }
ma_data_converter_uninit(&pDecoder->converter); ma_data_converter_uninit(&pDecoder->converter);
...@@ -48659,8 +48899,16 @@ MA_API ma_uint64 ma_decoder_get_length_in_pcm_frames(ma_decoder* pDecoder) ...@@ -48659,8 +48899,16 @@ MA_API ma_uint64 ma_decoder_get_length_in_pcm_frames(ma_decoder* pDecoder)
return 0; return 0;
} }
if (pDecoder->onGetLengthInPCMFrames) { if (pDecoder->pBackend != NULL || pDecoder->onGetLengthInPCMFrames != NULL) {
ma_uint64 nativeLengthInPCMFrames = pDecoder->onGetLengthInPCMFrames(pDecoder); ma_uint64 nativeLengthInPCMFrames;
if (pDecoder->pBackend != NULL) {
ma_data_source_get_length_in_pcm_frames(pDecoder->pBackend, &nativeLengthInPCMFrames);
} else {
/* Legacy. */
nativeLengthInPCMFrames = pDecoder->onGetLengthInPCMFrames(pDecoder);
}
if (pDecoder->internalSampleRate == pDecoder->outputSampleRate) { if (pDecoder->internalSampleRate == pDecoder->outputSampleRate) {
return nativeLengthInPCMFrames; return nativeLengthInPCMFrames;
} else { } else {
...@@ -48682,20 +48930,30 @@ MA_API ma_uint64 ma_decoder_read_pcm_frames(ma_decoder* pDecoder, void* pFramesO ...@@ -48682,20 +48930,30 @@ MA_API ma_uint64 ma_decoder_read_pcm_frames(ma_decoder* pDecoder, void* pFramesO
return 0; return 0;
} }
if (pDecoder->onReadPCMFrames == NULL) { if (pDecoder->pBackend == NULL && pDecoder->onReadPCMFrames == NULL) {
return 0; return 0;
} }
/* Fast path. */ /* Fast path. */
if (pDecoder->converter.isPassthrough) { if (pDecoder->converter.isPassthrough) {
totalFramesReadOut = pDecoder->onReadPCMFrames(pDecoder, pFramesOut, frameCount); if (pDecoder->pBackend != NULL) {
result = ma_data_source_read_pcm_frames(pDecoder->pBackend, pFramesOut, frameCount, &totalFramesReadOut, MA_FALSE);
} else {
/* Legacy. */
totalFramesReadOut = pDecoder->onReadPCMFrames(pDecoder, pFramesOut, frameCount);
}
} else { } else {
/* /*
Getting here means we need to do data conversion. If we're seeking forward and are _not_ doing resampling we can run this in a fast path. If we're doing resampling we Getting here means we need to do data conversion. If we're seeking forward and are _not_ doing resampling we can run this in a fast path. If we're doing resampling we
need to run through each sample because we need to ensure it's internal cache is updated. need to run through each sample because we need to ensure it's internal cache is updated.
*/ */
if (pFramesOut == NULL && pDecoder->converter.hasResampler == MA_FALSE) { if (pFramesOut == NULL && pDecoder->converter.hasResampler == MA_FALSE) {
totalFramesReadOut = pDecoder->onReadPCMFrames(pDecoder, NULL, frameCount); /* All decoder backends must support passing in NULL for the output buffer. */ if (pDecoder->pBackend != NULL) {
result = ma_data_source_read_pcm_frames(pDecoder->pBackend, NULL, frameCount, &totalFramesReadOut, MA_FALSE);
} else {
/* Legacy. */
totalFramesReadOut = pDecoder->onReadPCMFrames(pDecoder, NULL, frameCount); /* All decoder backends must support passing in NULL for the output buffer. */
}
} else { } else {
/* Slow path. Need to run everything through the data converter. */ /* Slow path. Need to run everything through the data converter. */
totalFramesReadOut = 0; totalFramesReadOut = 0;
...@@ -48723,7 +48981,13 @@ MA_API ma_uint64 ma_decoder_read_pcm_frames(ma_decoder* pDecoder, void* pFramesO ...@@ -48723,7 +48981,13 @@ MA_API ma_uint64 ma_decoder_read_pcm_frames(ma_decoder* pDecoder, void* pFramesO
} }
if (requiredInputFrameCount > 0) { if (requiredInputFrameCount > 0) {
framesReadThisIterationIn = pDecoder->onReadPCMFrames(pDecoder, pIntermediaryBuffer, framesToReadThisIterationIn); if (pDecoder->pBackend != NULL) {
result = ma_data_source_read_pcm_frames(pDecoder->pBackend, pIntermediaryBuffer, framesToReadThisIterationIn, &framesReadThisIterationIn, MA_FALSE);
} else {
/* Legacy. */
framesReadThisIterationIn = pDecoder->onReadPCMFrames(pDecoder, pIntermediaryBuffer, framesToReadThisIterationIn);
}
totalFramesReadIn += framesReadThisIterationIn; totalFramesReadIn += framesReadThisIterationIn;
} else { } else {
framesReadThisIterationIn = 0; framesReadThisIterationIn = 0;
...@@ -48763,7 +49027,7 @@ MA_API ma_result ma_decoder_seek_to_pcm_frame(ma_decoder* pDecoder, ma_uint64 fr ...@@ -48763,7 +49027,7 @@ MA_API ma_result ma_decoder_seek_to_pcm_frame(ma_decoder* pDecoder, ma_uint64 fr
return MA_INVALID_ARGS; return MA_INVALID_ARGS;
} }
if (pDecoder->onSeekToPCMFrame) { if (pDecoder->pBackend != NULL || pDecoder->onSeekToPCMFrame != NULL) {
ma_result result; ma_result result;
ma_uint64 internalFrameIndex; ma_uint64 internalFrameIndex;
if (pDecoder->internalSampleRate == pDecoder->outputSampleRate) { if (pDecoder->internalSampleRate == pDecoder->outputSampleRate) {
...@@ -48772,7 +49036,13 @@ MA_API ma_result ma_decoder_seek_to_pcm_frame(ma_decoder* pDecoder, ma_uint64 fr ...@@ -48772,7 +49036,13 @@ MA_API ma_result ma_decoder_seek_to_pcm_frame(ma_decoder* pDecoder, ma_uint64 fr
internalFrameIndex = ma_calculate_frame_count_after_resampling(pDecoder->internalSampleRate, pDecoder->outputSampleRate, frameIndex); internalFrameIndex = ma_calculate_frame_count_after_resampling(pDecoder->internalSampleRate, pDecoder->outputSampleRate, frameIndex);
} }
result = pDecoder->onSeekToPCMFrame(pDecoder, internalFrameIndex); if (pDecoder->pBackend != NULL) {
result = ma_data_source_seek_to_pcm_frame(pDecoder->pBackend, internalFrameIndex);
} else {
/* Legacy. */
result = pDecoder->onSeekToPCMFrame(pDecoder, internalFrameIndex);
}
if (result == MA_SUCCESS) { if (result == MA_SUCCESS) {
pDecoder->readPointerInPCMFrames = frameIndex; pDecoder->readPointerInPCMFrames = frameIndex;
} }
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment