-- Author: John Maxwell
-- last modified: May 19, 1982 9: 11 am
-- Last Edited by: Maxwell, November 22, 1983 8:19 am

DIRECTORY
	MusicDefs: FROM "MusicDefs";

Sheet: DEFINITIONS IMPORTS MusicDefs = 
BEGIN
OPEN MusicDefs;

New: PROC[length: CARDINAL, old: SheetPTR ← NIL] RETURNS[sheet: SheetPTR];
Free: PROC[sheet: SheetPTR];

Reset: PROC[score: ScorePTR]; -- reconstructs the sheet from the score
Scale: PROC[score: ScorePTR, newScale: INTEGER]; -- causes a Reset
SetBegin: PROC[sheet: SheetPTR, now: Time];
Scroll: PROC[sheet: SheetPTR, lines: INTEGER]; -- "currentLine ← currentLine+lines" 

Draw: PROC[sheet: SheetPTR]; -- draws what is currently visible
DrawKey: PROC[sheet: SheetPTR, key, oldKey: INTEGER, time: Time];
DrawClef: PROC[sheet: SheetPTR, pitch, staff: INTEGER, time: Time];
DrawOctava: PROC[sheet: SheetPTR, pitch, staff, height: INTEGER, t1, t2: Time];
HiLite: PROC[sheet: SheetPTR, texture: CARDINAL, t1, t2: Time];

FindLine: PROC[sheet: SheetPTR, t: Time] RETURNS[INTEGER];
FindSection: PROC[sheet: SheetPTR, t: Time] RETURNS[INTEGER];

-- INLINE Procedures

FindStaves: PROC[sheet: SheetPTR, t: Time] RETURNS[StavesPTR] = INLINE
	{RETURN[sheet.section[FindSection[sheet, t]].staves]}; 

Last: PROC[sheet: SheetPTR, l: CARDINAL] RETURNS[CARDINAL] = INLINE
   {FOR i: CARDINAL IN [l..sheet.length-1) DO
	IF sheet.section[i].time # sheet.section[i+1].time THEN RETURN[i];
	ENDLOOP;
     RETURN[sheet.length-1]};

LineNumber: PROC[sheet: SheetPTR, l: CARDINAL] RETURNS[n: CARDINAL ← 1] = INLINE
   {FOR i: CARDINAL IN (0..l] DO
	IF sheet.section[i].y # sheet.section[i-1].y THEN n ← n+1;
	ENDLOOP};

NextLine: PROC[sheet: SheetPTR, l: CARDINAL] RETURNS[CARDINAL] = INLINE 
   {FOR i: CARDINAL IN (l..sheetLength) DO
	IF sheet.section[i].y = sheet.section[l].y THEN LOOP;
	RETURN[Last[sheet, i]];
	ENDLOOP;
    RETURN[sheet.length-1]}; 

NextStaff: PROC[sheet: SheetPTR, s: CARDINAL, t: Time] RETURNS[CARDINAL] = INLINE 
   {staves: StavesPTR = FindStaves[sheet, t];
    FOR i: CARDINAL IN (s..staves.length) DO
	IF staves.staff[i].y # staves.staff[s].y THEN RETURN[i];
	ENDLOOP;
    RETURN[s]}; 

NormalPitch: PROC[sheet: SheetPTR, staff: CARDINAL] RETURNS[INTEGER] = INLINE 
   {RETURN[IF staff IN [0..1] THEN 48 ELSE 27]}; 

PriorLine: PROC[sheet: SheetPTR, l: CARDINAL] RETURNS[CARDINAL] = INLINE 
   {FOR i: CARDINAL DECREASING IN [0..l) DO
	IF sheet.section[i].y # sheet.section[l].y THEN {l ← i; EXIT};
	ENDLOOP;
    FOR i: CARDINAL DECREASING IN [0..l) DO
	IF sheet.section[i].y # sheet.section[l].y THEN RETURN[Last[sheet, i+1]];
	ENDLOOP;
    RETURN[Last[sheet, 0]]};

PriorStaff: PROC[sheet: SheetPTR, s: CARDINAL, t: Time] RETURNS[CARDINAL] = INLINE 
   {staves: StavesPTR = FindStaves[sheet, t];
    FOR i: CARDINAL DECREASING IN [0..s) DO
	IF staves.staff[i].y # staves.staff[s].y THEN RETURN[i];
	ENDLOOP;
    RETURN[s]};

OctavaHeight: PROC[pitch, height: INTEGER] RETURNS[INTEGER] = INLINE
   {IF pitch = 60 AND height < 40 THEN RETURN[40];
    IF pitch = 60 AND height > 90 THEN RETURN[90];
    IF pitch = 15 AND height < -40 THEN RETURN[-40];
    IF pitch = 15 AND height > -8 THEN RETURN[-8];
    RETURN[height]};

-- *************************************************************************
-- mapping from sheet to screen and vice-versa
-- *************************************************************************
-- NotePTR => [Time, Pitch, Staff] => [Time, Height] => ScreenPoint; UNAMBIGUOUSLY!
-- [Time, Height] < = ScreenPoint; ambiguous when point is between lines
-- [Time, Pitch, Staff] < = [Time, Height];  ambiguous between staffs 
-- NotePTR < = [Time, Pitch, Staff]; ambiguous because of graphical adjustments

default: INTEGER = 1000; 

AlternateTime: PROC[sheet: SheetPTR, t1: Time, h1, lines: INTEGER] 
	RETURNS[time: Time, height: INTEGER]; -- points on the screen are ambiguous
Height: PROC[sheet: SheetPTR, t: Time, pitch: INTEGER ← default, staff: CARDINAL] 
	RETURNS[INTEGER]; -- defaults to the bottom of the staff
NearestStaff: PROC[sheet: SheetPTR, t: Time, height: INTEGER] 
	RETURNS[INTEGER];
NearestTime: PROC[sheet: SheetPTR, x, y: INTEGER ← default] 
	RETURNS[time: Time, height: INTEGER]; -- defaults to the position of the mouse
Map: PROC[sheet: SheetPTR, t: Time, pitch: INTEGER ← default, staff: CARDINAL] 
	RETURNS[x, y: INTEGER]; -- defaults to the bottom of the staff
MapHeight: PROC[sheet: SheetPTR, t: Time, height: INTEGER] 
	RETURNS[x, y: INTEGER];
MapNote: PROC[sheet: SheetPTR, n: NotePTR] 
	RETURNS[x, y: INTEGER];
Pitch: PROC[sheet: SheetPTR, t: Time, height: INTEGER, staff: CARDINAL] 
	RETURNS[INTEGER];
ScreenPoint: PROC[sheet: SheetPTR] RETURNS[x, y: INTEGER];  -- position of the mouse

END..