//**************************************************************************
//*                     This file is part of the                           *
//*                      Mpxplay - audio player.                           *
//*                  The source code of Mpxplay is                         *
//*        (C) copyright 1998-2012 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: sofware-mixer routines - main

//#define MPXPLAY_USE_DEBUGF 1
#define MPXPLAY_DEBUG_OUTPUT NULL //stdout
//#define MPXPLAY_USE_DEBUGMSG 1

#include "mpxplay.h"
#include "mix_func.h"
#include "display\display.h"
#include <float.h>

extern struct mainvars mvps;
extern struct mpxplay_audioout_info_s au_infos;
extern unsigned int displaymode,crossfadepart,intsoundconfig;
extern unsigned int SOUNDLIMITvol;

extern struct one_mixerfunc_info MIXER_FUNCINFO_crossfader;
extern struct one_mixerfunc_info MIXER_FUNCINFO_swapchan;
extern struct one_mixerfunc_info MIXER_FUNCINFO_mute;
extern struct one_mixerfunc_info MIXER_FUNCINFO_balance;
extern struct one_mixerfunc_info MIXER_FUNCINFO_surround;
extern struct one_mixerfunc_info MIXER_FUNCINFO_tone_bass;
extern struct one_mixerfunc_info MIXER_FUNCINFO_tone_treble;
extern struct one_mixerfunc_info MIXER_FUNCINFO_tone_loudness;
extern struct one_mixerfunc_info MIXER_FUNCINFO_seekspeed;
extern struct one_mixerfunc_info MIXER_FUNCINFO_speed;
extern struct one_mixerfunc_info MIXER_FUNCINFO_autovolume;
extern struct one_mixerfunc_info MIXER_FUNCINFO_limiter;
extern struct one_mixerfunc_info MIXER_FUNCINFO_volume;

static struct one_mixerfunc_info *all_mixerfunc_info[MIXER_MAX_FUNCTIONS+1]={
 &MIXER_FUNCINFO_speed, // recommended to keep it on 1st place (to limit the buffer of other mixer functions, especially tone)
 &MIXER_FUNCINFO_tone_bass,
 &MIXER_FUNCINFO_crossfader, // last preprocess
 &MIXER_FUNCINFO_swapchan,
 &MIXER_FUNCINFO_surround,
 &MIXER_FUNCINFO_tone_loudness,
 &MIXER_FUNCINFO_tone_treble,// parallel dep. : loudness
 &MIXER_FUNCINFO_seekspeed,
 &MIXER_FUNCINFO_autovolume,
 &MIXER_FUNCINFO_mute,
 &MIXER_FUNCINFO_balance,
 &MIXER_FUNCINFO_limiter,    // last-1 (parallel dep.:surround,tone,(volume))
 &MIXER_FUNCINFO_volume,     // must be the last (parallel dep.:mute,balance,limiter)
 NULL
}; // !!! MIXER_BUILTIN_ in au_mixer.h

static unsigned int mixfunc_checkvar_setflags(struct mpxp_aumixer_passinfo_s *mpi,struct one_mixerfunc_info *infop,int functionnum);

unsigned int MIXER_controlbits;
static unsigned int MIXER_nb_mixerfuncs=MIXER_BUILTIN_FUNCTIONS;

static int aumixer_pcmoutbuf_realloc(struct mpxplay_audioout_info_s *aui,
  struct mpxpframe_s *frp,long blocksamples,float speed_expansion)
{
 struct mpxplay_audio_decoder_info_s *adi = frp->infile_infos->audio_decoder_infos;
 long newpcmoutbufsize;

 funcbit_smp_value_put(frp->pcmout_blocksize,(blocksamples*adi->outchannels));

 if(blocksamples < PCM_OUTSAMPLES)
  blocksamples = PCM_OUTSAMPLES;
 blocksamples += 1024; // tone control max (?) expansion (192000/200)
 blocksamples += 128;  // extra buffer overflow protection
 newpcmoutbufsize = (long)(speed_expansion*(float)(blocksamples*max(adi->outchannels,aui->chan_card))
                   *(float)(max(aui->freq_card,adi->freq))/(float)(adi->freq));
 newpcmoutbufsize *= sizeof(MPXPLAY_PCMOUT_FLOAT_T);
 if(frp->pcmout_bufsize < newpcmoutbufsize){
  mpxp_uint8_t *newbuf = (mpxp_uint8_t *)pds_malloc(newpcmoutbufsize);
  if(!newbuf)
   return 0;
  if(frp->pcmout_buffer){
   if(frp->pcmout_storedsamples)
    pds_memcpy(newbuf,frp->pcmout_buffer,(frp->pcmout_storedsamples*adi->bytespersample));
   free(frp->pcmout_buffer);
  }
  funcbit_smp_pointer_put(frp->pcmout_buffer,newbuf);
  funcbit_smp_value_put(frp->pcmout_bufsize,newpcmoutbufsize);
 }
 return 1;
}

static struct mpxp_aumixer_passinfo_s *aumixer_frpmpi_alloc(struct mpxpframe_s *frp)
{
 struct mpxp_aumixer_passinfo_s *mpi = frp->mpi;
 if(!mpi){
  mpi = (mpxp_aumixer_passinfo_s *)pds_calloc(1,sizeof(mpxp_aumixer_passinfo_s));
  if(!mpi)
   return NULL;
  funcbit_smp_pointer_put(frp->mpi,mpi);
 }
 return mpi;
}

//-------------------------------------------------------------------------
//configure mixer to the new audio file (decoding)
unsigned int MIXER_configure(struct mpxp_aumixer_main_info_s *mmi,struct mpxplay_audioout_info_s *aui,struct mpxpframe_s *frp)
{
 struct mpxplay_audio_decoder_info_s *adi = frp->infile_infos->audio_decoder_infos;
 struct mpxp_aumixer_passinfo_s *mpi = NULL;
 struct one_mixerfunc_info *infop_speed;
 long i,blocksamples;
 float speed_expansion;

 if(aui->card_infobits&AUINFOS_CARDINFOBIT_BITSTREAMOUT){
  frp->pcmout_blocksize = 1;
  MIXER_resetallfunc(mpi);
  return 1;
 }

 if(adi->freq < PCM_MIN_FREQ)
  return 0;

 infop_speed = MIXER_getfunction("MIX_SPEED");
 speed_expansion = 0.0;
 if(infop_speed)
  speed_expansion = (float)infop_speed->var_center/(float)infop_speed->var_min; // 2 by default
 if(speed_expansion < 2.0)
  speed_expansion = 2.0;

 //if(adi->freq==aui->freq_card)
  blocksamples = mpxplay_infile_get_samplenum_per_frame(adi->freq);
 //else
 // blocksamples = (long)((float)mpxplay_infile_get_samplenum_per_frame(aui->freq_card)*(float)adi->freq/(float)aui->freq_card);

 aumixer_pcmoutbuf_realloc(aui, frp, blocksamples, speed_expansion);

 if((frp->pcmout_bufsize/sizeof(MPXPLAY_PCMOUT_FLOAT_T))>(mmi->MIXER_int16pcm_bufsize/sizeof(PCM_CV_TYPE_S))){
  if(mmi->MIXER_int16pcm_buffer)
   free(mmi->MIXER_int16pcm_buffer);
  funcbit_smp_pointer_put(mmi->MIXER_int16pcm_buffer,((PCM_CV_TYPE_S *)pds_malloc(mmi->MIXER_int16pcm_bufsize)));
  if(!mmi->MIXER_int16pcm_buffer){
   funcbit_smp_value_put(mmi->MIXER_int16pcm_bufsize,0);
   return 0;
  }
  funcbit_smp_value_put(mmi->MIXER_int16pcm_bufsize,(frp->pcmout_bufsize/sizeof(MPXPLAY_PCMOUT_FLOAT_T)*sizeof(PCM_CV_TYPE_S)));
  funcbit_smp_pointer_put(aui->mvp->vds->pcm_data,mmi->MIXER_int16pcm_buffer);
  funcbit_smp_value_put(aui->mvp->vds->pcm_freq,aui->freq_card);
 }

 mpi = aumixer_frpmpi_alloc(frp);
 if(!mpi)
  return 0;

 funcbit_smp_pointer_put(mpi->control_cb, mpxplay_aumixer_control_cb);
 funcbit_smp_pointer_put(mpi->ccb_data  , mpi);
 funcbit_smp_pointer_put(mpi->pcm_sample, (PCM_CV_TYPE_S *)frp->pcmout_buffer);
 funcbit_smp_value_put(mpi->samplenum, 0);
 funcbit_smp_value_put(mpi->mixer_function_flags, 0);
 funcbit_smp_value_put(mpi->bytespersample_mixer, sizeof(PCM_CV_TYPE_F));
 funcbit_smp_value_put(mpi->freq_card, aui->freq_card);
 funcbit_smp_value_put(mpi->chan_card, aui->chan_card);
 funcbit_smp_value_put(mpi->bits_card, aui->bits_card);
 funcbit_smp_value_put(mpi->freq_song, adi->freq);
 funcbit_smp_value_put(mpi->chan_song, adi->outchannels);
 funcbit_smp_value_put(mpi->bits_song, adi->bits);
 funcbit_smp_pointer_put(mpi->mmi, mmi);
 funcbit_smp_pointer_put(mpi->aui, aui);
 funcbit_smp_pointer_put(mpi->mvp, aui->mvp);
 funcbit_smp_pointer_put(mpi->frp, frp);

 funcbit_smp_disable(mmi->mixer_infobits,AUINFOS_MIXERINFOBIT_CFFREQDIFF);

 if(crossfadepart){
  struct mpxpframe_s *frp1 = frp->fro;
  struct mpxp_aumixer_passinfo_s *mpi1 = frp1->mpi;
  struct mpxplay_audio_decoder_info_s *adi1 = frp1->infile_infos->audio_decoder_infos;
  unsigned long funcbit = 1;

  if(adi1->freq != adi->freq){
   //blocksamples=(long)((float)blocksamples*(float)adi1->freq/(float)adi->freq);
   blocksamples = (long)(((float)blocksamples*(float)adi1->freq+((adi1->freq<adi->freq)? ((float)adi->freq*0.49):0))/(float)adi->freq);
   aumixer_pcmoutbuf_realloc(aui,frp1,blocksamples,speed_expansion);
   funcbit_smp_enable(mmi->mixer_infobits,AUINFOS_MIXERINFOBIT_CFFREQDIFF);
  }else if((adi->freq != aui->freq_card) || (adi1->freq != aui->freq_card))
   funcbit_smp_enable(mmi->mixer_infobits,AUINFOS_MIXERINFOBIT_CFFREQDIFF);

  funcbit_smp_value_put(mpi1->freq_card, aui->freq_card);
  funcbit_smp_value_put(mpi->mixer_function_flags, mpi1->mixer_function_flags);
  pds_smp_memcpy(&mpi->mixerfuncs_private_datas[MIXER_BUILTIN_CFPREPROCESSNUM],
                 &mpi1->mixerfuncs_private_datas[MIXER_BUILTIN_CFPREPROCESSNUM],
                 (MIXER_nb_mixerfuncs - MIXER_BUILTIN_CFPREPROCESSNUM) * sizeof(struct mpxp_aumixer_passinfo_s *) );

  for(i=0; (i<MIXER_BUILTIN_CFPREPROCESSNUM); i++,funcbit<<=1 ){
   struct one_mixerfunc_info *infop = all_mixerfunc_info[i];
   if(infop && infop->function_init){
    mpi1->private_data = mpi1->mixerfuncs_private_datas[i];
    infop->function_init(mpi1,((mpi1->private_data)? MIXER_INITTYPE_REINIT:MIXER_INITTYPE_INIT));
    funcbit_smp_pointer_put(mpi1->mixerfuncs_private_datas[i], mpi1->private_data);
    mixfunc_checkvar_setflags(mpi1,infop,i);
    mpxplay_debugf(MPXPLAY_DEBUG_OUTPUT,"mfc1:%8.8X",mpi1->mixer_function_flags);

    mpi->private_data = mpi->mixerfuncs_private_datas[i];
    infop->function_init(mpi,((mpi->private_data)? MIXER_INITTYPE_REINIT:MIXER_INITTYPE_INIT));
    funcbit_smp_pointer_put(mpi->mixerfuncs_private_datas[i],mpi->private_data);
    mixfunc_checkvar_setflags(mpi,infop,i);
    if(mpi->mixer_function_flags&funcbit)
     infop->function_init(mpi,MIXER_INITTYPE_RESET); // !!! ??? we reset only the preprocess functions of the new file
   }
  }
 }else if(funcbit_test(intsoundconfig,INTSOUND_DECODER)){
  struct mpxp_aumixer_passinfo_s *mpi1 = frp->fro->mpi;
  funcbit_smp_value_put(mpi->mixer_function_flags, mpi1->mixer_function_flags);
  pds_smp_memxch((char *)&mpi->mixerfuncs_private_datas[0],
             (char *)&mpi1->mixerfuncs_private_datas[0],
             MIXER_BUILTIN_CFPREPROCESSNUM * sizeof(struct mpxp_aumixer_passinfo_s *) );
  pds_smp_memcpy(&mpi->mixerfuncs_private_datas[MIXER_BUILTIN_CFPREPROCESSNUM],
             &mpi1->mixerfuncs_private_datas[MIXER_BUILTIN_CFPREPROCESSNUM],
             (MIXER_nb_mixerfuncs - MIXER_BUILTIN_CFPREPROCESSNUM) * sizeof(struct mpxp_aumixer_passinfo_s *) );
 }

 MIXER_checkallfunc_dependencies(mpi,MIXER_INFOBIT_EXTERNAL_DEPENDENCY);
 MIXER_checkallfunc_dependencies(mpi,MIXER_INFOBIT_PARALLEL_DEPENDENCY);
 return 1;
}

// fill pcmout buffer (with pcmout_blocksize)
unsigned int AUMIXER_collect_pcmout_data(struct mpxpframe_s *frp,PCM_CV_TYPE_S *pcm_sample,unsigned int samplenum)
{
 struct mpxplay_audio_decoder_info_s *adi = frp->infile_infos->audio_decoder_infos;
 struct mpxp_aumixer_passinfo_s *mpi = frp->mpi;
 unsigned long leftspace;

 if(adi->infobits&ADI_CNTRLBIT_BITSTREAMOUT){
  frp->pcmout_storedsamples = samplenum;
  return samplenum;
 }

 mpi->pcm_sample = (short *)frp->pcmout_buffer;
 if(!samplenum || !pcm_sample || !adi->bytespersample || (frp->pcmout_storedsamples>=frp->pcmout_blocksize))
  return 0;

 leftspace=frp->pcmout_blocksize - frp->pcmout_storedsamples;
/*#ifdef MPXPLAY_USE_DEBUGF
 if((crossfadepart==CROSS_FADE) && (frp-mpi->mvp->frp0)==1){
  mpxplay_debugf(MPXPLAY_DEBUG_OUTPUT,"   %d: sn:%4d ss:%4d bl:%4d ls:%4d %d %d",
   (frp-frp->mpi->mvp->frp0+1),samplenum,
   frp->pcmout_storedsamples,frp->pcmout_blocksize,leftspace,
   (samplenum%adi->outchannels),(leftspace%adi->outchannels));
 }
#endif*/
 if(samplenum > leftspace)
  samplenum = leftspace;

 pds_memcpy(((mpxp_uint8_t *)mpi->pcm_sample)+(frp->pcmout_storedsamples*adi->bytespersample),pcm_sample,(samplenum*adi->bytespersample));

 frp->pcmout_storedsamples += samplenum;

 return samplenum; // returns used_samples
}

// audio-data to mixer-data conversion
void MIXER_conversion(struct mpxp_aumixer_main_info_s *mmi,struct mpxplay_audioout_info_s *aui,struct mpxpframe_s *frp)
{
 struct mpxplay_audio_decoder_info_s *adi = frp->infile_infos->audio_decoder_infos;
 struct mpxp_aumixer_passinfo_s *mpi = frp->mpi;

 if(aui->card_infobits&AUINFOS_CARDINFOBIT_BITSTREAMOUT){
  aui->samplenum = frp->pcmout_storedsamples;
  frp->pcmout_storedsamples = 0;
  return;
 }

 mpi->frp = frp; // !!! required

 mpi->samplenum = frp->pcmout_storedsamples;
 if(!mpi->samplenum)
  return;

 if(mpi->mixer_function_flags || ((adi->outchannels!=aui->chan_card) && (adi->outchannels>2) && adi->chanmatrix && (aui->chan_card<=2))){
  funcbit_enable(mmi->mixer_infobits,(AUINFOS_MIXERINFOBIT_ACONV|AUINFOS_MIXERINFOBIT_FCONV));
  mpi->bytespersample_mixer=sizeof(float);
  if(adi->infobits&ADI_FLAG_FLOATOUT) // decoder makes a 32-bit float output (aac,ac3,dts,mp2,mp3,mpc,ogg,wav-float)
   cv_scale_float(mpi->pcm_sample,mpi->samplenum,adi->bits,MIXER_SCALE_BITS,0);
  else                           // pcm (8,16,24,32 bit) output (ape,cdw,flac,wav)
   cv_n_bits_to_float(mpi->pcm_sample,mpi->samplenum,adi->bytespersample,MIXER_SCALE_BITS);
 }else{
  funcbit_disable(mmi->mixer_infobits,(AUINFOS_MIXERINFOBIT_ACONV|AUINFOS_MIXERINFOBIT_FCONV));
  mpi->bytespersample_mixer=adi->bytespersample;
 }

 if(adi->chanmatrix && (aui->chan_card<=2))
  mpi->samplenum=cv_channels_downmix(mpi->pcm_sample,mpi->samplenum,adi->outchannels,adi->chanmatrix,aui->chan_card,aui->card_channelmap,mpi->bytespersample_mixer);
 else if((adi->outchannels!=aui->chan_card) || adi->chanmatrix){
  if(adi->outchannels==1)
   mpi->samplenum=cv_channels_1_to_n(mpi->pcm_sample,mpi->samplenum,aui->chan_card,mpi->bytespersample_mixer);
  else
   mpi->samplenum=cv_channels_remap(mpi->pcm_sample,mpi->samplenum,adi->outchannels,adi->chanmatrix,aui->chan_card,aui->card_channelmap,mpi->bytespersample_mixer);
 }

 if((adi->infobits&ADI_FLAG_OWN_SPECTANAL) || (frp->infile_infos->audio_decoder_funcs && frp->infile_infos->audio_decoder_funcs->get_analiser_bands))
  funcbit_enable(mmi->mixer_infobits,AUINFOS_MIXERINFOBIT_DECODERANALISER);
 else
  funcbit_disable(mmi->mixer_infobits,AUINFOS_MIXERINFOBIT_DECODERANALISER);

 if(crossfadepart==CROSS_FADE){
  unsigned long i=0, funcflags=mpi->mixer_function_flags;
#ifdef MPXPLAY_USE_DEBUGF
  unsigned long old_samplenum=mpi->samplenum;
  unsigned int n=(frp-frp->mpi->mvp->frp0);
  //if(n==1)
#endif
  do{
   if(funcflags&1){
    struct one_mixerfunc_info *infop=all_mixerfunc_info[i];
    if(infop){
     mpi->private_data=mpi->mixerfuncs_private_datas[i];
     infop->process_routine(mpi);
     if(!mpi->samplenum)
      break;
    }
   }
   i++;
   funcflags>>=1;
  }while(funcflags && (i<MIXER_BUILTIN_CFPREPROCESSNUM));
#ifdef MPXPLAY_USE_DEBUGF
  {
   float e=(float)mpi->freq_card/(float)mpi->freq_song;
   //if(n==1)
   mpxplay_debugf(MPXPLAY_DEBUG_OUTPUT,"CV%d os:%d ns:%4d cs:%d f:%8.8X bl:%d ss:%d bs:%d ds:%d dl:%d",
    n, old_samplenum,mpi->samplenum,(long)(old_samplenum*e),
    mpi->mixer_function_flags,
    frp->pcmout_blocksize,frp->pcmout_storedsamples,frp->pcmout_bufsize,
    frp->pcmdec_storedsamples,frp->pcmdec_leftsamples);
  }
#endif
 }

 frp->pcmout_storedsamples = 0;
}

// do mixer functions
void MIXER_main(struct mpxp_aumixer_main_info_s *mmi,struct mpxplay_audioout_info_s *aui,struct mpxpframe_s *frp)
{
 struct mpxplay_audio_decoder_info_s *adi=frp->infile_infos->audio_decoder_infos;
 struct mpxp_aumixer_passinfo_s *mpi=frp->mpi;
 unsigned int do_pcm16;

 if(aui->card_infobits&AUINFOS_CARDINFOBIT_BITSTREAMOUT){
  aui->pcm_sample = (PCM_CV_TYPE_S *)frp->pcmdec_buffer;
  return;
 }

 mpi->pcm_sample = (PCM_CV_TYPE_S *)frp->pcmout_buffer;
 aui->samplenum = mpi->samplenum = frp->mpi->samplenum;

 funcbit_smp_value_put(aui->mvp->vds->pcm_samplenum, mpi->samplenum);

 aui->pcm_sample = (PCM_CV_TYPE_S *)frp->pcmout_buffer;

 if(!mpi->samplenum)
  return;

 //mixer functions (volume,surround,speed,swpachan ...)
 if(mmi->mixer_infobits&AUINFOS_MIXERINFOBIT_ACONV){
  unsigned long i=(crossfadepart==CROSS_FADE)? MIXER_BUILTIN_CFPREPROCESSNUM:0;
  unsigned long funcflags=mpi->mixer_function_flags;
  if(i)
   funcflags>>=i;
  do{
   if(funcflags&1){
    struct one_mixerfunc_info *infop=all_mixerfunc_info[i];
    if(infop){
     mpi->private_data=mpi->mixerfuncs_private_datas[i];
     infop->process_routine(mpi);
     if(!mpi->samplenum){
      aui->samplenum=0;
      return;
     }
    }
   }
   i++;
   funcflags>>=1;
  }while(funcflags);
 }

 if((displaymode&DISP_ANALISER) && (displaymode&DISP_NOFULLEDIT) && !(mmi->mixer_infobits&AUINFOS_MIXERINFOBIT_DECODERANALISER)){
  if(mpi->samplenum<2304)
   do_pcm16=2;
  else
   do_pcm16=1;
 }else
  if((displaymode&DISP_TIMEPOS) || SOUNDLIMITvol || aui->mvp->cfi->crossfadelimit)
   do_pcm16=1;
  else
   do_pcm16=0;

 if(do_pcm16){
  if((aui->card_wave_id==MPXPLAY_WAVEID_PCM_SLE) && (aui->bytespersample_card==2) && (do_pcm16<2)){
   mmi->pcm16=aui->pcm_sample;
   do_pcm16=0;
  }else{
   mmi->pcm16=mmi->MIXER_int16pcm_buffer;
   pds_memcpy((char *)mmi->pcm16,(char *)aui->pcm_sample,mpi->samplenum*mpi->bytespersample_mixer);
  }
 }

 if((mmi->mixer_infobits&AUINFOS_MIXERINFOBIT_ACONV) || (adi->infobits&ADI_FLAG_FLOATOUT)){
  unsigned int mixer_scalebits=(mmi->mixer_infobits&AUINFOS_MIXERINFOBIT_FCONV)? MIXER_SCALE_BITS:aui->bits_song;

  if(do_pcm16)
   cv_float_to_n_bits(mmi->pcm16,mpi->samplenum,mixer_scalebits,(16>>3),0);

  if(aui->card_wave_id==MPXPLAY_WAVEID_PCM_FLOAT)
   cv_scale_float(aui->pcm_sample,mpi->samplenum,mixer_scalebits,aui->bits_card,1);
  else
   cv_float_to_n_bits(aui->pcm_sample,mpi->samplenum,mixer_scalebits,aui->bytespersample_card,adi->infobits&ADI_FLAG_FPUROUND_CHOP);

 }else{
  if(do_pcm16)
   if(mpi->bytespersample_mixer!=2)
    cv_bits_n_to_m(mmi->pcm16,mpi->samplenum,mpi->bytespersample_mixer,2);

  if(aui->card_wave_id==MPXPLAY_WAVEID_PCM_FLOAT)
   cv_n_bits_to_float(aui->pcm_sample,mpi->samplenum,mpi->bytespersample_mixer,aui->bits_card);
  else
   cv_bits_n_to_m(aui->pcm_sample,mpi->samplenum,mpi->bytespersample_mixer,aui->bytespersample_card);
 }

 //get volume & spectrum analiser level
 if((displaymode&DISP_TIMEPOS) || SOUNDLIMITvol || aui->mvp->cfi->crossfadelimit)
  mixer_get_volumelevel(mmi->pcm16,mpi->samplenum,aui->chan_card);

 if((displaymode&DISP_ANALISER) && (displaymode&DISP_NOFULLEDIT) && !(mmi->mixer_infobits&AUINFOS_MIXERINFOBIT_DECODERANALISER))
  mixer_pcm_spectrum_analiser(mmi->pcm16,mpi->samplenum,aui->chan_card);

 aui->samplenum = mpi->samplenum;

 //cut silence
 mixer_soundlimit_check(aui);

 frp->mpi->samplenum = 0;
}

//--------------------------------------------------------------------------
//search function-name in all_mixerfunc_info
static int mixer_common_search_functionname(char *func_name)
{
 unsigned int i;

 for(i=0;i<MIXER_nb_mixerfuncs;i++){
  struct one_mixerfunc_info *infop=all_mixerfunc_info[i];
  if(infop && infop->name){
   if(pds_stricmp(infop->name,func_name)==0)
    return i;
  }
 }
 return -1;
}

//set function's variable by a new value
static unsigned int mixer_common_setvar(struct mpxp_aumixer_passinfo_s *mpi,struct one_mixerfunc_info *infop,int functionnum,unsigned int setmode,int value)
{
 if(!infop)
  return 0;
 if(infop->infobits&MIXER_INFOBIT_BUSY)
  return 0;
 funcbit_smp_enable(infop->infobits,MIXER_INFOBIT_BUSY);

 if(mpi && infop->own_setvar_routine){

  mpi->private_data=mpi->mixerfuncs_private_datas[functionnum];
  infop->own_setvar_routine(mpi,setmode,value);

 }else if(infop->variablep){
  int currmixval=*(infop->variablep),newmixval=currmixval;

  if(infop->infobits&MIXER_INFOBIT_SWITCH){
   switch(setmode){
    case MIXER_SETMODE_RELATIVE:funcbit_inverse(newmixval,infop->var_max);break;
    case MIXER_SETMODE_ABSOLUTE:if(value>0)
                                 funcbit_enable(newmixval,infop->var_max);
                                else
                                 funcbit_disable(newmixval,infop->var_max);
                                break;
    case MIXER_SETMODE_RESET   :funcbit_disable(newmixval,infop->var_max);
                                newmixval|=infop->var_center;
                                break;
   }
  }else{
   switch(setmode){
    case MIXER_SETMODE_RELATIVE:newmixval=currmixval+(value*infop->var_step);
                                if((currmixval<infop->var_center && newmixval>infop->var_center) || (currmixval>infop->var_center && newmixval<infop->var_center))
			         newmixval=infop->var_center;
				break;
    case MIXER_SETMODE_ABSOLUTE:newmixval=value;break;
    case MIXER_SETMODE_RESET   :newmixval=infop->var_center;break;
   }
   if(newmixval<infop->var_min)
    newmixval=infop->var_min;
   else
    if(newmixval>infop->var_max)
     newmixval=infop->var_max;
  }

  funcbit_smp_value_put(*(infop->variablep),newmixval);

 }else{
  funcbit_smp_disable(infop->infobits,MIXER_INFOBIT_BUSY);
  return 0;
 }

 funcbit_smp_disable(infop->infobits,MIXER_INFOBIT_BUSY);
 return 1;
}

//check function's variable and give back the (new) status of function (0/1)
static unsigned int mixer_common_checkvar(struct mpxp_aumixer_passinfo_s *mpi,struct one_mixerfunc_info *infop,int functionnum)
{
 unsigned int enable=0;

 if(!infop)
  return enable;

 if(infop->variablep){
  if(infop->infobits&MIXER_INFOBIT_SWITCH){
   if(*(infop->variablep))
    enable=1;
  }else{
   // ??? an extra range check
   if(*(infop->variablep)>infop->var_max)
    *(infop->variablep)=infop->var_max;
   else
    if(*(infop->variablep)<infop->var_min)
     *(infop->variablep)=infop->var_min;
   if(*(infop->variablep)!=infop->var_center)
    enable=1;
  }
 }

 if(mpi && infop->own_checkvar_routine){
  mpi->private_data=mpi->mixerfuncs_private_datas[functionnum];
  enable=infop->own_checkvar_routine(mpi);
 }

 return enable;
}

static void mixer_common_setflags(struct mpxp_aumixer_passinfo_s *mpi,struct one_mixerfunc_info *infop,int functionnum,unsigned int enable)
{
 unsigned int bit=1<<functionnum;

 if(!mpi || !infop)
  return;

 if(enable){
  funcbit_smp_enable(infop->infobits,MIXER_INFOBIT_ENABLED);
  if(infop->process_routine)
   funcbit_smp_enable(mpi->mixer_function_flags,bit);
 }else{
  funcbit_smp_disable(infop->infobits,MIXER_INFOBIT_ENABLED);
  funcbit_smp_disable(mpi->mixer_function_flags,bit);
 }
}

static unsigned int mixfunc_checkvar_setflags(struct mpxp_aumixer_passinfo_s *mpi,
  struct one_mixerfunc_info *infop,int functionnum)
{
 if(mpi && infop && (infop->infobits&MIXER_INFOBIT_RESETDONE) && !(infop->infobits&MIXER_INFOBIT_BUSY)){
  unsigned int enable,oldstatus;

  mpi->private_data=mpi->mixerfuncs_private_datas[functionnum];

  funcbit_smp_enable(infop->infobits,MIXER_INFOBIT_BUSY);

  oldstatus=infop->infobits&MIXER_INFOBIT_ENABLED;
  enable=mixer_common_checkvar(mpi,infop,functionnum);
  mixer_common_setflags(mpi,infop,functionnum,enable);

  if(infop->function_init){
   if(!oldstatus && enable){
    if(!infop->function_init(mpi,MIXER_INITTYPE_START)){
     mixer_common_setflags(mpi,infop,functionnum,0);
     infop->function_init(mpi,MIXER_INITTYPE_STOP);
    }
   }else{
    if(oldstatus && !enable)
     infop->function_init(mpi,MIXER_INITTYPE_STOP);
   }
  }

  funcbit_smp_pointer_put(mpi->mixerfuncs_private_datas[functionnum],mpi->private_data);

  funcbit_smp_disable(infop->infobits,MIXER_INFOBIT_BUSY);
  return 1;
 }
 return 0;
}

//----------------------------------------------------------------------
//set one mixer function (modify,enable/disable)
void MIXER_setfunction(struct mpxp_aumixer_passinfo_s *mpi,char *func_name,unsigned int setmode,int value)
{
 struct one_mixerfunc_info *infop;
 int functionnum;

 functionnum=mixer_common_search_functionname(func_name);
 if(functionnum<0)
  return;

 infop=all_mixerfunc_info[functionnum];
 if(!mixer_common_setvar(mpi,infop,functionnum,setmode,value))
  return;

 if(mixfunc_checkvar_setflags(mpi,infop,functionnum))
  MIXER_checkallfunc_dependencies(mpi,MIXER_INFOBIT_PARALLEL_DEPENDENCY);
}

// return mixerfunc structure (to read a value from it)
struct one_mixerfunc_info *MIXER_getfunction(char *func_name)
{
 int functionnum=mixer_common_search_functionname(func_name);

 if(functionnum<0)
  return NULL;

 return (all_mixerfunc_info[functionnum]);
}

int MIXER_getvalue(char *func_name)
{
 struct one_mixerfunc_info *infop=MIXER_getfunction(func_name);

 if(infop && infop->variablep)
  return (*infop->variablep);

 return 0;
}

// disabled(0)/enabled(1)
int MIXER_getstatus(char *func_name)
{
 struct one_mixerfunc_info *infop=MIXER_getfunction(func_name);

 if(infop)
  return (infop->infobits&MIXER_INFOBIT_ENABLED);

 return 0;
}

//check one function and set its flag (enable/disable)
void MIXER_checkfunc_setflags(struct mpxp_aumixer_passinfo_s *mpi,char *func_name)
{
 struct one_mixerfunc_info *infop;
 int functionnum;

 functionnum=mixer_common_search_functionname(func_name);
 if(functionnum<0)
  return;

 infop=all_mixerfunc_info[functionnum];

 if(mixfunc_checkvar_setflags(mpi,infop,functionnum))
  MIXER_checkallfunc_dependencies(mpi,MIXER_INFOBIT_PARALLEL_DEPENDENCY);
}

void MIXER_checkallfunc_setflags(struct mpxp_aumixer_passinfo_s *mpi)
{
 unsigned int functionnum;

 for(functionnum=0;functionnum<MIXER_nb_mixerfuncs;functionnum++){
  struct one_mixerfunc_info *infop=all_mixerfunc_info[functionnum];
  mixfunc_checkvar_setflags(mpi,infop,functionnum);
 }
 //MIXER_checkallfunc_dependencies(mmi,MIXER_INFOBIT_PARALLEL_DEPENDENCY); // ??? called from MIXER_configure
}

// set (enable/disable) functions witch have external or parallel dependency
void MIXER_checkallfunc_dependencies(struct mpxp_aumixer_passinfo_s *mpi,int flag)
{
 unsigned int functionnum;

 for(functionnum=0;functionnum<MIXER_nb_mixerfuncs;functionnum++){
  struct one_mixerfunc_info *infop=all_mixerfunc_info[functionnum];
  if(infop && (infop->infobits&flag))
   mixfunc_checkvar_setflags(mpi,infop,functionnum);
 }
}

//set all mixer variables/functions to default (zero) value
void MIXER_resetallfunc(struct mpxp_aumixer_passinfo_s *mpi)
{
 unsigned int functionnum;

 for(functionnum=0;functionnum<MIXER_nb_mixerfuncs;functionnum++){
  struct one_mixerfunc_info *infop=all_mixerfunc_info[functionnum];
  if(infop){
   mixer_common_setvar(mpi,infop,functionnum,MIXER_SETMODE_RESET,0);
   funcbit_smp_enable(infop->infobits,MIXER_INFOBIT_RESETDONE);
  }
 }
}

//--------------------------------------------------------------------
//init all functions (if it has function_init)
#ifdef MPXPLAY_LINK_DLLLOAD
#include <mem.h>

static void MIXER_load_plugins(void)
{
 int functionnum;
 mpxplay_module_entry_s *dll_found=NULL;

 do{
  dll_found=newfunc_dllload_getmodule(MPXPLAY_DLLMODULETYPE_AUMIXER,0,NULL,dll_found); // get next
  //fprintf(stdout,"dll:%8.8X sv:%4.4X\n",dll_found,dll_found->module_structure_version);
  if(dll_found && (dll_found->module_structure_version==MPXPLAY_DLLMODULEVER_AUMIXER)){ // !!!
   struct one_mixerfunc_info *infop=(struct one_mixerfunc_info *)dll_found->module_callpoint;
   if(infop){
    functionnum=mixer_common_search_functionname(infop->name);
    if(functionnum>=0)                      // overwrite built-in dsp routine
     all_mixerfunc_info[functionnum]=infop;
    else{                                   // insert the new dsp routine
     if(MIXER_nb_mixerfuncs<MIXER_MAX_FUNCTIONS){
      memmove((void *)&all_mixerfunc_info[MIXER_DLLFUNC_INSERTPOINT],(void *)&all_mixerfunc_info[MIXER_DLLFUNC_INSERTPOINT+1],(MIXER_nb_mixerfuncs-MIXER_DLLFUNC_INSERTPOINT)*sizeof(struct one_mixerfunc_info *));
      all_mixerfunc_info[MIXER_DLLFUNC_INSERTPOINT]=infop;
      MIXER_nb_mixerfuncs++;
     }
    }
   }
  }
 }while(dll_found);
}
#endif

void MIXER_init(struct mainvars *mvp, struct mpxplay_audioout_info_s *aui,struct mpxpframe_s *frp)
{
 struct mpxp_aumixer_main_info_s *mmi=&mvp->mmi;
 struct mpxp_aumixer_passinfo_s *mpi;

 mpi=aumixer_frpmpi_alloc(frp);
 if(!mpi)
  mpxplay_close_program(MPXERROR_XMS_MEM);

 mpi->control_cb= mpxplay_aumixer_control_cb;
 mpi->ccb_data  = mpi;
 mpi->freq_card = mpi->freq_song = 44100;
 mpi->chan_card = mpi->chan_song = PCM_CHANNELS_CFG;
 mpi->bits_card = mpi->bits_song = 16;
 mpi->mmi       = mmi;
 mpi->mvp       = mvp;
 mpi->aui       = aui;
 mpi->frp       = frp;

 funcbit_enable(MIXER_controlbits,MIXER_CONTROLBIT_LIMITER);
#ifdef MPXPLAY_LINK_DLLLOAD
 MIXER_load_plugins();
#endif
 MIXER_resetallfunc(mpi);
}

void MIXER_config_set(struct mpxp_aumixer_main_info_s *mmi)
{
 if(MIXER_controlbits&MIXER_CONTROLBIT_SPEED1000)
  funcbit_enable(mmi->mixer_infobits,AUINFOS_MIXERCTRLBIT_SPEED1000);
}

//first init (malloc)
void MIXER_allfuncinit_init(struct mainvars *mvp,struct mpxplay_audioout_info_s *aui,struct mpxpframe_s *frp)
{
 struct mpxp_aumixer_main_info_s *mmi=&mvp->mmi;
 struct mpxp_aumixer_passinfo_s *mpi=frp->mpi;
 unsigned int functionnum;

 mmi->MIXER_int16pcm_buffer=(PCM_CV_TYPE_S *)pds_malloc(2*PCM_BUFFER_SIZE);
 if(mmi->MIXER_int16pcm_buffer)
  mmi->MIXER_int16pcm_bufsize=PCM_BUFFER_SIZE;

 if(MIXER_controlbits&MIXER_CONTROLBIT_SOFTTONE)
  funcbit_disable(aui->card_infobits,AUINFOS_CARDINFOBIT_HWTONE);

 for(functionnum=0;functionnum<MIXER_nb_mixerfuncs;functionnum++){
  struct one_mixerfunc_info *infop=all_mixerfunc_info[functionnum];
  if(infop && infop->function_init){
   mpi->private_data=mpi->mixerfuncs_private_datas[functionnum];
   infop->function_init(mpi,MIXER_INITTYPE_INIT);
   mpi->mixerfuncs_private_datas[functionnum]=mpi->private_data;
  }
 }
}

//func close (free)
void MIXER_allfuncinit_close(struct mainvars *mvp)
{
 struct mpxp_aumixer_main_info_s *mmi=&mvp->mmi;
 struct mpxp_aumixer_passinfo_s *mpi=mvp->frp0->mpi;
 unsigned int functionnum;

 for(functionnum=0;functionnum<MIXER_nb_mixerfuncs;functionnum++){
  struct one_mixerfunc_info *infop=all_mixerfunc_info[functionnum];
  if(infop && infop->function_init){
   mpi->private_data=mpi->mixerfuncs_private_datas[functionnum];
   infop->function_init(mpi,MIXER_INITTYPE_CLOSE);
   mpi->mixerfuncs_private_datas[functionnum]=NULL;
  }
 }
 if(mmi->MIXER_int16pcm_buffer){
  free(mmi->MIXER_int16pcm_buffer);
  mmi->MIXER_int16pcm_buffer=NULL;
  mmi->MIXER_int16pcm_bufsize=0;
 }
}

// re-init buffers (ie: at new file (external dep))
void MIXER_allfuncinit_reinit(struct mpxp_aumixer_passinfo_s *mpi)
{
 unsigned int functionnum=(crossfadepart || funcbit_test(intsoundconfig,INTSOUND_DECODER))? MIXER_BUILTIN_CFPREPROCESSNUM:0;

 mpi->frp = mpi->mvp->frp0; // !!!

 for( ; functionnum<MIXER_nb_mixerfuncs; functionnum++){
  struct one_mixerfunc_info *infop=all_mixerfunc_info[functionnum];
  if(infop && infop->function_init && (infop->infobits&MIXER_INFOBIT_ENABLED)){
   mpi->private_data=mpi->mixerfuncs_private_datas[functionnum];
   infop->function_init(mpi,MIXER_INITTYPE_REINIT);
   mpi->mixerfuncs_private_datas[functionnum]=mpi->private_data;
  }
 }
}

//clear buffers (ie: at seeking)
void MIXER_allfuncinit_restart(struct mpxp_aumixer_passinfo_s *mpi)
{
 unsigned int functionnum;

 for(functionnum=0; functionnum<MIXER_nb_mixerfuncs; functionnum++){
  struct one_mixerfunc_info *infop=all_mixerfunc_info[functionnum];
  if(infop && infop->function_init && (infop->infobits&MIXER_INFOBIT_ENABLED)){
   mpi->private_data=mpi->mixerfuncs_private_datas[functionnum];
   infop->function_init(mpi,MIXER_INITTYPE_RESET);
   mpi->mixerfuncs_private_datas[functionnum]=mpi->private_data;
  }
 }
}

mpxp_int32_t mpxplay_aumixer_control_cb(void *cb_data,mpxp_uint32_t funcnum,void *argp1,void *argp2)
{
 struct mpxp_aumixer_passinfo_s *mpi=cb_data;
 struct mpxplay_audioout_info_s *aui;
 struct mpxp_aumixer_main_info_s *mmi;
 struct mpxpframe_s *frp;

 if(!mpi)
  return MPXPLAY_ERROR_CFGFUNC_ARGUMENTMISSING;

 switch(funcnum){
  case MPXPLAY_CFGFUNCNUM_AUMIXER_MIXVALUE_GET:
   if(!argp1 || !argp2)
    return MPXPLAY_ERROR_CFGFUNC_ARGUMENTMISSING;
   *((mpxp_int32_t *)argp2)=MIXER_getvalue((char *)argp1);
   goto err_out_ok;
  case MPXPLAY_CFGFUNCNUM_AUMIXER_MIXVALUE_SET_REL:
  case MPXPLAY_CFGFUNCNUM_AUMIXER_MIXVALUE_SET_ABS:
  case MPXPLAY_CFGFUNCNUM_AUMIXER_MIXVALUE_SET_RES:
   if(!argp1 || !argp2)
    return MPXPLAY_ERROR_CFGFUNC_ARGUMENTMISSING;
   MIXER_setfunction(mpi,(char *)argp1,(funcnum&3),*((mpxp_int32_t *)argp2));
   goto err_out_ok;
 }

 if(!argp1)
  return MPXPLAY_ERROR_CFGFUNC_ARGUMENTMISSING;
 mmi=mpi->mmi;
 if(!mmi)
  return MPXPLAY_ERROR_CFGFUNC_INVALIDDATA;

 switch(funcnum){
  case MPXPLAY_CFGFUNCNUM_AUMIXER_GET_INFOBITS:
   *((mpxp_int32_t *)argp1)=mmi->mixer_infobits;
   goto err_out_ok;
  case MPXPLAY_CFGFUNCNUM_AUMIXER_GET_BYTESPERSAMPLE:
   *((mpxp_int32_t *)argp1)=mpi->bytespersample_mixer;
   goto err_out_ok;
  case MPXPLAY_CFGFUNCNUM_AUMIXER_SET_INFOBITS:
   funcbit_smp_value_put(mmi->mixer_infobits,*((mpxp_uint32_t *)argp1));
   goto err_out_ok;
 }

 aui=mpi->aui;
 if(!aui)
  return MPXPLAY_ERROR_CFGFUNC_INVALIDDATA;

 switch(funcnum){
  case MPXPLAY_CFGFUNCNUM_AUMIXER_GET_SONG_FREQ:
   *((mpxp_int32_t *)argp1)=aui->freq_song;
   goto err_out_ok;
  case MPXPLAY_CFGFUNCNUM_AUMIXER_GET_SONG_CHAN:
   *((mpxp_int32_t *)argp1)=aui->chan_song;
   goto err_out_ok;
  case MPXPLAY_CFGFUNCNUM_AUMIXER_GET_SONG_BITS:
   *((mpxp_int32_t *)argp1)=aui->bits_song;
   goto err_out_ok;
  case MPXPLAY_CFGFUNCNUM_AUMIXER_GET_CARD_INFOBITS:
   *((mpxp_int32_t *)argp1)=aui->card_infobits;
   goto err_out_ok;
  case MPXPLAY_CFGFUNCNUM_AUMIXER_GET_CARD_CHANCFGNUM:
   *((mpxp_int32_t *)argp1)=PCM_CHANNELS_CFG;
   goto err_out_ok;
  case MPXPLAY_CFGFUNCNUM_AUMIXER_GET_CARD_BYTESPERSAMPLE:
   *((mpxp_int32_t *)argp1)=aui->bytespersample_card;
   goto err_out_ok;
  case MPXPLAY_CFGFUNCNUM_AUMIXER_GET_CARD_FREQ:
   *((mpxp_int32_t *)argp1)=aui->freq_card;
   goto err_out_ok;
  case MPXPLAY_CFGFUNCNUM_AUMIXER_GET_CARD_CHAN:
   *((mpxp_int32_t *)argp1)=aui->chan_card;
   goto err_out_ok;
  case MPXPLAY_CFGFUNCNUM_AUMIXER_GET_CARD_BITS:
   *((mpxp_int32_t *)argp1)=aui->bits_card;
   goto err_out_ok;
  case MPXPLAY_CFGFUNCNUM_AUMIXER_GET_CARD_BASS:
   *((mpxp_int32_t *)argp1)=aui->card_mixer_values[AU_MIXCHAN_BASS];
   goto err_out_ok;
  case MPXPLAY_CFGFUNCNUM_AUMIXER_GET_CARD_TREBLE:
   *((mpxp_int32_t *)argp1)=aui->card_mixer_values[AU_MIXCHAN_TREBLE];
   goto err_out_ok;
  case MPXPLAY_CFGFUNCNUM_AUMIXER_SET_CARD_BASS:
   funcbit_smp_value_put(aui->card_mixer_values[AU_MIXCHAN_BASS],*((mpxp_uint32_t *)argp1));
   if(aui->card_infobits&AUINFOS_CARDINFOBIT_HWTONE)
    AU_setmixer_one(aui,AU_MIXCHAN_BASS,MIXER_SETMODE_ABSOLUTE,aui->card_mixer_values[AU_MIXCHAN_BASS]);
   goto err_out_ok;
  case MPXPLAY_CFGFUNCNUM_AUMIXER_SET_CARD_TREBLE:
   funcbit_smp_value_put(aui->card_mixer_values[AU_MIXCHAN_TREBLE],*((mpxp_uint32_t *)argp1));
   if(aui->card_infobits&AUINFOS_CARDINFOBIT_HWTONE)
    AU_setmixer_one(aui,AU_MIXCHAN_TREBLE,MIXER_SETMODE_ABSOLUTE,aui->card_mixer_values[AU_MIXCHAN_TREBLE]);
   goto err_out_ok;
  case MPXPLAY_CFGFUNCNUM_AUMIXER_SET_CARD_INT08DECODERCYCLES:
   funcbit_smp_value_put(aui->int08_decoder_cycles,*((mpxp_uint32_t *)argp1));
   goto err_out_ok;
 }

 frp=mpi->frp;
 if(!frp)
  return MPXPLAY_ERROR_CFGFUNC_INVALIDDATA;

 switch(funcnum){
  case MPXPLAY_CFGFUNCNUM_AUMIXER_GET_PCMOUTBLOCKSIZE:
   *((mpxp_int32_t *)argp1)=frp->pcmout_blocksize;
   goto err_out_ok;
  case MPXPLAY_CFGFUNCNUM_AUMIXER_GET_PCMOUTBUFSIZE:
   *((mpxp_int32_t *)argp1)=frp->pcmout_bufsize;
   goto err_out_ok;
 }

 return MPXPLAY_ERROR_CFGFUNC_UNSUPPFUNC;

err_out_ok:
 return MPXPLAY_ERROR_OK;
}
