Commit e01b60a8 authored by David Reid's avatar David Reid

Add support for decoding from raw PCM data.

parent ecf34e45
...@@ -640,6 +640,7 @@ typedef int mal_result; ...@@ -640,6 +640,7 @@ typedef int mal_result;
#define MAL_FAILED_TO_CREATE_THREAD -28 #define MAL_FAILED_TO_CREATE_THREAD -28
#define MAL_INVALID_DEVICE_CONFIG -29 #define MAL_INVALID_DEVICE_CONFIG -29
#define MAL_ACCESS_DENIED -30 #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_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); 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 ...@@ -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]); void mal_get_standard_channel_map(mal_standard_channel_map standardChannelMap, mal_uint32 channels, mal_channel channelMap[MAL_MAX_CHANNELS]);
// Copies a channel map. // 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. // Determines whether or not a channel map is valid.
...@@ -2264,10 +2265,10 @@ typedef mal_result (* mal_decoder_uninit_proc) (mal_decoder* pDecoder); ...@@ -2264,10 +2265,10 @@ typedef mal_result (* mal_decoder_uninit_proc) (mal_decoder* pDecoder);
typedef struct typedef struct
{ {
mal_format outputFormat; // Set to 0 or mal_format_unknown to use the stream's internal format. mal_format format; // 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 channels; // 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_uint32 sampleRate; // Set to 0 to use the stream's internal sample rate.
mal_channel outputChannelMap[MAL_MAX_CHANNELS]; mal_channel channelMap[MAL_MAX_CHANNELS];
} mal_decoder_config; } mal_decoder_config;
struct mal_decoder struct mal_decoder
...@@ -2302,12 +2303,14 @@ mal_result mal_decoder_init_wav(mal_decoder_read_proc onRead, mal_decoder_seek_p ...@@ -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_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_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_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(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_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_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_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_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 #ifndef MAL_NO_STDIO
mal_result mal_decoder_init_file(const char* pFilePath, const mal_decoder_config* pConfig, mal_decoder* pDecoder); 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); ...@@ -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_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); 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 #endif // MAL_NO_DECODING
...@@ -3134,6 +3145,19 @@ static MAL_INLINE float mal_mix_f32(float x, float y, float a) ...@@ -3134,6 +3145,19 @@ static MAL_INLINE float mal_mix_f32(float x, float y, float a)
{ {
return x*(1-a) + y*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) 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 ...@@ -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) { if (pOut != NULL && pIn != NULL && channels > 0) {
mal_copy_memory(pOut, pIn, sizeof(*pOut) * channels); 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 ...@@ -19970,7 +19994,7 @@ mal_uint32 mal_dsp__post_format_converter_on_read_deinterleaved(mal_format_conve
if (pDSP->isSRCRequired) { if (pDSP->isSRCRequired) {
return (mal_uint32)mal_src_read_deinterleaved_ex(&pDSP->src, frameCount, ppSamplesOut, pData->flush, pUserData); return (mal_uint32)mal_src_read_deinterleaved_ex(&pDSP->src, frameCount, ppSamplesOut, pData->flush, pUserData);
} else { } 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 ...@@ -20610,10 +20634,10 @@ mal_decoder_config mal_decoder_config_init(mal_format outputFormat, mal_uint32 o
{ {
mal_decoder_config config; mal_decoder_config config;
mal_zero_object(&config); mal_zero_object(&config);
config.outputFormat = outputFormat; config.format = outputFormat;
config.outputChannels = outputChannels; config.channels = outputChannels;
config.outputSampleRate = outputSampleRate; config.sampleRate = outputSampleRate;
mal_get_standard_channel_map(mal_standard_channel_map_default, config.outputChannels, config.outputChannelMap); mal_get_standard_channel_map(mal_standard_channel_map_default, config.channels, config.channelMap);
return config; return config;
} }
...@@ -20635,28 +20659,28 @@ mal_result mal_decoder__init_dsp(mal_decoder* pDecoder, const mal_decoder_config ...@@ -20635,28 +20659,28 @@ mal_result mal_decoder__init_dsp(mal_decoder* pDecoder, const mal_decoder_config
mal_assert(pDecoder != NULL); mal_assert(pDecoder != NULL);
// Output format. // Output format.
if (pConfig->outputFormat == mal_format_unknown) { if (pConfig->format == mal_format_unknown) {
pDecoder->outputFormat = pDecoder->internalFormat; pDecoder->outputFormat = pDecoder->internalFormat;
} else { } else {
pDecoder->outputFormat = pConfig->outputFormat; pDecoder->outputFormat = pConfig->format;
} }
if (pConfig->outputChannels == 0) { if (pConfig->channels == 0) {
pDecoder->outputChannels = pDecoder->internalChannels; pDecoder->outputChannels = pDecoder->internalChannels;
} else { } else {
pDecoder->outputChannels = pConfig->outputChannels; pDecoder->outputChannels = pConfig->channels;
} }
if (pConfig->outputSampleRate == 0) { if (pConfig->sampleRate == 0) {
pDecoder->outputSampleRate = pDecoder->internalSampleRate; pDecoder->outputSampleRate = pDecoder->internalSampleRate;
} else { } 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); mal_get_standard_channel_map(mal_standard_channel_map_default, pDecoder->outputChannels, pDecoder->outputChannelMap);
} else { } 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 ...@@ -21237,7 +21261,7 @@ mal_result mal_decoder_init_mp3__internal(const mal_decoder_config* pConfig, mal
drmp3_config mp3Config; drmp3_config mp3Config;
mal_zero_object(&mp3Config); mal_zero_object(&mp3Config);
mp3Config.outputChannels = 2; 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)) { if (!drmp3_init(pMP3, mal_decoder_internal_on_read__mp3, mal_decoder_internal_on_seek__mp3, pDecoder, &mp3Config)) {
return MAL_ERROR; return MAL_ERROR;
} }
...@@ -21263,6 +21287,92 @@ mal_result mal_decoder_init_mp3__internal(const mal_decoder_config* pConfig, mal ...@@ -21263,6 +21287,92 @@ mal_result mal_decoder_init_mp3__internal(const mal_decoder_config* pConfig, mal
} }
#endif #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_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); mal_assert(pConfig != NULL);
...@@ -21349,21 +21459,29 @@ mal_result mal_decoder_init_mp3(mal_decoder_read_proc onRead, mal_decoder_seek_p ...@@ -21349,21 +21459,29 @@ mal_result mal_decoder_init_mp3(mal_decoder_read_proc onRead, mal_decoder_seek_p
#endif #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); mal_result result = mal_decoder__preinit(onRead, onSeek, pUserData, &config, pDecoder);
if (result != MAL_SUCCESS) { if (result != MAL_SUCCESS) {
return result; 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. // We use trial and error to open a decoder.
result = MAL_NO_BACKEND; mal_result result = MAL_NO_BACKEND;
#ifdef MAL_HAS_WAV #ifdef MAL_HAS_WAV
if (result != MAL_SUCCESS) { if (result != MAL_SUCCESS) {
result = mal_decoder_init_wav__internal(&config, pDecoder); result = mal_decoder_init_wav__internal(pConfig, pDecoder);
if (result != MAL_SUCCESS) { if (result != MAL_SUCCESS) {
onSeek(pDecoder, 0, mal_seek_origin_start); 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 ...@@ -21371,7 +21489,7 @@ mal_result mal_decoder_init(mal_decoder_read_proc onRead, mal_decoder_seek_proc
#endif #endif
#ifdef MAL_HAS_FLAC #ifdef MAL_HAS_FLAC
if (result != MAL_SUCCESS) { if (result != MAL_SUCCESS) {
result = mal_decoder_init_flac__internal(&config, pDecoder); result = mal_decoder_init_flac__internal(pConfig, pDecoder);
if (result != MAL_SUCCESS) { if (result != MAL_SUCCESS) {
onSeek(pDecoder, 0, mal_seek_origin_start); 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 ...@@ -21379,7 +21497,7 @@ mal_result mal_decoder_init(mal_decoder_read_proc onRead, mal_decoder_seek_proc
#endif #endif
#ifdef MAL_HAS_VORBIS #ifdef MAL_HAS_VORBIS
if (result != MAL_SUCCESS) { if (result != MAL_SUCCESS) {
result = mal_decoder_init_vorbis__internal(&config, pDecoder); result = mal_decoder_init_vorbis__internal(pConfig, pDecoder);
if (result != MAL_SUCCESS) { if (result != MAL_SUCCESS) {
onSeek(pDecoder, 0, mal_seek_origin_start); 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 ...@@ -21387,7 +21505,7 @@ mal_result mal_decoder_init(mal_decoder_read_proc onRead, mal_decoder_seek_proc
#endif #endif
#ifdef MAL_HAS_MP3 #ifdef MAL_HAS_MP3
if (result != MAL_SUCCESS) { if (result != MAL_SUCCESS) {
result = mal_decoder_init_mp3__internal(&config, pDecoder); result = mal_decoder_init_mp3__internal(pConfig, pDecoder);
if (result != MAL_SUCCESS) { if (result != MAL_SUCCESS) {
onSeek(pDecoder, 0, mal_seek_origin_start); 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 ...@@ -21401,6 +21519,18 @@ mal_result mal_decoder_init(mal_decoder_read_proc onRead, mal_decoder_seek_proc
return result; 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) 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 ...@@ -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) mal_result mal_decoder__preinit_memory(const void* pData, size_t dataSize, const mal_decoder_config* pConfig, mal_decoder* pDecoder)
{ {
if (pDecoder == NULL) { mal_result result = mal_decoder__preinit(mal_decoder__on_read_memory, mal_decoder__on_seek_memory, NULL, pConfig, pDecoder);
return MAL_INVALID_ARGS; if (result != MAL_SUCCESS) {
return result;
} }
mal_zero_object(pDecoder);
if (pData == NULL || dataSize == 0) { if (pData == NULL || dataSize == 0) {
return MAL_INVALID_ARGS; return MAL_INVALID_ARGS;
} }
...@@ -21467,52 +21596,90 @@ mal_result mal_decoder__preinit_memory(const void* pData, size_t dataSize, const ...@@ -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 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) { if (result != MAL_SUCCESS) {
return result; 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 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) { if (result != MAL_SUCCESS) {
return result; 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 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) { if (result != MAL_SUCCESS) {
return result; 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 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) { if (result != MAL_SUCCESS) {
return result; 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 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) { if (result != MAL_SUCCESS) {
return result; 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 #ifndef MAL_NO_STDIO
...@@ -21745,6 +21912,125 @@ mal_result mal_decoder_seek_to_frame(mal_decoder* pDecoder, mal_uint64 frameInde ...@@ -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. // Should never get here, but if we do it means onSeekToFrame was not set by the backend.
return MAL_INVALID_ARGS; 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 #endif // MAL_NO_DECODING
...@@ -21848,6 +22134,8 @@ mal_uint64 mal_sine_wave_read(mal_sine_wave* pSineWave, mal_uint64 count, float* ...@@ -21848,6 +22134,8 @@ mal_uint64 mal_sine_wave_read(mal_sine_wave* pSineWave, mal_uint64 count, float*
// - Make some APIs more const-correct. // - Make some APIs more const-correct.
// - Fix errors with OpenAL detection. // - Fix errors with OpenAL detection.
// - Fix some memory leaks. // - 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. // - Miscellaneous bug fixes.
// - Documentation updates. // - Documentation updates.
// //
......
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