/* StaffCalibration.c */
/*****************************************************************************/
/*                                                                           */
/*    Out Of Phase:  Digital Music Synthesis on General Purpose Computers    */
/*    Copyright (C) 1994  Thomas R. Lawrence                                 */
/*                                                                           */
/*    This program is free software; you can redistribute it and/or modify   */
/*    it under the terms of the GNU General Public License as published by   */
/*    the Free Software Foundation; either version 2 of the License, or      */
/*    (at your option) any later version.                                    */
/*                                                                           */
/*    This program is distributed in the hope that it will be useful,        */
/*    but WITHOUT ANY WARRANTY; without even the implied warranty of         */
/*    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the          */
/*    GNU General Public License for more details.                           */
/*                                                                           */
/*    You should have received a copy of the GNU General Public License      */
/*    along with this program; if not, write to the Free Software            */
/*    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.              */
/*                                                                           */
/*    Thomas R. Lawrence can be reached at tomlaw@world.std.com.             */
/*                                                                           */
/*****************************************************************************/

#include "MiscInfo.h"
#include "Audit.h"
#include "Debug.h"
#include "Definitions.h"

#include "StaffCalibration.h"
#include "Frequency.h"
#include "NoteObject.h"
#include "Memory.h"


/* a note line every 4 scan lines (a note line is 1/2 of a staff line, or */
/* a line on which a note may be plotted) */
#define STAFFSEPARATION (4)

/* total number of pixels that are in the drawing range */
#define TOTALPIXELS ((NUMNOTES / 12) * 7 * STAFFSEPARATION)


/* get the maximum number of vertical pixels needed to represent score range */
OrdType						MaxVerticalSize(void)
	{
		return TOTALPIXELS;
	}


/* get the pixel offset of the center note */
OrdType						GetCenterNotePixel(void)
	{
		return (TOTALPIXELS - 1) - ((CENTERNOTE / 12) * (7 * STAFFSEPARATION));
	}


/* convert pitch to vertical pixel offset. */
OrdType						ConvertPitchToPixel(short HalfStep, unsigned short SharpFlatFlags)
	{
		int							Octave;
		OrdType					NoteOffset;

		ERROR((SharpFlatFlags & ~(eFlatModifier | eSharpModifier)) != 0,PRERR(ForceAbort,
			"ConvertPitchToPixel:  extraneous bits in SharpFlatFlags"));
		ERROR((HalfStep < 0) || (HalfStep >= NUMNOTES - 1),PRERR(ForceAbort,
			"ConvertPitchToPixel:  pitch index is out of range"));
		Octave = HalfStep / 12;
		switch (HalfStep % 12)
			{
				case 0: /* B#/C */
					if ((SharpFlatFlags & eSharpModifier) != 0)
						{
							NoteOffset = -1 * STAFFSEPARATION;
						}
					else
						{
							NoteOffset = 0 * STAFFSEPARATION;
						}
					break;
				case 1: /* C#/Db */
					if ((SharpFlatFlags & eSharpModifier) != 0)
						{
							NoteOffset = 0 * STAFFSEPARATION;
						}
					 else
						{
							NoteOffset = 1 * STAFFSEPARATION;
						}
					break;
				case 2: /* D */
					NoteOffset = 1 * STAFFSEPARATION;
					break;
				case 3: /* D#/Eb */
					if ((SharpFlatFlags & eSharpModifier) != 0)
						{
							NoteOffset = 1 * STAFFSEPARATION;
						}
					 else
						{
							NoteOffset = 2 * STAFFSEPARATION;
						}
					break;
				case 4: /* E/Fb */
					if ((SharpFlatFlags & eFlatModifier) != 0)
						{
							NoteOffset = 3 * STAFFSEPARATION;
						}
					 else
						{
							NoteOffset = 2 * STAFFSEPARATION;
						}
					break;
				case 5: /* E#/F */
					if ((SharpFlatFlags & eSharpModifier) != 0)
						{
							NoteOffset = 2 * STAFFSEPARATION;
						}
					 else
						{
							NoteOffset = 3 * STAFFSEPARATION;
						}
					break;
				case 6: /* F#/Gb */
					if ((SharpFlatFlags & eSharpModifier) != 0)
						{
							NoteOffset = 3 * STAFFSEPARATION;
						}
					 else
						{
							NoteOffset = 4 * STAFFSEPARATION;
						}
					break;
				case 7: /* G */
					NoteOffset = 4 * STAFFSEPARATION;
					break;
				case 8: /* G#/Ab */
					if ((SharpFlatFlags & eSharpModifier) != 0)
						{
							NoteOffset = 4 * STAFFSEPARATION;
						}
					 else
						{
							NoteOffset = 5 * STAFFSEPARATION;
						}
					break;
				case 9: /* A */
					NoteOffset = 5 * STAFFSEPARATION;
					break;
				case 10: /* A#/Bb */
					if ((SharpFlatFlags & eSharpModifier) != 0)
						{
							NoteOffset = 5 * STAFFSEPARATION;
						}
					 else
						{
							NoteOffset = 6 * STAFFSEPARATION;
						}
					break;
				case 11: /* B/Cb */
					if ((SharpFlatFlags & eFlatModifier) != 0)
						{
							NoteOffset = 7 * STAFFSEPARATION;
						}
					 else
						{
							NoteOffset = 6 * STAFFSEPARATION;
						}
					break;
			}
		NoteOffset += 7 * STAFFSEPARATION * Octave;
		return (TOTALPIXELS - 1) - NoteOffset;
	}


static OrdType		OneOctaveHalfStepTable[] =
	{
		0, /* pixel 0 == C */
		2, /* pixel 4 == D */
		4, /* pixel 8 == E */
		5, /* pixel 12 == F */
		7, /* pixel 16 == G */
		9, /* pixel 20 == A */
		11 /* pixel 24 == B */
	};
#define OneOctaveHalfStepSize (sizeof(OneOctaveHalfStepTable)\
					/ sizeof(OneOctaveHalfStepTable[0]))


/* convert pixel offset to halfstep value */
short							ConvertPixelToPitch(OrdType Pixel)
	{
		short						OctaveCount;
		short						ReturnValue;

		Pixel = ((TOTALPIXELS - 1) - Pixel + 1) / STAFFSEPARATION;
		if (Pixel < 0)
			{
				Pixel = 0;
			}
		OctaveCount = 0;
		while (Pixel >= OneOctaveHalfStepSize)
			{
				Pixel -= OneOctaveHalfStepSize;
				OctaveCount += 1;
			}
		ReturnValue = OctaveCount * 12 + OneOctaveHalfStepTable[Pixel];
		if (ReturnValue > NUMNOTES - 1)
			{
				ReturnValue = NUMNOTES - 1;
			}
		return ReturnValue;
	}


#define MajorStaffSize (10)
static short			MajorStaffList[MajorStaffSize] =
	{
		CENTERNOTE + 4, /* E */
		CENTERNOTE + 7, /* G */
		CENTERNOTE + 11, /* B */
		CENTERNOTE + 14, /* D */
		CENTERNOTE + 17, /* F */
		CENTERNOTE - 4, /* A */
		CENTERNOTE - 7, /* F */
		CENTERNOTE - 11, /* D */
		CENTERNOTE - 14, /* B */
		CENTERNOTE - 17 /* G */
	};


/* get statically allocated list of major staff lines (10 elements in list) */
short*						GetMajorStaffList(void)
	{
		return MajorStaffList;
	}


/* get length of staff list */
long							GetMajorStaffListLength(void)
	{
		return MajorStaffSize;
	}


#define TwoOctaveStaffSize (7)
static OrdType		TwoOctaveStaffTable[TwoOctaveStaffSize] =
	{
		0, /* C0 */
		4, /* E0 */
		7, /* G0 */
		11, /* B0 */
		14, /* D1 */
		17, /* F1 */
		21 /* A1 */
	};


/* append the value to the array.  if it fails, then the array is disposed */
/* and NIL is returned */
static short*			AppendArray(short* Array, short Value)
	{
		short*						NewArray;
		long							Size;

		Size = PtrSize((char*)Array);
		NewArray = (short*)ResizePtr((char*)Array,Size + sizeof(short));
		if (NewArray == NIL)
			{
				ReleasePtr((char*)Array);
				return NIL;
			}
		NewArray[Size / sizeof(short)] = Value;
		return NewArray;
	}


/* get dynamically allocated list of minor staff lines */
short*						GetMinorStaffList(void)
	{
		short							HalfStep;
		short*						Table;

		Table = (short*)AllocPtrCanFail(0,"GetMinorStaffList");
		if (Table == NIL)
			{
				return NIL;
			}
		for (HalfStep = 0; HalfStep < NUMNOTES; HalfStep += 1)
			{

				if ((HalfStep < CENTERNOTE - 17) || (HalfStep > CENTERNOTE + 17))
					{
						short							TwoOctRelative;
						int								Scan;

						TwoOctRelative = HalfStep - CENTERNOTE;
						while (TwoOctRelative < 0)
							{
								TwoOctRelative += 24;
							}
						while (TwoOctRelative >= 24)
							{
								TwoOctRelative -= 24;
							}
						for (Scan = 0; Scan < TwoOctaveStaffSize; Scan += 1)
							{
								if (TwoOctRelative == TwoOctaveStaffTable[Scan])
									{
										Table = AppendArray(Table,HalfStep);
										if (Table == NIL)
											{
												return NIL;
											}
									}
							}
					}
			}
		return Table;
	}


/* set up note */
void							SetUpNoteInfo(short* Pitch, unsigned long* SharpFlatThing,
										MyBoolean Sharp, MyBoolean Flat, OrdType Pixel)
	{
		ERROR((Pitch == NIL) || (SharpFlatThing == NIL),PRERR(ForceAbort,
			"SetUpNoteInfo:  output parameter is NIL"));
		*SharpFlatThing = 0;
		*Pitch = ConvertPixelToPitch(Pixel);
		if (Sharp)
			{
				switch (*Pitch % 12)
					{
						case 0: /* C */
						case 2: /* D */
						case 5: /* F */
						case 7: /* G */
						case 9: /* A */
							*Pitch += 1;
							*SharpFlatThing |= eSharpModifier;
							break;
						case 4: /* E */
						case 11: /* B */
							*Pitch += 1;
							break;
						default:
							/* ? */
							break;
					}
			}
		if (Flat)
			{
				switch (*Pitch % 12)
					{
						case 2: /* D */
						case 4: /* E */
						case 7: /* G */
						case 9: /* A */
						case 11: /* B */
							*Pitch -= 1;
							*SharpFlatThing |= eFlatModifier;
							break;
						case 0: /* C */
						case 5: /* F */
							*Pitch -= 1;
							break;
						default:
							/* ? */
							break;
					}
			}
		if (*Pitch < 0)
			{
				*Pitch = 0;
			}
		if (*Pitch > NUMNOTES - 1)
			{
				*Pitch = NUMNOTES - 1;
			}
	}
