diff options
Diffstat (limited to 'akode/lib/buffered_decoder.cpp')
-rw-r--r-- | akode/lib/buffered_decoder.cpp | 298 |
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 |