//**************************************************************************
//*                     This file is part of the                           *
//*                      Mpxplay - audio player.                           *
//*                  The source code of Mpxplay is                         *
//*        (C) copyright 1998-2008 by PDSoft (Attila Padar)                *
//*                http://mpxplay.sourceforge.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: PCM (WAV) "decoding"

#include "mpxplay.h"
#include "au_mixer\mix_func.h"
#include <malloc.h>

#define ADPCM_MAX_CHANNELS 8

typedef struct pcm_decoder_data_s{
 mpxplay_bitstreambuf_s *bs;
 unsigned int wave_id;
 unsigned int channels;
 unsigned int bytespersample;
}pcm_decoder_data_s;

mpxp_uint8_t adpcm_channel_matrix[ADPCM_MAX_CHANNELS-2][ADPCM_MAX_CHANNELS]={
//{MPXPLAY_PCMOUTCHAN_FRONT_LEFT,  MPXPLAY_PCMOUTCHAN_DISABLED,MPXPLAY_PCMOUTCHAN_DISABLED,MPXPLAY_PCMOUTCHAN_DISABLED,MPXPLAY_PCMOUTCHAN_DISABLED,MPXPLAY_PCMOUTCHAN_DISABLED,MPXPLAY_PCMOUTCHAN_DISABLED,MPXPLAY_PCMOUTCHAN_DISABLED},
//{MPXPLAY_PCMOUTCHAN_FRONT_LEFT,MPXPLAY_PCMOUTCHAN_FRONT_RIGHT,  MPXPLAY_PCMOUTCHAN_DISABLED,MPXPLAY_PCMOUTCHAN_DISABLED,MPXPLAY_PCMOUTCHAN_DISABLED,MPXPLAY_PCMOUTCHAN_DISABLED,MPXPLAY_PCMOUTCHAN_DISABLED,MPXPLAY_PCMOUTCHAN_DISABLED},
{MPXPLAY_PCMOUTCHAN_FRONT_LEFT,MPXPLAY_PCMOUTCHAN_FRONT_RIGHT,MPXPLAY_PCMOUTCHAN_FRONT_CENTER,  MPXPLAY_PCMOUTCHAN_DISABLED,MPXPLAY_PCMOUTCHAN_DISABLED,MPXPLAY_PCMOUTCHAN_DISABLED,MPXPLAY_PCMOUTCHAN_DISABLED,MPXPLAY_PCMOUTCHAN_DISABLED},//3.0
{MPXPLAY_PCMOUTCHAN_FRONT_LEFT,MPXPLAY_PCMOUTCHAN_FRONT_RIGHT,MPXPLAY_PCMOUTCHAN_REAR_LEFT,MPXPLAY_PCMOUTCHAN_REAR_RIGHT,  MPXPLAY_PCMOUTCHAN_DISABLED,MPXPLAY_PCMOUTCHAN_DISABLED,MPXPLAY_PCMOUTCHAN_DISABLED,MPXPLAY_PCMOUTCHAN_DISABLED},// 4.0
{MPXPLAY_PCMOUTCHAN_FRONT_LEFT,MPXPLAY_PCMOUTCHAN_FRONT_RIGHT,MPXPLAY_PCMOUTCHAN_FRONT_CENTER,MPXPLAY_PCMOUTCHAN_REAR_LEFT,MPXPLAY_PCMOUTCHAN_REAR_RIGHT,  MPXPLAY_PCMOUTCHAN_DISABLED,MPXPLAY_PCMOUTCHAN_DISABLED,MPXPLAY_PCMOUTCHAN_DISABLED},// 5.0
{MPXPLAY_PCMOUTCHAN_FRONT_LEFT,MPXPLAY_PCMOUTCHAN_FRONT_RIGHT,MPXPLAY_PCMOUTCHAN_FRONT_CENTER,MPXPLAY_PCMOUTCHAN_LFE,MPXPLAY_PCMOUTCHAN_REAR_LEFT,MPXPLAY_PCMOUTCHAN_REAR_RIGHT,MPXPLAY_PCMOUTCHAN_DISABLED,MPXPLAY_PCMOUTCHAN_DISABLED},   // 5.1
{MPXPLAY_PCMOUTCHAN_FRONT_LEFT,MPXPLAY_PCMOUTCHAN_FRONT_RIGHT,MPXPLAY_PCMOUTCHAN_FRONT_CENTER,MPXPLAY_PCMOUTCHAN_LFE,MPXPLAY_PCMOUTCHAN_REAR_LEFT,MPXPLAY_PCMOUTCHAN_REAR_RIGHT,MPXPLAY_PCMOUTCHAN_REAR_CENTER,MPXPLAY_PCMOUTCHAN_DISABLED},// 6.1 ???
//{MPXPLAY_PCMOUTCHAN_FRONT_LEFT,MPXPLAY_PCMOUTCHAN_FRONT_RIGHT,MPXPLAY_PCMOUTCHAN_FRONT_CENTER,MPXPLAY_PCMOUTCHAN_LFE,MPXPLAY_PCMOUTCHAN_REAR_CENTER,MPXPLAY_PCMOUTCHAN_SIDE_LEFT,MPXPLAY_PCMOUTCHAN_SIDE_RIGHT,MPXPLAY_PCMOUTCHAN_DISABLED},// 6.1 ???
{MPXPLAY_PCMOUTCHAN_FRONT_LEFT,MPXPLAY_PCMOUTCHAN_FRONT_RIGHT,MPXPLAY_PCMOUTCHAN_FRONT_CENTER,MPXPLAY_PCMOUTCHAN_LFE,MPXPLAY_PCMOUTCHAN_REAR_LEFT,MPXPLAY_PCMOUTCHAN_REAR_RIGHT,MPXPLAY_PCMOUTCHAN_SIDE_LEFT,MPXPLAY_PCMOUTCHAN_SIDE_RIGHT},// 7.1
};

static void AD_PCM_close(struct mpxplay_audio_decoder_info_s *adi);

static int adpcmdec_config(struct pcm_decoder_data_s *pcmi, struct mpxplay_audio_decoder_info_s *adi,struct mpxplay_streampacket_info_s *spi)
{
 int new_bytespersample;

 if(!adi->outchannels)
  return MPXPLAY_ERROR_INFILE_CANTOPEN;

 if((spi->wave_id == MPXPLAY_WAVEID_PCM_FLOAT) && (adi->bits == 64))
  spi->wave_id = MPXPLAY_WAVEID_PCM_F64LE; // !!!
 if((spi->wave_id == MPXPLAY_WAVEID_PCM_FLOAT) || (spi->wave_id == MPXPLAY_WAVEID_PCM_F32BE)){
  funcbit_enable(adi->infobits, ADI_FLAG_FLOATOUT);
  adi->bits = 1; // sample scale bits
  new_bytespersample = sizeof(mpxp_float_t);
 } else if((spi->wave_id == MPXPLAY_WAVEID_PCM_F64LE) || (spi->wave_id == MPXPLAY_WAVEID_PCM_F64BE)){
  funcbit_enable(adi->infobits, ADI_FLAG_FLOATOUT);
  adi->bits=1; // sample scale bits
  new_bytespersample = sizeof(mpxp_double_t);
 } else if(spi->wave_id == MPXPLAY_WAVEID_PCM_DVD) {
  funcbit_disable(adi->infobits, ADI_FLAG_FLOATOUT);
  new_bytespersample = 2 * adi->bits / 8;
 } else {
  funcbit_disable(adi->infobits, ADI_FLAG_FLOATOUT);
  new_bytespersample = (adi->bits + 7) / 8;
 }
 adi->bytespersample = new_bytespersample; // TODO: check that PCM decoder overwrites source decoder bytespersample

 spi->block_align = adi->outchannels * new_bytespersample;

 if((pcmi->wave_id != spi->wave_id) || (pcmi->channels != adi->outchannels) || (pcmi->bytespersample != new_bytespersample)){
  mpxplay_bitstream_free(pcmi->bs);
  pcmi->bs = mpxplay_bitstream_alloc(spi->block_align);
  if(!pcmi->bs)
   return MPXPLAY_ERROR_INFILE_MEMORY;
  pcmi->wave_id = spi->wave_id;
  pcmi->channels = adi->outchannels;
  pcmi->bytespersample = new_bytespersample;
  spi->bs_leftbytes = 0;
  spi->bs_usedbytes = 0;
 }

 adi->pcm_framelen = spi->bs_framesize / adi->bytespersample / adi->outchannels;

 if(!adi->chanmatrix && (adi->outchannels > 2) && (adi->outchannels <= ADPCM_MAX_CHANNELS))
  adi->chanmatrix = &adpcm_channel_matrix[adi->outchannels-1-2][0];

 return MPXPLAY_ERROR_INFILE_OK;
}

static int adpcmdec_filltexts(struct mpxplay_audio_decoder_info_s *adi,struct mpxplay_streampacket_info_s *spi)
{
 if(!adi->bitratetext && ((spi->wave_id==MPXPLAY_WAVEID_PCM_FLOAT) || (spi->wave_id==MPXPLAY_WAVEID_PCM_F32BE))){
  adi->bitratetext=pds_malloc(MPXPLAY_ADITEXTSIZE_BITRATE+8);
  if(!adi->bitratetext)
   return MPXPLAY_ERROR_INFILE_MEMORY;
  pds_strcpy(adi->bitratetext, "32-float");
 }
 if(!adi->bitratetext && ((spi->wave_id==MPXPLAY_WAVEID_PCM_F64LE) || (spi->wave_id==MPXPLAY_WAVEID_PCM_F64BE))){
  adi->bitratetext=pds_malloc(MPXPLAY_ADITEXTSIZE_BITRATE+8);
  if(!adi->bitratetext)
   return MPXPLAY_ERROR_INFILE_MEMORY;
  pds_strcpy(adi->bitratetext, "64-float");
 }

 if(!adi->channeltext && (adi->outchannels > 5)){
  adi->channeltext=pds_malloc(MPXPLAY_ADITEXTSIZE_CHANNEL+8);
  if(adi->channeltext)
   sprintf(adi->channeltext,"%d.%d chan",((adi->outchannels>5)? (adi->outchannels-1):adi->outchannels),((adi->outchannels>5)? 1:0)); // FIXME: lfe
 }

 return MPXPLAY_ERROR_INFILE_OK;
}

static int AD_PCM_open(struct mpxplay_audio_decoder_info_s *adi,struct mpxplay_streampacket_info_s *spi)
{
 struct pcm_decoder_data_s *pcmi;
 int retVal;

 pcmi=(struct pcm_decoder_data_s *)pds_calloc(1,sizeof(struct pcm_decoder_data_s));
 if(!pcmi)
  return MPXPLAY_ERROR_INFILE_MEMORY;
 adi->private_data=pcmi;

 if((retVal = adpcmdec_config(pcmi, adi, spi)) != MPXPLAY_ERROR_INFILE_OK)
  goto err_out_cantopen;
 adpcmdec_filltexts(adi, spi);

 funcbit_disable(spi->flags, MPXPLAY_SPI_FLAG_NEED_PARSING);

 return retVal;

err_out_cantopen:
 AD_PCM_close(adi);
 return retVal;
}

static void AD_PCM_close(struct mpxplay_audio_decoder_info_s *adi)
{
 struct pcm_decoder_data_s *pcmi=(struct pcm_decoder_data_s *)adi->private_data;
 if(pcmi){
  mpxplay_bitstream_free(pcmi->bs);
  if(adi->bitratetext){
   pds_free(adi->bitratetext);
   adi->bitratetext = NULL;
  }
  if(adi->channeltext){
   pds_free(adi->channeltext);
   adi->channeltext = NULL;
  }
  pds_free(pcmi);
 }
}

static int AD_PCM_decode(struct mpxplay_audio_decoder_info_s *adi,struct mpxplay_streampacket_info_s *spi)
{
 struct pcm_decoder_data_s *pcmi=(struct pcm_decoder_data_s *)adi->private_data;
 unsigned long savedbytes, newbytes, allbytes, i;
 mpxp_uint8_t *bufptr,*src,*sbuf;
#ifdef MPXPLAY_LINK_ORIGINAL_FFMPEG
 int retVal;

 if((retVal = adpcmdec_config(pcmi, adi, spi)) != MPXPLAY_ERROR_INFILE_OK)
  return retVal;
#endif

 savedbytes = mpxplay_bitstream_leftbytes(pcmi->bs);
 newbytes = spi->bs_leftbytes;
 allbytes = savedbytes + newbytes;

 if(allbytes < spi->block_align){
  spi->bs_usedbytes = mpxplay_bitstream_putbytes(pcmi->bs, spi->bitstreambuf, spi->bs_leftbytes);
  return MPXPLAY_ERROR_INFILE_NODATA;
 }

 if(savedbytes)
  mpxplay_bitstream_readbytes(pcmi->bs,adi->pcm_bufptr,savedbytes);

 if(newbytes){
  unsigned long savebytes;
  allbytes-=allbytes%spi->block_align;
  newbytes=allbytes-savedbytes;
  pds_memcpy(adi->pcm_bufptr+savedbytes,spi->bitstreambuf,newbytes);
  savebytes=spi->bs_leftbytes-newbytes;
  mpxplay_bitstream_putbytes(pcmi->bs,spi->bitstreambuf+newbytes,savebytes);
  spi->bs_usedbytes=spi->bs_leftbytes;
 }

 adi->pcm_samplenum=(savedbytes+newbytes)/pcmi->bytespersample;

 switch(spi->wave_id){
  case MPXPLAY_WAVEID_PCM_SBE:if(adi->bits<=8) break;
  case MPXPLAY_WAVEID_PCM_F32BE:
  case MPXPLAY_WAVEID_PCM_F64BE:
   bufptr=adi->pcm_bufptr;
   for(i=0;i<adi->pcm_samplenum;i++,bufptr+=pcmi->bytespersample)
    pds_mem_reverse(bufptr,pcmi->bytespersample); // !!! slow
   if(spi->wave_id!=MPXPLAY_WAVEID_PCM_F64BE)
    break;
  case MPXPLAY_WAVEID_PCM_F64LE:
   aumixer_cvbits_float64le_to_float32le((PCM_CV_TYPE_S *)adi->pcm_bufptr,adi->pcm_samplenum);
   break;
  case MPXPLAY_WAVEID_PCM_DVD:
   bufptr=adi->pcm_bufptr;
   src=bufptr;
   sbuf=alloca(spi->block_align);
   for(i=0;i<adi->pcm_samplenum;i++){
    unsigned int c=adi->outchannels;
    mpxp_uint8_t *sptr=sbuf,*src8=sptr+4*c;
    pds_memcpy(sbuf,src,spi->block_align);
    switch(adi->bits){
     case 20: // not tested
      do{
       PDS_PUTB_LE24(bufptr,(((mpxp_uint32_t)PDS_GETB_BE16(sptr)<<8)|(mpxp_uint32_t)(src8[0]&0xf0)));
       bufptr+=3;sptr+=2;
       PDS_PUTB_LE24(bufptr,(((mpxp_uint32_t)PDS_GETB_BE16(sptr)<<8)|((mpxp_uint32_t)(src8[0]&0x0f)<<4)));
       bufptr+=3;sptr+=2;src8++;
      }while(--c);
      break;
     case 24:
      do{
       PDS_PUTB_LE24(bufptr,(((mpxp_uint32_t)PDS_GETB_BE16(sptr)<<8)|(mpxp_uint32_t)*src8));
       bufptr+=3;sptr+=2;src8++;
       PDS_PUTB_LE24(bufptr,(((mpxp_uint32_t)PDS_GETB_BE16(sptr)<<8)|(mpxp_uint32_t)*src8));
       bufptr+=3;sptr+=2;src8++;
      }while(--c);
      break;
    }
    src+=spi->block_align;
   }
   adi->pcm_samplenum*=2;
   break;
 }

 if(funcbit_test(spi->flags,MPXPLAY_SPI_FLAG_SILENTBLOCK))
  funcbit_enable(adi->infobits,ADI_CNTRLBIT_SILENTBLOCK);
 else
  funcbit_disable(adi->infobits,ADI_CNTRLBIT_SILENTBLOCK);

 return MPXPLAY_ERROR_INFILE_OK;
}

static void AD_PCM_clearbuff(struct mpxplay_audio_decoder_info_s *adi,unsigned int seektype)
{
 struct pcm_decoder_data_s *pcmi=(struct pcm_decoder_data_s *)adi->private_data;
 mpxplay_bitstream_reset(pcmi->bs);
}

struct mpxplay_audio_decoder_func_s AD_PCM_funcs={
 0,
 NULL,
 NULL,
 NULL,
 &AD_PCM_open,
 &AD_PCM_close,
 NULL,
 NULL,
 &AD_PCM_decode,
 &AD_PCM_clearbuff,
 NULL,
 NULL,
 0,
 0,
 {{MPXPLAY_WAVEID_PCM_SLE,"PCM"},{MPXPLAY_WAVEID_PCM_SBE,"PCM"},
 {MPXPLAY_WAVEID_PCM_ULE,"PCM"},{MPXPLAY_WAVEID_PCM_UBE,"PCM"}, // !!! works at 8 bit only (others are not implemented)
 {MPXPLAY_WAVEID_PCM_DVD,"PCM"},
 {MPXPLAY_WAVEID_PCM_FLOAT,"PCM"},{MPXPLAY_WAVEID_PCM_F32BE,"PCM"},
 {MPXPLAY_WAVEID_PCM_F64LE,"PCM"},{MPXPLAY_WAVEID_PCM_F64BE,"PCM"},
 {0,NULL}}
};
