/*
 *  Copyright (c) by Jaroslav Kysela (Perex soft)
 */

#include <string.h>
#include "pmod.h"
#ifdef USE_FLOATING_POINT
#include <math.h>
#endif

short sineOscTbl[][ 64 ] = { 
{
  0, 25, 50, 74, 98, 120, 142, 162, 180, 197, 
  212, 225, 236, 244, 250, 254, 255, 250, 244, 236, 
  225, 212, 197, 180, 162, 142, 120, 98, 74, 50, 
  25, 0,
  0, -25, -50, -74, -98, -120, -142, -162, -180, -197, 
  -212, -225, -236, -244, -250, -254, -255, -250, -244, -236, 
  -225, -212, -197, -180, -162, -142, -120, -98, -74, -50, 
  -25, 0
},
{
  0, 8, 16, 24, 32, 40, 48, 56, 64, 72, 
  80, 88, 96, 104, 112, 120, 128, 136, 144, 152, 
  160, 168, 176, 184, 192, 200, 208, 216, 224, 232, 
  240, 248,
  -255, -248, -240, -232, -224, -216, -208, -200, -192, -184, 
  -176, -168, -160, -152, -144, -136, -128, -120, -112, -104, 
  -96, -88, -80, -72, -64, -56, -48, -40, -32, -24, 
  -16, -8
},
{
  255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 
  255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 
  255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 
  255, 255,
  -255, -255, -255, -255, -255, -255, -255, -255, -255, -255,
  -255, -255, -255, -255, -255, -255, -255, -255, -255, -255,
  -255, -255, -255, -255, -255, -255, -255, -255, -255, -255,
  -255, -255
},
{
  80, 88, 96, 104, 112, 120, 128, 136, 144, 152, 
  0, 8, 16, 24, 32, 40, 48, 56, 64, 72, 
  240, 248,
  160, 168, 176, 184, 192, 200, 208, 216, 224, 232, 
  -96, -88, -80, -72, -64, -56, -48, -40, -32, -24, 
  -255, -248, -240, -232, -224, -216, -208, -200, -192, -184, 
  -16, -8
  -176, -168, -160, -152, -144, -136, -128, -120, -112, -104, 
},
};

void initVoices()
{
  int i;

  for ( i = 0; i < GUS_CHANNELS; i++ )
    {
      memset( &voices[ i ], 0, sizeof( struct VOICE ) );

      voices[ i ].instr = voices[ i ].sample = -1;

      voices[ i ].note = -1;
      voices[ i ].cmd[ 0 ] = voices[ i ].cmd[ 1 ] = -1;

      voices[ i ].slideToNoteArg = 255;
      voices[ i ].eVibPos = -1;
    }
}

static int oscilate( short first, byte *pos, word amplitude, byte speed, byte parm )
{
#ifdef 0

  if ( !first ) *pos += speed;
  parm &= 3;
#ifdef USE_FLOATING_POINT
  return rint( (double)( (int)sineOscTbl[ parm ][ ( *pos >> 2 ) & 0x3f ] * amplitude ) / (double)( 256.0 ) );
#else
  return ( (int)sineOscTbl[ parm ][ ( *pos >> 2 ) & 0x3f ] * (int)amplitude ) / 256;
#endif 

#else

  int temp;
  
  if ( !first ) *pos += speed;
  temp = sineOscTbl[ parm & 3 ][ *pos & 0x3f ];
  temp *= (int)amplitude;
  if ( songOptions & SNDO_VIBRATO )
    temp >>= 7;
   else
    temp >>= 8;
  return temp;

#endif
}

void doEffects( short what )
{
  int cmd;
  short arg;
  int i, w;
  
  for ( i = 0; i < songChannels; i++ )
    for ( w = 0; w < 2; w++ )
      {
        cmd = voices[ i ].cmd[ w ];
        arg = voices[ i ].arg[ w ];    
    
        switch ( cmd ) {
          case -1: break;
          case EFF_ARPEG:
            if ( !what ) break;
            if ( !(--arg) && voices[ i ].arpegArg )
              {
                arg = voices[ i ].arpegPos2;
                if ( arg )		/* 1/3 */
                  {
                    voices[ i ].arpegPos2 = 0;
                    
                    SET_NOTE( i, voices[ i ].arpegOldNote + 
                  	       	     ( ( voices[ i ].arpegArg >> 4 ) & 0x0f ) );
                    SET_NOTE_PERIOD( i );
                  }
                 else			/* 2/3 */
                  {
                    SET_NOTE( i, voices[ i ].arpegOldNote +
                   		     ( voices[ i ].arpegArg & 0x0f ) );
                    SET_NOTE_PERIOD( i );
                  }
              }
            break;
          case EFF_SLIDE_UP:
            {
              int tmp;

              if ( !what )
                {
                  if ( voices[ i ].slideUpFirst )
                    {
                      voices[ i ].slideUpFirst = 0;
                      break;
                    }
                   else
                    cmd = -1;
                }
              tmp = voices[ i ].basePeriod - voices[ i ].slideUpArg;
              if ( tmp < MIN_PERIOD ) tmp = MIN_PERIOD;
              SET_BPERIOD( i, tmp );
            }
            break;
          case EFF_SLIDE_DOWN:
            {
              int tmp;
            
              if ( !what )
                {
                  if ( voices[ i ].slideDownFirst )
                    {
                      voices[ i ].slideDownFirst = 0;
                      break;
                    }
                   else
                    cmd = -1;
                }
              tmp = voices[ i ].basePeriod + voices[ i ].slideDownArg;
	      if ( tmp > MAX_PERIOD ) tmp = MAX_PERIOD;
	      SET_BPERIOD( i, tmp );
            }
            break;
          case EFF_SLIDE_TO:
          case EFF_PORT_AND_VOL:
            if ( voices[ i ].glissando == 0 )
              {
                if ( voices[ i ].basePeriod > voices[ i ].slideToPeriod )
                  { 
                    voices[ i ].basePeriod -= voices[ i ].slideToNoteArg;
                    if ( voices[ i ].basePeriod <= voices[ i ].slideToPeriod )
                      {
                        SET_NOTE( i, voices[ i ].slideToNote );
                        voices[ i ].basePeriod = voices[ i ].slideToPeriod;
                        cmd = -1;
                      }
                  }
                 else
                  {
                    voices[ i ].basePeriod += voices[ i ].slideToNoteArg;
                    if ( voices[ i ].basePeriod >= voices[ i ].slideToPeriod )
                      {
                        SET_NOTE( i, voices[ i ].slideToNote );
                        voices[ i ].basePeriod = voices[ i ].slideToPeriod;
                        cmd = -1;
                      }
                  }
	        SET_BPERIOD1( i );
              }
             else
              {
                if ( voices[ i ].note > voices[ i ].slideToNote )
                  {
                    voices[ i ].note -= voices[ i ].slideToNoteArg;
                    if ( voices[ i ].note <= voices[ i ].slideToNote )
                      {
                        voices[ i ].note = voices[ i ].slideToNote;
                        cmd = -1;
                      }
                  }
                 else
                  {
                    voices[ i ].note += voices[ i ].slideToNoteArg;
                    if ( voices[ i ].note >= voices[ i ].slideToNote )
                      {
                        voices[ i ].note = voices[ i ].slideToNote;
                        cmd = -1;
                      }
                  }
                SET_NOTE( i, voices[ i ].note );
                SET_NOTE_PERIOD( i );
              }
	    if ( cmd == EFF_PORT_AND_VOL )
	      {
                if ( ( voices[ i ].volSlideArg & 0xf0 ) != 0 )
                  voices[ i ].volume += ( voices[ i ].volSlideArg >> 4 ) & 0x0f;
                 else
                  voices[ i ].volume -= voices[ i ].volSlideArg & 0x0f;
                SET_VOLUME( i );
	      }
            break;
          case EFF_VIBRATO:
          case EFF_VIBRA_AND_VOL:
            {
              int tmp;
            
              tmp = oscilate( !what && voices[ i ].vibratoFirst,
                              &voices[ i ].vibratoPek,
                              ( voices[ i ].vibratoArg & 0x0f ) << 2,
                              voices[ i ].vibratoArg >> 4,
                              voices[ i ].vibratoWave );
#ifdef 0
              dprintf( "channel: %i, tmp: %i\n", i, tmp );
#endif
	      voices[ i ].vibratoFirst = 0;
              tmp += voices[ i ].basePeriod;
              if ( tmp > MAX_PERIOD ) tmp = MAX_PERIOD;
              if ( tmp < MIN_PERIOD ) tmp = MIN_PERIOD;
              SET_PERIOD( i, tmp );
	      if ( cmd == EFF_VIBRA_AND_VOL )
	        {
                  if ( ( voices[ i ].volSlideArg & 0xf0 ) != 0 )
                    voices[ i ].volume += ( voices[ i ].volSlideArg >> 4 ) & 0x0f;
                   else
                    voices[ i ].volume -= voices[ i ].volSlideArg & 0x0f;
                  SET_VOLUME( i );
	        }
            }
            break;
          case EFF_FINE_VIBRATO:
            {
              int tmp;
            
              tmp = oscilate( !what && voices[ i ].vibratoFirst,
                              &voices[ i ].vibratoPek,          		    
                              voices[ i ].fineVibratoArg & 0x0f,
            		      voices[ i ].fineVibratoArg >> 4,
            		      voices[ i ].vibratoWave );
              tmp += voices[ i ].basePeriod;
              if ( tmp > MAX_PERIOD ) tmp = MAX_PERIOD;
              if ( tmp < MIN_PERIOD ) tmp = MIN_PERIOD;
              SET_PERIOD( i, tmp );
            }
            break;
          case EFF_TREMOLO:
            {
              short oldVol = voices[ i ].volume;
           
              voices[ i ].volume +=
                  oscilate( !what && voices[ i ].tremoloFirst,
                            &voices[ i ].tremoloPek,
                            ( voices[ i ].tremoloArg & 0x0f ) << 1,
                            voices[ i ].tremoloArg >> 4,
                            voices[ i ].tremoloWave );
#ifdef 0
              dprintf( "tremolo: %i\n", voices[ i ].volume - oldVol );
#endif
              SET_VOLUME( i );
              voices[ i ].volume = oldVol;
            }
            break; 
          case EFF_TREMOR:
            if ( !what && voices[ i ].tremorFirst )
              {
                voices[ i ].tremorFirst = 0;
                break;
              }
            if ( --voices[ i ].tremor == 0 )
              {
                if ( voices[ i ].volume == 0 )	/* off -> turn note on */
                  {
                    voices[ i ].tremor = voices[ i ].tremorArg >> 4;
                    SET_VOLUME( i );
                  }
                 else
                  {
                    short oldVol;
                  
                    voices[ i ].tremor = voices[ i ].tremorArg & 0x0f;
                    oldVol = voices[ i ].volume;
                    voices[ i ].volume = 0;
                    SET_VOLUME( i );
                    voices[ i ].volume = oldVol;
                  }
              }
            break;
          case EFF_VOL_SLIDE:
            if ( ( voices[ i ].volSlideArg & 0xf0 ) != 0 )
              voices[ i ].volume += ( voices[ i ].volSlideArg >> 4 ) & 0x0f;
             else
              voices[ i ].volume -= voices[ i ].volSlideArg & 0x0f;
            SET_VOLUME( i );
            break;
	  case EFF_DELAY_NOTE:
          case EFF_RETRIGGER:
          case EFF_MRETRIGGER:
#if 1
            if ( !what )
              {
                if ( cmd == EFF_DELAY_NOTE && voices[ i ].delayNoteFirst )
                  {
                    voices[ i ].delayNoteFirst = 0;
                    break;
                  }
                 else
                if ( ( cmd == EFF_RETRIGGER || cmd == EFF_MRETRIGGER ) &&
                     voices[ i ].retrigFirst )
                  {
                    voices[ i ].retrigFirst = 0;
                    break;
                  }
              }
#endif
            if ( !(--arg) )
              {
	        SET_NOTE_PERIOD( i );
                START_NOTE( i );
                switch ( voices[ i ].retrigArg >> 4 ) {
                  case 1: voices[ i ].volume--; break;
                  case 2: voices[ i ].volume -= 2; break;
                  case 3: voices[ i ].volume -= 4; break;
                  case 4: voices[ i ].volume -= 8; break;
                  case 5: voices[ i ].volume -= 16; break;
                  case 6: voices[ i ].volume = ( voices[ i ].volume * 2 ) / 3; break;
                  case 7: voices[ i ].volume >>= 1; break;
                  case 9: voices[ i ].volume++; break;
                  case 10: voices[ i ].volume += 2; break;
                  case 11: voices[ i ].volume += 4; break;
                  case 12: voices[ i ].volume += 8; break;
                  case 13: voices[ i ].volume += 16; break;
                  case 14: voices[ i ].volume = ( voices[ i ].volume * 3 ) / 2; break;
                  case 15: voices[ i ].volume <<= 1; break;
                }
                SET_VOLUME( i );
	        if ( cmd != EFF_MRETRIGGER )
	          {
	            cmd = -1;
	            arg = voices[ i ].retrigArg & 0x0f;
	          }
              }
            break;
          case EFF_CUT_NOTE:
            if ( !what && voices[ i ].cutNoteFirst )
              {
                voices[ i ].cutNoteFirst = 0;
                break;
              }
            if ( !(--arg) ) STOP_NOTE( i );
            break;
          case EFF_PAN_SLIDE:
            if ( ( voices[ i ].panSlideArg & 0xf0 ) != 0 )
              voices[ i ].pan += ( voices[ i ].panSlideArg >> 4 ) & 0x0f;
             else
              voices[ i ].pan -= voices[ i ].panSlideArg & 0x0f;
            if ( voices[ i ].pan < 0 ) voices[ i ].pan = 0;
            if ( voices[ i ].pan > 255 ) voices[ i ].pan = 255;
            voices[ i ].what_is_new = WIN_PAN;
            break;
          case EFF_GLOBAL_VOL_SLIDE:
            if ( ( voices[ i ].volSlideArg & 0xf0 ) != 0 )
              songGlobalVolume += ( voices[ i ].volSlideArg >> 4 ) & 0x0f;
             else
              songGlobalVolume -= voices[ i ].volSlideArg & 0x0f;
            if ( songGlobalVolume < 0 ) songGlobalVolume = 0;
            if ( songGlobalVolume > 128 ) songGlobalVolume = 128;
            {
              short tmp;
              
              for ( tmp = 0; tmp < songChannels; tmp++ )
                SET_VOLUME( tmp );
            }
            break;
            
          default:
            fatal( "doEffect: bad command" );
        }
        
        voices[ i ].cmd[ w ] = cmd;
        voices[ i ].arg[ w ] = arg;
      }
}

void playEffectOnly( byte what, byte channel, byte instr, byte effect, byte arg )
{
  word tmp;
  int cmdx   = voices[ channel ].cmd[ what ];
  short argx = voices[ channel ].arg[ what ];

#if 0
  if ( effect != EFF_NONE && ( disabledChannels & ( 1 << channel ) ) == 0 )
    dprintf( "channel: %i, effect: 0x%x, arg: 0x%x\n", channel, effect, arg );
#endif

  if ( effect != EFF_SLIDE_TO && effect != EFF_PORT_AND_VOL &&
       ( cmdx == EFF_SLIDE_TO || cmdx == EFF_PORT_AND_VOL ) )
    {
      SET_NOTE( channel, voices[ channel ].slideToNote );
      SET_PERIOD( channel, voices[ channel ].basePeriod = voices[ channel ].slideToPeriod );
    }

  switch ( effect ) {

    case EFF_NONE:
      break;

    case EFF_VOLUME:
      voices[ channel ].volume = arg;
      SET_VOLUME( channel );
      break;
    case EFF_VOL_SLIDE:
      cmdx = EFF_VOL_SLIDE;
      if ( arg ) voices[ channel ].volSlideArg = arg;
      break;
    case EFF_FINE_VOL_UP:
      voices[ channel ].volume += arg;
      SET_VOLUME( channel );
      break;
    case EFF_FINE_VOL_DOWN:
      voices[ channel ].volume -= arg;
      SET_VOLUME( channel );
      break;
    case EFF_TREMOLO_WAVE:
      voices[ channel ].tremoloWave = arg & 7;
      break;
    case EFF_TREMOLO:
      cmdx = EFF_TREMOLO;
      if ( arg != 0 )
        {
          if ( !( voices[ channel ].tremoloWave & 4 ) )
            {
              voices[ channel ].tremoloPek = 0;
              voices[ channel ].tremoloFirst = 1;
            }
          voices[ channel ].tremoloArg = arg;
        }
      break;
    case EFF_TREMOR:
      cmdx = EFF_TREMOR;
      if ( arg != 0 )
        {
          voices[ channel ].tremorArg = arg;
          voices[ channel ].tremor = ( arg >> 4 ) + 1;
        }
       else
        voices[ channel ].tremor = ( voices[ channel ].tremorArg >> 4 ) + 1;
      voices[ channel ].tremorFirst = 1;
      break;
    case EFF_GLOBAL_VOL:
      tmp = arg << 1;
      if ( tmp > 128 ) tmp = 128;
      if ( tmp < 1 ) tmp = 1;
      songGlobalVolume = tmp;
      for ( tmp = 0; tmp < songChannels; tmp++ )
        SET_VOLUME( tmp );
      break;
    case EFF_GLOBAL_VOL_SLIDE:
      cmdx = EFF_GLOBAL_VOL_SLIDE;
      if ( arg ) voices[ channel ].gvolSlideArg = arg;
      break;

    case EFF_ARPEG:
      if ( songPlaySpeed >= 3 )
        {
          cmdx = EFF_ARPEG;
          argx = songPlaySpeed / 3;
          voices[ channel ].arpegArg = arg;
          voices[ channel ].arpegPos2 = ( songPlaySpeed - argx ) >> 1;
          voices[ channel ].arpegOldNote = voices[ channel ].note;
        }
      break;
    case EFF_VIBRATO_WAVE:
      voices[ channel ].vibratoWave = arg & 7;
      break;
    case EFF_VIBRATO:
      cmdx = EFF_VIBRATO;
      if ( arg != 0 )
        {
          if ( ( arg & 0xf0 ) == 0 )
            arg |= voices[ channel ].vibratoArg & 0xf0;
          if ( ( arg & 0x0f ) == 0 )
            arg |= voices[ channel ].vibratoArg & 0x0f;
          if ( !(voices[ channel ].vibratoWave & 4) )
            {
              voices[ channel ].vibratoPek = 0;
              voices[ channel ].vibratoFirst = 1;
            }
          voices[ channel ].vibratoArg = arg;
        }
      break;
    case EFF_FINE_VIBRATO:
      cmdx = EFF_FINE_VIBRATO;
      if ( arg != 0 )
        {
          if ( !(voices[ channel ].vibratoWave & 4) )
            {
              voices[ channel ].vibratoPek = 0;
              voices[ channel ].vibratoFirst = 1;
            }
          voices[ channel ].fineVibratoArg = arg;
        }
      break;
    case EFF_SLIDE_UP:
      voices[ channel ].slideUpFirst = 1;
      if ( cmdx == EFF_SLIDE_UP )
        {
          voices[ channel ].basePeriod -= (word)arg << 2;
          if ( voices[ channel ].basePeriod < MIN_PERIOD )
            voices[ channel ].basePeriod = MIN_PERIOD;
          SET_BPERIOD1( channel );
        }
      cmdx = EFF_SLIDE_UP;
      if ( arg ) voices[ channel ].slideUpArg = (word)arg << 2;
      break;
    case EFF_SLIDE_DOWN:
      voices[ channel ].slideDownFirst = 1;
      if ( cmdx == EFF_SLIDE_DOWN )
        {
          voices[ channel ].basePeriod += (word)arg << 2;
          if ( voices[ channel ].basePeriod > MAX_PERIOD )
            voices[ channel ].basePeriod = MAX_PERIOD;
          SET_BPERIOD1( channel );
        }
      cmdx = EFF_SLIDE_DOWN;
      if ( arg ) voices[ channel ].slideDownArg = (word)arg << 2;
      break;
    case EFF_FINE_SLIDE_UP:
    case EFF_EXTRA_SLIDE_UP:
      tmp = arg;
      if ( !tmp ) tmp = voices[ channel ].extraSlideUpArg; else
                  voices[ channel ].extraSlideUpArg = tmp;
      if ( effect == EFF_FINE_SLIDE_UP ) tmp <<= 2;
      voices[ channel ].basePeriod -= tmp;
      if ( voices[ channel ].basePeriod < MIN_PERIOD )
        voices[ channel ].basePeriod = MIN_PERIOD;
      SET_BPERIOD1( channel );
      break;
    case EFF_FINE_SLIDE_DOWN:
    case EFF_EXTRA_SLIDE_DOWN:
      tmp = arg;
      if ( !tmp ) tmp = voices[ channel ].extraSlideDownArg; else
                  voices[ channel ].extraSlideDownArg = arg;
      if ( effect == EFF_FINE_SLIDE_DOWN ) tmp <<= 2;
      voices[ channel ].basePeriod += tmp;
      if ( voices[ channel ].basePeriod > MAX_PERIOD )
        voices[ channel ].basePeriod = MAX_PERIOD;
      SET_BPERIOD1( channel );
      break;
    case EFF_GLISSANDO:
      voices[ channel ].glissando = arg & 1;
      break;
    case EFF_SLIDE_TO:
      break;
    case EFF_FINETUNE:
      songInstrs[ voices[ channel ].instr ] -> samples[ voices[ channel ].sample ] -> finetune = 
        finetuneTable[ arg & 0x0f ];
      SET_BPERIOD1( channel );
      break;

    case EFF_PORT_AND_VOL:
      cmdx = EFF_PORT_AND_VOL;
      if ( arg ) voices[ channel ].volSlideArg = arg;
      break;
    case EFF_VIBRA_AND_VOL:
      cmdx = EFF_VIBRA_AND_VOL;
      if ( arg ) voices[ channel ].volSlideArg = arg;
      break;

    case EFF_MOD_SPEED:
      if ( arg < 32 )
        {
          if ( arg > 0 )
            songPlaySpeed = arg;
        }
       else
        {
          int tmp = CALC_BPM( arg );

          GUS_DO_SET_TIMER( tmp );
        }
      break;
    case EFF_SPEED:
      if ( arg > 0 )
        songPlaySpeed = arg;
      break;
    case EFF_TEMPO:
      {
        int tmp = CALC_BPM( arg );

        GUS_DO_SET_TIMER( tmp );
      }
      break;

    case EFF_RETRIGGER:
    case EFF_MRETRIGGER:
      cmdx = EFF_RETRIGGER;
      argx = arg & 0x0f;
      if ( argx == 0 ) arg = voices[ channel ].retrigArg & 0x0f;
      voices[ channel ].retrigArg = arg;
      voices[ channel ].retrigFirst = 1;
      break;
    case EFF_SET_OFFSET:
    case EFF_MOD_SET_OFFSET:
      {
        dword x;
        
        x = (dword)arg << ( 8 + ( effect == EFF_MOD_SET_OFFSET ) );
#if 0
        if ( songInstrs[ voices[ channel ].instr ].
                 samples[ voices[ channel ].sample ].type & SMP_TYPE_16BIT )
          x <<= 1;
#endif
        
        if ( x > 0 ) x -= 2;
        if ( songInstrs[ voices[ channel ].instr ] -> 
        	samples[ voices[ channel ].sample ] -> length <= x )
          break;
        voices[ channel ].gusOffset = x;
        voices[ channel ].what_is_new |= WIN_OFFSET;
      }
      break;
    case EFF_CUT_NOTE:
      cmdx = EFF_CUT_NOTE;
      argx = arg;
      voices[ channel ].cutNoteFirst = 1;
      break;
    case EFF_DELAY_NOTE:
      voices[ channel ].delayNoteFirst = 1;
      break;
    case EFF_KEY_OFF:
      keyoff( channel );
      break;

    case EFF_FILTER:		/* GUS have no filter */
      break;
    case EFF_SET_PAN:
    case EFF_SET_PAN_DMP:
    case EFF_SET_PAN_255:
#if 0
      dprintf( "pan = 0x%x, arg = 0x%x\n", effect, arg );
#endif
      switch ( effect ) {
        case EFF_SET_PAN:
          arg &= 0x0f;
          arg <<= 4;
          break;
        case EFF_SET_PAN_DMP:
          if ( arg >= 0x80 ) arg = 0x7f;
          arg <<= 1;
          break;
      }
#if 0
      dprintf( "opan = 0x%x, arg = 0x%x\n", effect, arg );
#endif
      voices[ channel ].pan = arg;
      voices[ channel ].what_is_new |= WIN_PAN;
      break;
    case EFF_PAN_SLIDE:
      cmdx = EFF_PAN_SLIDE;
      if ( arg ) voices[ channel ].panSlideArg = arg;
      break;
    case EFF_PATTERN_DELAY:
      delayPattern = arg;
      break;
    case EFF_ENVELOPE_POS:
      voices[ channel ].eVolPos = arg;
      voices[ channel ].ePanPos = arg;
      break;

    default:
      if ( effect != 0 || arg != 0 )
        dprintf( "effects: uknown effect - %02x/%02x\n", effect, arg );
  }

  voices[ channel ].cmd[ what ] = cmdx;
  voices[ channel ].arg[ what ] = argx;
}

void playEffect( byte what, byte channel, byte instr, byte volume, byte effect, byte arg )
{
  short i, j;

#ifdef 0
  if ( effect != EFF_NONE && ( disabledChannels & ( 1 << channel ) ) == 0 )
    dprintf( "playEffect: ch %02i, eff %02x, arg %02x\n", channel, effect, arg );
#endif

  if ( volume != 255 )
    {
      voices[ channel ].volume = volume;
      SET_VOLUME( channel );
    }
   else
  if ( instr != 255 && ( voices[ channel ].what_is_new & WIN_NOTE_START ) &&
       ( i = voices[ channel ].instr ) >= 0 && songInstrs[ i ] &&
       ( j = voices[ channel ].sample ) >= 0 && songInstrs[ i ] -> samples[ j ] )
    {
      voices[ channel ].volume = songInstrs[ i ] -> samples[ j ] -> volume;
      SET_VOLUME( channel );
    }
  playEffectOnly( what, channel, instr, effect, arg );
}

void hiddenPlayEffect( byte what, byte channel, byte effect, byte arg )
{
  word tmp;

  switch ( effect ) {

    case EFF_GLOBAL_VOL:
      tmp = arg << 1;
      if ( tmp > 128 ) tmp = 128;
      if ( tmp < 1 ) tmp = 1;
      songGlobalVolume = tmp;
      break;
      
    case EFF_SET_PAN:
    case EFF_SET_PAN_DMP:
    case EFF_SET_PAN_255:
#if 0
      dprintf( "pan = 0x%x, arg = 0x%x\n", effect, arg );
#endif
      switch ( effect ) {
        case EFF_SET_PAN:
          arg &= 0x0f;
          arg <<= 4;
          break;
        case EFF_SET_PAN_DMP:
          if ( arg >= 0x80 ) arg = 0x7f;
          arg <<= 1;
          break;
      }
#if 0
      dprintf( "opan = 0x%x, arg = 0x%x\n", effect, arg );
#endif
      voices[ channel ].pan = arg;
      voices[ channel ].what_is_new |= WIN_PAN;
      break;

    case EFF_MOD_SPEED:
      if ( arg < 32 )
        {
          if ( arg > 0 )
            songPlaySpeed = arg;
        }
       else
        {
          int tmp = CALC_BPM( arg );

          GUS_DO_SET_TIMER( tmp );
        }
      break;
    case EFF_SPEED:
      if ( arg > 0 )
        songPlaySpeed = arg;
      break;
    case EFF_TEMPO:
      {
        int tmp = CALC_BPM( arg );

        GUS_DO_SET_TIMER( tmp );
      }
      break;

  }
}

int doPreEffect( byte what, byte channel, byte instr, byte note, byte volume, byte effect, byte arg )
{
  short ok = 0;
  int cmdx = voices[ channel ].cmd[ what ];
  short argx = voices[ channel ].arg[ what ];

#if 0
  if ( effect == EFF_SLIDE_TO || effect == EFF_DELAY_NOTE )
    dprintf( "doPreEffect: ch %02i, eff %02x, arg %02x\n", channel, effect, arg );
#endif

  switch ( effect ) {
    case EFF_SLIDE_TO:
      if ( note != 255 )
        {
          if ( voices[ channel ].note < 0 ) return 0;
          voices[ channel ].slideToNote = note;
          voices[ channel ].slideToPeriod = GET_NOTE_PERIOD( channel, note );
        }
      cmdx = EFF_SLIDE_TO;
      if ( arg ) voices[ channel ].slideToNoteArg = (word)arg << 2; 
      ok = 1;
      break;
    case EFF_DELAY_NOTE:
      if ( arg == 0 ) break;
      cmdx = EFF_DELAY_NOTE;
      argx = arg & 0x0f;
      if ( instr != 255 ) SET_INSTR( channel, instr );
      if ( note != 255 ) SET_NOTE( channel, note );
      voices[ channel ].retrigArg = 0;
      ok = 2;
      break;
  }
  voices[ channel ].cmd[ what ] = cmdx;
  voices[ channel ].arg[ what ] = argx;
  if ( ok )
    {
      if ( volume != 255 )
        voices[ channel ].volume = volume;
       else
      if ( instr != 255 )
        voices[ channel ].volume = songInstrs[ instr ] -> samples[ voices[ channel ].sample ] -> volume;
      if ( ok == 1 ) SET_VOLUME( channel );
      return 1;
    }
  return 0;
}

void keyoff( byte channel )
{
  /* keyoff request - handled in doEnvelopeThings() */
  voices[ channel ].eKeyoffVol = 1;
  voices[ channel ].eKeyoffPan = 1;
}

void clearEffects()
{
  int w, i, cmd;
  
  for ( i = 0; i < songChannels; i++ )
    for ( w = 0; w < 2; w++ )
      {
        cmd = voices[ i ].cmd[ w ];
        if ( cmd < 0 ) continue;
        if ( cmd == EFF_ARPEG )
          {
            voices[ i ].note = voices[ i ].arpegOldNote;
            SET_NOTE_PERIOD( i );
            voices[ i ].cmd[ w ] = -1;
            continue;
          }
        if ( cmd == EFF_SLIDE_TO ||
	     cmd == EFF_PORT_AND_VOL ||
#if 0
             cmd == EFF_SLIDE_UP ||
             cmd == EFF_SLIDE_DOWN ||
#endif
             cmd == EFF_RETRIGGER ||
             cmd == EFF_MRETRIGGER ||
             cmd == EFF_CUT_NOTE ||
             cmd == EFF_DELAY_NOTE ) break;
        voices[ i ].cmd[ w ] = -1; 	/* stop effect */
      }
}

void stopNewNoteEffects( byte channel )
{
  int cmd;
  short i;

  for ( i = 0; i < 2; i++ )
    {
      cmd = voices[ channel ].cmd[ i ];
      if ( cmd == EFF_SLIDE_TO || 
           cmd == EFF_PORT_AND_VOL || 
           cmd == EFF_ARPEG ||
           cmd == EFF_SLIDE_UP ||
           cmd == EFF_SLIDE_DOWN ||
           cmd == EFF_VIBRATO ||
           cmd == EFF_FINE_VIBRATO ||
           cmd == EFF_VIBRA_AND_VOL ||
           cmd == EFF_CUT_NOTE ||
           cmd == EFF_DELAY_NOTE )
        voices[ channel ].cmd[ i ] = -1;
    }
}
