mirror of
https://github.com/mackron/miniaudio.git
synced 2026-04-22 00:06:59 +02:00
Add support for decoding from raw PCM data.
This commit is contained in:
@@ -640,6 +640,7 @@ typedef int mal_result;
|
||||
#define MAL_FAILED_TO_CREATE_THREAD -28
|
||||
#define MAL_INVALID_DEVICE_CONFIG -29
|
||||
#define MAL_ACCESS_DENIED -30
|
||||
#define MAL_TOO_LARGE -31
|
||||
|
||||
typedef void (* mal_log_proc) (mal_context* pContext, mal_device* pDevice, const char* message);
|
||||
typedef void (* mal_recv_proc)(mal_device* pDevice, mal_uint32 frameCount, const void* pSamples);
|
||||
@@ -1914,7 +1915,7 @@ static MAL_INLINE mal_device_config mal_device_config_init_playback(mal_format f
|
||||
void mal_get_standard_channel_map(mal_standard_channel_map standardChannelMap, mal_uint32 channels, mal_channel channelMap[MAL_MAX_CHANNELS]);
|
||||
|
||||
// Copies a channel map.
|
||||
void mal_channel_map_copy(mal_channel* pOut, mal_channel* pIn, mal_uint32 channels);
|
||||
void mal_channel_map_copy(mal_channel* pOut, const mal_channel* pIn, mal_uint32 channels);
|
||||
|
||||
|
||||
// Determines whether or not a channel map is valid.
|
||||
@@ -2264,10 +2265,10 @@ typedef mal_result (* mal_decoder_uninit_proc) (mal_decoder* pDecoder);
|
||||
|
||||
typedef struct
|
||||
{
|
||||
mal_format outputFormat; // Set to 0 or mal_format_unknown to use the stream's internal format.
|
||||
mal_uint32 outputChannels; // Set to 0 to use the stream's internal channels.
|
||||
mal_uint32 outputSampleRate; // Set to 0 to use the stream's internal sample rate.
|
||||
mal_channel outputChannelMap[MAL_MAX_CHANNELS];
|
||||
mal_format format; // Set to 0 or mal_format_unknown to use the stream's internal format.
|
||||
mal_uint32 channels; // Set to 0 to use the stream's internal channels.
|
||||
mal_uint32 sampleRate; // Set to 0 to use the stream's internal sample rate.
|
||||
mal_channel channelMap[MAL_MAX_CHANNELS];
|
||||
} mal_decoder_config;
|
||||
|
||||
struct mal_decoder
|
||||
@@ -2302,12 +2303,14 @@ mal_result mal_decoder_init_wav(mal_decoder_read_proc onRead, mal_decoder_seek_p
|
||||
mal_result mal_decoder_init_flac(mal_decoder_read_proc onRead, mal_decoder_seek_proc onSeek, void* pUserData, const mal_decoder_config* pConfig, mal_decoder* pDecoder);
|
||||
mal_result mal_decoder_init_vorbis(mal_decoder_read_proc onRead, mal_decoder_seek_proc onSeek, void* pUserData, const mal_decoder_config* pConfig, mal_decoder* pDecoder);
|
||||
mal_result mal_decoder_init_mp3(mal_decoder_read_proc onRead, mal_decoder_seek_proc onSeek, void* pUserData, const mal_decoder_config* pConfig, mal_decoder* pDecoder);
|
||||
mal_result mal_decoder_init_raw(mal_decoder_read_proc onRead, mal_decoder_seek_proc onSeek, void* pUserData, const mal_decoder_config* pConfigIn, const mal_decoder_config* pConfigOut, mal_decoder* pDecoder);
|
||||
|
||||
mal_result mal_decoder_init_memory(const void* pData, size_t dataSize, const mal_decoder_config* pConfig, mal_decoder* pDecoder);
|
||||
mal_result mal_decoder_init_memory_wav(const void* pData, size_t dataSize, const mal_decoder_config* pConfig, mal_decoder* pDecoder);
|
||||
mal_result mal_decoder_init_memory_flac(const void* pData, size_t dataSize, const mal_decoder_config* pConfig, mal_decoder* pDecoder);
|
||||
mal_result mal_decoder_init_memory_vorbis(const void* pData, size_t dataSize, const mal_decoder_config* pConfig, mal_decoder* pDecoder);
|
||||
mal_result mal_decoder_init_memory_mp3(const void* pData, size_t dataSize, const mal_decoder_config* pConfig, mal_decoder* pDecoder);
|
||||
mal_result mal_decoder_init_memory_raw(const void* pData, size_t dataSize, const mal_decoder_config* pConfigIn, const mal_decoder_config* pConfigOut, mal_decoder* pDecoder);
|
||||
|
||||
#ifndef MAL_NO_STDIO
|
||||
mal_result mal_decoder_init_file(const char* pFilePath, const mal_decoder_config* pConfig, mal_decoder* pDecoder);
|
||||
@@ -2319,6 +2322,14 @@ mal_result mal_decoder_uninit(mal_decoder* pDecoder);
|
||||
mal_uint64 mal_decoder_read(mal_decoder* pDecoder, mal_uint64 frameCount, void* pFramesOut);
|
||||
mal_result mal_decoder_seek_to_frame(mal_decoder* pDecoder, mal_uint64 frameIndex);
|
||||
|
||||
|
||||
// Helper for opening and decoding a file into a heap allocated block of memory. Free the returned pointer with mal_free(). On input,
|
||||
// pConfig should be set to what you want. On output it will be set to what you got.
|
||||
#ifndef MAL_NO_STDIO
|
||||
mal_result mal_decode_file(const char* pFilePath, mal_decoder_config* pConfig, mal_uint64* pFrameCountOut, void** ppDataOut);
|
||||
#endif
|
||||
mal_result mal_decode_memory(const void* pData, size_t dataSize, mal_decoder_config* pConfig, mal_uint64* pFrameCountOut, void** ppDataOut);
|
||||
|
||||
#endif // MAL_NO_DECODING
|
||||
|
||||
|
||||
@@ -3134,6 +3145,19 @@ static MAL_INLINE float mal_mix_f32(float x, float y, float a)
|
||||
{
|
||||
return x*(1-a) + y*a;
|
||||
}
|
||||
static MAL_INLINE float mal_mix_f32_fast(float x, float y, float a)
|
||||
{
|
||||
return x + (y - x)*a;
|
||||
}
|
||||
|
||||
static MAL_INLINE double mal_mix_f64(double x, double y, double a)
|
||||
{
|
||||
return x*(1-a) + y*a;
|
||||
}
|
||||
static MAL_INLINE double mal_mix_f64_fast(double x, double y, double a)
|
||||
{
|
||||
return x + (y - x)*a;
|
||||
}
|
||||
|
||||
static MAL_INLINE float mal_scale_to_range_f32(float x, float lo, float hi)
|
||||
{
|
||||
@@ -16996,7 +17020,7 @@ void mal_get_standard_channel_map(mal_standard_channel_map standardChannelMap, m
|
||||
}
|
||||
}
|
||||
|
||||
void mal_channel_map_copy(mal_channel* pOut, mal_channel* pIn, mal_uint32 channels)
|
||||
void mal_channel_map_copy(mal_channel* pOut, const mal_channel* pIn, mal_uint32 channels)
|
||||
{
|
||||
if (pOut != NULL && pIn != NULL && channels > 0) {
|
||||
mal_copy_memory(pOut, pIn, sizeof(*pOut) * channels);
|
||||
@@ -19970,7 +19994,7 @@ mal_uint32 mal_dsp__post_format_converter_on_read_deinterleaved(mal_format_conve
|
||||
if (pDSP->isSRCRequired) {
|
||||
return (mal_uint32)mal_src_read_deinterleaved_ex(&pDSP->src, frameCount, ppSamplesOut, pData->flush, pUserData);
|
||||
} else {
|
||||
return (mal_uint32)mal_format_converter_read_deinterleaved(&pDSP->formatConverterIn, frameCount, ppSamplesOut, pUserData);
|
||||
return (mal_uint32)mal_channel_router_read_deinterleaved(&pDSP->channelRouter, frameCount, ppSamplesOut, pUserData);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -20610,10 +20634,10 @@ mal_decoder_config mal_decoder_config_init(mal_format outputFormat, mal_uint32 o
|
||||
{
|
||||
mal_decoder_config config;
|
||||
mal_zero_object(&config);
|
||||
config.outputFormat = outputFormat;
|
||||
config.outputChannels = outputChannels;
|
||||
config.outputSampleRate = outputSampleRate;
|
||||
mal_get_standard_channel_map(mal_standard_channel_map_default, config.outputChannels, config.outputChannelMap);
|
||||
config.format = outputFormat;
|
||||
config.channels = outputChannels;
|
||||
config.sampleRate = outputSampleRate;
|
||||
mal_get_standard_channel_map(mal_standard_channel_map_default, config.channels, config.channelMap);
|
||||
|
||||
return config;
|
||||
}
|
||||
@@ -20635,28 +20659,28 @@ mal_result mal_decoder__init_dsp(mal_decoder* pDecoder, const mal_decoder_config
|
||||
mal_assert(pDecoder != NULL);
|
||||
|
||||
// Output format.
|
||||
if (pConfig->outputFormat == mal_format_unknown) {
|
||||
if (pConfig->format == mal_format_unknown) {
|
||||
pDecoder->outputFormat = pDecoder->internalFormat;
|
||||
} else {
|
||||
pDecoder->outputFormat = pConfig->outputFormat;
|
||||
pDecoder->outputFormat = pConfig->format;
|
||||
}
|
||||
|
||||
if (pConfig->outputChannels == 0) {
|
||||
if (pConfig->channels == 0) {
|
||||
pDecoder->outputChannels = pDecoder->internalChannels;
|
||||
} else {
|
||||
pDecoder->outputChannels = pConfig->outputChannels;
|
||||
pDecoder->outputChannels = pConfig->channels;
|
||||
}
|
||||
|
||||
if (pConfig->outputSampleRate == 0) {
|
||||
if (pConfig->sampleRate == 0) {
|
||||
pDecoder->outputSampleRate = pDecoder->internalSampleRate;
|
||||
} else {
|
||||
pDecoder->outputSampleRate = pConfig->outputSampleRate;
|
||||
pDecoder->outputSampleRate = pConfig->sampleRate;
|
||||
}
|
||||
|
||||
if (mal_channel_map_blank(pDecoder->outputChannels, pConfig->outputChannelMap)) {
|
||||
if (mal_channel_map_blank(pDecoder->outputChannels, pConfig->channelMap)) {
|
||||
mal_get_standard_channel_map(mal_standard_channel_map_default, pDecoder->outputChannels, pDecoder->outputChannelMap);
|
||||
} else {
|
||||
mal_copy_memory(pDecoder->outputChannelMap, pConfig->outputChannelMap, sizeof(pConfig->outputChannelMap));
|
||||
mal_copy_memory(pDecoder->outputChannelMap, pConfig->channelMap, sizeof(pConfig->channelMap));
|
||||
}
|
||||
|
||||
|
||||
@@ -21237,7 +21261,7 @@ mal_result mal_decoder_init_mp3__internal(const mal_decoder_config* pConfig, mal
|
||||
drmp3_config mp3Config;
|
||||
mal_zero_object(&mp3Config);
|
||||
mp3Config.outputChannels = 2;
|
||||
mp3Config.outputSampleRate = (pConfig->outputSampleRate != 0) ? pConfig->outputSampleRate : 44100;
|
||||
mp3Config.outputSampleRate = (pConfig->sampleRate != 0) ? pConfig->sampleRate : 44100;
|
||||
if (!drmp3_init(pMP3, mal_decoder_internal_on_read__mp3, mal_decoder_internal_on_seek__mp3, pDecoder, &mp3Config)) {
|
||||
return MAL_ERROR;
|
||||
}
|
||||
@@ -21263,6 +21287,92 @@ mal_result mal_decoder_init_mp3__internal(const mal_decoder_config* pConfig, mal
|
||||
}
|
||||
#endif
|
||||
|
||||
// Raw
|
||||
mal_result mal_decoder_internal_on_seek_to_frame__raw(mal_decoder* pDecoder, mal_uint64 frameIndex)
|
||||
{
|
||||
mal_assert(pDecoder != NULL);
|
||||
|
||||
if (pDecoder->onSeek == NULL) {
|
||||
return MAL_ERROR;
|
||||
}
|
||||
|
||||
mal_bool32 result = MAL_FALSE;
|
||||
|
||||
// The callback uses a 32 bit integer whereas we use a 64 bit unsigned integer. We just need to continuously seek until we're at the correct position.
|
||||
mal_uint64 totalBytesToSeek = frameIndex * mal_get_bytes_per_frame(pDecoder->internalFormat, pDecoder->internalChannels);
|
||||
if (totalBytesToSeek < 0x7FFFFFFF) {
|
||||
// Simple case.
|
||||
result = pDecoder->onSeek(pDecoder, (int)(frameIndex * mal_get_bytes_per_frame(pDecoder->internalFormat, pDecoder->internalChannels)), mal_seek_origin_start);
|
||||
} else {
|
||||
// Complex case. Start by doing a seek relative to the start. Then keep looping using offset seeking.
|
||||
result = pDecoder->onSeek(pDecoder, 0x7FFFFFFF, mal_seek_origin_start);
|
||||
if (result == MAL_TRUE) {
|
||||
totalBytesToSeek -= 0x7FFFFFFF;
|
||||
|
||||
while (totalBytesToSeek > 0) {
|
||||
mal_uint64 bytesToSeekThisIteration = totalBytesToSeek;
|
||||
if (bytesToSeekThisIteration > 0x7FFFFFFF) {
|
||||
bytesToSeekThisIteration = 0x7FFFFFFF;
|
||||
}
|
||||
|
||||
result = pDecoder->onSeek(pDecoder, (int)bytesToSeekThisIteration, mal_seek_origin_current);
|
||||
if (result != MAL_TRUE) {
|
||||
break;
|
||||
}
|
||||
|
||||
totalBytesToSeek -= bytesToSeekThisIteration;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (result) {
|
||||
return MAL_SUCCESS;
|
||||
} else {
|
||||
return MAL_ERROR;
|
||||
}
|
||||
}
|
||||
|
||||
mal_result mal_decoder_internal_on_uninit__raw(mal_decoder* pDecoder)
|
||||
{
|
||||
(void)pDecoder;
|
||||
return MAL_SUCCESS;
|
||||
}
|
||||
|
||||
mal_uint32 mal_decoder_internal_on_read_frames__raw(mal_dsp* pDSP, mal_uint32 frameCount, void* pSamplesOut, void* pUserData)
|
||||
{
|
||||
(void)pDSP;
|
||||
|
||||
mal_decoder* pDecoder = (mal_decoder*)pUserData;
|
||||
mal_assert(pDecoder != NULL);
|
||||
|
||||
// For raw decoding we just read directly from the decoder's callbacks.
|
||||
mal_uint32 bpf = mal_get_bytes_per_frame(pDecoder->internalFormat, pDecoder->internalChannels);
|
||||
return pDecoder->onRead(pDecoder, pSamplesOut, frameCount * bpf) / bpf;
|
||||
}
|
||||
|
||||
mal_result mal_decoder_init_raw__internal(const mal_decoder_config* pConfigIn, const mal_decoder_config* pConfigOut, mal_decoder* pDecoder)
|
||||
{
|
||||
mal_assert(pConfigIn != NULL);
|
||||
mal_assert(pConfigOut != NULL);
|
||||
mal_assert(pDecoder != NULL);
|
||||
|
||||
pDecoder->onSeekToFrame = mal_decoder_internal_on_seek_to_frame__raw;
|
||||
pDecoder->onUninit = mal_decoder_internal_on_uninit__raw;
|
||||
|
||||
// Internal format.
|
||||
pDecoder->internalFormat = pConfigIn->format;
|
||||
pDecoder->internalChannels = pConfigIn->channels;
|
||||
pDecoder->internalSampleRate = pConfigIn->sampleRate;
|
||||
mal_channel_map_copy(pDecoder->internalChannelMap, pConfigIn->channelMap, pConfigIn->channels);
|
||||
|
||||
mal_result result = mal_decoder__init_dsp(pDecoder, pConfigOut, mal_decoder_internal_on_read_frames__raw);
|
||||
if (result != MAL_SUCCESS) {
|
||||
return result;
|
||||
}
|
||||
|
||||
return MAL_SUCCESS;
|
||||
}
|
||||
|
||||
mal_result mal_decoder__preinit(mal_decoder_read_proc onRead, mal_decoder_seek_proc onSeek, void* pUserData, const mal_decoder_config* pConfig, mal_decoder* pDecoder)
|
||||
{
|
||||
mal_assert(pConfig != NULL);
|
||||
@@ -21349,21 +21459,29 @@ mal_result mal_decoder_init_mp3(mal_decoder_read_proc onRead, mal_decoder_seek_p
|
||||
#endif
|
||||
}
|
||||
|
||||
mal_result mal_decoder_init(mal_decoder_read_proc onRead, mal_decoder_seek_proc onSeek, void* pUserData, const mal_decoder_config* pConfig, mal_decoder* pDecoder)
|
||||
mal_result mal_decoder_init_raw(mal_decoder_read_proc onRead, mal_decoder_seek_proc onSeek, void* pUserData, const mal_decoder_config* pConfigIn, const mal_decoder_config* pConfigOut, mal_decoder* pDecoder)
|
||||
{
|
||||
mal_decoder_config config = mal_decoder_config_init_copy(pConfig);
|
||||
mal_decoder_config config = mal_decoder_config_init_copy(pConfigOut);
|
||||
|
||||
mal_result result = mal_decoder__preinit(onRead, onSeek, pUserData, &config, pDecoder);
|
||||
if (result != MAL_SUCCESS) {
|
||||
return result;
|
||||
}
|
||||
|
||||
return mal_decoder_init_raw__internal(pConfigIn, &config, pDecoder);
|
||||
}
|
||||
|
||||
mal_result mal_decoder_init__internal(mal_decoder_read_proc onRead, mal_decoder_seek_proc onSeek, void* pUserData, const mal_decoder_config* pConfig, mal_decoder* pDecoder)
|
||||
{
|
||||
mal_assert(pConfig != NULL);
|
||||
mal_assert(pDecoder != NULL);
|
||||
|
||||
// We use trial and error to open a decoder.
|
||||
result = MAL_NO_BACKEND;
|
||||
mal_result result = MAL_NO_BACKEND;
|
||||
|
||||
#ifdef MAL_HAS_WAV
|
||||
if (result != MAL_SUCCESS) {
|
||||
result = mal_decoder_init_wav__internal(&config, pDecoder);
|
||||
result = mal_decoder_init_wav__internal(pConfig, pDecoder);
|
||||
if (result != MAL_SUCCESS) {
|
||||
onSeek(pDecoder, 0, mal_seek_origin_start);
|
||||
}
|
||||
@@ -21371,7 +21489,7 @@ mal_result mal_decoder_init(mal_decoder_read_proc onRead, mal_decoder_seek_proc
|
||||
#endif
|
||||
#ifdef MAL_HAS_FLAC
|
||||
if (result != MAL_SUCCESS) {
|
||||
result = mal_decoder_init_flac__internal(&config, pDecoder);
|
||||
result = mal_decoder_init_flac__internal(pConfig, pDecoder);
|
||||
if (result != MAL_SUCCESS) {
|
||||
onSeek(pDecoder, 0, mal_seek_origin_start);
|
||||
}
|
||||
@@ -21379,7 +21497,7 @@ mal_result mal_decoder_init(mal_decoder_read_proc onRead, mal_decoder_seek_proc
|
||||
#endif
|
||||
#ifdef MAL_HAS_VORBIS
|
||||
if (result != MAL_SUCCESS) {
|
||||
result = mal_decoder_init_vorbis__internal(&config, pDecoder);
|
||||
result = mal_decoder_init_vorbis__internal(pConfig, pDecoder);
|
||||
if (result != MAL_SUCCESS) {
|
||||
onSeek(pDecoder, 0, mal_seek_origin_start);
|
||||
}
|
||||
@@ -21387,7 +21505,7 @@ mal_result mal_decoder_init(mal_decoder_read_proc onRead, mal_decoder_seek_proc
|
||||
#endif
|
||||
#ifdef MAL_HAS_MP3
|
||||
if (result != MAL_SUCCESS) {
|
||||
result = mal_decoder_init_mp3__internal(&config, pDecoder);
|
||||
result = mal_decoder_init_mp3__internal(pConfig, pDecoder);
|
||||
if (result != MAL_SUCCESS) {
|
||||
onSeek(pDecoder, 0, mal_seek_origin_start);
|
||||
}
|
||||
@@ -21401,6 +21519,18 @@ mal_result mal_decoder_init(mal_decoder_read_proc onRead, mal_decoder_seek_proc
|
||||
return result;
|
||||
}
|
||||
|
||||
mal_result mal_decoder_init(mal_decoder_read_proc onRead, mal_decoder_seek_proc onSeek, void* pUserData, const mal_decoder_config* pConfig, mal_decoder* pDecoder)
|
||||
{
|
||||
mal_decoder_config config = mal_decoder_config_init_copy(pConfig);
|
||||
|
||||
mal_result result = mal_decoder__preinit(onRead, onSeek, pUserData, &config, pDecoder);
|
||||
if (result != MAL_SUCCESS) {
|
||||
return result;
|
||||
}
|
||||
|
||||
return mal_decoder_init__internal(onRead, onSeek, pUserData, &config, pDecoder);
|
||||
}
|
||||
|
||||
|
||||
size_t mal_decoder__on_read_memory(mal_decoder* pDecoder, void* pBufferOut, size_t bytesToRead)
|
||||
{
|
||||
@@ -21447,12 +21577,11 @@ mal_bool32 mal_decoder__on_seek_memory(mal_decoder* pDecoder, int byteOffset, ma
|
||||
|
||||
mal_result mal_decoder__preinit_memory(const void* pData, size_t dataSize, const mal_decoder_config* pConfig, mal_decoder* pDecoder)
|
||||
{
|
||||
if (pDecoder == NULL) {
|
||||
return MAL_INVALID_ARGS;
|
||||
mal_result result = mal_decoder__preinit(mal_decoder__on_read_memory, mal_decoder__on_seek_memory, NULL, pConfig, pDecoder);
|
||||
if (result != MAL_SUCCESS) {
|
||||
return result;
|
||||
}
|
||||
|
||||
mal_zero_object(pDecoder);
|
||||
|
||||
if (pData == NULL || dataSize == 0) {
|
||||
return MAL_INVALID_ARGS;
|
||||
}
|
||||
@@ -21467,52 +21596,90 @@ mal_result mal_decoder__preinit_memory(const void* pData, size_t dataSize, const
|
||||
|
||||
mal_result mal_decoder_init_memory(const void* pData, size_t dataSize, const mal_decoder_config* pConfig, mal_decoder* pDecoder)
|
||||
{
|
||||
mal_result result = mal_decoder__preinit_memory(pData, dataSize, pConfig, pDecoder);
|
||||
mal_decoder_config config = mal_decoder_config_init_copy(pConfig); // Make sure the config is not NULL.
|
||||
|
||||
mal_result result = mal_decoder__preinit_memory(pData, dataSize, &config, pDecoder);
|
||||
if (result != MAL_SUCCESS) {
|
||||
return result;
|
||||
}
|
||||
|
||||
return mal_decoder_init(mal_decoder__on_read_memory, mal_decoder__on_seek_memory, NULL, pConfig, pDecoder);
|
||||
return mal_decoder_init__internal(mal_decoder__on_read_memory, mal_decoder__on_seek_memory, NULL, &config, pDecoder);
|
||||
}
|
||||
|
||||
mal_result mal_decoder_init_memory_wav(const void* pData, size_t dataSize, const mal_decoder_config* pConfig, mal_decoder* pDecoder)
|
||||
{
|
||||
mal_result result = mal_decoder__preinit_memory(pData, dataSize, pConfig, pDecoder);
|
||||
mal_decoder_config config = mal_decoder_config_init_copy(pConfig); // Make sure the config is not NULL.
|
||||
|
||||
mal_result result = mal_decoder__preinit_memory(pData, dataSize, &config, pDecoder);
|
||||
if (result != MAL_SUCCESS) {
|
||||
return result;
|
||||
}
|
||||
|
||||
return mal_decoder_init_wav(mal_decoder__on_read_memory, mal_decoder__on_seek_memory, NULL, pConfig, pDecoder);
|
||||
#ifdef MAL_HAS_WAV
|
||||
return mal_decoder_init_wav__internal(&config, pDecoder);
|
||||
#else
|
||||
return MAL_NO_BACKEND;
|
||||
#endif
|
||||
}
|
||||
|
||||
mal_result mal_decoder_init_memory_flac(const void* pData, size_t dataSize, const mal_decoder_config* pConfig, mal_decoder* pDecoder)
|
||||
{
|
||||
mal_result result = mal_decoder__preinit_memory(pData, dataSize, pConfig, pDecoder);
|
||||
mal_decoder_config config = mal_decoder_config_init_copy(pConfig); // Make sure the config is not NULL.
|
||||
|
||||
mal_result result = mal_decoder__preinit_memory(pData, dataSize, &config, pDecoder);
|
||||
if (result != MAL_SUCCESS) {
|
||||
return result;
|
||||
}
|
||||
|
||||
return mal_decoder_init_flac(mal_decoder__on_read_memory, mal_decoder__on_seek_memory, NULL, pConfig, pDecoder);
|
||||
#ifdef MAL_HAS_FLAC
|
||||
return mal_decoder_init_flac__internal(&config, pDecoder);
|
||||
#else
|
||||
return MAL_NO_BACKEND;
|
||||
#endif
|
||||
}
|
||||
|
||||
mal_result mal_decoder_init_memory_vorbis(const void* pData, size_t dataSize, const mal_decoder_config* pConfig, mal_decoder* pDecoder)
|
||||
{
|
||||
mal_result result = mal_decoder__preinit_memory(pData, dataSize, pConfig, pDecoder);
|
||||
mal_decoder_config config = mal_decoder_config_init_copy(pConfig); // Make sure the config is not NULL.
|
||||
|
||||
mal_result result = mal_decoder__preinit_memory(pData, dataSize, &config, pDecoder);
|
||||
if (result != MAL_SUCCESS) {
|
||||
return result;
|
||||
}
|
||||
|
||||
return mal_decoder_init_vorbis(mal_decoder__on_read_memory, mal_decoder__on_seek_memory, NULL, pConfig, pDecoder);
|
||||
#ifdef MAL_HAS_VORBIS
|
||||
return mal_decoder_init_vorbis__internal(&config, pDecoder);
|
||||
#else
|
||||
return MAL_NO_BACKEND;
|
||||
#endif
|
||||
}
|
||||
|
||||
mal_result mal_decoder_init_memory_mp3(const void* pData, size_t dataSize, const mal_decoder_config* pConfig, mal_decoder* pDecoder)
|
||||
{
|
||||
mal_result result = mal_decoder__preinit_memory(pData, dataSize, pConfig, pDecoder);
|
||||
mal_decoder_config config = mal_decoder_config_init_copy(pConfig); // Make sure the config is not NULL.
|
||||
|
||||
mal_result result = mal_decoder__preinit_memory(pData, dataSize, &config, pDecoder);
|
||||
if (result != MAL_SUCCESS) {
|
||||
return result;
|
||||
}
|
||||
|
||||
return mal_decoder_init_mp3(mal_decoder__on_read_memory, mal_decoder__on_seek_memory, NULL, pConfig, pDecoder);
|
||||
#ifdef MAL_HAS_MP3
|
||||
return mal_decoder_init_mp3__internal(&config, pDecoder);
|
||||
#else
|
||||
return MAL_NO_BACKEND;
|
||||
#endif
|
||||
}
|
||||
|
||||
mal_result mal_decoder_init_memory_raw(const void* pData, size_t dataSize, const mal_decoder_config* pConfigIn, const mal_decoder_config* pConfigOut, mal_decoder* pDecoder)
|
||||
{
|
||||
mal_decoder_config config = mal_decoder_config_init_copy(pConfigOut); // Make sure the config is not NULL.
|
||||
|
||||
mal_result result = mal_decoder__preinit_memory(pData, dataSize, &config, pDecoder);
|
||||
if (result != MAL_SUCCESS) {
|
||||
return result;
|
||||
}
|
||||
|
||||
return mal_decoder_init_raw__internal(pConfigIn, &config, pDecoder);
|
||||
}
|
||||
|
||||
#ifndef MAL_NO_STDIO
|
||||
@@ -21745,6 +21912,125 @@ mal_result mal_decoder_seek_to_frame(mal_decoder* pDecoder, mal_uint64 frameInde
|
||||
// Should never get here, but if we do it means onSeekToFrame was not set by the backend.
|
||||
return MAL_INVALID_ARGS;
|
||||
}
|
||||
|
||||
|
||||
mal_result mal_decoder__full_decode_and_uninit(mal_decoder* pDecoder, mal_decoder_config* pConfigOut, mal_uint64* pFrameCountOut, void** ppDataOut)
|
||||
{
|
||||
mal_assert(pDecoder != NULL);
|
||||
|
||||
mal_uint64 totalFrameCount = 0;
|
||||
mal_uint64 bpf = mal_get_bytes_per_frame(pDecoder->outputFormat, pDecoder->outputChannels);
|
||||
|
||||
// The frame count is unknown until we try reading. Thus, we just run in a loop.
|
||||
mal_uint64 dataCapInFrames = 0;
|
||||
void* pDataOut = NULL;
|
||||
for (;;) {
|
||||
// Make room if there's not enough.
|
||||
if (totalFrameCount == dataCapInFrames) {
|
||||
mal_uint64 newDataCapInFrames = dataCapInFrames*2;
|
||||
if (newDataCapInFrames == 0) {
|
||||
newDataCapInFrames = 4096;
|
||||
}
|
||||
|
||||
if ((newDataCapInFrames * bpf) > SIZE_MAX) {
|
||||
mal_free(pDataOut);
|
||||
return MAL_TOO_LARGE;
|
||||
}
|
||||
|
||||
|
||||
void* pNewDataOut = (void*)mal_realloc(pDataOut, (size_t)(newDataCapInFrames * bpf));
|
||||
if (pNewDataOut == NULL) {
|
||||
mal_free(pDataOut);
|
||||
return MAL_OUT_OF_MEMORY;
|
||||
}
|
||||
|
||||
dataCapInFrames = newDataCapInFrames;
|
||||
pDataOut = pNewDataOut;
|
||||
}
|
||||
|
||||
mal_uint64 frameCountToTryReading = dataCapInFrames - totalFrameCount;
|
||||
mal_assert(frameCountToTryReading > 0);
|
||||
|
||||
mal_uint64 framesJustRead = mal_decoder_read(pDecoder, frameCountToTryReading, (mal_uint8*)pDataOut + (totalFrameCount * bpf));
|
||||
totalFrameCount += framesJustRead;
|
||||
|
||||
if (framesJustRead < frameCountToTryReading) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if (pConfigOut != NULL) {
|
||||
pConfigOut->format = pDecoder->outputFormat;
|
||||
pConfigOut->channels = pDecoder->outputChannels;
|
||||
pConfigOut->sampleRate = pDecoder->outputSampleRate;
|
||||
mal_channel_map_copy(pConfigOut->channelMap, pDecoder->outputChannelMap, pDecoder->outputChannels);
|
||||
}
|
||||
|
||||
if (ppDataOut != NULL) {
|
||||
*ppDataOut = pDataOut;
|
||||
} else {
|
||||
mal_free(pDataOut);
|
||||
}
|
||||
|
||||
if (pFrameCountOut != NULL) {
|
||||
*pFrameCountOut = totalFrameCount;
|
||||
}
|
||||
|
||||
mal_decoder_uninit(pDecoder);
|
||||
return MAL_SUCCESS;
|
||||
}
|
||||
|
||||
#ifndef MAL_NO_STDIO
|
||||
mal_result mal_decode_file(const char* pFilePath, mal_decoder_config* pConfig, mal_uint64* pFrameCountOut, void** ppDataOut)
|
||||
{
|
||||
if (pFrameCountOut != NULL) {
|
||||
*pFrameCountOut = 0;
|
||||
}
|
||||
if (ppDataOut != NULL) {
|
||||
*ppDataOut = NULL;
|
||||
}
|
||||
|
||||
if (pFilePath == NULL) {
|
||||
return MAL_INVALID_ARGS;
|
||||
}
|
||||
|
||||
mal_decoder_config config = mal_decoder_config_init_copy(pConfig);
|
||||
|
||||
mal_decoder decoder;
|
||||
mal_result result = mal_decoder_init_file(pFilePath, &config, &decoder);
|
||||
if (result != MAL_SUCCESS) {
|
||||
return result;
|
||||
}
|
||||
|
||||
return mal_decoder__full_decode_and_uninit(&decoder, pConfig, pFrameCountOut, ppDataOut);
|
||||
}
|
||||
#endif
|
||||
|
||||
mal_result mal_decode_memory(const void* pData, size_t dataSize, mal_decoder_config* pConfig, mal_uint64* pFrameCountOut, void** ppDataOut)
|
||||
{
|
||||
if (pFrameCountOut != NULL) {
|
||||
*pFrameCountOut = 0;
|
||||
}
|
||||
if (ppDataOut != NULL) {
|
||||
*ppDataOut = NULL;
|
||||
}
|
||||
|
||||
if (pData == NULL || dataSize == 0) {
|
||||
return MAL_INVALID_ARGS;
|
||||
}
|
||||
|
||||
mal_decoder_config config = mal_decoder_config_init_copy(pConfig);
|
||||
|
||||
mal_decoder decoder;
|
||||
mal_result result = mal_decoder_init_memory(pData, dataSize, &config, &decoder);
|
||||
if (result != MAL_SUCCESS) {
|
||||
return result;
|
||||
}
|
||||
|
||||
return mal_decoder__full_decode_and_uninit(&decoder, pConfig, pFrameCountOut, ppDataOut);
|
||||
}
|
||||
|
||||
#endif // MAL_NO_DECODING
|
||||
|
||||
|
||||
@@ -21848,6 +22134,8 @@ mal_uint64 mal_sine_wave_read(mal_sine_wave* pSineWave, mal_uint64 count, float*
|
||||
// - Make some APIs more const-correct.
|
||||
// - Fix errors with OpenAL detection.
|
||||
// - Fix some memory leaks.
|
||||
// - Fix a bug with opening decoders from memory.
|
||||
// - Add support for decoding from raw PCM data (mal_decoder_init_raw(), etc.)
|
||||
// - Miscellaneous bug fixes.
|
||||
// - Documentation updates.
|
||||
//
|
||||
|
||||
Reference in New Issue
Block a user