/**************************************************************************
*
* FunktrackerGOLD - By Jason Nunn
* (Derived from the DOS32 FunkTracker module - FUNKLITE.ASM)
*
* The 2nd revision Playback routines..
*
* Snail: 32 Rothdale Road, Moil, Darwin, NT, 0810, Australia
*
* ======================================================================
*
* NB/ Funktracker was first developed with Borland Turbo Assembler
* in 80386 Intel 32 bit Assembly for Protected mode.
*
**************************************************************************/
#pragma pack(1) /*force byte aligement. exact memory alignment is essential*/
#include "everything.h"
#include "funktracker.h"

/*protos*/
extern void DSPi_freq_convert(int chan_no,long rfreq);
extern void DSPi_volume_convert(int chan_no,int mvolume,int rvolume,int balance);
extern tchmix chmix[MAXIMUM_CHANNELS];
extern int no_active_channels;

/*funk ptrs*/
tfunk_info funk_info;
tfunk_hr *funk_hr_ptr;                   /*header pointer*/
tslot *funk_pat_ptr;
void *funk_sam_ptrs[64];                 /*sample pointers ptr*/
tfunk_chan funk_chan[MAXIMUM_CHANNELS];
void (*CARD_freq_convert)(int,long) = DSPi_freq_convert;
void (*CARD_volume_convert)(int,int,int,int) = DSPi_volume_convert;

/*******************************************
* private vars
*******************************************/
int note_table[64] =
{
13696,12928,12192,11520,10848,10240, 9664, 9120, 8608, 8128, 7680, 7248,
 6848, 6464, 6096, 5760, 5424, 5120, 4832, 4560, 4304, 4064, 3840, 3624,
 3424, 3232, 3048, 2880, 2712, 2560, 2416, 2280, 2152, 2032, 1920, 1808,
 1712, 1616, 1520, 1440, 1360, 1280, 1208, 1144, 1080, 1016,  960,  904,
  856,  808,  760,  720,  680,  640,  600,  568,  536,  504,  480,  448,
    0,    0,    0,    0
};

int com_volume_inc[16] =
{
    1,  2,  3,  4,  5,  6,  7,  8,
   10, 12, 16, 20, 24, 32, 48, 64
};

int com_sine_table[64] =
{
   -6, -19, -31, -43, -55, -66, -76, -86,
  -95,-103,-110,-116,-120,-124,-127,-128,
 -128,-127,-124,-121,-116,-110,-103, -95,
  -86, -77, -66, -55, -44, -32, -19,  -7,
    6,  18,  30,  42,  54,  65,  76,  85,
   94, 102, 109, 115, 120, 124, 126, 127,
  127, 126, 124, 121, 116, 110, 104,  96,
   87,  77,  67,  56,  44,  32,  20,   8
};

int com_triangle_table[64] =
{
   -4, -12, -20, -28, -36, -44, -52, -60,
  -68, -76, -84, -92,-100,-108,-116,-124,
 -124,-116,-108,-100, -92, -84, -76, -68,
  -60, -52, -44, -36, -28, -20, -12,  -4,
    4,  12,  20,  28,  36,  44,  52,  60,
   68,  76,  84,  92, 100, 108, 116, 124,
  123, 115, 107,  99,  91,  83,  75,  67,
   59,  51,  43,  35,  27,  19,  11,   3
};

int com_square_table[64] =
{
  -64,-128,-128,-127,-127,-128,-128,-127,
 -127,-128,-128,-127,-127,-128,-128,-127,
 -127,-128,-128,-127,-127,-128,-128,-127,
 -127,-128,-128,-127,-127,-128,-128, -64,
   95, 127, 127, 126, 126, 127, 127, 126,
  126, 127, 127, 126, 126, 127, 127, 126,
  126, 127, 127, 126, 126, 127, 127, 126,
  126, 127, 127, 126, 126, 127, 127,  95
};

int com_sawtooth_table[64] =
{
   -1,  -5,  -9, -13, -17, -21, -25, -29,
  -33, -37, -41, -45, -49, -53, -57, -61,
  -65, -69, -73, -77, -81, -85, -89, -93,
  -97,-101,-105,-109,-113,-117,-121,-125,
  127, 123, 119, 115, 111, 107, 103,  99,
   95,  91,  87,  83,  79,  75,  71,  67,
   63,  59,  55,  51,  47,  43,  39,  35,
   31,  27,  23,  19,  15,  11,   7,   3
};

int com_random_table[64] =
{
  168, 167, 167, 165, 201, 201, 202, 201,
  244, 244, 244, 244,  85,  82,  85,  85,
   44,  44,  44,  44, 114, 114, 114, 114,
   77,  77,  73,  77, 116, 116, 116, 116,
   87,  87,  87,  87,  31,  31,  31,  31,
  198, 198, 198, 198, 105, 104, 105, 105,
  103, 105, 103, 103, 166, 166, 165, 166,
  171, 172, 171, 171, 217, 217, 217, 217
};

int c_channel;
tslot *pattern_ptr;  /*ptr to extract data from pattern*/

/**************************************************************************
***************************************************************************
***************************************************************************
***************************************************************************
***************************************************************************
*
* Tracker Code
*
***************************************************************************
***************************************************************************
***************************************************************************
***************************************************************************
**************************************************************************/

/**************************************************************************
*
**************************************************************************/

/***********************************
*
* ebx = ifreq, returns = eax
*
***********************************/
long ifreq_to_rfreq(long ifreq)
{
  if(ifreq > 0)
    return (0x1b4f4d0 / ifreq);
  else
    return 0;
}

long ifreq_to_rfreq_vib(long ifreq)
{
  ifreq += funk_chan[c_channel].ifreq_vibrato;
  if(ifreq <= 0) ifreq = 1;
  return ifreq_to_rfreq(ifreq);
}

/***********************************
*
***********************************/
void volume_convert(void)
{
  if(funk_chan[c_channel].channel_kill == STOP)
    CARD_volume_convert(c_channel,0,0,0);
  else
    CARD_volume_convert(
      c_channel,
      funk_info.master_volume,
      funk_chan[c_channel].rvolume,
      funk_chan[c_channel].balance);
}

void vol_to_realvol(void)
{
  funk_chan[c_channel].rvolume = funk_chan[c_channel].volume;
}

void vol_to_realvol_vib(void)
{
  funk_chan[c_channel].rvolume =
    funk_chan[c_channel].volume + funk_chan[c_channel].volume_vibrato;
  if(funk_chan[c_channel].rvolume < 0) funk_chan[c_channel].rvolume = 0;
  if(funk_chan[c_channel].rvolume > 255) funk_chan[c_channel].rvolume = 255;
}

/***********************************
* bl = note, returns ebx = ifreq
***********************************/
#define note_2_ifreq(note) note_table[note]

/***********************************
*
* misc. vibrato function used by
* frequency and volume functions
*
* al = waveform
* ah = amplitude
* cl = speed
* ebx = ptr to vibrato table
* returns al = value, ebx = ptr
*
***********************************/
int com_vib_func(int waveform,
                 int amplitude,
                 int speed,
                 int *vib_table_ptr)
{
  register int tmp;

  amplitude++;
  switch(waveform)
  {
    case 1:        /*triangle*/
      tmp = com_triangle_table[*vib_table_ptr];
      break;
    case 2:        /*square*/
      tmp = com_square_table[*vib_table_ptr];
      break;
    case 3:        /*sawtooth*/
      tmp = com_sawtooth_table[*vib_table_ptr];
      break;
    case 4:        /*random*/
      tmp = com_random_table[*vib_table_ptr];
      break;
    default:       /*sine*/
      tmp = com_sine_table[*vib_table_ptr];
      break;
  }
  *vib_table_ptr += (~speed & 0xf) + 1;
  *vib_table_ptr &= 0x3f;
  return (tmp * amplitude) >> 4;
}

/***********************************
*
* Decode values form slot patterns
*
***********************************/
int decode_sample_no(void)
{
  funk_chan[c_channel].sample = 
    ((pattern_ptr->not_sam & 0x3) << 4) + (pattern_ptr->sam_com >> 4);
  return funk_chan[c_channel].sample;
}

int decode_note_no(void)
{
  return pattern_ptr->not_sam >> 2;
}

/***********************************
*
* for NOTE 3D: reload samples attrs
*
* returns sample volume
*
***********************************/
int reload_sample2(int sam_no)
{
  register int tmp1;

  funk_chan[c_channel].balance = funk_hr_ptr->funk_sb[sam_no].balance;

  tmp1 = funk_hr_ptr->funk_sb[sam_no].pt_and_sop;
  funk_chan[c_channel].port_type = tmp1 >> 4;
  funk_chan[c_channel].sample_ofs_parm = tmp1 & 0xf;

  tmp1 = funk_hr_ptr->funk_sb[sam_no].vv_waveform;
  funk_chan[c_channel].vib_waveform = tmp1 >> 4;
  funk_chan[c_channel].vol_vib_waveform = tmp1 & 0xf;

  tmp1 = funk_hr_ptr->funk_sb[sam_no].rl_and_as;
  funk_chan[c_channel].retrig_limit = tmp1 >> 4;
  funk_chan[c_channel].arp_speed = tmp1 & 0xf;

  return funk_hr_ptr->funk_sb[sam_no].volume;
}

int reload_sample_attr(void)
{
  return reload_sample2(decode_sample_no());
}

/***********************************
*
* returns sample volume
*
***********************************/
int do_retrig_sample(int sam_no)
{
  if(funk_hr_ptr->funk_sb[sam_no].length > 0)
  {
    if(funk_hr_ptr->funk_sb[sam_no].start == 0xffffffff)
      chmix[c_channel].funkctrl = 0x2;    /*set funkctrl*/
    else
      chmix[c_channel].funkctrl = 0x3;
    chmix[c_channel].sample_addr = funk_sam_ptrs[sam_no];
    chmix[c_channel].start = funk_hr_ptr->funk_sb[sam_no].start;

    if(chmix[c_channel].start > funk_hr_ptr->funk_sb[sam_no].length)
      chmix[c_channel].start = funk_hr_ptr->funk_sb[sam_no].length;

    chmix[c_channel].length = funk_hr_ptr->funk_sb[sam_no].length;
    chmix[c_channel].sample_ptr = 0;
  }
  return reload_sample2(sam_no);
}

int decode_sample(void)
{
  return do_retrig_sample(decode_sample_no());
}

/***********************************
*
***********************************/
void do_full_note(int note,int volume)
{
  funk_chan[c_channel].note = note;
  funk_chan[c_channel].volume = volume;
  funk_chan[c_channel].ifreq = note_2_ifreq(note);
  funk_chan[c_channel].rfreq = ifreq_to_rfreq(funk_chan[c_channel].ifreq);
  vol_to_realvol();
  CARD_freq_convert(c_channel,funk_chan[c_channel].rfreq);
  volume_convert();
}

void load_attr_active(int volume)
{
  funk_chan[c_channel].volume = volume;
  vol_to_realvol();
  volume_convert();
}

/***********************************
*
***********************************/
void normal_decode_system(void)
{
  register int note,volume;

  note = decode_note_no();
  if(note == 0x3d)                /*if ReLOad samples then ...*/
    load_attr_active(reload_sample_attr());
  else
  {
    volume = decode_sample();
    if(note != 0x3e)              /*if SAMPLE ONLY slot,then dont change note*/
    {
      funk_chan[c_channel].note_command = 0xf;
      funk_chan[c_channel].volume_command = 0xf;
      do_full_note(note,volume);
    }
  }
}

void normal_decode_note(void)
{
  register int note,volume;

  note = decode_note_no();
  if(note == 0x3d)                /*if ReLOad samples then ...*/
    load_attr_active(reload_sample_attr());
  else
  {
    volume = decode_sample();
    if(note != 0x3e)              /*if SAMPLE ONLY slot,then dont change note*/
    {
      funk_chan[c_channel].volume_command = 0xf;
      do_full_note(note,volume);
    }
  }
}

void normal_decode_volume(void)
{
  register int note,volume;

  note = decode_note_no();
  if(note == 0x3d)                /*if ReLOad samples then ...*/
    load_attr_active(reload_sample_attr());
  else
  {
    volume = decode_sample();
    if(note != 0x3e)              /*if SAMPLE ONLY slot,then dont change note*/
    {
      funk_chan[c_channel].note_command = 0xf;
      do_full_note(note,volume);
    }
  }
}

/***********************************
*
***********************************/
void comc_decode(void)
{
  register int note,volume;

  note = decode_note_no();
  if(note == 0x3d)                /*if ReLOad samples then ...*/
    load_attr_active(reload_sample_attr());
  else
  {
    volume = decode_sample();
    if(note != 0x3e)              /*if SAMPLE ONLY slot,then dont change note*/
    {
      funk_chan[c_channel].volume_command = 0xf;
      funk_chan[c_channel].volume = volume;
      funk_chan[c_channel].note = note;
      funk_chan[c_channel].ifreq_portdest = note_2_ifreq(note);
      funk_chan[c_channel].rfreq_portdest = ifreq_to_rfreq(funk_chan[c_channel].ifreq_portdest);
      vol_to_realvol();
      CARD_freq_convert(c_channel,funk_chan[c_channel].rfreq);
      volume_convert();
    }
  }
}

/***********************************
*
***********************************/
void comi_decode(void)
{
  register int note,volume;

  note = decode_note_no();
  if(note == 0x3d)                /*if ReLOad samples then ...*/
    funk_chan[c_channel].volume_portdest = reload_sample_attr();
  else
  {
    volume = decode_sample();
    if(note != 0x3e)              /*if SAMPLE ONLY slot,then dont change note*/
    {
      funk_chan[c_channel].note_command = 0xf;
      funk_chan[c_channel].volume_portdest = volume;
      funk_chan[c_channel].note = note;
      funk_chan[c_channel].ifreq = note_2_ifreq(note);
      funk_chan[c_channel].rfreq = ifreq_to_rfreq(funk_chan[c_channel].ifreq);
      vol_to_realvol();
      CARD_freq_convert(c_channel,funk_chan[c_channel].rfreq);
      volume_convert();
    }
    else
    {
      funk_chan[c_channel].volume_portdest = volume;
    }
  }
}

/***************************************************************************
*
***************************************************************************/
#define IPORT_TOP_LIM 448
#define RPORT_TOP_LIM (0x1b4f4d0 / 448)

#define IPORT_BOT_LIM 13696
#define RPORT_BOT_LIM (0x1b4f4d0 / 13696)

/***********************************
*0Com A: port up  (modified as from R2)
*
* com_val : 00000000
*           \ rate /
*
***********************************/
void com_port_up_log(int note_com_val)
{
  funk_chan[c_channel].ifreq -= (note_com_val + 1) << 2;
  if(funk_chan[c_channel].ifreq <= IPORT_TOP_LIM)
  {
    funk_chan[c_channel].note_command = 0xf;
    funk_chan[c_channel].ifreq = IPORT_TOP_LIM;
  }
  funk_chan[c_channel].rfreq = ifreq_to_rfreq(funk_chan[c_channel].ifreq);
  CARD_freq_convert(c_channel,funk_chan[c_channel].rfreq);
}

void com_port_up_lin(int note_com_val)
{
  funk_chan[c_channel].rfreq += (note_com_val + 1) << 3;
  if(funk_chan[c_channel].rfreq >= RPORT_TOP_LIM)
  {
    funk_chan[c_channel].note_command = 0xf;
    funk_chan[c_channel].rfreq = RPORT_TOP_LIM;
  }
  CARD_freq_convert(c_channel,funk_chan[c_channel].rfreq);
}

/***********************************
*1Com B: port down (modified as of R2)
*
* com_val : 00000000
*           \ rate /
*
***********************************/
void com_port_dn_log(int note_com_val)
{
  funk_chan[c_channel].ifreq += (note_com_val + 1) << 2;
  if(funk_chan[c_channel].ifreq >= IPORT_BOT_LIM)
  {
    funk_chan[c_channel].note_command = 0xf;
    funk_chan[c_channel].ifreq = IPORT_BOT_LIM;
  }
  funk_chan[c_channel].rfreq = ifreq_to_rfreq(funk_chan[c_channel].ifreq);
  CARD_freq_convert(c_channel,funk_chan[c_channel].rfreq);
}

void com_port_dn_lin(int note_com_val)
{
  funk_chan[c_channel].rfreq -= (note_com_val + 1) << 3;
  if(funk_chan[c_channel].rfreq <= RPORT_BOT_LIM)
  {
    funk_chan[c_channel].note_command = 0xf;
    funk_chan[c_channel].rfreq = RPORT_BOT_LIM;
  }
  CARD_freq_convert(c_channel,funk_chan[c_channel].rfreq);
}

/***********************************
*2Com C: port to note (modified as of R2)
*
***********************************/
void com_porta_log_subm1(void)
{
  funk_chan[c_channel].note_command = 0xf;
  funk_chan[c_channel].ifreq = funk_chan[c_channel].ifreq_portdest;
  funk_chan[c_channel].rfreq = ifreq_to_rfreq(funk_chan[c_channel].ifreq);
  CARD_freq_convert(c_channel,funk_chan[c_channel].rfreq);
}

void com_porta_log(void)
{
  if(funk_chan[c_channel].ifreq == funk_chan[c_channel].ifreq_portdest)
    funk_chan[c_channel].note_command = 0xf;
  else
    if(funk_chan[c_channel].ifreq > funk_chan[c_channel].ifreq_portdest)
    {
      com_port_up_log(funk_chan[c_channel].note_com_val);
      if(funk_chan[c_channel].ifreq <= funk_chan[c_channel].ifreq_portdest)
        com_porta_log_subm1();
    }
    else
    {
      com_port_dn_log(funk_chan[c_channel].note_com_val);
      if(funk_chan[c_channel].ifreq >= funk_chan[c_channel].ifreq_portdest)
        com_porta_log_subm1();
    }
}

void com_porta_lin_subm1(void)
{
  funk_chan[c_channel].note_command = 0xf;
  funk_chan[c_channel].rfreq = funk_chan[c_channel].rfreq_portdest;
  CARD_freq_convert(c_channel,funk_chan[c_channel].rfreq);
}

void com_porta_lin(void)
{
  if(funk_chan[c_channel].rfreq == funk_chan[c_channel].rfreq_portdest)
    funk_chan[c_channel].note_command = 0xf;
  else
    if(funk_chan[c_channel].rfreq < funk_chan[c_channel].rfreq_portdest)
    {
      com_port_up_lin(funk_chan[c_channel].note_com_val);
      if(funk_chan[c_channel].rfreq >= funk_chan[c_channel].rfreq_portdest)
        com_porta_lin_subm1();
    }
    else
    {
      com_port_dn_lin(funk_chan[c_channel].note_com_val);
      if(funk_chan[c_channel].rfreq <= funk_chan[c_channel].rfreq_portdest)
        com_porta_lin_subm1();
    }
}

/***********************************
*3Com D: vibrato
*
* com_val :   0000   0000
*           \speed/ \amplitude/
*
*vib_waveform = 0   : sine
*               1   : triangle
*               2   : square
*               3   : sawtooth
*               4   : random
*
***********************************/
#define VIBAMP 1

void com_vibrato(void)
{
  funk_chan[c_channel].ifreq_vibrato = com_vib_func(
    funk_chan[c_channel].vib_waveform,
    funk_chan[c_channel].note_com_val & 0xf, /*amp*/
    funk_chan[c_channel].note_com_val >> 4,  /*speed*/
    &funk_chan[c_channel].vib_ptr) << VIBAMP;
  funk_chan[c_channel].rfreq = ifreq_to_rfreq_vib(funk_chan[c_channel].ifreq);
  CARD_freq_convert(c_channel,funk_chan[c_channel].rfreq);
}

/***********************************
*4Com E: vibrato Fanin
*
* com_val :   0000   0000
*           \speed/ \fanin value/
*
*vib_waveform = 0   : sine
*               1   : triangle
*               2   : square
*               3   : sawtooth
*               4   : random
*
***********************************/
void com_vib_fanin(void)
{
  register int fanin,speed;

  fanin = funk_chan[c_channel].note_com_val & 0xf;
  speed = (funk_chan[c_channel].note_com_val & 0xf0) >> 2;

  if(funk_chan[c_channel].note_beat_count < 0xf)
    if(funk_chan[c_channel].note_comspd_count < speed)
      funk_chan[c_channel].note_comspd_count++;
    else
    {
      funk_chan[c_channel].note_comspd_count = 0;
      funk_chan[c_channel].note_beat_count++;
    }
  funk_chan[c_channel].ifreq_vibrato = com_vib_func(
    funk_chan[c_channel].vib_waveform,
    fanin,                                 /*amp*/
    funk_chan[c_channel].note_beat_count,  /*speed*/
    &funk_chan[c_channel].vib_ptr) << VIBAMP;
  funk_chan[c_channel].rfreq = ifreq_to_rfreq_vib(funk_chan[c_channel].ifreq);
  CARD_freq_convert(c_channel,funk_chan[c_channel].rfreq);
}

/***********************************
*5Com F: vibrato Fanout
*
* com_val :   0000   0000
*           \speed/ \fanin value/
*
*vib_waveform = 0   : sine
*               1   : triangle
*               2   : square
*               3   : sawtooth
*               4   : random
*
***********************************/
void com_vib_fanout(void)
{
  register int fanin,speed;

  fanin = funk_chan[c_channel].note_com_val & 0xf;
  speed = (funk_chan[c_channel].note_com_val & 0xf0) >> 2;

  if(funk_chan[c_channel].note_beat_count > 0)
    if(funk_chan[c_channel].note_comspd_count < speed)
      funk_chan[c_channel].note_comspd_count++;
    else
    {
      funk_chan[c_channel].note_comspd_count = 0;
      funk_chan[c_channel].note_beat_count--;
    }
  funk_chan[c_channel].ifreq_vibrato = com_vib_func(
    funk_chan[c_channel].vib_waveform,
    fanin,                                 /*amp*/
    funk_chan[c_channel].note_beat_count,  /*speed*/
    &funk_chan[c_channel].vib_ptr) << VIBAMP;
  funk_chan[c_channel].rfreq = ifreq_to_rfreq_vib(funk_chan[c_channel].ifreq);
  CARD_freq_convert(c_channel,funk_chan[c_channel].rfreq);
}

/***********************************
*6Com G: volume sld up
*
* com_val : 0000   0000
*                 \rate/
*
***********************************/
void com_vol_up(void)
{
  funk_chan[c_channel].volume +=
    com_volume_inc[funk_chan[c_channel].volume_com_val & 0xf];

  if(funk_chan[c_channel].volume > 255)
  {
    funk_chan[c_channel].volume_command = 0xf;
    funk_chan[c_channel].volume = 255;
  }
  vol_to_realvol();
  volume_convert();
}

/***********************************
*7Com H: volume slide down
*
* com_val : 0000   0000
*                 \rate/
*
***********************************/
void com_vol_dn(void)
{
  funk_chan[c_channel].volume -=
    com_volume_inc[funk_chan[c_channel].volume_com_val & 0xf];
  if(funk_chan[c_channel].volume < 0)
  {
    funk_chan[c_channel].volume_command = 0xf;
    funk_chan[c_channel].volume = 0;
  }
  vol_to_realvol();
  volume_convert();
}

/***********************************
*8Com I: volume porta (Rapid ctrl)
*
***********************************/
void com_vol_porta_subm1(void)
{
  funk_chan[c_channel].volume_command = 0xf;
  funk_chan[c_channel].volume = funk_chan[c_channel].volume_portdest;
  vol_to_realvol();
  volume_convert();
}

void com_vol_porta(void)
{
  if(funk_chan[c_channel].volume == funk_chan[c_channel].volume_portdest)
    funk_chan[c_channel].volume_command = 0xf;
  else
  {
    if(funk_chan[c_channel].volume >= funk_chan[c_channel].volume_portdest)
    {
      com_vol_dn();
      if(funk_chan[c_channel].volume <= funk_chan[c_channel].volume_portdest)
        com_vol_porta_subm1();
    }
    else
    {
      com_vol_up();
      if(funk_chan[c_channel].volume >= funk_chan[c_channel].volume_portdest)
        com_vol_porta_subm1();
    }
  }
}

/*********************************
*
*********************************/
void vol_sys_rapid_p1(void)
{
  if((funk_chan[c_channel].volume_command >= 6) &&
     (funk_chan[c_channel].volume_command <= 8))
    if(funk_chan[c_channel].volume_comspd_count <
      (funk_chan[c_channel].volume_com_val >> 4))
    {
      funk_chan[c_channel].volume_comspd_count++;
    }
    else
    {
      funk_chan[c_channel].volume_comspd_count = 0;
      switch(funk_chan[c_channel].volume_command)
      {
        case 0x6:
          com_vol_up();
          break;
        case 0x7:
          com_vol_dn();
          break;
        case 0x8:
          com_vol_porta();
          break;
      }
    }
}

/***********************************
*9Com J: Volume Reverb
*
* A simulated echo effect
*
* com_val : 0000   0000
*
*
***********************************/
void com_reverb(void)
{
  register int incre = (funk_chan[c_channel].volume_com_val & 0xf);
  register int speed = (funk_chan[c_channel].volume_com_val & 0xf0) >> 2;
  if(funk_chan[c_channel].volume_comspd_count < speed)
  {
    funk_chan[c_channel].volume_comspd_count++;
    if(funk_chan[c_channel].volume > 0)
    {
      funk_chan[c_channel].volume -= com_volume_inc[incre];
      if(funk_chan[c_channel].volume < 0)
        funk_chan[c_channel].volume = 0;
    }
  }
  else /*echo*/
  {
    funk_chan[c_channel].volume_comspd_count = 0;
    funk_chan[c_channel].volume_beat_count -=
      (-funk_chan[c_channel].volume_com_val & 0xf0) >> 4;
    if(funk_chan[c_channel].volume_beat_count > 0)
    {
      funk_chan[c_channel].volume = funk_chan[c_channel].volume_beat_count;
    }
    else
    {
      funk_chan[c_channel].volume_command = 0xf;
      funk_chan[c_channel].volume = 0;
    }
  }
  vol_to_realvol();
  volume_convert();
}

/***********************************
*ACom K: tremola
*
* com_val :  0000    0000
*                   \rate/
*
***********************************/
void com_tremola(void)
{
  funk_chan[c_channel].volume_vibrato = com_vib_func(
    funk_chan[c_channel].vol_vib_waveform,
    funk_chan[c_channel].volume_com_val & 0xf, /*amp*/
    funk_chan[c_channel].volume_com_val >> 4,  /*speed*/
    &funk_chan[c_channel].vol_vib_ptr);
  vol_to_realvol_vib();
  volume_convert();
}

/***********************************
*BCom L: arpeggio
*
* com_val :  0000    0000
*            \N1/    \N2/
*
***********************************/
void com_arpeggio(void)
{
  register int arp_note;

  if(funk_chan[c_channel].note_comspd_count < funk_chan[c_channel].arp_speed)
    funk_chan[c_channel].note_comspd_count++;
  else
  {
    funk_chan[c_channel].note_comspd_count = 0;
    arp_note = funk_chan[c_channel].note;
    switch(funk_chan[c_channel].note_beat_count)
    {
      case 1:
        arp_note += (funk_chan[c_channel].note_com_val >> 4);
        break;
      case 2:
        arp_note += (funk_chan[c_channel].note_com_val & 0xf);
        break;
    }
    if(arp_note > 59)
      arp_note = funk_chan[c_channel].note;
    funk_chan[c_channel].ifreq = note_2_ifreq(arp_note);
    funk_chan[c_channel].rfreq = ifreq_to_rfreq(funk_chan[c_channel].ifreq);
    CARD_freq_convert(c_channel,funk_chan[c_channel].rfreq);
    if(funk_chan[c_channel].note_beat_count == 2)
      funk_chan[c_channel].note_beat_count = 0;
    else
      funk_chan[c_channel].note_beat_count++;
  }
}

/***********************************
*CCom M: sample offset
*
* This is a all present sample
* offset that is effective all the
* time for a given channel
*
*
***********************************/
void com_sample_offset(void)
{
  register long offs = funk_chan[c_channel].com_val <<
                       funk_chan[c_channel].sample_ofs_parm;
  if(offs > funk_hr_ptr->funk_sb[funk_chan[c_channel].sample].length)
    offs = funk_hr_ptr->funk_sb[funk_chan[c_channel].sample].length;
  chmix[c_channel].sample_ptr += offs;
}

/***********************************
*DCom N: volume
*
***********************************/
void com_volume(void)
{
  funk_chan[c_channel].volume = funk_chan[c_channel].volume_com_val;
  vol_to_realvol();
  volume_convert();
}

/***********************************
*ECom O0: misc control (cmd revised since R1)
*
*value  control set
*------------------
*0     vibrato sine
*1     vibrato triangle
*2     vibrato square
*3     vibrato sawtooth
*4     vibrato random
*5     tremola sine
*6     tremola triangle
*7     tremola square
*8     tremola sawtooth
*9     tremola random
*a     halt note system
*b     halt volume system
*c     halt all systems
*d     invert funkcrtl
*e     fine balance pann left
*f     fine balance pann right
*
***********************************/
void com_misccrtl(void)
{
  switch(funk_chan[c_channel].com_val & 0xf)
  {
    case 0x0:
      funk_chan[c_channel].vib_waveform = 0;
      break;
    case 0x1:
      funk_chan[c_channel].vib_waveform = 1;
      break;
    case 0x2:
      funk_chan[c_channel].vib_waveform = 2;
      break;
    case 0x3:
      funk_chan[c_channel].vib_waveform = 3;
      break;
    case 0x4:
      funk_chan[c_channel].vib_waveform = 4;
      break;
    case 0x5:
      funk_chan[c_channel].vol_vib_waveform = 0;
      break;
    case 0x6:
      funk_chan[c_channel].vol_vib_waveform = 1;
      break;
    case 0x7:
      funk_chan[c_channel].vol_vib_waveform = 2;
      break;
    case 0x8:
      funk_chan[c_channel].vol_vib_waveform = 3;
      break;
    case 0x9:
      funk_chan[c_channel].vol_vib_waveform = 4;
      break;
    case 0xa:
      funk_chan[c_channel].note_command = 0xf;
      break;
    case 0xb:
      funk_chan[c_channel].volume_command = 0xf;
      break;
    case 0xc:
      funk_chan[c_channel].note_command = 0xf;
      funk_chan[c_channel].volume_command = 0xf;
      break;
    case 0xd:
      /* NOT USED*/
      break;
    case 0xe:
    case 0xf:
      funk_chan[c_channel].volume_command = funk_chan[c_channel].command;
      funk_chan[c_channel].volume_com_val = funk_chan[c_channel].com_val;
      break;
  }
}

void com_pan_left(void)
{
  funk_chan[c_channel].balance--;
  if(funk_chan[c_channel].balance < 0)
  {
    funk_chan[c_channel].volume_command = 0xf;
    funk_chan[c_channel].balance = 0;
  }
  volume_convert();
}

void com_pan_right(void)
{
  funk_chan[c_channel].balance++;
  if(funk_chan[c_channel].balance > 0xff)
  {
    funk_chan[c_channel].volume_command = 0xf;
    funk_chan[c_channel].balance = 0xff;
  }
  volume_convert();
}

/***********************************
* Com O1: volume cut               *
*                                  *
***********************************/
void com_volume_cut(void)
{
  if(funk_chan[c_channel].volume_comspd_count <
     (funk_chan[c_channel].volume_com_val & 0xf))
  {
    funk_chan[c_channel].volume_comspd_count++;
  }
  else
  {
    funk_chan[c_channel].volume_command = 0xf;
    funk_chan[c_channel].volume = 0;
    vol_to_realvol();
    volume_convert();
  }
}

/***********************************
* Com O2: Note Delay
*
*NB/ will only work within a slot time period.
*when the next slot is loaded, the command and value are
*purged (just like retrig)
***********************************/
void com_note_delay(void)
{
  if(funk_chan[c_channel].comspd_count <
     (funk_chan[c_channel].com_val & 0xf))
  {
    funk_chan[c_channel].comspd_count++;
  }
  else
  {
    funk_chan[c_channel].command = 0xf;
    pattern_ptr = funk_chan[c_channel].delay_pattern_ptr;
    normal_decode_system();
  }
}

/***********************************
* Com O3: set arpeggio speed
*
***********************************/
void com_arp_speed_set(void)
{
  funk_chan[c_channel].arp_speed = funk_chan[c_channel].com_val & 0xf;
}

/***********************************
* Com O4: fine port_up
*
* com_val :     0000
*           \ rate /
*
***********************************/
void com_fine_port_up(void)
{
  register int x = funk_chan[c_channel].note_com_val & 0xf;
  if(funk_chan[c_channel].port_type)
    com_port_up_lin(x);
  else
    com_port_up_log(x);
}

/***********************************
* Com O5: fine port_dn
*
* com_val :     0000
*           \ rate /
*
***********************************/
void com_fine_port_dn(void)
{
  register int x = funk_chan[c_channel].note_com_val & 0xf;
  if(funk_chan[c_channel].port_type)
    com_port_dn_lin(x);
  else
    com_port_dn_log(x);
}

/***********************************
* Com O6: fine vol sld up
*
* com_val :        0000
*                 \rate/
*
***********************************/

/*refer to com_vol_up(void)*/

/***********************************
* Com O7: fine vol sld dn
*
* com_val :        0000
*                 \rate/
*
***********************************/

/*refer to com_vol_dn(void)*/

/***********************************
* Com O8: volume crest
*
* com_val :        0000
*                 \speed/ (prehand)
*
* volume slides up then down,
* starting at current frequency
*
***********************************/
void com_vol_crest(void)
{
  switch(funk_chan[c_channel].volume_beat_count)
  {
    case 1: /*@@wait_a_bit*/
      if(funk_chan[c_channel].volume_comspd_count <
        (-((funk_chan[c_channel].volume_com_val & 0xf) + 1) & 0xf))
      {
        funk_chan[c_channel].volume_comspd_count++;
      }
      else
      {
        funk_chan[c_channel].volume_comspd_count = 0;
        funk_chan[c_channel].volume_beat_count++;
      }
      break;
    case 2: /*@@slide_down*/
      funk_chan[c_channel].volume -=
        (com_volume_inc[funk_chan[c_channel].volume_com_val & 0xf] >> 2) + 1;
      if(funk_chan[c_channel].volume <= funk_chan[c_channel].volume_portdest)
      {
        funk_chan[c_channel].volume_command = 0xf;
        funk_chan[c_channel].volume = funk_chan[c_channel].volume_portdest;
      }
      vol_to_realvol();
      volume_convert();
      break;
    default:
      funk_chan[c_channel].volume +=
        com_volume_inc[funk_chan[c_channel].volume_com_val & 0xf];
      if(funk_chan[c_channel].volume > 0xff)
      {
        funk_chan[c_channel].volume_comspd_count = 0;
        funk_chan[c_channel].volume_beat_count++;
        funk_chan[c_channel].volume = 0xff;
      }
      vol_to_realvol();
      volume_convert();
      break;
  }
}

/***********************************
* Com O9: Echo Feedback Gain (was volume though in R1)
*
***********************************/
void com_echo_feedback(void)
{
  chmix[c_channel].echo_feedback = funk_chan[c_channel].com_val & 0xf;
}

/***********************************
* Com OA: master volume set (cmd revised since R1)
*
* master effects all volumes.
*
* format:  4 3 2 1
*         \| \---/
*         dir   increment
*          1 = up
*          0 = down
***********************************/
void com_master_set(void)
{
  register int tmp = c_channel;
  register int dec_val = (funk_chan[c_channel].com_val & 0x7) << 2;

  if(funk_chan[c_channel].com_val & 0x8)
  {
    funk_info.master_volume += dec_val;
    if(funk_info.master_volume > 0xff)
      funk_info.master_volume = 0xff;
  }
  else
  {
    funk_info.master_volume -= dec_val;
    if(funk_info.master_volume < 0)
      funk_info.master_volume = 0;
  }
  for(c_channel = 0;c_channel < no_active_channels;c_channel++)
    volume_convert();
  c_channel = tmp;
}

/***********************************
* Com OB: Echo Delay
*
* In the Funktracker Revision 1 (DOS32 Funktracker), this command
* was "Expand Loop". As the command wasn't used very often (and
* wasn't very useful anyway, it has been replaced by this extremely
* useful command.
***********************************/
void com_echo_delay(void)
{
  chmix[c_channel].echo_delay = funk_chan[c_channel].com_val & 0xf;
}

/***********************************
* Com OC: Echo Decay
*
* Refer to the above comment in Com OB. This command used to be
* "Colapse Loop". I don't think i've ever used these old commands
* actually.
***********************************/
void com_echo_decay(void)
{
   chmix[c_channel].echo_decay = funk_chan[c_channel].com_val & 0xf;
}

/***********************************
* Com OD: note retrig
*
* com_val : ????   0000
*                 \speed/ (prehand)
*
* command only handled on full
* or sample only slots
*
***********************************/
void com_note_retrig(void)
{
  if(funk_chan[c_channel].retrig_spd_count <
    (funk_chan[c_channel].com_val & 0xf))
  {
    funk_chan[c_channel].retrig_spd_count++;
  }
  else
  {
    funk_chan[c_channel].retrig_spd_count = 0;
    if(funk_chan[c_channel].retrig_count < funk_chan[c_channel].retrig_limit)
    {
      funk_chan[c_channel].retrig_count++;
      do_retrig_sample(funk_chan[c_channel].sample);
    }
    else
      funk_chan[c_channel].command = 0xf;
  }
}

/***********************************
* Com OE: set channel balance
*
***********************************/
void com_balance(void)
{
  funk_chan[c_channel].balance = (funk_chan[c_channel].com_val & 0xf) << 4;
  volume_convert();
}

/***********************************
* Com OF: tempo
*
***********************************/
void com_tempo(void)
{
  funk_info.tempo = funk_chan[c_channel].com_val & 0xf;
}

/***************************************************************************
*
* Command control for null slots only
*
***************************************************************************/
void cnc_vs_basic(void)
{
  funk_chan[c_channel].volume_command = funk_chan[c_channel].command;
  funk_chan[c_channel].volume_com_val = funk_chan[c_channel].com_val;
  funk_chan[c_channel].volume_comspd_count = 0;
}

void com_null_ctrl(void)
{
  switch(funk_chan[c_channel].command)
  {
    case 0x0:                                          /*com a*/
      funk_chan[c_channel].note_command = funk_chan[c_channel].command;
      funk_chan[c_channel].note_com_val = funk_chan[c_channel].com_val;
      break;
    case 0x1:                                          /*com b*/
      funk_chan[c_channel].note_command = funk_chan[c_channel].command;
      funk_chan[c_channel].note_com_val = funk_chan[c_channel].com_val;
      break;
    case 0x2:                                          /*com c*/
      funk_chan[c_channel].note_command = funk_chan[c_channel].command;
      funk_chan[c_channel].note_com_val = funk_chan[c_channel].com_val;
      break;
    case 0x3:                                          /*com d*/
      funk_chan[c_channel].note_command = funk_chan[c_channel].command;
      funk_chan[c_channel].note_com_val = funk_chan[c_channel].com_val;
      funk_chan[c_channel].note_comspd_count = 0;
      break;
    case 0x4:                                          /*com e*/
      funk_chan[c_channel].note_beat_count = 0;
      goto set_vib_fan;
    case 0x5:                                          /*com f*/
      funk_chan[c_channel].note_beat_count = 0xf;
set_vib_fan:
      if(funk_chan[c_channel].note_command == 0x3)/*if vibrato command*/
        funk_chan[c_channel].note_beat_count =
          funk_chan[c_channel].note_com_val >> 4;
      funk_chan[c_channel].note_command = funk_chan[c_channel].command;
      funk_chan[c_channel].note_com_val = funk_chan[c_channel].com_val;
      funk_chan[c_channel].note_comspd_count = 0;
      break;
    case 0x6:                                          /*com g*/
      cnc_vs_basic();
      break;
    case 0x7:                                          /*com h*/
      cnc_vs_basic();
      break;
    case 0x8:                                          /*com i*/
      cnc_vs_basic();
      break;
    case 0x9:                                          /*com j*/
      funk_chan[c_channel].volume_command = funk_chan[c_channel].command;
      funk_chan[c_channel].volume_com_val = funk_chan[c_channel].com_val;
      funk_chan[c_channel].volume_comspd_count = 0;
      funk_chan[c_channel].volume_beat_count = funk_chan[c_channel].volume;
      break;
    case 0xa:                                          /*com k*/
      cnc_vs_basic();
      break;
    case 0xb:                                          /*com l*/
      funk_chan[c_channel].note_command = funk_chan[c_channel].command;
      funk_chan[c_channel].note_com_val = funk_chan[c_channel].com_val;
      funk_chan[c_channel].note_comspd_count = 0;
      funk_chan[c_channel].note_beat_count = 1;
      break;
    case 0xd:                                          /*com n*/
      funk_chan[c_channel].volume_command = funk_chan[c_channel].command;
      funk_chan[c_channel].volume_com_val = funk_chan[c_channel].com_val;
      com_volume();
      break;
    case 0xe:                                          /*O commands*/
      switch(funk_chan[c_channel].com_val & 0xf0)
      {
        case 0x00:                                     /*com o0*/
          com_misccrtl();
          break;
        case 0x10:                                     /*com o1*/
          cnc_vs_basic();
          break;
        case 0x30:                                     /*com o3*/
          com_arp_speed_set();
          break;
        case 0x40:                                     /*com o4*/
          funk_chan[c_channel].note_command = funk_chan[c_channel].command;
          funk_chan[c_channel].note_com_val = funk_chan[c_channel].com_val;
          com_fine_port_up();
          break;
        case 0x50:                                     /*com o5*/
          funk_chan[c_channel].note_command = funk_chan[c_channel].command;
          funk_chan[c_channel].note_com_val = funk_chan[c_channel].com_val;
          com_fine_port_dn();
          break;
        case 0x60:                                     /*com o6*/
          funk_chan[c_channel].volume_command = funk_chan[c_channel].command;
          funk_chan[c_channel].volume_com_val = funk_chan[c_channel].com_val;
          com_vol_up();
          break;
        case 0x70:                                     /*com o7*/
          funk_chan[c_channel].volume_command = funk_chan[c_channel].command;
          funk_chan[c_channel].volume_com_val = funk_chan[c_channel].com_val;
          com_vol_dn();
          break;
        case 0x80:                                     /*com o8*/
          funk_chan[c_channel].volume_command = funk_chan[c_channel].command;
          funk_chan[c_channel].volume_com_val = funk_chan[c_channel].com_val;
          funk_chan[c_channel].volume_comspd_count = 0;
          funk_chan[c_channel].volume_beat_count = 0;
          funk_chan[c_channel].volume_portdest = funk_chan[c_channel].volume;
          break;
        case 0x90:                                     /*com o9*/
          com_echo_feedback();
          break;
        case 0xa0:                                     /*com oa*/
          com_master_set();
          break;
        case 0xb0:                                     /*com ob*/
          com_echo_delay();
          break;
        case 0xc0:                                     /*com oc*/
          com_echo_decay();
          break;
        case 0xe0:                                     /*com oe*/
          com_balance();
          break;
        case 0xf0:                                     /*com of*/
          com_tempo();
          break;
      }
      break;
  }
}

/***************************************************************************
*
* Command control for full slots
*
***************************************************************************/
void cfc_vs_basic(void)
{
  funk_chan[c_channel].volume_command = funk_chan[c_channel].command;
  funk_chan[c_channel].volume_com_val = funk_chan[c_channel].com_val;
  funk_chan[c_channel].volume_comspd_count = 0;
  normal_decode_volume();
}

void com_full_ctrl(void)
{
  switch(funk_chan[c_channel].command)
  {
    case 0x0:                                          /*com a*/
      funk_chan[c_channel].note_command = funk_chan[c_channel].command;
      funk_chan[c_channel].note_com_val = funk_chan[c_channel].com_val;
      normal_decode_note();
      break;
    case 0x1:                                          /*com b*/
      funk_chan[c_channel].note_command = funk_chan[c_channel].command;
      funk_chan[c_channel].note_com_val = funk_chan[c_channel].com_val;
      normal_decode_note();
      break;
    case 0x2:                                          /*com c*/
      funk_chan[c_channel].note_command = funk_chan[c_channel].command;
      funk_chan[c_channel].note_com_val = funk_chan[c_channel].com_val;
      comc_decode();
      break;
    case 0x3:                                          /*com d*/
      funk_chan[c_channel].note_command = funk_chan[c_channel].command;
      funk_chan[c_channel].note_com_val = funk_chan[c_channel].com_val;
      funk_chan[c_channel].note_comspd_count = 0;
      normal_decode_note();
      break;
    case 0x4:                                          /*com e*/
      funk_chan[c_channel].note_beat_count = 0;
      goto set_vib_fan;
    case 0x5:                                          /*com f*/
      funk_chan[c_channel].note_beat_count = 0xf;
set_vib_fan:
      if(funk_chan[c_channel].note_command == 0x3) /*if vibrato command*/
        funk_chan[c_channel].note_beat_count =
          funk_chan[c_channel].note_com_val >> 4;
      funk_chan[c_channel].note_command = funk_chan[c_channel].command;
      funk_chan[c_channel].note_com_val = funk_chan[c_channel].com_val;
      funk_chan[c_channel].note_comspd_count = 0;
      normal_decode_note();
      break;
    case 0x6:                                          /*com g*/
      cfc_vs_basic();
      break;
    case 0x7:                                          /*com h*/
      cfc_vs_basic();
      break;
    case 0x8:                                          /*com i*/
      funk_chan[c_channel].volume_command = funk_chan[c_channel].command;
      funk_chan[c_channel].volume_com_val = funk_chan[c_channel].com_val;
      funk_chan[c_channel].volume_comspd_count = 0;
      comi_decode();
      break;
    case 0x9:                                          /*com j*/
      funk_chan[c_channel].volume_command = funk_chan[c_channel].command;
      funk_chan[c_channel].volume_com_val = funk_chan[c_channel].com_val;
      funk_chan[c_channel].volume_comspd_count = 0;
      normal_decode_volume();
      funk_chan[c_channel].volume_beat_count = funk_chan[c_channel].volume;
      break;
    case 0xa:                                          /*com k*/
      cfc_vs_basic();
      break;
    case 0xb:                                          /*com l*/
      funk_chan[c_channel].note_command = funk_chan[c_channel].command;
      funk_chan[c_channel].note_com_val = funk_chan[c_channel].com_val;
      funk_chan[c_channel].note_comspd_count = 0;
      funk_chan[c_channel].note_beat_count = 1;
      normal_decode_note();
      break;
    case 0xc:                                          /*com m*/
      normal_decode_system();
      com_sample_offset();
      break;
    case 0xd:                                          /*com n*/
      funk_chan[c_channel].volume_command = funk_chan[c_channel].command;
      funk_chan[c_channel].volume_com_val = funk_chan[c_channel].com_val;
      normal_decode_volume();
      com_volume();
      break;
    case 0xe:                                          /*O commands*/
      switch(funk_chan[c_channel].com_val & 0xf0)
      {
        case 0x00:                                     /*com o0*/
          normal_decode_system();
          com_misccrtl();
          break;
        case 0x10:                                     /*com o1*/
          cfc_vs_basic();
          break;
        case 0x20:                                     /*com o2*/
          funk_chan[c_channel].comspd_count = 0;
          funk_chan[c_channel].delay_pattern_ptr = pattern_ptr;
          break;
        case 0x30:                                     /*com o3*/
          normal_decode_system();
          com_arp_speed_set();
          break;
        case 0x40:                                     /*com o4*/
          funk_chan[c_channel].note_command = funk_chan[c_channel].command;
          funk_chan[c_channel].note_com_val = funk_chan[c_channel].com_val;
          normal_decode_note();
          com_fine_port_up();
          break;
        case 0x50:                                     /*com o5*/
          funk_chan[c_channel].note_command = funk_chan[c_channel].command;
          funk_chan[c_channel].note_com_val = funk_chan[c_channel].com_val;
          normal_decode_note();
          com_fine_port_dn();
          break;
        case 0x60:                                     /*com o6*/
          funk_chan[c_channel].volume_command = funk_chan[c_channel].command;
          funk_chan[c_channel].volume_com_val = funk_chan[c_channel].com_val;
          normal_decode_volume();
          com_vol_up();
          break;
        case 0x70:                                     /*com o7*/
          funk_chan[c_channel].volume_command = funk_chan[c_channel].command;
          funk_chan[c_channel].volume_com_val = funk_chan[c_channel].com_val;
          normal_decode_volume();
          com_vol_dn();
          break;
        case 0x80:                                     /*com o8*/
          funk_chan[c_channel].volume_command = funk_chan[c_channel].command;
          funk_chan[c_channel].volume_com_val = funk_chan[c_channel].com_val;
          funk_chan[c_channel].volume_comspd_count = 0;
          funk_chan[c_channel].volume_beat_count = 0;
          normal_decode_volume();
          funk_chan[c_channel].volume_portdest = funk_chan[c_channel].volume;
          break;
        case 0x90:                                     /*com o9*/
          normal_decode_system();
          com_echo_feedback();
          break;
        case 0xa0:                                     /*com oa*/
          com_master_set();
          normal_decode_system();
          break;
        case 0xb0:                                     /*com ob*/
          normal_decode_system();
          com_echo_delay();
          break;
        case 0xc0:                                     /*com oc*/
          normal_decode_system();
          com_echo_decay();
          break;
        case 0xd0:                                     /*com od*/
          funk_chan[c_channel].retrig_count = 1;
          funk_chan[c_channel].retrig_spd_count = 0;
          normal_decode_system();
          break;
        case 0xe0:                                     /*com oe*/
          normal_decode_system();
          com_balance();
          break;
        case 0xf0:                                     /*com of*/
          normal_decode_system();
          com_tempo();
          break;
      }
      break;
    case 0xf:
      normal_decode_system();
      break;
  }
}

/***************************************************************************
*
* Command control for rapid "on the fly"
* Rapid commands that have a speed parameter
*
***************************************************************************/
void com_rapid_ctrl(void)
{
  for(c_channel = 0;c_channel < no_active_channels;c_channel++)
  {
    vol_sys_rapid_p1();
    switch(funk_chan[c_channel].note_command)
    {
      case 0x0:
        if(funk_chan[c_channel].port_type)
          com_port_up_lin(funk_chan[c_channel].note_com_val);
        else
          com_port_up_log(funk_chan[c_channel].note_com_val);
        break;
      case 0x1:
        if(funk_chan[c_channel].port_type)
          com_port_dn_lin(funk_chan[c_channel].note_com_val);
        else
          com_port_dn_log(funk_chan[c_channel].note_com_val);
        break;
      case 0x2:
        if(funk_chan[c_channel].port_type)
          com_porta_lin();
        else
          com_porta_log();
        break;
      case 0x3:                                        /*command d*/
        com_vibrato();
        break;
      case 0x4:                                        /*command e*/
        com_vib_fanin();
        break;
      case 0x5:                                        /*command f*/
        com_vib_fanout();
        break;
      case 0xb:                                        /*command l*/
        com_arpeggio();
        break;
    }
    switch(funk_chan[c_channel].volume_command)
    {
      case 0x9:                                        /*command j*/
        com_reverb();
        break;
      case 0xa:                                        /*command k*/
        com_tremola();
        break;
      case 0xe:
        switch(funk_chan[c_channel].volume_com_val)
        {
          case 0x0e:
            com_pan_left();
            break;
          case 0x0f:
            com_pan_right();
            break;
        }
        switch(funk_chan[c_channel].volume_com_val & 0xf0)
        {
          case 0x10:                                   /*command o1*/
            com_volume_cut();
            break;
          case 0x80:                                   /*command o8*/
            com_vol_crest();
            break;
        }
        break;
    }
    if(funk_chan[c_channel].command == 0xe)
      switch(funk_chan[c_channel].com_val & 0xf0)
      {
        case 0x20:                                    /*command o2*/
          com_note_delay();
          break;
        case 0xd0:                                    /*command od*/
          com_note_retrig();
          break;
      }
  }
}

/***************************************************************************
*
* ESI = current pattern
*
* format:
*
* 00000000 11111111 22222222
* \    /\     /\  / \      /
*  note  sample com  command value
*
* - if note:  = 3D, then reload sample attrs
*             = 3F, then it's a null slot
*             = 3E, then sample only slot
*
***************************************************************************/
void trekk_slot(int pattern_no)
{
  register tslot *pattern_addr =
    funk_pat_ptr + (pattern_no * 64 * no_active_channels) +
    (funk_info.pattern_ofs * no_active_channels);
  for(c_channel = 0;c_channel < no_active_channels;c_channel++)
  {
    pattern_ptr = pattern_addr + c_channel;
    funk_chan[c_channel].command = pattern_ptr->sam_com & 0xf;
    funk_chan[c_channel].com_val = pattern_ptr->com_val;
    if((pattern_ptr->not_sam >> 2) == 0x3f)
      com_null_ctrl();
    else
      com_full_ctrl();                  /*IF FULL SLOT or SAMPLE ONLY SLOT*/
  }
}

/***************************************************************************
****************************************************************************
****************************************************************************
****************************************************************************
*
* The actual tracker thingy :) ....0.02 second tick (or roughly)
*
****************************************************************************
****************************************************************************
****************************************************************************
***************************************************************************/
void funk_tracker(void)
{
  if(funk_info.trek_status == PLAY)
  {
    com_rapid_ctrl();
    if(funk_info.tempo_count < funk_info.tempo)
      funk_info.tempo_count++;
    else
    {
      funk_info.tempo_count = 0;
      funk_info.pattern_ofs_display = funk_info.pattern_ofs;
      funk_info.sequence_ofs_display = funk_info.sequence_ofs;
      trekk_slot(funk_hr_ptr->order_list[funk_info.sequence_ofs]);
      if(funk_info.pattern_ofs ==
        funk_hr_ptr->break_list[funk_hr_ptr->order_list[funk_info.sequence_ofs]])
      {
        if(funk_info.sequence_ofs < funk_info.no_of_sequences)
        {
          funk_info.sequence_ofs++;
          funk_info.pattern_ofs = 0;
        }
        else
        {
          if(funk_hr_ptr->loop_order == 0xFF)
            funk_info.trek_status = STOP;
          else
          {
            funk_info.sequence_ofs = funk_hr_ptr->loop_order;
            funk_info.pattern_ofs = 0;
          }
        }
      }
      else
        funk_info.pattern_ofs++;
    }
  }
}

#pragma pack() /*kill byte alignment*/
