/* automaton.c */

/* $Author: espie $
 * $Id: automaton.c,v 2.12 1992/10/24 15:58:49 espie Exp espie $
 * $Revision: 2.12 $
 * $Log: automaton.c,v $
 * Revision 2.12  1992/10/24  15:58:49  espie
 * ???
 *
 * Revision 2.11  1992/07/23  13:52:28  espie
 * Modified repeat logic: now works regardless of repeat points.
 *
 * Revision 2.10  1992/07/22  14:50:25  espie
 * defs.h support
 *
 * Revision 2.9  1992/07/17  13:43:00  espie
 * Added start
 *
 * Revision 2.8  1992/07/14  14:23:41  espie
 * Changed fine speed command and comments.
 *
 * Revision 2.7  1992/07/14  13:20:53  espie
 * *** empty log message ***
 *
 * Revision 2.5  1991/11/20  20:46:35  espie
 * Minor correction.
 *
 * Revision 2.4  1991/11/19  16:07:19  espie
 * Added comments, moved minor stuff around.
 *
 * Revision 2.3  1991/11/18  01:23:30  espie
 * Added two level of fault tolerancy.
 *
 * Revision 2.2  1991/11/18  01:12:31  espie
 * Minor changes.
 *
 * Revision 2.1  1991/11/17  23:07:58  espie
 * Coming from str32.
 *
 *
 */
     

#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 2.12 1992/10/24 15:58:49 espie Exp espie $";
     

void reset_repeats(a)
struct automaton *a;
    {
    int i;

        /* check up for repetitions */
    for(i = 0; i < a->info->length; i++)
        a->gonethrough[i] = FALSE;
    a->gonethrough[a->info->length] = TRUE;
    }

/* updates the pattern to play in the automaton.
 * Checks that the pattern actually exists.
 */
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);
        }

        /* there is a level of indirection in the format,
         * i.e., patterns can be repeated.
         */
    p = a->info->patnumber[a->pattern_num];
    a->gonethrough[a->pattern_num] = TRUE;
    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 */

    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%)    */
    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 */

void advance_pattern(a)
struct automaton *a;
    {
#ifdef SHOW_SEQ
    printf("\n");
#endif
    printf("%3d", a->pattern_num);
    fflush(stdout);
#ifdef SHOW_SEQ
    printf("\n");
#endif
    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 and set_fastskip.
 */
void next_tick(a)
struct automaton *a;
    {
    if (a->do_stuff & SET_SPEED && a->new_speed)
        {
            /* there are three classes of speed changes:
             * 0 does nothing.
             * <=32 is the effect speed (resets the fine speed).
             * >32 changes the finespeed, so this is 1% to 223%
             * Note: 0% makes no sense, so 32 is still an effect speed.
             */
        if (a->new_speed > 32)
            a->finespeed = a->new_speed - 32;
        else 
            {
            a->speed = a->new_speed;
            a->finespeed = 100;
            }
        }
    if (++a->counter >= a->speed)
        {
        a->counter = 0;
#ifdef SHOW_SEQ
        printf("\n");
#endif
        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;
        }
    }


