diff --git a/src/external/dr_mp3.h b/src/external/dr_mp3.h index cc033ce87..c0969c476 100644 --- a/src/external/dr_mp3.h +++ b/src/external/dr_mp3.h @@ -1,6 +1,6 @@ /* MP3 audio decoder. Choice of public domain or MIT-0. See license statements at the end of this file. -dr_mp3 - v0.7.0 - TBD +dr_mp3 - v0.7.4 - TBD David Reid - mackron@gmail.com @@ -72,7 +72,7 @@ extern "C" { #define DRMP3_VERSION_MAJOR 0 #define DRMP3_VERSION_MINOR 7 -#define DRMP3_VERSION_REVISION 0 +#define DRMP3_VERSION_REVISION 4 #define DRMP3_VERSION_STRING DRMP3_XSTRINGIFY(DRMP3_VERSION_MAJOR) "." DRMP3_XSTRINGIFY(DRMP3_VERSION_MINOR) "." DRMP3_XSTRINGIFY(DRMP3_VERSION_REVISION) #include /* For size_t. */ @@ -257,16 +257,45 @@ typedef struct Low Level Push API ================== */ +#define DRMP3_MAX_BITRESERVOIR_BYTES 511 +#define DRMP3_MAX_FREE_FORMAT_FRAME_SIZE 2304 /* more than ISO spec's */ +#define DRMP3_MAX_L3_FRAME_PAYLOAD_BYTES DRMP3_MAX_FREE_FORMAT_FRAME_SIZE /* MUST be >= 320000/8/32000*1152 = 1440 */ + typedef struct { int frame_bytes, channels, sample_rate, layer, bitrate_kbps; } drmp3dec_frame_info; +typedef struct +{ + const drmp3_uint8 *buf; + int pos, limit; +} drmp3_bs; + +typedef struct +{ + const drmp3_uint8 *sfbtab; + drmp3_uint16 part_23_length, big_values, scalefac_compress; + drmp3_uint8 global_gain, block_type, mixed_block_flag, n_long_sfb, n_short_sfb; + drmp3_uint8 table_select[3], region_count[3], subblock_gain[3]; + drmp3_uint8 preflag, scalefac_scale, count1_table, scfsi; +} drmp3_L3_gr_info; + +typedef struct +{ + drmp3_bs bs; + drmp3_uint8 maindata[DRMP3_MAX_BITRESERVOIR_BYTES + DRMP3_MAX_L3_FRAME_PAYLOAD_BYTES]; + drmp3_L3_gr_info gr_info[4]; + float grbuf[2][576], scf[40], syn[18 + 15][2*32]; + drmp3_uint8 ist_pos[2][39]; +} drmp3dec_scratch; + typedef struct { float mdct_overlap[2][9*32], qmf_state[15*2*32]; int reserv, free_format_bytes; drmp3_uint8 header[4], reserv_buf[511]; + drmp3dec_scratch scratch; } drmp3dec; /* Initializes a low level decoder. */ @@ -592,14 +621,10 @@ DRMP3_API const char* drmp3_version_string(void) #define DRMP3_OFFSET_PTR(p, offset) ((void*)((drmp3_uint8*)(p) + (offset))) -#define DRMP3_MAX_FREE_FORMAT_FRAME_SIZE 2304 /* more than ISO spec's */ #ifndef DRMP3_MAX_FRAME_SYNC_MATCHES #define DRMP3_MAX_FRAME_SYNC_MATCHES 10 #endif -#define DRMP3_MAX_L3_FRAME_PAYLOAD_BYTES DRMP3_MAX_FREE_FORMAT_FRAME_SIZE /* MUST be >= 320000/8/32000*1152 = 1440 */ - -#define DRMP3_MAX_BITRESERVOIR_BYTES 511 #define DRMP3_SHORT_BLOCK_TYPE 2 #define DRMP3_STOP_BLOCK_TYPE 3 #define DRMP3_MODE_MONO 3 @@ -632,8 +657,10 @@ DRMP3_API const char* drmp3_version_string(void) #if !defined(DR_MP3_NO_SIMD) -#if !defined(DR_MP3_ONLY_SIMD) && (defined(_M_X64) || defined(__x86_64__) || defined(__aarch64__) || defined(_M_ARM64) || defined(_M_ARM64EC)) -/* x64 always have SSE2, arm64 always have neon, no need for generic code */ +#if !defined(DR_MP3_ONLY_SIMD) && ((defined(_MSC_VER) && _MSC_VER >= 1400) && defined(_M_X64)) || ((defined(__i386) || defined(_M_IX86) || defined(__i386__) || defined(__x86_64__)) && ((defined(_M_IX86_FP) && _M_IX86_FP == 2) || defined(__SSE2__))) +#define DR_MP3_ONLY_SIMD +#endif +#if !defined(DR_MP3_ONLY_SIMD) && (defined(__ARM_NEON) || defined(__aarch64__) || defined(_M_ARM64) || defined(_M_ARM64EC)) #define DR_MP3_ONLY_SIMD #endif @@ -655,7 +682,7 @@ DRMP3_API const char* drmp3_version_string(void) #define DRMP3_VMUL_S(x, s) _mm_mul_ps(x, _mm_set1_ps(s)) #define DRMP3_VREV(x) _mm_shuffle_ps(x, x, _MM_SHUFFLE(0, 1, 2, 3)) typedef __m128 drmp3_f4; -#if defined(_MSC_VER) || defined(DR_MP3_ONLY_SIMD) +#if (defined(_MSC_VER) || defined(DR_MP3_ONLY_SIMD)) && !defined(__clang__) #define drmp3_cpuid __cpuid #else static __inline__ __attribute__((always_inline)) void drmp3_cpuid(int CPUInfo[], const int InfoType) @@ -779,11 +806,7 @@ static __inline__ __attribute__((always_inline)) drmp3_int32 drmp3_clip_int16_ar #define DRMP3_FREE(p) free((p)) #endif -typedef struct -{ - const drmp3_uint8 *buf; - int pos, limit; -} drmp3_bs; + typedef struct { @@ -796,24 +819,6 @@ typedef struct drmp3_uint8 tab_offset, code_tab_width, band_count; } drmp3_L12_subband_alloc; -typedef struct -{ - const drmp3_uint8 *sfbtab; - drmp3_uint16 part_23_length, big_values, scalefac_compress; - drmp3_uint8 global_gain, block_type, mixed_block_flag, n_long_sfb, n_short_sfb; - drmp3_uint8 table_select[3], region_count[3], subblock_gain[3]; - drmp3_uint8 preflag, scalefac_scale, count1_table, scfsi; -} drmp3_L3_gr_info; - -typedef struct -{ - drmp3_bs bs; - drmp3_uint8 maindata[DRMP3_MAX_BITRESERVOIR_BYTES + DRMP3_MAX_L3_FRAME_PAYLOAD_BYTES]; - drmp3_L3_gr_info gr_info[4]; - float grbuf[2][576], scf[40], syn[18 + 15][2*32]; - drmp3_uint8 ist_pos[2][39]; -} drmp3dec_scratch; - static void drmp3_bs_init(drmp3_bs *bs, const drmp3_uint8 *data, int bytes) { bs->buf = data; @@ -1227,6 +1232,14 @@ static float drmp3_L3_ldexp_q2(float y, int exp_q2) return y; } +/* +I've had reports of GCC 14 throwing an incorrect -Wstringop-overflow warning here. This is an attempt +to silence this warning. +*/ +#if (defined(__GNUC__) && (__GNUC__ >= 13)) && !defined(__clang__) + #pragma GCC diagnostic push + #pragma GCC diagnostic ignored "-Wstringop-overflow" +#endif static void drmp3_L3_decode_scalefactors(const drmp3_uint8 *hdr, drmp3_uint8 *ist_pos, drmp3_bs *bs, const drmp3_L3_gr_info *gr, float *scf, int ch) { static const drmp3_uint8 g_scf_partitions[3][28] = { @@ -1288,6 +1301,9 @@ static void drmp3_L3_decode_scalefactors(const drmp3_uint8 *hdr, drmp3_uint8 *is scf[i] = drmp3_L3_ldexp_q2(gain, iscf[i] << scf_shift); } } +#if (defined(__GNUC__) && (__GNUC__ >= 13)) && !defined(__clang__) + #pragma GCC diagnostic pop +#endif static const float g_drmp3_pow43[129 + 16] = { 0,-1,-2.519842f,-4.326749f,-6.349604f,-8.549880f,-10.902724f,-13.390518f,-16.000000f,-18.720754f,-21.544347f,-24.463781f,-27.473142f,-30.567351f,-33.741992f,-36.993181f, @@ -2299,7 +2315,6 @@ DRMP3_API int drmp3dec_decode_frame(drmp3dec *dec, const drmp3_uint8 *mp3, int m int i = 0, igr, frame_size = 0, success = 1; const drmp3_uint8 *hdr; drmp3_bs bs_frame[1]; - drmp3dec_scratch scratch; if (mp3_bytes > 4 && dec->header[0] == 0xff && drmp3_hdr_compare(dec->header, mp3)) { @@ -2336,23 +2351,23 @@ DRMP3_API int drmp3dec_decode_frame(drmp3dec *dec, const drmp3_uint8 *mp3, int m if (info->layer == 3) { - int main_data_begin = drmp3_L3_read_side_info(bs_frame, scratch.gr_info, hdr); + int main_data_begin = drmp3_L3_read_side_info(bs_frame, dec->scratch.gr_info, hdr); if (main_data_begin < 0 || bs_frame->pos > bs_frame->limit) { drmp3dec_init(dec); return 0; } - success = drmp3_L3_restore_reservoir(dec, bs_frame, &scratch, main_data_begin); + success = drmp3_L3_restore_reservoir(dec, bs_frame, &dec->scratch, main_data_begin); if (success && pcm != NULL) { for (igr = 0; igr < (DRMP3_HDR_TEST_MPEG1(hdr) ? 2 : 1); igr++, pcm = DRMP3_OFFSET_PTR(pcm, sizeof(drmp3d_sample_t)*576*info->channels)) { - DRMP3_ZERO_MEMORY(scratch.grbuf[0], 576*2*sizeof(float)); - drmp3_L3_decode(dec, &scratch, scratch.gr_info + igr*info->channels, info->channels); - drmp3d_synth_granule(dec->qmf_state, scratch.grbuf[0], 18, info->channels, (drmp3d_sample_t*)pcm, scratch.syn[0]); + DRMP3_ZERO_MEMORY(dec->scratch.grbuf[0], 576*2*sizeof(float)); + drmp3_L3_decode(dec, &dec->scratch, dec->scratch.gr_info + igr*info->channels, info->channels); + drmp3d_synth_granule(dec->qmf_state, dec->scratch.grbuf[0], 18, info->channels, (drmp3d_sample_t*)pcm, dec->scratch.syn[0]); } } - drmp3_L3_save_reservoir(dec, &scratch); + drmp3_L3_save_reservoir(dec, &dec->scratch); } else { #ifdef DR_MP3_ONLY_MP3 @@ -2366,15 +2381,15 @@ DRMP3_API int drmp3dec_decode_frame(drmp3dec *dec, const drmp3_uint8 *mp3, int m drmp3_L12_read_scale_info(hdr, bs_frame, sci); - DRMP3_ZERO_MEMORY(scratch.grbuf[0], 576*2*sizeof(float)); + DRMP3_ZERO_MEMORY(dec->scratch.grbuf[0], 576*2*sizeof(float)); for (i = 0, igr = 0; igr < 3; igr++) { - if (12 == (i += drmp3_L12_dequantize_granule(scratch.grbuf[0] + i, bs_frame, sci, info->layer | 1))) + if (12 == (i += drmp3_L12_dequantize_granule(dec->scratch.grbuf[0] + i, bs_frame, sci, info->layer | 1))) { i = 0; - drmp3_L12_apply_scf_384(sci, sci->scf + igr, scratch.grbuf[0]); - drmp3d_synth_granule(dec->qmf_state, scratch.grbuf[0], 12, info->channels, (drmp3d_sample_t*)pcm, scratch.syn[0]); - DRMP3_ZERO_MEMORY(scratch.grbuf[0], 576*2*sizeof(float)); + drmp3_L12_apply_scf_384(sci, sci->scf + igr, dec->scratch.grbuf[0]); + drmp3d_synth_granule(dec->qmf_state, dec->scratch.grbuf[0], 12, info->channels, (drmp3d_sample_t*)pcm, dec->scratch.syn[0]); + DRMP3_ZERO_MEMORY(dec->scratch.grbuf[0], 576*2*sizeof(float)); pcm = DRMP3_OFFSET_PTR(pcm, sizeof(drmp3d_sample_t)*384*info->channels); } if (bs_frame->pos > bs_frame->limit) @@ -3005,23 +3020,27 @@ static drmp3_bool32 drmp3_init_internal(drmp3* pMP3, drmp3_read_proc onRead, drm ((drmp3_uint32)ape[26] << 16) | ((drmp3_uint32)ape[27] << 24); - streamEndOffset -= 32 + tagSize; - streamLen -= 32 + tagSize; - - /* Fire a metadata callback for the APE data. Must include both the main content and footer. */ - if (onMeta != NULL) { - /* We first need to seek to the start of the APE tag. */ - if (onSeek(pUserData, streamEndOffset, DRMP3_SEEK_END)) { - size_t apeTagSize = (size_t)tagSize + 32; - drmp3_uint8* pTagData = (drmp3_uint8*)drmp3_malloc(apeTagSize, pAllocationCallbacks); - if (pTagData != NULL) { - if (onRead(pUserData, pTagData, apeTagSize) == apeTagSize) { - drmp3__on_meta(pMP3, DRMP3_METADATA_TYPE_APE, pTagData, apeTagSize); - } + if (32 + tagSize < streamLen) { + streamEndOffset -= 32 + tagSize; + streamLen -= 32 + tagSize; + + /* Fire a metadata callback for the APE data. Must include both the main content and footer. */ + if (onMeta != NULL) { + /* We first need to seek to the start of the APE tag. */ + if (onSeek(pUserData, streamEndOffset, DRMP3_SEEK_END)) { + size_t apeTagSize = (size_t)tagSize + 32; + drmp3_uint8* pTagData = (drmp3_uint8*)drmp3_malloc(apeTagSize, pAllocationCallbacks); + if (pTagData != NULL) { + if (onRead(pUserData, pTagData, apeTagSize) == apeTagSize) { + drmp3__on_meta(pMP3, DRMP3_METADATA_TYPE_APE, pTagData, apeTagSize); + } - drmp3_free(pTagData, pAllocationCallbacks); + drmp3_free(pTagData, pAllocationCallbacks); + } } } + } else { + /* The tag size is larger than the stream. Invalid APE tag. */ } } } @@ -3153,7 +3172,6 @@ static drmp3_bool32 drmp3_init_internal(drmp3* pMP3, drmp3_read_proc onRead, drm { drmp3_bs bs; drmp3_L3_gr_info grInfo[4]; - const drmp3_uint8* pTagData = pFirstFrameData; drmp3_bs_init(&bs, pFirstFrameData + DRMP3_HDR_SIZE, firstFrameInfo.frame_bytes - DRMP3_HDR_SIZE); @@ -3164,6 +3182,7 @@ static drmp3_bool32 drmp3_init_internal(drmp3* pMP3, drmp3_read_proc onRead, drm if (drmp3_L3_read_side_info(&bs, grInfo, pFirstFrameData) >= 0) { drmp3_bool32 isXing = DRMP3_FALSE; drmp3_bool32 isInfo = DRMP3_FALSE; + const drmp3_uint8* pTagData; const drmp3_uint8* pTagDataBeg; pTagDataBeg = pFirstFrameData + DRMP3_HDR_SIZE + (bs.pos/8); @@ -3246,6 +3265,13 @@ static drmp3_bool32 drmp3_init_internal(drmp3* pMP3, drmp3_read_proc onRead, drm /* The start offset needs to be moved to the end of this frame so it's not included in any audio processing after seeking. */ pMP3->streamStartOffset += (drmp3_uint32)(firstFrameInfo.frame_bytes); pMP3->streamCursor = pMP3->streamStartOffset; + + /* + The internal decoder needs to be reset to clear out any state. If we don't reset this state, it's possible for + there to be inconsistencies in the number of samples read when reading to the end of the stream depending on + whether or not the caller seeks to the start of the stream. + */ + drmp3dec_init(&pMP3->decoder); } } else { /* Failed to read the side info. */ @@ -3307,8 +3333,6 @@ static drmp3_bool32 drmp3__on_seek_memory(void* pUserData, int byteOffset, drmp3 DRMP3_ASSERT(pMP3 != NULL); - newCursor = pMP3->memory.currentReadPos; - if (origin == DRMP3_SEEK_SET) { newCursor = 0; } else if (origin == DRMP3_SEEK_CUR) { @@ -3981,7 +4005,7 @@ static drmp3_bool32 drmp3__on_tell_stdio(void* pUserData, drmp3_int64* pCursor) DRMP3_ASSERT(pFileStdio != NULL); DRMP3_ASSERT(pCursor != NULL); -#if defined(_WIN32) +#if defined(_WIN32) && !defined(NXDK) #if defined(_MSC_VER) && _MSC_VER > 1200 result = _ftelli64(pFileStdio); #else @@ -4780,6 +4804,8 @@ static float* drmp3__full_read_and_close_f32(drmp3* pMP3, drmp3_config* pConfig, pNewFrames = (float*)drmp3__realloc_from_callbacks(pFrames, (size_t)newFramesBufferSize, (size_t)oldFramesBufferSize, &pMP3->allocationCallbacks); if (pNewFrames == NULL) { drmp3__free_from_callbacks(pFrames, &pMP3->allocationCallbacks); + pFrames = NULL; + totalFramesRead = 0; break; } @@ -4847,6 +4873,8 @@ static drmp3_int16* drmp3__full_read_and_close_s16(drmp3* pMP3, drmp3_config* pC pNewFrames = (drmp3_int16*)drmp3__realloc_from_callbacks(pFrames, (size_t)newFramesBufferSize, (size_t)oldFramesBufferSize, &pMP3->allocationCallbacks); if (pNewFrames == NULL) { drmp3__free_from_callbacks(pFrames, &pMP3->allocationCallbacks); + pFrames = NULL; + totalFramesRead = 0; break; } @@ -4981,7 +5009,24 @@ DIFFERENCES BETWEEN minimp3 AND dr_mp3 /* REVISION HISTORY ================ -v0.7.0 - TBD +v0.7.4 - TBD + - Improvements to SIMD detection. + +v0.7.3 - 2026-01-17 + - Fix an error in drmp3_open_and_read_pcm_frames_s16() and family when memory allocation fails. + - Fix some compilation warnings. + +v0.7.2 - 2025-12-02 + - Reduce stack space to improve robustness on embedded systems. + - Fix a compilation error with MSVC Clang toolset relating to cpuid. + - Fix an error with APE tag parsing. + +v0.7.1 - 2025-09-10 + - Silence a warning with GCC. + - Fix an error with the NXDK build. + - Fix a decoding inconsistency when seeking. Prior to this change, reading to the end of the stream immediately after initializing will result in a different number of samples read than if the stream is seeked to the start and read to the end. + +v0.7.0 - 2025-07-23 - The old `DRMP3_IMPLEMENTATION` has been removed. Use `DR_MP3_IMPLEMENTATION` instead. The reason for this change is that in the future everything will eventually be using the underscored naming convention in the future, so `drmp3` will become `dr_mp3`. - API CHANGE: Seek origins have been renamed to match the naming convention used by dr_wav and my other libraries. - drmp3_seek_origin_start -> DRMP3_SEEK_SET