//**************************************************************************
//*                     This file is part of the                           *
//*                      Mpxplay - audio player.                           *
//*                  The source code of Mpxplay is                         *
//*        (C) copyright 1998-2014 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: software tone (bass and treble only)

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

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

#define PDS_FINITE(x) _finite(x) // !!! Watcom (else?)

#define TONE_VALUE_CENTER 100
#define TONE_VALUE_MAX    200
#define TONE_VALUE_STEP     6

#define TONE_EQ_BANDS  10
#define TONE_EQdB  15

#define TONE_SYNCFLAG_STOP  0 // eq stopped
#define TONE_SYNCFLAG_BEGIN 1 // synchronize at start
#define TONE_SYNCFLAG_END   2 // synchronize at stop
#define TONE_SYNCFLAG_RUN   3 // eq running

#define TONE_CONFIGFLAG_MIXERTONE (1<<0) // else hw (soundcard) or decoder tone

#define TONE_LOWPASS_FREQ    200 // in Hz
#define TONE_HIGHPASS_FREQ 15000 //

typedef double fxtone_float_t; // the inside precision

extern unsigned int crossfadepart;
extern struct mpxplay_audioout_info_s au_infos;

static unsigned long eq_freqs[TONE_EQ_BANDS]={44,87,173,345,690,1379,2757,5513,11025,16538};
static float eq_band_powers[TONE_EQ_BANDS];
static unsigned int treble_set_req;

one_mixerfunc_info MIXER_FUNCINFO_tone_bass;
one_mixerfunc_info MIXER_FUNCINFO_tone_treble;

typedef struct mpxplay_fxtone_info_s{
 unsigned int  sync_flag;
 unsigned long config_flags;
 unsigned long high_filter_len;
 unsigned long low_filter_len, low_filter_bufpos;
 unsigned long low_filter_buflen;  // chan_card * low_filter_len
 PCM_CV_TYPE_F *low_filter_buffer;
 fxtone_float_t low_filter_sum[PCM_MAX_CHANNELS], high_filter_sum[PCM_MAX_CHANNELS];
}mpxplay_fxtone_info_s;

static void fxtone_get_mixvalues(struct mpxp_aumixer_passinfo_s *mpi,
 mpxp_uint32_t *card_infobits,mpxp_int32_t *mixval_bass, mpxp_int32_t *mixval_treble)
{
 if(card_infobits){
  *card_infobits=0;
  mpi->control_cb(mpi->ccb_data,MPXPLAY_CFGFUNCNUM_AUMIXER_GET_CARD_INFOBITS,card_infobits,NULL);
 }
 if(mixval_bass){
  *mixval_bass=0;
  mpi->control_cb(mpi->ccb_data,MPXPLAY_CFGFUNCNUM_AUMIXER_GET_CARD_BASS,mixval_bass,NULL);
 }
 if(mixval_treble){
  *mixval_treble=0;
  mpi->control_cb(mpi->ccb_data,MPXPLAY_CFGFUNCNUM_AUMIXER_GET_CARD_TREBLE,mixval_treble,NULL);
 }
}

static unsigned int mixer_tone_check_decoder_eq(struct mpxp_aumixer_passinfo_s *mpi)
{
 struct mpxpframe_s *frp=mpi->frp;
 struct mpxplay_infile_info_s *miis=frp->infile_infos;

 if(MIXER_controlbits&MIXER_CONTROLBIT_DISABLE_DECODERTONE)
  return 0;
 if(!miis || mpxplay_decoders_audio_eq_exists(frp))
  return 1;
 return 0;
}

static void mixer_tone_calculate_eqgain(struct mpxp_aumixer_passinfo_s *mpi)
{
 mpxp_int32_t mixval_bass,mixval_treble;
 int i,data[TONE_EQ_BANDS];

 if(!(MIXER_controlbits&MIXER_CONTROLBIT_DISABLE_DECODERTONE) && mixer_tone_check_decoder_eq(mpi)){

  fxtone_get_mixvalues(mpi,NULL,&mixval_bass,&mixval_treble);

  data[0]=mixval_bass;
  data[1]=mixval_bass;

  for(i=2;i<TONE_EQ_BANDS-3;i++)
   data[i]=TONE_VALUE_CENTER;

  data[TONE_EQ_BANDS-3]=(mixval_treble-TONE_VALUE_CENTER)/4+TONE_VALUE_CENTER;
  data[TONE_EQ_BANDS-2]=(mixval_treble-TONE_VALUE_CENTER)/2+TONE_VALUE_CENTER;
  data[TONE_EQ_BANDS-1]=mixval_treble;

  for(i=0;i<TONE_EQ_BANDS;i++)
   if(data[i]>TONE_VALUE_CENTER)
    eq_band_powers[i] = (float)pow(10,(float)(data[i]-TONE_VALUE_CENTER)/((float)TONE_VALUE_CENTER)*TONE_EQdB/20.0); // logarithmic
   else
    eq_band_powers[i] = (float)((data[i]))/((float)TONE_VALUE_CENTER); // linear to zero

  mpxplay_decoders_audio_eq_config(mpi->frp,TONE_EQ_BANDS,&eq_freqs[0],&(eq_band_powers[0]));
 }
}

static void mixer_tone_reset_filters(struct mpxp_aumixer_passinfo_s *mpi)
{
 struct mpxplay_fxtone_info_s *tis = mpi->private_data;
 unsigned long card_infobits=0;

 if(!tis)
  return;

 fxtone_get_mixvalues(mpi,&card_infobits,NULL,NULL);

 if(card_infobits&AUINFOS_CARDINFOBIT_HWTONE){
  funcbit_disable(tis->config_flags,TONE_CONFIGFLAG_MIXERTONE);
 }else if(!(MIXER_controlbits&MIXER_CONTROLBIT_DISABLE_DECODERTONE) && mpxplay_decoders_audio_eq_exists(mpi->frp)){
  mixer_tone_calculate_eqgain(mpi);
  funcbit_disable(tis->config_flags,TONE_CONFIGFLAG_MIXERTONE);
 }else if(!(MIXER_controlbits&MIXER_CONTROLBIT_DISABLE_MIXERTONE)){
  if(tis->low_filter_buffer && tis->low_filter_len)
   pds_memset(tis->low_filter_buffer, 0, (mpi->chan_card*tis->low_filter_len+32)*sizeof(PCM_CV_TYPE_F));
  pds_memset(tis->low_filter_sum, 0, sizeof(tis->low_filter_sum));
  pds_memset(tis->high_filter_sum, 0, sizeof(tis->high_filter_sum));
  tis->low_filter_bufpos = 0;
  funcbit_enable(tis->config_flags,TONE_CONFIGFLAG_MIXERTONE);
  mpxplay_debugf(MPXPLAY_DEBUG_OUTPUT,"reset filters %d. ",(mpi->frp-mpi->mvp->fr_base+1));
 }
}

static unsigned int mixer_tone_init_filters(struct mpxp_aumixer_passinfo_s *mpi)
{
 struct mpxplay_fxtone_info_s *tis = mpi->private_data;
 unsigned long card_infobits = 0;

 if(!tis)
  return 0;

 fxtone_get_mixvalues(mpi,&card_infobits,NULL,NULL);

 if(card_infobits&AUINFOS_CARDINFOBIT_HWTONE){
  funcbit_disable(tis->config_flags,TONE_CONFIGFLAG_MIXERTONE);
 }else if(!(MIXER_controlbits&MIXER_CONTROLBIT_DISABLE_DECODERTONE) && mpxplay_decoders_audio_eq_exists(mpi->frp)){
  mixer_tone_calculate_eqgain(mpi);
  funcbit_disable(tis->config_flags,TONE_CONFIGFLAG_MIXERTONE);
 }else if(!(MIXER_controlbits&MIXER_CONTROLBIT_DISABLE_MIXERTONE)){
  long lowfiltlen=((long)((float)mpi->freq_card/(float)TONE_LOWPASS_FREQ))&(~1)+1; // even buffer size
  if((mpi->chan_card * lowfiltlen) > tis->low_filter_buflen){
   if(tis->low_filter_buffer)
    pds_free(tis->low_filter_buffer);
   tis->low_filter_buffer = pds_malloc((mpi->chan_card*lowfiltlen+64)*sizeof(PCM_CV_TYPE_F));
   if(!tis->low_filter_buffer){
    tis->low_filter_buflen = 0;
    return 0;
   }
   tis->low_filter_buflen = mpi->chan_card * lowfiltlen;
  }
  if(tis->low_filter_len != lowfiltlen){
   tis->low_filter_len = lowfiltlen;
   mixer_tone_reset_filters(mpi);
  }
  tis->high_filter_len = ((long)((float)mpi->freq_card/(float)TONE_HIGHPASS_FREQ))&(~1)+1;
  if(tis->high_filter_len < 3)
   tis->high_filter_len = 3;
  funcbit_enable(tis->config_flags,TONE_CONFIGFLAG_MIXERTONE);
  mpxplay_debugf(MPXPLAY_DEBUG_OUTPUT,"init filters %d. hfl:%d",(mpi->frp-mpi->mvp->fr_base+1),tis->high_filter_len);
 }
 return 1;
}

static void mixer_tone_apply_filters(struct mpxp_aumixer_passinfo_s *mpi)
{
 struct mpxplay_fxtone_info_s *tis = mpi->private_data;
 int samplenum = mpi->samplenum, ch;
 PCM_CV_TYPE_F *pcm = (PCM_CV_TYPE_F *)mpi->pcm_sample;
 mpxp_int32_t mixval_bass,mixval_treble;
 const long center_bass = MIXER_FUNCINFO_tone_bass.var_center;
 const long center_treble = MIXER_FUNCINFO_tone_treble.var_center;
 const float low_filt_len = (float)tis->low_filter_len;
 const float high_filt_len = (float)tis->high_filter_len;

 if(!samplenum || !pcm || !mpi->chan_card || !tis->low_filter_buffer)
  return;

 fxtone_get_mixvalues(mpi,NULL,&mixval_bass,&mixval_treble);

 //mpxplay_debugf(MPXPLAY_DEBUG_OUTPUT,"bs:%d tr:%d bp:%d",mixval_bass,mixval_treble,tis->low_filter_bufpos);

 if(tis->low_filter_bufpos>=tis->low_filter_len)
  tis->low_filter_bufpos=0;

 do{
  unsigned int ch=mpi->chan_card, bufpos, i;
  PCM_CV_TYPE_F *putptr,*getptr,*hfptr_add,*hfptr_sub;

  bufpos = tis->low_filter_bufpos;
  putptr = &tis->low_filter_buffer[bufpos*ch];

  bufpos += tis->low_filter_len/2+1;
  if(bufpos>=tis->low_filter_len)
   bufpos -= tis->low_filter_len;
  getptr = &tis->low_filter_buffer[bufpos*ch];

  i = (tis->high_filter_len/2)+1;
  if(bufpos<i)
   bufpos += tis->low_filter_len;
  bufpos -= i;
  hfptr_sub = &tis->low_filter_buffer[bufpos*ch];

  bufpos += tis->high_filter_len;
  if(bufpos>=tis->low_filter_len)
   bufpos -= tis->low_filter_len;
  hfptr_add = &tis->low_filter_buffer[bufpos*ch];

  for(ch=0; ch<mpi->chan_card ; ch++){
   fxtone_float_t sum_l, sum_h;

   tis->high_filter_sum[ch] = (sum_h = tis->high_filter_sum[ch] - *hfptr_sub + *hfptr_add);
   tis->low_filter_sum[ch] = (sum_l = tis->low_filter_sum[ch] - *putptr + *pcm);

   *putptr = *pcm;

   if(mixval_bass != center_bass){
    if(mixval_bass > center_bass)
     sum_l *= (float)(mixval_bass - center_bass) / 15.0f;
    else
     sum_l *= (float)mixval_bass / (float)center_bass - 1.0f;
    sum_l = sum_l / low_filt_len + *getptr;
   }else
    sum_l = *getptr;

   if(mixval_treble != center_treble){
    fxtone_float_t hfs;
    sum_h = *getptr - (hfs = sum_h / high_filt_len);
    if(mixval_treble > center_treble)
     sum_h *= 1.0f + (float)(mixval_treble - center_treble) / 20.0f;
    else
     sum_h *= (float)mixval_treble / (float)center_treble;
    sum_h += hfs;
   }else
    sum_h = *getptr;

   *pcm = (sum_l + sum_h) * 0.5f;

   putptr++; getptr++; hfptr_sub++; hfptr_add++; pcm++;
  }

  if((++tis->low_filter_bufpos) >= tis->low_filter_len)
   tis->low_filter_bufpos = 0;

  samplenum-=mpi->chan_card;
 }while(samplenum>0);

 for(ch=0; ch<mpi->chan_card; ch++) // to avoid sound "freezing" at bad pcm data
  if(!PDS_FINITE(tis->high_filter_sum[ch]) || !PDS_FINITE(tis->low_filter_sum[ch])){
   mixer_tone_reset_filters(mpi);
   pds_memset(mpi->pcm_sample, 0, mpi->samplenum*sizeof(PCM_CV_TYPE_F));
   return;
  }

 //if(crossfadepart==CROSS_FADE) // !!!
 // return;

 if(tis->sync_flag==TONE_SYNCFLAG_BEGIN){ // skip/drop buffered input
  samplenum = (tis->low_filter_len / 2) * mpi->chan_card;
  if(mpi->samplenum > samplenum){
   if(crossfadepart==CROSS_FADE){
    pds_memset(mpi->pcm_sample, 0, samplenum * sizeof(PCM_CV_TYPE_F));
   }else{
    mpi->samplenum -= samplenum;
    pds_memcpy(mpi->pcm_sample, ((PCM_CV_TYPE_F *)mpi->pcm_sample)+samplenum, mpi->samplenum*sizeof(PCM_CV_TYPE_F));
   }
  }else
   mpi->samplenum = 0;
 }else if((tis->sync_flag==TONE_SYNCFLAG_END) && (crossfadepart!=CROSS_FADE)){ // flush/add buffered input to the block
  PCM_CV_TYPE_F *getptr;
  unsigned long block=tis->low_filter_len/2*mpi->chan_card, bufpos=tis->low_filter_bufpos;
  bufpos+=tis->low_filter_len/2;
  if(bufpos>=tis->low_filter_len)
   bufpos-=tis->low_filter_len;
  getptr=&tis->low_filter_buffer[bufpos*mpi->chan_card];
  samplenum = (tis->low_filter_len - bufpos) * mpi->chan_card;
  samplenum = min(samplenum,block);
  pds_memcpy(((PCM_CV_TYPE_F *)mpi->pcm_sample) + mpi->samplenum, getptr, samplenum*sizeof(PCM_CV_TYPE_F)); // end of buffer
  mpi->samplenum += samplenum; // !!! extends the size of audio frame (pcmout_block)
  if(samplenum<block){
   samplenum = block - samplenum;
   pds_memcpy(((PCM_CV_TYPE_F *)mpi->pcm_sample) + mpi->samplenum, tis->low_filter_buffer, samplenum*sizeof(PCM_CV_TYPE_F)); // begin of buffer
   mpi->samplenum += samplenum; // !!!
  }
 }
}

static int mixer_tone_init(struct mpxp_aumixer_passinfo_s *mpi,int inittype)
{
 struct mpxplay_fxtone_info_s *tis=mpi->private_data;

 switch(inittype){
  case MIXER_INITTYPE_INIT:
        if(!tis){
         tis=(struct mpxplay_fxtone_info_s *)pds_calloc(1,sizeof(struct mpxplay_fxtone_info_s));
         if(!tis)
          return 0;
         mpi->private_data=tis;
         mpxplay_debugf(MPXPLAY_DEBUG_OUTPUT,"tone init %d. %8.8X %d",(int)(mpi->frp-mpi->mvp->fr_base+1),(unsigned long)tis->low_filter_buffer,tis->low_filter_len);
        }
  case MIXER_INITTYPE_REINIT:
        if(!mixer_tone_init_filters(mpi)){
         mpxplay_debugf(MPXPLAY_DEBUG_OUTPUT,"tone init filters failed!");
         return 0;
        }
        if(inittype==MIXER_INITTYPE_REINIT){
         mpxplay_debugf(MPXPLAY_DEBUG_OUTPUT,"tone reinit %d. %8.8X %d",(int)(mpi->frp-mpi->mvp->fr_base+1),(unsigned long)tis->low_filter_buffer,tis->low_filter_len);
        }
        break;
  case MIXER_INITTYPE_START:
        if(!mixer_tone_init_filters(mpi))
         return 0;
        mpxplay_debugf(MPXPLAY_DEBUG_OUTPUT,"tone start %d. %8.8X %d",(int)(mpi->frp-mpi->mvp->fr_base+1),(unsigned long)tis->low_filter_buffer,tis->low_filter_len);
  case MIXER_INITTYPE_RESET:
        if(!tis)
         return 0;
        if(tis->sync_flag==TONE_SYNCFLAG_END) // to avoid on-off-on
         tis->sync_flag=TONE_SYNCFLAG_RUN;
        else{
         tis->sync_flag=TONE_SYNCFLAG_BEGIN;
         mixer_tone_reset_filters(mpi);
        }
        if(inittype==MIXER_INITTYPE_RESET){
         mpxplay_debugf(MPXPLAY_DEBUG_OUTPUT,"tone reset %d. %8.8X %d",(int)(mpi->frp-mpi->mvp->fr_base+1),(unsigned long)tis->low_filter_buffer,tis->low_filter_len);
        }
        break;
  case MIXER_INITTYPE_CLOSE:
        if(tis){
         if(tis->low_filter_buffer)
          pds_free(tis->low_filter_buffer);
         pds_free(tis);
         mpi->private_data=NULL;
        }
        break;
 }
 return 1;
}

static void mixer_tone_process(struct mpxp_aumixer_passinfo_s *mpi)
{
 struct mpxplay_fxtone_info_s *tis=mpi->private_data;

 if(!mpi || !tis)
  return;

 if(funcbit_test(tis->config_flags,TONE_CONFIGFLAG_MIXERTONE))
  mixer_tone_apply_filters(mpi);

 if(tis->sync_flag==TONE_SYNCFLAG_END){
  tis->sync_flag=TONE_SYNCFLAG_STOP;
  mpi->control_cb(mpi->ccb_data, MPXPLAY_CFGFUNCNUM_AUMIXER_CHECKVAR, (void *)"MIX_TONE_BASS", NULL);
 }else{
  if(tis->sync_flag==TONE_SYNCFLAG_BEGIN)
   tis->sync_flag=TONE_SYNCFLAG_RUN;
  if(treble_set_req)
   mixer_tone_calculate_eqgain(mpi);
 }
 treble_set_req = 0;
}

static int mixer_tone_checkvar_bass(struct mpxp_aumixer_passinfo_s *mpi)
{
 mpxp_uint32_t card_infobits;
 mpxp_int32_t mixval_bass, mixval_treble;
 struct mpxplay_fxtone_info_s *tis=mpi->private_data;

 fxtone_get_mixvalues(mpi,&card_infobits,&mixval_bass,&mixval_treble);

 if(card_infobits&AUINFOS_CARDINFOBIT_HWTONE)
  return 0;

 if((mixval_bass!=MIXER_FUNCINFO_tone_bass.var_center)
   || (mixval_treble!=MIXER_FUNCINFO_tone_treble.var_center)){
  return 1;
 }

 if(!tis)
  return 0;
 if((tis->sync_flag==TONE_SYNCFLAG_RUN) || (tis->sync_flag==TONE_SYNCFLAG_END)){ // to do a post processing
  tis->sync_flag=TONE_SYNCFLAG_END;
  return 1;
 }
 tis->sync_flag=TONE_SYNCFLAG_STOP;

 return 0;
}

static int mixer_tone_checkvar_treble(struct mpxp_aumixer_passinfo_s *mpi)
{
 mpxp_uint32_t card_infobits;
 mpxp_int32_t mixval_treble;

 fxtone_get_mixvalues(mpi,&card_infobits,NULL,&mixval_treble);

 if(card_infobits&AUINFOS_CARDINFOBIT_HWTONE)
  return 0;
 if(mixval_treble!=MIXER_FUNCINFO_tone_treble.var_center)
  return 1;

 return 0;
}

static int tone_setvar(one_mixerfunc_info *infop,int currvalue,unsigned int setmode,int modvalue)
{
 int newvalue;
 switch(setmode){
  case MIXER_SETMODE_RELATIVE:newvalue=currvalue+modvalue*infop->var_step;
                              if((currvalue<infop->var_center && newvalue>infop->var_center) || (currvalue>infop->var_center && newvalue<infop->var_center))
                               newvalue=infop->var_center;
                              break;
  case MIXER_SETMODE_ABSOLUTE:newvalue=modvalue;break;
  case MIXER_SETMODE_RESET   :newvalue=infop->var_center;break;
 }
 if(newvalue<infop->var_min)
  newvalue=infop->var_min;
 else
  if(newvalue>infop->var_max)
   newvalue=infop->var_max;

 return newvalue;
}

static void mixer_tone_setvar_bass(struct mpxp_aumixer_passinfo_s *mpi,unsigned int setmode,int value)
{
 struct mpxplay_fxtone_info_s *tis=mpi->private_data;
 mpxp_uint32_t card_infobits;
 mpxp_int32_t mixval_bass;

 fxtone_get_mixvalues(mpi,&card_infobits,&mixval_bass,NULL);

 if(!(card_infobits&AUINFOS_CARDINFOBIT_HWTONE) && (setmode==MIXER_SETMODE_RESET) && (mixval_bass==MIXER_FUNCINFO_tone_bass.var_center))
  return;

 mixval_bass=tone_setvar(&MIXER_FUNCINFO_tone_bass,mixval_bass,setmode,value);

 mpi->control_cb(mpi->ccb_data,MPXPLAY_CFGFUNCNUM_AUMIXER_SET_CARD_BASS,&mixval_bass,NULL);

 if(card_infobits&AUINFOS_CARDINFOBIT_HWTONE)
  return; 
 if(funcbit_test(card_infobits,AUINFOS_CARDINFOBIT_PLAYING) && tis && ((tis->sync_flag==TONE_SYNCFLAG_BEGIN) || (tis->sync_flag==TONE_SYNCFLAG_END)))
  return;

 mixer_tone_calculate_eqgain(mpi);
}

static void mixer_tone_setvar_treble(struct mpxp_aumixer_passinfo_s *mpi,unsigned int setmode,int value)
{
 mpxp_uint32_t card_infobits;
 mpxp_int32_t mixval_treble;

 fxtone_get_mixvalues(mpi,&card_infobits,NULL,&mixval_treble);

 if(!(card_infobits&AUINFOS_CARDINFOBIT_HWTONE) && (setmode==MIXER_SETMODE_RESET) && (mixval_treble==MIXER_FUNCINFO_tone_treble.var_center))
  return;

 mixval_treble=tone_setvar(&MIXER_FUNCINFO_tone_treble,mixval_treble,setmode,value);

 mpi->control_cb(mpi->ccb_data,MPXPLAY_CFGFUNCNUM_AUMIXER_SET_CARD_TREBLE,&mixval_treble,NULL);

 if(card_infobits&AUINFOS_CARDINFOBIT_HWTONE)
  return;
 if(funcbit_test(card_infobits,AUINFOS_CARDINFOBIT_PLAYING)){
  treble_set_req = 1;
  return;
 }

 mixer_tone_calculate_eqgain(mpi);
}

one_mixerfunc_info MIXER_FUNCINFO_tone_bass={
 "MIX_TONE_BASS",
 "mxtb",
 &au_infos.card_mixer_values[AU_MIXCHAN_BASS], // for MIXER_getvalue only
 MIXER_INFOBIT_PARALLEL_DEPENDENCY|MIXER_INFOBIT_EXTERNAL_DEPENDENCY, // loudness|newfile
 0,TONE_VALUE_MAX,TONE_VALUE_CENTER,TONE_VALUE_STEP,
 &mixer_tone_init,
 NULL,
 &mixer_tone_process,
 &mixer_tone_checkvar_bass,
 &mixer_tone_setvar_bass
};

one_mixerfunc_info MIXER_FUNCINFO_tone_treble={
 "MIX_TONE_TREBLE",
 "mxtt",
 &au_infos.card_mixer_values[AU_MIXCHAN_TREBLE],
 MIXER_INFOBIT_PARALLEL_DEPENDENCY|MIXER_INFOBIT_EXTERNAL_DEPENDENCY, // loudness|newfile
 0,TONE_VALUE_MAX,TONE_VALUE_CENTER,TONE_VALUE_STEP,
 NULL,
 NULL,
 NULL,
 &mixer_tone_checkvar_treble,
 &mixer_tone_setvar_treble
};

//-------------------------------------------------------------------
#define MIXER_TONE_LOUDNESS_DEFAULT_VOL    230
#define MIXER_TONE_LOUDNESS_DEFAULT_SURR   120
#define MIXER_TONE_LOUDNESS_DEFAULT_BASS   130
#define MIXER_TONE_LOUDNESS_DEFAULT_TREBLE 150

unsigned long MIXER_loudness_val_volume = MIXER_TONE_LOUDNESS_DEFAULT_VOL;
unsigned long MIXER_loudness_val_surround = MIXER_TONE_LOUDNESS_DEFAULT_SURR;
unsigned long MIXER_loudness_val_bass = MIXER_TONE_LOUDNESS_DEFAULT_BASS;
unsigned long MIXER_loudness_val_treble = MIXER_TONE_LOUDNESS_DEFAULT_TREBLE;

static int loudness_enabled;

static int mixer_tone_checkvar_loudness(struct mpxp_aumixer_passinfo_s *mpi)
{
 mpxp_int32_t mixval_bass, mixval_treble;
 if(!loudness_enabled){
  fxtone_get_mixvalues(mpi, NULL, &mixval_bass, &mixval_treble);
  if((mixval_bass >= MIXER_loudness_val_bass) && (mixval_treble >= MIXER_loudness_val_treble))
   loudness_enabled = 1; // to correct flag after program restart (this flag is not saved in mpxplay.ini)
 }
 return loudness_enabled;
}

static void mixer_tone_setvar_loudness(struct mpxp_aumixer_passinfo_s *mpi, unsigned int setmode, int value)
{
 mpxp_uint32_t card_infobits;

 switch(setmode){
  case MIXER_SETMODE_RESET:
   if(!loudness_enabled)
    break;
  case MIXER_SETMODE_ABSOLUTE:
  case MIXER_SETMODE_RELATIVE:
   mixer_tone_checkvar_loudness(mpi);
   if(!loudness_enabled || ((setmode == MIXER_SETMODE_ABSOLUTE) && value)){
    mpi->control_cb(mpi->ccb_data, MPXPLAY_CFGFUNCNUM_AUMIXER_MIXVALUE_SET_ABS, "MIX_VOLUME",((void *)&MIXER_loudness_val_volume));
    mpi->control_cb(mpi->ccb_data, MPXPLAY_CFGFUNCNUM_AUMIXER_MIXVALUE_SET_ABS, "MIX_SURROUND",((void *)&MIXER_loudness_val_surround));
    mpi->control_cb(mpi->ccb_data, MPXPLAY_CFGFUNCNUM_AUMIXER_MIXVALUE_SET_ABS, "MIX_TONE_BASS",((void *)&MIXER_loudness_val_bass));
    mpi->control_cb(mpi->ccb_data, MPXPLAY_CFGFUNCNUM_AUMIXER_MIXVALUE_SET_ABS, "MIX_TONE_TREBLE",((void *)&MIXER_loudness_val_treble));
    loudness_enabled=1;
   }else{
    mpi->control_cb(mpi->ccb_data, MPXPLAY_CFGFUNCNUM_AUMIXER_MIXVALUE_SET_RES, "MIX_VOLUME", NULL);
    mpi->control_cb(mpi->ccb_data, MPXPLAY_CFGFUNCNUM_AUMIXER_MIXVALUE_SET_RES, "MIX_SURROUND", NULL);
    mpi->control_cb(mpi->ccb_data, MPXPLAY_CFGFUNCNUM_AUMIXER_MIXVALUE_SET_RES, "MIX_TONE_BASS", NULL);
    mpi->control_cb(mpi->ccb_data, MPXPLAY_CFGFUNCNUM_AUMIXER_MIXVALUE_SET_RES, "MIX_TONE_TREBLE", NULL);
    loudness_enabled=0;
   }
   fxtone_get_mixvalues(mpi, &card_infobits, NULL, NULL);
   if(!(card_infobits & AUINFOS_CARDINFOBIT_HWTONE))
    mixer_tone_calculate_eqgain(mpi);
 }
}

one_mixerfunc_info MIXER_FUNCINFO_tone_loudness={
 "MIX_TONE_LOUDNESS",
 "mxtl",
 &loudness_enabled,
 MIXER_INFOBIT_SWITCH,
 0,1,0,0,
 NULL,
 NULL,
 NULL,
 &mixer_tone_checkvar_loudness,
 &mixer_tone_setvar_loudness
};
