SheetImplB.mesa
Copyright (C) 1983, 1984 Xerox Corporation. All rights reserved.
Author: John Maxwell
Last Edited by: Maxwell, November 14, 1983 10:06 am
Last Edited by: Doug Wyatt, June 16, 1984 12:40:50 pm PDT
DIRECTORY
MusicDefs,
Note USING [Delta],
Real USING [FixI],
Score USING [KeyHeight, ShowPitch],
Sheet USING [default, FindLine, FindSection, FindStaves, Height, NextLine, PriorLine];
SheetImplB: CEDAR PROGRAM
IMPORTS MusicDefs, Note, Real, Score, Sheet
EXPORTS Sheet
= BEGIN OPEN MusicDefs, Sheet;
****************************************************************************
procedures that map from note to screen
****************************************************************************
MapNote: PUBLIC PROC[sheet: SheetPTR, n: NotePTR]
RETURNS[x, y: INTEGER] = {
l: Section;
show: INTEGER;
t: Time ← n.sync.time;
IF n.rest AND n.value = whole THEN t ← t-7;
l ← sheet[FindSection[sheet, t]];
x ← t-l.time+l.x+Note.Delta[sheet, n];
show ← Score.ShowPitch[sheet, n.pitch, n.spelled, l.key];
RETURN[x, l.y-sheet.top+PitchHeight[sheet, t, show, l.key, l.staves.staff[n.staff]]];
};
Map: PUBLIC PROC[sheet: SheetPTR, t: Time, pitch: INTEGER, staff: CARDINAL]
RETURNS[x, y: INTEGER] = {
l: Section ← sheet[FindSection[sheet, t]];
RETURN[t-l.time+l.x, l.y-sheet.top + PitchHeight[sheet, t, pitch, l.key, l.staves.staff[staff]]];
};
MapHeight: PUBLIC PROC[sheet: SheetPTR, t: Time, height: INTEGER]
RETURNS[x, y: INTEGER] = {
l: Section ← sheet[FindSection[sheet, t]];
RETURN[t-l.time+l.x, l.y-sheet.top + height];
};
Height: PUBLIC PROC[sheet: SheetPTR, t: Time, pitch: INTEGER, staff: CARDINAL]
RETURNS[INTEGER] = {
l: Section ← sheet[FindSection[sheet, t]];
RETURN[PitchHeight[sheet, t, pitch, l.key, l.staves.staff[staff]]];
};
Pitch: PUBLIC PROC[sheet: SheetPTR, t: Time, height: INTEGER, staff: CARDINAL]
RETURNS[INTEGER] = {
i: CARDINAL;
pitch: INTEGER ← 12;
octave: INTEGER;
l: Section ← sheet[FindSection[sheet, t]];
keyPitch: INTEGER ← (44+l.key*7) MOD 12;
height ← height - PitchHeight[sheet, t, keyPitch, l.key, l.staves.staff[staff]];
IF height < 0 THEN octave ← height/28-1 ELSE octave ← height/28;
octave ← height/28 - (IF height < 0 THEN 1 ELSE 0);
height ← Mod[height, 28];
FOR i IN [0..12) DO
IF pitchHeight[i] >= height THEN { pitch ← i; EXIT; };
ENDLOOP;
RETURN[pitch + keyPitch + 12*octave];
};
PitchHeight: PROC[sheet: SheetPTR, t: Time, pitch, key: INTEGER, staff: Staff]
RETURNS[INTEGER] = {
return the y height of the pitch relative to the top of the staves
if pitch > 100 use the staff's pitch
height, octave: INTEGER;
IF pitch > 100 THEN RETURN[staff.y];
IF NOT sheet.accidental THEN RETURN[CrackHeight[t, pitch, staff]];
octave ← (pitch-Mod[pitch, 12]-staff.pitch+Mod[staff.pitch, 12])/12;
height ← 28*octave + Score.KeyHeight[key, pitch] - Score.KeyHeight[key, staff.pitch];
RETURN[staff.y+height];
};
CrackHeight: PROC[t: Time, pitch: INTEGER, staff: Staff]
RETURNS[INTEGER] = {
pitches with accidentals are placed between lines and spaces
height: INTEGER;
octave: INTEGER;
ms: CARDINAL ← Mod[staff.pitch, 12];
mp: CARDINAL ← Mod[pitch, 12];
octave ← (pitch-mp-staff.pitch+ms)/12;
height ← 28*octave + crackHeight[mp] - crackHeight[ms];
RETURN[staff.y+height];
};
crackHeight: ARRAY[0..12) OF INTEGER = [0, 4, 6, 8, 10, 12, 14, 16, 20, 22, 24, 26];
pitchHeight: ARRAY [0..12) OF INTEGER = [0, 1, 4, 5, 8, 12, 13, 16, 17, 20, 21, 24];
****************************************************************************
procedures that map from screen back
****************************************************************************
ScreenPoint: PUBLIC PROC[sheet: SheetPTR] RETURNS[x, y: INTEGER] = {
rx, ry: REAL ← 0;
[rx, ry] ← Graphics.WorldToUser[sheet.context, mouse.x, 808 - mouse.y];
x ← Real.FixI[rx];
y ← Real.FixI[ry]+sheet.top;
};
NearestTime: PUBLIC PROC[sheet: SheetPTR, x, y: INTEGER]
RETURNS[time: Time, height: INTEGER] = {
line, next: CARDINAL ← 0;
IF x = default THEN [x, y] ← ScreenPoint[sheet];
WHILE sheet[next].y >= y DO
line ← next;
next ← Sheet.NextLine[sheet, line];
ENDLOOP;
IF y < ((sheet[line].y+Sheet.Height[sheet, sheet[line].time, , 3]+8)/2+sheet[next].y/2) THEN line ← next;
IF x < sheet[line].x THEN x ← sheet[line].x;
IF x > sheet.width THEN x ← sheet.width;
time ← sheet[line].time+ x- sheet[line].x;
height ← y- sheet[line].y;
};
AlternateTime: PUBLIC PROC[sheet: SheetPTR, t1: Time, h1: INTEGER, lines: INTEGER]
RETURNS[time: Time, height: INTEGER] = {
next, prior, l: CARDINAL;
time ← t1;
height ← h1;
l ← Sheet.FindLine[sheet, time];
IF lines > 0 THEN FOR i: CARDINAL IN [0..ABS[lines]) DO
next ← Sheet.NextLine[sheet, l];
time ← time + (sheet.width - sheet[next].x);
height ← height - (sheet[next].y - sheet[l].y);
l ← next;
ENDLOOP;
IF lines < 0 THEN FOR i: CARDINAL IN [0..ABS[lines]) DO
prior ← Sheet.PriorLine[sheet, l];
time ← time - (sheet.width - sheet[l].x);
height ← height + (sheet[l].y - sheet[prior].y);
l ← prior;
ENDLOOP;
RETURN[time, height];
};
NearestStaff: PUBLIC PROC[sheet: SheetPTR, t: Time, height: INTEGER]
RETURNS[CARDINAL] = {
j: CARDINAL ← 10;
staves: StavesPTR = Sheet.FindStaves[sheet, t];
FOR i: CARDINAL IN [0..staves.length) DO
IF i < 2 AND staves.staff[i] = staves.staff[i+1] THEN LOOP;
IF height < staves.staff[i].y THEN { j ← i; LOOP; };
IF j = 10 THEN RETURN[i];
IF height < (staves.staff[j].y+staves.staff[i].y+34)/2 THEN RETURN[i] ELSE RETURN[j];
ENDLOOP;
RETURN[staves.length-1];
};
END.
GetStaves: PROC[time: Time] RETURNS[StavesPTR] = {
sync: SyncPTR ← NIL;
FOR i: CARDINAL IN [0..cacheLength) DO
IF cache[i] = NIL THEN EXIT;
IF cache[i].type # staves THEN LOOP;
IF cache[i].time > time AND sync = NIL THEN sync ← cache[i];
IF cache[i].time > time THEN EXIT;
sync ← cache[i];
ENDLOOP;
IF sync = NIL THEN { Sheet.SetStaves[2, 0, EndOfScore[]]; RETURN[GetStaves[time]]; };
RETURN[LOOPHOLE[@sync.event]];
};
Reset: PUBLIC PROC = {
staves: Staves;
page: INTEGER ← 2;
i, j: CARDINAL ← 0;
sync: SyncPTR ← NIL;
time, break, top, cacheTime: Time ← 0;
height, x, sc, sheetHeight: INTEGER ← 0;
Score.BuildCache[];
MakeSheetConsistent[];
IF scale = 2 THEN sc ← 3 ELSE sc ← 2*scale;
FOR i IN [0..cacheLength] DO
IF i # cacheLength AND cache[i].type NOT IN SheetSwitch THEN LOOP;
IF i = cacheLength AND sync = NIL THEN RETURN;
IF i = cacheLength THEN cacheTime ← 400000 ELSE cacheTime ← cache[i].time;
IF sync = NIL THEN {sync ← cache[i]; staves ← LOOPHOLE[sync.event]};
WHILE time < cacheTime DO
sheet[j] ← [, height, 0, Key[time], time, LOOPHOLE[@sync.event]];
IF x = 0 AND top-height > (650*sc)/2 THEN {
top ← height; sheet[j].page ← page; page ← page+1; };
SELECT TRUE FROM
x = 0 => {x ← ABS[8*(sheet[j].key)]; IF x = 0 THEN x ← 8};
ENDCASE => x ← x + time-sheet[j-1].time;
sheet[j].x ← x;
IF time >= break THEN break ← break+staffLength-x;
time ← MIN[cacheTime, break];
IF time >= break THEN {
sheetHeight ← -staves.staff[staves.sl].y;
height ← height - sheetHeight - staves.offset;
x ← 0; };
j ← j+1; IF j = sheetLength THEN RETURN;
ENDLOOP;
IF i = cacheLength THEN EXIT;
sync ← cache[i];
staves ← LOOPHOLE[sync.event];
ENDLOOP;
FOR i: CARDINAL IN [0..beamHeapLength) DO
Beam.SetStems[beamHeap[i]];
ENDLOOP;
};