--- a
+++ b/sc2src/flacdecoder.cpp
@@ -0,0 +1,215 @@
+/* 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);
+}