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/mpeglib_artsplug/splayPlayObject_impl.cpp

501 lines
12 KiB

/*
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<Arts::mcopbyte> *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<Arts::mcopbyte>* currentPacket;
currentPacket=(Arts::DataPacket<Arts::mcopbyte>*) 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"<<endl;
exit(0);
}
memcpy(inputbuffer,ptr,bytes);
ptr=inputbuffer;
}
framer->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"<<endl;
exit(0);
}
}
//cout << "rest-last:"<<rest<<" cnt:"<<cnt<<endl;
if (rest == 0) {
arts_debug("packet processed");
currentPacket->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;i<wantSamples;i++) {
left[i] = right[i] = 0.0;
}
return;
}
// else (file access)
getMoreSamples(wantSamples*2);
}
// Yep, we have enough data
// now check if we need resampling.
AudioFrame* audioFrame=frameQueue->getCurrent();
double wav_samplingRate=(double)audioFrame->getFrequenceHZ();
/* difference between the sampling rates in percent */
float diff = fabs(wav_samplingRate-samplingRateFloat) / samplingRateFloat;
//cout << "wav_samplingRate:"<<wav_samplingRate<<endl;
/*
* efficient optimized case:
* 1. decoder -> 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;i<wantSamples;i++) {
left[i] = right[i] = 0.0;
}
frameQueue->forwardStreamDouble(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; i<wantSamples; i++) {
left[i] = right[i] = 0.0;
}
if (lStreaming) {
processQueue();
}
}
// ok, eof stop.
/*
if (haveSamples < wantSamples) {
halt();
}
*/
}
void SplayPlayObject_impl::streamEnd() {
arts_debug("streamEnd");
if (file != NULL) {
fclose(file);
file=NULL;
}
return;
}
void SplayPlayObject_impl::checkResampleBuffer(int size) {
if (resampleBufferSize != size) {
if (resampleBuffer != NULL) {
delete resampleBuffer;
}
resampleBuffer = new float[size];
resampleBufferSize=size;
}
}
//
// This method shows how we insert data in the framer,
// decode a frame and store it in the queue.
// This damned queue is necessary, becasue a sync out
// stream needs a guranteed streamsize == samples.
// an async stream would simply send a packet
// and we are done.
void SplayPlayObject_impl::getMoreSamples(int needLen) {
while( (feof(file) == false) && (frameQueue->getLen() < 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"<<endl;
exit(0);
}
}
if (feof(file)==true) {
halt();
return;
}
}
REGISTER_IMPLEMENTATION(SplayPlayObject_impl);
// vim:ts=8:sw=2:sts=2