/* commands.c */

/* $Id: commands.c,v 3.14 1993/11/17 15:31:16 espie Exp espie $
 * $Log: commands.c,v $
 * Revision 3.14  1993/11/17  15:31:16  espie
 * play_note instead of ch->mode.
 *
 * Revision 3.13  1993/08/04  11:34:33  espie
 * *** empty log message ***
 *
 * Revision 3.12  1993/07/18  10:39:44  espie
 * Cleaned up.
 *
 * Revision 3.11  1993/07/17  22:23:41  espie
 * Fixed bug with bad loops.
 *
 * Revision 3.9  1993/05/09  14:06:03  espie
 * Modified the way set_speed works.
 *
 * Revision 3.8  1993/04/28  20:13:13  espie
 * Very small bug with volume (Lawrence).
 *
 * Revision 3.7  1993/01/15  14:00:28  espie
 * Added bg/fg test.
 *
 * Revision 3.6  1992/11/27  10:29:00  espie
 * General cleanup
 *
 * Revision 3.5  1992/11/24  10:51:19  espie
 * More precise vibrato table.
 *
 * Revision 3.4  1992/11/23  10:12:23  espie
 * *** empty log message ***
 *
 * Revision 3.3  1992/11/22  17:20:01  espie
 * Simplified delay_pattern.
 *
 * Revision 3.2  1992/11/20  14:53:32  espie
 * Added finetune.
 *
 * Revision 3.1  1992/11/19  20:44:47  espie
 * Protracker commands.
 *
 * Revision 3.0  1992/11/18  16:08:05  espie
 * New release.
 *
 * 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 <stdio.h>

#include "defs.h"
#include "extern.h"
#include "channel.h"
#include "song.h"
     
LOCAL char *id = "$Id: commands.c,v 3.14 1993/11/17 15:31:16 espie Exp espie $";

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

int vibrato_table[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
	};

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


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

void set_nothing(a, ch)
struct automaton *a;
struct channel *ch;
    {
    }

/* slide pitch (up or down) */
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_current_pitch(ch, ch->pitch);
    }

void set_upslide(a, ch)
struct automaton *a;
struct channel *ch;
    {
    ch->adjust = do_slide;
    if (a->para)
        ch->slide = a->para;
    }

void set_downslide(a, ch)
struct automaton *a;
struct channel *ch;
    {
    ch->adjust = do_slide;
    if (a->para)
        ch->slide = -a->para;
    }

/* modulating the pitch with vibrato */
void do_vibrato(ch)
struct channel *ch;
    {
    int offset;

        /* this is no longer a literal transcription of the pt
         * code. I have rescaled the vibrato table.
         */
    ch->viboffset += ch->vibrate;
    ch->viboffset &= 63;
		/* please don't use logical shift on signed values */
    offset = (vibrato_table[ch->viboffset] * ch->vibdepth)/256;
        /* temporary update of only the step value,
         * note that we do not change the saved pitch.
         */
    set_current_pitch(ch, ch->pitch + offset);
    }

void set_vibrato(a, ch)
struct automaton *a;
struct channel *ch;
    {
    ch->adjust = do_vibrato;
	if (HI(a->para))
		ch->vibrate = HI(a->para);
	if (LOW(a->para))
		ch->vibdepth = LOW(a->para);
    }

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

void set_arpeggio(a, ch)
struct automaton *a;
struct channel *ch;
    {
        /* 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)
        {
        fprintf(stderr,
            "No note present for arpeggio");
        /* error = FAULT; */
        }
    else
        {
        int note;

        ch->arp[0] = pitch_table[ch->note][ch->finetune];
        note = ch->note + HI(a->para);
        if (note < NUMBER_NOTES)
            ch->arp[1] = pitch_table[note][ch->finetune];
        else
            {
            fprintf(stderr,
                "Arpeggio note out of range");
            error = FAULT;
            }
        note = ch->note + LOW(a->para);
        if (note < NUMBER_NOTES)
            ch->arp[2] = pitch_table[note][ch->finetune];
        else
            {
            fprintf(stderr,
                "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).
 */
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.
 */
void parse_slidevol(ch, para)
struct channel *ch;
int para;
    {
    if (LOW(para))
        ch->volumerate = -LOW(para);
    else
        ch->volumerate = HI(para);
    }

void set_slidevol(a, ch)
struct automaton *a;
struct channel *ch;
    {
    ch->adjust = do_slidevol;
    parse_slidevol(ch, a->para);
    }

/* portamento: gets from a given pitch to another.
 * We can simplify the routine by cutting it in
 * a pitch up and pitch down part while setting up
 * the effect.
 */
void do_portamento(ch)
struct channel *ch;
    {
    if (ch->pitch < ch->pitchgoal)
        {
        ch->pitch += ch->pitchrate;
        ch->pitch = MIN(ch->pitch, ch->pitchgoal);
        }
    else if (ch->pitch > ch->pitchgoal)
        {
        ch->pitch -= ch->pitchrate;
        ch->pitch = MAX(ch->pitch, ch->pitchgoal);
        }
		/* if we want to implement funk glissando, we need a change right
		 * there
		 */
    set_current_pitch(ch, ch->pitch);
    }

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

/*
 * combined commands.
 */
void do_portaslide(ch)
struct channel *ch;
    {
    do_portamento(ch);
    do_slidevol(ch);
    }

void set_portaslide(a, ch)
struct automaton *a;
struct channel *ch;
    {
    ch->adjust = do_portaslide;
	if (a->pitch)
		ch->pitchgoal = a->pitch;
    parse_slidevol(ch, a->para);
    }

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

void set_vibratoslide(a, ch)
struct automaton *a;
struct channel *ch;
    {
    ch->adjust = do_vibratoslide;
    parse_slidevol(ch, a->para);
    }

/***
 *
 *  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.
 */
void set_speed(a, ch)
struct automaton *a;
struct channel *ch;
    {
	if (a->para >= 32)
		{
		a->new_finespeed = a->para;
		a->do_stuff |= SET_FINESPEED;
		}
	else if (a->para)
		{
		a->new_speed = a->para;
		a->do_stuff |= SET_SPEED;
		}
    }

void set_skip(a, ch)
struct automaton *a;
struct channel *ch;
    {
        /* BCD decoding in read.c */
    a->new_note = a->para;
    a->do_stuff |= SET_SKIP;
    }

void set_fastskip(a, ch)
struct automaton *a;
struct channel *ch;
    {
    a->new_pattern = a->para;
    a->do_stuff |= SET_FASTSKIP;
    }

/* immediate effect: starts the sample somewhere
 * off the start.
 */
void set_offset(a, ch)
struct automaton *a;
struct channel *ch;
    {
	set_position(ch, a->para * 256);
    }

/* 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.
 */
void set_volume(a, ch)
struct automaton *a;
struct channel *ch;
    {
	set_current_volume(ch, a->para);
    }



/***
 *
 *	EXTENDED COMMANDS
 *
 ***/

/* extended command: retrig note at a fast pace
 */
void do_retrig(ch)
struct channel *ch;
    {
    if (--ch->current <= 0)
        {
        reset_note(ch, ch->note, ch->pitch);
        ch->current = ch->retrig;
        }
    }

void set_retrig(a, ch)
struct automaton *a;
struct channel *ch;
	{
	ch->retrig = a->para;
	ch->current = ch->retrig;
	ch->adjust = do_retrig;
	}

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

void set_late_start(a, ch)
struct automaton *a;
struct channel *ch;
	{
	play_note(ch->audio, NULL, 0);
	ch->current = a->para;
	ch->adjust = do_latestart;
	}

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

void set_note_cut(a, ch)
struct automaton *a;
struct channel *ch;
	{
	ch->retrig = a->para;
	ch->adjust = do_cut;
	}


void set_smooth_up(a, ch)
struct automaton *a;
struct channel *ch;
	{
	ch->pitch += a->para;
	ch->pitch = MIN(ch->pitch, MAX_PITCH);
	ch->pitch = MAX(ch->pitch, MIN_PITCH);
	set_current_pitch(ch, ch->pitch);
	}

void set_smooth_down(a, ch)
struct automaton *a;
struct channel *ch;
	{
	ch->pitch -= a->para;
	ch->pitch = MIN(ch->pitch, MAX_PITCH);
	ch->pitch = MAX(ch->pitch, MIN_PITCH);
	set_current_pitch(ch, ch->pitch);
	}

void set_change_finetune(a, ch)
struct automaton *a;
struct channel *ch;
	{
	ch->finetune = a->para;
	}


void set_loop(a, ch)
struct automaton *a;
struct channel *ch;
	{
		/* 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 (a->para == 0) 
		a->loop_note_num = a->note_num;
	else
		{
		if (a->loop_counter == 0)
			a->loop_counter = a->para + 1;
		/* We have to defer the actual count-down and 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)
		 */
		a->do_stuff |= JUMP_PATTERN;
		}
	}

void set_smooth_upvolume(a, ch)
struct automaton *a;
struct channel *ch;
	{
	set_current_volume(ch, ch->volume + a->para);
	}

void set_smooth_downvolume(a, ch)
struct automaton *a;
struct channel *ch;
	{
	set_current_volume(ch, ch->volume - a->para);
	}


void set_delay_pattern(a, ch)
struct automaton *a;
struct channel *ch;
	{
	a->counter -= (a->para + 1) * a->speed;
	a->do_stuff |= DELAY_PATTERN;
	}



/* Initialize the whole effect table */

void init_effects(table)
void (*table[])();
    {
	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_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;
    }

void make_effects(int cmd, struct automaton *a, struct channel *ch)
{
	switch(cmd)
	{
    case EFF_ARPEGGIO: set_arpeggio(a, ch); break;
    case EFF_SPEED: set_speed(a, ch); break;
    case EFF_SKIP: set_skip(a, ch); break;
    case EFF_FF: set_fastskip(a, ch); break;
    case EFF_VOLUME: set_volume(a, ch); break;
    case EFF_VOLSLIDE: set_slidevol(a, ch); break;
    case EFF_OFFSET: set_offset(a, ch); break;
    case EFF_PORTA: set_portamento(a, ch); break;
    case EFF_PORTASLIDE: set_portaslide(a, ch); break;
    case EFF_UP: set_upslide(a, ch); break;
    case EFF_DOWN: set_downslide(a, ch); break;
    case EFF_VIBRATO: set_vibrato(a, ch); break;
    case EFF_VIBSLIDE: set_vibratoslide(a, ch); break;
	case EFF_SMOOTH_UP: set_smooth_up(a, ch); break;
	case EFF_SMOOTH_DOWN: set_smooth_down(a, ch); break;
	case EFF_CHG_FTUNE: set_change_finetune(a, ch); break;
	case EFF_LOOP: set_loop(a, ch); break;
	case EFF_RETRIG: set_retrig(a, ch); break;
	case EFF_S_UPVOL: set_smooth_upvolume(a, ch); break;
	case EFF_S_DOWNVOL: set_smooth_downvolume(a, ch); break;
	case EFF_NOTECUT: set_note_cut(a, ch); break;
	case EFF_LATESTART: set_late_start(a, ch); break;
	case EFF_DELAY: set_delay_pattern(a, ch); break;
	default: set_nothing(a, ch); break;
	}
}

