#include "FlacReader.h"
#include <cassert>
#include <algorithm>
#include <iostream>
// https://github.com/SFML/SFML/blob/cd9b8b9a150b8c883e827ebd3e060dd7daa402e6/src/SFML/Audio/SoundFileReaderFlac.cpp
FLAC__StreamDecoderReadStatus streamRead(const FLAC__StreamDecoder*, FLAC__byte buffer[], std::size_t* bytes, void* clientData)
{
FlacReader::ClientData* data = static_cast<FlacReader::ClientData*>(clientData);
if (*bytes > 0) {
data->stream->read(reinterpret_cast<char*>(buffer), *bytes);
*bytes = data->stream->gcount();
if (data->stream->eof()) {
return FLAC__STREAM_DECODER_READ_STATUS_END_OF_STREAM;
}
if (data->stream->fail()) {
return FLAC__STREAM_DECODER_READ_STATUS_ABORT;
}
else {
return FLAC__STREAM_DECODER_READ_STATUS_CONTINUE;
}
}
else {
return FLAC__STREAM_DECODER_READ_STATUS_ABORT;
}
}
FLAC__StreamDecoderSeekStatus streamSeek(const FLAC__StreamDecoder*, FLAC__uint64 absoluteByteOffset, void* clientData)
{
FlacReader::ClientData* data = static_cast<FlacReader::ClientData*>(clientData);
data->stream->seekg(absoluteByteOffset);
if (data->stream->tellg() >= 0)
return FLAC__STREAM_DECODER_SEEK_STATUS_OK;
else
return FLAC__STREAM_DECODER_SEEK_STATUS_ERROR;
}
FLAC__StreamDecoderTellStatus streamTell(const FLAC__StreamDecoder*, FLAC__uint64 * absoluteByteOffset, void* clientData)
{
FlacReader::ClientData* data = static_cast<FlacReader::ClientData*>(clientData);
int64_t position = data->stream->tellg();
if (position >= 0)
{
*absoluteByteOffset = position;
return FLAC__STREAM_DECODER_TELL_STATUS_OK;
}
else
{
return FLAC__STREAM_DECODER_TELL_STATUS_ERROR;
}
}
FLAC__StreamDecoderLengthStatus streamLength(const FLAC__StreamDecoder*, FLAC__uint64 * streamLength, void* clientData)
{
FlacReader::ClientData* data = static_cast<FlacReader::ClientData*>(clientData);
std::streampos currentPos = data->stream->tellg();
data->stream->seekg(std::ios::end);
int64_t count = data->stream->tellg();
data->stream->seekg(currentPos);
if (count >= 0)
{
*streamLength = count;
return FLAC__STREAM_DECODER_LENGTH_STATUS_OK;
}
else
{
return FLAC__STREAM_DECODER_LENGTH_STATUS_ERROR;
}
}
FLAC__bool streamEof(const FLAC__StreamDecoder*, void* clientData)
{
FlacReader::ClientData* data = static_cast<FlacReader::ClientData*>(clientData);
std::streampos currentPos = data->stream->tellg();
data->stream->seekg(std::ios::end);
int64_t size = data->stream->tellg();
data->stream->seekg(currentPos);
return data->stream->tellg() == size;
}
FLAC__StreamDecoderWriteStatus streamWrite(const FLAC__StreamDecoder*, const FLAC__Frame* frame, const FLAC__int32* const buffer[], void* clientData)
{
FlacReader::ClientData* data = static_cast<FlacReader::ClientData*>(clientData);
// Reserve memory if we're going to use the leftovers buffer
unsigned int frameSamples = frame->header.blocksize * frame->header.channels;
if (data->remaining < frameSamples)
data->leftovers.reserve(static_cast<std::size_t>(frameSamples - data->remaining));
int shift = 32 - frame->header.bits_per_sample;
// Decode the samples
for (unsigned i = 0; i < frame->header.blocksize; ++i)
{
for (unsigned int j = 0; j < frame->header.channels; ++j)
{
// Decode the current sample
int32_t sample = buffer[j][i] << shift;
if (data->buffer && data->remaining > 0)
{
// If there's room in the output buffer, copy the sample there
*data->buffer++ = sample;
data->remaining--;
}
else
{
// 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);
}
}
}
return FLAC__STREAM_DECODER_WRITE_STATUS_CONTINUE;
}
void streamMetadata(const FLAC__StreamDecoder*, const FLAC__StreamMetadata * meta, void* clientData)
{
FlacReader::ClientData* data = static_cast<FlacReader::ClientData*>(clientData);
if (meta->type == FLAC__METADATA_TYPE_STREAMINFO)
{
data->info.sampleCount = meta->data.stream_info.total_samples * meta->data.stream_info.channels;
std::cout << "Sample count: " << meta->data.stream_info.total_samples * meta->data.stream_info.channels << std::endl;
data->info.sampleRate = meta->data.stream_info.sample_rate;
std::cout << "Sample rate: " << meta->data.stream_info.sample_rate << std::endl;
data->info.channelCount = meta->data.stream_info.channels;
std::cout << "Channels: " << meta->data.stream_info.channels << std::endl;
std::cout << "Bits per sample: " << meta->data.stream_info.bits_per_sample << std::endl;
}
}
void streamError(const FLAC__StreamDecoder*, FLAC__StreamDecoderErrorStatus, void* clientData)
{
FlacReader::ClientData* data = static_cast<FlacReader::ClientData*>(clientData);
data->error = true;
}
////////////////////////////////////////////////////////////
bool FlacReader::check(std::shared_ptr<std::ifstream> stream)
{
// Create a decoder
std::unique_ptr<FLAC__StreamDecoder> decoder(FLAC__stream_decoder_new());
if (!decoder)
return false;
// Initialize the decoder with our callbacks
ClientData data;
data.stream = stream;
data.error = false;
FLAC__stream_decoder_init_stream(decoder.get(), &streamRead, &streamSeek, &streamTell, &streamLength, &streamEof, &streamWrite, nullptr, &streamError, &data);
// Read the header
bool valid = FLAC__stream_decoder_process_until_end_of_metadata(decoder.get()) != 0;
// Destroy the decoder
FLAC__stream_decoder_finish(decoder.get());
FLAC__stream_decoder_delete(decoder.get());
return valid && !data.error;
}
////////////////////////////////////////////////////////////
FlacReader::FlacReader() :
m_decoder(nullptr),
m_clientData()
{
}
////////////////////////////////////////////////////////////
FlacReader::~FlacReader()
{
close();
}
////////////////////////////////////////////////////////////
bool FlacReader::open(std::shared_ptr<std::ifstream> stream, Info& info)
{
// Create the decoder
m_decoder = std::unique_ptr<FLAC__StreamDecoder>(FLAC__stream_decoder_new());
if (!m_decoder)
{
//err() << "Failed to open FLAC file (failed to allocate the decoder)" << std::endl;
return false;
}
// Initialize the decoder with our callbacks
m_clientData.stream = stream;
FLAC__stream_decoder_init_stream(m_decoder.get(), &streamRead, &streamSeek, &streamTell, &streamLength, &streamEof, &streamWrite, &streamMetadata, &streamError, &m_clientData);
// Read the header
if (!FLAC__stream_decoder_process_until_end_of_metadata(m_decoder.get()))
{
close();
//err() << "Failed to open FLAC file (failed to read metadata)" << std::endl;
return false;
}
// Retrieve the sound properties
info = m_clientData.info; // was filled in the "metadata" callback
return true;
}
////////////////////////////////////////////////////////////
void FlacReader::seek(uint64_t sampleOffset)
{
assert(m_decoder);
// Reset the callback data (the "write" callback will be called)
m_clientData.buffer = nullptr;
m_clientData.remaining = 0;
m_clientData.leftovers.clear();
// FLAC decoder expects absolute sample offset, so we take the channel count out
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.get(), sampleOffset / m_clientData.info.channelCount);
}
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.get(), (m_clientData.info.sampleCount / m_clientData.info.channelCount) - 1);
FLAC__stream_decoder_skip_single_frame(m_decoder.get());
// This was re-populated during the seek, but we're skipping everything in this, so we need it emptied
m_clientData.leftovers.clear();
}
}
////////////////////////////////////////////////////////////
uint64_t FlacReader::read(int32_t * samples, uint64_t maxCount)
{
assert(m_decoder);
// If there are leftovers from previous call, use it first
uint64_t left = m_clientData.leftovers.size();
if (left > 0)
{
if (left > maxCount)
{
// There are more leftovers than needed
std::copy(m_clientData.leftovers.begin(), m_clientData.leftovers.end(), samples);
std::vector<int32_t> leftovers(m_clientData.leftovers.begin() + maxCount, m_clientData.leftovers.end());
m_clientData.leftovers.swap(leftovers);
return maxCount;
}
else
{
// We can use all the leftovers and decode new frames
std::copy(m_clientData.leftovers.begin(), m_clientData.leftovers.end(), samples);
}
}
// Reset the data that will be used in the callback
m_clientData.buffer = samples + left;
m_clientData.remaining = maxCount - left;
m_clientData.leftovers.clear();
// Decode frames one by one until we reach the requested sample count, the end of file or an error
while (m_clientData.remaining > 0)
{
// Everything happens in the "write" callback
// This will break on any fatal error (does not include EOF)
if (!FLAC__stream_decoder_process_single(m_decoder.get()))
break;
// Break on EOF
if (FLAC__stream_decoder_get_state(m_decoder.get()) == FLAC__STREAM_DECODER_END_OF_STREAM)
break;
}
return maxCount - m_clientData.remaining;
}
////////////////////////////////////////////////////////////
void FlacReader::close()
{
if (m_decoder)
{
FLAC__stream_decoder_finish(m_decoder.get());
FLAC__stream_decoder_delete(m_decoder.get());
m_decoder = nullptr;
}
}