Commit 7a4816e1 authored by David Reid's avatar David Reid

Update dr_flac.

parent 0b996c0c
// FLAC audio decoder. Public domain. See "unlicense" statement at the end of this file. // FLAC audio decoder. Public domain. See "unlicense" statement at the end of this file.
// dr_flac - v0.9.3 - 2018-05-22 // dr_flac - v0.9.4 - 2018-06-14
// //
// David Reid - mackron@gmail.com // David Reid - mackron@gmail.com
...@@ -482,17 +482,16 @@ typedef struct ...@@ -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. // The container type. This is set based on whether or not the decoder was opened from a native or Ogg stream.
drflac_container container; drflac_container container;
// The number of seekpoints in the seektable.
// The position of the seektable in the file. drflac_uint32 seekpointCount;
drflac_uint64 seektablePos;
// The size of the seektable.
drflac_uint32 seektableSize;
// Information about the frame the decoder is currently sitting on. // Information about the frame the decoder is currently sitting on.
drflac_frame currentFrame; 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. // The position of the first frame in the stream. This is only ever used for seeking.
drflac_uint64 firstFramePos; drflac_uint64 firstFramePos;
...@@ -504,6 +503,9 @@ typedef struct ...@@ -504,6 +503,9 @@ typedef struct
// A pointer to the decoded sample data. This is an offset of pExtraData. // A pointer to the decoded sample data. This is an offset of pExtraData.
drflac_int32* pDecodedSamples; 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. // Internal use only. Only used with Ogg containers. Points to a drflac_oggbs object. This is an offset of pExtraData.
void* _oggbs; void* _oggbs;
...@@ -2287,31 +2289,74 @@ static drflac_bool32 drflac__decode_samples_with_residual__rice__simple(drflac_b ...@@ -2287,31 +2289,74 @@ static drflac_bool32 drflac__decode_samples_with_residual__rice__simple(drflac_b
drflac_assert(count > 0); drflac_assert(count > 0);
drflac_assert(pSamplesOut != NULL); drflac_assert(pSamplesOut != NULL);
drflac_uint32 zeroCountPart; static drflac_uint32 t[2] = {0x00000000, 0xFFFFFFFF};
drflac_uint32 riceParamPart;
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);
riceParamPart0 = (riceParamPart0 >> 1) ^ t[riceParamPart0 & 0x01];
riceParamPart1 = (riceParamPart1 >> 1) ^ t[riceParamPart1 & 0x01];
riceParamPart2 = (riceParamPart2 >> 1) ^ t[riceParamPart2 & 0x01];
riceParamPart3 = (riceParamPart3 >> 1) ^ t[riceParamPart3 & 0x01];
drflac_uint32 i = 0; 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) { while (i < count) {
// Rice extraction. // Rice extraction.
if (!drflac__read_rice_parts(bs, riceParam, &zeroCountPart, &riceParamPart)) { if (!drflac__read_rice_parts(bs, riceParam, &zeroCountPart0, &riceParamPart0)) {
return DRFLAC_FALSE; return DRFLAC_FALSE;
} }
// Rice reconstruction. // Rice reconstruction.
static drflac_uint32 t[2] = {0x00000000, 0xFFFFFFFF}; riceParamPart0 |= (zeroCountPart0 << riceParam);
riceParamPart0 = (riceParamPart0 >> 1) ^ t[riceParamPart0 & 0x01];
riceParamPart |= (zeroCountPart << riceParam); //riceParamPart0 = (riceParamPart0 >> 1) ^ (~(riceParamPart0 & 0x01) + 1);
riceParamPart = (riceParamPart >> 1) ^ t[riceParamPart & 0x01];
//riceParamPart = (riceParamPart >> 1) ^ (~(riceParamPart & 0x01) + 1);
// Sample reconstruction. // Sample reconstruction.
if (bitsPerSample > 16) { 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 { } 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; i += 1;
pSamplesOut += 1;
} }
return DRFLAC_TRUE; return DRFLAC_TRUE;
...@@ -3112,6 +3157,8 @@ static drflac_bool32 drflac__seek_to_first_frame(drflac* pFlac) ...@@ -3112,6 +3157,8 @@ static drflac_bool32 drflac__seek_to_first_frame(drflac* pFlac)
drflac_bool32 result = drflac__seek_to_byte(&pFlac->bs, pFlac->firstFramePos); drflac_bool32 result = drflac__seek_to_byte(&pFlac->bs, pFlac->firstFramePos);
drflac_zero_memory(&pFlac->currentFrame, sizeof(pFlac->currentFrame)); drflac_zero_memory(&pFlac->currentFrame, sizeof(pFlac->currentFrame));
pFlac->currentSample = 0;
return result; return result;
} }
...@@ -3124,18 +3171,42 @@ static DRFLAC_INLINE drflac_result drflac__seek_to_next_frame(drflac* pFlac) ...@@ -3124,18 +3171,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) 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 drflac_assert(pFlac != NULL);
// 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_uint64 runningSampleCount = 0; drflac_bool32 isMidFrame = DRFLAC_FALSE;
for (;;) {
// If we are seeking foward 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 foward. 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)) { if (!drflac__read_next_frame_header(&pFlac->bs, pFlac->bitsPerSample, &pFlac->currentFrame.header)) {
return DRFLAC_FALSE; 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 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.
for (;;) {
drflac_uint64 firstSampleInFrame = 0; drflac_uint64 firstSampleInFrame = 0;
drflac_uint64 lastSampleInFrame = 0; drflac_uint64 lastSampleInFrame = 0;
drflac__get_current_frame_sample_range(pFlac, &firstSampleInFrame, &lastSampleInFrame); drflac__get_current_frame_sample_range(pFlac, &firstSampleInFrame, &lastSampleInFrame);
...@@ -3144,35 +3215,52 @@ static drflac_bool32 drflac__seek_to_sample__brute_force(drflac* pFlac, drflac_u ...@@ -3144,35 +3215,52 @@ static drflac_bool32 drflac__seek_to_sample__brute_force(drflac* pFlac, drflac_u
if (sampleIndex < (runningSampleCount + sampleCountInThisFrame)) { 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, however if it's an invalid frame (a CRC mismatch), we need to pretend
// it never existed and keep iterating. // it never existed and keep iterating.
drflac_result result = drflac__decode_frame(pFlac); drflac_uint64 samplesToDecode = sampleIndex - runningSampleCount;
if (result == DRFLAC_SUCCESS) {
// The frame is valid. We just need to skip over some samples to ensure it's sample-exact. if (!isMidFrame) {
drflac_uint64 samplesToDecode = (size_t)(sampleIndex - runningSampleCount); // <-- Safe cast because the maximum number of samples in a frame is 65535. drflac_result result = drflac__decode_frame(pFlac);
if (samplesToDecode == 0) { if (result == DRFLAC_SUCCESS) {
return DRFLAC_TRUE; // 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).
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.
} else { } 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 { } 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 // 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. // frame never existed and leave the running sample count untouched.
drflac_result result = drflac__seek_to_next_frame(pFlac); if (!isMidFrame) {
if (result == DRFLAC_SUCCESS) { drflac_result result = drflac__seek_to_next_frame(pFlac);
runningSampleCount += sampleCountInThisFrame; if (result == DRFLAC_SUCCESS) {
} else { runningSampleCount += sampleCountInThisFrame;
if (result == DRFLAC_CRC_MISMATCH) {
continue; // CRC mismatch. Pretend this frame never existed.
} else { } 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;
}
} }
} }
...@@ -3181,95 +3269,107 @@ static drflac_bool32 drflac__seek_to_sample__seek_table(drflac* pFlac, drflac_ui ...@@ -3181,95 +3269,107 @@ static drflac_bool32 drflac__seek_to_sample__seek_table(drflac* pFlac, drflac_ui
{ {
drflac_assert(pFlac != NULL); drflac_assert(pFlac != NULL);
if (pFlac->seektablePos == 0) { if (pFlac->pSeekpoints == NULL || pFlac->seekpointCount == 0) {
return DRFLAC_FALSE; 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 iClosestSeekpoint = 0;
drflac_uint32 seekpointCount = pFlac->seektableSize / 18; // 18 = the size of each seek point. for (drflac_uint32 iSeekpoint = 0; iSeekpoint < pFlac->seekpointCount; ++iSeekpoint) {
if (seekpointCount == 0) { if (pFlac->pSeekpoints[iSeekpoint].firstSample*pFlac->channels >= sampleIndex) {
return DRFLAC_FALSE; // Would this ever happen? break;
}
iClosestSeekpoint = iSeekpoint;
} }
drflac_seekpoint closestSeekpoint = {0, 0, 0}; drflac_bool32 isMidFrame = DRFLAC_FALSE;
drflac_uint32 seekpointsRemaining = seekpointCount; // 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
while (seekpointsRemaining > 0) { // just seek forward from where we are. Otherwise we start seeking from the seekpoint's first sample.
drflac_seekpoint seekpoint; drflac_uint64 runningSampleCount;
if (!drflac__read_uint64(&pFlac->bs, 64, &seekpoint.firstSample)) { if ((sampleIndex >= pFlac->currentSample) && (pFlac->pSeekpoints[iClosestSeekpoint].firstSample*pFlac->channels <= pFlac->currentSample)) {
break; // Optimized case. Just seek forward from where we are.
} runningSampleCount = pFlac->currentSample;
if (!drflac__read_uint64(&pFlac->bs, 64, &seekpoint.frameOffset)) {
break;
}
if (!drflac__read_uint16(&pFlac->bs, 16, &seekpoint.sampleCount)) {
break;
}
// Note that the seekpoint sample is based on a single channel. The input sample (sampleIndex) is based on interleaving, thus // The frame header for the first frame may not yet have been read. We need to do that if necessary.
// we need to multiple the seekpoint's sample by the channel count. if (pFlac->currentSample == 0 && pFlac->currentFrame.samplesRemaining == 0) {
if (seekpoint.firstSample*pFlac->channels > sampleIndex) { if (!drflac__read_next_frame_header(&pFlac->bs, pFlac->bitsPerSample, &pFlac->currentFrame.header)) {
break; 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; if (!drflac__seek_to_byte(&pFlac->bs, pFlac->firstFramePos + pFlac->pSeekpoints[iClosestSeekpoint].frameOffset)) {
seekpointsRemaining -= 1; return DRFLAC_FALSE;
} }
// 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;
}
drflac_uint64 runningSampleCount = closestSeekpoint.firstSample*pFlac->channels; // Grab the frame the seekpoint is sitting on in preparation for the sample-exact seeking below.
for (;;) {
if (!drflac__read_next_frame_header(&pFlac->bs, pFlac->bitsPerSample, &pFlac->currentFrame.header)) { if (!drflac__read_next_frame_header(&pFlac->bs, pFlac->bitsPerSample, &pFlac->currentFrame.header)) {
return DRFLAC_FALSE; return DRFLAC_FALSE;
} }
}
for (;;) {
drflac_uint64 firstSampleInFrame = 0; drflac_uint64 firstSampleInFrame = 0;
drflac_uint64 lastSampleInFrame = 0; drflac_uint64 lastSampleInFrame = 0;
drflac__get_current_frame_sample_range(pFlac, &firstSampleInFrame, &lastSampleInFrame); drflac__get_current_frame_sample_range(pFlac, &firstSampleInFrame, &lastSampleInFrame);
drflac_uint64 sampleCountInThisFrame = (lastSampleInFrame - firstSampleInFrame) + 1; drflac_uint64 sampleCountInThisFrame = (lastSampleInFrame - firstSampleInFrame) + 1;
if (sampleIndex < (runningSampleCount + sampleCountInThisFrame)) { 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. // it never existed and keep iterating.
drflac_result result = drflac__decode_frame(pFlac); drflac_uint64 samplesToDecode = sampleIndex - runningSampleCount;
if (result == DRFLAC_SUCCESS) {
// The frame is valid. We just need to skip over some samples to ensure it's sample-exact. if (!isMidFrame) {
drflac_uint64 samplesToDecode = (size_t)(sampleIndex - runningSampleCount); // <-- Safe cast because the maximum number of samples in a frame is 65535. drflac_result result = drflac__decode_frame(pFlac);
if (samplesToDecode == 0) { if (result == DRFLAC_SUCCESS) {
return DRFLAC_TRUE; // 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).
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.
} else { } 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 { } 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 // 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. // frame never existed and leave the running sample count untouched.
drflac_result result = drflac__seek_to_next_frame(pFlac); if (!isMidFrame) {
if (result == DRFLAC_SUCCESS) { drflac_result result = drflac__seek_to_next_frame(pFlac);
runningSampleCount += sampleCountInThisFrame; if (result == DRFLAC_SUCCESS) {
} else { runningSampleCount += sampleCountInThisFrame;
if (result == DRFLAC_CRC_MISMATCH) {
continue; // CRC mismatch. Pretend this frame never existed.
} else { } 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;
}
} }
} }
...@@ -3377,10 +3477,8 @@ drflac_bool32 drflac__read_streaminfo(drflac_read_proc onRead, void* pUserData, ...@@ -3377,10 +3477,8 @@ drflac_bool32 drflac__read_streaminfo(drflac_read_proc onRead, void* pUserData,
return DRFLAC_TRUE; 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 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. // we'll be sitting on byte 42.
drflac_uint64 runningFilePos = 42; drflac_uint64 runningFilePos = 42;
...@@ -3391,7 +3489,7 @@ drflac_bool32 drflac__read_and_decode_metadata(drflac* pFlac) ...@@ -3391,7 +3489,7 @@ drflac_bool32 drflac__read_and_decode_metadata(drflac* pFlac)
drflac_uint8 isLastBlock = 0; drflac_uint8 isLastBlock = 0;
drflac_uint8 blockType; drflac_uint8 blockType;
drflac_uint32 blockSize; 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; return DRFLAC_FALSE;
} }
runningFilePos += 4; runningFilePos += 4;
...@@ -3406,13 +3504,13 @@ drflac_bool32 drflac__read_and_decode_metadata(drflac* pFlac) ...@@ -3406,13 +3504,13 @@ drflac_bool32 drflac__read_and_decode_metadata(drflac* pFlac)
{ {
case DRFLAC_METADATA_BLOCK_TYPE_APPLICATION: case DRFLAC_METADATA_BLOCK_TYPE_APPLICATION:
{ {
if (pFlac->onMeta) { if (onMeta) {
void* pRawData = DRFLAC_MALLOC(blockSize); void* pRawData = DRFLAC_MALLOC(blockSize);
if (pRawData == NULL) { if (pRawData == NULL) {
return DRFLAC_FALSE; return DRFLAC_FALSE;
} }
if (pFlac->bs.onRead(pFlac->bs.pUserData, pRawData, blockSize) != blockSize) { if (onRead(pUserData, pRawData, blockSize) != blockSize) {
DRFLAC_FREE(pRawData); DRFLAC_FREE(pRawData);
return DRFLAC_FALSE; return DRFLAC_FALSE;
} }
...@@ -3422,7 +3520,7 @@ drflac_bool32 drflac__read_and_decode_metadata(drflac* pFlac) ...@@ -3422,7 +3520,7 @@ drflac_bool32 drflac__read_and_decode_metadata(drflac* pFlac)
metadata.data.application.id = drflac__be2host_32(*(drflac_uint32*)pRawData); 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.pData = (const void*)((drflac_uint8*)pRawData + sizeof(drflac_uint32));
metadata.data.application.dataSize = blockSize - sizeof(drflac_uint32); metadata.data.application.dataSize = blockSize - sizeof(drflac_uint32);
pFlac->onMeta(pFlac->pUserDataMD, &metadata); onMeta(pUserDataMD, &metadata);
DRFLAC_FREE(pRawData); DRFLAC_FREE(pRawData);
} }
...@@ -3433,13 +3531,13 @@ drflac_bool32 drflac__read_and_decode_metadata(drflac* pFlac) ...@@ -3433,13 +3531,13 @@ drflac_bool32 drflac__read_and_decode_metadata(drflac* pFlac)
seektablePos = runningFilePos; seektablePos = runningFilePos;
seektableSize = blockSize; seektableSize = blockSize;
if (pFlac->onMeta) { if (onMeta) {
void* pRawData = DRFLAC_MALLOC(blockSize); void* pRawData = DRFLAC_MALLOC(blockSize);
if (pRawData == NULL) { if (pRawData == NULL) {
return DRFLAC_FALSE; return DRFLAC_FALSE;
} }
if (pFlac->bs.onRead(pFlac->bs.pUserData, pRawData, blockSize) != blockSize) { if (onRead(pUserData, pRawData, blockSize) != blockSize) {
DRFLAC_FREE(pRawData); DRFLAC_FREE(pRawData);
return DRFLAC_FALSE; return DRFLAC_FALSE;
} }
...@@ -3457,7 +3555,7 @@ drflac_bool32 drflac__read_and_decode_metadata(drflac* pFlac) ...@@ -3457,7 +3555,7 @@ drflac_bool32 drflac__read_and_decode_metadata(drflac* pFlac)
pSeekpoint->sampleCount = drflac__be2host_16(pSeekpoint->sampleCount); pSeekpoint->sampleCount = drflac__be2host_16(pSeekpoint->sampleCount);
} }
pFlac->onMeta(pFlac->pUserDataMD, &metadata); onMeta(pUserDataMD, &metadata);
DRFLAC_FREE(pRawData); DRFLAC_FREE(pRawData);
} }
...@@ -3465,13 +3563,13 @@ drflac_bool32 drflac__read_and_decode_metadata(drflac* pFlac) ...@@ -3465,13 +3563,13 @@ drflac_bool32 drflac__read_and_decode_metadata(drflac* pFlac)
case DRFLAC_METADATA_BLOCK_TYPE_VORBIS_COMMENT: case DRFLAC_METADATA_BLOCK_TYPE_VORBIS_COMMENT:
{ {
if (pFlac->onMeta) { if (onMeta) {
void* pRawData = DRFLAC_MALLOC(blockSize); void* pRawData = DRFLAC_MALLOC(blockSize);
if (pRawData == NULL) { if (pRawData == NULL) {
return DRFLAC_FALSE; return DRFLAC_FALSE;
} }
if (pFlac->bs.onRead(pFlac->bs.pUserData, pRawData, blockSize) != blockSize) { if (onRead(pUserData, pRawData, blockSize) != blockSize) {
DRFLAC_FREE(pRawData); DRFLAC_FREE(pRawData);
return DRFLAC_FALSE; return DRFLAC_FALSE;
} }
...@@ -3484,7 +3582,7 @@ drflac_bool32 drflac__read_and_decode_metadata(drflac* pFlac) ...@@ -3484,7 +3582,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.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.commentCount = drflac__le2host_32(*(drflac_uint32*)pRunningData); pRunningData += 4;
metadata.data.vorbis_comment.comments = pRunningData; metadata.data.vorbis_comment.comments = pRunningData;
pFlac->onMeta(pFlac->pUserDataMD, &metadata); onMeta(pUserDataMD, &metadata);
DRFLAC_FREE(pRawData); DRFLAC_FREE(pRawData);
} }
...@@ -3492,13 +3590,13 @@ drflac_bool32 drflac__read_and_decode_metadata(drflac* pFlac) ...@@ -3492,13 +3590,13 @@ drflac_bool32 drflac__read_and_decode_metadata(drflac* pFlac)
case DRFLAC_METADATA_BLOCK_TYPE_CUESHEET: case DRFLAC_METADATA_BLOCK_TYPE_CUESHEET:
{ {
if (pFlac->onMeta) { if (onMeta) {
void* pRawData = DRFLAC_MALLOC(blockSize); void* pRawData = DRFLAC_MALLOC(blockSize);
if (pRawData == NULL) { if (pRawData == NULL) {
return DRFLAC_FALSE; return DRFLAC_FALSE;
} }
if (pFlac->bs.onRead(pFlac->bs.pUserData, pRawData, blockSize) != blockSize) { if (onRead(pUserData, pRawData, blockSize) != blockSize) {
DRFLAC_FREE(pRawData); DRFLAC_FREE(pRawData);
return DRFLAC_FALSE; return DRFLAC_FALSE;
} }
...@@ -3512,7 +3610,7 @@ drflac_bool32 drflac__read_and_decode_metadata(drflac* pFlac) ...@@ -3512,7 +3610,7 @@ drflac_bool32 drflac__read_and_decode_metadata(drflac* pFlac)
metadata.data.cuesheet.isCD = ((pRunningData[0] & 0x80) >> 7) != 0; pRunningData += 259; metadata.data.cuesheet.isCD = ((pRunningData[0] & 0x80) >> 7) != 0; pRunningData += 259;
metadata.data.cuesheet.trackCount = pRunningData[0]; pRunningData += 1; metadata.data.cuesheet.trackCount = pRunningData[0]; pRunningData += 1;
metadata.data.cuesheet.pTrackData = (const drflac_uint8*)pRunningData; metadata.data.cuesheet.pTrackData = (const drflac_uint8*)pRunningData;
pFlac->onMeta(pFlac->pUserDataMD, &metadata); onMeta(pUserDataMD, &metadata);
DRFLAC_FREE(pRawData); DRFLAC_FREE(pRawData);
} }
...@@ -3520,13 +3618,13 @@ drflac_bool32 drflac__read_and_decode_metadata(drflac* pFlac) ...@@ -3520,13 +3618,13 @@ drflac_bool32 drflac__read_and_decode_metadata(drflac* pFlac)
case DRFLAC_METADATA_BLOCK_TYPE_PICTURE: case DRFLAC_METADATA_BLOCK_TYPE_PICTURE:
{ {
if (pFlac->onMeta) { if (onMeta) {
void* pRawData = DRFLAC_MALLOC(blockSize); void* pRawData = DRFLAC_MALLOC(blockSize);
if (pRawData == NULL) { if (pRawData == NULL) {
return DRFLAC_FALSE; return DRFLAC_FALSE;
} }
if (pFlac->bs.onRead(pFlac->bs.pUserData, pRawData, blockSize) != blockSize) { if (onRead(pUserData, pRawData, blockSize) != blockSize) {
DRFLAC_FREE(pRawData); DRFLAC_FREE(pRawData);
return DRFLAC_FALSE; return DRFLAC_FALSE;
} }
...@@ -3546,7 +3644,7 @@ drflac_bool32 drflac__read_and_decode_metadata(drflac* pFlac) ...@@ -3546,7 +3644,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.indexColorCount = drflac__be2host_32(*(drflac_uint32*)pRunningData); pRunningData += 4;
metadata.data.picture.pictureDataSize = 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; metadata.data.picture.pPictureData = (const drflac_uint8*)pRunningData;
pFlac->onMeta(pFlac->pUserDataMD, &metadata); onMeta(pUserDataMD, &metadata);
DRFLAC_FREE(pRawData); DRFLAC_FREE(pRawData);
} }
...@@ -3554,14 +3652,14 @@ drflac_bool32 drflac__read_and_decode_metadata(drflac* pFlac) ...@@ -3554,14 +3652,14 @@ drflac_bool32 drflac__read_and_decode_metadata(drflac* pFlac)
case DRFLAC_METADATA_BLOCK_TYPE_PADDING: case DRFLAC_METADATA_BLOCK_TYPE_PADDING:
{ {
if (pFlac->onMeta) { if (onMeta) {
metadata.data.padding.unused = 0; 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. // 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)) { if (!onSeek(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. 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.
} else { } else {
pFlac->onMeta(pFlac->pUserDataMD, &metadata); onMeta(pUserDataMD, &metadata);
} }
} }
} break; } break;
...@@ -3569,8 +3667,8 @@ drflac_bool32 drflac__read_and_decode_metadata(drflac* pFlac) ...@@ -3569,8 +3667,8 @@ drflac_bool32 drflac__read_and_decode_metadata(drflac* pFlac)
case DRFLAC_METADATA_BLOCK_TYPE_INVALID: case DRFLAC_METADATA_BLOCK_TYPE_INVALID:
{ {
// Invalid chunk. Just skip over this one. // Invalid chunk. Just skip over this one.
if (pFlac->onMeta) { if (onMeta) {
if (!pFlac->bs.onSeek(pFlac->bs.pUserData, blockSize, drflac_seek_origin_current)) { if (!onSeek(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. 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.
} }
} }
...@@ -3580,20 +3678,20 @@ drflac_bool32 drflac__read_and_decode_metadata(drflac* pFlac) ...@@ -3580,20 +3678,20 @@ drflac_bool32 drflac__read_and_decode_metadata(drflac* pFlac)
{ {
// It's an unknown chunk, but not necessarily invalid. There's a chance more metadata blocks might be defined later on, so we // 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. // 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); void* pRawData = DRFLAC_MALLOC(blockSize);
if (pRawData == NULL) { if (pRawData == NULL) {
return DRFLAC_FALSE; return DRFLAC_FALSE;
} }
if (pFlac->bs.onRead(pFlac->bs.pUserData, pRawData, blockSize) != blockSize) { if (onRead(pUserData, pRawData, blockSize) != blockSize) {
DRFLAC_FREE(pRawData); DRFLAC_FREE(pRawData);
return DRFLAC_FALSE; return DRFLAC_FALSE;
} }
metadata.pRawData = pRawData; metadata.pRawData = pRawData;
metadata.rawDataSize = blockSize; metadata.rawDataSize = blockSize;
pFlac->onMeta(pFlac->pUserDataMD, &metadata); onMeta(pUserDataMD, &metadata);
DRFLAC_FREE(pRawData); DRFLAC_FREE(pRawData);
} }
...@@ -3601,8 +3699,8 @@ drflac_bool32 drflac__read_and_decode_metadata(drflac* pFlac) ...@@ -3601,8 +3699,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 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 (onMeta == NULL && blockSize > 0) {
if (!pFlac->bs.onSeek(pFlac->bs.pUserData, blockSize, drflac_seek_origin_current)) { if (!onSeek(pUserData, blockSize, drflac_seek_origin_current)) {
isLastBlock = DRFLAC_TRUE; isLastBlock = DRFLAC_TRUE;
} }
} }
...@@ -3613,9 +3711,9 @@ drflac_bool32 drflac__read_and_decode_metadata(drflac* pFlac) ...@@ -3613,9 +3711,9 @@ drflac_bool32 drflac__read_and_decode_metadata(drflac* pFlac)
} }
} }
pFlac->seektablePos = seektablePos; *pSeektablePos = seektablePos;
pFlac->seektableSize = seektableSize; *pSeektableSize = seektableSize;
pFlac->firstFramePos = runningFilePos; *pFirstFramePos = runningFilePos;
return DRFLAC_TRUE; return DRFLAC_TRUE;
} }
...@@ -4012,6 +4110,8 @@ static drflac_bool32 drflac_oggbs__goto_next_page(drflac_oggbs* oggbs, drflac_og ...@@ -4012,6 +4110,8 @@ static drflac_bool32 drflac_oggbs__goto_next_page(drflac_oggbs* oggbs, drflac_og
return DRFLAC_FALSE; return DRFLAC_FALSE;
} }
} }
#else
(void)recoveryMethod; // <-- Silence a warning.
#endif #endif
oggbs->currentPageHeader = header; oggbs->currentPageHeader = header;
...@@ -4592,43 +4692,116 @@ drflac* drflac_open_with_metadata_private(drflac_read_proc onRead, drflac_seek_p ...@@ -4592,43 +4692,116 @@ drflac* drflac_open_with_metadata_private(drflac_read_proc onRead, drflac_seek_p
#ifndef DR_FLAC_NO_OGG #ifndef DR_FLAC_NO_OGG
// There's additional data required for Ogg streams. // There's additional data required for Ogg streams.
drflac_uint32 oggbsAllocationSize = 0;
if (init.container == drflac_container_ogg) {
oggbsAllocationSize = sizeof(drflac_oggbs);
allocationSize += oggbsAllocationSize;
}
drflac_oggbs oggbs;
if (init.container == drflac_container_ogg) { if (init.container == drflac_container_ogg) {
allocationSize += sizeof(drflac_oggbs); drflac_zero_memory(&oggbs, sizeof(oggbs));
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 #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* pFlac = (drflac*)DRFLAC_MALLOC(allocationSize);
drflac__init_from_info(pFlac, &init); drflac__init_from_info(pFlac, &init);
pFlac->pDecodedSamples = (drflac_int32*)drflac_align((size_t)pFlac->pExtraData, DRFLAC_MAX_SIMD_VECTOR_SIZE); pFlac->pDecodedSamples = (drflac_int32*)drflac_align((size_t)pFlac->pExtraData, DRFLAC_MAX_SIMD_VECTOR_SIZE);
#ifndef DR_FLAC_NO_OGG #ifndef DR_FLAC_NO_OGG
if (init.container == drflac_container_ogg) { if (init.container == drflac_container_ogg) {
drflac_oggbs* oggbs = (drflac_oggbs*)((drflac_uint8*)pFlac->pDecodedSamples + decodedSamplesAllocationSize); drflac_oggbs* pInternalOggbs = (drflac_oggbs*)((drflac_uint8*)pFlac->pDecodedSamples + decodedSamplesAllocationSize + seektableSize);
oggbs->onRead = onRead; *pInternalOggbs = oggbs;
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;
// The Ogg bistream needs to be layered on top of the original bitstream. // The Ogg bistream needs to be layered on top of the original bitstream.
pFlac->bs.onRead = drflac__on_read_ogg; pFlac->bs.onRead = drflac__on_read_ogg;
pFlac->bs.onSeek = drflac__on_seek_ogg; pFlac->bs.onSeek = drflac__on_seek_ogg;
pFlac->bs.pUserData = (void*)oggbs; pFlac->bs.pUserData = (void*)pInternalOggbs;
pFlac->_oggbs = (void*)oggbs; pFlac->_oggbs = (void*)pInternalOggbs;
} }
#endif #endif
// Decode metadata before returning. pFlac->firstFramePos = firstFramePos;
if (init.hasMetadataBlocks) {
if (!drflac__read_and_decode_metadata(pFlac)) { // NOTE: Seektables are not currently compatible with Ogg encapsulation (Ogg has it's own accelerated seeking system). I may change this later, so I'm leaving this here for now.
DRFLAC_FREE(pFlac); #ifndef DR_FLAC_NO_OGG
return NULL; 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 // 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. // the first frame.
if (!init.hasStreamInfoBlock) { if (!init.hasStreamInfoBlock) {
...@@ -4796,6 +4969,7 @@ static drflac_bool32 drflac__on_seek_memory(void* pUserData, int offset, drflac_ ...@@ -4796,6 +4969,7 @@ static drflac_bool32 drflac__on_seek_memory(void* pUserData, int offset, drflac_
drflac__memory_stream* memoryStream = (drflac__memory_stream*)pUserData; drflac__memory_stream* memoryStream = (drflac__memory_stream*)pUserData;
drflac_assert(memoryStream != NULL); drflac_assert(memoryStream != NULL);
drflac_assert(offset > 0 || (offset == 0 && origin == drflac_seek_origin_start)); 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 (origin == drflac_seek_origin_current) {
if (memoryStream->currentReadPos + offset <= memoryStream->dataSize) { if (memoryStream->currentReadPos + offset <= memoryStream->dataSize) {
...@@ -5012,18 +5186,25 @@ drflac_uint64 drflac__seek_forward_by_samples(drflac* pFlac, drflac_uint64 sampl ...@@ -5012,18 +5186,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. break; // Couldn't read the next frame, so just break from the loop and return.
} }
} else { } else {
samplesRead += 1; if (pFlac->currentFrame.samplesRemaining > samplesToRead) {
pFlac->currentFrame.samplesRemaining -= 1; samplesRead += samplesToRead;
samplesToRead -= 1; 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; return samplesRead;
} }
drflac_uint64 drflac_read_s32(drflac* pFlac, drflac_uint64 samplesToRead, drflac_int32* bufferOut) 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) { if (pFlac == NULL || samplesToRead == 0) {
return 0; return 0;
} }
...@@ -5050,10 +5231,11 @@ drflac_uint64 drflac_read_s32(drflac* pFlac, drflac_uint64 samplesToRead, drflac ...@@ -5050,10 +5231,11 @@ drflac_uint64 drflac_read_s32(drflac* pFlac, drflac_uint64 samplesToRead, drflac
drflac_uint64 misalignedSampleCount = samplesReadFromFrameSoFar % channelCount; drflac_uint64 misalignedSampleCount = samplesReadFromFrameSoFar % channelCount;
if (misalignedSampleCount > 0) { if (misalignedSampleCount > 0) {
drflac_uint64 misalignedSamplesRead = drflac__read_s32__misaligned(pFlac, misalignedSampleCount, bufferOut); drflac_uint64 misalignedSamplesRead = drflac__read_s32__misaligned(pFlac, misalignedSampleCount, bufferOut);
samplesRead += misalignedSamplesRead; samplesRead += misalignedSamplesRead;
samplesReadFromFrameSoFar += misalignedSamplesRead; samplesReadFromFrameSoFar += misalignedSamplesRead;
bufferOut += misalignedSamplesRead; bufferOut += misalignedSamplesRead;
samplesToRead -= misalignedSamplesRead; samplesToRead -= misalignedSamplesRead;
pFlac->currentSample += misalignedSamplesRead;
} }
...@@ -5138,14 +5320,14 @@ drflac_uint64 drflac_read_s32(drflac* pFlac, drflac_uint64 samplesToRead, drflac ...@@ -5138,14 +5320,14 @@ drflac_uint64 drflac_read_s32(drflac* pFlac, drflac_uint64 samplesToRead, drflac
} }
drflac_uint64 alignedSamplesRead = alignedSampleCountPerChannel * channelCount; drflac_uint64 alignedSamplesRead = alignedSampleCountPerChannel * channelCount;
samplesRead += alignedSamplesRead; samplesRead += alignedSamplesRead;
samplesReadFromFrameSoFar += alignedSamplesRead; samplesReadFromFrameSoFar += alignedSamplesRead;
bufferOut += alignedSamplesRead; bufferOut += alignedSamplesRead;
samplesToRead -= alignedSamplesRead; samplesToRead -= alignedSamplesRead;
pFlac->currentSample += alignedSamplesRead;
pFlac->currentFrame.samplesRemaining -= (unsigned int)alignedSamplesRead; pFlac->currentFrame.samplesRemaining -= (unsigned int)alignedSamplesRead;
// At this point we may still have some excess samples left to read. // At this point we may still have some excess samples left to read.
if (samplesToRead > 0 && pFlac->currentFrame.samplesRemaining > 0) { if (samplesToRead > 0 && pFlac->currentFrame.samplesRemaining > 0) {
drflac_uint64 excessSamplesRead = 0; drflac_uint64 excessSamplesRead = 0;
...@@ -5155,10 +5337,11 @@ drflac_uint64 drflac_read_s32(drflac* pFlac, drflac_uint64 samplesToRead, drflac ...@@ -5155,10 +5337,11 @@ drflac_uint64 drflac_read_s32(drflac* pFlac, drflac_uint64 samplesToRead, drflac
excessSamplesRead = drflac__read_s32__misaligned(pFlac, pFlac->currentFrame.samplesRemaining, bufferOut); excessSamplesRead = drflac__read_s32__misaligned(pFlac, pFlac->currentFrame.samplesRemaining, bufferOut);
} }
samplesRead += excessSamplesRead; samplesRead += excessSamplesRead;
samplesReadFromFrameSoFar += excessSamplesRead; samplesReadFromFrameSoFar += excessSamplesRead;
bufferOut += excessSamplesRead; bufferOut += excessSamplesRead;
samplesToRead -= excessSamplesRead; samplesToRead -= excessSamplesRead;
pFlac->currentSample += excessSamplesRead;
} }
} }
} }
...@@ -5184,8 +5367,8 @@ drflac_uint64 drflac_read_s16(drflac* pFlac, drflac_uint64 samplesToRead, drflac ...@@ -5184,8 +5367,8 @@ drflac_uint64 drflac_read_s16(drflac* pFlac, drflac_uint64 samplesToRead, drflac
} }
totalSamplesRead += samplesJustRead; totalSamplesRead += samplesJustRead;
samplesToRead -= samplesJustRead; samplesToRead -= samplesJustRead;
pBufferOut += samplesJustRead; pBufferOut += samplesJustRead;
} }
return totalSamplesRead; return totalSamplesRead;
...@@ -5209,8 +5392,8 @@ drflac_uint64 drflac_read_f32(drflac* pFlac, drflac_uint64 samplesToRead, float* ...@@ -5209,8 +5392,8 @@ drflac_uint64 drflac_read_f32(drflac* pFlac, drflac_uint64 samplesToRead, float*
} }
totalSamplesRead += samplesJustRead; totalSamplesRead += samplesJustRead;
samplesToRead -= samplesJustRead; samplesToRead -= samplesJustRead;
pBufferOut += samplesJustRead; pBufferOut += samplesJustRead;
} }
return totalSamplesRead; return totalSamplesRead;
...@@ -5229,33 +5412,57 @@ drflac_bool32 drflac_seek_to_sample(drflac* pFlac, drflac_uint64 sampleIndex) ...@@ -5229,33 +5412,57 @@ drflac_bool32 drflac_seek_to_sample(drflac* pFlac, drflac_uint64 sampleIndex)
} }
if (sampleIndex == 0) { if (sampleIndex == 0) {
pFlac->currentSample = 0;
return drflac__seek_to_first_frame(pFlac); return drflac__seek_to_first_frame(pFlac);
} } else {
drflac_bool32 wasSuccessful = DRFLAC_FALSE;
// Clamp the sample to the end.
if (sampleIndex >= pFlac->totalSampleCount) {
sampleIndex = pFlac->totalSampleCount - 1;
}
// 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 // If the target sample and the current sample are in the same frame we just move the position forward.
// we'll instead use Ogg's natural seeking facility. if (sampleIndex > pFlac->currentSample) {
#ifndef DR_FLAC_NO_OGG // Forward.
if (pFlac->container == drflac_container_ogg) drflac_uint32 offset = (drflac_uint32)(sampleIndex - pFlac->currentSample);
{ if (pFlac->currentFrame.samplesRemaining > offset) {
return drflac_ogg__seek_to_sample(pFlac, sampleIndex); pFlac->currentFrame.samplesRemaining -= offset;
} pFlac->currentSample = sampleIndex;
else return DRFLAC_TRUE;
#endif }
{ } else {
// First try seeking via the seek table. If this fails, fall back to a brute force seek which is much slower. // Backward.
if (!drflac__seek_to_sample__seek_table(pFlac, sampleIndex)) { drflac_uint32 offsetAbs = (drflac_uint32)(pFlac->currentSample - sampleIndex);
return drflac__seek_to_sample__brute_force(pFlac, 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;
}
} }
...@@ -5512,6 +5719,10 @@ const char* drflac_next_vorbis_comment(drflac_vorbis_comment_iterator* pIter, dr ...@@ -5512,6 +5719,10 @@ const char* drflac_next_vorbis_comment(drflac_vorbis_comment_iterator* pIter, dr
// REVISION HISTORY // REVISION HISTORY
// //
// v0.9.4 - 2018-06-14
// - Optimizations to seeking.
// - Clean up.
//
// v0.9.3 - 2018-05-22 // v0.9.3 - 2018-05-22
// - Bug fix. // - Bug fix.
// //
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment