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

/* $Id: automaton.c,v 4.29 1995/09/17 23:27:04 espie Exp espie $
 * $Log: automaton.c,v $
 * Revision 4.29  1995/09/17 23:27:04  espie
 * *** empty log message ***
 *
 * Revision 4.28  1995/09/16 15:32:12  espie
 * Put back pref_speedmode in.
 *
 * Revision 4.25  1995/09/03 13:39:10  espie
 * Use tools.
 *
 * Revision 4.24  1995/09/02 22:18:25  espie
 * Display_pattern now shows running time.
 * automaton loop stuff obsolete.
 *
 * Revision 4.23  1995/08/31 13:30:07  espie
 * display time up to microseconds.
 *
 * Revision 4.22  1995/08/27 18:43:08  espie
 * tempo.
 *
 * Revision 4.21  1995/07/02 16:10:49  espie
 * Use substructure tempo.
 *
 * Revision 4.19  1995/06/26 15:44:05  espie
 * Removed pblock.
 *
 * Revision 4.18  1995/06/01 16:36:12  espie
 * Coredump cause.
 *
 * Revision 4.17  1995/05/11  12:30:20  espie
 * Corrected DELAY PATTERN effect.
 *
 * Revision 4.16  1995/03/11  21:40:02  espie
 * Modified jump pattern to work...
 *
 * Revision 4.15  1995/03/03  14:23:28  espie
 * Spurious bug fixed.
 *
 * Revision 4.14  1995/03/01  15:24:51  espie
 * Block length was hardcoded.
 *
 * Revision 4.13  1995/02/21  21:13:16  espie
 * Cleaned up source. Moved minor pieces of code around.
 *
 * Revision 4.12  1995/02/21  17:54:32  espie
 * Internal problem: buggy RCS. Fixed logs.
 *
 * 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 calls.
 * Use display_pattern.
 * Fixed up repeat code, should work better now.
 * Fixed bug with bad loops.
 * Modified the way set_speed works.
 * Corrected stupid bug (run_in_fg)
 * Added bg/fg test.
 * General cleanup
 * Added finetune.
 * Protracker commands.
 *
 * Revision 2.16  1992/11/17  17:15:37  espie
 * New output for new interface
 * Modified repeat logic: now works irregardless of repeat points.
 * start
 *
 * Revision 2.8  1992/07/14  14:23:41  espie
 * Changed fine speed command and comments.
 * Added two level of fault tolerancy.
 */
     

     
#include "defs.h"
#include "song.h"
#include "channel.h"
#include "extern.h"
#include "prefs.h"
     
ID("$Id: automaton.c,v 4.29 1995/09/17 23:27:04 espie Exp espie $")
     

/* set up the automaton so that I haven't got through patterns 
 * #from to #to
 */
LOCAL void clear_repeats(a, from, upto)
struct automaton *a;
int from, upto;
   {
   int i;

   for (i = from; i <= upto; i++)
      a->gonethrough[i] = FALSE;
   }

/* set up the automaton so that I haven't got through any patterns
 */
LOCAL void reset_repeats(a)
struct automaton *a;
   {
   clear_repeats(a, 0, a->info->length);
   a->gonethrough[a->info->length] = TRUE;
   }

/* update the pattern to play in the automaton. Checks that the pattern 
 * actually exists. Handle repetitions as well.
 */
LOCAL void set_pattern(a)
struct automaton *a;
   {
   if (a->pattern_num >= a->info->length)
      {
      error = UNRECOVERABLE;
      return;
      }

   if (a->gonethrough[a->pattern_num])
      {
      error = ENDED;
      reset_repeats(a);
      }
   else
      a->gonethrough[a->pattern_num] = TRUE;

      /* there is a level of indirection in the format,
       * i.e., patterns can be repeated.
       */
   a->pattern = a->info->patterns+a->pattern_num;


   display_pattern(a->pattern_num, a->info->length, a->pattern->number, 
	a->pattern->total, a->info->duration);
	if (error == ENDED)
		display_time(a->time_spent, a->info->duration);
   }

/* initialize all the fields of the automaton necessary
 * to play a given song.
 */
void init_automaton(a, song, start)
struct automaton *a;
struct song *song;
int start;
   {
   a->info = &song->info;
   a->pattern_num = start;    /* first pattern */

   a->delay_counter = 0;

   reset_repeats(a);

   a->note_num = 0;           /* first note in pattern */
   a->counter = 0;            /* counter for the effect tempo */
   a->speed = NORMAL_SPEED;
   a->finespeed = NORMAL_FINESPEED;
										/* (100%=NORMAL_FINESPEED) */
   a->do_stuff = DO_NOTHING;  /* some effects affect the automaton,
                               * we keep them here.  */
   error = NONE;              /* Maybe we should not reset errors at
                               * this point ?  */
   a->time_spent = 0;
   set_pattern(a);
   }

/* get to the next pattern, and display stuff 
 */
LOCAL void advance_pattern(a)
struct automaton *a;
   {
   if (++a->pattern_num >= a->info->length)
		{
		error = ENDED;
		reset_repeats(a);
      a->pattern_num = 0;
		}
   set_pattern(a);
   a->note_num = 0;
   }

void update_tempo(a)
struct automaton *a;
   {        
      /* there are three classes of speed changes:
       * 0 does nothing. (should stop for genuine protracker)
       * <32 is the effect speed (resets the fine speed).
       * >=32 changes the finespeed, default 125
       */

	switch(a->do_stuff & (SET_SPEED | SET_FINESPEED))
		{
	case SET_SPEED | SET_FINESPEED:
		if (get_pref_scalar(PREF_SPEEDMODE) != FINESPEED_ONLY)
			a->finespeed = a->new_finespeed;
		if (get_pref_scalar(PREF_SPEEDMODE) != SPEED_ONLY)
			a->speed = a->new_speed;
		break;
	case SET_SPEED:
		a->speed = a->new_speed;
		if (get_pref_scalar(PREF_SPEEDMODE) == ALTER_PROTRACKER)
			a->finespeed = NORMAL_FINESPEED;
		break;
	case SET_FINESPEED:
		a->finespeed = a->new_finespeed;
		break;
	default:
		break;
		}

	if (a->finespeed == 0)
		{
		status("Finespeed of 0");
		a->finespeed = NORMAL_FINESPEED;
		error = FAULT;
		}
   }

/* process all the stuff which we need to advance in the song,
 * including set_skip, set_fastskip, and set_loop.
 */
void next_tick(a)
struct automaton *a;
   {
   a->time_spent += ratio2time(NORMAL_FINESPEED, a->finespeed * 50);
   if (++a->counter >= a->speed)
      {
      a->counter = 0;
				/* if we are in delay mode, count down delay */
		if (a->delay_counter > 0)
			a->delay_counter--;
			/* get to next tick ONLY if no delay */
		if (a->delay_counter == 0)
			{
				/* loop: may change note in pattern right away */
			if (a->do_stuff & JUMP_PATTERN)
				a->note_num = a->loop_note_num;
			else if (a->do_stuff & SET_FASTSKIP)
				{
				a->pattern_num = a->new_pattern;
				set_pattern(a);
				a->note_num = 0;
				}
			else if (a->do_stuff & SET_SKIP)
				{
				advance_pattern(a);
				a->note_num = a->new_note;
				}
			else
				{
            if (++a->note_num >= a->info->plength)
               {
               advance_pattern(a);
               }
				}
			a->do_stuff = DO_NOTHING;
         }
      }


   }


