aboutsummaryrefslogtreecommitdiff
path: root/src/plugins/Input/flac/decoder_flac.cpp
diff options
context:
space:
mode:
authorvovanec <vovanec@90c681e8-e032-0410-971d-27865f9a5e38>2008-02-07 13:36:34 +0000
committervovanec <vovanec@90c681e8-e032-0410-971d-27865f9a5e38>2008-02-07 13:36:34 +0000
commit06d1877811fa6aa97dddc0e03bcde4e766928c87 (patch)
treec25462d0e58c3d58c728664440412bf4f16a49ec /src/plugins/Input/flac/decoder_flac.cpp
parent3f6b60f23c44a8ba8dd97ca6f41a16e2af7ef2f7 (diff)
downloadqmmp-06d1877811fa6aa97dddc0e03bcde4e766928c87.tar.gz
qmmp-06d1877811fa6aa97dddc0e03bcde4e766928c87.tar.bz2
qmmp-06d1877811fa6aa97dddc0e03bcde4e766928c87.zip
new directory structure
git-svn-id: http://svn.code.sf.net/p/qmmp-dev/code/trunk/qmmp@232 90c681e8-e032-0410-971d-27865f9a5e38
Diffstat (limited to 'src/plugins/Input/flac/decoder_flac.cpp')
-rw-r--r--src/plugins/Input/flac/decoder_flac.cpp567
1 files changed, 567 insertions, 0 deletions
diff --git a/src/plugins/Input/flac/decoder_flac.cpp b/src/plugins/Input/flac/decoder_flac.cpp
new file mode 100644
index 000000000..b2a895bca
--- /dev/null
+++ b/src/plugins/Input/flac/decoder_flac.cpp
@@ -0,0 +1,567 @@
+/***************************************************************************
+ * Copyright (C) 2006 by Ilya Kotov *
+ * forkotov02@hotmail.ru *
+ * *
+ * 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 "constants.h"
+#include "buffer.h"
+#include "output.h"
+#include "recycler.h"
+
+#include <QObject>
+#include <QIODevice>
+#include <FLAC/all.h>
+#include "decoder_flac.h"
+
+
+
+static size_t pack_pcm_signed (FLAC__byte *data,
+ const FLAC__int32 * const input[],
+ unsigned wide_samples,
+ unsigned channels, unsigned bps)
+{
+ FLAC__byte * const start = data;
+ FLAC__int32 sample;
+ const FLAC__int32 *input_;
+ unsigned samples, channel;
+ unsigned bytes_per_sample;
+ unsigned incr;
+
+ if (bps == 24)
+ bps = 32; /* we encode to 32-bit words */
+ bytes_per_sample = bps / 8;
+ incr = bytes_per_sample * channels;
+
+ for (channel = 0; channel < channels; channel++)
+ {
+ samples = wide_samples;
+ data = start + bytes_per_sample * channel;
+ input_ = input[channel];
+
+ while (samples--)
+ {
+ sample = *input_++;
+
+ switch (bps)
+ {
+ case 8:
+ data[0] = sample;
+ break;
+ case 16:
+ data[1] = (FLAC__byte)(sample >> 8);
+ data[0] = (FLAC__byte)sample;
+ break;
+ case 32:
+ data[3] = (FLAC__byte)(sample >> 16);
+ data[2] = (FLAC__byte)(sample >> 8);
+ data[1] = (FLAC__byte)sample;
+ data[0] = 0;
+ break;
+ }
+
+ data += incr;
+ }
+ }
+
+ return wide_samples * channels * bytes_per_sample;
+}
+
+static int flac_decode (void *void_data, char *buf, int buf_len) /*,
+ struct sound_params *sound_params)*/
+{
+ //struct flac_data *data = (struct flac_data *)void_data;
+ DecoderFLAC *dflac = (DecoderFLAC *) void_data;
+ unsigned to_copy;
+ int bytes_per_sample;
+ FLAC__uint64 decode_position;
+
+ bytes_per_sample = dflac->data()->bits_per_sample / 8;
+
+ /*switch (bytes_per_sample) {
+ case 1:
+ sound_params->fmt = SFMT_S8;
+ break;
+ case 2:
+ sound_params->fmt = SFMT_S16 | SFMT_LE;
+ break;
+ case 3:
+ sound_params->fmt = SFMT_S32 | SFMT_LE;
+ break;
+ }
+
+ sound_params->rate = data->sample_rate;
+ sound_params->channels = data->channels;*/
+
+ //decoder_error_clear (&data->error);
+
+ if (!dflac->data()->sample_buffer_fill)
+ {
+
+ if (FLAC__stream_decoder_get_state(dflac->data()->decoder)
+ == FLAC__STREAM_DECODER_END_OF_STREAM)
+ {
+ return 0;
+ }
+
+ if (!FLAC__stream_decoder_process_single(
+ dflac->data()->decoder))
+ {
+ return 0;
+ }
+
+ /* Count the bitrate */
+ if (!FLAC__stream_decoder_get_decode_position(
+ dflac->data()->decoder, &decode_position))
+ decode_position = 0;
+ if (decode_position > dflac->data()->last_decode_position)
+ {
+ int bytes_per_sec = bytes_per_sample * dflac->data()->sample_rate
+ * dflac->data()->channels;
+
+ dflac->data()->bitrate = int(((float)decode_position -
+ dflac->data()->last_decode_position) * 8.0 *
+ bytes_per_sec /
+ dflac->data()->sample_buffer_fill / 1000);
+ }
+
+ dflac->data()->last_decode_position = decode_position;
+ }
+
+ to_copy = qMin((unsigned)buf_len, dflac->data()->sample_buffer_fill);
+ memcpy (buf, dflac->data()->sample_buffer, to_copy);
+ memmove (dflac->data()->sample_buffer,
+ dflac->data()->sample_buffer + to_copy,
+ dflac->data()->sample_buffer_fill - to_copy);
+ dflac->data()->sample_buffer_fill -= to_copy;
+ return to_copy;
+}
+
+
+static FLAC__StreamDecoderReadStatus
+flac_callback_read (const FLAC__StreamDecoder *, FLAC__byte buffer[],
+ size_t *bytes, void *client_data)
+{
+ DecoderFLAC *dflac = (DecoderFLAC *) client_data;
+ qint64 res;
+
+ res = dflac->input()->read((char *)buffer, *bytes);
+
+ if (res > 0)
+ {
+ *bytes = res;
+ return FLAC__STREAM_DECODER_READ_STATUS_CONTINUE;
+ }
+ if (res == 0)
+ {
+ *bytes = res;
+ return FLAC__STREAM_DECODER_READ_STATUS_END_OF_STREAM;
+ }
+
+ return FLAC__STREAM_DECODER_READ_STATUS_ABORT;
+
+}
+
+static FLAC__StreamDecoderWriteStatus
+flac_callback_write (const FLAC__StreamDecoder *, const FLAC__Frame *frame, const FLAC__int32 * const buffer[], void *client_data)
+{
+ DecoderFLAC *dflac = (DecoderFLAC *) client_data;
+ const unsigned wide_samples = frame->header.blocksize;
+
+ if (dflac->data()->abort)
+ return FLAC__STREAM_DECODER_WRITE_STATUS_ABORT;
+
+
+ dflac->data()->sample_buffer_fill = pack_pcm_signed (
+ dflac->data()->sample_buffer,
+ buffer, wide_samples,
+ dflac->data()->channels,
+ dflac->data()->bits_per_sample);
+
+ return FLAC__STREAM_DECODER_WRITE_STATUS_CONTINUE;
+}
+
+static FLAC__StreamDecoderTellStatus
+flac_callback_tell (const FLAC__StreamDecoder *, FLAC__uint64 *offset, void *client_data)
+{
+ DecoderFLAC *dflac = (DecoderFLAC *) client_data;
+ *offset = dflac->input()->pos ();
+ return FLAC__STREAM_DECODER_TELL_STATUS_OK;
+}
+
+static FLAC__StreamDecoderSeekStatus
+flac_callback_seek (const FLAC__StreamDecoder *, FLAC__uint64 offset, void *client_data)
+{
+ DecoderFLAC *dflac = (DecoderFLAC *) client_data;
+
+ return dflac->input()->seek(offset)
+ ? FLAC__STREAM_DECODER_SEEK_STATUS_OK
+ : FLAC__STREAM_DECODER_SEEK_STATUS_ERROR;
+}
+
+static FLAC__StreamDecoderLengthStatus
+flac_callback_length (const FLAC__StreamDecoder *, FLAC__uint64 *stream_length, void *client_data)
+{
+ DecoderFLAC *dflac = (DecoderFLAC *) client_data;
+ *stream_length = dflac->input()->size();
+ return FLAC__STREAM_DECODER_LENGTH_STATUS_OK;
+}
+
+static void
+flac_callback_metadata (const FLAC__StreamDecoder *, const FLAC__StreamMetadata *metadata, void *client_data)
+{
+ DecoderFLAC *dflac = (DecoderFLAC *) client_data;
+
+ if (metadata->type == FLAC__METADATA_TYPE_STREAMINFO)
+ {
+ qDebug ("DecoderFLAC: getting metadata info");
+
+ dflac->data()->total_samples =
+ (unsigned)(metadata->data.stream_info.total_samples
+ & 0xffffffff);
+ dflac->data()->bits_per_sample =
+ metadata->data.stream_info.bits_per_sample;
+ dflac->data()->channels = metadata->data.stream_info.channels;
+ dflac->data()->sample_rate = metadata->data.stream_info.sample_rate;
+ dflac->data()->length = dflac->data()->total_samples / dflac->data()->sample_rate;
+ }
+}
+
+static FLAC__bool
+flac_callback_eof (const FLAC__StreamDecoder *, void *)
+{
+ return FALSE;
+}
+
+static void
+flac_callback_error (const FLAC__StreamDecoder *, FLAC__StreamDecoderErrorStatus status, void *client_data)
+{}
+
+// Decoder class
+
+DecoderFLAC::DecoderFLAC(QObject *parent, DecoderFactory *d, QIODevice *i, Output *o)
+ : Decoder(parent, d, i, o)
+{
+ inited = FALSE;
+ user_stop = FALSE;
+ stat = 0;
+ output_buf = 0;
+ output_bytes = 0;
+ output_at = 0;
+ bks = 0;
+ done = FALSE;
+ finish = FALSE;
+ len = 0;
+ freq = 0;
+ bitrate = 0;
+ seekTime = -1.0;
+ totalTime = 0.0;
+ chan = 0;
+ output_size = 0;
+ m_data = 0;
+
+
+
+
+}
+
+
+DecoderFLAC::~DecoderFLAC()
+{
+ deinit();
+ if (data())
+ {
+ if (data()->decoder)
+ FLAC__stream_decoder_delete (data()->decoder);
+ delete data();
+ m_data = 0;
+ }
+
+ if (output_buf)
+ delete [] output_buf;
+ output_buf = 0;
+}
+
+
+void DecoderFLAC::stop()
+{
+ user_stop = TRUE;
+}
+
+
+void DecoderFLAC::flush(bool final)
+{
+ //qDebug("DecoderFLAC: flush()");
+ ulong min = final ? 0 : bks;
+
+ while ((! done && ! finish) && output_bytes > min)
+ {
+ output()->recycler()->mutex()->lock();
+
+ while ((! done && ! finish) && output()->recycler()->full())
+ {
+ mutex()->unlock();
+
+ output()->recycler()->cond()->wait(output()->recycler()->mutex());
+
+ mutex()->lock();
+ done = user_stop;
+ }
+
+ if (user_stop || finish)
+ {
+ inited = FALSE;
+ done = TRUE;
+ }
+ else
+ {
+ output_bytes -= produceSound(output_buf, output_bytes, bitrate, chan);
+ output_size += bks;
+ output_at = output_bytes;
+ }
+
+ if (output()->recycler()->full())
+ {
+ output()->recycler()->cond()->wakeOne();
+ }
+
+ output()->recycler()->mutex()->unlock();
+ }
+}
+
+
+bool DecoderFLAC::initialize()
+{
+ bks = blockSize();
+ inited = user_stop = done = finish = FALSE;
+ len = freq = bitrate = 0;
+ stat = chan = 0;
+ output_size = 0;
+ seekTime = -1.0;
+ totalTime = 0.0;
+
+
+ if (! input())
+ {
+ error("DecoderFLAC: cannot initialize. No input.");
+
+ return FALSE;
+ }
+
+ if (! output_buf)
+ output_buf = new char[globalBufferSize];
+ output_at = 0;
+ output_bytes = 0;
+
+ if (! input()->isOpen())
+ {
+ if (! input()->open(QIODevice::ReadOnly))
+ {
+
+ return FALSE;
+ }
+ }
+
+
+ if (! input())
+ {
+ error("DecoderFLAC: cannot initialize. No input.");
+
+ return FALSE;
+ }
+
+ if (! output_buf)
+ output_buf = new char[globalBufferSize];
+ output_at = 0;
+ output_bytes = 0;
+
+ if (! input()->isOpen())
+ {
+ if (! input()->open(QIODevice::ReadOnly))
+ {
+ return FALSE;
+ }
+ }
+ if (!m_data)
+ {
+ m_data = new flac_data;
+ m_data->decoder = NULL;
+ }
+
+ m_data->bitrate = -1;
+ m_data->abort = 0;
+ m_data->sample_buffer_fill = 0;
+ m_data->last_decode_position = 0;
+ if (!m_data->decoder)
+ {
+ qDebug("DecoderFLAC: creating FLAC__StreamDecoder");
+ m_data->decoder = FLAC__stream_decoder_new ();
+ }
+ qDebug("DecoderFLAC: setting callbacks");
+ if (FLAC__stream_decoder_init_stream(
+ m_data->decoder,
+ flac_callback_read,
+ flac_callback_seek,
+ flac_callback_tell,
+ flac_callback_length,
+ flac_callback_eof,
+ flac_callback_write,
+ flac_callback_metadata,
+ flac_callback_error,
+ this) != FLAC__STREAM_DECODER_INIT_STATUS_OK)
+ {
+ data()->ok = 0;
+ return FALSE;
+ }
+
+ if (!FLAC__stream_decoder_process_until_end_of_metadata(
+ data()->decoder))
+ {
+ data()->ok = 0;
+ return FALSE;
+ }
+ chan = data()->channels;
+ configure(data()->sample_rate, data()->channels, 16, bitrate);
+ totalTime = data()->length;
+
+ inited = TRUE;
+ qDebug("DecoderFLAC: initialize succes");
+ return TRUE;
+}
+
+
+double DecoderFLAC::lengthInSeconds()
+{
+ if (! inited)
+ return 0;
+
+ return totalTime;
+}
+
+
+void DecoderFLAC::seek(double pos)
+{
+ seekTime = pos;
+}
+
+
+void DecoderFLAC::deinit()
+{
+ if(data())
+ FLAC__stream_decoder_finish (data()->decoder);
+ inited = user_stop = done = finish = FALSE;
+ len = freq = bitrate = 0;
+ stat = chan = 0;
+ output_size = 0;
+}
+
+void DecoderFLAC::run()
+{
+ mutex()->lock ();
+
+ if (! inited)
+ {
+ mutex()->unlock();
+
+ return;
+ }
+ stat = DecoderState::Decoding;
+ mutex()->unlock();
+ {
+ dispatch(DecoderState ((DecoderState::Type) stat));
+ }
+
+ while (! done && ! finish)
+ {
+ mutex()->lock ();
+ // decode
+
+ if (seekTime >= 0.0)
+ {
+ FLAC__uint64 target_sample;
+
+ target_sample = (FLAC__uint64)((seekTime/(double)data()->length) *
+ (double)data()->total_samples);
+
+ FLAC__stream_decoder_seek_absolute(data()->decoder,
+ target_sample);
+ seekTime = -1.0;
+ }
+ len = flac_decode (this, (char *) (output_buf + output_at), bks);
+
+ if (len > 0)
+ {
+ bitrate = data()->bitrate;
+ output_at += len;
+ output_bytes += len;
+
+ if (output())
+ flush();
+
+ }
+ else if (len == 0)
+ {
+ flush(TRUE);
+
+ if (output())
+ {
+ output()->recycler()->mutex()->lock ();
+ // end of stream
+ while (! output()->recycler()->empty() && ! user_stop)
+ {
+ output()->recycler()->cond()->wakeOne();
+ mutex()->unlock();
+ output()->recycler()->cond()->wait(output()->recycler()->mutex());
+ mutex()->lock ();
+ }
+ output()->recycler()->mutex()->unlock();
+ }
+
+ done = TRUE;
+ if (! user_stop)
+ {
+ finish = TRUE;
+ }
+ }
+ else
+ {
+ // error in read
+ error("DecoderFLAC: Error while decoding stream, File appears to be "
+ "corrupted");
+
+ finish = TRUE;
+ }
+
+ mutex()->unlock();
+ }
+
+ mutex()->lock ();
+
+ if (finish)
+ stat = DecoderState::Finished;
+ else if (user_stop)
+ stat = DecoderState::Stopped;
+
+ mutex()->unlock();
+
+ {
+ dispatch(DecoderState ((DecoderState::Type) stat));
+ }
+
+ deinit();
+}