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

/* $Id: st_virt.c,v 1.5 1995/09/03 13:40:37 espie Exp espie $
 * $Log: st_virt.c,v $
 * Revision 1.5  1995/09/03 13:40:37  espie
 * Use tools.
 *
 * Revision 1.4  1995/09/02 22:21:18  espie
 * string_time now displays minutes/seconds.
 * loop_start allocation moved out of the loop.
 *
 * Revision 1.3  1995/08/31 13:31:51  espie
 * Duration computation.
 *
 * Revision 1.2  1995/08/27 18:43:08  espie
 * *** empty log message ***
 *
 * Revision 1.1  1995/07/02 16:12:04  espie
 * Initial revision
 *
 */

#include "defs.h"

#include <ctype.h>
#include <assert.h>

#include "extern.h"
#include "song.h"
#include "channel.h"
#include "prefs.h"


ID("$Id: st_virt.c,v 1.5 1995/09/03 13:40:37 espie Exp espie $")



LOCAL unsigned long *loop_start;

LOCAL unsigned long compute_pattern_duration(base, plength, ntracks, a)
struct event *base;
int plength, ntracks;
struct automaton *a;
	{
	int i, j;
	unsigned long d;
	struct event *e;

	d = 0;
	for(i = 0; i < ntracks; i++)
		loop_start[i] = 0;

	for (i = ((a->do_stuff & SET_SKIP) ? a->new_note : 0); i < plength; i++)
		{
		a->do_stuff = DO_NOTHING;
		a->delay_counter = 1;
		for (j = 0; j < ntracks; j++)
			{
			e = base + j * plength + i;
			switch(e->effect)
				{
			case EFF_SPEED:
				if (e->parameters >= 32 && 
					get_pref_scalar(PREF_SPEEDMODE) != OLD_SPEEDMODE)
					{
					a->new_finespeed = e->parameters;
					a->do_stuff |= SET_FINESPEED;
					}
				else if (e->parameters)
					{
					a->new_speed = e->parameters;
					a->do_stuff |= SET_SPEED;
					}
				break;
		   case EFF_DELAY:
		      a->delay_counter = (e->parameters + 1);
		      break;
			case EFF_SKIP:
				a->do_stuff |= SET_SKIP;
				a->new_note = e->parameters;
				break;
			case EFF_FF:
				a->do_stuff |= SET_FASTSKIP;
				a->new_pattern = e->parameters;
				break;
			case EFF_LOOP:
				if (!e->parameters)
					loop_start[j] = d;
				break;
			default:
				break;
				}
			}
      
      update_tempo(a);
		d += ratio2time(NORMAL_FINESPEED * a->delay_counter * a->speed,
			 a->finespeed * 50);

		for (j = 0; j < ntracks; j++)
			{
			e = base + j * plength + i;
			if ( (e->effect == EFF_LOOP) && e->parameters)
				{
				d += (d - loop_start[j]) * e->parameters;
				break;
				}
			}
				
		if ((a->do_stuff & SET_SKIP) || (a->do_stuff & SET_FASTSKIP))
			break;
		}
	return d;
	}
			
LOCAL void set_pattern(a)
struct automaton *a;
	{
	if ((a->pattern_num >= a->info->length) ||
		a->gonethrough[a->pattern_num])
		{
		error = ENDED;
		return;
		}
	a->gonethrough[a->pattern_num] = TRUE;
	a->pattern = a->info->patterns+a->pattern_num;
	}

void compute_duration(a, song)
struct automaton *a;
struct song *song;
	{
	unsigned long duration;

	loop_start = calloc(song->ntracks, sizeof(unsigned long));
	if (!loop_start)
		return;
	duration = 0;
	a->pattern->total = 0;
	error = NONE;
	while (error != ENDED)
		{
		a->pattern->duration =
			compute_pattern_duration(a->pattern->e,
				song->info.plength, song->ntracks, a);
		duration += a->pattern->duration;
		if (a->do_stuff & SET_FASTSKIP)
			a->pattern_num = a->new_pattern;
		else
			a->pattern_num++; 
		set_pattern(a);
		a->pattern->total = duration;
		}
	song->info.duration = duration;
	free(loop_start);
	}
