/* 
 * Mpeg Audio Player (see version.h for version number)
 * ------------------------
 * copyright (c) 1995,1996,1997,1998,1999 by Michael Hipp, All rights reserved.
 * See also 'README' !
 *
 */

#include <stdlib.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <sys/time.h>
#include <sys/resource.h>

#include <errno.h>
#include <string.h>
#include <fcntl.h>

#include "mpg123.h"

#include "version.h"
#include "../mpeg_play.h"

#include <esd.h>

#define FIXED_RATE 44100

static void print_title (void);

int outfd;
double offset=0;

struct parameter param = {
  FALSE,                        /* silent operation */
  TRUE,                         /* resync after stream error */
  0,                            /* verbose level */
  FALSE,                        /* checkrange */
};

int32_t outscale = 32768;

static struct frame fr;
#define FRAMEBUFUNIT (18 * 64 * 4)


void set_synth_functions (struct frame *fr);

typedef struct MPG_struct
{
  double length;

  int nframes;
  off_t filesize;
  int lsf;
  int stereo;
  int bitrate;
  int frequency;
  int layer;
  double tpf;
  double bpf;
 
  int esd;
  int bufsize;

  int havexing;
  XHEADDATA xing;

  Eweid e;
  CFH c;
  enum mpegp_state state;
}
 *MPG;

static int screwup=0;


float
mult_to_vol (float m)
{
  if (m == 0.0)
    return 0.0;

  m = log10 (m);
  m += 2.5;
  m = m / 3.0;

  return m;
}

float
vol_to_mult (float m)
{
  if (m == 0.0)
    return 0.0;

  m *= 3.0;
  m -= 2.5;

  m = pow (10.0, m);

  return m;
}

void screw_up(void)
{
printf("SCREWUP....\n");
screwup++;
}

struct mpegp_time do_time(int s)
{
struct mpegp_time ret;

ret.secs=s;
ret.bsecs=s % 60;
ret.bmins=s/60;

return ret;
}

void
set_synth_functions (struct frame *fr)
{
  typedef int (*func) (real *, int, unsigned char *, int *);
  typedef int (*func_mono) (real *, unsigned char *, int *);
  int ds = fr->down_sample;

  static func funcs[4] = { synth_1to1,
    synth_2to1,
    synth_4to1,
    synth_ntom
  };

  static func_mono funcs_mono[2][4] = {
    {synth_1to1_mono2stereo,
     synth_2to1_mono2stereo,
     synth_4to1_mono2stereo,
     synth_ntom_mono2stereo},
    {synth_1to1_mono,
     synth_2to1_mono,
     synth_4to1_mono,
     synth_ntom_mono}
  };

  fr->synth = funcs[ds];
  fr->synth_mono = funcs_mono[0][ds];

}

void
tuneup_engine (MPG mpg, struct frame *fr)
{

  int32_t n = freqs[fr->sampling_frequency];
  int32_t m = FIXED_RATE;


  if (n == m)
    {
      fr->down_sample = 0;
      fr->down_sample_sblimit = SBLIMIT;
    }
  else
    {

      synth_ntom_set_step (n, m);

      if (n > m)
        {
          fr->down_sample_sblimit = SBLIMIT * m;
          fr->down_sample_sblimit /= n;
        }
      else
        {
          fr->down_sample_sblimit = SBLIMIT;
        }


      fr->down_sample = 3;
    }

  fr->single = -1;


  set_synth_functions (fr);
  init_layer3 (fr->down_sample_sblimit);


}


void tuneup_data(CFH c,MPG mpg,struct frame *fr)
{

 if (!mpg->havexing) {
  if (check_for_xing_header(c,fr,&mpg->xing)) {
  mpg->havexing++;
  mpg->nframes=mpg->xing.frames;

  printf("HAVE XING\n");

  read_frame(c,fr);
offset+=mpg->tpf;
  }
}

  mpg->filesize=c->size(c);

  mpg->bpf=compute_bpf(fr);

  if (!mpg->havexing)
    {
      mpg->nframes = mpg->filesize / mpg->bpf;
    }

  mpg->tpf = compute_tpf (fr);
  mpg->lsf = fr->lsf;
  mpg->stereo = fr->stereo;

  mpg->bitrate = tabsel_123[fr->lsf][fr->lay - 1][fr->bitrate_index];
  mpg->frequency = freqs[fr->sampling_frequency];
  mpg->layer = fr->lay;

  mpg->length = mpg->nframes * mpg->tpf ;

//  printf("Bitrate %d Length %f\n",mpg->bitrate,mpg->length);

}


void *mpegp_create(void)
{
  MPG mpg=(MPG) malloc(sizeof(struct MPG_struct));
  int out_format;
  int clip;
  


  if (sizeof (short) != 2)
    {
      fprintf (stderr, "Ouch SHORT has size of %d bytes (required: '2')\n",
               (int) sizeof (short));
      exit (1);
    }
  if (sizeof (int32_t) < 4)
    {
      fprintf (stderr,
               "Ouch LONG has size of %d bytes (required: at least 4)\n",
               (int) sizeof (int32_t));
    }

  bzero(mpg,sizeof(struct MPG_struct));

  mpg->state=MPEGP_STOPPED;
  mpg->e=NULL;

    out_format = ESD_BITS16 | ESD_STREAM | ESD_PLAY | ESD_STEREO ;

  mpg->bufsize = 16384 ;
  mpg->esd = esd_play_stream_fallback (out_format, FIXED_RATE, NULL, "mpeg");
  audiobufsize= (mpg->bufsize / 2)/2 ;

  pcm_sample = (unsigned char *) malloc ((audiobufsize  + 1024)*2);
    if (!pcm_sample) {
      perror ("malloc()");
      exit (1);
    }


  if (!param.quiet)
    print_title ();

  fr.down_sample=0;
  set_synth_functions (&fr);
  make_decode_tables (outscale);
  init_layer2 ();               /* inits also shared tables with layer1 */
  init_layer3 (fr.down_sample);


mpg->state=MPEGP_STOPPED;

{
float l,r;
extern int voll,volr;

l=mult_to_vol(0.8);
r=mult_to_vol(0.8);
voll=(int) (256.0*vol_to_mult(l));
volr=(int) (256.0*vol_to_mult(r));
}

return (void *) mpg;

}

void mpegp_stop(void *um)
{
MPG m=(MPG) um;

if (m->c) {
	m->c->close(m->c);
	m->c=NULL;
}


m->state=MPEGP_STOPPED;

}

static void startplayer(MPG m)
{
offset=0;
m->c->seek(m->c,0,SEEK_SET);

    read_frame (m->c,&fr);

    tuneup_engine (m, &fr);
    tuneup_data (m->c,m, &fr);

offset+=m->tpf;
}


int mpegp_cfhstatus(void *um)
{
MPG m=(MPG) um;

if (!m->c) return 0;

return (m->c->status(m->c));
}


int mpegp_play(void *um,Files f,Eweid e,CFH cfh)
{
MPG m=(MPG) um;

mpegp_stop(um);

//m->c=files_open(f,e,O_RDONLY);
m->c=cfh;
m->c->seek(m->c,0,SEEK_SET);
if (!m->c) return 1;

if (m->e) {
	Obj_Delete(m->e);
	m->e=NULL;
}
m->e=e;
//eweid_dup(e);

m->state=MPEGP_PLAYING;
m->havexing=0;

read_frame_init ();

startplayer(m);

return 0;
}



int mpegp_dispatch(void *um)
{
MPG mpg=(MPG) um;
int clip;


if (mpg->state!=MPEGP_PLAYING) {
  bzero(pcm_sample,2*audiobufsize);
  write(mpg->esd,pcm_sample,2*audiobufsize);
  return 0;
}


if (screwup) {
	screwup=0;
	startplayer(mpg);
}

//if (!mpg->c->canread(mpg->c)) return 0;

    if (!read_frame (mpg->c,&fr)) {
		mpegp_stop(um);

	printf("EOFEOFEOFEOF\n");
		return 1;
    }
		
offset+=mpg->tpf;

        if ((mpg->frequency != freqs[fr.sampling_frequency])
            || (mpg->layer != fr.lay))
          tuneup_engine (mpg, &fr);

        if ((mpg->lsf != fr.lsf) ||
            (mpg->stereo != fr.stereo) ||
            (mpg->bitrate != tabsel_123[fr.lsf][fr.lay - 1][fr.bitrate_index])
            || (mpg->frequency != freqs[fr.sampling_frequency])
            || (mpg->layer != fr.lay))
          tuneup_data (mpg->c,mpg, &fr);



        if (fr.error_protection)
          {
            getbits (16);       /* skip crc */
          }

	outfd=mpg->esd;

        /* do the decoding */
        clip = (fr.do_layer) (&fr, NULL);

        if (clip > 0 && param.checkrange)
          fprintf (stderr, "%d samples clipped\n", clip);


return 0;
  }


struct mpegp_status
mpegp_status(void *um)
{
MPG m=(MPG) um;
struct mpegp_status s;
extern int voll,volr;
bzero(&s,sizeof(s));

s.state=m->state;

s.length=do_time((int) m->length);
s.position=do_time((int) offset);

s.voll=mult_to_vol((1.0/256.0) * (float) voll);
s.volr=mult_to_vol((1.0/256.0) * (float) volr);

s.e=m->e;

return s;
}


int 
mpegp_fd(void *um)
{
MPG m=(MPG) um;

return m->esd;
}
void
mpegp_setvol (void *um, float l, float r)
{
extern int voll,volr;

  MPG m = (MPG) um;
voll=(int) (256.0*vol_to_mult(l));
volr=(int) (256.0*vol_to_mult(r));

}

static void do_seek(MPG m, int where)
{

 if (!m->havexing) {

   offset=(double) where;

   printf("Seek to %d/%d\n",where,(int) m->length);

   jump_to_frame(m->c,&fr, (int) (offset / m->tpf));
 } else {

 offset=(double) where;

 jump_to_byte(m->c,&fr, mpg123_seek_point(m->xing.toc, m->xing.bytes, ( offset * 100.0) / ((double) m->nframes * m->tpf)));

 }

}

void
mpegp_seek (void *um, int where)
{
  MPG m = (MPG) um;
  int try;

  if (!m)
    return;

  if (m->state!=MPEGP_PLAYING) return;

  for (try=0;try<20;++try) {
  do_seek(m,where);
offset+=m->tpf;
offset+=m->tpf;
  read_frame(m->c,&fr);
  if (read_frame(m->c,&fr)) return; /*OK*/
  
  /*Try again*/
  where++;

  }





}

#if 0
mpeg_delete()

  free (pcm_sample);
#endif

static void
print_title (void)
{
  fprintf (stderr,
           "High Performance MPEG 1.0/2.0/2.5 Audio Player for Layer 1, 2 and 3.\n");
  fprintf (stderr,
           "Version %s (%s). Written and copyrights by Michael Hipp.\n",
           prgVersion, prgDate);
  fprintf (stderr, "Uses code from various people. See 'README' for more!\n");
  fprintf (stderr,
           "THIS SOFTWARE COMES WITH ABSOLUTELY NO WARRANTY! USE AT YOUR OWN RISK!\n");
}
