summaryrefslogtreecommitdiffhomepage
path: root/src/external/dr_flac.h
diff options
context:
space:
mode:
authorDavid Reid <[email protected]>2018-07-05 22:57:45 +1000
committerDavid Reid <[email protected]>2018-07-05 22:57:45 +1000
commit6c96fa6301b47826b6a237bcd7353c4f812bc6b2 (patch)
tree47f65a75951a24b15ad13d65fa29a08b76acd807 /src/external/dr_flac.h
parentc598701873988f713d51e749f3a3611ca8253da0 (diff)
downloadraylib-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.h657
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.
//