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.
tdemultimedia/akode_artsplugin/akodePlayObject_impl.cpp

488 lines
11 KiB

/* akodePlayObject
Copyright (C) 2003-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 Street, Fifth Floor,
Boston, MA 02110-1301, USA.
*/
//#define AKODEARTS_SINGLETHREADED
//#define AKODEARTS_SRCRESAMPLING
#define AKODEARTS_FRAMEBUFFER 32
// The instream-buffer must be smaller than 64kbytes (1<<16)
#define AKODEARTS_INSTREAMBUFFER (1<<14)
#include <math.h>
#include <soundserver.h>
#include <audiosubsys.h>
#include <debug.h>
#include "config.h"
#include <akode/audioframe.h>
#include <akode/localfile.h>
#include <akode/mmapfile.h>
#include <akode/decoder.h>
#include <akode/resampler.h>
#include <akode/fast_resampler.h>
#include <akode/wav_decoder.h>
#include "arts_inputstream.h"
#ifndef AKODEARTS_SINGLETHREADED
#include <akode/buffered_decoder.h>
#endif
#ifdef AKODEARTS_SRCRESAMPLING
#define AKODEARTS_RESAMPLER "src"
#else
#define AKODEARTS_RESAMPLER "fast"
#endif
#include "akodePlayObject_impl.h"
using namespace Arts;
using namespace aKode;
akodePlayObject_impl::akodePlayObject_impl(const string &plugin)
: source(0)
, frameDecoder(0)
, decoder(0)
, bufferedDecoder(0)
, resampler(0)
, buffer(0)
, inBuffer(0)
, buf_pos(0)
, mState(posIdle)
, mSpeed(1.0)
, m_packetQueue(0)
, m_bytebuffer(0)
, m_fading(false)
, decoderPlugin(plugin)
, resamplerPlugin(AKODEARTS_RESAMPLER)
{
m_packetQueue = new queue<DataPacket<mcopbyte>*>;
if(!resamplerPlugin.isLoaded())
resamplerPlugin.load("fast");
}
akodePlayObject_impl::~akodePlayObject_impl()
{
delete m_packetQueue;
unload();
}
bool akodePlayObject_impl::loadPlugin(const string &plugin)
{
return decoderPlugin.load(plugin);
}
bool akodePlayObject_impl::streamMedia(Arts::InputStream inputstream)
{
arts_debug("akode: opening input-stream");
m_bytebuffer = new aKode::ByteBuffer(AKODEARTS_INSTREAMBUFFER);
instream = inputstream;
Arts::StreamPlayObject self = Arts::StreamPlayObject::_from_base(_copy());
connect(instream, "outdata", self, "indata");
source = new Arts_InputStream(instream, m_bytebuffer);
return loadSource();
}
bool akodePlayObject_impl::loadMedia(const string &filename)
{
arts_debug("akode: opening %s", filename.c_str());
source = new MMapFile(filename.c_str());
if (!source->openRO()) {
delete source;
source = new LocalFile(filename.c_str());
if (!source->openRO()) {
delete source;
source = 0;
return false;
}
}
source->close();
return loadSource();
}
bool akodePlayObject_impl::loadSource()
{
if (!decoderPlugin.isLoaded()) {
return false;
}
frameDecoder = decoderPlugin.openDecoder(source);
if (!frameDecoder) {
delete source;
source = 0;
arts_warning("akode: Could not open frame-decoder");
return false;
}
#ifndef AKODEARTS_SINGLETHREADED
bufferedDecoder = new BufferedDecoder();
bufferedDecoder->setBufferSize(AKODEARTS_FRAMEBUFFER);
bufferedDecoder->openDecoder(frameDecoder);
decoder = bufferedDecoder;
#else
decoder = frameDecoder;
#endif
return true;
}
string akodePlayObject_impl::description()
{
return "akodePlayObject";
}
Arts::InputStream akodePlayObject_impl::inputStream() {
return instream;
}
string akodePlayObject_impl::mediaName()
{
if (source)
return source->filename;
else
return string();
}
poCapabilities akodePlayObject_impl::capabilities()
{
return (poCapabilities)(capPause | capSeek);
}
poState akodePlayObject_impl::state()
{
return mState;
}
void akodePlayObject_impl::play()
{
arts_debug("akode: play");
if (!decoder) {
arts_warning("akode: No media loaded");
return;
}
if (mState == posIdle) {
mState = posPlaying;
if (!inBuffer) inBuffer = new AudioFrame;
if (!buffer) buffer = inBuffer;
buf_pos = 0;
} else
mState = posPlaying;
}
void akodePlayObject_impl::pause()
{
arts_debug("akode: pause");
mState = posPaused;
}
void akodePlayObject_impl::halt()
{
arts_debug("akode: halt");
if (mState == posIdle) return;
mState = posIdle;
unload();
}
void akodePlayObject_impl::unload()
{
arts_debug("akode: unload");
if (m_bytebuffer) m_bytebuffer->release();
#ifndef AKODEARTS_SINGLETHREADED
if (bufferedDecoder) {
bufferedDecoder->stop();
bufferedDecoder->closeDecoder();
delete bufferedDecoder;
bufferedDecoder = 0;
}
#endif
delete frameDecoder;
frameDecoder = 0;
decoder = 0;
if (buffer != inBuffer)
delete inBuffer;
delete buffer;
inBuffer = buffer = 0;
buf_pos = 0;
delete resampler;
resampler = 0;
delete source;
source = 0;
#ifndef AKODEARTS_SINGLETHREADED
delete m_bytebuffer;
m_bytebuffer = 0;
#endif
}
poTime akodePlayObject_impl::currentTime()
{
poTime time;
long pos;
if (decoder) {
pos = decoder->position(); // decoder time
if (pos < 0 ) pos = 0;
else
if (samplingRate > 0 && buffer)
{
float lpos = (float)(buf_pos-buffer->length) / (float)samplingRate; // local time
pos += (long)(lpos*1000.0);
}
}
else
pos = 0;
time.seconds = pos / 1000 ;
time.ms = pos % 1000;
return time;
}
poTime akodePlayObject_impl::overallTime()
{
poTime time;
long len;
if (decoder) {
len = decoder->length();
if (len < 0 ) len = 0;
}
else
len = 0;
time.seconds = len / 1000;
time.ms = len % 1000;
return time;
}
void akodePlayObject_impl::seek(const poTime &time)
{
arts_debug("akode: seek");
if (!decoder) {
arts_warning("akode: No media loaded");
return;
}
long akode_pos = time.seconds*1000+time.ms;
if (decoder->seek(akode_pos) && buffer) {
buffer->length = 0; // force re-read
buf_pos = 0;
}
}
bool akodePlayObject_impl::readFrame()
{
arts_debug("akode: readFrame");
if (!inBuffer || !decoder) return false;
if (m_bytebuffer) processQueue();
if(!decoder->readFrame(inBuffer)) {
if (decoder->eof()) {
arts_debug("akode: eof");
halt();
} else
if (decoder->error()) {
arts_debug("akode: error");
halt();
} else
buffer->length=0;
return false;
}
// Invalid frame from broken plugin
if (inBuffer->sample_rate == 0) return false;
if (samplingRate != inBuffer->sample_rate || mSpeed != 1.0) {
//arts_debug("akode: resampling to %d/%d", inBuffer->sample_rate, samplingRate);
if ( !buffer || buffer==inBuffer ) buffer = new AudioFrame;
if ( !resampler)
resampler = resamplerPlugin.openResampler();
resampler->setSampleRate(samplingRate);
resampler->setSpeed(mSpeed);
resampler->doFrame(inBuffer, buffer);
} else {
if ( buffer !=inBuffer) delete buffer;
buffer = inBuffer;
}
buf_pos = 0;
return true;
}
bool akodePlayObject_impl::eof()
{
if (!decoder || !buffer) return true;
else
return buf_pos>=buffer->length && decoder->eof();
}
// GCC's lack of good template support means this is easyist done using DEFINE
#define SEND_BLOCK(T) \
T* data = (T*)buffer->data[0]; \
j = i; bp = buf_pos; \
while (bp < buffer->length && j<count) { \
left[j] = data[bp]*scale; \
j++; bp++; \
} \
if (buffer->channels > 1) \
data = (T*)buffer->data[1]; \
j = i; bp = buf_pos; \
while (bp < buffer->length && j<count) { \
right[j] = data[bp]*scale; \
j++; bp++; \
}
void akodePlayObject_impl::calculateBlock(unsigned long cnt)
{
long count = (long)cnt;
// arts_debug("akode: calculateBlock");
long i = 0, j, bp;
if (!decoder) {
arts_warning("akode: No media loaded");
goto fill_out;
}
if (!buffer)
// Not playing yet
goto fill_out;
while ((mState == posPlaying || m_fading) && i<count) {
if (buf_pos >= buffer->length) {
buf_pos = 0;
if (!readFrame()) {
break;
}
}
if (buffer->channels > 2 || buffer->sample_width > 24 || buffer->sample_width == 0) {
arts_warning("akode: Incompatible media");
halt();
break;
}
signed char width = buffer->sample_width;
float scale = (float)(1<<(width-1));
if (width >= 0) {
scale = 1/scale;
if (width <= 8) {
SEND_BLOCK(int8_t);
i=j;
buf_pos=bp;
}
else if (width <= 16) {
SEND_BLOCK(int16_t);
i=j;
buf_pos=bp;
}
else {
SEND_BLOCK(int32_t);
i=j;
buf_pos=bp;
}
}
else {
scale = 1.0;
SEND_BLOCK(float);
i=j;
buf_pos=bp;
}
}
// fill-out the rest with silence
fill_out:
for (; i < count; i++) {
left[i] = right[i] = 0;
}
}
void akodePlayObject_impl::streamInit()
{
arts_debug("akode: streamInit");
}
void akodePlayObject_impl::streamStart()
{
arts_debug("akode: streamStart");
#ifndef AKODEARTS_SINGLETHREADED
bufferedDecoder->start();
#endif
}
void akodePlayObject_impl::streamEnd()
{
arts_debug("akode: streamEnd");
mState = posIdle;
if (decoder) unload();
}
void akodePlayObject_impl::speed(float newValue)
{
mSpeed = newValue;
}
float akodePlayObject_impl::speed()
{
return mSpeed;
}
void akodePlayObject_impl::process_indata(DataPacket<mcopbyte> *inpacket) {
arts_debug("akode: process_indata");
m_packetQueue->push(inpacket);
if (!m_bytebuffer) return;
processQueue();
}
void akodePlayObject_impl::processQueue()
{
while (!m_packetQueue->empty()) {
long freespace = m_bytebuffer->space();
DataPacket<mcopbyte> *inpacket = m_packetQueue->front();
if (!inpacket) return;
if (freespace >= inpacket->size) {
if (m_bytebuffer->write((char*)inpacket->contents, inpacket->size, false)) {
m_packetQueue->pop();
inpacket->processed();
}
} else
return;
}
if (instream.eof()) m_bytebuffer->close();
}
REGISTER_IMPLEMENTATION(akodePlayObject_impl);