/* Copyright (C) 2015-2018 J.F.Dockes
* 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 2 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, write to the
* Free Software Foundation, Inc.,
* 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
#include "flacdecoder.h"
#include "rcvqueue.h"
#include "log.h"
#include <string.h>
#ifdef DEBUG_DECODER
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#endif
using namespace std;
FlacDecoder::FlacDecoder()
{
LOGDEB("New FLAC decoder\n");
m_decoder = FLAC__stream_decoder_new();
if (m_decoder == nullptr)
LOGERR("FlacDecoder: Failed to construct stream decoder\n");
#ifdef DEBUG_DECODER
dumpfd = open("/tmp/sc2dump.flac", O_WRONLY|O_CREAT|O_TRUNC, 0666);
#endif
}
FlacDecoder::~FlacDecoder()
{
LOGDEB("Delete FLAC decoder\n");
FLAC__stream_decoder_delete(m_decoder);
#ifdef DEBUG_DECODER
if (dumpfd > 0)
close(dumpfd);
#endif
}
void FlacDecoder::start()
{
LOGDEB("Start FLAC decoder\n");
FLAC__StreamDecoderInitStatus init_status;
init_status = FLAC__stream_decoder_init_stream(m_decoder,
readCallback, nullptr, nullptr, nullptr, nullptr,
writeCallback, nullptr, errorCallback, this);
if (init_status != FLAC__STREAM_DECODER_INIT_STATUS_OK)
LOGERR("FlacDecoder: Failed to initialize stream decoder: "
<< string(FLAC__StreamDecoderInitStatusString[init_status])
<< endl);
}
int FlacDecoder::decode(OhmMsgAudio& aMsg)
{
if (aMsg.Audio().Bytes() == 0) {
if (aMsg.Halt()) {
LOGDEB("FlacDecoder::decode: empty message with halt flag set\n");
putAudioMessage(0, 0, 0, 0, true);
} else {
LOGDEB("FlacDecoder::decode: ignoring empty message\n");
}
return 0;
}
m_buffer = aMsg.Audio().Ptr();
m_bytes = aMsg.Audio().Bytes();
m_halt = aMsg.Halt();
if (!FLAC__stream_decoder_process_single(m_decoder))
LOGERR("FlacDecoder: Failed to process audio frame\n");
return m_bytes;
}
void FlacDecoder::finish()
{
LOGDEB("Finish FLAC decoder\n");
FLAC__stream_decoder_finish(m_decoder);
}
void FlacDecoder::putAudioMessage(
unsigned int bits_per_sample,
unsigned int channels,
unsigned int frames,
unsigned int sample_rate,
bool halt)
{
unsigned int bytes = 0;
unsigned allocbytes = 0;
char *buf = nullptr;
if (frames > 0) {
bytes = frames * (bits_per_sample / 8) * channels;
// We allocate a bit more space to avoid reallocations in the resampler
allocbytes = bytes + 100;
buf = (char *)malloc(allocbytes);
if (buf == 0) {
LOGERR("OhmReceiverDriver::Process: can't allocate "
<< allocbytes << " bytes\n");
return;
}
memcpy(buf, m_pBuffer, bytes);
}
AudioMessage *ap = new AudioMessage(bits_per_sample,
channels,
frames,
sample_rate,
halt,
buf,
allocbytes);
// There is nothing special we can do if put fails: no way to
// return status. Should we just exit ?
if (!audioqueue.put(ap, false)) {
LOGERR("sc2mpd: queue dead: exiting\n");
exit(1);
}
}
FLAC__StreamDecoderReadStatus FlacDecoder::readCallback(
const FLAC__StreamDecoder *decoder,
FLAC__byte buffer[],
size_t *bytes,
void *client_data)
{
FlacDecoder* flacDecoder = reinterpret_cast<FlacDecoder*>(client_data);
memcpy(buffer, flacDecoder->m_buffer, flacDecoder->m_bytes);
*bytes = flacDecoder->m_bytes;
if (*bytes == 0) {
LOGDEB("FlacDecoder: End of stream\n");
return FLAC__STREAM_DECODER_READ_STATUS_END_OF_STREAM;
}
return FLAC__STREAM_DECODER_READ_STATUS_CONTINUE;
}
FLAC__StreamDecoderWriteStatus FlacDecoder::writeCallback(
const FLAC__StreamDecoder *decoder,
const FLAC__Frame *frame,
const FLAC__int32 * const buffer[],
void *client_data)
{
FlacDecoder* flacDecoder = reinterpret_cast<FlacDecoder*>(client_data);
const int bytes_per_sample = frame->header.bits_per_sample / 8;
char* outptr = flacDecoder->m_pBuffer/* + flacDecoder->m_BufferSize*/;
FLAC__int32* outptr32 = (FLAC__int32 *) outptr;
for (unsigned current_sample = 0; current_sample < frame->header.blocksize;
current_sample++) {
for (unsigned channel = 0; channel < frame->header.channels;
channel++) {
switch (bytes_per_sample) {
case 2:
outptr[1] = (buffer[channel][current_sample] >> 8 ) & 0xff;
outptr[0] = (buffer[channel][current_sample] >> 0 ) & 0xff;
outptr += bytes_per_sample;
break;
case 3:
outptr[2] = (buffer[channel][current_sample] >> 16) & 0xff;
outptr[1] = (buffer[channel][current_sample] >> 8 ) & 0xff;
outptr[0] = (buffer[channel][current_sample] >> 0 ) & 0xff;
outptr += bytes_per_sample;
break;
default:
outptr32[current_sample * frame->header.channels + channel]
= buffer[channel][current_sample];
break;
}
}
}
//LOGDEB("writeCallback:: bytes: "
// << frame->header.blocksize * bytes_per_sample * frame->header.channels
// << ", samples: " << frame->header.blocksize << endl);
flacDecoder->putAudioMessage(frame->header.bits_per_sample,
frame->header.channels,
frame->header.blocksize,
frame->header.sample_rate,
flacDecoder->m_halt);
return FLAC__STREAM_DECODER_WRITE_STATUS_CONTINUE;
}
void FlacDecoder::errorCallback(const FLAC__StreamDecoder *decoder,
FLAC__StreamDecoderErrorStatus status,
void *client_data)
{
LOGERR("FlacDecoder: error: "
<< FLAC__StreamDecoderErrorStatusString[status] << endl);
}