Update dr_flac.h

This commit is contained in:
Ray
2026-03-29 21:18:28 +02:00
parent a1bf8d9c75
commit 0b87a35e5a

306
src/external/dr_flac.h vendored
View File

@@ -1,6 +1,6 @@
/*
FLAC audio decoder. Choice of public domain or MIT-0. See license statements at the end of this file.
dr_flac - v0.13.0 - TBD
dr_flac - v0.13.3 - 2026-01-17
David Reid - mackron@gmail.com
@@ -126,7 +126,7 @@ extern "C" {
#define DRFLAC_VERSION_MAJOR 0
#define DRFLAC_VERSION_MINOR 13
#define DRFLAC_VERSION_REVISION 0
#define DRFLAC_VERSION_REVISION 3
#define DRFLAC_VERSION_STRING DRFLAC_XSTRINGIFY(DRFLAC_VERSION_MAJOR) "." DRFLAC_XSTRINGIFY(DRFLAC_VERSION_MINOR) "." DRFLAC_XSTRINGIFY(DRFLAC_VERSION_REVISION)
#include <stddef.h> /* For size_t. */
@@ -331,6 +331,12 @@ typedef struct
*/
drflac_uint32 type;
/* The size in bytes of the block and the buffer pointed to by pRawData if it's non-NULL. */
drflac_uint32 rawDataSize;
/* The offset in the stream of the raw data. */
drflac_uint64 rawDataOffset;
/*
A pointer to the raw data. This points to a temporary buffer so don't hold on to it. It's best to
not modify the contents of this buffer. Use the structures below for more meaningful and structured
@@ -338,9 +344,6 @@ typedef struct
*/
const void* pRawData;
/* The size in bytes of the block and the buffer pointed to by pRawData if it's non-NULL. */
drflac_uint32 rawDataSize;
union
{
drflac_streaminfo streaminfo;
@@ -392,6 +395,7 @@ typedef struct
drflac_uint32 colorDepth;
drflac_uint32 indexColorCount;
drflac_uint32 pictureDataSize;
drflac_uint64 pictureDataOffset; /* Offset from the start of the stream. */
const drflac_uint8* pPictureData;
} picture;
} data;
@@ -2712,9 +2716,17 @@ static DRFLAC_INLINE drflac_uint32 drflac__clz_lzcnt(drflac_cache_t x)
#if defined(__GNUC__) || defined(__clang__)
#if defined(DRFLAC_X64)
{
/*
A note on lzcnt.
We check for the presence of the lzcnt instruction at runtime before calling this function, but we still generate this code. I have had
a report where the assembler does not recognize the lzcnt instruction. To work around this we are going to use `rep; bsr` instead which
has an identical byte encoding as lzcnt, and should hopefully improve compatibility with older assemblers.
*/
drflac_uint64 r;
__asm__ __volatile__ (
"lzcnt{ %1, %0| %0, %1}" : "=r"(r) : "r"(x) : "cc"
"rep; bsr{q %1, %0| %0, %1}" : "=r"(r) : "r"(x) : "cc"
/*"lzcnt{ %1, %0| %0, %1}" : "=r"(r) : "r"(x) : "cc"*/
);
return (drflac_uint32)r;
@@ -2723,12 +2735,13 @@ static DRFLAC_INLINE drflac_uint32 drflac__clz_lzcnt(drflac_cache_t x)
{
drflac_uint32 r;
__asm__ __volatile__ (
"lzcnt{l %1, %0| %0, %1}" : "=r"(r) : "r"(x) : "cc"
"rep; bsr{l %1, %0| %0, %1}" : "=r"(r) : "r"(x) : "cc"
/*"lzcnt{l %1, %0| %0, %1}" : "=r"(r) : "r"(x) : "cc"*/
);
return r;
}
#elif defined(DRFLAC_ARM) && (defined(__ARM_ARCH) && __ARM_ARCH >= 5) && !defined(__ARM_ARCH_6M__) && !defined(DRFLAC_64BIT) /* <-- I haven't tested 64-bit inline assembly, so only enabling this for the 32-bit build for now. */
#elif defined(DRFLAC_ARM) && (defined(__ARM_ARCH) && __ARM_ARCH >= 5) && !defined(__ARM_ARCH_6M__) && !(defined(__thumb__) && !defined(__thumb2__)) && !defined(DRFLAC_64BIT) /* <-- I haven't tested 64-bit inline assembly, so only enabling this for the 32-bit build for now. */
{
unsigned int r;
__asm__ __volatile__ (
@@ -6434,8 +6447,9 @@ static drflac_bool32 drflac__read_and_decode_metadata(drflac_read_proc onRead, d
runningFilePos += 4;
metadata.type = blockType;
metadata.pRawData = NULL;
metadata.rawDataSize = 0;
metadata.rawDataOffset = runningFilePos;
metadata.pRawData = NULL;
switch (blockType)
{
@@ -6712,59 +6726,151 @@ static drflac_bool32 drflac__read_and_decode_metadata(drflac_read_proc onRead, d
}
if (onMeta) {
void* pRawData;
const char* pRunningData;
const char* pRunningDataEnd;
drflac_bool32 result = DRFLAC_TRUE;
drflac_uint32 blockSizeRemaining = blockSize;
char* pMime = NULL;
char* pDescription = NULL;
void* pPictureData = NULL;
pRawData = drflac__malloc_from_callbacks(blockSize, pAllocationCallbacks);
if (pRawData == NULL) {
return DRFLAC_FALSE;
if (blockSizeRemaining < 4 || onRead(pUserData, &metadata.data.picture.type, 4) != 4) {
result = DRFLAC_FALSE;
goto done_flac;
}
blockSizeRemaining -= 4;
metadata.data.picture.type = drflac__be2host_32(metadata.data.picture.type);
if (blockSizeRemaining < 4 || onRead(pUserData, &metadata.data.picture.mimeLength, 4) != 4) {
result = DRFLAC_FALSE;
goto done_flac;
}
blockSizeRemaining -= 4;
metadata.data.picture.mimeLength = drflac__be2host_32(metadata.data.picture.mimeLength);
pMime = (char*)drflac__malloc_from_callbacks(metadata.data.picture.mimeLength + 1, pAllocationCallbacks); /* +1 for null terminator. */
if (pMime == NULL) {
result = DRFLAC_FALSE;
goto done_flac;
}
if (onRead(pUserData, pRawData, blockSize) != blockSize) {
drflac__free_from_callbacks(pRawData, pAllocationCallbacks);
return DRFLAC_FALSE;
if (blockSizeRemaining < metadata.data.picture.mimeLength || onRead(pUserData, pMime, metadata.data.picture.mimeLength) != metadata.data.picture.mimeLength) {
result = DRFLAC_FALSE;
goto done_flac;
}
blockSizeRemaining -= metadata.data.picture.mimeLength;
pMime[metadata.data.picture.mimeLength] = '\0'; /* Null terminate for safety. */
metadata.data.picture.mime = (const char*)pMime;
if (blockSizeRemaining < 4 || onRead(pUserData, &metadata.data.picture.descriptionLength, 4) != 4) {
result = DRFLAC_FALSE;
goto done_flac;
}
blockSizeRemaining -= 4;
metadata.data.picture.descriptionLength = drflac__be2host_32(metadata.data.picture.descriptionLength);
pDescription = (char*)drflac__malloc_from_callbacks(metadata.data.picture.descriptionLength + 1, pAllocationCallbacks); /* +1 for null terminator. */
if (pDescription == NULL) {
result = DRFLAC_FALSE;
goto done_flac;
}
metadata.pRawData = pRawData;
metadata.rawDataSize = blockSize;
pRunningData = (const char*)pRawData;
pRunningDataEnd = (const char*)pRawData + blockSize;
metadata.data.picture.type = drflac__be2host_32_ptr_unaligned(pRunningData); pRunningData += 4;
metadata.data.picture.mimeLength = drflac__be2host_32_ptr_unaligned(pRunningData); pRunningData += 4;
/* Need space for the rest of the block */
if ((pRunningDataEnd - pRunningData) - 24 < (drflac_int64)metadata.data.picture.mimeLength) { /* <-- Note the order of operations to avoid overflow to a valid value */
drflac__free_from_callbacks(pRawData, pAllocationCallbacks);
return DRFLAC_FALSE;
if (blockSizeRemaining < metadata.data.picture.descriptionLength || onRead(pUserData, pDescription, metadata.data.picture.descriptionLength) != metadata.data.picture.descriptionLength) {
result = DRFLAC_FALSE;
goto done_flac;
}
metadata.data.picture.mime = pRunningData; pRunningData += metadata.data.picture.mimeLength;
metadata.data.picture.descriptionLength = drflac__be2host_32_ptr_unaligned(pRunningData); pRunningData += 4;
blockSizeRemaining -= metadata.data.picture.descriptionLength;
pDescription[metadata.data.picture.descriptionLength] = '\0'; /* Null terminate for safety. */
metadata.data.picture.description = (const char*)pDescription;
/* Need space for the rest of the block */
if ((pRunningDataEnd - pRunningData) - 20 < (drflac_int64)metadata.data.picture.descriptionLength) { /* <-- Note the order of operations to avoid overflow to a valid value */
drflac__free_from_callbacks(pRawData, pAllocationCallbacks);
return DRFLAC_FALSE;
if (blockSizeRemaining < 4 || onRead(pUserData, &metadata.data.picture.width, 4) != 4) {
result = DRFLAC_FALSE;
goto done_flac;
}
metadata.data.picture.description = pRunningData; pRunningData += metadata.data.picture.descriptionLength;
metadata.data.picture.width = drflac__be2host_32_ptr_unaligned(pRunningData); pRunningData += 4;
metadata.data.picture.height = drflac__be2host_32_ptr_unaligned(pRunningData); pRunningData += 4;
metadata.data.picture.colorDepth = drflac__be2host_32_ptr_unaligned(pRunningData); pRunningData += 4;
metadata.data.picture.indexColorCount = drflac__be2host_32_ptr_unaligned(pRunningData); pRunningData += 4;
metadata.data.picture.pictureDataSize = drflac__be2host_32_ptr_unaligned(pRunningData); pRunningData += 4;
metadata.data.picture.pPictureData = (const drflac_uint8*)pRunningData;
blockSizeRemaining -= 4;
metadata.data.picture.width = drflac__be2host_32(metadata.data.picture.width);
/* Need space for the picture after the block */
if (pRunningDataEnd - pRunningData < (drflac_int64)metadata.data.picture.pictureDataSize) { /* <-- Note the order of operations to avoid overflow to a valid value */
drflac__free_from_callbacks(pRawData, pAllocationCallbacks);
return DRFLAC_FALSE;
if (blockSizeRemaining < 4 || onRead(pUserData, &metadata.data.picture.height, 4) != 4) {
result = DRFLAC_FALSE;
goto done_flac;
}
blockSizeRemaining -= 4;
metadata.data.picture.height = drflac__be2host_32(metadata.data.picture.height);
if (blockSizeRemaining < 4 || onRead(pUserData, &metadata.data.picture.colorDepth, 4) != 4) {
result = DRFLAC_FALSE;
goto done_flac;
}
blockSizeRemaining -= 4;
metadata.data.picture.colorDepth = drflac__be2host_32(metadata.data.picture.colorDepth);
if (blockSizeRemaining < 4 || onRead(pUserData, &metadata.data.picture.indexColorCount, 4) != 4) {
result = DRFLAC_FALSE;
goto done_flac;
}
blockSizeRemaining -= 4;
metadata.data.picture.indexColorCount = drflac__be2host_32(metadata.data.picture.indexColorCount);
/* Picture data. */
if (blockSizeRemaining < 4 || onRead(pUserData, &metadata.data.picture.pictureDataSize, 4) != 4) {
result = DRFLAC_FALSE;
goto done_flac;
}
blockSizeRemaining -= 4;
metadata.data.picture.pictureDataSize = drflac__be2host_32(metadata.data.picture.pictureDataSize);
if (blockSizeRemaining < metadata.data.picture.pictureDataSize) {
result = DRFLAC_FALSE;
goto done_flac;
}
onMeta(pUserDataMD, &metadata);
/* For the actual image data we want to store the offset to the start of the stream. */
metadata.data.picture.pictureDataOffset = runningFilePos + (blockSize - blockSizeRemaining);
drflac__free_from_callbacks(pRawData, pAllocationCallbacks);
/*
For the allocation of image data, we can allow memory allocation to fail, in which case we just leave
the pointer as null. If it fails, we need to fall back to seeking past the image data.
*/
#ifndef DR_FLAC_NO_PICTURE_METADATA_MALLOC
pPictureData = drflac__malloc_from_callbacks(metadata.data.picture.pictureDataSize, pAllocationCallbacks);
if (pPictureData != NULL) {
if (onRead(pUserData, pPictureData, metadata.data.picture.pictureDataSize) != metadata.data.picture.pictureDataSize) {
result = DRFLAC_FALSE;
goto done_flac;
}
} else
#endif
{
/* Allocation failed. We need to seek past the picture data. */
if (!onSeek(pUserData, metadata.data.picture.pictureDataSize, DRFLAC_SEEK_CUR)) {
result = DRFLAC_FALSE;
goto done_flac;
}
}
blockSizeRemaining -= metadata.data.picture.pictureDataSize;
(void)blockSizeRemaining;
metadata.data.picture.pPictureData = (const drflac_uint8*)pPictureData;
/* Only fire the callback if we actually have a way to read the image data. We must have either a valid offset, or a valid data pointer. */
if (metadata.data.picture.pictureDataOffset != 0 || metadata.data.picture.pPictureData != NULL) {
onMeta(pUserDataMD, &metadata);
} else {
/* Don't have a valid offset or data pointer, so just pretend we don't have a picture metadata. */
}
done_flac:
drflac__free_from_callbacks(pMime, pAllocationCallbacks);
drflac__free_from_callbacks(pDescription, pAllocationCallbacks);
drflac__free_from_callbacks(pPictureData, pAllocationCallbacks);
if (result != DRFLAC_TRUE) {
return DRFLAC_FALSE;
}
}
} break;
@@ -6800,13 +6906,16 @@ static drflac_bool32 drflac__read_and_decode_metadata(drflac_read_proc onRead, d
*/
if (onMeta) {
void* pRawData = drflac__malloc_from_callbacks(blockSize, pAllocationCallbacks);
if (pRawData == NULL) {
return DRFLAC_FALSE;
}
if (onRead(pUserData, pRawData, blockSize) != blockSize) {
drflac__free_from_callbacks(pRawData, pAllocationCallbacks);
return DRFLAC_FALSE;
if (pRawData != NULL) {
if (onRead(pUserData, pRawData, blockSize) != blockSize) {
drflac__free_from_callbacks(pRawData, pAllocationCallbacks);
return DRFLAC_FALSE;
}
} else {
/* Allocation failed. We need to seek past the block. */
if (!onSeek(pUserData, blockSize, DRFLAC_SEEK_CUR)) {
return DRFLAC_FALSE;
}
}
metadata.pRawData = pRawData;
@@ -8699,7 +8808,7 @@ static drflac_bool32 drflac__on_tell_stdio(void* pUserData, drflac_int64* pCurso
DRFLAC_ASSERT(pFileStdio != NULL);
DRFLAC_ASSERT(pCursor != NULL);
#if defined(_WIN32)
#if defined(_WIN32) && !defined(NXDK)
#if defined(_MSC_VER) && _MSC_VER > 1200
result = _ftelli64(pFileStdio);
#else
@@ -8821,8 +8930,6 @@ static drflac_bool32 drflac__on_seek_memory(void* pUserData, int offset, drflac_
DRFLAC_ASSERT(memoryStream != NULL);
newCursor = memoryStream->currentReadPos;
if (origin == DRFLAC_SEEK_SET) {
newCursor = 0;
} else if (origin == DRFLAC_SEEK_CUR) {
@@ -11702,58 +11809,43 @@ static type* drflac__full_read_and_close_ ## extension (drflac* pFlac, unsigned
{ \
type* pSampleData = NULL; \
drflac_uint64 totalPCMFrameCount; \
type buffer[4096]; \
drflac_uint64 pcmFramesRead; \
size_t sampleDataBufferSize = sizeof(buffer); \
\
DRFLAC_ASSERT(pFlac != NULL); \
\
totalPCMFrameCount = pFlac->totalPCMFrameCount; \
totalPCMFrameCount = 0; \
\
if (totalPCMFrameCount == 0) { \
type buffer[4096]; \
drflac_uint64 pcmFramesRead; \
size_t sampleDataBufferSize = sizeof(buffer); \
pSampleData = (type*)drflac__malloc_from_callbacks(sampleDataBufferSize, &pFlac->allocationCallbacks); \
if (pSampleData == NULL) { \
goto on_error; \
} \
\
pSampleData = (type*)drflac__malloc_from_callbacks(sampleDataBufferSize, &pFlac->allocationCallbacks); \
if (pSampleData == NULL) { \
goto on_error; \
} \
while ((pcmFramesRead = (drflac_uint64)drflac_read_pcm_frames_##extension(pFlac, sizeof(buffer)/sizeof(buffer[0])/pFlac->channels, buffer)) > 0) { \
if (((totalPCMFrameCount + pcmFramesRead) * pFlac->channels * sizeof(type)) > sampleDataBufferSize) { \
type* pNewSampleData; \
size_t newSampleDataBufferSize; \
\
while ((pcmFramesRead = (drflac_uint64)drflac_read_pcm_frames_##extension(pFlac, sizeof(buffer)/sizeof(buffer[0])/pFlac->channels, buffer)) > 0) { \
if (((totalPCMFrameCount + pcmFramesRead) * pFlac->channels * sizeof(type)) > sampleDataBufferSize) { \
type* pNewSampleData; \
size_t newSampleDataBufferSize; \
\
newSampleDataBufferSize = sampleDataBufferSize * 2; \
pNewSampleData = (type*)drflac__realloc_from_callbacks(pSampleData, newSampleDataBufferSize, sampleDataBufferSize, &pFlac->allocationCallbacks); \
if (pNewSampleData == NULL) { \
drflac__free_from_callbacks(pSampleData, &pFlac->allocationCallbacks); \
goto on_error; \
} \
\
sampleDataBufferSize = newSampleDataBufferSize; \
pSampleData = pNewSampleData; \
newSampleDataBufferSize = sampleDataBufferSize * 2; \
pNewSampleData = (type*)drflac__realloc_from_callbacks(pSampleData, newSampleDataBufferSize, sampleDataBufferSize, &pFlac->allocationCallbacks); \
if (pNewSampleData == NULL) { \
drflac__free_from_callbacks(pSampleData, &pFlac->allocationCallbacks); \
goto on_error; \
} \
\
DRFLAC_COPY_MEMORY(pSampleData + (totalPCMFrameCount*pFlac->channels), buffer, (size_t)(pcmFramesRead*pFlac->channels*sizeof(type))); \
totalPCMFrameCount += pcmFramesRead; \
sampleDataBufferSize = newSampleDataBufferSize; \
pSampleData = pNewSampleData; \
} \
\
/* At this point everything should be decoded, but we just want to fill the unused part buffer with silence - need to \
protect those ears from random noise! */ \
DRFLAC_ZERO_MEMORY(pSampleData + (totalPCMFrameCount*pFlac->channels), (size_t)(sampleDataBufferSize - totalPCMFrameCount*pFlac->channels*sizeof(type))); \
} else { \
drflac_uint64 dataSize = totalPCMFrameCount*pFlac->channels*sizeof(type); \
if (dataSize > (drflac_uint64)DRFLAC_SIZE_MAX) { \
goto on_error; /* The decoded data is too big. */ \
} \
\
pSampleData = (type*)drflac__malloc_from_callbacks((size_t)dataSize, &pFlac->allocationCallbacks); /* <-- Safe cast as per the check above. */ \
if (pSampleData == NULL) { \
goto on_error; \
} \
\
totalPCMFrameCount = drflac_read_pcm_frames_##extension(pFlac, pFlac->totalPCMFrameCount, pSampleData); \
DRFLAC_COPY_MEMORY(pSampleData + (totalPCMFrameCount*pFlac->channels), buffer, (size_t)(pcmFramesRead*pFlac->channels*sizeof(type))); \
totalPCMFrameCount += pcmFramesRead; \
} \
\
/* At this point everything should be decoded, but we just want to fill the unused part buffer with silence - need to \
protect those ears from random noise! */ \
DRFLAC_ZERO_MEMORY(pSampleData + (totalPCMFrameCount*pFlac->channels), (size_t)(sampleDataBufferSize - totalPCMFrameCount*pFlac->channels*sizeof(type))); \
\
if (sampleRateOut) *sampleRateOut = pFlac->sampleRate; \
if (channelsOut) *channelsOut = pFlac->channels; \
if (totalPCMFrameCountOut) *totalPCMFrameCountOut = totalPCMFrameCount; \
@@ -12077,7 +12169,19 @@ DRFLAC_API drflac_bool32 drflac_next_cuesheet_track(drflac_cuesheet_track_iterat
/*
REVISION HISTORY
================
v0.13.0 - TBD
v0.13.3 - 2026-01-17
- Fix a compiler compatibility issue with some inlined assembly.
- Fix a compilation warning.
v0.13.2 - 2025-12-02
- Improve robustness of the parsing of picture metadata to improve support for memory constrained embedded devices.
- Fix a warning about an assigned by unused variable.
- Improvements to drflac_open_and_read_pcm_frames_*() and family to avoid excessively large memory allocations from malformed files.
v0.13.1 - 2025-09-10
- Fix an error with the NXDK build.
v0.13.0 - 2025-07-23
- API CHANGE: Seek origin enums have been renamed to match the naming convention used by other dr_libs libraries:
- drflac_seek_origin_start -> DRFLAC_SEEK_SET
- drflac_seek_origin_current -> DRFLAC_SEEK_CUR