//**************************************************************************
//*                     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: MP2/MP3 file handling (with MP3 decoder parsing)

#include "in_file.h"

#ifdef MPXPLAY_LINK_INFILE_MPX

#include "in_rawau.h"
#include "tagging.h"
#include "newfunc\newfunc.h"
#include "decoders\ad_mp3\mp3dec.h"

typedef struct mp3_demux_data_s{
 struct mpxplay_bitstreambuf_s *bs;
 unsigned long databegin;  // ie: end of ID3v2
 unsigned long datalen;
 struct mp3_decoder_data mp3d;
}mp3_demux_data_s;

#define INMP3_BSBUF_SIZE      256
#define INMP3_ID3V2_HEADSIZE   10
#define INMP3_XING_HEADSIZE    64
#define XING_FRAMES_FLAG 0x00000001

static unsigned long mpx_check_xingheader(struct mp3_decoder_data *mp3d,unsigned char *headbufp)
{
 int xing_flags,offs;

 if(mpxdec_syncinfo(mp3d,headbufp)<0)
  return 0;

 if(!mp3d->lsf){ // mpeg1
  if(mp3d->mpg_chmode!=MPG_MD_MONO)
   offs=(32+4);
  else
   offs=(17+4);
 }else{          // mpeg2
  if(mp3d->mpg_chmode!=MPG_MD_MONO)
   offs=(17+4);
  else
   offs=(9+4);
 }

 headbufp+=offs;
 if(PDS_GETB_LE32(headbufp)!=PDS_GET4C_LE32('X','i','n','g'))
  return 0;
 headbufp+=4;
 xing_flags=PDS_GETB_BE32(headbufp);
 headbufp+=4;
 if(xing_flags&XING_FRAMES_FLAG)
  return PDS_GETB_BE32(headbufp);

 return 0;
}

static unsigned int inmp3_infile_open(struct mpxplay_filehand_buffered_func_s *fbfs,void *fbds,char *filename,struct mpxplay_infile_info_s *miis)
{
 struct mp3_demux_data_s *mp3i;
 unsigned long id3v2size,allframes,retcode,samplenum_per_frame;

 retcode=INRAWAU_infile_open(fbfs,fbds,filename,miis);
 if(retcode!=MPXPLAY_ERROR_INFILE_OK)
  return retcode;

 mp3i=(struct mp3_demux_data_s *)calloc(1,sizeof(mp3_demux_data_s));
 if(!mp3i)
  goto err_out_open;
 miis->private_data=mp3i;

 mp3i->bs=mpxplay_bitstream_alloc(INMP3_BSBUF_SIZE);
 if(!mp3i->bs)
  goto err_out_open;

 if(mpxplay_bitstream_fill(mp3i->bs,fbfs,fbds,INMP3_ID3V2_HEADSIZE)!=MPXPLAY_ERROR_MPXINBUF_OK)
  goto err_out_open;

 id3v2size=mpxplay_tagging_id3v2_totalsize(mpxplay_bitstream_getbufpos(mp3i->bs));
 if(id3v2size){
  if(fbfs->fseek(fbds,id3v2size,SEEK_SET)>0){
   mp3i->databegin=id3v2size;
   mpxplay_bitstream_reset(mp3i->bs);
  }
 }

 mp3i->datalen=fbfs->filelength(fbds)-mp3i->databegin; // ??? - ID3v1 (128)

 if(mpxplay_bitstream_fill(mp3i->bs,fbfs,fbds,INMP3_XING_HEADSIZE)!=MPXPLAY_ERROR_MPXINBUF_OK)
  goto err_out_open;

 allframes=mpx_check_xingheader(&mp3i->mp3d,mpxplay_bitstream_getbufpos(mp3i->bs));
 if(allframes && mp3i->mp3d.freq){ // VBR
  miis->allframes=allframes;
  //miis->audio_stream->bs_framesize=mp3i->datalen/allframes+1; // average (not good, if the allframes is wrong)
  miis->audio_stream->bs_framesize=MPXDEC_FRAMESIZE_MAX/2;
  samplenum_per_frame=mpxplay_infile_get_samplenum_per_frame(mp3i->mp3d.freq);
  miis->timemsec=(long)(1000.0*(float)(allframes*samplenum_per_frame)/(float)mp3i->mp3d.freq);
  if(miis->timemsec)
   miis->audio_decoder_infos->bitrate=(long)((float)mp3i->datalen*8.0/(float)miis->timemsec);
 }

 return MPXPLAY_ERROR_INFILE_OK;

err_out_open:
 return MPXPLAY_ERROR_INFILE_CANTOPEN;
}

static void INMP3_infile_close(struct mpxplay_filehand_buffered_func_s *fbfs,void *fbds,struct mpxplay_infile_info_s *miis)
{
 struct mp3_demux_data_s *mp3i=miis->private_data;
 if(mp3i){
  mpxplay_bitstream_free(mp3i->bs);
  free(mp3i);
 }
 INRAWAU_infile_close(fbfs,fbds,miis);
}

long INMP3_infile_fseek(struct mpxplay_filehand_buffered_func_s *fbfs,void *fbds,struct mpxplay_infile_info_s *miis,long newframenum)
{
 struct mp3_demux_data_s *mp3i=miis->private_data;
 long newfilepos;

 pds_ftoi((float)newframenum*(float)mp3i->datalen/(float)miis->allframes,&newfilepos);
 newfilepos+=mp3i->databegin;

 if(fbfs->fseek(fbds,newfilepos,SEEK_SET)<0)
  return MPXPLAY_ERROR_INFILE_EOF;

 return newframenum;
}

//------------------------------------------------------------------

static int INMP2_infile_open(struct mpxplay_filehand_buffered_func_s *fbfs,void *fbds,char *filename,struct mpxplay_infile_info_s *miis)
{
 int retcode;

 retcode=inmp3_infile_open(fbfs,fbds,filename,miis);
 if(retcode!=MPXPLAY_ERROR_INFILE_OK)
  return retcode;

 miis->audio_stream->wave_id=MPXPLAY_WAVEID_MP2;
 miis->longname="Layer II";

 return MPXPLAY_ERROR_INFILE_OK;
}

struct mpxplay_infile_func_s IN_MP2_funcs={
 (MPXPLAY_TAGTYPE_PUT_SUPPORT(MPXPLAY_TAGTYPE_ID3V1|MPXPLAY_TAGTYPE_ID3V2|MPXPLAY_TAGTYPE_APETAG)
 |MPXPLAY_TAGTYPE_PUT_PRIMARY(MPXPLAY_TAGTYPE_ID3V1)),
 NULL,
 NULL,
 &INMP2_infile_open,
 &INMP2_infile_open,
 &INMP2_infile_open,
 &INMP3_infile_close,
 &INRAWAU_infile_decode,
 &INMP3_infile_fseek,
 NULL,
 NULL,
 NULL,
 NULL,
 {"MP2",NULL}
};

static int INMP3_infile_open(struct mpxplay_filehand_buffered_func_s *fbfs,void *fbds,char *filename,struct mpxplay_infile_info_s *miis)
{
 int retcode;

 retcode=inmp3_infile_open(fbfs,fbds,filename,miis);
 if(retcode!=MPXPLAY_ERROR_INFILE_OK)
  return retcode;

 miis->audio_stream->wave_id=MPXPLAY_WAVEID_MP3;
 miis->longname="LayerIII";

 return MPXPLAY_ERROR_INFILE_OK;
}

struct mpxplay_infile_func_s IN_MP3_funcs={
 (MPXPLAY_TAGTYPE_PUT_SUPPORT(MPXPLAY_TAGTYPE_ID3V1|MPXPLAY_TAGTYPE_ID3V2|MPXPLAY_TAGTYPE_APETAG)
 |MPXPLAY_TAGTYPE_PUT_PRIMARY(MPXPLAY_TAGTYPE_ID3V1)),
 NULL,
 NULL,
 &INMP3_infile_open,
 &INMP3_infile_open,
 &INMP3_infile_open,
 &INMP3_infile_close,
 &INRAWAU_infile_decode,
 &INMP3_infile_fseek,
 NULL,
 NULL,
 NULL,
 NULL,
 {"MP3",NULL}
};

#endif // MPXPLAY_LINK_INFILE_MPX
