/* 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 "flacencoder.h"
#include "audioutil.h"
#include "log.h"
#include <string.h>
#ifdef DEBUG_ENCODER
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#endif
using namespace std;
FlacEncoder::FlacEncoder(AudioReader* audioReader, OhmSenderDriver *ohmSender)
: AudioEncoder(audioReader, ohmSender, true, "FLAC")
{
LOGDEB("New FLAC encoder\n");
m_encoder = FLAC__stream_encoder_new();
if (m_encoder == nullptr)
LOGERR("FlacEncoder: Failed to construct stream encoder\n");
FLAC__bool ok = true;
ok &= FLAC__stream_encoder_set_compression_level(m_encoder, 0);
ok &= FLAC__stream_encoder_set_channels(m_encoder,
m_audioReader->numChannels());
ok &= FLAC__stream_encoder_set_bits_per_sample(m_encoder,
m_audioReader->bitsPerSample());
ok &= FLAC__stream_encoder_set_sample_rate(m_encoder,
m_audioReader->sampleRate());
if (!ok)
LOGERR("FlacEncoder: Failed to set parameters for stream encoder\n");
#ifdef DEBUG_ENCODER
dumpfd = open("/tmp/mpd2dump.flac", O_WRONLY|O_CREAT|O_TRUNC, 0666);
#endif
}
FlacEncoder::~FlacEncoder()
{
LOGDEB("Delete FLAC encoder\n");
FLAC__stream_encoder_delete(m_encoder);
#ifdef DEBUG_ENCODER
if (dumpfd > 0)
close(dumpfd);
#endif
}
void FlacEncoder::start()
{
LOGDEB("Start FLAC encoder\n");
FLAC__StreamEncoderInitStatus init_status;
init_status = FLAC__stream_encoder_init_stream(m_encoder,
writeCallback, nullptr, nullptr, nullptr, this);
if (init_status != FLAC__STREAM_ENCODER_INIT_STATUS_OK)
LOGERR("FlacEncoder: Failed to init stream for stream encoder: "
<< string(FLAC__StreamEncoderInitStatusString[init_status])
<< endl);
}
int FlacEncoder::encode(const unsigned char *buffer, unsigned num_bytes,
bool halt)
{
if (num_bytes == 0) {
LOGDEB("FlacEncoder: Sending an empty audio message with halt="
<< halt << endl);
m_ohmSender->SendAudio((const TByte*)buffer, 0, halt, 0);
return 0;
}
m_halt = halt;
unsigned samples_left = num_bytes / 2; // each sample takes 2 bytes
// (16 bits per sample)
while (samples_left > 0) {
unsigned samples = samples_left > SAMPLES_BUF_SIZE ? SAMPLES_BUF_SIZE
: samples_left;
// convert the packed little-endian 16-bit PCM samples into an
// interleaved FLAC__int32 buffer for libFLAC
//
// NOTE: we assume 44:16:2 + needswap, which is not correct,
// we should use the parameters from audioReader. Works for
// now because scmakempdsender configures mpd to perform the
// conversion.
for (unsigned i = 0; i < samples; i++) {
// inefficient but simple and works on big- or little-endian
// machines.
m_samplesBuf[i] = (FLAC__int32)
(((FLAC__int16)(FLAC__int8)buffer[2*i+1] << 8)
| (FLAC__int16)buffer[2*i]);
}
// feed samples to encoder
if (!FLAC__stream_encoder_process_interleaved(m_encoder, m_samplesBuf,
samples / 2)) {
return 0;
}
samples_left -= samples;
buffer += samples * 2; // skip processed samples
}
return num_bytes; // consumed everything
}
void FlacEncoder::finish()
{
LOGDEB("Finish FLAC encoder\n");
FLAC__stream_encoder_finish(m_encoder);
}
FLAC__StreamEncoderWriteStatus FlacEncoder::writeCallback(
const FLAC__StreamEncoder *encoder,
const FLAC__byte buffer[], // An array of encoded data.
size_t bytes, // The byte length of buffer.
unsigned samples, // The number of samples encoded by buffer
// 0 has a special meaning.
unsigned current_frame, // The number of the current frame being
// encoded.
void *client_data)
{
FlacEncoder *enc = reinterpret_cast<FlacEncoder*>(client_data);
//LOGDEB("writeCallback: " << bytes << ", " << samples << ", "
// << current_frame << ", " << enc->m_halt << endl);
if (samples > 0)
enc->m_ohmSender->SendAudio(buffer, bytes, enc->m_halt, samples);
#ifdef DEBUG_ENCODER
if (enc->dumpfd > 0)
write(enc->dumpfd, buffer, bytes);
#endif
return FLAC__STREAM_ENCODER_WRITE_STATUS_OK;
}