| @@ -1,10 +1,6 @@ | |||||
| /* | /* | ||||
| WAV audio loader and writer. Choice of public domain or MIT-0. See license statements at the end of this file. | WAV audio loader and writer. Choice of public domain or MIT-0. See license statements at the end of this file. | ||||
| <<<<<<< HEAD | |||||
| dr_wav - v0.13.17 - TBD | |||||
| ======= | |||||
| dr_wav - v0.13.16 - 2024-02-27 | |||||
| >>>>>>> e80c373575b337720703a41691536272b21b554c | |||||
| dr_wav - v0.14.0 - TBD | |||||
| David Reid - mackron@gmail.com | David Reid - mackron@gmail.com | ||||
| @@ -150,12 +146,8 @@ extern "C" { | |||||
| #define DRWAV_XSTRINGIFY(x) DRWAV_STRINGIFY(x) | #define DRWAV_XSTRINGIFY(x) DRWAV_STRINGIFY(x) | ||||
| #define DRWAV_VERSION_MAJOR 0 | #define DRWAV_VERSION_MAJOR 0 | ||||
| #define DRWAV_VERSION_MINOR 13 | |||||
| <<<<<<< HEAD | |||||
| #define DRWAV_VERSION_REVISION 17 | |||||
| ======= | |||||
| #define DRWAV_VERSION_REVISION 16 | |||||
| >>>>>>> e80c373575b337720703a41691536272b21b554c | |||||
| #define DRWAV_VERSION_MINOR 14 | |||||
| #define DRWAV_VERSION_REVISION 0 | |||||
| #define DRWAV_VERSION_STRING DRWAV_XSTRINGIFY(DRWAV_VERSION_MAJOR) "." DRWAV_XSTRINGIFY(DRWAV_VERSION_MINOR) "." DRWAV_XSTRINGIFY(DRWAV_VERSION_REVISION) | #define DRWAV_VERSION_STRING DRWAV_XSTRINGIFY(DRWAV_VERSION_MAJOR) "." DRWAV_XSTRINGIFY(DRWAV_VERSION_MINOR) "." DRWAV_XSTRINGIFY(DRWAV_VERSION_REVISION) | ||||
| #include <stddef.h> /* For size_t. */ | #include <stddef.h> /* For size_t. */ | ||||
| @@ -184,7 +176,7 @@ typedef unsigned int drwav_uint32; | |||||
| #pragma GCC diagnostic pop | #pragma GCC diagnostic pop | ||||
| #endif | #endif | ||||
| #endif | #endif | ||||
| #if defined(__LP64__) || defined(_WIN64) || (defined(__x86_64__) && !defined(__ILP32__)) || defined(_M_X64) || defined(__ia64) || defined (_M_IA64) || defined(__aarch64__) || defined(_M_ARM64) || defined(__powerpc64__) | |||||
| #if defined(__LP64__) || defined(_WIN64) || (defined(__x86_64__) && !defined(__ILP32__)) || defined(_M_X64) || defined(__ia64) || defined (_M_IA64) || defined(__aarch64__) || defined(_M_ARM64) || defined(_M_ARM64EC) || defined(__powerpc64__) | |||||
| typedef drwav_uint64 drwav_uintptr; | typedef drwav_uint64 drwav_uintptr; | ||||
| #else | #else | ||||
| typedef drwav_uint32 drwav_uintptr; | typedef drwav_uint32 drwav_uintptr; | ||||
| @@ -563,11 +555,11 @@ typedef struct | |||||
| /* See drwav_smpl_loop_type. */ | /* See drwav_smpl_loop_type. */ | ||||
| drwav_uint32 type; | drwav_uint32 type; | ||||
| /* The byte offset of the first sample to be played in the loop. */ | |||||
| drwav_uint32 firstSampleByteOffset; | |||||
| /* The offset of the first sample to be played in the loop. */ | |||||
| drwav_uint32 firstSampleOffset; | |||||
| /* The byte offset into the audio data of the last sample to be played in the loop. */ | |||||
| drwav_uint32 lastSampleByteOffset; | |||||
| /* The offset into the audio data of the last sample to be played in the loop. */ | |||||
| drwav_uint32 lastSampleOffset; | |||||
| /* A value to represent that playback should occur at a point between samples. This value ranges from 0 to UINT32_MAX. Where a value of 0 means no fraction, and a value of (UINT32_MAX / 2) would mean half a sample. */ | /* A value to represent that playback should occur at a point between samples. This value ranges from 0 to UINT32_MAX. Where a value of 0 means no fraction, and a value of (UINT32_MAX / 2) would mean half a sample. */ | ||||
| drwav_uint32 sampleFraction; | drwav_uint32 sampleFraction; | ||||
| @@ -645,8 +637,8 @@ typedef struct | |||||
| /* Set to 0 for uncompressed formats. Else the last byte in compressed wave data where decompression can begin to find the value of the corresponding sample value. */ | /* Set to 0 for uncompressed formats. Else the last byte in compressed wave data where decompression can begin to find the value of the corresponding sample value. */ | ||||
| drwav_uint32 blockStart; | drwav_uint32 blockStart; | ||||
| /* For uncompressed formats this is the byte offset of the cue point into the audio data. For compressed formats this is relative to the block specified with blockStart. */ | |||||
| drwav_uint32 sampleByteOffset; | |||||
| /* For uncompressed formats this is the offset of the cue point into the audio data. For compressed formats this is relative to the block specified with blockStart. */ | |||||
| drwav_uint32 sampleOffset; | |||||
| } drwav_cue_point; | } drwav_cue_point; | ||||
| typedef struct | typedef struct | ||||
| @@ -1330,8 +1322,6 @@ DRWAV_API drwav_bool32 drwav_fourcc_equal(const drwav_uint8* a, const char* b); | |||||
| #endif | #endif | ||||
| #endif /* dr_wav_h */ | #endif /* dr_wav_h */ | ||||
| <<<<<<< HEAD | |||||
| ======= | |||||
| /************************************************************************************************************************************************************ | /************************************************************************************************************************************************************ | ||||
| ************************************************************************************************************************************************************ | ************************************************************************************************************************************************************ | ||||
| @@ -1394,7 +1384,7 @@ DRWAV_API drwav_bool32 drwav_fourcc_equal(const drwav_uint8* a, const char* b); | |||||
| #define DRWAV_MAX_SIMD_VECTOR_SIZE 32 | #define DRWAV_MAX_SIMD_VECTOR_SIZE 32 | ||||
| /* Architecture Detection */ | /* Architecture Detection */ | ||||
| #if defined(__x86_64__) || defined(_M_X64) | |||||
| #if defined(__x86_64__) || (defined(_M_X64) && !defined(_M_ARM64EC)) | |||||
| #define DRWAV_X64 | #define DRWAV_X64 | ||||
| #elif defined(__i386) || defined(_M_IX86) | #elif defined(__i386) || defined(_M_IX86) | ||||
| #define DRWAV_X86 | #define DRWAV_X86 | ||||
| @@ -2106,7 +2096,7 @@ DRWAV_PRIVATE drwav_uint8* drwav__metadata_get_memory(drwav__metadata_parser* pP | |||||
| pParser->pDataCursor += align - modulo; | pParser->pDataCursor += align - modulo; | ||||
| } | } | ||||
| } | } | ||||
| pResult = pParser->pDataCursor; | pResult = pParser->pDataCursor; | ||||
| /* | /* | ||||
| @@ -2199,12 +2189,12 @@ DRWAV_PRIVATE drwav_uint64 drwav__read_smpl_to_metadata_obj(drwav__metadata_pars | |||||
| bytesJustRead = drwav__metadata_parser_read(pParser, smplLoopData, sizeof(smplLoopData), &totalBytesRead); | bytesJustRead = drwav__metadata_parser_read(pParser, smplLoopData, sizeof(smplLoopData), &totalBytesRead); | ||||
| if (bytesJustRead == sizeof(smplLoopData)) { | if (bytesJustRead == sizeof(smplLoopData)) { | ||||
| pMetadata->data.smpl.pLoops[iSampleLoop].cuePointId = drwav_bytes_to_u32(smplLoopData + 0); | |||||
| pMetadata->data.smpl.pLoops[iSampleLoop].type = drwav_bytes_to_u32(smplLoopData + 4); | |||||
| pMetadata->data.smpl.pLoops[iSampleLoop].firstSampleByteOffset = drwav_bytes_to_u32(smplLoopData + 8); | |||||
| pMetadata->data.smpl.pLoops[iSampleLoop].lastSampleByteOffset = drwav_bytes_to_u32(smplLoopData + 12); | |||||
| pMetadata->data.smpl.pLoops[iSampleLoop].sampleFraction = drwav_bytes_to_u32(smplLoopData + 16); | |||||
| pMetadata->data.smpl.pLoops[iSampleLoop].playCount = drwav_bytes_to_u32(smplLoopData + 20); | |||||
| pMetadata->data.smpl.pLoops[iSampleLoop].cuePointId = drwav_bytes_to_u32(smplLoopData + 0); | |||||
| pMetadata->data.smpl.pLoops[iSampleLoop].type = drwav_bytes_to_u32(smplLoopData + 4); | |||||
| pMetadata->data.smpl.pLoops[iSampleLoop].firstSampleOffset = drwav_bytes_to_u32(smplLoopData + 8); | |||||
| pMetadata->data.smpl.pLoops[iSampleLoop].lastSampleOffset = drwav_bytes_to_u32(smplLoopData + 12); | |||||
| pMetadata->data.smpl.pLoops[iSampleLoop].sampleFraction = drwav_bytes_to_u32(smplLoopData + 16); | |||||
| pMetadata->data.smpl.pLoops[iSampleLoop].playCount = drwav_bytes_to_u32(smplLoopData + 20); | |||||
| } else { | } else { | ||||
| break; | break; | ||||
| } | } | ||||
| @@ -2264,7 +2254,7 @@ DRWAV_PRIVATE drwav_uint64 drwav__read_cue_to_metadata_obj(drwav__metadata_parse | |||||
| pMetadata->data.cue.pCuePoints[iCuePoint].dataChunkId[3] = cuePointData[11]; | pMetadata->data.cue.pCuePoints[iCuePoint].dataChunkId[3] = cuePointData[11]; | ||||
| pMetadata->data.cue.pCuePoints[iCuePoint].chunkStart = drwav_bytes_to_u32(cuePointData + 12); | pMetadata->data.cue.pCuePoints[iCuePoint].chunkStart = drwav_bytes_to_u32(cuePointData + 12); | ||||
| pMetadata->data.cue.pCuePoints[iCuePoint].blockStart = drwav_bytes_to_u32(cuePointData + 16); | pMetadata->data.cue.pCuePoints[iCuePoint].blockStart = drwav_bytes_to_u32(cuePointData + 16); | ||||
| pMetadata->data.cue.pCuePoints[iCuePoint].sampleByteOffset = drwav_bytes_to_u32(cuePointData + 20); | |||||
| pMetadata->data.cue.pCuePoints[iCuePoint].sampleOffset = drwav_bytes_to_u32(cuePointData + 20); | |||||
| } else { | } else { | ||||
| break; | break; | ||||
| } | } | ||||
| @@ -2417,7 +2407,7 @@ DRWAV_PRIVATE drwav_result drwav_buffer_reader_read(drwav_buffer_reader* pReader | |||||
| size_t bytesRemaining; | size_t bytesRemaining; | ||||
| DRWAV_ASSERT(pReader != NULL); | DRWAV_ASSERT(pReader != NULL); | ||||
| if (pBytesRead != NULL) { | if (pBytesRead != NULL) { | ||||
| *pBytesRead = 0; | *pBytesRead = 0; | ||||
| } | } | ||||
| @@ -2497,7 +2487,7 @@ DRWAV_PRIVATE drwav_uint64 drwav__read_bext_to_metadata_obj(drwav__metadata_pars | |||||
| size_t bytesRead = drwav__metadata_parser_read(pParser, bextData, sizeof(bextData), NULL); | size_t bytesRead = drwav__metadata_parser_read(pParser, bextData, sizeof(bextData), NULL); | ||||
| DRWAV_ASSERT(pParser->stage == drwav__metadata_parser_stage_read); | DRWAV_ASSERT(pParser->stage == drwav__metadata_parser_stage_read); | ||||
| if (bytesRead == sizeof(bextData)) { | if (bytesRead == sizeof(bextData)) { | ||||
| drwav_buffer_reader reader; | drwav_buffer_reader reader; | ||||
| drwav_uint32 timeReferenceLow; | drwav_uint32 timeReferenceLow; | ||||
| @@ -2559,7 +2549,7 @@ DRWAV_PRIVATE drwav_uint64 drwav__read_list_label_or_note_to_metadata_obj(drwav_ | |||||
| drwav_uint64 totalBytesRead = 0; | drwav_uint64 totalBytesRead = 0; | ||||
| size_t bytesJustRead = drwav__metadata_parser_read(pParser, cueIDBuffer, sizeof(cueIDBuffer), &totalBytesRead); | size_t bytesJustRead = drwav__metadata_parser_read(pParser, cueIDBuffer, sizeof(cueIDBuffer), &totalBytesRead); | ||||
| DRWAV_ASSERT(pParser->stage == drwav__metadata_parser_stage_read); | |||||
| DRWAV_ASSERT(pParser->stage == drwav__metadata_parser_stage_read); | |||||
| if (bytesJustRead == sizeof(cueIDBuffer)) { | if (bytesJustRead == sizeof(cueIDBuffer)) { | ||||
| drwav_uint32 sizeIncludingNullTerminator; | drwav_uint32 sizeIncludingNullTerminator; | ||||
| @@ -2731,7 +2721,7 @@ DRWAV_PRIVATE drwav_uint64 drwav__metadata_process_chunk(drwav__metadata_parser* | |||||
| } | } | ||||
| } else { | } else { | ||||
| /* Loop count in header does not match the size of the chunk. */ | /* Loop count in header does not match the size of the chunk. */ | ||||
| } | |||||
| } | |||||
| } | } | ||||
| } else { | } else { | ||||
| bytesRead = drwav__read_smpl_to_metadata_obj(pParser, pChunkHeader, &pParser->pMetadata[pParser->metadataCursor]); | bytesRead = drwav__read_smpl_to_metadata_obj(pParser, pChunkHeader, &pParser->pMetadata[pParser->metadataCursor]); | ||||
| @@ -3352,7 +3342,7 @@ DRWAV_PRIVATE drwav_bool32 drwav_init__internal(drwav* pWav, drwav_chunk_proc on | |||||
| if (((pWav->container == drwav_container_riff || pWav->container == drwav_container_rifx || pWav->container == drwav_container_rf64) && drwav_fourcc_equal(header.id.fourcc, "data")) || | if (((pWav->container == drwav_container_riff || pWav->container == drwav_container_rifx || pWav->container == drwav_container_rf64) && drwav_fourcc_equal(header.id.fourcc, "data")) || | ||||
| ((pWav->container == drwav_container_w64) && drwav_guid_equal(header.id.guid, drwavGUID_W64_DATA))) { | ((pWav->container == drwav_container_w64) && drwav_guid_equal(header.id.guid, drwavGUID_W64_DATA))) { | ||||
| foundChunk_data = DRWAV_TRUE; | foundChunk_data = DRWAV_TRUE; | ||||
| pWav->dataChunkDataPos = cursor; | pWav->dataChunkDataPos = cursor; | ||||
| if (pWav->container != drwav_container_rf64) { /* The data chunk size for RF64 will always be set to 0xFFFFFFFF here. It was set to it's true value earlier. */ | if (pWav->container != drwav_container_rf64) { /* The data chunk size for RF64 will always be set to 0xFFFFFFFF here. It was set to it's true value earlier. */ | ||||
| @@ -3442,7 +3432,7 @@ DRWAV_PRIVATE drwav_bool32 drwav_init__internal(drwav* pWav, drwav_chunk_proc on | |||||
| return DRWAV_FALSE; | return DRWAV_FALSE; | ||||
| } | } | ||||
| channels = drwav_bytes_to_u16_ex (commData + 0, pWav->container); | channels = drwav_bytes_to_u16_ex (commData + 0, pWav->container); | ||||
| frameCount = drwav_bytes_to_u32_ex (commData + 2, pWav->container); | frameCount = drwav_bytes_to_u32_ex (commData + 2, pWav->container); | ||||
| sampleSizeInBits = drwav_bytes_to_u16_ex (commData + 6, pWav->container); | sampleSizeInBits = drwav_bytes_to_u16_ex (commData + 6, pWav->container); | ||||
| @@ -3475,12 +3465,15 @@ DRWAV_PRIVATE drwav_bool32 drwav_init__internal(drwav* pWav, drwav_chunk_proc on | |||||
| compressionFormat = DR_WAVE_FORMAT_MULAW; | compressionFormat = DR_WAVE_FORMAT_MULAW; | ||||
| } else if (drwav_fourcc_equal(type, "ima4")) { | } else if (drwav_fourcc_equal(type, "ima4")) { | ||||
| compressionFormat = DR_WAVE_FORMAT_DVI_ADPCM; | compressionFormat = DR_WAVE_FORMAT_DVI_ADPCM; | ||||
| sampleSizeInBits = 4; | |||||
| sampleSizeInBits = 4; | |||||
| /* | /* | ||||
| I haven't been able to figure out how to get correct decoding for IMA ADPCM. Until this is figured out | I haven't been able to figure out how to get correct decoding for IMA ADPCM. Until this is figured out | ||||
| we'll need to abort when we encounter such an encoding. Advice welcome! | we'll need to abort when we encounter such an encoding. Advice welcome! | ||||
| */ | */ | ||||
| (void)compressionFormat; | |||||
| (void)sampleSizeInBits; | |||||
| return DRWAV_FALSE; | return DRWAV_FALSE; | ||||
| } else { | } else { | ||||
| return DRWAV_FALSE; /* Unknown or unsupported compression format. Need to abort. */ | return DRWAV_FALSE; /* Unknown or unsupported compression format. Need to abort. */ | ||||
| @@ -3517,7 +3510,7 @@ DRWAV_PRIVATE drwav_bool32 drwav_init__internal(drwav* pWav, drwav_chunk_proc on | |||||
| /* In AIFF, samples are padded to 8 byte boundaries. We need to round up our bits per sample here. */ | /* In AIFF, samples are padded to 8 byte boundaries. We need to round up our bits per sample here. */ | ||||
| fmt.bitsPerSample += (fmt.bitsPerSample & 7); | fmt.bitsPerSample += (fmt.bitsPerSample & 7); | ||||
| /* If the form type is AIFC there will be some additional data in the chunk. We need to seek past it. */ | /* If the form type is AIFC there will be some additional data in the chunk. We need to seek past it. */ | ||||
| if (isAIFCFormType) { | if (isAIFCFormType) { | ||||
| @@ -3543,20 +3536,46 @@ DRWAV_PRIVATE drwav_bool32 drwav_init__internal(drwav* pWav, drwav_chunk_proc on | |||||
| return DRWAV_FALSE; | return DRWAV_FALSE; | ||||
| } | } | ||||
| /* We need to seek forward by the offset. */ | |||||
| /* The position of the audio data starts at an offset. */ | |||||
| offset = drwav_bytes_to_u32_ex(offsetAndBlockSizeData + 0, pWav->container); | offset = drwav_bytes_to_u32_ex(offsetAndBlockSizeData + 0, pWav->container); | ||||
| if (drwav__seek_forward(pWav->onSeek, offset, pWav->pUserData) == DRWAV_FALSE) { | |||||
| return DRWAV_FALSE; | |||||
| } | |||||
| cursor += offset; | |||||
| pWav->dataChunkDataPos = cursor + offset; | |||||
| pWav->dataChunkDataPos = cursor; | |||||
| /* The data chunk size needs to be reduced by the offset or else seeking will break. */ | |||||
| dataChunkSize = chunkSize; | dataChunkSize = chunkSize; | ||||
| if (dataChunkSize > offset) { | |||||
| dataChunkSize -= offset; | |||||
| } else { | |||||
| dataChunkSize = 0; | |||||
| } | |||||
| /* If we're running in sequential mode, or we're not reading metadata, we have enough now that we can get out of the loop. */ | |||||
| if (sequential || !isProcessingMetadata) { | |||||
| break; /* No need to keep reading beyond the data chunk. */ | |||||
| if (sequential) { | |||||
| if (foundChunk_fmt) { /* <-- Name is misleading, but will be set to true if the COMM chunk has been parsed. */ | |||||
| /* | |||||
| Getting here means we're opening in sequential mode and we've found the SSND (data) and COMM (fmt) chunks. We need | |||||
| to get out of the loop here or else we'll end up going past the data chunk and will have no way of getting back to | |||||
| it since we're not allowed to seek backwards. | |||||
| One subtle detail here is that there is an offset with the SSND chunk. We need to make sure we seek past this offset | |||||
| so we're left sitting on the first byte of actual audio data. | |||||
| */ | |||||
| if (drwav__seek_forward(pWav->onSeek, offset, pWav->pUserData) == DRWAV_FALSE) { | |||||
| return DRWAV_FALSE; | |||||
| } | |||||
| cursor += offset; | |||||
| break; | |||||
| } else { | |||||
| /* | |||||
| Getting here means the COMM chunk was not found. In sequential mode, if we haven't yet found the COMM chunk | |||||
| we'll need to abort because we can't be doing a backwards seek back to the SSND chunk in order to read the | |||||
| data. For this reason, this configuration of AIFF files are not supported with sequential mode. | |||||
| */ | |||||
| return DRWAV_FALSE; | |||||
| } | |||||
| } else { | } else { | ||||
| chunkSize += header.paddingSize; /* <-- Make sure we seek past the padding. */ | |||||
| chunkSize -= sizeof(offsetAndBlockSizeData); /* <-- This was read earlier. */ | |||||
| if (drwav__seek_forward(pWav->onSeek, chunkSize, pWav->pUserData) == DRWAV_FALSE) { | if (drwav__seek_forward(pWav->onSeek, chunkSize, pWav->pUserData) == DRWAV_FALSE) { | ||||
| break; | break; | ||||
| } | } | ||||
| @@ -3567,7 +3586,6 @@ DRWAV_PRIVATE drwav_bool32 drwav_init__internal(drwav* pWav, drwav_chunk_proc on | |||||
| } | } | ||||
| /* Getting here means it's not a chunk that we care about internally, but might need to be handled as metadata by the caller. */ | /* Getting here means it's not a chunk that we care about internally, but might need to be handled as metadata by the caller. */ | ||||
| if (isProcessingMetadata) { | if (isProcessingMetadata) { | ||||
| drwav__metadata_process_chunk(&metadataParser, &header, drwav_metadata_type_all_including_unknown); | drwav__metadata_process_chunk(&metadataParser, &header, drwav_metadata_type_all_including_unknown); | ||||
| @@ -3659,6 +3677,10 @@ DRWAV_PRIVATE drwav_bool32 drwav_init__internal(drwav* pWav, drwav_chunk_proc on | |||||
| /* At this point we should be sitting on the first byte of the raw audio data. */ | /* At this point we should be sitting on the first byte of the raw audio data. */ | ||||
| if (drwav__seek_from_start(pWav->onSeek, pWav->dataChunkDataPos, pWav->pUserData) == DRWAV_FALSE) { | |||||
| drwav_free(pWav->pMetadata, &pWav->allocationCallbacks); | |||||
| return DRWAV_FALSE; | |||||
| } | |||||
| /* | /* | ||||
| I've seen a WAV file in the wild where a RIFF-ecapsulated file has the size of it's "RIFF" and | I've seen a WAV file in the wild where a RIFF-ecapsulated file has the size of it's "RIFF" and | ||||
| @@ -3680,18 +3702,30 @@ DRWAV_PRIVATE drwav_bool32 drwav_init__internal(drwav* pWav, drwav_chunk_proc on | |||||
| } | } | ||||
| } | } | ||||
| if (drwav__seek_from_start(pWav->onSeek, pWav->dataChunkDataPos, pWav->pUserData) == DRWAV_FALSE) { | |||||
| drwav_free(pWav->pMetadata, &pWav->allocationCallbacks); | |||||
| return DRWAV_FALSE; | |||||
| } | |||||
| pWav->fmt = fmt; | pWav->fmt = fmt; | ||||
| pWav->sampleRate = fmt.sampleRate; | pWav->sampleRate = fmt.sampleRate; | ||||
| pWav->channels = fmt.channels; | pWav->channels = fmt.channels; | ||||
| pWav->bitsPerSample = fmt.bitsPerSample; | pWav->bitsPerSample = fmt.bitsPerSample; | ||||
| pWav->bytesRemaining = dataChunkSize; | |||||
| pWav->translatedFormatTag = translatedFormatTag; | pWav->translatedFormatTag = translatedFormatTag; | ||||
| /* | |||||
| I've had a report where files would start glitching after seeking. The reason for this is the data | |||||
| chunk is not a clean multiple of the PCM frame size in bytes. Where this becomes a problem is when | |||||
| seeking, because the number of bytes remaining in the data chunk is used to calculate the current | |||||
| byte position. If this byte position is not aligned to the number of bytes in a PCM frame, it will | |||||
| result in the seek not being cleanly positioned at the start of the PCM frame thereby resulting in | |||||
| all decoded frames after that being corrupted. | |||||
| To address this, we need to round the data chunk size down to the nearest multiple of the frame size. | |||||
| */ | |||||
| if (!drwav__is_compressed_format_tag(translatedFormatTag)) { | |||||
| drwav_uint32 bytesPerFrame = drwav_get_bytes_per_pcm_frame(pWav); | |||||
| if (bytesPerFrame > 0) { | |||||
| dataChunkSize -= (dataChunkSize % bytesPerFrame); | |||||
| } | |||||
| } | |||||
| pWav->bytesRemaining = dataChunkSize; | |||||
| pWav->dataChunkDataSize = dataChunkSize; | pWav->dataChunkDataSize = dataChunkSize; | ||||
| if (sampleCountFromFactChunk != 0) { | if (sampleCountFromFactChunk != 0) { | ||||
| @@ -4005,8 +4039,8 @@ DRWAV_PRIVATE size_t drwav__write_or_count_metadata(drwav* pWav, drwav_metadata* | |||||
| for (iLoop = 0; iLoop < pMetadata->data.smpl.sampleLoopCount; ++iLoop) { | for (iLoop = 0; iLoop < pMetadata->data.smpl.sampleLoopCount; ++iLoop) { | ||||
| bytesWritten += drwav__write_or_count_u32ne_to_le(pWav, pMetadata->data.smpl.pLoops[iLoop].cuePointId); | bytesWritten += drwav__write_or_count_u32ne_to_le(pWav, pMetadata->data.smpl.pLoops[iLoop].cuePointId); | ||||
| bytesWritten += drwav__write_or_count_u32ne_to_le(pWav, pMetadata->data.smpl.pLoops[iLoop].type); | bytesWritten += drwav__write_or_count_u32ne_to_le(pWav, pMetadata->data.smpl.pLoops[iLoop].type); | ||||
| bytesWritten += drwav__write_or_count_u32ne_to_le(pWav, pMetadata->data.smpl.pLoops[iLoop].firstSampleByteOffset); | |||||
| bytesWritten += drwav__write_or_count_u32ne_to_le(pWav, pMetadata->data.smpl.pLoops[iLoop].lastSampleByteOffset); | |||||
| bytesWritten += drwav__write_or_count_u32ne_to_le(pWav, pMetadata->data.smpl.pLoops[iLoop].firstSampleOffset); | |||||
| bytesWritten += drwav__write_or_count_u32ne_to_le(pWav, pMetadata->data.smpl.pLoops[iLoop].lastSampleOffset); | |||||
| bytesWritten += drwav__write_or_count_u32ne_to_le(pWav, pMetadata->data.smpl.pLoops[iLoop].sampleFraction); | bytesWritten += drwav__write_or_count_u32ne_to_le(pWav, pMetadata->data.smpl.pLoops[iLoop].sampleFraction); | ||||
| bytesWritten += drwav__write_or_count_u32ne_to_le(pWav, pMetadata->data.smpl.pLoops[iLoop].playCount); | bytesWritten += drwav__write_or_count_u32ne_to_le(pWav, pMetadata->data.smpl.pLoops[iLoop].playCount); | ||||
| } | } | ||||
| @@ -4046,7 +4080,7 @@ DRWAV_PRIVATE size_t drwav__write_or_count_metadata(drwav* pWav, drwav_metadata* | |||||
| bytesWritten += drwav__write_or_count(pWav, pMetadata->data.cue.pCuePoints[iCuePoint].dataChunkId, 4); | bytesWritten += drwav__write_or_count(pWav, pMetadata->data.cue.pCuePoints[iCuePoint].dataChunkId, 4); | ||||
| bytesWritten += drwav__write_or_count_u32ne_to_le(pWav, pMetadata->data.cue.pCuePoints[iCuePoint].chunkStart); | bytesWritten += drwav__write_or_count_u32ne_to_le(pWav, pMetadata->data.cue.pCuePoints[iCuePoint].chunkStart); | ||||
| bytesWritten += drwav__write_or_count_u32ne_to_le(pWav, pMetadata->data.cue.pCuePoints[iCuePoint].blockStart); | bytesWritten += drwav__write_or_count_u32ne_to_le(pWav, pMetadata->data.cue.pCuePoints[iCuePoint].blockStart); | ||||
| bytesWritten += drwav__write_or_count_u32ne_to_le(pWav, pMetadata->data.cue.pCuePoints[iCuePoint].sampleByteOffset); | |||||
| bytesWritten += drwav__write_or_count_u32ne_to_le(pWav, pMetadata->data.cue.pCuePoints[iCuePoint].sampleOffset); | |||||
| } | } | ||||
| } break; | } break; | ||||
| @@ -4205,7 +4239,7 @@ DRWAV_PRIVATE size_t drwav__write_or_count_metadata(drwav* pWav, drwav_metadata* | |||||
| if (pMetadata->data.labelOrNote.stringLength > 0) { | if (pMetadata->data.labelOrNote.stringLength > 0) { | ||||
| chunkSize += pMetadata->data.labelOrNote.stringLength + 1; | chunkSize += pMetadata->data.labelOrNote.stringLength + 1; | ||||
| } | |||||
| } | |||||
| } break; | } break; | ||||
| case drwav_metadata_type_list_labelled_cue_region: | case drwav_metadata_type_list_labelled_cue_region: | ||||
| @@ -4714,7 +4748,7 @@ DRWAV_PRIVATE drwav_result drwav_result_from_errno(int e) | |||||
| #ifdef ENOSYS | #ifdef ENOSYS | ||||
| case ENOSYS: return DRWAV_NOT_IMPLEMENTED; | case ENOSYS: return DRWAV_NOT_IMPLEMENTED; | ||||
| #endif | #endif | ||||
| #ifdef ENOTEMPTY | |||||
| #if defined(ENOTEMPTY) && ENOTEMPTY != EEXIST /* In AIX, ENOTEMPTY and EEXIST use the same value. */ | |||||
| case ENOTEMPTY: return DRWAV_DIRECTORY_NOT_EMPTY; | case ENOTEMPTY: return DRWAV_DIRECTORY_NOT_EMPTY; | ||||
| #endif | #endif | ||||
| #ifdef ELOOP | #ifdef ELOOP | ||||
| @@ -5189,7 +5223,7 @@ DRWAV_PRIVATE drwav_bool32 drwav_init_file__internal_FILE(drwav* pWav, FILE* pFi | |||||
| fclose(pFile); | fclose(pFile); | ||||
| return result; | return result; | ||||
| } | } | ||||
| result = drwav_init__internal(pWav, onChunk, pChunkUserData, flags); | result = drwav_init__internal(pWav, onChunk, pChunkUserData, flags); | ||||
| if (result != DRWAV_TRUE) { | if (result != DRWAV_TRUE) { | ||||
| fclose(pFile); | fclose(pFile); | ||||
| @@ -6111,6 +6145,13 @@ DRWAV_PRIVATE drwav_uint64 drwav_read_pcm_frames_s16__msadpcm(drwav* pWav, drwav | |||||
| { | { | ||||
| drwav_uint64 totalFramesRead = 0; | drwav_uint64 totalFramesRead = 0; | ||||
| static drwav_int32 adaptationTable[] = { | |||||
| 230, 230, 230, 230, 307, 409, 512, 614, | |||||
| 768, 614, 512, 409, 307, 230, 230, 230 | |||||
| }; | |||||
| static drwav_int32 coeff1Table[] = { 256, 512, 0, 192, 240, 460, 392 }; | |||||
| static drwav_int32 coeff2Table[] = { 0, -256, 0, 64, 0, -208, -232 }; | |||||
| DRWAV_ASSERT(pWav != NULL); | DRWAV_ASSERT(pWav != NULL); | ||||
| DRWAV_ASSERT(framesToRead > 0); | DRWAV_ASSERT(framesToRead > 0); | ||||
| @@ -6136,6 +6177,11 @@ DRWAV_PRIVATE drwav_uint64 drwav_read_pcm_frames_s16__msadpcm(drwav* pWav, drwav | |||||
| pWav->msadpcm.cachedFrames[2] = pWav->msadpcm.prevFrames[0][0]; | pWav->msadpcm.cachedFrames[2] = pWav->msadpcm.prevFrames[0][0]; | ||||
| pWav->msadpcm.cachedFrames[3] = pWav->msadpcm.prevFrames[0][1]; | pWav->msadpcm.cachedFrames[3] = pWav->msadpcm.prevFrames[0][1]; | ||||
| pWav->msadpcm.cachedFrameCount = 2; | pWav->msadpcm.cachedFrameCount = 2; | ||||
| /* The predictor is used as an index into coeff1Table so we'll need to validate to ensure it never overflows. */ | |||||
| if (pWav->msadpcm.predictor[0] >= drwav_countof(coeff1Table)) { | |||||
| return totalFramesRead; /* Invalid file. */ | |||||
| } | |||||
| } else { | } else { | ||||
| /* Stereo. */ | /* Stereo. */ | ||||
| drwav_uint8 header[14]; | drwav_uint8 header[14]; | ||||
| @@ -6158,6 +6204,11 @@ DRWAV_PRIVATE drwav_uint64 drwav_read_pcm_frames_s16__msadpcm(drwav* pWav, drwav | |||||
| pWav->msadpcm.cachedFrames[2] = pWav->msadpcm.prevFrames[0][1]; | pWav->msadpcm.cachedFrames[2] = pWav->msadpcm.prevFrames[0][1]; | ||||
| pWav->msadpcm.cachedFrames[3] = pWav->msadpcm.prevFrames[1][1]; | pWav->msadpcm.cachedFrames[3] = pWav->msadpcm.prevFrames[1][1]; | ||||
| pWav->msadpcm.cachedFrameCount = 2; | pWav->msadpcm.cachedFrameCount = 2; | ||||
| /* The predictor is used as an index into coeff1Table so we'll need to validate to ensure it never overflows. */ | |||||
| if (pWav->msadpcm.predictor[0] >= drwav_countof(coeff1Table) || pWav->msadpcm.predictor[1] >= drwav_countof(coeff2Table)) { | |||||
| return totalFramesRead; /* Invalid file. */ | |||||
| } | |||||
| } | } | ||||
| } | } | ||||
| @@ -6191,13 +6242,6 @@ DRWAV_PRIVATE drwav_uint64 drwav_read_pcm_frames_s16__msadpcm(drwav* pWav, drwav | |||||
| if (pWav->msadpcm.bytesRemainingInBlock == 0) { | if (pWav->msadpcm.bytesRemainingInBlock == 0) { | ||||
| continue; | continue; | ||||
| } else { | } else { | ||||
| static drwav_int32 adaptationTable[] = { | |||||
| 230, 230, 230, 230, 307, 409, 512, 614, | |||||
| 768, 614, 512, 409, 307, 230, 230, 230 | |||||
| }; | |||||
| static drwav_int32 coeff1Table[] = { 256, 512, 0, 192, 240, 460, 392 }; | |||||
| static drwav_int32 coeff2Table[] = { 0, -256, 0, 64, 0, -208, -232 }; | |||||
| drwav_uint8 nibbles; | drwav_uint8 nibbles; | ||||
| drwav_int32 nibble0; | drwav_int32 nibble0; | ||||
| drwav_int32 nibble1; | drwav_int32 nibble1; | ||||
| @@ -8360,6 +8404,15 @@ DRWAV_API drwav_bool32 drwav_fourcc_equal(const drwav_uint8* a, const char* b) | |||||
| /* | /* | ||||
| REVISION HISTORY | REVISION HISTORY | ||||
| ================ | ================ | ||||
| v0.14.0 - TBD | |||||
| - API CHANGE: The `firstSampleByteOffset`, `lastSampleByteOffset` and `sampleByteOffset` members of `drwav_cue_point` have been renamed to `firstSampleOffset`, `lastSampleOffset` and `sampleOffset`, respectively. | |||||
| - Fix a static analysis warning. | |||||
| - Fix compilation for AIX OS. | |||||
| v0.13.17 - 2024-12-17 | |||||
| - Fix a possible crash when reading from MS-ADPCM encoded files. | |||||
| - Improve detection of ARM64EC | |||||
| v0.13.16 - 2024-02-27 | v0.13.16 - 2024-02-27 | ||||
| - Fix a Wdouble-promotion warning. | - Fix a Wdouble-promotion warning. | ||||
| @@ -8823,4 +8876,3 @@ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | |||||
| OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE | ||||
| SOFTWARE. | SOFTWARE. | ||||
| */ | */ | ||||
| >>>>>>> e80c373575b337720703a41691536272b21b554c | |||||