//**************************************************************************
//*                     This file is part of the                           *
//*                      Mpxplay - audio player.                           *
//*                  The source code of Mpxplay is                         *
//*        (C) copyright 1998-2006 by PDSoft (Attila Padar)                *
//*                    http://mpxplay.cjb.net                              *
//*                  email: mpxplay@freemail.hu                            *
//**************************************************************************
//*  This program is distributed in the hope that it will be useful,       *
//*  but WITHOUT ANY WARRANTY; without even the implied warranty of        *
//*  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.                  *
//*  Please contact with the author (with me) if you want to use           *
//*  or modify this source.                                                *
//**************************************************************************
//function: MP3 decoder handling
//requires the ad_mp3\mp3dec.lib and mp3dec.h files

//#define MPXPLAY_USE_DEBUGF 1
//#define ADMP3_DEBUG_OUTPUT stdout

#include "in_file.h"

#ifdef MPXPLAY_LINK_DECODER_MPX

#include "newfunc\newfunc.h"
#include "ad_mp3\mp3dec.h"

#define MPXDEC_HEADERSIZE       4
#define MPXDEC_BLOCKSAMPLES_MAX 1152

#define MPXDEC_BITSTREAM_BUFSIZE (MPXDEC_FRAMESIZE_MAX*2)

#define MPXDEC_SYNC_RETRY_BYTE   (MPXDEC_FRAMESIZE_MAX*8) // will not reach these
#define MPXDEC_SYNC_RETRY_FRAME  64                       // in ad_mp3_sync_frame

typedef struct mp3_decoder_data_s{
 struct mpxplay_bitstreambuf_s *bs;
 struct mp3_decoder_data *mp3d;
 unsigned int lastframesize;
 unsigned int pcm_samplenum;
 unsigned int error;
 unsigned int goodframe_count;
 unsigned int goodframe_limit;
 unsigned int gooddec_count;
 unsigned int gooddec_limit;
}mp3_decoder_data_s;

static int ad_mp3_sync_frame(struct mp3_decoder_data_s *mp3i,unsigned int headsync);
static int ad_mp3_assign_values(struct mp3_decoder_data_s *mp3i,struct mpxplay_audio_decoder_info_s *adi);

static int AD_MP3_open(struct mpxplay_audio_decoder_info_s *adi,struct mpxplay_streampacket_info_s *spi)
{
 struct mp3_decoder_data_s *mp3i;
 struct mp3_decoder_data *mp3d;

 mp3i=calloc(1,sizeof(struct mp3_decoder_data_s));
 if(!mp3i)
  return MPXPLAY_ERROR_INFILE_MEMORY;

 adi->private_data=mp3i;

 mp3i->mp3d=mpxdec_init();
 if(!mp3i->mp3d)
  return MPXPLAY_ERROR_INFILE_MEMORY;
 mp3d=mp3i->mp3d;

 mp3i->bs=mpxplay_bitstream_alloc(MPXDEC_BITSTREAM_BUFSIZE);
 if(!mp3i->bs)
  return MPXPLAY_ERROR_INFILE_MEMORY;

 // decoder can work without parsing too, the following settings are for this
 // it doesn't work at single MP3 files, only at containers (where we get freq and filechannels values)
 switch(adi->wave_id){
  case MPXPLAY_WAVEID_MP2:mp3d->lay=2;break;
  case MPXPLAY_WAVEID_MP3:
                  default:mp3d->lay=3;break;
 }
 if((adi->channelcfg==CHM_LEFT) || (adi->channelcfg==CHM_DOWNMIX))
  mp3d->outchannels=1;
 else
  mp3d->outchannels=adi->filechannels; // !!!
 mp3d->synthdata->outchannels=mp3d->outchannels;
 adi->outchannels=mp3d->outchannels;

 if(adi->freq && (adi->freq<=24000)) // !!!
  mp3d->lsf=1;
 mp3i->pcm_samplenum=(MPXDEC_BLOCKSAMPLES_MAX>>mp3d->lsf)*mp3d->outchannels;
 adi->pcm_framelen=(MPXDEC_BLOCKSAMPLES_MAX>>mp3d->lsf);

 if(mp3d->lay==3){
  mp3i->goodframe_limit=2; // dynamic bit allocation
  mp3i->gooddec_limit=2;   // mdct12/36 overlap
 }else{ // lay 2
  mp3i->goodframe_limit=1;
  mp3i->gooddec_limit=0;
 }

 adi->bits=16;
 adi->infobits|=ADI_FLAG_OWN_SPECTANAL;
#ifndef MPXDEC_INTEGER_OUTPUT
 adi->infobits|=ADI_FLAG_FLOATOUT;//|ADI_FLAG_FPUROUND_CHOP;
 adi->infobits|=ADI_FLAG_FPUROUND_CHOP;
#endif
 if(adi->infobits&ADI_CNTRLBIT_BITSTREAMOUT)
  adi->infobits|=ADI_FLAG_BITSTREAMOUT;

 return MPXPLAY_ERROR_INFILE_OK;
}

static void AD_MP3_close(struct mpxplay_audio_decoder_info_s *adi)
{
 struct mp3_decoder_data_s *mp3i=(struct mp3_decoder_data_s *)adi->private_data;
 if(mp3i){
  mpxplay_bitstream_free(mp3i->bs);
  mpxdec_close(mp3i->mp3d);
  free(mp3i);
 }
}

static int AD_MP3_parse_frame(struct mpxplay_audio_decoder_info_s *adi,struct mpxplay_streampacket_info_s *spi)
{
 struct mp3_decoder_data_s *mp3i=(struct mp3_decoder_data_s *)adi->private_data;
 int retcode;

 spi->bs_usedbytes=mpxplay_bitstream_putbytes(mp3i->bs,spi->bitstreambuf,spi->bs_leftbytes);

 mpxplay_debugf(ADMP3_DEBUG_OUTPUT,"parse lb:%4d ub:%4d gfc:%d",spi->bs_leftbytes,spi->bs_usedbytes,mp3i->goodframe_count);

 mp3i->goodframe_limit=3; // for head sync

 retcode=ad_mp3_sync_frame(mp3i,1);
 if(retcode!=MPXPLAY_ERROR_INFILE_OK)
  return retcode;

 if(!spi->bs_framesize)
  spi->bs_framesize=mp3i->lastframesize;

 ad_mp3_assign_values(mp3i,adi);

 return MPXPLAY_ERROR_INFILE_OK;
}

static int AD_MP3_decode(struct mpxplay_audio_decoder_info_s *adi,struct mpxplay_streampacket_info_s *spi)
{
 struct mp3_decoder_data_s *mp3i=(struct mp3_decoder_data_s *)adi->private_data;
 int retcode;

 if(adi->infobits&ADI_CNTRLBIT_BITSTREAMOUT){
  spi->bs_usedbytes=mpxplay_bitstream_putbytes(mp3i->bs,spi->bitstreambuf,spi->bs_leftbytes);
  retcode=ad_mp3_sync_frame(mp3i,0);
  if(retcode!=MPXPLAY_ERROR_INFILE_OK)
   return retcode;
  mpxplay_bitstream_readbytes(mp3i->bs,adi->pcm_bufptr,mp3i->lastframesize);
  adi->pcm_samplenum=mp3i->lastframesize;
  return MPXPLAY_ERROR_INFILE_OK;
 }

 if(mp3i->lastframesize){
  mpxplay_bitstream_skipbytes(mp3i->bs,mp3i->lastframesize);
  mp3i->lastframesize=0;
 }
 spi->bs_usedbytes=mpxplay_bitstream_putbytes(mp3i->bs,spi->bitstreambuf,spi->bs_leftbytes);
 mpxplay_debugf(ADMP3_DEBUG_OUTPUT,"decode ub:%4d lb:%4d gfc:%4d gdc:%4d gdl:%d",spi->bs_usedbytes,spi->bs_leftbytes,mp3i->goodframe_count,mp3i->gooddec_count,mp3i->gooddec_limit);

 retcode=ad_mp3_sync_frame(mp3i,0);
 if(retcode!=MPXPLAY_ERROR_INFILE_OK)
  return retcode;

 if(mp3i->error){
  mp3i->gooddec_count=0;
  mp3i->error=0;
 }

 if(mpxdec_decode_part1(mp3i->mp3d,adi->channelcfg)==MPXDEC_ERROR_OK)
  mp3i->gooddec_count++;
 else{
  mp3i->gooddec_count=0;
  //mpxdec_reset_decoding(mp3i->mp3d);
 }

 if(mp3i->gooddec_count>=mp3i->gooddec_limit){
  mpxdec_decode_part2(mp3i->mp3d->synthdata,adi->pcm_bufptr);
  adi->pcm_samplenum=mp3i->pcm_samplenum;
 }

 return MPXPLAY_ERROR_INFILE_OK;
}

static void AD_MP3_clearbuff(struct mpxplay_audio_decoder_info_s *adi,unsigned int seektype)
{
 struct mp3_decoder_data_s *mp3i=(struct mp3_decoder_data_s *)adi->private_data;
 mpxplay_bitstream_reset(mp3i->bs);
 mp3i->lastframesize=0;
 mp3i->goodframe_count=0;
 mp3i->gooddec_count=0;
 if(seektype&MPX_SEEKTYPE_BOF){
  mpxdec_reset_bitstream(mp3i->mp3d);
  mp3i->goodframe_count=mp3i->goodframe_limit;
  mp3i->gooddec_count=mp3i->gooddec_limit;
 }else{
  mp3i->goodframe_count=0;
  mp3i->gooddec_count=0;
 }

 mpxdec_reset_decoding(mp3i->mp3d);
 if(seektype&(MPX_SEEKTYPE_BOF|MPX_SEEKTYPE_PAUSE))
  mpxdec_reset_synth(mp3i->mp3d);
}

//------------------------------------------------------------------------
static int ad_mp3_sync_frame(struct mp3_decoder_data_s *mp3i,unsigned int headsync)
{
 unsigned int retry_frame=MPXDEC_SYNC_RETRY_FRAME;
 unsigned int retry_byte=MPXDEC_SYNC_RETRY_BYTE;
 do{
  int framesize;
  if(mpxplay_bitstream_leftbytes(mp3i->bs)<=MPXDEC_HEADERSIZE)
   return MPXPLAY_ERROR_INFILE_NODATA;

  framesize=mpxdec_syncinfo(mp3i->mp3d,mpxplay_bitstream_getbufpos(mp3i->bs));
  if(framesize<=0){
   //mpxplay_debugf(ADMP3_DEBUG_OUTPUT,"sync fail %8.8X %8.8X",PDS_GETB_BE32(mpxplay_bitstream_getbufpos(mp3i->bs)),mp3i->mp3d->firsthead);
   if(headsync)
    mp3i->mp3d->firsthead=0;
   mp3i->goodframe_count=0;
   mp3i->error=MPXDEC_ERROR_BITSTREAM;
   mpxplay_bitstream_skipbytes(mp3i->bs,1);
   if(!(--retry_byte))
    break;
   continue;
  }
#ifdef MPXPLAY_USE_DEBUGF
  //else
  // mpxplay_debugf(ADMP3_DEBUG_OUTPUT,"sync OK   %8.8X %8.8X",PDS_GETB_BE32(mpxplay_bitstream_getbufpos(mp3i->bs)),mp3i->mp3d->firsthead);
#endif

  if(mpxplay_bitstream_leftbytes(mp3i->bs)<framesize){
   if(!mp3i->goodframe_count && headsync)
    mp3i->mp3d->firsthead=0;
   return MPXPLAY_ERROR_INFILE_NODATA;
  }

  if(mpxdec_read_frame(mp3i->mp3d,mpxplay_bitstream_getbufpos(mp3i->bs))==MPXDEC_ERROR_OK){
   mp3i->goodframe_count++;
   if(mp3i->goodframe_count>=mp3i->goodframe_limit){
    mp3i->lastframesize=framesize;
    return MPXPLAY_ERROR_INFILE_OK;
   }else{
    mpxplay_bitstream_skipbytes(mp3i->bs,framesize);
    continue;
   }
  }

  if(headsync)
   mp3i->mp3d->firsthead=0;
  mp3i->goodframe_count=0;
  mp3i->error=MPXDEC_ERROR_BITSTREAM;
  mpxplay_bitstream_skipbytes(mp3i->bs,framesize); // skips frame on error
  if(!(--retry_frame))
   break;
 }while(1);

 return MPXPLAY_ERROR_INFILE_EOF;
}

static int ad_mp3_assign_values(struct mp3_decoder_data_s *mp3i,struct mpxplay_audio_decoder_info_s *adi)
{
 struct mp3_decoder_data *mp3d=mp3i->mp3d;

 switch(mp3d->mpg_chmode){
  case MPG_MD_DUALCHAN    :adi->channeltext="DualChan";break;
  case MPG_MD_JOINT_STEREO:if(mp3d->mpg_chmode_ext&0x1)
                            adi->channeltext="i-Stereo";
                           else
                            adi->channeltext="msStereo";
                           break;
 }

 adi->freq=mp3d->freq;
 adi->filechannels=mp3d->filechannels;
 if((adi->channelcfg==CHM_LEFT) || (adi->channelcfg==CHM_DOWNMIX))
  mp3d->outchannels=1;
 else
  mp3d->outchannels=mp3d->filechannels;
 adi->outchannels=mp3d->outchannels;
 mp3d->synthdata->outchannels=mp3d->outchannels;

 adi->pcm_framelen=(MPXDEC_BLOCKSAMPLES_MAX>>mp3d->lsf);
 mp3i->pcm_samplenum=(MPXDEC_BLOCKSAMPLES_MAX>>mp3d->lsf)*mp3d->outchannels;

#ifdef MPXDEC_ENABLE_FREEFORMAT
 if(mp3d->infobits&MP3DI_INFOBIT_FREEFORMAT){
  if(!adi->bitrate)
   adi->bitrate=mp3d->bitrate=(mp3d->fsize_curr*adi->freq+(576>>mp3d->lsf)*(1000/8))/((MPXDEC_BLOCKSAMPLES_MAX>>mp3d->lsf)*(1000/8)); // rounding
  mp3d->firsthead&=MPXDEC_HEADMASK_FREEFORMAT;
 }else
#else
 if(!adi->bitrate) // not XING-VBR (didn't get bitrate in in_mp3.c)
  adi->bitrate=mp3d->bitrate;
#endif

 // for decoding
 if(mp3d->lay==3){
  mp3i->goodframe_limit=2;
  if(mp3d->bitrate<=96)
   mp3i->gooddec_limit=3;
  else
   if(mp3d->bitrate<256)
    mp3i->gooddec_limit=2;
   else
    mp3i->gooddec_limit=1;
 }else{ // lay 2
  mp3i->goodframe_limit=1;
  mp3i->gooddec_limit=0;
 }

 return MPXPLAY_ERROR_INFILE_OK;
}

struct mpxplay_audio_decoder_func_s AD_MP3_funcs={
 0,
 &mpxdec_preinit,
 NULL,
 &AD_MP3_open,
 &AD_MP3_close,
 NULL,
 &AD_MP3_parse_frame,
 &AD_MP3_decode,
 &AD_MP3_clearbuff,
 NULL,
 NULL,
 MPXDEC_FRAMESIZE_MAX,
 MPXDEC_BLOCKSAMPLES_MAX,
 {{MPXPLAY_WAVEID_MP2,"MP2"},{MPXPLAY_WAVEID_MP3,"MP3"},{0,NULL}}
};

#endif // MPXPLAY_LINK_DECODER_MPX
