ESP8266Audio/src/AudioGeneratorFLAC.cpp

203 lines
6.6 KiB
C++

/*
AudioGeneratorFLAC
Audio output generator that plays FLAC audio files
Copyright (C) 2017 Earle F. Philhower, III
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <AudioGeneratorFLAC.h>
AudioGeneratorFLAC::AudioGeneratorFLAC()
{
flac = NULL;
channels = 0;
sampleRate = 0;
bitsPerSample = 0;
buff[0] = NULL;
buff[1] = NULL;
buffPtr = 0;
buffLen = 0;
running = false;
}
AudioGeneratorFLAC::~AudioGeneratorFLAC()
{
if (flac)
FLAC__stream_decoder_delete(flac);
flac = NULL;
}
bool AudioGeneratorFLAC::begin(AudioFileSource *source, AudioOutput *output)
{
if (!source) return false;
file = source;
if (!output) return false;
this->output = output;
if (!file->isOpen()) return false; // Error
flac = FLAC__stream_decoder_new();
if (!flac) return false;
(void)FLAC__stream_decoder_set_md5_checking(flac, false);
FLAC__StreamDecoderInitStatus ret = FLAC__stream_decoder_init_stream(flac, _read_cb, _seek_cb, _tell_cb, _length_cb, _eof_cb, _write_cb, _metadata_cb, _error_cb, reinterpret_cast<void*>(this) );
if (ret != FLAC__STREAM_DECODER_INIT_STATUS_OK) {
FLAC__stream_decoder_delete(flac);
flac = NULL;
return false;
}
output->begin();
running = true;
lastSample[0] = 0;
lastSample[1] = 0;
channels = 0;
return true;
}
bool AudioGeneratorFLAC::loop()
{
FLAC__bool ret;
if (!running) goto done;
if (channels && !output->ConsumeSample(lastSample)) goto done; // Try and send last buffered sample
do {
if (buffPtr == buffLen) {
ret = FLAC__stream_decoder_process_single(flac);
if (!ret) {
running = false;
goto done;
} else {
// We might be done...
if (FLAC__stream_decoder_get_state(flac)==FLAC__STREAM_DECODER_END_OF_STREAM) {
running = false;
goto done;
}
unsigned newsr = FLAC__stream_decoder_get_sample_rate(flac);
unsigned newch = FLAC__stream_decoder_get_channels(flac);
unsigned newbps = FLAC__stream_decoder_get_bits_per_sample(flac);
if (newsr != sampleRate) output->SetRate(sampleRate = newsr);
if (newch != channels) output->SetChannels(channels = newch);
if (newbps != bitsPerSample) output->SetBitsPerSample( bitsPerSample = newbps);
}
}
// Check for some weird case where above didn't give any data
if (buffPtr == buffLen) {
goto done; // At some point the flac better error and we'll return
}
if (bitsPerSample <= 16) {
lastSample[AudioOutput::LEFTCHANNEL] = buff[0][buffPtr] & 0xffff;
if (channels==2) lastSample[AudioOutput::RIGHTCHANNEL] = buff[1][buffPtr] & 0xffff;
else lastSample[AudioOutput::RIGHTCHANNEL] = lastSample[AudioOutput::LEFTCHANNEL];
} else if (bitsPerSample <= 24) {
lastSample[AudioOutput::LEFTCHANNEL] = (buff[0][buffPtr]>>8) & 0xffff;
if (channels==2) lastSample[AudioOutput::RIGHTCHANNEL] = (buff[1][buffPtr]>>8) & 0xffff;
else lastSample[AudioOutput::RIGHTCHANNEL] = lastSample[AudioOutput::LEFTCHANNEL];
} else {
lastSample[AudioOutput::LEFTCHANNEL] = (buff[0][buffPtr]>>16) & 0xffff;
if (channels==2) lastSample[AudioOutput::RIGHTCHANNEL] = (buff[1][buffPtr]>>16) & 0xffff;
else lastSample[AudioOutput::RIGHTCHANNEL] = lastSample[AudioOutput::LEFTCHANNEL];
}
buffPtr++;
} while (running && output->ConsumeSample(lastSample));
done:
file->loop();
output->loop();
return running;
}
bool AudioGeneratorFLAC::stop()
{
if (flac)
FLAC__stream_decoder_delete(flac);
flac = NULL;
running = false;
output->stop();
return true;
}
bool AudioGeneratorFLAC::isRunning()
{
return running;
}
FLAC__StreamDecoderReadStatus AudioGeneratorFLAC::read_cb(const FLAC__StreamDecoder *decoder, FLAC__byte buffer[], size_t *bytes)
{
(void) decoder;
if (*bytes==0) return FLAC__STREAM_DECODER_READ_STATUS_ABORT;
*bytes = file->read(buffer, sizeof(FLAC__byte) * (*bytes));
if (*bytes==0) return FLAC__STREAM_DECODER_READ_STATUS_END_OF_STREAM;
return FLAC__STREAM_DECODER_READ_STATUS_CONTINUE;
}
FLAC__StreamDecoderSeekStatus AudioGeneratorFLAC::seek_cb(const FLAC__StreamDecoder *decoder, FLAC__uint64 absolute_byte_offset)
{
(void) decoder;
if (!file->seek((int32_t)absolute_byte_offset, 0)) return FLAC__STREAM_DECODER_SEEK_STATUS_ERROR;
return FLAC__STREAM_DECODER_SEEK_STATUS_OK;
}
FLAC__StreamDecoderTellStatus AudioGeneratorFLAC::tell_cb(const FLAC__StreamDecoder *decoder, FLAC__uint64 *absolute_byte_offset)
{
(void) decoder;
*absolute_byte_offset = file->getPos();
return FLAC__STREAM_DECODER_TELL_STATUS_OK;
}
FLAC__StreamDecoderLengthStatus AudioGeneratorFLAC::length_cb(const FLAC__StreamDecoder *decoder, FLAC__uint64 *stream_length)
{
(void) decoder;
*stream_length = file->getSize();
return FLAC__STREAM_DECODER_LENGTH_STATUS_OK;
}
FLAC__bool AudioGeneratorFLAC::eof_cb(const FLAC__StreamDecoder *decoder)
{
(void) decoder;
if (file->getPos() >= file->getSize()) return true;
return false;
}
FLAC__StreamDecoderWriteStatus AudioGeneratorFLAC::write_cb(const FLAC__StreamDecoder *decoder, const FLAC__Frame *frame, const FLAC__int32 *const buffer[])
{
(void) decoder;
// Hackish warning here. FLAC sends the buffer but doesn't free it until the next call to decode_frame, so we stash
// the pointers here and use it in our loop() instead of memcpy()'ing into yet another buffer.
buffLen = frame->header.blocksize;
buff[0] = (const int *)buffer[0];
if (frame->header.channels>1) buff[1] = (const int *)buffer[1];
else buff[1] = (const int *)buffer[0];
buffPtr = 0;
return FLAC__STREAM_DECODER_WRITE_STATUS_CONTINUE;
}
void AudioGeneratorFLAC::metadata_cb(const FLAC__StreamDecoder *decoder, const FLAC__StreamMetadata *metadata)
{
(void) decoder;
(void) metadata;
audioLogger->printf_P(PSTR("Metadata\n"));
}
char AudioGeneratorFLAC::error_cb_str[64];
void AudioGeneratorFLAC::error_cb(const FLAC__StreamDecoder *decoder, FLAC__StreamDecoderErrorStatus status)
{
(void) decoder;
strncpy_P(error_cb_str, FLAC__StreamDecoderErrorStatusString[status], sizeof(AudioGeneratorFLAC::error_cb_str) - 1);
cb.st((int)status, error_cb_str);
}