/* automaton.c */

/* $Id: automaton.c,v 3.15 1993/11/17 15:31:16 espie Exp espie $
 * $Log: automaton.c,v $
 * Revision 3.15  1993/11/17  15:31:16  espie
 * *** empty log message ***
 *
 * Revision 3.13  1993/07/18  10:39:44  espie
 * Fixed up repeat code, should work better now.
 *
 * Revision 3.12  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/01/16  17:00:27  espie
 * Corrected stupid bug (run_in_fg)
 *
 * 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
 * un#ifdef'ed showseq code.
 *
 * 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.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 <stdio.h>
#include <stdlib.h>
#include <string.h>
     
#include "defs.h"
#include "extern.h"
#include "song.h"
#include "channel.h"
     
LOCAL char *id = "$Id: automaton.c,v 3.15 1993/11/17 15:31:16 espie Exp espie $";
     

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;
	}

LOCAL void reset_repeats(a)
struct automaton *a;
    {
	clear_repeats(a, 0, a->info->length);
    a->gonethrough[a->info->length] = TRUE;
    }

/* updates the pattern to play in the automaton.
 * Checks that the pattern actually exists.
 * Checks for repetitions as well.
 */
LOCAL void set_pattern(a)
struct automaton *a;
    {
    int p;


    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;

	if (run_in_fg())
		{
		if (show)
			printf("\n%3d/%3d\n", a->pattern_num, a->info->length);
		else
			printf("%3d/%3d\b\b\b\b\b\b\b", a->pattern_num, a->info->length);
    	fflush(stdout); 
		}
        /* there is a level of indirection in the format,
         * i.e., patterns can be repeated.
         */
    p = a->info->patnumber[a->pattern_num];
    if (p >= a->info->maxpat)
        {
        error = UNRECOVERABLE;
        return;
        }
    a->pattern = a->info->pblocks + p;
    }

/* 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->loop_note_num = 0;
	a->loop_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;/* this is the default effect tempo */
    a->finespeed = NORMAL_FINESPEED;    
                            /* this is the fine speed (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.
                             */
    set_pattern(a);
    }

/* Gets to the next pattern, and displays stuff */
LOCAL void advance_pattern(a)
struct automaton *a;
    {
	if (run_in_fg() && show)
		printf("\n\n");
    if (++a->pattern_num >= a->info->length)
        a->pattern_num = 0;
    set_pattern(a);
    a->note_num = 0;
    }

        

/* process all the stuff which we need to advance in the song,
 * including set_speed, set_skip, set_fastskip, and set_loop.
 */
void next_tick(a)
struct automaton *a;
    {
            /* there are three classes of speed changes:
             * 0 does nothing. (should stop)
             * <32 is the effect speed (resets the fine speed).
             * >=32 changes the finespeed, default 125
             */
    if ((a->do_stuff & SET_SPEED) && (a->do_stuff & SET_FINESPEED))
        {
		a->speed = a->new_speed;
		a->finespeed = a->new_finespeed;
		}
	else if (a->do_stuff & SET_FINESPEED)
		{
		a->finespeed = a->new_finespeed;
		}
	else if (a->do_stuff & SET_SPEED)
		{
		a->speed = a->new_speed;
		/*
		a->finespeed = NORMAL_FINESPEED;
		*/
		}

    if (++a->counter >= a->speed)
        {
		a->counter = 0;
		if (run_in_fg() && show)
			printf("\n");
			/* loop: may change note in pattern right away */
		if ((a->do_stuff & JUMP_PATTERN) && --a->loop_counter > 0)
			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 >= BLOCK_LENGTH)
                advance_pattern(a);
            }
        a->do_stuff = DO_NOTHING;
        }
    }


