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

/* $Id: st_play.c,v 4.35 1995/09/17 23:27:30 espie Exp espie $
 * $Log: st_play.c,v $
 * Revision 4.35  1995/09/17 23:27:30  espie
 * *** empty log message ***
 *
 * Revision 4.34  1995/09/03 21:55:51  espie
 * Simplified player.
 *
 * Revision 4.33  1995/09/03 14:21:27  espie
 * *** empty log message ***
 *
 * Revision 4.32  1995/09/03 13:40:09  espie
 * Moved functions around.
 *
 * Revision 4.31  1995/08/31 13:31:25  espie
 * NO_OUTPUT option.
 *
 * Revision 4.30  1995/08/27 18:43:08  espie
 * *** empty log message ***
 *
 * Revision 4.29  1995/07/02 17:52:53  espie
 * *** empty log message ***
 *
 * Revision 4.28  1995/07/02 16:11:49  espie
 * *** empty log message ***
 *
 * Revision 4.27  1995/06/26 17:12:49  espie
 * *** empty log message ***
 *
 * Revision 4.26  1995/06/26 15:45:08  espie
 * Removed blocks, enter patterns.
 *
 * Revision 4.25  1995/06/26 10:11:17  espie
 * EVENT() abstraction.
 *
 * Revision 4.24  1995/06/23 09:35:51  espie
 * No longer pitch saved.
 *
 * Revision 4.23  1995/05/30  13:09:25  espie
 * Changed effect handling.
 *
 * Revision 4.22  1995/05/23  13:24:53  espie
 * Experimental new release.
 *
 * Revision 4.21  1995/05/15  12:20:09  espie
 * *** empty log message ***
 *
 * Revision 4.20  1995/05/11  12:30:46  espie
 * DELAY PATTERN correction.
 *
 * Revision 4.19  1995/03/11  21:41:01  espie
 * Added invert_loop.
 *
 * Revision 4.18  1995/03/04  00:16:21  espie
 * implemented vibrato wave.
 *
 * Revision 4.17  1995/03/03  14:24:21  espie
 * *** empty log message ***
 *
 * Revision 4.16  1995/03/01  15:24:51  espie
 * data width.
 *
 * Revision 4.15  1995/02/21  21:13:16  espie
 * Cleaned up source. Moved minor pieces of code around.
 *
 * Revision 4.14  1995/02/21  17:54:32  espie
 * Internal problem: buggy RCS. Fixed logs.
 *
 * Revision 4.12  1995/02/20  22:53:31  espie
 * Some display updated.
 *
 * Revision 4.11  1995/02/20  22:28:50  espie
 * 8 channels.
 *
 * Revision 4.10  1995/02/20  16:49:58  espie
 * Added funk_glissando
 *
 * Revision 4.9  1995/02/14  04:02:28  espie
 * Suppressed comment.
 *
 * Revision 4.8  1995/02/06  14:50:47  espie
 * Changed sample_info.
 *
 * Revision 4.7  1995/02/01  20:41:45  espie
 * Added color.
 *
 * Revision 4.6  1995/02/01  16:39:04  espie
 * Left/Right channels.
 *
 * Revision 4.0  1994/01/11  17:51:40  espie
 * Use the new UI calls.
 * Use the new pref settings.
 *
 * Revision 1.14  1994/01/09  23:24:37  Espie
 * Last bug fix.
 * Suppressed really outdated code.
 * New names: new_channel_tag_list, release_audio_channel.
 * Some notice to status.
 * Use new pref scheme.
 * Use get_ui
 * Use autoinit feature of display.c
 * discard_buffer forgotten...
 * Handle errors better.
 *
 * Revision 3.18  1993/12/04  16:12:50  espie
 * Lots of changes.
 * New high-level functions.
 * Amiga support.
 * Bug with delay_pattern: can't factorize the check for effect thingy.
 * Reniced verbose output display.
 * Bug fix: now use correct finetune when loading samples/starting notes.
 * Added bg/fg test.
 * General cleanup
 * Added <> operators.
 * Added update frequency on the fly.
 * Added finetune.
 * Protracker commands.
 *
 * Revision 2.19  1992/11/17  17:15:37  espie
 * Added interface using may_getchar(). Still primitive, though.
 * imask, start.
 * Added transpose feature.
 * Added possibility to get back to MONO for the sgi.
 * Added stereo capabilities to the indigo version.
 * Added two level of fault tolerancy.
 * Added some control on the number of replays,
 * and better error recovery.
 */
     
#include "defs.h"
#include "song.h"
#include "channel.h"
#include "extern.h"
#include "tags.h"
#include "prefs.h"
     

extern short vibrato_table[3][64];

ID("$Id: st_play.c,v 4.35 1995/09/17 23:27:30 espie Exp espie $")
     

/* setting up a given note */
void start_note(ch)
struct channel *ch;
   {
   ch->vib.offset = 0;
	ch->trem.offset = 0;
   play_note(ch->audio, ch->samp, ch->pitch);
   }

void stop_note(ch)
struct channel *ch;
	{
	play_note(ch->audio, empty_sample(), 0);
	}

void set_current_note(ch, note, pitch)
struct channel *ch;
int note;
int pitch;
	{
	ch->pitch = pitch;
	ch->note = note;
	}

/* changing the current pitch (value may be temporary, and so is not stored
 * in channel pitch, for instance vibratos)
 */
void set_temp_pitch(ch, pitch)
struct channel *ch;
int pitch;
   {
   set_play_pitch(ch->audio, pitch);
   }

/* changing the current volume, storing it in ch->volume
 */
void set_current_volume(ch, volume)
struct channel *ch;
int volume;
   {
   ch->volume = MAX(MIN(volume, MAX_VOLUME), MIN_VOLUME);
   set_play_volume(ch->audio, ch->volume);
   }

/* changing the current volume WITHOUT storing it
 */
void set_temp_volume(ch, volume)
struct channel *ch;
int volume;
	{
	volume = MAX(MIN(volume, MAX_VOLUME), MIN_VOLUME);
	set_play_volume(ch->audio, volume);
	}

void set_position(ch, pos)
struct channel *ch;
int pos;
   {
   set_play_position(ch->audio, pos);
   }


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


LOCAL unsigned int VSYNC;        /* base number of sample to output */
LOCAL void (*eval[NUMBER_EFFECTS])();
LOCAL int type[NUMBER_EFFECTS];
                    		/* the effect table */
LOCAL int oversample;   /* oversample value */
LOCAL unsigned long frequency;    /* output frequency */

LOCAL struct channel chan[MAX_TRACKS];
                    /* every channel */

LOCAL int ntracks;		/* number of tracks of the current song */
LOCAL struct sample_info **voices;

LOCAL struct automaton a;



/* init_channel(ch, dummy):
 * setup channel, with initially a dummy sample ready to play, and no note.
 */
LOCAL void init_channel(ch, side)
struct channel *ch;
int side;
   {
	struct tag tags[2];
	tags[0].type = AUDIO_SIDE;
	tags[0].data.scalar = side;
	tags[1].type = TAG_END;
   ch->samp = empty_sample();
   ch->finetune = 0;
   ch->audio = new_channel_tag_list(tags);
   ch->volume = 0; 
   ch->pitch = 0; 
   ch->note = NO_NOTE;

      /* we don't setup arpeggio values. */
   ch->vib.offset = 0; 
   ch->vib.depth = 0;
   ch->vib.rate = 0;
	ch->vib.table = vibrato_table[0];
	ch->vib.reset = FALSE;

	ch->trem.offset = 0;
	ch->trem.depth = 0;
	ch->trem.rate = 0;
	ch->trem.table = vibrato_table[0];
	ch->trem.reset = FALSE;

   ch->slide = 0; 

   ch->pitchgoal = 0; 
   ch->pitchrate = 0;

   ch->volumerate = 0;

	
	ch->funk_glissando = FALSE;
	ch->start_offset = 0;
   ch->adjust = do_nothing;
		/* initialize loop to no loop, loop start at 0 
		 * (needed for don't you want me, for instance) */
	ch->loop_counter = -1;
	ch->loop_note_num = 0;

   ch->special = do_nothing;
	ch->invert_speed = 0;
	ch->invert_offset = 0;
	ch->invert_position = 0;
   }


LOCAL void init_channels()
	{
   release_audio_channels();

	init_channel(chan, LEFT_SIDE);
	init_channel(chan + 1, RIGHT_SIDE);
	init_channel(chan + 2, RIGHT_SIDE);
	init_channel(chan + 3, LEFT_SIDE);
	if (ntracks > 4)
		{
		init_channel(chan + 4, LEFT_SIDE);
		init_channel(chan + 5, RIGHT_SIDE);
		}
	if (ntracks > 6)
		{
		init_channel(chan + 6, RIGHT_SIDE);
		init_channel(chan + 7, LEFT_SIDE);
		}
	}


LOCAL void adjust_sync(ofreq, tempo)
unsigned long ofreq;
int tempo;
   {
   VSYNC = ofreq * NORMAL_FINESPEED / tempo;
   }


void init_player(o, f)
int o;
unsigned long f;
   {
   oversample = o;
   frequency = f;
   init_tables(o, f);
   init_effects(eval, type);
   }


LOCAL void dump_events(a)
struct automaton *a;
	{
		/* display the output in a reasonable order:
		 * LEFT1 LEFT2 || RIGHT1 RIGHT 2
		 */
	dump_event(chan, EVENT(a, 0));
	dump_delimiter();
	dump_event(chan+3, EVENT(a, 3));
	dump_delimiter();
	if (ntracks > 4)
		{
		dump_event(chan+4, EVENT(a, 4));
		dump_delimiter();
		}
	if (ntracks > 7)
		{
		dump_event(chan+7, EVENT(a, 7));
		dump_delimiter();
		}
	dump_delimiter();
	dump_event(chan+1, EVENT(a, 1));
	dump_delimiter();
	dump_event(chan+2, EVENT(a, 2));
	if (ntracks > 5)
		{
		dump_delimiter();
		dump_event(chan+5, EVENT(a, 5));
		}
	if (ntracks > 6)
		{
		dump_delimiter();
		dump_event(chan+6, EVENT(a, 6));
		}
	dump_event(0, 0);
	}

LOCAL void setup_effect(ch, a, e)
struct channel *ch;
struct automaton *a;
struct event *e;
   {
   int samp, cmd;
	int pitch;

      /* retrieves all the parameters */
   samp = e->sample_number;

      /* load new instrument */
   if (samp)  
      {  /* note that we can change sample in the middle of a note. This 
			 * is a *feature*, not a bug (see made). Precisely: the sample 
			 * change will be taken into account for the next note, BUT the 
			 * volume change takes effect immediately.
          */
      ch->samp = voices[samp];
		ch->finetune = voices[samp]->finetune;
		if ((1L<<samp) & get_pref_scalar(PREF_IMASK))
			ch->samp = empty_sample();
		set_current_volume(ch, voices[samp]->volume);
      }

	pitch = pitch_table[e->note][ch->finetune];

   cmd = e->effect;

   if (pitch >= REAL_MAX_PITCH)
      {
      char buffer[60];

      sprintf(buffer,"Pitch out of bounds %d", pitch);
      status(buffer);
      pitch = 0;
      error = FAULT;
      }

   ch->adjust = do_nothing;

   switch(type[cmd])
		{
	case CH_E:
		if (pitch)
			set_current_note(ch, e->note, pitch);
		(eval[cmd])(ch, e);
		if (pitch)
			start_note(ch);
		break;
	case A_E:
		if (pitch)
			set_current_note(ch, e->note, pitch);
		(eval[cmd])(a, e);
		if (pitch)
			start_note(ch);
		break;
	case NO_NOTE_CH_E:
		if (pitch)
			set_current_note(ch, e->note, pitch);
		(eval[cmd])(ch, e);
		break;
	case PORTA_CH_PITCH_E:
		(eval[cmd])(ch, pitch, e);
		break;
	case CH_A_E:
		if (pitch)
			set_current_note(ch, e->note, pitch);
		(eval[cmd])(ch, a, e);
		if (pitch)
			start_note(ch);
		break;
		}
   }


LOCAL void play_one_tick(a)
struct automaton *a;
   {
   int channel;

   if (a->counter == 0)
      {	/* do new effects only if not in delay mode */
      if (a->delay_counter == 0) 
         {
         for (channel = 0; channel < ntracks; channel++)
            /* setup effects */
            setup_effect(chan + channel, a, EVENT(a, channel));
  			if (get_pref_scalar(PREF_SHOW))
     			dump_events(a);
         }
      }
   else
      for (channel = 0; channel < ntracks; channel++)
			{
         /* do the effects */
   		(chan[channel].special)(chan + channel);
         (chan[channel].adjust)(chan + channel);
			}

	update_tempo(a);
      /* actually output samples */
	if (get_pref_scalar(PREF_OUTPUT))
		resample(oversample, VSYNC / a->finespeed);
   }

LOCAL struct tag pres[2];

struct tag *play_song(song, start)
struct song *song;
int start;
   {
   int tempo;
   int countdown;      /* keep playing the tune or not */

   song_title(song->title);
   pres[1].type = TAG_END;
   
	ntracks = song->ntracks;
	set_number_tracks(ntracks);
   tempo = get_pref_scalar(PREF_SPEED);

   adjust_sync(frequency, tempo);
    /* a repeats of 0 is infinite replays */
   
   countdown = get_pref_scalar(PREF_REPEATS);
   if (countdown == 0)
      countdown = 50000;   /* ridiculously huge number */

   voices = song->samples; 

   init_automaton(&a, song, start);

	init_channels();

	set_data_width(song->side_width, song->max_sample_width);

   while(countdown)
      {
      struct tag *result;
      
      play_one_tick(&a);
		next_tick(&a);
      result = get_ui();
      while( (result = get_tag(result)) )
         {
         switch(result->type)
            {  
         case UI_LOAD_SONG:
            if (!result->data.pointer)
               break;
         case UI_NEXT_SONG:
         case UI_PREVIOUS_SONG:
            discard_buffer();
            pres[0].type = result->type;
            pres[0].data = result->data;
            return pres;
         case UI_QUIT:
            discard_buffer();
            end_all(0);
            /* NOTREACHED */
         case UI_SET_BPM:
            tempo = result->data.scalar;
            adjust_sync(frequency, tempo);
            break;
         case UI_RESTART:
            discard_buffer();
            init_automaton(&a, song, start);
				init_channels();
            break;
         case UI_JUMP_TO_PATTERN:
            if (result->data.scalar >= 0 && 
						result->data.scalar < a.info->length)
               {
               discard_buffer();
               init_automaton(&a, song, result->data.scalar);
               }
            break;
            /*
         case ' ':
            while (may_getchar() == EOF)
               ;
            break;
             */
         default:
            break;
            }
         result++;
         }

      {
      unsigned long new_freq;
      if ( (new_freq = update_frequency()) )
         {
         frequency = new_freq;
         adjust_sync(frequency, tempo);
         init_tables(oversample, frequency);
         }
      }

      switch(error)
         {
      case NONE:
         break;
      case ENDED:
         countdown--;
         break;
      case SAMPLE_FAULT:
      case FAULT:
      case PREVIOUS_SONG:
      case NEXT_SONG:
      case UNRECOVERABLE:
         if ( (error == SAMPLE_FAULT && get_pref_scalar(PREF_TOLERATE))
            ||(error == FAULT && get_pref_scalar(PREF_TOLERATE) > 1) )
            break;
         pres[0].type = PLAY_ERROR;
         pres[0].data.scalar = error;
         return pres;
      default:
         break;
         }
         error = NONE;
      }
   pres[0].type = PLAY_ENDED;
   return pres;
   }

