text: /***************************************************************************
   *             __________               __   ___.
   *   Open      \______   \ ____   ____ |  | _\_ |__   _______  ___
   *   Source     |       _//  _ \_/ ___\|  |/ /| __ \ /  _ \  \/  /
   *   Jukebox    |    |   (  <_> )  \___|    < | \_\ (  <_> > <  <
   *   Firmware   |____|_  /\____/ \___  >__|_ \|___  /\____/__/\_ \
   *                     \/            \/     \/    \/            \/
   * $Id$
   *
   * Copyright (C) 2006-2007 Adam Gashlin (hcs)
   * Copyright (C) 2004-2007 Shay Green (blargg)
   * Copyright (C) 2002 Brad Martin
   *
   * This program is free software; you can redistribute it and/or
   * modify it under the terms of the GNU General Public License
   * as published by the Free Software Foundation; either version 2
   * of the License, or (at your option) any later version.
   *
   * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
   * KIND, either express or implied.
   *
   ****************************************************************************/
  static inline int apply_gen_envx( struct voice_t* voice, int output )
  {
      return (output * voice->envx) >> 11;
  }
  
  static inline int apply_gen_volume( struct voice_t* voice, int output,
                                      int* amp_0, int* amp_1 )
  {
      *amp_0 = voice->volume [0] * output;
      *amp_1 = voice->volume [1] * output;
      return output;
  }
  
  static inline int apply_gen_amp( struct voice_t* voice, int output,
                                   int* amp_0, int* amp_1)
  {
      output = apply_gen_envx( voice, output );
      output = apply_gen_volume( voice, output, amp_0, amp_1 );
      return output;
  }
  
  #if !SPC_NOINTERP
  
  #ifndef SPC_GAUSSIAN_FAST_INTERP
  static inline int gaussian_fast_interp( int16_t const* samples,
                                          int32_t position,
                                          int16_t const* fwd,
                                          int16_t const* rev )
  {
      samples += position >> 12;
      return (fwd [0] * samples [0] +
              fwd [1] * samples [1] +
              rev [1] * samples [2] +
              rev [0] * samples [3]) >> 11;
  }
  #endif /* SPC_GAUSSIAN_FAST_INTERP */
  
  #ifndef SPC_GAUSSIAN_FAST_AMP
  #define gaussian_fast_amp   apply_amp
  #endif /* SPC_GAUSSIAN_FAST_AMP */
  
  #ifndef SPC_GAUSSIAN_SLOW_INTERP
  static inline int gaussian_slow_interp( int16_t const* samples,
                                          int32_t position,
                                          int16_t const* fwd,
                                          int16_t const* rev )
  {
      int output;
      samples += position >> 12;
      output = (fwd [0] * samples [0]) & ~0xFFF;
      output = (output + fwd [1] * samples [1]) & ~0xFFF;
      output = (output + rev [1] * samples [2]) >> 12;
      output = (int16_t) (output * 2);
      output += ((rev [0] * samples [3]) >> 12) * 2;
      return CLAMP16( output );
  }
  #endif /* SPC_GAUSSIAN_SLOW_INTERP */
  
  #ifndef SPC_GAUSSIAN_SLOW_AMP
  static inline int gaussian_slow_amp( struct voice_t* voice, int output,
                                       int *amp_0, int *amp_1 )
  {
      output = apply_gen_envx( voice, output ) & ~1;
      output = apply_gen_volume( voice, output, amp_0, amp_1 );
      return output;
  }
  #endif /* SPC_GAUSSIAN_SLOW_AMP */
  
  #define interp      gaussian_slow_interp
  #define apply_amp   gaussian_slow_amp
  
  #else /* SPC_NOINTERP */
  
  #ifndef SPC_LINEAR_INTERP
  static inline int linear_interp( int16_t const* samples, int32_t position )
  {
      int32_t fraction = position & 0xfff;
      int16_t const* pos = (samples + (position >> 12)) + 1;
      return pos[0] + ((fraction * (pos[1] - pos[0])) >> 12);
  }
  #endif /* SPC_LINEAR_INTERP */
  
  #define interp( samp, pos, fwd, rev ) \
      linear_interp( (samp), (pos) )
  
  #ifndef SPC_LINEAR_AMP
  #define linear_amp   apply_gen_amp
  #endif /* SPC_LINEAR_AMP */
  
  #define apply_amp    linear_amp
  #endif /* SPC_NOINTERP */
  
  
  #if !SPC_NOECHO
  
  #ifndef SPC_DSP_ECHO_APPLY
  /* Init FIR filter */
  static inline void echo_init( struct Spc_Dsp* this )
  {
      this->fir.pos = 0;
      ci->memset( this->fir.buf, 0, sizeof this->fir.buf );
  }
  
  /* Apply FIR filter */
  static inline void echo_apply(struct Spc_Dsp* this,
          uint8_t* const echo_ptr, int* out_0, int* out_1)
  {
      int fb_0 = GET_LE16SA( echo_ptr     );
      int fb_1 = GET_LE16SA( echo_ptr + 2 );
          
      /* Keep last 8 samples */
      int (* const fir_ptr) [2] = this->fir.buf + this->fir.pos;
      this->fir.pos = (this->fir.pos + 1) & (FIR_BUF_HALF - 1);
  
      fir_ptr [           0] [0] = fb_0;
      fir_ptr [           0] [1] = fb_1;
      /* duplicate at +8 eliminates wrap checking below */
      fir_ptr [FIR_BUF_HALF] [0] = fb_0;
      fir_ptr [FIR_BUF_HALF] [1] = fb_1;
  
      fb_0 *= this->fir.coeff [0];
      fb_1 *= this->fir.coeff [0];
  
      #define DO_PT( i ) \
          fb_0 += fir_ptr [i] [0] * this->fir.coeff [i]; \
          fb_1 += fir_ptr [i] [1] * this->fir.coeff [i];
  
      DO_PT( 1 )
      DO_PT( 2 )
      DO_PT( 3 )
      DO_PT( 4 )
      DO_PT( 5 )
      DO_PT( 6 )
      DO_PT( 7 )
  
      #undef DO_PT
  
      *out_0 = fb_0;
      *out_1 = fb_1;
  }
  #endif /* SPC_DSP_ECHO_APPLY */
  
  #ifndef SPC_DSP_ECHO_FEEDBACK
  /* Feedback into echo buffer */
  static inline void echo_feedback( struct Spc_Dsp* this, uint8_t *echo_ptr,
                                    int echo_0, int echo_1, int fb_0, int fb_1 )
  {
      int e0 = (echo_0 >> 7) + ((fb_0 * this->r.g.echo_feedback) >> 14);
      int e1 = (echo_1 >> 7) + ((fb_1 * this->r.g.echo_feedback) >> 14);
      e0 = CLAMP16( e0 );
      SET_LE16A( echo_ptr    , e0 );
      e1 = CLAMP16( e1 );
      SET_LE16A( echo_ptr + 2, e1 );
  }
  #endif /* SPC_DSP_ECHO_FEEDBACK */
  
  #ifndef SPC_DSP_GENERATE_OUTPUT
  /* Generate final output */
  static inline void echo_output( struct Spc_Dsp* this, int global_muting,
      int global_vol_0, int global_vol_1, int chans_0, int chans_1, 
      int fb_0, int fb_1, int* out_0, int* out_1 )
  {
      *out_0 = (chans_0 * global_vol_0 + fb_0 * this->r.g.echo_volume_0)
                  >> global_muting;
      *out_1 = (chans_1 * global_vol_1 + fb_1 * this->r.g.echo_volume_1)
                  >> global_muting;
  }
  #endif /* SPC_DSP_GENERATE_OUTPUT */
  
  #define mix_output echo_output
  
  #else /* SPC_NOECHO */
  
  #ifndef SPC_DSP_GENERATE_OUTPUT
  /* Generate final output */
  static inline void noecho_output( struct Spc_Dsp* this, int global_muting,
      int global_vol_0, int global_vol_1, int chans_0, int chans_1, 
      int* out_0, int* out_1 )
  {
      *out_0 = (chans_0 * global_vol_0) >> global_muting;
      *out_1 = (chans_1 * global_vol_1) >> global_muting;
      (void)this;
  }
  #endif /* SPC_DSP_GENERATE_OUTPUT */
  
  #define mix_output(this, gm, gv0, gv1, ch0, ch1, fb_0, fb_1, o0, o1) \
      noecho_output( (this), (gm), (gv0), (gv1), (ch0), (ch1), (o0), (o1) )
  
  #endif /* !SPC_NOECHO */
  