mirror of
https://github.com/mackron/miniaudio.git
synced 2026-04-22 00:06:59 +02:00
Initial work on the MP3 decoder.
This currently uses minimp3, but this may change.
This commit is contained in:
@@ -1,13 +1,25 @@
|
|||||||
#define _CRT_SECURE_NO_WARNINGS
|
#define _CRT_SECURE_NO_WARNINGS
|
||||||
|
|
||||||
// These are implemented at the bottom of this file.
|
// These are implemented at the bottom of this file.
|
||||||
#define STB_VORBIS_HEADER_ONLY
|
//#define STB_VORBIS_HEADER_ONLY
|
||||||
#include "../extras/stb_vorbis.c"
|
//#include "../extras/stb_vorbis.c"
|
||||||
#include "../extras/dr_flac.h"
|
#include "../extras/dr_flac.h"
|
||||||
#include "../extras/dr_wav.h"
|
#include "../extras/dr_wav.h"
|
||||||
#include "../extras/jar_mod.h"
|
#include "../extras/jar_mod.h"
|
||||||
#include "../extras/jar_xm.h"
|
#include "../extras/jar_xm.h"
|
||||||
|
|
||||||
|
#if defined(_MSC_VER)
|
||||||
|
#pragma warning(push)
|
||||||
|
#pragma warning(disable:4244)
|
||||||
|
#endif
|
||||||
|
#define MINIMP3_IMPLEMENTATION
|
||||||
|
#include "../extras/minimp3.h"
|
||||||
|
#if defined(_MSC_VER)
|
||||||
|
#pragma warning(pop)
|
||||||
|
#pragma warning(disable:4244)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
#define MAL_IMPLEMENTATION
|
#define MAL_IMPLEMENTATION
|
||||||
#include "../mini_al.h"
|
#include "../mini_al.h"
|
||||||
|
|
||||||
@@ -254,7 +266,7 @@ end:;
|
|||||||
#endif
|
#endif
|
||||||
#endif
|
#endif
|
||||||
#undef STB_VORBIS_HEADER_ONLY
|
#undef STB_VORBIS_HEADER_ONLY
|
||||||
#include "../extras/stb_vorbis.c"
|
//#include "../extras/stb_vorbis.c"
|
||||||
#if defined(_MSC_VER)
|
#if defined(_MSC_VER)
|
||||||
#pragma warning(pop)
|
#pragma warning(pop)
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@@ -1416,6 +1416,9 @@ static inline mal_device_config mal_device_config_init_playback(mal_format forma
|
|||||||
// Initializes a sample rate conversion object.
|
// Initializes a sample rate conversion object.
|
||||||
mal_result mal_src_init(mal_src_config* pConfig, mal_src_read_proc onRead, void* pUserData, mal_src* pSRC);
|
mal_result mal_src_init(mal_src_config* pConfig, mal_src_read_proc onRead, void* pUserData, mal_src* pSRC);
|
||||||
|
|
||||||
|
// Dynamically adjusts the input sample rate.
|
||||||
|
mal_result mal_src_set_input_sample_rate(mal_src* pSRC, mal_uint32 sampleRateIn);
|
||||||
|
|
||||||
// Dynamically adjusts the output sample rate.
|
// Dynamically adjusts the output sample rate.
|
||||||
//
|
//
|
||||||
// This is useful for dynamically adjust pitch. Keep in mind, however, that this will speed up or slow down the sound. If this
|
// This is useful for dynamically adjust pitch. Keep in mind, however, that this will speed up or slow down the sound. If this
|
||||||
@@ -1603,11 +1606,13 @@ mal_result mal_decoder_init(mal_decoder_read_proc onRead, mal_decoder_seek_proc
|
|||||||
mal_result mal_decoder_init_wav(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_wav(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_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_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);
|
||||||
|
|
||||||
#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);
|
||||||
@@ -10397,6 +10402,19 @@ mal_result mal_src_init(mal_src_config* pConfig, mal_src_read_proc onRead, void*
|
|||||||
return MAL_SUCCESS;
|
return MAL_SUCCESS;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
mal_result mal_src_set_input_sample_rate(mal_src* pSRC, mal_uint32 sampleRateIn)
|
||||||
|
{
|
||||||
|
if (pSRC == NULL) return MAL_INVALID_ARGS;
|
||||||
|
|
||||||
|
// Must have a sample rate of > 0.
|
||||||
|
if (sampleRateIn == 0) {
|
||||||
|
return MAL_INVALID_ARGS;
|
||||||
|
}
|
||||||
|
|
||||||
|
pSRC->config.sampleRateIn = sampleRateIn;
|
||||||
|
return MAL_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
mal_result mal_src_set_output_sample_rate(mal_src* pSRC, mal_uint32 sampleRateOut)
|
mal_result mal_src_set_output_sample_rate(mal_src* pSRC, mal_uint32 sampleRateOut)
|
||||||
{
|
{
|
||||||
if (pSRC == NULL) return MAL_INVALID_ARGS;
|
if (pSRC == NULL) return MAL_INVALID_ARGS;
|
||||||
@@ -11787,6 +11805,8 @@ static mal_uint32 mal_vorbis_decoder_read(mal_vorbis_decoder* pVorbis, mal_decod
|
|||||||
if (pNewData == NULL) {
|
if (pNewData == NULL) {
|
||||||
return totalFramesRead; // Out of memory.
|
return totalFramesRead; // Out of memory.
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pVorbis->pData = pNewData;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Fill in a chunk.
|
// Fill in a chunk.
|
||||||
@@ -11820,6 +11840,7 @@ static mal_result mal_vorbis_decoder_seek_to_frame(mal_vorbis_decoder* pVorbis,
|
|||||||
stb_vorbis_flush_pushdata(pVorbis->pInternalVorbis);
|
stb_vorbis_flush_pushdata(pVorbis->pInternalVorbis);
|
||||||
pVorbis->framesConsumed = 0;
|
pVorbis->framesConsumed = 0;
|
||||||
pVorbis->framesRemaining = 0;
|
pVorbis->framesRemaining = 0;
|
||||||
|
pVorbis->dataSize = 0;
|
||||||
|
|
||||||
float buffer[4096];
|
float buffer[4096];
|
||||||
while (frameIndex > 0) {
|
while (frameIndex > 0) {
|
||||||
@@ -11982,6 +12003,269 @@ mal_result mal_decoder_init_vorbis__internal(const mal_decoder_config* pConfig,
|
|||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
// MP3
|
||||||
|
#if defined(MINIMP3_MAX_SAMPLES_PER_FRAME) || defined(MINIMP3_IMPLEMENTATION)
|
||||||
|
#define MAL_HAS_MP3
|
||||||
|
|
||||||
|
// The size in bytes of each chunk of data to read from the MP3 stream. minimp3 recommends 16K.
|
||||||
|
#define MAL_MP3_DATA_CHUNK_SIZE 16384
|
||||||
|
|
||||||
|
typedef struct
|
||||||
|
{
|
||||||
|
mp3dec_t internalMP3;
|
||||||
|
mal_uint8* pData;
|
||||||
|
size_t dataSize;
|
||||||
|
size_t dataCapacity;
|
||||||
|
mal_uint32 framesConsumed; // The number of frames consumed in pPacketData.
|
||||||
|
mal_uint32 framesRemaining; // The number of frames remaining in pPacketData.
|
||||||
|
mal_uint32 packetChannels; // The channel count can technically be different across frames.
|
||||||
|
mal_uint32 packetSampleRate; // The sample rate can change across frames.
|
||||||
|
mal_int16 pPacketData[MINIMP3_MAX_SAMPLES_PER_FRAME];
|
||||||
|
mal_src src; // For handling variable sample rates between frames.
|
||||||
|
} mal_mp3_decoder;
|
||||||
|
|
||||||
|
static mal_uint32 mal_mp3_decoder_read_src(mal_src* pSRC, mal_uint32 frameCount, void* pFramesOut, void* pUserData)
|
||||||
|
{
|
||||||
|
mal_decoder* pDecoder = (mal_decoder*)pUserData;
|
||||||
|
mal_assert(pDecoder != NULL);
|
||||||
|
mal_assert(pDecoder->onRead != NULL);
|
||||||
|
|
||||||
|
mal_mp3_decoder* pMP3 = (mal_mp3_decoder*)pDecoder->pInternalDecoder;
|
||||||
|
mal_assert(pMP3 != NULL);
|
||||||
|
|
||||||
|
mal_int16* pFramesOut16 = (mal_int16*)pFramesOut;
|
||||||
|
mal_uint32 totalFramesRead = 0;
|
||||||
|
|
||||||
|
while (frameCount > 0) {
|
||||||
|
// Read from the in-memory buffer first.
|
||||||
|
while (pMP3->framesRemaining > 0 && frameCount > 0) {
|
||||||
|
if (pMP3->packetChannels == 1) {
|
||||||
|
pFramesOut16[0] = pMP3->pPacketData[pMP3->framesConsumed];
|
||||||
|
pFramesOut16[1] = pMP3->pPacketData[pMP3->framesConsumed];
|
||||||
|
} else {
|
||||||
|
mal_assert(pMP3->packetChannels == 2);
|
||||||
|
pFramesOut16[0] = pMP3->pPacketData[(pMP3->framesConsumed*pMP3->packetChannels)+0];
|
||||||
|
pFramesOut16[1] = pMP3->pPacketData[(pMP3->framesConsumed*pMP3->packetChannels)+1];
|
||||||
|
}
|
||||||
|
|
||||||
|
pMP3->framesConsumed += 1;
|
||||||
|
pMP3->framesRemaining -= 1;
|
||||||
|
frameCount -= 1;
|
||||||
|
totalFramesRead += 1;
|
||||||
|
pFramesOut16 += pSRC->config.channels; // Should always be equal to 2.
|
||||||
|
}
|
||||||
|
|
||||||
|
if (frameCount == 0) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
mal_assert(pMP3->framesRemaining == 0);
|
||||||
|
|
||||||
|
// At this point we have exhausted our in-memory buffer so we need to re-fill. Note that the sample rate may have changed
|
||||||
|
// at this point which means we'll also need to update our sample rate conversion pipeline.
|
||||||
|
do
|
||||||
|
{
|
||||||
|
// minimp3 recommends doing data submission in 16K chunks. If we don't have at least 16K bytes available, get more.
|
||||||
|
if (pMP3->dataSize < MAL_MP3_DATA_CHUNK_SIZE) {
|
||||||
|
if (pMP3->dataCapacity < MAL_MP3_DATA_CHUNK_SIZE) {
|
||||||
|
pMP3->dataCapacity = MAL_MP3_DATA_CHUNK_SIZE;
|
||||||
|
mal_uint8* pNewData = (mal_uint8*)mal_realloc(pMP3->pData, pMP3->dataCapacity);
|
||||||
|
if (pNewData == NULL) {
|
||||||
|
return totalFramesRead; // Out of memory.
|
||||||
|
}
|
||||||
|
|
||||||
|
pMP3->pData = pNewData;
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t bytesRead = pDecoder->onRead(pDecoder, pMP3->pData + pMP3->dataSize, (pMP3->dataCapacity - pMP3->dataSize));
|
||||||
|
pMP3->dataSize += bytesRead;
|
||||||
|
}
|
||||||
|
|
||||||
|
mp3dec_frame_info_t info;
|
||||||
|
mal_uint32 samplesRead = mp3dec_decode_frame(&pMP3->internalMP3, pMP3->pData, pMP3->dataSize, pMP3->pPacketData, &info);
|
||||||
|
if (samplesRead != 0) {
|
||||||
|
size_t leftoverDataSize = (pMP3->dataSize - (size_t)info.frame_bytes);
|
||||||
|
for (size_t i = 0; i < leftoverDataSize; ++i) {
|
||||||
|
pMP3->pData[i] = pMP3->pData[i + (size_t)info.frame_bytes];
|
||||||
|
}
|
||||||
|
|
||||||
|
pMP3->dataSize = leftoverDataSize;
|
||||||
|
pMP3->framesConsumed = 0;
|
||||||
|
pMP3->framesRemaining = samplesRead;
|
||||||
|
pMP3->packetChannels = info.channels;
|
||||||
|
pMP3->packetSampleRate = info.hz;
|
||||||
|
mal_src_set_input_sample_rate(&pMP3->src, pMP3->packetSampleRate);
|
||||||
|
break;
|
||||||
|
} else {
|
||||||
|
// Need more data. minimp3 recommends doing data submission in 16K chunks.
|
||||||
|
if (pMP3->dataCapacity == pMP3->dataSize) {
|
||||||
|
// No room. Expand.
|
||||||
|
pMP3->dataCapacity += MAL_MP3_DATA_CHUNK_SIZE;
|
||||||
|
mal_uint8* pNewData = (mal_uint8*)mal_realloc(pMP3->pData, pMP3->dataCapacity);
|
||||||
|
if (pNewData == NULL) {
|
||||||
|
return totalFramesRead; // Out of memory.
|
||||||
|
}
|
||||||
|
|
||||||
|
pMP3->pData = pNewData;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fill in a chunk.
|
||||||
|
size_t bytesRead = pDecoder->onRead(pDecoder, pMP3->pData + pMP3->dataSize, (pMP3->dataCapacity - pMP3->dataSize));
|
||||||
|
if (bytesRead == 0) {
|
||||||
|
return totalFramesRead; // Error reading more data.
|
||||||
|
}
|
||||||
|
|
||||||
|
pMP3->dataSize += bytesRead;
|
||||||
|
}
|
||||||
|
} while (MAL_TRUE);
|
||||||
|
}
|
||||||
|
|
||||||
|
return totalFramesRead;
|
||||||
|
}
|
||||||
|
|
||||||
|
static mal_uint32 mal_mp3_decoder_read(mal_mp3_decoder* pMP3, mal_decoder* pDecoder, mal_uint32 frameCount, void* pSamplesOut)
|
||||||
|
{
|
||||||
|
(void)pDecoder;
|
||||||
|
|
||||||
|
mal_assert(pMP3 != NULL);
|
||||||
|
mal_assert(pDecoder != NULL);
|
||||||
|
|
||||||
|
return mal_src_read_frames_ex(&pMP3->src, frameCount, pSamplesOut, MAL_TRUE);
|
||||||
|
}
|
||||||
|
|
||||||
|
static mal_result mal_mp3_decoder_seek_to_frame(mal_mp3_decoder* pMP3, mal_decoder* pDecoder, mal_uint64 frameIndex)
|
||||||
|
{
|
||||||
|
mal_assert(pMP3 != NULL);
|
||||||
|
mal_assert(pDecoder != NULL);
|
||||||
|
mal_assert(pDecoder->onRead != NULL);
|
||||||
|
mal_assert(pDecoder->onSeek != NULL);
|
||||||
|
|
||||||
|
// This is terribly inefficient because stb_vorbis does not have a good seeking solution with it's push API. Currently this just performs
|
||||||
|
// a full decode right from the start of the stream. Later on I'll need to write a layer that goes through all of the Ogg pages until we
|
||||||
|
// find the one containing the sample we need. Then we know exactly where to seek for stb_vorbis.
|
||||||
|
if (!pDecoder->onSeek(pDecoder, 0, mal_seek_origin_start)) {
|
||||||
|
return MAL_ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
|
pMP3->framesConsumed = 0;
|
||||||
|
pMP3->framesRemaining = 0;
|
||||||
|
pMP3->dataSize = 0;
|
||||||
|
|
||||||
|
float buffer[4096];
|
||||||
|
while (frameIndex > 0) {
|
||||||
|
mal_uint32 framesToRead = mal_countof(buffer)/pDecoder->internalChannels;
|
||||||
|
if (framesToRead > frameIndex) {
|
||||||
|
framesToRead = (mal_uint32)frameIndex;
|
||||||
|
}
|
||||||
|
|
||||||
|
mal_uint32 framesRead = mal_mp3_decoder_read(pMP3, pDecoder, framesToRead, buffer);
|
||||||
|
if (framesRead == 0) {
|
||||||
|
return MAL_ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
|
frameIndex -= framesRead;
|
||||||
|
}
|
||||||
|
|
||||||
|
return MAL_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static mal_result mal_decoder_internal_on_seek_to_frame__mp3(mal_decoder* pDecoder, mal_uint64 frameIndex)
|
||||||
|
{
|
||||||
|
mal_assert(pDecoder != NULL);
|
||||||
|
mal_assert(pDecoder->onRead != NULL);
|
||||||
|
mal_assert(pDecoder->onSeek != NULL);
|
||||||
|
|
||||||
|
mal_mp3_decoder* pMP3 = (mal_mp3_decoder*)pDecoder->pInternalDecoder;
|
||||||
|
mal_assert(pMP3 != NULL);
|
||||||
|
|
||||||
|
return mal_mp3_decoder_seek_to_frame(pMP3, pDecoder, frameIndex);
|
||||||
|
}
|
||||||
|
|
||||||
|
static mal_result mal_decoder_internal_on_uninit__mp3(mal_decoder* pDecoder)
|
||||||
|
{
|
||||||
|
mal_mp3_decoder* pMP3 = (mal_mp3_decoder*)pDecoder->pInternalDecoder;
|
||||||
|
mal_assert(pMP3 != NULL);
|
||||||
|
|
||||||
|
mal_free(pMP3->pData);
|
||||||
|
mal_free(pMP3);
|
||||||
|
|
||||||
|
return MAL_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
static mal_uint32 mal_decoder_internal_on_read_frames__mp3(mal_dsp* pDSP, mal_uint32 frameCount, void* pSamplesOut, void* pUserData)
|
||||||
|
{
|
||||||
|
(void)pDSP;
|
||||||
|
|
||||||
|
mal_decoder* pDecoder = (mal_decoder*)pUserData;
|
||||||
|
mal_assert(pDecoder != NULL);
|
||||||
|
mal_assert(pDecoder->internalFormat == mal_format_f32);
|
||||||
|
mal_assert(pDecoder->onRead != NULL);
|
||||||
|
mal_assert(pDecoder->onSeek != NULL);
|
||||||
|
|
||||||
|
mal_mp3_decoder* pMP3 = (mal_mp3_decoder*)pDecoder->pInternalDecoder;
|
||||||
|
mal_assert(pMP3 != NULL);
|
||||||
|
|
||||||
|
return mal_mp3_decoder_read(pMP3, pDecoder, frameCount, pSamplesOut);
|
||||||
|
}
|
||||||
|
|
||||||
|
mal_result mal_decoder_init_mp3__internal(const mal_decoder_config* pConfig, mal_decoder* pDecoder)
|
||||||
|
{
|
||||||
|
mal_assert(pConfig != NULL);
|
||||||
|
mal_assert(pDecoder != NULL);
|
||||||
|
mal_assert(pDecoder->onRead != NULL);
|
||||||
|
mal_assert(pDecoder->onSeek != NULL);
|
||||||
|
|
||||||
|
mal_mp3_decoder* pMP3 = (mal_mp3_decoder*)mal_malloc(sizeof(*pMP3));
|
||||||
|
if (pMP3 == NULL) {
|
||||||
|
return MAL_OUT_OF_MEMORY;
|
||||||
|
}
|
||||||
|
|
||||||
|
mal_zero_object(pMP3);
|
||||||
|
mp3dec_init(&pMP3->internalMP3);
|
||||||
|
|
||||||
|
pDecoder->onSeekToFrame = mal_decoder_internal_on_seek_to_frame__mp3;
|
||||||
|
pDecoder->onUninit = mal_decoder_internal_on_uninit__mp3;
|
||||||
|
pDecoder->pInternalDecoder = pMP3;
|
||||||
|
|
||||||
|
// minimp3 doesn't actually define the channel count nor sample rate global - it's instead per frame/packet. We therefore need
|
||||||
|
// to use some smarts to determine the most appropriate internal sample rate. These are the rules we're going to use:
|
||||||
|
//
|
||||||
|
// Sample Rates
|
||||||
|
// 1) If an output sample rate is specified in pConfig we just use that. Otherwise;
|
||||||
|
// 2) Fall back to 44100.
|
||||||
|
//
|
||||||
|
// The internal channel count is always stereo, and the internal format is always f32.
|
||||||
|
pDecoder->internalFormat = mal_format_f32;
|
||||||
|
pDecoder->internalChannels = 2;
|
||||||
|
pDecoder->internalSampleRate = (pConfig->outputSampleRate != 0) ? pConfig->outputSampleRate : 44100;
|
||||||
|
mal_get_default_device_config_channel_map(pDecoder->internalChannels, pDecoder->internalChannelMap);
|
||||||
|
|
||||||
|
mal_src_config srcConfig;
|
||||||
|
srcConfig.sampleRateIn = 44100;
|
||||||
|
srcConfig.sampleRateOut = pDecoder->internalSampleRate;
|
||||||
|
srcConfig.formatIn = mal_format_s16;
|
||||||
|
srcConfig.formatOut = mal_format_f32;
|
||||||
|
srcConfig.channels = 2;
|
||||||
|
srcConfig.algorithm = mal_src_algorithm_linear;
|
||||||
|
srcConfig.cacheSizeInFrames = 0;
|
||||||
|
mal_result result = mal_src_init(&srcConfig, mal_mp3_decoder_read_src, pDecoder, &pMP3->src);
|
||||||
|
if (result != MAL_SUCCESS) {
|
||||||
|
mal_free(pMP3);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
result = mal_decoder__init_dsp(pDecoder, pConfig, mal_decoder_internal_on_read_frames__mp3);
|
||||||
|
if (result != MAL_SUCCESS) {
|
||||||
|
mal_free(pMP3);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
return MAL_SUCCESS;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
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);
|
||||||
@@ -12049,6 +12333,22 @@ mal_result mal_decoder_init_vorbis(mal_decoder_read_proc onRead, mal_decoder_see
|
|||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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_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;
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef MAL_HAS_MP3
|
||||||
|
return mal_decoder_init_mp3__internal(&config, pDecoder);
|
||||||
|
#else
|
||||||
|
return MAL_NO_BACKEND;
|
||||||
|
#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(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_decoder_config config = mal_decoder_config_init_copy(pConfig);
|
||||||
@@ -12077,6 +12377,14 @@ mal_result mal_decoder_init(mal_decoder_read_proc onRead, mal_decoder_seek_proc
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
#ifdef MAL_HAS_MP3
|
||||||
|
if (result != MAL_SUCCESS) {
|
||||||
|
result = mal_decoder_init_mp3__internal(&config, pDecoder);
|
||||||
|
if (result != MAL_SUCCESS) {
|
||||||
|
onSeek(pDecoder, 0, mal_seek_origin_start);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#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(&config, pDecoder);
|
||||||
@@ -12194,6 +12502,16 @@ mal_result mal_decoder_init_memory_vorbis(const void* pData, size_t dataSize, co
|
|||||||
return mal_decoder_init_vorbis(mal_decoder__on_read_memory, mal_decoder__on_seek_memory, NULL, pConfig, pDecoder);
|
return mal_decoder_init_vorbis(mal_decoder__on_read_memory, mal_decoder__on_seek_memory, NULL, pConfig, 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);
|
||||||
|
if (result == MAL_SUCCESS) {
|
||||||
|
return MAL_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
return mal_decoder_init_mp3(mal_decoder__on_read_memory, mal_decoder__on_seek_memory, NULL, pConfig, pDecoder);
|
||||||
|
}
|
||||||
|
|
||||||
#ifndef MAL_NO_STDIO
|
#ifndef MAL_NO_STDIO
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#ifndef _MSC_VER
|
#ifndef _MSC_VER
|
||||||
@@ -12317,6 +12635,16 @@ mal_result mal_decoder_init_file(const char* pFilePath, const mal_decoder_config
|
|||||||
mal_decoder__on_seek_stdio(pDecoder, 0, mal_seek_origin_start);
|
mal_decoder__on_seek_stdio(pDecoder, 0, mal_seek_origin_start);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// MP3
|
||||||
|
if (mal_path_extension_equal(pFilePath, "mp3")) {
|
||||||
|
mal_result result = mal_decoder_init_mp3(mal_decoder__on_read_stdio, mal_decoder__on_seek_stdio, (void*)pFile, pConfig, pDecoder);
|
||||||
|
if (result == MAL_SUCCESS) {
|
||||||
|
return MAL_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
mal_decoder__on_seek_stdio(pDecoder, 0, mal_seek_origin_start);
|
||||||
|
}
|
||||||
|
|
||||||
// Trial and error.
|
// Trial and error.
|
||||||
return mal_decoder_init(mal_decoder__on_read_stdio, mal_decoder__on_seek_stdio, (void*)pFile, pConfig, pDecoder);
|
return mal_decoder_init(mal_decoder__on_read_stdio, mal_decoder__on_seek_stdio, (void*)pFile, pConfig, pDecoder);
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user