summaryrefslogtreecommitdiffstats
path: root/akode/lib/buffered_decoder.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'akode/lib/buffered_decoder.cpp')
-rw-r--r--akode/lib/buffered_decoder.cpp298
1 files changed, 298 insertions, 0 deletions
diff --git a/akode/lib/buffered_decoder.cpp b/akode/lib/buffered_decoder.cpp
new file mode 100644
index 0000000..7bba728
--- /dev/null
+++ b/akode/lib/buffered_decoder.cpp
@@ -0,0 +1,298 @@
+/* aKode: Buffered Decoder
+
+ Copyright (C) 2004-2005 Allan Sandfeld Jensen <kde@carewolf.com>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This library 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
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Steet, Fifth Floor,
+ Boston, MA 02110-1301, USA.
+*/
+
+#include <pthread.h>
+#include <assert.h>
+
+#include "audioframe.h"
+#include "audiobuffer.h"
+#include "decoder.h"
+#include "crossfader.h"
+#include "buffered_decoder.h"
+
+namespace aKode {
+
+// States are used to ensure we always have a welldefined state
+enum BufferedDecoderStatus { Closed, Open, Playing, Paused, XFadingSeek };
+
+struct BufferedDecoder::private_data
+{
+ private_data() : buffer(0)
+ , decoder(0)
+ , xfader(0)
+ , fading_time(50)
+ , buffer_size(16)
+ , blocking(false)
+ , running(false)
+ , state(Closed)
+ , halt(false)
+ , seek_pos(-1) {};
+ AudioBuffer *buffer;
+ Decoder *decoder;
+ CrossFader *xfader;
+ unsigned int fading_time, buffer_size;
+ bool blocking;
+ bool running;
+ BufferedDecoderStatus state;
+
+ // Thread controls
+ volatile bool halt;
+ volatile long seek_pos;
+ pthread_t thread;
+};
+
+// The decoder-thread. It is controlled through the two variables
+// halt and seek_pos in d
+static void* run_decoder(void* arg) {
+ BufferedDecoder::private_data *d = (BufferedDecoder::private_data*)arg;
+
+ AudioFrame frame;
+ bool no_error;
+
+ while(true) {
+ if (d->halt) break;
+ if (d->seek_pos>=0) {
+ d->decoder->seek(d->seek_pos);
+ d->seek_pos = -1;
+ }
+
+ no_error = d->decoder->readFrame(&frame);
+ if (no_error)
+ d->buffer->put(&frame, true);
+ else {
+ if (d->decoder->error() || d->decoder->eof()) {
+ break;
+ }
+ }
+ }
+
+ d->buffer->setEOF();
+
+ return (void*)0;
+}
+
+BufferedDecoder::BufferedDecoder(){
+ d = new private_data;
+}
+
+BufferedDecoder::~BufferedDecoder() {
+ if (d->state != Closed) closeDecoder();
+ delete d;
+}
+
+void BufferedDecoder::openDecoder(Decoder *decoder) {
+ if (d->state != Closed) closeDecoder();
+
+ d->decoder = decoder;
+ d->buffer = new AudioBuffer(d->buffer_size);
+ d->state = Open;
+}
+
+void BufferedDecoder::closeDecoder() {
+ if (d->state == Closed) return;
+ if (d->state != Open) stop();
+
+ delete d->buffer;
+ d->buffer = 0;
+ d->decoder = 0;
+ d->state = Closed;
+}
+
+void BufferedDecoder::start()
+{
+ if (d->state != Open) return;
+
+ d->halt = false;
+ d->seek_pos = -1;
+
+ d->buffer->reset();
+
+ if (pthread_create(&d->thread, 0, run_decoder, d) == 0) {
+ d->running = true;
+ }
+
+ d->state = Playing;
+}
+
+void BufferedDecoder::stop() {
+ if (d->state == Closed || d->state == Open) return;
+
+ if (d->state != Playing) {
+ // Stop fading
+ delete d->xfader;
+ d->xfader = 0;
+ }
+
+ d->buffer->release();
+
+ if (d->running) {
+ d->halt = true;
+ pthread_join(d->thread, 0);
+ d->running = false;
+ }
+
+ d->state = Open;
+}
+
+bool BufferedDecoder::readFrame(AudioFrame* frame)
+{
+ if (d->state == Closed || eof()) return false;
+ if (d->state == Open) start();
+
+ // Potentially blocking..
+ if (d->buffer->get(frame, d->blocking)) {
+ if (d->state == XFadingSeek) {
+ if(!d->xfader->doFrame(frame)) {
+ delete d->xfader;
+ d->xfader = 0;
+ d->state = Playing;
+ }
+ }
+ return true;
+ }
+ else
+ return false;
+}
+
+long BufferedDecoder::length() {
+ if (d->decoder)
+ return d->decoder->length();
+ else
+ return -1;
+}
+
+long BufferedDecoder::position() {
+ long pos = -1;
+ pos = d->seek_pos;
+ if (pos > 0) return pos;
+ if (d->buffer) {
+ pos = d->buffer->position();
+ if (pos > 0) return pos;
+ }
+ if (d->decoder) {
+ pos = d->decoder->position();
+ }
+ return pos;
+}
+
+bool BufferedDecoder::eof() {
+ return d->buffer && d->buffer->eof();
+}
+
+bool BufferedDecoder::error() {
+ return d->decoder && d->decoder->error();
+}
+
+bool BufferedDecoder::seekable() {
+ if (d->decoder)
+ return d->decoder->seekable();
+ else
+ return false;
+}
+
+bool BufferedDecoder::seek(long pos) {
+ if (d->state == Closed) return false;
+ if (!d->decoder->seekable()) return false;
+ if (d->state == Open) {
+ return d->decoder->seek(pos);
+ }
+
+ if (d->fading_time > 0 && !d->buffer->empty()) {
+ d->xfader = new CrossFader(d->fading_time*2);
+ fillFader();
+ d->state = XFadingSeek;
+ }
+
+ d->seek_pos = pos;
+ d->buffer->flush();
+
+ // we have to assume the seek will go well at this point
+ return true;
+}
+
+void BufferedDecoder::pause() {
+ if (d->state == Closed || d->state == Open || d->state == Paused) return;
+ /*
+ if (d->state == Playing && !d->buffer->empty()) {
+ d->xfader = new CrossFader(d->fading_time);
+ fillFader();
+ d->state = FadingOut;
+ } */
+
+ d->buffer->pause();
+
+ d->state = Paused;
+}
+
+void BufferedDecoder::resume() {
+ if (d->state != Paused);
+ /*
+ if (d->state == Playing || d->state == Paused || d->state == FadingOut) {
+ d->xfader = new CrossFader(d->fading_time);
+ d->state = FadingIn;
+ } */
+
+ d->buffer->resume();
+
+ d->state = Playing;
+}
+
+
+void BufferedDecoder::setBufferSize(int size) {
+ d->buffer_size = size;
+ if (d->state == Open) {
+ delete d->buffer;
+ d->buffer = new AudioBuffer(d->buffer_size);
+ }
+
+}
+
+void BufferedDecoder::setFadingTime(int time) {
+ d->fading_time = time;
+}
+
+void BufferedDecoder::setBlockingRead(bool block) {
+ d->blocking = block;
+}
+
+AudioBuffer * BufferedDecoder::buffer() const {
+ return d->buffer;
+}
+
+const AudioConfiguration* BufferedDecoder::audioConfiguration() {
+ // ### Might the buffer contain a different configuration?
+ if (d->decoder)
+ return d->decoder->audioConfiguration();
+ else
+ return 0;
+}
+
+void BufferedDecoder::fillFader() {
+ if (!d->xfader) return;
+
+ AudioFrame frame;
+ while (true) // fill the crossfader with what might be in buffer
+ {
+ if (!d->buffer->get(&frame, false)) break;
+ if (!d->xfader->writeFrame(&frame)) break;
+ }
+}
+
+} // namespace