-
Notifications
You must be signed in to change notification settings - Fork 1.7k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Addressed Seeking Quirks in FLAC Reader #1162
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -107,10 +107,6 @@ namespace | |
{ | ||
sf::priv::SoundFileReaderFlac::ClientData* data = static_cast<sf::priv::SoundFileReaderFlac::ClientData*>(clientData); | ||
|
||
// If there's no output buffer, it means that we are seeking | ||
if (!data->buffer) | ||
return FLAC__STREAM_DECODER_WRITE_STATUS_CONTINUE; | ||
|
||
// Reserve memory if we're going to use the leftovers buffer | ||
unsigned int frameSamples = frame->header.blocksize * frame->header.channels; | ||
if (data->remaining < frameSamples) | ||
|
@@ -142,15 +138,16 @@ namespace | |
break; | ||
} | ||
|
||
if (data->remaining > 0) | ||
if (data->buffer && data->remaining > 0) | ||
{ | ||
// There's room in the output buffer, copy the sample there | ||
// If there's room in the output buffer, copy the sample there | ||
*data->buffer++ = sample; | ||
data->remaining--; | ||
} | ||
else | ||
{ | ||
// We have decoded all the requested samples, put the sample in a temporary buffer until next call | ||
// We are either seeking (null buffer) or have decoded all the requested samples during a | ||
// normal read (0 remaining), so we put the sample in a temporary buffer until next call | ||
data->leftovers.push_back(sample); | ||
} | ||
} | ||
|
@@ -210,8 +207,7 @@ bool SoundFileReaderFlac::check(InputStream& stream) | |
//////////////////////////////////////////////////////////// | ||
SoundFileReaderFlac::SoundFileReaderFlac() : | ||
m_decoder(NULL), | ||
m_clientData(), | ||
m_channelCount(0) | ||
m_clientData() | ||
{ | ||
} | ||
|
||
|
@@ -249,9 +245,6 @@ bool SoundFileReaderFlac::open(InputStream& stream, Info& info) | |
// Retrieve the sound properties | ||
info = m_clientData.info; // was filled in the "metadata" callback | ||
|
||
// We must keep the channel count for the seek function | ||
m_channelCount = info.channelCount; | ||
|
||
return true; | ||
} | ||
|
||
|
@@ -267,7 +260,21 @@ void SoundFileReaderFlac::seek(Uint64 sampleOffset) | |
m_clientData.leftovers.clear(); | ||
|
||
// FLAC decoder expects absolute sample offset, so we take the channel count out | ||
FLAC__stream_decoder_seek_absolute(m_decoder, sampleOffset / m_channelCount); | ||
if (sampleOffset < m_clientData.info.sampleCount) | ||
{ | ||
// The "write" callback will populate the leftovers buffer with the first batch of samples from the | ||
// seek destination, and since we want that data in this typical case, we don't re-clear it afterward | ||
FLAC__stream_decoder_seek_absolute(m_decoder, sampleOffset / m_clientData.info.channelCount); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Slight improvement: I'd compute and store (and comment) There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Is this purely for readability? The result of that division op is only used once in the if case and once in the mutually-exclusive else case. It's never repeated in any single call to There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Of course it is for maintainability reasons, not for optimizing anything. If the expression ever has to change in the future, there will only be one place to modify, not two. Duplicated code, as small as it is, is never a good thing. Plus, it gives a name to the expression, making more obvious what it represents. But now that I think about it, the There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Whoops. I think |
||
} | ||
else | ||
{ | ||
// FLAC decoder can't skip straight to EOF, so we short-seek by one sample and skip the rest | ||
FLAC__stream_decoder_seek_absolute(m_decoder, (m_clientData.info.sampleCount / m_clientData.info.channelCount) - 1); | ||
FLAC__stream_decoder_skip_single_frame(m_decoder); | ||
|
||
// This was re-populated during the seek, but we're skipping everything in this, so we need it emptied | ||
m_clientData.leftovers.clear(); | ||
} | ||
} | ||
|
||
|
||
|
@@ -283,7 +290,7 @@ Uint64 SoundFileReaderFlac::read(Int16* samples, Uint64 maxCount) | |
if (left > maxCount) | ||
{ | ||
// There are more leftovers than needed | ||
std::copy(m_clientData.leftovers.begin(), m_clientData.leftovers.end(), samples); | ||
std::copy(m_clientData.leftovers.begin(), m_clientData.leftovers.begin() + maxCount, samples); | ||
std::vector<Int16> leftovers(m_clientData.leftovers.begin() + maxCount, m_clientData.leftovers.end()); | ||
m_clientData.leftovers.swap(leftovers); | ||
return maxCount; | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What's the effect of decrementing data->remaining without adding data to the buffer? Well, is it useful at all to execute this statement if there's no buffer?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Previously, the whole function returned early when
data->buffer
was null. Since I removed that check, I figured I'd put a smaller one around the point where the buffer is actually dereferenced. I know that in practice,data->remaining
will always be 0 whendata->buffer
is null (since that's the seek condition), and thedata->leftovers.push_back(sample)
path should always be taken then. But I'm always wary of dereferencing a pointer that hasn't been checked locally. So I just covered the single dereferencing line of code with the new check. I didn't touch the decrement because that preserves the countdown to where it will start loading the samples into the always-valid leftover buffer. But that's a bit of moot point since that should never happen anyway.Maybe I should change the surrounding if condition to
if (data->buffer && data->remaining > 0)
.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That's the way to go in my opinion. I'd also change the comments because now the
else
block is also for the seeking case.