/* periods.c */

/* Computes the period table */
/* $Author: Espie $
 * $Date: 91/05/12 19:54:18 $
 * $Revision: 1.7 $
 * $Log:	periods.c,v $
 * Revision 1.7  91/05/12  19:54:18  Espie
 * Experimental find_note, handle stray periods with less space.
 * 
 * Revision 1.6  91/05/06  23:34:24  Espie
 * New finetunes from 0 to 15 with 8 as default value.
 * Gives an idea that something is wrong in the window
 * title bar.
 * 
 * Revision 1.5  91/05/05  19:07:07  Espie
 * *** empty log message ***
 * 
 * Revision 1.4  91/05/05  15:39:58  Espie
 * The period_table to fill is now an argument.
 * 
 * Revision 1.3  91/04/30  00:36:03  Espie
 * Stable version III.
 * 
 * Revision 1.2  91/04/28  22:54:30  Espie
 * Now computes the periods.
 * 
 * Revision 1.1  91/04/21  20:03:33  Espie
 * Initial revision
 * 
 */
 
#include <exec/types.h>
#include <dos/dos.h>
#include "proto.h"
#include "periods.h"

/* This file computes the period for all notes with all tuning.
 * It is also able to find a note from a given period.
 * Standard tuning has been moved to a middle value (8).
 */


/* to compute the note periods, we deal with finite precision arithmetic
 * note/2^precision ~ 2^(1/12), adjust/2^precision ~ 2^(1/96)
 */
 
#define precision (16)
#define note 69433
#define adjust 66011
#define rounded_div(a,b) ((a+b/2)/b)
#define rounded_shift(a,i) ((a+(1<<((i)-1)))>>(i))



LOCAL UWORD **lookup_table;

void init_periods(UWORD **period_table)
	{
	int i, k;
		/* everything MUST be unsigned */
	unsigned long starting_period, period;
	ULONG buffer[12];
		lookup_table = period_table;
		starting_period = rounded_div(3579545, 440) <<precision;
		period = starting_period;
		for (k = 0; k < NUMBER_TUNING-1; k++)
			{
					/* get the basic period ok */
				buffer[0] = period;
					/* compute one high precision octave */
				for (i = 1; i < 12; i++)
					buffer[i] = rounded_div(buffer[i-1], note)<<precision;
					/* use it to get every octave */
				for (i = 0; i < NUMBER_NOTES; i++)
					period_table[k][i] = 
						rounded_shift(buffer[i%12], precision+i/12);
					/* for a better precision: 96/8 = 12 */
				if (k == 7)
					period = rounded_div(starting_period, note)<<precision;
				else
					period = rounded_div(period, adjust)<<precision;
			}
		lookup_table[NUMBER_TUNING-1][0] = 1;
	}
				

/* this routine was taking too much time as a dumb linear scan (half
 * the running time, said the lattice profiler), so it got optimized.
 * Also, Lattice C wasn't able to fact the period_table[fine] constant,
 * had to do it by hand. Shesh !
 */
 
UBYTE find_note(UWORD period, UBYTE fine)
	{
	UWORD *t;
	UBYTE a, b, i;	
		if (period == 0)
			return NO_NOTE;
		t = lookup_table[fine];
		a = 0;
		b = NUMBER_NOTES-1;
		while(b-a > 1)
			{
				i = (a+b)/2;
				if (t[i] == period)
					return i;
				if (t[i] > period)
					a = i;
				else
					b = i;
			}
			/* at that point we have t[b]<= period <= t[a]
			 * We want to allow for rounding errors.
			 */
		check_abort();
		if (t[a] - 2 <= period)
			return a;
		if (t[b] + 2 >= period)
			return b;
		temporary_title("Read problem");
		t = lookup_table[NUMBER_TUNING-1];
		for (a = 1; a < t[0]; a++)
			if (period - t[a] <= 2 && period - t[a] >= -2)
				{
					return FINE_PERIOD + a;
				}
		t[t[0]] = period;
		if (t[0] >= NUMBER_NOTES - 1)
			return 0;
		return FINE_PERIOD + t[0]++;
	}		

/* remap the tuning so that 8 is now in the middle... MUCH more sensible. */

LOCAL BYTE remap_tunes[16] = {8,9,10,11,12,13,14,15,0,1,2,3,4,5,6,7};
	
BYTE normalize_finetune(UBYTE fine)
	{
		if (fine > 15)
			return -1;
		else
			return remap_tunes[fine];
	}
