/* MPEG/WAVE Sound library

   (C) 1997 by Jung woo-jae */

// Mpegtoraw.cc
// Server which get mpeg format and put raw format.

 

#include "mpegsound.h"





static void *threadlinker(void *arg) {
  ((Mpegtoraw *)arg)->threadedplayer();

  return NULL;
}




Mpegtoraw::Mpegtoraw(MpegAudioStream* mpegAudioStream,
		     MpegAudioHeader* mpegAudioHeader,
		     OutputStream *output,
		     bool doFloat) : doFloat(doFloat)
{
  forcetomonoflag=false;
  downfrequency=0;
	
  this->mpegAudioStream=mpegAudioStream;
  this->mpegAudioHeader=mpegAudioHeader;
  this->output=output;

  int sampleSize = doFloat?sizeof(float):sizeof(short int);
  // from decoder thread to "output" thread
  // buffer must be bigger than: 2*sizeof(sampleSize)*RAWDATASIZE
  bufferInputStream=new BufferInputStream(2*sampleSize*RAWDATASIZE,
					  sampleSize*RAWDATASIZE,
					  "audioLoopback");
  lThreadRun=true;
  lastAudioStamp=new TimeStamp();
  
  int inputstereo=mpegAudioHeader->getInputstereo();
  outputstereo=inputstereo;
  initialize();
  lInThread=false;
  commandPipe=new CommandPipe();

  abs_thread_create(&thread,threadlinker,this);

  // we send a ping. because this signal is synchron
  // we block until the thread is started and
  // has read the ping singnal
  Command cmd(_COMMAND_PING);
  commandPipe->sendCommand(cmd);

}

Mpegtoraw::~Mpegtoraw() {
  lThreadRun=false;
  void* ret;
  bufferInputStream->close();
  bufferInputStream->clear();
  
  abs_thread_join(thread,&ret);


  delete bufferInputStream;
  delete lastAudioStamp;
  delete commandPipe;
  
}







void Mpegtoraw::setforcetomono(bool flag) {
  forcetomonoflag=flag;
  if (forcetomonoflag==true) {
    outputstereo=false;
  }
}

void Mpegtoraw::setdownfrequency(int value) {
  downfrequency=0;
  if(value)downfrequency=1;
}

bool Mpegtoraw::getforcetomono() {
  return forcetomonoflag;
}

int Mpegtoraw::getdownfrequency() {
  return downfrequency;
}






// Convert mpeg to raw
// Mpeg headder class
void Mpegtoraw::initialize() {

  int i;

  scalefactor=SCALE;
  calcbufferoffset=15;
  currentcalcbuffer=0;
  loutputEnabled=true;


  for(i=CALCBUFFERSIZE-1;i>=0;i--)
    calcbufferL[0][i]=calcbufferL[1][i]=
    calcbufferR[0][i]=calcbufferR[1][i]=0.0;






  layer3initialize();


  lastfrequency=-1;
  laststereo=-1;
};




void Mpegtoraw::flushrawdata(void) {
  TimeStamp* startStamp=&startAudioStamp;
  

  

  // fabulous bad hack to trick all these underexperienced users
  // who really think: wow! the mp3 player has 0.1% cpu usage
  // we simply poll on the end of the buffer and trick
  // the kernel schedular

  int rawdatasize = doFloat?(rawdataoffset<<2):(rawdataoffset<<1);
  char *rawdatastart = doFloat?(char *)floatdata:(char *)rawdata;

#ifdef PTHREADEDMPEG
  int freeWrite=bufferInputStream->getFreeSpace();

  while(freeWrite < rawdatasize) {
    freeWrite=bufferInputStream->getFreeSpace();
    TimeWrapper::usleep(10000);
  }
  
#endif

  bufferInputStream->write(rawdatastart,rawdatasize,startStamp);
  rawdataoffset=0;
}






/***************************/
/* Playing in multi-thread */
/***************************/

/* Player routine */
void Mpegtoraw::threadedplayer(void) {
  TimeStamp* start;
  TimeStamp* end;
  int bytesToDeliver;
  int bytePos;
  int readBytes;
  lInThread=true;
  Command threadCommand(_COMMAND_NONE);

  // we wait for the handshake "the ping"
  commandPipe->waitForCommand();
  commandPipe->hasCommand(&threadCommand);
  

  while(lThreadRun)  {
    //bytesToDeliver=output->getPreferredDeliverSize();
    bytesToDeliver=2048;
    
    if (bytesToDeliver <= 0) {
      bytesToDeliver=4096;
    }
    bytePos=bufferInputStream->getBytePosition();
    start=bufferInputStream->getTimeStamp(bytePos);
    readBytes=bufferInputStream->read((char*)deliverBuffer,bytesToDeliver);
    
    end=bufferInputStream->getTimeStamp(bytePos);
    if (readBytes==bytesToDeliver) {
      output->audioPlay(start,end,(char*)deliverBuffer,readBytes);
      // invalidate PTS info
      start->setPTSFlag(false);
    }
  }
    
}

void Mpegtoraw::clearbuffer(void) {
  bufferInputStream->clear();
}






// Convert mpeg to raw
int Mpegtoraw::decode(MpegAudioHeader* mpegHeader) {
  clearrawdata();
  int layer=mpegHeader->getLayer();
  TimeStamp* stamp=mpegAudioStream->getCurrentAudioTimeStamp(mpegHeader);


  stamp->copyTo(&startAudioStamp);

  // make this stamp invalid for synchronisation
  stamp->setPTSFlag(false);

  if (layer==3) {
    extractlayer3();
  } else {
    if(layer==2) {
      extractlayer2();
    } else {
      if(layer==1) {
	extractlayer1();
      }
    }
  }
  //flushrawdata();
  return true;

}
