/* commands.c 
	vi:ts=3 sw=3:
 */

/* $Id: commands.c,v 4.29 1995/09/07 14:32:09 espie Exp espie $
 * $Log: commands.c,v $
 * Revision 4.29  1995/09/07 14:32:09  espie
 * Outstanding bug removed in portaslide... small bug (effect old speed
 * not working).
 *
 * Revision 4.28  1995/09/05 19:20:44  espie
 * *** empty log message ***
 *
 * Revision 4.27  1995/09/03 21:55:19  espie
 * Simplified player.
 *
 * Revision 4.26  1995/09/03 14:21:13  espie
 * *** empty log message ***
 *
 * Revision 4.25  1995/09/03 13:39:20  espie
 * *** empty log message ***
 *
 * Revision 4.24  1995/08/27 18:43:08  espie
 * *** empty log message ***
 *
 * Revision 4.23  1995/07/02 16:11:16  espie
 * *** empty log message ***
 *
 * Revision 4.22  1995/06/23 09:34:53  espie
 * Well...
 *
 * Revision 4.21  1995/05/30  13:08:23  espie
 * Temp change (maybe).
 *
 * Revision 4.20  1995/05/23  13:24:10  espie
 * Experimental new release.
 *
 * Revision 4.19  1995/05/11  12:23:43  espie
 * Corrected DELAY, SET_LOOP (+1), and volslide.
 *
 * Revision 4.18  1995/03/11  21:40:25  espie
 * Added jump_pattern, invert_loop.
 *
 * Revision 4.17  1995/03/04  00:15:48  espie
 * Implemented vibrato control.
 *
 * Revision 4.16  1995/03/01  15:24:51  espie
 * tone portamento and set offset more conformant to protracker.
 *
 * Revision 4.15  1995/02/25  15:43:11  espie
 * Bug in tremolo FIXED !!!
 *
 * Revision 4.14  1995/02/21  21:13:16  espie
 * Cleaned up source. Moved minor pieces of code around.
 *
 * Revision 4.13  1995/02/21  17:54:32  espie
 * Internal problem: buggy RCS. Fixed logs.
 *
 * Revision 4.11  1995/02/20  22:28:50  espie
 * tremolo
 *
 * Revision 4.10  1995/02/20  16:49:58  espie
 * Checked in new commands, fixed bug in set_finetune:
 * need to adjust the frequency of the current note according to
 * the new finetune.
 *
 * Revision 4.8  1995/02/06  14:50:47  espie
 * Changed sample_info.
 *
 * Revision 4.6  1995/02/01  16:39:04  espie
 * Includes moved to defs.h
 *
 * Revision 4.2  1994/08/23  18:19:46  espie
 * Added speedmode option
 * Abstracted IO.
 * Some notice to status.
 * play_note instead of ch->mode.
 * Fixed bug with bad loops.
 * Modified the way set_speed works.
 * Very small bug with volume (Lawrence).
 * Added bg/fg test.
 * More precise vibrato table.
 *
 * Revision 2.12  1992/11/13  13:24:24  espie
 * Added some extended commands: E12AB, and some.
 * now use set_volume in audio.c. All the device-dependent operation
 * is there.
 * Defensive programming: check the range of each note
 * for arpeggio setup.
 * Structured part of the code, especially replay ``automaton''
 * and setting up of effects.
 *
 * Revision 1.9  1991/11/17  17:09:53  espie
 * Added missing prototypes.
 * Dynamic oversample and frequency.
 * Added arpeggio.
 * Fixed up vibrato depth.
 * Added vibslide and portaslide.
 * Added command 9.
 */


#include "defs.h"
#include "channel.h"
#include "song.h"
#include "extern.h"
#include "prefs.h"
     
ID("$Id: commands.c,v 4.29 1995/09/07 14:32:09 espie Exp espie $")

/* sine table for the vibrato effect (obtained through build_vibrato.c) */

short vibrato_table[3][64] = 
	{
   {
   0,50,100,149,196,241,284,325,362,396,426,452,473,490,502,510,512,
   510,502,490,473,452,426,396,362,325,284,241,196,149,100,50,0,-49,
   -99,-148,-195,-240,-283,-324,-361,-395,-425,-451,-472,-489,-501,
   -509,-511,-509,-501,-489,-472,-451,-425,-395,-361,-324,-283,-240,
   -195,-148,-99,-49
   },
	{
	512,512,512,512,512,512,512,512,512,512,512,512,512,512,512,512,512,
	512,512,512,512,512,512,512,512,512,512,512,512,512,512,512,-512,-512,
	-512,-512,-512,-512,-512,-512,-512,-512,-512,-512,-512,-512,-512,-512, 
	-512,-512,-512,-512,-512,-512,-512,-512,-512,-512,-512,-512,-512,-512,
	-512,-512
	},
	{
	0,16,32,48,64,80,96,112,128,144,160,176,192,208,224,240,256,272,288,
	304,320,336,352,368,384,400,416,432,448,464,480,496,-512,-496,-480,
	-464,-448,-432,-416,-400,-384,-368,-352,-336,-320,-304,-288,-272,-256,
	-240,-224,-208,-192,-176,-160,-144,-128,-112,-96,-80,-64,-48,-32,-16
	}
	};

/***	setting up effects/doing effects :
 ***		set_xxx gets called while parsing the effect,
 *** 		do_xxx gets called each tick, and update the
 ***		sound parameters while playing.
 ***/




/***
 ***	base effects
 ***/

void do_nothing(ch)
struct channel *ch;
   {
   }

/*
LOCAL void not_implemented(a, ch)
struct automaton *a;
struct channel *ch;
	{
	notice("Not implemented");
	}
 */

void set_nothing()
   {
   }

/* slide pitch (up or down) */
LOCAL void do_slide(ch)
struct channel *ch;
   {
   ch->pitch += ch->slide;
   ch->pitch = MIN(ch->pitch, MAX_PITCH);
   ch->pitch = MAX(ch->pitch, MIN_PITCH);
   set_temp_pitch(ch, ch->pitch);
   }

LOCAL void set_upslide(ch, e)
struct channel *ch;
struct event *e;
   {
   ch->adjust = do_slide;
   if (e->parameters)
      ch->slide = e->parameters;
   }

LOCAL void set_downslide(ch, e)
struct channel *ch;
struct event *e;
   {
   ch->adjust = do_slide;
   if (e->parameters)
      ch->slide = -e->parameters;
   }

LOCAL int sinusoid_value(s)
struct sinusoid *s;
	{
	s->offset += s->rate;
	s->offset &= 63;
	return s->table[s->offset] * s->depth;
	}

LOCAL void set_sinusoid(e, s)
struct event *e;
struct sinusoid *s;
	{
   if (HI(e->parameters))
      s->rate = HI(e->parameters);
   if (LOW(e->parameters))
      s->depth = LOW(e->parameters);
	if (s->reset)
		s->offset = 0;
	}

/* modulate the pitch with vibrato */
LOCAL void do_vibrato(ch)
struct channel *ch;
   {
      /* temporary update of only the step value,
       * note that we do not change the saved pitch.
       */
   set_temp_pitch(ch, 
		ch->pitch + sinusoid_value(&(ch->vib))/256);
   }

LOCAL void set_vibrato(ch, e)
struct channel *ch;
struct event *e;
   {
   ch->adjust = do_vibrato;
	set_sinusoid(e, &(ch->vib));
   }

LOCAL void do_tremolo(ch)
struct channel *ch;
	{
	set_temp_volume(ch, ch->volume + sinusoid_value(&(ch->trem))/128);
	}

LOCAL void set_tremolo(ch, e)
struct channel *ch;
struct event *e;
	{
	ch->adjust = do_tremolo;
	set_sinusoid(e, &(ch->trem));
	}

/* arpeggio looks a bit like chords: we alternate between two
 * or three notes very fast.
 */
LOCAL void do_arpeggio(ch)
struct channel *ch;
   {
   if (++ch->arpindex >= MAX_ARP)
      ch->arpindex =0;
   set_temp_pitch(ch, ch->arp[ch->arpindex]);
   }

LOCAL void set_arpeggio(ch, e)
struct channel *ch;
struct event *e;
   {
      /* arpeggio can be installed relative to the
       * previous note, so we have to check that there
       * actually is a current(previous) note
       */
   if (ch->note == NO_NOTE)
      {
      status("No note present for arpeggio");
      error = FAULT;
      }
   else
      {
      int note;

      ch->arp[0] = pitch_table[ch->note][ch->finetune];
      note = ch->note + HI(e->parameters);
      if (note < NUMBER_NOTES)
         ch->arp[1] = pitch_table[note][ch->finetune];
      else
         {
         status("Arpeggio note out of range");
         error = FAULT;
         }
      note = ch->note + LOW(e->parameters);
      if (note < NUMBER_NOTES)
         ch->arp[2] = pitch_table[note][ch->finetune];
      else
         {
         status("Arpeggio note out of range");
         error = FAULT;
         }
      ch->arpindex = 0;
      ch->adjust = do_arpeggio;
      }
   }

/* volume slide. Mostly used to simulate waveform control.
 * (attack/decay/sustain).
 */
LOCAL void do_slidevol(ch)
struct channel *ch;
   {
   set_current_volume(ch, ch->volume + ch->volumerate);
   }

/* note that volumeslide does not have a ``take default''
 * behavior. If para is 0, this is truly a 0 volumeslide.
 * Issue: is the test really necessary ? Can't we do
 * a HI(para) - LOW(para). Answer: protracker does not.
 * DON'T GET the test order wrong!!! or ghouls will sound strange
 */
LOCAL void parse_slidevol(ch, para)
struct channel *ch;
int para;
   {
   if (HI(para))
      ch->volumerate = HI(para);
   else
      ch->volumerate = -LOW(para);
   }

LOCAL void set_slidevol(ch, e)
struct channel *ch;
struct event *e;
   {
   ch->adjust = do_slidevol;
   parse_slidevol(ch, e->parameters);
   }

/* portamento goes from a given pitch to another.  We could optimize 
 * that effect by splitting the routine into a pitch up/pitch down 
 * part while setting up the effect.
 */
LOCAL void do_portamento(ch)
struct channel *ch;
   {
	if (ch->pitchgoal)
		{
		if (ch->pitch < ch->pitchgoal)
			{
			ch->pitch += ch->pitchrate;
			if (ch->pitch >= ch->pitchgoal)
				{
				ch->pitch = ch->pitchgoal;
				ch->pitchgoal = 0;		/* ch->pitchgoal reset in protracker */
				}
			}
		else if (ch->pitch > ch->pitchgoal)
			{
			ch->pitch -= ch->pitchrate;
			if (ch->pitch <= ch->pitchgoal)
				{
				ch->pitch = ch->pitchgoal;
				ch->pitchgoal = 0;		/* ch->pitchgoal reset in protracker */
				}
			}
			/* funk glissando: round to the nearest note each time */
		if (ch->funk_glissando)
			set_temp_pitch(ch, nearest_note(ch->pitch, ch->finetune));
		else
			set_temp_pitch(ch, ch->pitch);
		}
   }

/* if para and pitch are 0, this is obviously a continuation
 * of the previous portamento.
 */
LOCAL void set_portamento(ch, pitch, e)
struct channel *ch;
int pitch;
struct event *e;
   {
   if (e->parameters)
      ch->pitchrate = e->parameters;
   if (pitch)
      ch->pitchgoal = pitch;
	if (ch->pitchgoal)
		ch->adjust = do_portamento;
   }




/***
 ***	combined commands
 ***/

LOCAL void do_portaslide(ch)
struct channel *ch;
   {
	do_portamento(ch);
   do_slidevol(ch);
   }

LOCAL void set_portaslide(ch, pitch, e)
struct channel *ch;
int pitch;
struct event *e;
   {
   if (pitch)
      ch->pitchgoal = pitch;
   parse_slidevol(ch, e->parameters);
	if (ch->pitchgoal)
		ch->adjust = do_portaslide;
	else
		ch->adjust = do_slidevol;
   }

LOCAL void do_vibratoslide(ch)
struct channel *ch;
   {
   do_vibrato(ch);
   do_slidevol(ch);
   }

LOCAL void set_vibratoslide(ch, e)
struct channel *ch;
struct event *e;
   {
   ch->adjust = do_vibratoslide;
   parse_slidevol(ch, e->parameters);
	if (ch->vib.reset)
		ch->vib.offset = 0;
   }




/***
 ***	effects that just need a setup part
 ***/

/* IMPORTANT: because of the special nature of the player, we can't 
 * process each effect independently, we have to merge effects from 
 * the four channel before doing anything about it. For instance, 
 * there can be several speed change in the same note.
 */
LOCAL void set_speed(a, e)
struct automaton *a;
struct event *e;
   {
   if (e->parameters >= 32 && get_pref_scalar(PREF_SPEEDMODE) != OLD_SPEEDMODE)
      {
      a->new_finespeed = e->parameters;
      a->do_stuff |= SET_FINESPEED;
      }
   else if (e->parameters)
      {
      a->new_speed = e->parameters;
      a->do_stuff |= SET_SPEED;
      }
   }

/* older soundtracker speed change effect */
LOCAL void set_st_speed(a, e)
struct automaton *a;
struct event *e;
	{
	a->new_speed = e->parameters;
	a->do_stuff |= SET_SPEED;
	}

LOCAL void set_skip(a, e)
struct automaton *a;
struct event *e;
   {
      /* BCD decoding in read.c */
   a->new_note = e->parameters;
   a->do_stuff |= SET_SKIP;
   }

LOCAL void set_fastskip(a, e)
struct automaton *a;
struct event *e;
   {
   a->new_pattern = e->parameters;
   a->do_stuff |= SET_FASTSKIP;
   }

/* immediate effect: starts the sample somewhere
 * off the start.
 */
LOCAL void set_offset(ch, e)
struct channel *ch;
struct event *e;
   {
	if (e->parameters)
		ch->start_offset = e->parameters * 256;
	start_note(ch);
   set_position(ch, ch->start_offset);
   }

/* change the volume of the current channel. Is effective until there 
 * is a new set_volume, slide_volume, or an instrument is reloaded 
 * explicitly by giving its number. Obviously, if you load an instrument 
 * and do a set_volume in the same note, the set_volume will take precedence.
 */
LOCAL void set_volume(ch, e)
struct channel *ch;
struct event *e;
   {
   set_current_volume(ch, e->parameters);
   }




/***
 ***	extended commands
 ***/

/* retrig note at a fast pace
 */
LOCAL void do_retrig(ch)
struct channel *ch;
   {
   if (--ch->current <= 0)
      {
		start_note(ch);
      ch->current = ch->retrig;
      }
   }

LOCAL void set_retrig(ch, e)
struct channel *ch;
struct event *e;
   {
   ch->retrig = e->parameters;
   ch->current = ch->retrig;
   ch->adjust = do_retrig;
   }

/* start note after a small delay
 */
LOCAL void do_latestart(ch)
struct channel *ch;
   {
   if (--ch->current <= 0)
      {
		set_current_note(ch, ch->note, ch->pitch);
		start_note(ch);
      ch->adjust = do_nothing;
      }
   }

LOCAL void set_late_start(ch, e)
struct channel *ch;
struct event *e;
   {
		/* stop previous note if necessary */
	stop_note(ch);
   ch->current = e->parameters;
   ch->adjust = do_latestart;
   }

/* cut note after some time. Note we only kill the volume, 
 * as protracker does (compatibility...)
 */
LOCAL void do_cut(ch)
struct channel *ch;
   {
   if (ch->retrig)
      {
      if (--ch->retrig == 0)
         set_current_volume(ch, 0);
      }
   }

LOCAL void set_note_cut(ch, e)
struct channel *ch;
struct event *e;
   {
   ch->retrig = e->parameters;
   ch->adjust = do_cut;
   }


LOCAL void set_smooth_up(ch, e)
struct channel *ch;
struct event *e;
   {
   ch->pitch += e->parameters;
   ch->pitch = MIN(ch->pitch, MAX_PITCH);
   ch->pitch = MAX(ch->pitch, MIN_PITCH);
   set_temp_pitch(ch, ch->pitch);
   }

LOCAL void set_smooth_down(ch, e)
struct channel *ch;
struct event *e;
   {
   ch->pitch -= e->parameters;
   ch->pitch = MIN(ch->pitch, MAX_PITCH);
   ch->pitch = MAX(ch->pitch, MIN_PITCH);
   set_temp_pitch(ch, ch->pitch);
   }

LOCAL void set_change_finetune(ch, e)
struct channel *ch;
struct event *e;
   {
   ch->finetune = e->parameters;
	if (e->note != NO_NOTE)
		{
		int pitch;

		pitch = pitch_table[e->note][ch->finetune];
		set_current_note(ch, e->note, pitch);
		}
	start_note(ch);
   }


LOCAL void set_loop(ch, a, e)
struct channel *ch;
struct automaton *a;
struct event *e;
   {
      /* Note: the current implementation of protracker
       * does not allow for a jump from pattern to pattern,
       * even though it looks like a logical extension to the current 
       * format.
       */
   if (e->parameters == 0) 
      ch->loop_note_num = a->note_num;
   else
      {
      if (ch->loop_counter == -1)
         ch->loop_counter = e->parameters + 1;
      /* We have to defer the actual note jump
       * to automaton.c, because some modules include several
       * loops on the same measure, which is a bit confusing
       * (see don't you want me for a good example)
       */
   	ch->loop_counter--;
		if (ch->loop_counter > 0)
	   	{
		   a->do_stuff |= JUMP_PATTERN;
			a->loop_note_num = ch->loop_note_num;
			}
		else
			ch->loop_counter = -1;
		}
   }

LOCAL void set_smooth_upvolume(ch, e)
struct channel *ch;
struct event *e;
   {
   set_current_volume(ch, ch->volume + e->parameters);
   }

LOCAL void set_smooth_downvolume(ch, e)
struct channel *ch;
struct event *e;
   {
   set_current_volume(ch, ch->volume - e->parameters);
   }


LOCAL void set_delay_pattern(a, e)
struct automaton *a;
struct event *e;
   {
   a->delay_counter = (e->parameters + 1);
   }



LOCAL void set_gliss_ctrl(ch, e)
struct channel *ch;
struct event *e;
	{
	if (e->parameters)
		{
		ch->funk_glissando = TRUE;
/*		notice("Funk true");		*/
		}
	else
		{
		ch->funk_glissando = FALSE;
/*		notice("Funk false");	*/
		}
	}

LOCAL void set_sine_wave(e, s)
struct event *e;
struct sinusoid *s;
	{
	s->table = vibrato_table[e->parameters & 3];
	if (e->parameters & 4)
		s->reset = FALSE;
	else
		s->reset = TRUE;
	}

LOCAL void set_vibrato_wave(ch, e)
struct channel *ch;
struct event *e;
	{
	set_sine_wave(e, &(ch->vib));
	}

LOCAL void set_tremolo_wave(ch, e)
struct channel *ch;
struct event *e;
	{
	set_sine_wave(e, &(ch->trem));
	}

LOCAL void do_invert(ch)
struct channel *ch;
	{
	ch->invert_offset += ch->invert_speed;
	if (ch->invert_offset >= 128)
		{
		ch->invert_offset = 0;
		if (ch->samp->rp_length)
			{
			if (++ch->invert_position >= ch->samp->rp_length)
				ch->invert_position = 0;
			ch->samp->rp_start[ch->invert_position] = -1 
				- ch->samp->rp_start[ch->invert_position];
			}
		}
	}

LOCAL void set_invert_loop(ch, e)
struct channel *ch;
struct event *e;
	{
	LOCAL unsigned char funk_table[] =
		{0, 5, 6, 7, 8, 10, 11, 13, 16, 19, 22, 26, 32, 43, 64, 128};
	if (e->parameters)
		{
		ch->invert_speed = funk_table[e->parameters];
		ch->special = do_invert;
		}
	else
		ch->special = do_nothing;
	}


/* initialize the whole effect table */
void init_effects(table, type)
void (*table[])();
int type[];
   {
   int i;

   for (i = 0; i < NUMBER_EFFECTS; i++)
      table[i] = set_nothing;
   table[EFF_ARPEGGIO] = set_arpeggio;
   table[EFF_SPEED] = set_speed;
   table[EFF_OLD_SPEED] = set_st_speed;
   table[EFF_SKIP] = set_skip;
   table[EFF_FF] = set_fastskip;
   table[EFF_VOLUME] = set_volume;
   table[EFF_VOLSLIDE] = set_slidevol;
   table[EFF_OFFSET] = set_offset;
   table[EFF_PORTA] = set_portamento;
   table[EFF_PORTASLIDE] = set_portaslide;
   table[EFF_UP] = set_upslide;
   table[EFF_DOWN] = set_downslide;
   table[EFF_VIBRATO] = set_vibrato;
   table[EFF_VIBSLIDE] = set_vibratoslide;
   table[EFF_SMOOTH_UP] = set_smooth_up;
   table[EFF_SMOOTH_DOWN] = set_smooth_down;
   table[EFF_CHG_FTUNE] = set_change_finetune;
   table[EFF_LOOP] = set_loop;
   table[EFF_RETRIG] = set_retrig;
   table[EFF_S_UPVOL] = set_smooth_upvolume;
   table[EFF_S_DOWNVOL] = set_smooth_downvolume;
   table[EFF_NOTECUT] = set_note_cut;
   table[EFF_LATESTART] = set_late_start;
   table[EFF_DELAY] = set_delay_pattern;
	table[EFF_TREMOLO] = set_tremolo;
	table[EFF_GLISS_CTRL] = set_gliss_ctrl;
	table[EFF_VIBRATO_WAVE] = set_vibrato_wave;
	table[EFF_TREMOLO_WAVE] = set_tremolo_wave;
	table[EFF_INVERT_LOOP] = set_invert_loop;

	type[EFF_ARPEGGIO] = CH_E;
	type[EFF_SPEED] = A_E;
	type[EFF_OLD_SPEED] = A_E;
	type[EFF_SKIP] = A_E;
	type[EFF_FF] = A_E;
	type[EFF_VOLUME] = CH_E;
	type[EFF_VOLSLIDE] = CH_E;
	type[EFF_OFFSET] = NO_NOTE_CH_E;
	type[EFF_PORTA] = PORTA_CH_PITCH_E;
	type[EFF_PORTASLIDE] = PORTA_CH_PITCH_E;
	type[EFF_UP] = CH_E;
	type[EFF_DOWN] = CH_E;
	type[EFF_VIBRATO] = CH_E;
	type[EFF_VIBSLIDE] = CH_E;
	type[EFF_SMOOTH_UP] = CH_E;
	type[EFF_SMOOTH_DOWN] = CH_E;
	type[EFF_CHG_FTUNE] = NO_NOTE_CH_E;
	type[EFF_LOOP] = CH_A_E;
	type[EFF_RETRIG] = CH_E;
	type[EFF_S_UPVOL] = CH_E;
	type[EFF_S_DOWNVOL] = CH_E;
	type[EFF_NOTECUT] = CH_E;
	type[EFF_LATESTART] = NO_NOTE_CH_E;
	type[EFF_DELAY] = A_E;
	type[EFF_TREMOLO] = CH_E;
	type[EFF_GLISS_CTRL] = CH_E;
	type[EFF_VIBRATO_WAVE] = CH_E;
	type[EFF_TREMOLO_WAVE] = CH_E;
	type[EFF_INVERT_LOOP] = CH_E;
   }

