
#include <stdio.h>
#include <string.h>
#include <errno.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdlib.h>

#include "madplayer.hpp"


MadPlayer::MadPlayer(char *file)
{
 char *ptr;
 waveplay=0;
 mp3in=fopen(file,"rb");
 ptr=file+strlen(file)-3;
 if(strcmp(ptr,"wav") == 0 || strcmp(ptr,"Wav") == 0 || strcmp(ptr,"WAV") == 0) waveplay=1;
 if(mp3in == NULL)
 {
  printf("Cannot open file %s\n",file);
  exit(1);
 }
 if(!waveplay)
 {
  mad_stream_init(&mp3stream);
  mad_frame_init(&mp3frame);
  mad_synth_init(&mp3synth);
  mad_timer_reset(&mp3timer);
  outputbuflen=0;
  outputbufptr=0;
 }
}

void MadPlayer::seek(int position)
{
 size_t ReadSize, Remaining;
 unsigned char *ReadStart;
 if(waveplay)
 {
  fseek(mp3in,4*position+44,SEEK_SET);
  return;
 }
 fseek(mp3in,0,SEEK_SET);
 mad_stream_init(&mp3stream);
 mad_timer_reset(&mp3timer);
 outputbuflen=0;
 outputbufptr=0; 
 // this is a workaround
 read(NULL,position);
 return;
 // for this buggy procedure. it seeks appr. 19.8 times too far, because
 // mad_header_decode(&mp3frame.header,&mp3stream); returns a too low
 // duration in header

 while( ((double)mp3timer.seconds+(double)mp3timer.fraction/(double)MAD_TIMER_RESOLUTION)*44100.0 < position-2000 )
 {
  if(mp3stream.buffer==NULL || mp3stream.error==MAD_ERROR_BUFLEN)
  {
   if(mp3stream.next_frame!=NULL)
   {
    Remaining=mp3stream.bufend-mp3stream.next_frame;
    memmove(InputBuffer,mp3stream.next_frame,Remaining);
    ReadStart=InputBuffer+Remaining;
    ReadSize=MP3BUFFERSIZE-Remaining;
   }
   else
   {
    ReadSize=MP3BUFFERSIZE;
    ReadStart=InputBuffer;
    Remaining=0;
   } 
  }
  ReadSize=fread(ReadStart,1,ReadSize,mp3in);
  if(ReadSize<=0)
  {
   return;
  }
  mad_stream_buffer(&mp3stream,InputBuffer,ReadSize+Remaining);
  mp3stream.error=(mad_error) 0;
  mad_header_decode(&mp3frame.header,&mp3stream);
  mad_timer_add(&mp3timer,mp3frame.header.duration);
  printf("%f \n",(double)mp3frame.header.duration.fraction/(double)MAD_TIMER_RESOLUTION);
  mp3stream.this_frame=mp3stream.next_frame;
 }
 read(NULL,position-(int)(((double)mp3timer.seconds+(double)mp3timer.fraction/(double)MAD_TIMER_RESOLUTION)*44100.0));
 
}

int MadPlayer::refill(void)
{
 size_t ReadSize, Remaining;
 unsigned char *ReadStart;
 int i;
 unsigned short Sample;
 if(mp3stream.buffer==NULL || mp3stream.error==MAD_ERROR_BUFLEN)
 {
  if(mp3stream.next_frame!=NULL)
  {
   Remaining=mp3stream.bufend-mp3stream.next_frame;
   memmove(InputBuffer,mp3stream.next_frame,Remaining);
   ReadStart=InputBuffer+Remaining;
   ReadSize=MP3BUFFERSIZE-Remaining;
  }
  else
  {
   ReadSize=MP3BUFFERSIZE;
   ReadStart=InputBuffer;
   Remaining=0;
  } 
  ReadSize=fread(ReadStart,1,ReadSize,mp3in);
  if(ReadSize<=0)
  {
   return -1;
  }
  mad_stream_buffer(&mp3stream,InputBuffer,ReadSize+Remaining);
  mp3stream.error=(mad_error) 0;
 }
 if(mad_frame_decode(&mp3frame,&mp3stream))
 {
  if(mp3stream.error == MAD_ERROR_BUFLEN) return 0;
  if(!MAD_RECOVERABLE(mp3stream.error)) return -1;
 }
 mad_timer_add(&mp3timer,mp3frame.header.duration);
 mad_synth_frame(&mp3synth,&mp3frame);
 for(i=0;i<mp3synth.pcm.length;i++)
 {
  Sample=mp3synth.pcm.samples[0][i]>>(MAD_F_FRACBITS-15);

  OutputBuffer[outputbuflen++]=Sample&0xff;
  OutputBuffer[outputbuflen++]=Sample>>8;

  if(MAD_NCHANNELS(&mp3frame.header)==2) Sample=mp3synth.pcm.samples[0][i]>>(MAD_F_FRACBITS-15);
  OutputBuffer[outputbuflen++]=Sample&0xff;
  OutputBuffer[outputbuflen++]=Sample>>8;
 } 
 return 0;
}

int MadPlayer::read(char *buffer,int count)
{
 int buflen=0;
 int process;
 count*=4;
 if(waveplay)
 {
  if(fread(buffer,1,count,mp3in) <=0 )
  {
   return -1;
  }
  return 0;
 }
 while(count >0)
 {
  if(outputbufptr == outputbuflen)
  {
   outputbufptr=0;
   outputbuflen=0;
   do
   {
    if(refill() == -1)
    {
     return -1;
    }
   }
   while(outputbuflen == 0);
  }
  process=count;
  if(process > outputbuflen-outputbufptr) process=outputbuflen-outputbufptr;
  if(buffer != NULL)
  {
   memcpy(buffer+buflen,OutputBuffer+outputbufptr,process);
   buffer+=process;
  }
  outputbufptr+=process;
  count-=process;
 }
 return 0;
}


MadPlayer::~MadPlayer(void)
{
 mad_synth_finish(&mp3synth);
 mad_frame_finish(&mp3frame);
 mad_stream_finish(&mp3stream);
 fclose(mp3in);
}


