summaryrefslogtreecommitdiffstats
path: root/akode_artsplugin/akodePlayObject_impl.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'akode_artsplugin/akodePlayObject_impl.cpp')
-rw-r--r--akode_artsplugin/akodePlayObject_impl.cpp487
1 files changed, 487 insertions, 0 deletions
diff --git a/akode_artsplugin/akodePlayObject_impl.cpp b/akode_artsplugin/akodePlayObject_impl.cpp
new file mode 100644
index 00000000..78708e13
--- /dev/null
+++ b/akode_artsplugin/akodePlayObject_impl.cpp
@@ -0,0 +1,487 @@
+/* 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., 59 Temple Place - Suite 330,
+ Boston, MA 02111-1307, 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);