You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
299 lines
6.8 KiB
299 lines
6.8 KiB
/* 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
|