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

/* $Id: notes.c,v 4.20 1995/09/16 19:04:42 espie Exp espie $
 * $Log: notes.c,v $
 * Revision 4.20  1995/09/16 19:04:42  espie
 * *** empty log message ***
 *
 * Revision 4.19  1995/09/05 19:21:16  espie
 * *** empty log message ***
 *
 * Revision 4.18  1995/08/27 18:43:08  espie
 * *** empty log message ***
 *
 * Revision 4.17  1995/07/02 17:52:42  espie
 * *** empty log message ***
 *
 * Revision 4.16  1995/07/02 16:11:35  espie
 * *** empty log message ***
 *
 * Revision 4.15  1995/06/26 15:44:43  espie
 * Removed blocks.
 *
 * Revision 4.14  1995/06/23 09:35:42  espie
 * *** empty log message ***
 *
 * Revision 4.13  1995/03/01  15:24:51  espie
 * *** empty log message ***
 *
 * Revision 4.12  1995/02/21  21:13:16  espie
 * Cleaned up source. Moved minor pieces of code around.
 *
 * Revision 4.11  1995/02/21  17:54:32  espie
 * Internal problem: buggy RCS. Fixed logs.
 *
 * Revision 4.9  1995/02/20  22:28:50  espie
 * bug in nearest_note.
 *
 * Revision 4.8  1995/02/20  16:49:58  espie
 * Added nearest_note: round pitch to nearest entire note.
 *
 * Revision 4.5  1995/02/01  16:39:04  espie
 * Moved includes to defs.h
 *
 * Revision 4.0  1994/01/11  17:50:04  espie
 * Makes use of autoinit. Uses less memory, starts up faster.
 * auto_init'd create_notes_table(),
 * suppressed note_name static table,
 * use name_of_note() instead (about 120 * 8 bytes gain).
 * Amiga support.
 * Added finetune.
 */

#include "defs.h"

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

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

ID("$Id: notes.c,v 4.20 1995/09/16 19:04:42 espie Exp espie $")


/* we can put it autoinit since find_note is ALWAYS called
 * prior to using pitch_table !
 */
LOCAL void create_notes_table P((void));
LOCAL void (*INIT)P((void)) = create_notes_table;


/* the musical notes correspond to some specific pitch.
 * It's useful to be able to find them back, at least for
 * arpeggii.
 */
/* pitch values are stored in the range 1 <= n <= NUMBER_NOTES
 * note 0 is NO_NOTE, with corresponding null pitch
 */
short pitch_table[NUMBER_NOTES+1][NUMBER_FINETUNES];

LOCAL char *note_template = "C-C#D-D#E-F-F#G-G#A-A#B-";

/* note = find_note(pitch): 
 * find note corresponding to the stated pitch 
 */
int find_note(pitch)
int pitch;
   {
   int a, b, i;
   
   INIT_ONCE;

   if (pitch == 0)
      return NO_NOTE;
   a = 1;
   b = NUMBER_NOTES;
   while(b-a > 1)
      {
      i = (a+b)/2;
      if (pitch_table[i][0] == pitch)
         return i;
      if (pitch_table[i][0] > pitch)
         a = i;
      else
         b = i;
      }
   if (pitch_table[a][0] - FUZZ <= pitch)
      return a;
   if (pitch_table[b][0] + FUZZ >= pitch)
      return b;
	error = CORRUPT_FILE;
   return 0;
   }

/* pitch = nearest_note(pitch, finetune):
 * return the pitch corresponding to the nearest note for the given
 * finetune
 */
int nearest_note(pitch, finetune)
int pitch;
int finetune;
	{
   int a, b, i;
   
   INIT_ONCE;

   if (pitch == 0)
      return NO_NOTE;
   a = 1;
   b = NUMBER_NOTES;
   while(b-a > 1)
      {
      i = (a+b)/2;
      if (pitch_table[i][finetune] == pitch)
         return pitch;
      if (pitch_table[i][finetune] > pitch)
         a = i;
      else
         b = i;
      }
			/* need some check for the actual nearest note ? */
	return pitch_table[i][finetune];
   }


LOCAL void create_notes_table()
   {
   double base, pitch;
   int i, j, k;

   for (j = -8; j < 8; j++)
      {
      k = j < 0 ? j + 16 : j;
      base = AMIGA_CLOCKFREQ/440.0/4.0 / pow(2.0, j/96.0);
			/* relies on NO_NOTE being 0 */
		pitch_table[NO_NOTE][k] = 0;

      for (i = 0; i < NUMBER_NOTES;)
         {
         pitch = base / pow(2.0, i/12.0);
				/* take care of offset between i and stored table */
         pitch_table[++i][k] = floor(pitch + 0.5);
         }
      }
    }

char *name_of_note(i)
int i;
   {
   static char name[4];

	switch(i)
		{
	case NO_NOTE:
		return "   ";
	default:
      name[0] = note_template[(i+8)%12 * 2];
      name[1] = note_template[(i+8)%12 * 2 +1];
      name[2] = '0' + (i-4)/12;
		name[3] = 0;
      return name;
      }
   }
   
