diff options
| author | David Reid <[email protected]> | 2018-07-05 22:57:45 +1000 |
|---|---|---|
| committer | David Reid <[email protected]> | 2018-07-05 22:57:45 +1000 |
| commit | 6c96fa6301b47826b6a237bcd7353c4f812bc6b2 (patch) | |
| tree | 47f65a75951a24b15ad13d65fa29a08b76acd807 /src/external/dr_flac.h | |
| parent | c598701873988f713d51e749f3a3611ca8253da0 (diff) | |
| download | raylib-6c96fa6301b47826b6a237bcd7353c4f812bc6b2.tar.gz raylib-6c96fa6301b47826b6a237bcd7353c4f812bc6b2.zip | |
Update external audio libraries.
Diffstat (limited to 'src/external/dr_flac.h')
| -rw-r--r-- | src/external/dr_flac.h | 657 |
1 files changed, 440 insertions, 217 deletions
diff --git a/src/external/dr_flac.h b/src/external/dr_flac.h index ff23bdd1..c836847e 100644 --- a/src/external/dr_flac.h +++ b/src/external/dr_flac.h @@ -1,5 +1,5 @@ // FLAC audio decoder. Public domain. See "unlicense" statement at the end of this file. -// dr_flac - v0.8g - 2018-04-19 +// dr_flac - v0.9.7 - 2018-07-05 // // David Reid - [email protected] @@ -111,7 +111,7 @@ // - This has not been tested on big-endian architectures. // - Rice codes in unencoded binary form (see https://xiph.org/flac/format.html#rice_partition) has not been tested. If anybody // knows where I can find some test files for this, let me know. -// - dr_flac is not thread-safe, but it's APIs can be called from any thread so long as you do your own synchronization. +// - dr_flac is not thread-safe, but its APIs can be called from any thread so long as you do your own synchronization. // - When using Ogg encapsulation, a corrupted metadata block will result in drflac_open_with_metadata() and drflac_open() // returning inconsistent samples. @@ -467,7 +467,7 @@ typedef struct // value specified in the STREAMINFO block. drflac_uint8 channels; - // The bits per sample. Will be set to somthing like 16, 24, etc. + // The bits per sample. Will be set to something like 16, 24, etc. drflac_uint8 bitsPerSample; // The maximum block size, in samples. This number represents the number of samples in each channel (not combined). @@ -482,17 +482,16 @@ typedef struct // The container type. This is set based on whether or not the decoder was opened from a native or Ogg stream. drflac_container container; - - // The position of the seektable in the file. - drflac_uint64 seektablePos; - - // The size of the seektable. - drflac_uint32 seektableSize; + // The number of seekpoints in the seektable. + drflac_uint32 seekpointCount; // Information about the frame the decoder is currently sitting on. drflac_frame currentFrame; + // The index of the sample the decoder is currently sitting on. This is only used for seeking. + drflac_uint64 currentSample; + // The position of the first frame in the stream. This is only ever used for seeking. drflac_uint64 firstFramePos; @@ -504,6 +503,9 @@ typedef struct // A pointer to the decoded sample data. This is an offset of pExtraData. drflac_int32* pDecodedSamples; + // A pointer to the seek table. This is an offset of pExtraData, or NULL if there is no seek table. + drflac_seekpoint* pSeekpoints; + // Internal use only. Only used with Ogg containers. Points to a drflac_oggbs object. This is an offset of pExtraData. void* _oggbs; @@ -577,7 +579,7 @@ drflac* drflac_open_relaxed(drflac_read_proc onRead, drflac_seek_proc onSeek, dr // See also: drflac_open_file_with_metadata(), drflac_open_memory_with_metadata(), drflac_open(), drflac_close() drflac* drflac_open_with_metadata(drflac_read_proc onRead, drflac_seek_proc onSeek, drflac_meta_proc onMeta, void* pUserData); -// The same as drflac_open_with_metadata(), except attemps to open the stream even when a header block is not present. +// The same as drflac_open_with_metadata(), except attempts to open the stream even when a header block is not present. // // See also: drflac_open_with_metadata(), drflac_open_relaxed() drflac* drflac_open_with_metadata_relaxed(drflac_read_proc onRead, drflac_seek_proc onSeek, drflac_meta_proc onMeta, drflac_container container, void* pUserData); @@ -766,7 +768,7 @@ const char* drflac_next_vorbis_comment(drflac_vorbis_comment_iterator* pIter, dr // Compile-time CPU feature support. #if !defined(DR_FLAC_NO_SIMD) && (defined(DRFLAC_X86) || defined(DRFLAC_X64)) - #ifdef _MSC_VER + #if defined(_MSC_VER) && !defined(__clang__) #if _MSC_VER >= 1400 #include <intrin.h> static void drflac__cpuid(int info[4], int fid) @@ -780,19 +782,8 @@ const char* drflac_next_vorbis_comment(drflac_vorbis_comment_iterator* pIter, dr #if defined(__GNUC__) || defined(__clang__) static void drflac__cpuid(int info[4], int fid) { - asm ( - "movl %[fid], %%eax\n\t" - "cpuid\n\t" - "movl %%eax, %[info0]\n\t" - "movl %%ebx, %[info1]\n\t" - "movl %%ecx, %[info2]\n\t" - "movl %%edx, %[info3]\n\t" - : [info0] "=rm"(info[0]), - [info1] "=rm"(info[1]), - [info2] "=rm"(info[2]), - [info3] "=rm"(info[3]) - : [fid] "rm"(fid) - : "eax", "ebx", "ecx", "edx" + __asm__ __volatile__ ( + "cpuid" : "=a"(info[0]), "=b"(info[1]), "=c"(info[2]), "=d"(info[3]) : "a"(fid), "c"(0) ); } #else @@ -1447,7 +1438,7 @@ static DRFLAC_INLINE drflac_bool32 drflac__read_uint32(drflac_bs* bs, unsigned i if (bitCount <= DRFLAC_CACHE_L1_BITS_REMAINING(bs)) { if (bitCount < DRFLAC_CACHE_L1_SIZE_BITS(bs)) { - *pResultOut = DRFLAC_CACHE_L1_SELECT_AND_SHIFT(bs, bitCount); + *pResultOut = (drflac_uint32)DRFLAC_CACHE_L1_SELECT_AND_SHIFT(bs, bitCount); bs->consumedBits += bitCount; bs->cache <<= bitCount; } else { @@ -1460,13 +1451,13 @@ static DRFLAC_INLINE drflac_bool32 drflac__read_uint32(drflac_bs* bs, unsigned i // It straddles the cached data. It will never cover more than the next chunk. We just read the number in two parts and combine them. drflac_uint32 bitCountHi = DRFLAC_CACHE_L1_BITS_REMAINING(bs); drflac_uint32 bitCountLo = bitCount - bitCountHi; - drflac_uint32 resultHi = DRFLAC_CACHE_L1_SELECT_AND_SHIFT(bs, bitCountHi); + drflac_uint32 resultHi = (drflac_uint32)DRFLAC_CACHE_L1_SELECT_AND_SHIFT(bs, bitCountHi); if (!drflac__reload_cache(bs)) { return DRFLAC_FALSE; } - *pResultOut = (resultHi << bitCountLo) | DRFLAC_CACHE_L1_SELECT_AND_SHIFT(bs, bitCountLo); + *pResultOut = (resultHi << bitCountLo) | (drflac_uint32)DRFLAC_CACHE_L1_SELECT_AND_SHIFT(bs, bitCountLo); bs->consumedBits += bitCountLo; bs->cache <<= bitCountLo; return DRFLAC_TRUE; @@ -1492,6 +1483,7 @@ static drflac_bool32 drflac__read_int32(drflac_bs* bs, unsigned int bitCount, dr return DRFLAC_TRUE; } +#ifdef DRFLAC_64BIT static drflac_bool32 drflac__read_uint64(drflac_bs* bs, unsigned int bitCount, drflac_uint64* pResultOut) { drflac_assert(bitCount <= 64); @@ -1510,6 +1502,7 @@ static drflac_bool32 drflac__read_uint64(drflac_bs* bs, unsigned int bitCount, d *pResultOut = (((drflac_uint64)resultHi) << 32) | ((drflac_uint64)resultLo); return DRFLAC_TRUE; } +#endif // Function below is unused, but leaving it here in case I need to quickly add it again. #if 0 @@ -1742,7 +1735,7 @@ static DRFLAC_INLINE drflac_bool32 drflac__is_lzcnt_supported() static DRFLAC_INLINE drflac_uint32 drflac__clz_lzcnt(drflac_cache_t x) { -#ifdef _MSC_VER +#if defined(_MSC_VER) && !defined(__clang__) #ifdef DRFLAC_64BIT return (drflac_uint32)__lzcnt64(x); #else @@ -1805,7 +1798,6 @@ static inline drflac_bool32 drflac__seek_past_next_set_bit(drflac_bs* bs, unsign } drflac_uint32 setBitOffsetPlus1 = drflac__clz(bs->cache); - zeroCounter += setBitOffsetPlus1; setBitOffsetPlus1 += 1; bs->consumedBits += setBitOffsetPlus1; @@ -2299,31 +2291,74 @@ static drflac_bool32 drflac__decode_samples_with_residual__rice__simple(drflac_b drflac_assert(count > 0); drflac_assert(pSamplesOut != NULL); - drflac_uint32 zeroCountPart; - drflac_uint32 riceParamPart; + static drflac_uint32 t[2] = {0x00000000, 0xFFFFFFFF}; + + drflac_uint32 zeroCountPart0; + drflac_uint32 zeroCountPart1; + drflac_uint32 zeroCountPart2; + drflac_uint32 zeroCountPart3; + drflac_uint32 riceParamPart0; + drflac_uint32 riceParamPart1; + drflac_uint32 riceParamPart2; + drflac_uint32 riceParamPart3; + drflac_uint32 i4 = 0; + drflac_uint32 count4 = count >> 2; + while (i4 < count4) { + // Rice extraction. + if (!drflac__read_rice_parts(bs, riceParam, &zeroCountPart0, &riceParamPart0) || + !drflac__read_rice_parts(bs, riceParam, &zeroCountPart1, &riceParamPart1) || + !drflac__read_rice_parts(bs, riceParam, &zeroCountPart2, &riceParamPart2) || + !drflac__read_rice_parts(bs, riceParam, &zeroCountPart3, &riceParamPart3)) { + return DRFLAC_FALSE; + } + + riceParamPart0 |= (zeroCountPart0 << riceParam); + riceParamPart1 |= (zeroCountPart1 << riceParam); + riceParamPart2 |= (zeroCountPart2 << riceParam); + riceParamPart3 |= (zeroCountPart3 << riceParam); - drflac_uint32 i = 0; + riceParamPart0 = (riceParamPart0 >> 1) ^ t[riceParamPart0 & 0x01]; + riceParamPart1 = (riceParamPart1 >> 1) ^ t[riceParamPart1 & 0x01]; + riceParamPart2 = (riceParamPart2 >> 1) ^ t[riceParamPart2 & 0x01]; + riceParamPart3 = (riceParamPart3 >> 1) ^ t[riceParamPart3 & 0x01]; + + if (bitsPerSample > 16) { + pSamplesOut[0] = riceParamPart0 + drflac__calculate_prediction_64(order, shift, coefficients, pSamplesOut + 0); + pSamplesOut[1] = riceParamPart1 + drflac__calculate_prediction_64(order, shift, coefficients, pSamplesOut + 1); + pSamplesOut[2] = riceParamPart2 + drflac__calculate_prediction_64(order, shift, coefficients, pSamplesOut + 2); + pSamplesOut[3] = riceParamPart3 + drflac__calculate_prediction_64(order, shift, coefficients, pSamplesOut + 3); + } else { + pSamplesOut[0] = riceParamPart0 + drflac__calculate_prediction_32(order, shift, coefficients, pSamplesOut + 0); + pSamplesOut[1] = riceParamPart1 + drflac__calculate_prediction_32(order, shift, coefficients, pSamplesOut + 1); + pSamplesOut[2] = riceParamPart2 + drflac__calculate_prediction_32(order, shift, coefficients, pSamplesOut + 2); + pSamplesOut[3] = riceParamPart3 + drflac__calculate_prediction_32(order, shift, coefficients, pSamplesOut + 3); + } + + i4 += 1; + pSamplesOut += 4; + } + + drflac_uint32 i = i4 << 2; while (i < count) { // Rice extraction. - if (!drflac__read_rice_parts(bs, riceParam, &zeroCountPart, &riceParamPart)) { + if (!drflac__read_rice_parts(bs, riceParam, &zeroCountPart0, &riceParamPart0)) { return DRFLAC_FALSE; } // Rice reconstruction. - static drflac_uint32 t[2] = {0x00000000, 0xFFFFFFFF}; - - riceParamPart |= (zeroCountPart << riceParam); - riceParamPart = (riceParamPart >> 1) ^ t[riceParamPart & 0x01]; - //riceParamPart = (riceParamPart >> 1) ^ (~(riceParamPart & 0x01) + 1); + riceParamPart0 |= (zeroCountPart0 << riceParam); + riceParamPart0 = (riceParamPart0 >> 1) ^ t[riceParamPart0 & 0x01]; + //riceParamPart0 = (riceParamPart0 >> 1) ^ (~(riceParamPart0 & 0x01) + 1); // Sample reconstruction. if (bitsPerSample > 16) { - pSamplesOut[i] = riceParamPart + drflac__calculate_prediction_64(order, shift, coefficients, pSamplesOut + i); + pSamplesOut[0] = riceParamPart0 + drflac__calculate_prediction_64(order, shift, coefficients, pSamplesOut + 0); } else { - pSamplesOut[i] = riceParamPart + drflac__calculate_prediction_32(order, shift, coefficients, pSamplesOut + i); + pSamplesOut[0] = riceParamPart0 + drflac__calculate_prediction_32(order, shift, coefficients, pSamplesOut + 0); } i += 1; + pSamplesOut += 1; } return DRFLAC_TRUE; @@ -3124,6 +3159,8 @@ static drflac_bool32 drflac__seek_to_first_frame(drflac* pFlac) drflac_bool32 result = drflac__seek_to_byte(&pFlac->bs, pFlac->firstFramePos); drflac_zero_memory(&pFlac->currentFrame, sizeof(pFlac->currentFrame)); + pFlac->currentSample = 0; + return result; } @@ -3136,18 +3173,42 @@ static DRFLAC_INLINE drflac_result drflac__seek_to_next_frame(drflac* pFlac) static drflac_bool32 drflac__seek_to_sample__brute_force(drflac* pFlac, drflac_uint64 sampleIndex) { - // We need to find the frame that contains the sample. To do this, we iterate over each frame and inspect it's header. If based on the - // header we can determine that the frame contains the sample, we do a full decode of that frame. - if (!drflac__seek_to_first_frame(pFlac)) { - return DRFLAC_FALSE; - } + drflac_assert(pFlac != NULL); - drflac_uint64 runningSampleCount = 0; - for (;;) { + drflac_bool32 isMidFrame = DRFLAC_FALSE; + + // If we are seeking forward we start from the current position. Otherwise we need to start all the way from the start of the file. + drflac_uint64 runningSampleCount; + if (sampleIndex >= pFlac->currentSample) { + // Seeking forward. Need to seek from the current position. + runningSampleCount = pFlac->currentSample; + + // The frame header for the first frame may not yet have been read. We need to do that if necessary. + if (pFlac->currentSample == 0 && pFlac->currentFrame.samplesRemaining == 0) { + if (!drflac__read_next_frame_header(&pFlac->bs, pFlac->bitsPerSample, &pFlac->currentFrame.header)) { + return DRFLAC_FALSE; + } + } else { + isMidFrame = DRFLAC_TRUE; + } + } else { + // Seeking backwards. Need to seek from the start of the file. + runningSampleCount = 0; + + // Move back to the start. + if (!drflac__seek_to_first_frame(pFlac)) { + return DRFLAC_FALSE; + } + + // Decode the first frame in preparation for sample-exact seeking below. if (!drflac__read_next_frame_header(&pFlac->bs, pFlac->bitsPerSample, &pFlac->currentFrame.header)) { return DRFLAC_FALSE; } + } + // We need to as quickly as possible find the frame that contains the target sample. To do this, we iterate over each frame and inspect its + // header. If based on the header we can determine that the frame contains the sample, we do a full decode of that frame. + for (;;) { drflac_uint64 firstSampleInFrame = 0; drflac_uint64 lastSampleInFrame = 0; drflac__get_current_frame_sample_range(pFlac, &firstSampleInFrame, &lastSampleInFrame); @@ -3156,35 +3217,52 @@ static drflac_bool32 drflac__seek_to_sample__brute_force(drflac* pFlac, drflac_u if (sampleIndex < (runningSampleCount + sampleCountInThisFrame)) { // The sample should be in this frame. We need to fully decode it, however if it's an invalid frame (a CRC mismatch), we need to pretend // it never existed and keep iterating. - drflac_result result = drflac__decode_frame(pFlac); - if (result == DRFLAC_SUCCESS) { - // The frame is valid. We just need to skip over some samples to ensure it's sample-exact. - drflac_uint64 samplesToDecode = (size_t)(sampleIndex - runningSampleCount); // <-- Safe cast because the maximum number of samples in a frame is 65535. - if (samplesToDecode == 0) { - return DRFLAC_TRUE; - } - return drflac_read_s32(pFlac, samplesToDecode, NULL) != 0; // <-- If this fails, something bad has happened (it should never fail). - } else { - if (result == DRFLAC_CRC_MISMATCH) { - continue; // CRC mismatch. Pretend this frame never existed. + drflac_uint64 samplesToDecode = sampleIndex - runningSampleCount; + + if (!isMidFrame) { + drflac_result result = drflac__decode_frame(pFlac); + if (result == DRFLAC_SUCCESS) { + // The frame is valid. We just need to skip over some samples to ensure it's sample-exact. + return drflac_read_s32(pFlac, samplesToDecode, NULL) == samplesToDecode; // <-- If this fails, something bad has happened (it should never fail). } else { - return DRFLAC_FALSE; + if (result == DRFLAC_CRC_MISMATCH) { + goto next_iteration; // CRC mismatch. Pretend this frame never existed. + } else { + return DRFLAC_FALSE; + } } + } else { + // We started seeking mid-frame which means we need to skip the frame decoding part. + return drflac_read_s32(pFlac, samplesToDecode, NULL) == samplesToDecode; } } else { // It's not in this frame. We need to seek past the frame, but check if there was a CRC mismatch. If so, we pretend this // frame never existed and leave the running sample count untouched. - drflac_result result = drflac__seek_to_next_frame(pFlac); - if (result == DRFLAC_SUCCESS) { - runningSampleCount += sampleCountInThisFrame; - } else { - if (result == DRFLAC_CRC_MISMATCH) { - continue; // CRC mismatch. Pretend this frame never existed. + if (!isMidFrame) { + drflac_result result = drflac__seek_to_next_frame(pFlac); + if (result == DRFLAC_SUCCESS) { + runningSampleCount += sampleCountInThisFrame; } else { - return DRFLAC_FALSE; + if (result == DRFLAC_CRC_MISMATCH) { + goto next_iteration; // CRC mismatch. Pretend this frame never existed. + } else { + return DRFLAC_FALSE; + } } + } else { + // We started seeking mid-frame which means we need to seek by reading to the end of the frame instead of with + // drflac__seek_to_next_frame() which only works if the decoder is sitting on the byte just after the frame header. + runningSampleCount += pFlac->currentFrame.samplesRemaining; + pFlac->currentFrame.samplesRemaining = 0; + isMidFrame = DRFLAC_FALSE; } } + + next_iteration: + // Grab the next frame in preparation for the next iteration. + if (!drflac__read_next_frame_header(&pFlac->bs, pFlac->bitsPerSample, &pFlac->currentFrame.header)) { + return DRFLAC_FALSE; + } } } @@ -3193,95 +3271,107 @@ static drflac_bool32 drflac__seek_to_sample__seek_table(drflac* pFlac, drflac_ui { drflac_assert(pFlac != NULL); - if (pFlac->seektablePos == 0) { + if (pFlac->pSeekpoints == NULL || pFlac->seekpointCount == 0) { return DRFLAC_FALSE; } - if (!drflac__seek_to_byte(&pFlac->bs, pFlac->seektablePos)) { - return DRFLAC_FALSE; - } - // The number of seek points is derived from the size of the SEEKTABLE block. - drflac_uint32 seekpointCount = pFlac->seektableSize / 18; // 18 = the size of each seek point. - if (seekpointCount == 0) { - return DRFLAC_FALSE; // Would this ever happen? + drflac_uint32 iClosestSeekpoint = 0; + for (drflac_uint32 iSeekpoint = 0; iSeekpoint < pFlac->seekpointCount; ++iSeekpoint) { + if (pFlac->pSeekpoints[iSeekpoint].firstSample*pFlac->channels >= sampleIndex) { + break; + } + + iClosestSeekpoint = iSeekpoint; } - drflac_seekpoint closestSeekpoint = {0, 0, 0}; + drflac_bool32 isMidFrame = DRFLAC_FALSE; - drflac_uint32 seekpointsRemaining = seekpointCount; - while (seekpointsRemaining > 0) { - drflac_seekpoint seekpoint; - if (!drflac__read_uint64(&pFlac->bs, 64, &seekpoint.firstSample)) { - break; - } - if (!drflac__read_uint64(&pFlac->bs, 64, &seekpoint.frameOffset)) { - break; - } - if (!drflac__read_uint16(&pFlac->bs, 16, &seekpoint.sampleCount)) { - break; - } + // At this point we should have found the seekpoint closest to our sample. If we are seeking forward and the closest seekpoint is _before_ the current sample, we + // just seek forward from where we are. Otherwise we start seeking from the seekpoint's first sample. + drflac_uint64 runningSampleCount; + if ((sampleIndex >= pFlac->currentSample) && (pFlac->pSeekpoints[iClosestSeekpoint].firstSample*pFlac->channels <= pFlac->currentSample)) { + // Optimized case. Just seek forward from where we are. + runningSampleCount = pFlac->currentSample; - // Note that the seekpoint sample is based on a single channel. The input sample (sampleIndex) is based on interleaving, thus - // we need to multiple the seekpoint's sample by the channel count. - if (seekpoint.firstSample*pFlac->channels > sampleIndex) { - break; + // The frame header for the first frame may not yet have been read. We need to do that if necessary. + if (pFlac->currentSample == 0 && pFlac->currentFrame.samplesRemaining == 0) { + if (!drflac__read_next_frame_header(&pFlac->bs, pFlac->bitsPerSample, &pFlac->currentFrame.header)) { + return DRFLAC_FALSE; + } + } else { + isMidFrame = DRFLAC_TRUE; } + } else { + // Slower case. Seek to the start of the seekpoint and then seek forward from there. + runningSampleCount = pFlac->pSeekpoints[iClosestSeekpoint].firstSample*pFlac->channels; - closestSeekpoint = seekpoint; - seekpointsRemaining -= 1; - } - - // At this point we should have found the seekpoint closest to our sample. We need to seek to it using basically the same - // technique as we use with the brute force method. - if (!drflac__seek_to_byte(&pFlac->bs, pFlac->firstFramePos + closestSeekpoint.frameOffset)) { - return DRFLAC_FALSE; - } + if (!drflac__seek_to_byte(&pFlac->bs, pFlac->firstFramePos + pFlac->pSeekpoints[iClosestSeekpoint].frameOffset)) { + return DRFLAC_FALSE; + } - drflac_uint64 runningSampleCount = closestSeekpoint.firstSample*pFlac->channels; - for (;;) { + // Grab the frame the seekpoint is sitting on in preparation for the sample-exact seeking below. if (!drflac__read_next_frame_header(&pFlac->bs, pFlac->bitsPerSample, &pFlac->currentFrame.header)) { return DRFLAC_FALSE; } + } + for (;;) { drflac_uint64 firstSampleInFrame = 0; drflac_uint64 lastSampleInFrame = 0; drflac__get_current_frame_sample_range(pFlac, &firstSampleInFrame, &lastSampleInFrame); drflac_uint64 sampleCountInThisFrame = (lastSampleInFrame - firstSampleInFrame) + 1; if (sampleIndex < (runningSampleCount + sampleCountInThisFrame)) { - // The sample should be in this frame. We need to fully decode it, however if it's an invalid frame (a CRC mismatch), we need to pretend + // The sample should be in this frame. We need to fully decode it, but if it's an invalid frame (a CRC mismatch) we need to pretend // it never existed and keep iterating. - drflac_result result = drflac__decode_frame(pFlac); - if (result == DRFLAC_SUCCESS) { - // The frame is valid. We just need to skip over some samples to ensure it's sample-exact. - drflac_uint64 samplesToDecode = (size_t)(sampleIndex - runningSampleCount); // <-- Safe cast because the maximum number of samples in a frame is 65535. - if (samplesToDecode == 0) { - return DRFLAC_TRUE; - } - return drflac_read_s32(pFlac, samplesToDecode, NULL) != 0; // <-- If this fails, something bad has happened (it should never fail). - } else { - if (result == DRFLAC_CRC_MISMATCH) { - continue; // CRC mismatch. Pretend this frame never existed. + drflac_uint64 samplesToDecode = sampleIndex - runningSampleCount; + + if (!isMidFrame) { + drflac_result result = drflac__decode_frame(pFlac); + if (result == DRFLAC_SUCCESS) { + // The frame is valid. We just need to skip over some samples to ensure it's sample-exact. + return drflac_read_s32(pFlac, samplesToDecode, NULL) == samplesToDecode; // <-- If this fails, something bad has happened (it should never fail). } else { - return DRFLAC_FALSE; + if (result == DRFLAC_CRC_MISMATCH) { + goto next_iteration; // CRC mismatch. Pretend this frame never existed. + } else { + return DRFLAC_FALSE; + } } + } else { + // We started seeking mid-frame which means we need to skip the frame decoding part. + return drflac_read_s32(pFlac, samplesToDecode, NULL) == samplesToDecode; } } else { // It's not in this frame. We need to seek past the frame, but check if there was a CRC mismatch. If so, we pretend this // frame never existed and leave the running sample count untouched. - drflac_result result = drflac__seek_to_next_frame(pFlac); - if (result == DRFLAC_SUCCESS) { - runningSampleCount += sampleCountInThisFrame; - } else { - if (result == DRFLAC_CRC_MISMATCH) { - continue; // CRC mismatch. Pretend this frame never existed. + if (!isMidFrame) { + drflac_result result = drflac__seek_to_next_frame(pFlac); + if (result == DRFLAC_SUCCESS) { + runningSampleCount += sampleCountInThisFrame; } else { - return DRFLAC_FALSE; + if (result == DRFLAC_CRC_MISMATCH) { + goto next_iteration; // CRC mismatch. Pretend this frame never existed. + } else { + return DRFLAC_FALSE; + } } + } else { + // We started seeking mid-frame which means we need to seek by reading to the end of the frame instead of with + // drflac__seek_to_next_frame() which only works if the decoder is sitting on the byte just after the frame header. + runningSampleCount += pFlac->currentFrame.samplesRemaining; + pFlac->currentFrame.samplesRemaining = 0; + isMidFrame = DRFLAC_FALSE; } } + + next_iteration: + // Grab the next frame in preparation for the next iteration. + if (!drflac__read_next_frame_header(&pFlac->bs, pFlac->bitsPerSample, &pFlac->currentFrame.header)) { + return DRFLAC_FALSE; + } } } @@ -3389,10 +3479,8 @@ drflac_bool32 drflac__read_streaminfo(drflac_read_proc onRead, void* pUserData, return DRFLAC_TRUE; } -drflac_bool32 drflac__read_and_decode_metadata(drflac* pFlac) +drflac_bool32 drflac__read_and_decode_metadata(drflac_read_proc onRead, drflac_seek_proc onSeek, drflac_meta_proc onMeta, void* pUserData, void* pUserDataMD, drflac_uint64* pFirstFramePos, drflac_uint64* pSeektablePos, drflac_uint32* pSeektableSize) { - drflac_assert(pFlac != NULL); - // We want to keep track of the byte position in the stream of the seektable. At the time of calling this function we know that // we'll be sitting on byte 42. drflac_uint64 runningFilePos = 42; @@ -3403,7 +3491,7 @@ drflac_bool32 drflac__read_and_decode_metadata(drflac* pFlac) drflac_uint8 isLastBlock = 0; drflac_uint8 blockType; drflac_uint32 blockSize; - if (!drflac__read_and_decode_block_header(pFlac->bs.onRead, pFlac->bs.pUserData, &isLastBlock, &blockType, &blockSize)) { + if (!drflac__read_and_decode_block_header(onRead, pUserData, &isLastBlock, &blockType, &blockSize)) { return DRFLAC_FALSE; } runningFilePos += 4; @@ -3418,13 +3506,13 @@ drflac_bool32 drflac__read_and_decode_metadata(drflac* pFlac) { case DRFLAC_METADATA_BLOCK_TYPE_APPLICATION: { - if (pFlac->onMeta) { + if (onMeta) { void* pRawData = DRFLAC_MALLOC(blockSize); if (pRawData == NULL) { return DRFLAC_FALSE; } - if (pFlac->bs.onRead(pFlac->bs.pUserData, pRawData, blockSize) != blockSize) { + if (onRead(pUserData, pRawData, blockSize) != blockSize) { DRFLAC_FREE(pRawData); return DRFLAC_FALSE; } @@ -3434,7 +3522,7 @@ drflac_bool32 drflac__read_and_decode_metadata(drflac* pFlac) metadata.data.application.id = drflac__be2host_32(*(drflac_uint32*)pRawData); metadata.data.application.pData = (const void*)((drflac_uint8*)pRawData + sizeof(drflac_uint32)); metadata.data.application.dataSize = blockSize - sizeof(drflac_uint32); - pFlac->onMeta(pFlac->pUserDataMD, &metadata); + onMeta(pUserDataMD, &metadata); DRFLAC_FREE(pRawData); } @@ -3445,13 +3533,13 @@ drflac_bool32 drflac__read_and_decode_metadata(drflac* pFlac) seektablePos = runningFilePos; seektableSize = blockSize; - if (pFlac->onMeta) { + if (onMeta) { void* pRawData = DRFLAC_MALLOC(blockSize); if (pRawData == NULL) { return DRFLAC_FALSE; } - if (pFlac->bs.onRead(pFlac->bs.pUserData, pRawData, blockSize) != blockSize) { + if (onRead(pUserData, pRawData, blockSize) != blockSize) { DRFLAC_FREE(pRawData); return DRFLAC_FALSE; } @@ -3469,7 +3557,7 @@ drflac_bool32 drflac__read_and_decode_metadata(drflac* pFlac) pSeekpoint->sampleCount = drflac__be2host_16(pSeekpoint->sampleCount); } - pFlac->onMeta(pFlac->pUserDataMD, &metadata); + onMeta(pUserDataMD, &metadata); DRFLAC_FREE(pRawData); } @@ -3477,13 +3565,13 @@ drflac_bool32 drflac__read_and_decode_metadata(drflac* pFlac) case DRFLAC_METADATA_BLOCK_TYPE_VORBIS_COMMENT: { - if (pFlac->onMeta) { + if (onMeta) { void* pRawData = DRFLAC_MALLOC(blockSize); if (pRawData == NULL) { return DRFLAC_FALSE; } - if (pFlac->bs.onRead(pFlac->bs.pUserData, pRawData, blockSize) != blockSize) { + if (onRead(pUserData, pRawData, blockSize) != blockSize) { DRFLAC_FREE(pRawData); return DRFLAC_FALSE; } @@ -3496,7 +3584,7 @@ drflac_bool32 drflac__read_and_decode_metadata(drflac* pFlac) metadata.data.vorbis_comment.vendor = pRunningData; pRunningData += metadata.data.vorbis_comment.vendorLength; metadata.data.vorbis_comment.commentCount = drflac__le2host_32(*(drflac_uint32*)pRunningData); pRunningData += 4; metadata.data.vorbis_comment.comments = pRunningData; - pFlac->onMeta(pFlac->pUserDataMD, &metadata); + onMeta(pUserDataMD, &metadata); DRFLAC_FREE(pRawData); } @@ -3504,13 +3592,13 @@ drflac_bool32 drflac__read_and_decode_metadata(drflac* pFlac) case DRFLAC_METADATA_BLOCK_TYPE_CUESHEET: { - if (pFlac->onMeta) { + if (onMeta) { void* pRawData = DRFLAC_MALLOC(blockSize); if (pRawData == NULL) { return DRFLAC_FALSE; } - if (pFlac->bs.onRead(pFlac->bs.pUserData, pRawData, blockSize) != blockSize) { + if (onRead(pUserData, pRawData, blockSize) != blockSize) { DRFLAC_FREE(pRawData); return DRFLAC_FALSE; } @@ -3524,7 +3612,7 @@ drflac_bool32 drflac__read_and_decode_metadata(drflac* pFlac) metadata.data.cuesheet.isCD = ((pRunningData[0] & 0x80) >> 7) != 0; pRunningData += 259; metadata.data.cuesheet.trackCount = pRunningData[0]; pRunningData += 1; metadata.data.cuesheet.pTrackData = (const drflac_uint8*)pRunningData; - pFlac->onMeta(pFlac->pUserDataMD, &metadata); + onMeta(pUserDataMD, &metadata); DRFLAC_FREE(pRawData); } @@ -3532,13 +3620,13 @@ drflac_bool32 drflac__read_and_decode_metadata(drflac* pFlac) case DRFLAC_METADATA_BLOCK_TYPE_PICTURE: { - if (pFlac->onMeta) { + if (onMeta) { void* pRawData = DRFLAC_MALLOC(blockSize); if (pRawData == NULL) { return DRFLAC_FALSE; } - if (pFlac->bs.onRead(pFlac->bs.pUserData, pRawData, blockSize) != blockSize) { + if (onRead(pUserData, pRawData, blockSize) != blockSize) { DRFLAC_FREE(pRawData); return DRFLAC_FALSE; } @@ -3558,7 +3646,7 @@ drflac_bool32 drflac__read_and_decode_metadata(drflac* pFlac) metadata.data.picture.indexColorCount = drflac__be2host_32(*(drflac_uint32*)pRunningData); pRunningData += 4; metadata.data.picture.pictureDataSize = drflac__be2host_32(*(drflac_uint32*)pRunningData); pRunningData += 4; metadata.data.picture.pPictureData = (const drflac_uint8*)pRunningData; - pFlac->onMeta(pFlac->pUserDataMD, &metadata); + onMeta(pUserDataMD, &metadata); DRFLAC_FREE(pRawData); } @@ -3566,14 +3654,14 @@ drflac_bool32 drflac__read_and_decode_metadata(drflac* pFlac) case DRFLAC_METADATA_BLOCK_TYPE_PADDING: { - if (pFlac->onMeta) { + if (onMeta) { metadata.data.padding.unused = 0; // Padding doesn't have anything meaningful in it, so just skip over it, but make sure the caller is aware of it by firing the callback. - if (!pFlac->bs.onSeek(pFlac->bs.pUserData, blockSize, drflac_seek_origin_current)) { - isLastBlock = DRFLAC_TRUE; // An error occured while seeking. Attempt to recover by treating this as the last block which will in turn terminate the loop. + if (!onSeek(pUserData, blockSize, drflac_seek_origin_current)) { + isLastBlock = DRFLAC_TRUE; // An error occurred while seeking. Attempt to recover by treating this as the last block which will in turn terminate the loop. } else { - pFlac->onMeta(pFlac->pUserDataMD, &metadata); + onMeta(pUserDataMD, &metadata); } } } break; @@ -3581,31 +3669,31 @@ drflac_bool32 drflac__read_and_decode_metadata(drflac* pFlac) case DRFLAC_METADATA_BLOCK_TYPE_INVALID: { // Invalid chunk. Just skip over this one. - if (pFlac->onMeta) { - if (!pFlac->bs.onSeek(pFlac->bs.pUserData, blockSize, drflac_seek_origin_current)) { - isLastBlock = DRFLAC_TRUE; // An error occured while seeking. Attempt to recover by treating this as the last block which will in turn terminate the loop. + if (onMeta) { + if (!onSeek(pUserData, blockSize, drflac_seek_origin_current)) { + isLastBlock = DRFLAC_TRUE; // An error occurred while seeking. Attempt to recover by treating this as the last block which will in turn terminate the loop. } } - } + } break; default: { // It's an unknown chunk, but not necessarily invalid. There's a chance more metadata blocks might be defined later on, so we // can at the very least report the chunk to the application and let it look at the raw data. - if (pFlac->onMeta) { + if (onMeta) { void* pRawData = DRFLAC_MALLOC(blockSize); if (pRawData == NULL) { return DRFLAC_FALSE; } - if (pFlac->bs.onRead(pFlac->bs.pUserData, pRawData, blockSize) != blockSize) { + if (onRead(pUserData, pRawData, blockSize) != blockSize) { DRFLAC_FREE(pRawData); return DRFLAC_FALSE; } metadata.pRawData = pRawData; metadata.rawDataSize = blockSize; - pFlac->onMeta(pFlac->pUserDataMD, &metadata); + onMeta(pUserDataMD, &metadata); DRFLAC_FREE(pRawData); } @@ -3613,8 +3701,8 @@ drflac_bool32 drflac__read_and_decode_metadata(drflac* pFlac) } // If we're not handling metadata, just skip over the block. If we are, it will have been handled earlier in the switch statement above. - if (pFlac->onMeta == NULL && blockSize > 0) { - if (!pFlac->bs.onSeek(pFlac->bs.pUserData, blockSize, drflac_seek_origin_current)) { + if (onMeta == NULL && blockSize > 0) { + if (!onSeek(pUserData, blockSize, drflac_seek_origin_current)) { isLastBlock = DRFLAC_TRUE; } } @@ -3625,9 +3713,9 @@ drflac_bool32 drflac__read_and_decode_metadata(drflac* pFlac) } } - pFlac->seektablePos = seektablePos; - pFlac->seektableSize = seektableSize; - pFlac->firstFramePos = runningFilePos; + *pSeektablePos = seektablePos; + *pSeektableSize = seektableSize; + *pFirstFramePos = runningFilePos; return DRFLAC_TRUE; } @@ -3917,7 +4005,7 @@ drflac_result drflac_ogg__read_page_header(drflac_read_proc onRead, void* pUserD // The main part of the Ogg encapsulation is the conversion from the physical Ogg bitstream to the native FLAC bitstream. It works -// in three general stages: Ogg Physical Bitstream -> Ogg/FLAC Logical Bitstream -> FLAC Native Bitstream. dr_flac is architecured +// in three general stages: Ogg Physical Bitstream -> Ogg/FLAC Logical Bitstream -> FLAC Native Bitstream. dr_flac is designed // in such a way that the core sections assume everything is delivered in native format. Therefore, for each encapsulation type // dr_flac is supporting there needs to be a layer sitting on top of the onRead and onSeek callbacks that ensures the bits read from // the physical Ogg bitstream are converted and delivered in native FLAC format. @@ -4024,6 +4112,8 @@ static drflac_bool32 drflac_oggbs__goto_next_page(drflac_oggbs* oggbs, drflac_og return DRFLAC_FALSE; } } +#else + (void)recoveryMethod; // <-- Silence a warning. #endif oggbs->currentPageHeader = header; @@ -4263,13 +4353,13 @@ drflac_bool32 drflac_ogg__seek_to_sample(drflac* pFlac, drflac_uint64 sampleInde // // Another thing to consider is that using the Ogg framing system will perform direct seeking of the physical Ogg // bitstream. This is important to consider because it means we cannot read data from the drflac_bs object using the - // standard drflac__*() APIs because that will read in extra data for it's own internal caching which in turn breaks + // standard drflac__*() APIs because that will read in extra data for its own internal caching which in turn breaks // the positioning of the read pointer of the physical Ogg bitstream. Therefore, anything that would normally be read // using the native FLAC decoding APIs, such as drflac__read_next_frame_header(), need to be re-implemented so as to // avoid the use of the drflac_bs object. // // Considering these issues, I have decided to use the slower native FLAC decoding method for the following reasons: - // 1) Seeking is already partially accellerated using Ogg's paging system in the code block above. + // 1) Seeking is already partially accelerated using Ogg's paging system in the code block above. // 2) Seeking in an Ogg encapsulated FLAC stream is probably quite uncommon. // 3) Simplicity. if (!drflac__read_next_frame_header(&pFlac->bs, pFlac->bitsPerSample, &pFlac->currentFrame.header)) { @@ -4462,7 +4552,7 @@ drflac_bool32 drflac__init_private__ogg(drflac_init_info* pInit, drflac_read_pro // If we get here it means we found a FLAC audio stream. We should be sitting on the first byte of the header of the next page. The next - // packets in the FLAC logical stream contain the metadata. The only thing left to do in the initialiation phase for Ogg is to create the + // packets in the FLAC logical stream contain the metadata. The only thing left to do in the initialization phase for Ogg is to create the // Ogg bistream object. pInit->hasMetadataBlocks = DRFLAC_TRUE; // <-- Always have at least VORBIS_COMMENT metadata block. return DRFLAC_TRUE; @@ -4604,43 +4694,116 @@ drflac* drflac_open_with_metadata_private(drflac_read_proc onRead, drflac_seek_p #ifndef DR_FLAC_NO_OGG // There's additional data required for Ogg streams. + drflac_uint32 oggbsAllocationSize = 0; if (init.container == drflac_container_ogg) { - allocationSize += sizeof(drflac_oggbs); + oggbsAllocationSize = sizeof(drflac_oggbs); + allocationSize += oggbsAllocationSize; } + + drflac_oggbs oggbs; + drflac_zero_memory(&oggbs, sizeof(oggbs)); + if (init.container == drflac_container_ogg) { + oggbs.onRead = onRead; + oggbs.onSeek = onSeek; + oggbs.pUserData = pUserData; + oggbs.currentBytePos = init.oggFirstBytePos; + oggbs.firstBytePos = init.oggFirstBytePos; + oggbs.serialNumber = init.oggSerial; + oggbs.bosPageHeader = init.oggBosHeader; + oggbs.bytesRemainingInPage = 0; + } +#endif + + // This part is a bit awkward. We need to load the seektable so that it can be referenced in-memory, but I want the drflac object to + // consist of only a single heap allocation. To this, the size of the seek table needs to be known, which we determine when reading + // and decoding the metadata. + drflac_uint64 firstFramePos = 42; // <-- We know we are at byte 42 at this point. + drflac_uint64 seektablePos = 0; + drflac_uint32 seektableSize = 0; + if (init.hasMetadataBlocks) { + drflac_read_proc onReadOverride = onRead; + drflac_seek_proc onSeekOverride = onSeek; + void* pUserDataOverride = pUserData; + +#ifndef DR_FLAC_NO_OGG + if (init.container == drflac_container_ogg) { + onReadOverride = drflac__on_read_ogg; + onSeekOverride = drflac__on_seek_ogg; + pUserDataOverride = (void*)&oggbs; + } #endif + if (!drflac__read_and_decode_metadata(onReadOverride, onSeekOverride, onMeta, pUserDataOverride, pUserDataMD, &firstFramePos, &seektablePos, &seektableSize)) { + return NULL; + } + + allocationSize += seektableSize; + } + + drflac* pFlac = (drflac*)DRFLAC_MALLOC(allocationSize); drflac__init_from_info(pFlac, &init); pFlac->pDecodedSamples = (drflac_int32*)drflac_align((size_t)pFlac->pExtraData, DRFLAC_MAX_SIMD_VECTOR_SIZE); #ifndef DR_FLAC_NO_OGG if (init.container == drflac_container_ogg) { - drflac_oggbs* oggbs = (drflac_oggbs*)((drflac_uint8*)pFlac->pDecodedSamples + decodedSamplesAllocationSize); - oggbs->onRead = onRead; - oggbs->onSeek = onSeek; - oggbs->pUserData = pUserData; - oggbs->currentBytePos = init.oggFirstBytePos; - oggbs->firstBytePos = init.oggFirstBytePos; - oggbs->serialNumber = init.oggSerial; - oggbs->bosPageHeader = init.oggBosHeader; - oggbs->bytesRemainingInPage = 0; + drflac_oggbs* pInternalOggbs = (drflac_oggbs*)((drflac_uint8*)pFlac->pDecodedSamples + decodedSamplesAllocationSize + seektableSize); + *pInternalOggbs = oggbs; // The Ogg bistream needs to be layered on top of the original bitstream. pFlac->bs.onRead = drflac__on_read_ogg; pFlac->bs.onSeek = drflac__on_seek_ogg; - pFlac->bs.pUserData = (void*)oggbs; - pFlac->_oggbs = (void*)oggbs; + pFlac->bs.pUserData = (void*)pInternalOggbs; + pFlac->_oggbs = (void*)pInternalOggbs; } #endif - // Decode metadata before returning. - if (init.hasMetadataBlocks) { - if (!drflac__read_and_decode_metadata(pFlac)) { - DRFLAC_FREE(pFlac); - return NULL; + pFlac->firstFramePos = firstFramePos; + + // NOTE: Seektables are not currently compatible with Ogg encapsulation (Ogg has its own accelerated seeking system). I may change this later, so I'm leaving this here for now. +#ifndef DR_FLAC_NO_OGG + if (init.container == drflac_container_ogg) + { + pFlac->pSeekpoints = NULL; + pFlac->seekpointCount = 0; + } + else +#endif + { + // If we have a seektable we need to load it now, making sure we move back to where we were previously. + if (seektablePos != 0) { + pFlac->seekpointCount = seektableSize / sizeof(*pFlac->pSeekpoints); + pFlac->pSeekpoints = (drflac_seekpoint*)((drflac_uint8*)pFlac->pDecodedSamples + decodedSamplesAllocationSize); + + // Seek to the seektable, then just read directly into our seektable buffer. + if (pFlac->bs.onSeek(pFlac->bs.pUserData, (int)seektablePos, drflac_seek_origin_start)) { + if (pFlac->bs.onRead(pFlac->bs.pUserData, pFlac->pSeekpoints, seektableSize) == seektableSize) { + // Endian swap. + for (drflac_uint32 iSeekpoint = 0; iSeekpoint < pFlac->seekpointCount; ++iSeekpoint) { + pFlac->pSeekpoints[iSeekpoint].firstSample = drflac__be2host_64(pFlac->pSeekpoints[iSeekpoint].firstSample); + pFlac->pSeekpoints[iSeekpoint].frameOffset = drflac__be2host_64(pFlac->pSeekpoints[iSeekpoint].frameOffset); + pFlac->pSeekpoints[iSeekpoint].sampleCount = drflac__be2host_16(pFlac->pSeekpoints[iSeekpoint].sampleCount); + } + } else { + // Failed to read the seektable. Pretend we don't have one. + pFlac->pSeekpoints = NULL; + pFlac->seekpointCount = 0; + } + + // We need to seek back to where we were. If this fails it's a critical error. + if (!pFlac->bs.onSeek(pFlac->bs.pUserData, (int)pFlac->firstFramePos, drflac_seek_origin_start)) { + return NULL; + } + } else { + // Failed to seek to the seektable. Ominous sign, but for now we can just pretend we don't have one. + pFlac->pSeekpoints = NULL; + pFlac->seekpointCount = 0; + } } } + + // If we get here, but don't have a STREAMINFO block, it means we've opened the stream in relaxed mode and need to decode // the first frame. if (!init.hasStreamInfoBlock) { @@ -4808,6 +4971,7 @@ static drflac_bool32 drflac__on_seek_memory(void* pUserData, int offset, drflac_ drflac__memory_stream* memoryStream = (drflac__memory_stream*)pUserData; drflac_assert(memoryStream != NULL); drflac_assert(offset > 0 || (offset == 0 && origin == drflac_seek_origin_start)); + drflac_assert(offset <= (drflac_int64)memoryStream->dataSize); if (origin == drflac_seek_origin_current) { if (memoryStream->currentReadPos + offset <= memoryStream->dataSize) { @@ -5024,18 +5188,25 @@ drflac_uint64 drflac__seek_forward_by_samples(drflac* pFlac, drflac_uint64 sampl break; // Couldn't read the next frame, so just break from the loop and return. } } else { - samplesRead += 1; - pFlac->currentFrame.samplesRemaining -= 1; - samplesToRead -= 1; + if (pFlac->currentFrame.samplesRemaining > samplesToRead) { + samplesRead += samplesToRead; + pFlac->currentFrame.samplesRemaining -= (drflac_uint32)samplesToRead; // <-- Safe cast. Will always be < currentFrame.samplesRemaining < 65536. + samplesToRead = 0; + } else { + samplesRead += pFlac->currentFrame.samplesRemaining; + samplesToRead -= pFlac->currentFrame.samplesRemaining; + pFlac->currentFrame.samplesRemaining = 0; + } } } + pFlac->currentSample += samplesRead; return samplesRead; } drflac_uint64 drflac_read_s32(drflac* pFlac, drflac_uint64 samplesToRead, drflac_int32* bufferOut) { - // Note that <bufferOut> is allowed to be null, in which case this will be treated as something like a seek. + // Note that <bufferOut> is allowed to be null, in which case this will act like a seek. if (pFlac == NULL || samplesToRead == 0) { return 0; } @@ -5062,10 +5233,11 @@ drflac_uint64 drflac_read_s32(drflac* pFlac, drflac_uint64 samplesToRead, drflac drflac_uint64 misalignedSampleCount = samplesReadFromFrameSoFar % channelCount; if (misalignedSampleCount > 0) { drflac_uint64 misalignedSamplesRead = drflac__read_s32__misaligned(pFlac, misalignedSampleCount, bufferOut); - samplesRead += misalignedSamplesRead; + samplesRead += misalignedSamplesRead; samplesReadFromFrameSoFar += misalignedSamplesRead; - bufferOut += misalignedSamplesRead; - samplesToRead -= misalignedSamplesRead; + bufferOut += misalignedSamplesRead; + samplesToRead -= misalignedSamplesRead; + pFlac->currentSample += misalignedSamplesRead; } @@ -5150,14 +5322,14 @@ drflac_uint64 drflac_read_s32(drflac* pFlac, drflac_uint64 samplesToRead, drflac } drflac_uint64 alignedSamplesRead = alignedSampleCountPerChannel * channelCount; - samplesRead += alignedSamplesRead; + samplesRead += alignedSamplesRead; samplesReadFromFrameSoFar += alignedSamplesRead; - bufferOut += alignedSamplesRead; - samplesToRead -= alignedSamplesRead; + bufferOut += alignedSamplesRead; + samplesToRead -= alignedSamplesRead; + pFlac->currentSample += alignedSamplesRead; pFlac->currentFrame.samplesRemaining -= (unsigned int)alignedSamplesRead; - // At this point we may still have some excess samples left to read. if (samplesToRead > 0 && pFlac->currentFrame.samplesRemaining > 0) { drflac_uint64 excessSamplesRead = 0; @@ -5167,10 +5339,11 @@ drflac_uint64 drflac_read_s32(drflac* pFlac, drflac_uint64 samplesToRead, drflac excessSamplesRead = drflac__read_s32__misaligned(pFlac, pFlac->currentFrame.samplesRemaining, bufferOut); } - samplesRead += excessSamplesRead; + samplesRead += excessSamplesRead; samplesReadFromFrameSoFar += excessSamplesRead; - bufferOut += excessSamplesRead; - samplesToRead -= excessSamplesRead; + bufferOut += excessSamplesRead; + samplesToRead -= excessSamplesRead; + pFlac->currentSample += excessSamplesRead; } } } @@ -5196,8 +5369,8 @@ drflac_uint64 drflac_read_s16(drflac* pFlac, drflac_uint64 samplesToRead, drflac } totalSamplesRead += samplesJustRead; - samplesToRead -= samplesJustRead; - pBufferOut += samplesJustRead; + samplesToRead -= samplesJustRead; + pBufferOut += samplesJustRead; } return totalSamplesRead; @@ -5221,8 +5394,8 @@ drflac_uint64 drflac_read_f32(drflac* pFlac, drflac_uint64 samplesToRead, float* } totalSamplesRead += samplesJustRead; - samplesToRead -= samplesJustRead; - pBufferOut += samplesJustRead; + samplesToRead -= samplesJustRead; + pBufferOut += samplesJustRead; } return totalSamplesRead; @@ -5241,33 +5414,57 @@ drflac_bool32 drflac_seek_to_sample(drflac* pFlac, drflac_uint64 sampleIndex) } if (sampleIndex == 0) { + pFlac->currentSample = 0; return drflac__seek_to_first_frame(pFlac); - } - - // Clamp the sample to the end. - if (sampleIndex >= pFlac->totalSampleCount) { - sampleIndex = pFlac->totalSampleCount - 1; - } + } else { + drflac_bool32 wasSuccessful = DRFLAC_FALSE; + // Clamp the sample to the end. + if (sampleIndex >= pFlac->totalSampleCount) { + sampleIndex = pFlac->totalSampleCount - 1; + } - // Different techniques depending on encapsulation. Using the native FLAC seektable with Ogg encapsulation is a bit awkward so - // we'll instead use Ogg's natural seeking facility. -#ifndef DR_FLAC_NO_OGG - if (pFlac->container == drflac_container_ogg) - { - return drflac_ogg__seek_to_sample(pFlac, sampleIndex); - } - else -#endif - { - // First try seeking via the seek table. If this fails, fall back to a brute force seek which is much slower. - if (!drflac__seek_to_sample__seek_table(pFlac, sampleIndex)) { - return drflac__seek_to_sample__brute_force(pFlac, sampleIndex); + // If the target sample and the current sample are in the same frame we just move the position forward. + if (sampleIndex > pFlac->currentSample) { + // Forward. + drflac_uint32 offset = (drflac_uint32)(sampleIndex - pFlac->currentSample); + if (pFlac->currentFrame.samplesRemaining > offset) { + pFlac->currentFrame.samplesRemaining -= offset; + pFlac->currentSample = sampleIndex; + return DRFLAC_TRUE; + } + } else { + // Backward. + drflac_uint32 offsetAbs = (drflac_uint32)(pFlac->currentSample - sampleIndex); + drflac_uint32 currentFrameSampleCount = pFlac->currentFrame.header.blockSize * drflac__get_channel_count_from_channel_assignment(pFlac->currentFrame.header.channelAssignment); + drflac_uint32 currentFrameSamplesConsumed = (drflac_uint32)(currentFrameSampleCount - pFlac->currentFrame.samplesRemaining); + if (currentFrameSamplesConsumed > offsetAbs) { + pFlac->currentFrame.samplesRemaining += offsetAbs; + pFlac->currentSample = sampleIndex; + return DRFLAC_TRUE; + } } - } + // Different techniques depending on encapsulation. Using the native FLAC seektable with Ogg encapsulation is a bit awkward so + // we'll instead use Ogg's natural seeking facility. + #ifndef DR_FLAC_NO_OGG + if (pFlac->container == drflac_container_ogg) + { + wasSuccessful = drflac_ogg__seek_to_sample(pFlac, sampleIndex); + } + else + #endif + { + // First try seeking via the seek table. If this fails, fall back to a brute force seek which is much slower. + wasSuccessful = drflac__seek_to_sample__seek_table(pFlac, sampleIndex); + if (!wasSuccessful) { + wasSuccessful = drflac__seek_to_sample__brute_force(pFlac, sampleIndex); + } + } - return DRFLAC_TRUE; + pFlac->currentSample = sampleIndex; + return wasSuccessful; + } } @@ -5524,6 +5721,32 @@ const char* drflac_next_vorbis_comment(drflac_vorbis_comment_iterator* pIter, dr // REVISION HISTORY // +// v0.9.7 - 2018-07-05 +// - Fix a warning. +// +// v0.9.6 - 2018-06-29 +// - Fix some typos. +// +// v0.9.5 - 2018-06-23 +// - Fix some warnings. +// +// v0.9.4 - 2018-06-14 +// - Optimizations to seeking. +// - Clean up. +// +// v0.9.3 - 2018-05-22 +// - Bug fix. +// +// v0.9.2 - 2018-05-12 +// - Fix a compilation error due to a missing break statement. +// +// v0.9.1 - 2018-04-29 +// - Fix compilation error with Clang. +// +// v0.9 - 2018-04-24 +// - Fix Clang build. +// - Start using major.minor.revision versioning. +// // v0.8g - 2018-04-19 // - Fix build on non-x86/x64 architectures. // |
