/*
 *  Copyright (c) by Jaroslav Kysela <perex@jcu.cz>
 *
 *
 *   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 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.  See the
 *   GNU General Public License for more details.
 *
 *   You should have received a copy of the GNU General Public License
 *   along with this program; if not, write to the Free Software
 *   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 *
 */

#include "driver.h"
#include "gus.h"
#define __GUS_TABLES_ALLOC__
#include "gus_tables.h"

unsigned short snd_gf1_lvol_to_gvol_raw( unsigned int vol )
{
  unsigned short e, m, tmp;
  
  if ( vol > 65535 ) vol = 65535;
  tmp = vol;
  e = 7;
  if ( tmp < 128 )
    {
      while ( e > 0 && tmp < ( 1 << e ) ) e--;
    }
   else
    {
      while ( tmp > 255 ) { tmp >>= 1; e++; }
    }
  m = vol - ( 1 << e );
  if ( m > 0 )
    {
      if ( e > 8 ) m >>= e - 8; else
      if ( e < 8 ) m <<= 8 - e;
      m &= 255;
    }

  return ( e << 8 ) | m;
}

unsigned int snd_gf1_gvol_to_lvol_raw( unsigned short gf1_vol )
{
  unsigned int rvol;
  unsigned short e, m;
  
  if ( !gf1_vol ) return 0;
  e = gf1_vol >> 8;
  m = (unsigned char)gf1_vol;
  rvol = 1 << e;
  if ( e > 8 )
    return rvol | (m << (e - 8));
  return rvol | (m >> (8 - e));
}

unsigned int snd_gf1_calc_ramp_rate( snd_gus_card_t *gus,
                                     unsigned short start,
                                     unsigned short end,
                                     unsigned int us )
{
  static unsigned char vol_rates[ 19 ] = {
    23, 24, 26, 28, 29, 31, 32, 34,
    36, 37, 39, 40, 42, 44, 45, 47,
    49, 50, 52
  };  
  unsigned short range, increment, value, i;
  
  start >>= 4;
  end >>= 4;
  if ( start < end )
    us /= end - start;
   else
    us /= start - end;
  range = 4;
  value = gus -> gf1.enh_mode ?
            vol_rates[ 0 ] :
            vol_rates[ gus -> gf1.active_voices - 14 ];
  for ( i = 0; i < 3; i++ )
    {
      if ( us < value )
        {
          range = i;
          break;
        }
       else
        value <<= 3;
    }
  if ( range == 4 )
    {
      range = 3;
      increment = 1;
    }
   else
    increment = ( value + ( value >> 1 ) ) / us;
  return ( range << 6 ) | ( increment & 0x3f );
}

unsigned short snd_gf1_translate_freq( snd_gus_card_t *gus, unsigned int freq2 )
{
  if ( freq2 < 50 ) freq2 = 50;
#ifdef SNDCFG_DEBUG
  if ( freq2 & 0xf8000000 )
    printk( "ultra_translate_freq: overflow - freq = 0x%x\n", freq2 );
#endif
  return ( ( freq2 << 9 ) + ( gus -> gf1.playback_freq >> 1 ) ) / gus -> gf1.playback_freq;
}

short snd_gf1_compute_vibrato( short cents, unsigned short fc_register )
{
  static short vibrato_table[] = {
  	0, 0,		32, 592,	61, 1175,	93, 1808,
  	124, 2433,	152, 3007,	182, 3632,	213, 4290,
  	241, 4834,	255, 5200
  };
  
  long depth;
  short *vi1, *vi2, pcents, v1;
  
  pcents = cents < 0 ? -cents : cents;
  for ( vi1 = vibrato_table, vi2 = vi1 + 2; pcents > *vi2; vi1 = vi2, vi2 += 2 );
  v1 = *(vi1 + 1);
  /* The FC table above is a list of pairs. The first number in the pair     */
  /* is the cents index from 0-255 cents, and the second number in the       */
  /* pair is the FC adjustment needed to change the pitch by the indexed     */
  /* number of cents. The table was created for an FC of 32768.              */
  /* The following expression does a linear interpolation against the        */
  /* approximated log curve in the table above, and then scales the number   */
  /* by the FC before the LFO. This calculation also adjusts the output      */
  /* value to produce the appropriate depth for the hardware. The depth      */
  /* is 2 * desired FC + 1.                                                  */
  depth = (((int)(*(vi2 + 1) - *vi1) * (pcents - *vi1) / (*vi2 - *vi1)) + v1) * fc_register >> 14;
  if ( depth ) depth++;
  if ( depth > 255 ) depth = 255;
  return cents < 0 ? -(short)depth : (short)depth;
}

unsigned short snd_gf1_compute_pitchbend( unsigned short pitchbend, unsigned short sens )
{  
  static long log_table[] = { 1024, 1085, 1149, 1218, 1290, 1367, 1448, 1534, 1625, 1722, 1825, 1933 };
  int wheel, sensitivity;
  unsigned int mantissa, f1, f2;
  unsigned short semitones, f1_index, f2_index, f1_power, f2_power;
  char bend_down = 0;
  int bend;

  if ( !sens ) return 1024;
  wheel = (int)pitchbend - 8192;
  sensitivity = ( (int)sens * wheel ) / 128;
  if ( sensitivity < 0 )
    {
      bend_down = 1;
      sensitivity = -sensitivity;
    }
  semitones = (unsigned int)(sensitivity >> 13);
  mantissa = sensitivity % 8192;
  f1_index = semitones % 12;
  f2_index = (semitones + 1) % 12;
  f1_power = semitones / 12;
  f2_power = (semitones + 1) / 12;
  f1 = log_table[ f1_index ] << f1_power;
  f2 = log_table[ f2_index ] << f2_power;
  bend = (int)((((f2 - f1) * mantissa) >> 13) + f1);
  if ( bend_down ) bend = 1048576L / bend;
  return bend;
}

unsigned short snd_gf1_compute_freq( unsigned int freq,
                                     unsigned int rate,
                                     unsigned short mix_rate )
{
  unsigned int fc;
  int scale = 0;
  
  while ( freq >= 4194304L ) { scale++; freq >>= 1; }
  fc = ( freq << 10 ) / rate;
#ifdef SNDCFG_DEBUG
  if ( fc > 97391L )
    printk( "patch: (1) fc frequency overflow - %u\n", fc );
#endif
  fc = ( fc * 44100UL ) / mix_rate;
  while ( scale-- ) fc <<= 1;
#ifdef SNDCFG_DEBUG
  if ( fc > 65535L )
    printk( "patch: (2) fc frequency overflow - %u\n", fc );
#endif
  return (unsigned short)fc;
}
