/* base class for splay mp3 decoder Copyright (C) 2000 Martin Vogt This program 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. For more information look at the file COPYRIGHT in this package */ #include "splayPlayObject_impl.h" #include "../mpeglib/lib/splay/splayDecoder.h" #include "../mpeglib/lib/frame/audioFrameQueue.h" #include "../mpeglib/lib/splay/mpegAudioFrame.h" #include "debug.h" #define INPUT_SIZE 8192 /** Problems with streaming, which must be solved: ============================================== Deadlocks. I think we cannot have sync streams for input/output. This example is an OLD style PlayObject. (it uses sync streams) The task is to convert this into an async PlayObject. What does this PlayObject do: ----------------------------- Current: It reads data from a file and insert it into a queue. Future: A "remote" sender of raw data, can test how much bytes he can insert into the "framer". If the framer produces a frame, we decode it, and send an async dataPacket out. (Then the frameQueue is not necessary anymore -IMHO) */ SplayPlayObject_impl::SplayPlayObject_impl() { flpos=0.0; splay=new SplayDecoder(); frameQueue= new AudioFrameQueue(10,MP3FRAMESIZE,_FRAME_AUDIO_FLOAT); framer=new MpegAudioFrame(); arts_debug("outputStream created"); _state=Arts::posIdle; file=NULL; lStreaming=false; resampleBuffer=NULL; resampleBufferSize=0; currentPacket=NULL; currentPos=0; // ok, we store packets here. Someday I should make this a template. // use STL? maybe. packetQueue=new FrameQueue(10); // Note: The inputbuffer must NOT NOT NOT be on the stack! inputbuffer=new unsigned char[INPUT_SIZE]; } SplayPlayObject_impl::~SplayPlayObject_impl() { arts_debug("~SplayPlayObject_impl -s"); delete splay; delete frameQueue; delete framer; arts_debug("~SplayPlayObject_impl -e"); if (resampleBuffer != NULL) { delete resampleBuffer; } // // arts wants to free the packets and packetQueue too. // so we dequeue all packets here, before deleting packetQueue // otherwise we call delete on the packets twice. // while (packetQueue->getFillgrade() > 0) packetQueue->dequeue(); // now delete the (empty) queue delete packetQueue; delete [] inputbuffer; } bool SplayPlayObject_impl::loadMedia(const string &filename) { arts_debug("loadMedia"); if (file != NULL) { arts_fatal("~SplayPlayObject_impl already loaded"); } lStreaming=false; file=fopen(filename.c_str(),"r"); if (file == NULL) { arts_debug("splay cannot open file"); return false; } flpos=0.0; return true; } bool SplayPlayObject_impl::streamMedia(Arts::InputStream instream) { arts_debug("streamMedia"); lStreaming=true; currentStream = instream; Arts::StreamPlayObject self = Arts::StreamPlayObject::_from_base(_copy()); connect(currentStream, "outdata", self); return true; } void SplayPlayObject_impl::process_indata(Arts::DataPacket *inpacket) { arts_debug("receiving packet"); packetQueue->enqueue((Frame*)inpacket); if (packetQueue->getFillgrade() == 1) { currentPos=0; } processQueue(); } void SplayPlayObject_impl::processQueue() { unsigned char* ptr; if (packetQueue->getFillgrade() == 0) { return; } Arts::DataPacket* currentPacket; currentPacket=(Arts::DataPacket*) packetQueue->peekqueue(0); int rest=currentPacket->size-currentPos; int cnt=0; while( (rest > 0) && (frameQueue->emptyQueueCanRead()) ) { int state=framer->getState(); switch(state) { case FRAME_NEED: { int bytes=framer->canStore(); ptr=currentPacket->contents+currentPos; // don't read beyond the packet. if (bytes >= rest) { bytes=rest; // we must copy this, because we can commit a packet, // which then deletes the pointer to the packet. // but the framer still wants to access the data, later! if (bytes > INPUT_SIZE) { cout << "inputbuffer too small"<store(ptr,bytes); currentPos+=bytes; rest-=bytes; break; } case FRAME_WORK: framer->work(); break; case FRAME_HAS:{ AudioFrame* emptyFrame= frameQueue->emptyQueueDequeue(); if (splay->decode(framer->outdata(),framer->len(),emptyFrame)==true) { frameQueue->dataQueueEnqueue(emptyFrame); cnt++; } break; } default: cout << "unknown state in mpeg audio framing"<processed(); packetQueue->dequeue(); currentPos=0; } } string SplayPlayObject_impl::description() { arts_debug("description [GET1]"); string back; return back; } void SplayPlayObject_impl::description(const string &) { arts_debug("description [GET2]"); } Arts::poTime SplayPlayObject_impl::currentTime() { Arts::poTime time; return time; } Arts::poTime SplayPlayObject_impl::overallTime() { Arts::poTime time; return time; } Arts::poCapabilities SplayPlayObject_impl::capabilities() { arts_debug("capabilities"); return (Arts::poCapabilities)(Arts::capPause); } string SplayPlayObject_impl::mediaName() { arts_debug("mediaName"); string back; return back; } Arts::poState SplayPlayObject_impl::state() { return _state; } void SplayPlayObject_impl::play() { arts_debug("play:"); if (file == NULL) { arts_debug("file is NULL:"); if (lStreaming && _state != Arts::posPlaying) { currentStream.streamStart(); _state = Arts::posPlaying; } } else { _state = Arts::posPlaying; } } void SplayPlayObject_impl::seek(const class Arts::poTime& ) { arts_debug("SEEK - RESET in decoder."); framer->reset(); // and remove pre-decoded frame: frameQueue->clear(); return; } void SplayPlayObject_impl::pause() { arts_debug("pause"); _state=Arts::posPaused; } /* * * halt() (which the normal programmer would probably refer to as stop()) * should seek to the beginning and go into the posIdle state, like a just * opened PlayObject * */ void SplayPlayObject_impl::halt() { arts_debug("halt"); _state=Arts::posIdle; } void SplayPlayObject_impl::streamInit() { arts_debug("streamInit"); return; } void SplayPlayObject_impl::streamStart() { arts_debug("streamStart"); return; } void SplayPlayObject_impl::calculateBlock(unsigned long wantSamples) { unsigned long i; unsigned long haveSamples=frameQueue->getLen(); // the wantSamples*2 takes care that there is enough data // even if we have stereo. if (haveSamples < wantSamples*2) { if (lStreaming) { for(i=0;igetCurrent(); double wav_samplingRate=(double)audioFrame->getFrequenceHZ(); /* difference between the sampling rates in percent */ float diff = fabs(wav_samplingRate-samplingRateFloat) / samplingRateFloat; //cout << "wav_samplingRate:"< float rendering * 2. no resampling (i.e. artsd running @ 44100 Hz, playing an 44100 Hz mp3) */ if(diff < 0.02) { // no resample (easy+fast case) haveSamples=frameQueue->copy(left,right,wantSamples); for(i=haveSamples;iforwardStreamDouble(haveSamples); if (lStreaming) { processQueue(); } } else { /** FIXME: you can use this to implement pitchable playobject **/ double _speed = 1.0; // calculate "how fast" we consume input samples (speed = 2.0 means, // we need 2 input samples to generate one output sample) double speed = (double)wav_samplingRate/(double)(samplingRateFloat/_speed); // calculate how many samples we need to read from the frameQueue to // satisfy the request long readSamples = long((double)wantSamples*speed+8.0); checkResampleBuffer(readSamples*2); // read the samples // - readSamplesOk contains how much we really got // - we put the samples non-interleaved into the resampleBuffer, // first the left channel, then the right channel long readSamplesOk = frameQueue->copy(&resampleBuffer[0], &resampleBuffer[readSamples], readSamples); // check how many output samples we will be able to generate from that long samplesToConvert = long((double)readSamplesOk/speed)-4; if(samplesToConvert < 0) samplesToConvert = 0; if(samplesToConvert > wantSamples) samplesToConvert = wantSamples; // do the conversion Arts::interpolate_mono_float_float(samplesToConvert,flpos,speed, &resampleBuffer[0],left); Arts::interpolate_mono_float_float(samplesToConvert,flpos,speed, &resampleBuffer[readSamples],right); // calculate where we are now (as floating point position) in our // inputsample buffer flpos += (double)samplesToConvert * speed; // Good - so how many input samples we won't need anymore (for the // next request)? Skip them. int skip = (int)floor(flpos); if(skip) { frameQueue->forwardStreamDouble(skip); flpos = flpos - floor(flpos); } for(i=samplesToConvert; igetLen() < needLen) ) { // // This switch/case statement "partitions" the fileinput // into mp3frames. We directly decode them to pcmFrames. // // // There are three states. One is "I want Input, give me input" // The other is "I have enough input, dont' bother me and let // me do my work" // The last ist "I have a frame here, take care of this, or // I will continue with my work" int state=framer->getState(); int cnt=0; switch(state) { case FRAME_NEED: { int bytes=framer->canStore(); int read=fread(inputbuffer,1,bytes,file); if (read != bytes) { // read error. reset framer framer->reset(); continue; } // Note: The inputbuffer must NOT NOT NOT be on the stack! framer->store(inputbuffer,bytes); break; } case FRAME_WORK: framer->work(); break; case FRAME_HAS:{ AudioFrame* emptyFrame= frameQueue->emptyQueueDequeue(); if (splay->decode(framer->outdata(),framer->len(),emptyFrame)==true) { frameQueue->dataQueueEnqueue(emptyFrame); cnt++; } break; } default: cout << "unknown state in mpeg audio framing"<