ScoreImplA.mesa
Copyright (C) 1983, 1984 Xerox Corporation. All rights reserved.
Author: John Maxwell
Last Edited by: Maxwell, November 22, 1983 12:23 pm
Last Edited by: Doug Wyatt, June 18, 1984 11:45:48 am PDT
DIRECTORY
Beam USING [SetStems],
Convert USING [RopeFromInt],
Event USING [SetStave],
Imager USING [Context],
MusicDefs,
Piece USING [AddEvent, RemoveEvent],
Rope USING [ROPE],
Score USING [GetKey, Look],
Selection USING [AddLine],
Sheet USING [FindLine, FindSection, FindStaves, Map, NormalPitch, OctavaHeight, Reset],
Utility;
ScoreImplA: CEDAR PROGRAM
IMPORTS Beam, Convert, Event, MusicDefs, Piece, Score, Selection, Sheet, Utility
EXPORTS MusicDefs, Score
= BEGIN OPEN MusicDefs, Score, Utility;
Error: PUBLIC SIGNAL[s: Rope.ROPE] = CODE;
ShowPitch: PUBLIC PROC[sheet: SheetPTR, pitch: INTEGER, spelled: Accidental, key: INTEGER] RETURNS[INTEGER] ~ {
IF NOT sheet.accidental THEN RETURN[pitch];
IF spelled = inKey THEN RETURN[pitch];
SELECT spelled FROM
doubleSharp => pitch ← pitch-2;
sharp => pitch ← pitch-1;
natural => pitch ← pitch-0;
flat => pitch ← pitch+1;
doubleFlat => pitch ← pitch+2;
ENDCASE => ERROR;
IF Mod[pitch, 12] = 7 AND key < -5 THEN RETURN[pitch-1];
IF Mod[pitch, 12] = 0 AND key < -6 THEN RETURN[pitch-1];
IF Mod[pitch, 12] = 1 AND key > 5 THEN RETURN[pitch+1];
IF Mod[pitch, 12] = 8 AND key > 6 THEN RETURN[pitch+1];
RETURN[pitch];
};
****************************************************************************
cache
****************************************************************************
cache: PUBLIC ARRAY [0..maxCacheLength) OF EventPTR;
cacheLength: PUBLIC CARDINAL ← 0;
currentKey: INTEGER ← 0;
keyStart, keyStop: Time ← 0;
BuildCache: PUBLIC PROC[score: ScorePTR, time: Time] = {
score.cache.key1 ← score.cache.key2 ← NIL;
score.cache.met1 ← score.cache.met2 ← NIL;
score.cache.ts1 ← score.cache.ts2 ← NIL;
[] ← GetKey[score, time];
[] ← GetTimeSignature[score, time];
[] ← GetMetronome[score, time];
};
Initialize: PUBLIC PROC[score: ScorePTR, context: Imager.Context] = {
score.sheet.context ← context;
Utility.SetFont[context, music, 8];
pianoroll
score.style[0] ← NEW[EventRec.staves];
score.style[0].height ← 0;
score.style[0].offset ← 60;
score.style[0].staff ← [[72, -32], [48, -88], [27, -136], [3, -196]];
one staff
score.style[1] ← NEW[EventRec.staves];
score.style[1].height ← 0;
score.style[1].offset ← 80;
score.style[1].staff ← [[48, -32], [48, -32], [48, -32], [48, -32]];
two staffs
score.style[2] ← NEW[EventRec.staves];
score.style[2].height ← 0;
score.style[2].offset ← 85;
score.style[2].staff ← [[48, -32], [48, -32], [27, -125], [27, -125]];
two staffs
score.style[12] ← NEW[EventRec.staves];
score.style[12].height ← 0;
score.style[12].offset ← 85;
score.style[12].staff ← [[48, -32], [48, -32], [27, -150], [27, -150]];
three staffs
score.style[3] ← NEW[EventRec.staves];
score.style[3].height ← 0;
score.style[3].offset ← 75;
score.style[3].staff ← [[48, -32], [48, -32], [27, -110], [27, -185]];
three staffs
score.style[13] ← NEW[EventRec.staves];
score.style[13].height ← 0;
score.style[13].offset ← 75;
score.style[13].staff ← [[48, -32], [48, -32], [27, -130], [27, -220]];
four staffs
score.style[4] ← NEW[EventRec.staves];
score.style[4].height ← 0;
score.style[4].offset ← 75;
score.style[4].staff ← [[48, -32], [48, -110], [27, -185], [27, -260]];
Score.Look[score, style, , 2];
Selection.AddLine[score, 0, 0];
};
****************************************************************************
key
****************************************************************************
SetKey: PUBLIC PROC[score: ScorePTR, key: INTEGER, time1, time2: Time] = {
ENABLE Piece.Overflow => IF score = old THEN score ← new;
oldKey: INTEGER;
keySig: KeySignaturePTR ← NIL;
IF time1 > time2 THEN RETURN;
IF key NOT IN [-7..7] THEN RETURN;
score.sheet.accidental ← TRUE;
oldKey ← Score.GetKey[score, time2];
FOR i: CARDINAL DECREASING IN [0..score.length) DO
IF score.event[i].time > time2 THEN LOOP;
IF score.event[i].time < time1 THEN EXIT;
IF score.event[i].type # keySignature THEN LOOP;
Piece.RemoveEvent[score, score.event[i], TRUE];
Utility.FreeEvent[@score.event[i]];
ENDLOOP;
put in the new key signature
IF key # Score.GetKey[score, time1] THEN {
keySig ← NEW[EventRec.keySignature];
keySig.time ← time1;
keySig.key ← key;
Piece.AddEvent[score, keySig];
};
put back the old signature
IF oldKey # key AND time2 < EndOfScore[score] THEN {
keySig ← NEW[EventRec.keySignature];
keySig.time ← time2;
keySig.key ← oldKey;
Piece.AddEvent[score, keySig];
};
score.cache.key1 ← score.cache.key2 ← NIL; -- invalidate cache
SetDirty[score, time1, LAST[Time]];
Sheet.Reset[score]; -- rebuild sheet
};
GetKey: PUBLIC PROC[score: ScorePTR, t: Time] RETURNS[key: INTEGER ← 0] = {
caches the last key range
IF score.cache.key1 # NIL AND t >= score.cache.key1.time AND
(score.cache.key2 = NIL OR t < score.cache.key2.time) THEN RETURN[score.cache.key1.key];
score.cache.key1 ← score.cache.key2 ← NIL;
FOR i: CARDINAL IN [0..score.length) DO
WITH score.event[i] SELECT FROM
key: KeySignaturePTR => {
IF key.time > t THEN { score.cache.key2 ← key; EXIT };
score.cache.key1 ← key;
};
ENDCASE;
ENDLOOP;
IF score.cache.key1 # NIL THEN key ← score.cache.key1.key;
};
****************************************************************************
metronome
****************************************************************************
SetMetronome: PUBLIC PROC[score: ScorePTR, m: INTEGER, time1, time2: Time] = {
ENABLE Piece.Overflow => IF score = old THEN score ← new;
metronome: MetronomePTR ← NIL;
IF time1 > time2 THEN RETURN;
SetDirty[score, time1, time1+50];
remove old metronomes
FOR i: CARDINAL DECREASING IN [0..score.length) DO
IF score.event[i].time > time2 THEN LOOP;
IF score.event[i].time < time1 THEN EXIT;
IF score.event[i].type # metronome THEN LOOP;
SetDirty[score, time1, score.event[i].time+50];
Piece.RemoveEvent[score, score.event[i], TRUE];
Utility.FreeEvent[@sync];
ENDLOOP;
insert new one
metronome ← NEW[EventRec.metronome];
metronome.time ← time1;
metronome.metronome ← m;
Piece.AddEvent[score, metronome];
score.cache.met1 ← score.cache.met2 ← NIL; -- invalidates cache
};
GetMetronome: PUBLIC PROC[score: ScorePTR, t: Time] RETURNS[metronome: INTEGER ← 128] = {
IF score.cache.met1 # NIL AND t >= score.cache.met1.time AND
(score.cache.met2 = NIL OR t < score.cache.met2.time) THEN RETURN[score.cache.met1.metronome];
score.cache.met1 ← score.cache.met2 ← NIL;
FOR i: CARDINAL IN [0..score.length) DO
WITH score.event[i] SELECT FROM
met: MetronomePTR => {
IF met.time > t THEN { score.cache.met2 ← met; EXIT };
score.cache.met1 ← met;
};
ENDCASE;
ENDLOOP;
IF score.cache.met1 # NIL THEN metronome ← score.cache.met1.metronome;
};
DrawMetronome: PUBLIC PROC[score: ScorePTR, metronome: INTEGER, time: Time] = {
x, y: INTEGER;
context: Imager.Context ← score.sheet.context;
IF score.sheet.printing THEN RETURN;
[x, y] ← Sheet.Map[score.sheet, time, , 0];
Utility.SetCP[context, x, y+40];
DrawChar[context, 't];
Utility.SetFont[context, text, 12];
DrawChar[context, '= ];
DrawString[context, Convert.RopeFromInt[metronome]];
Utility.SetFont[context, music, 8];
};
****************************************************************************
time signature
****************************************************************************
SetTimeSignature: PUBLIC PROC[score: ScorePTR, ts: TimeSignature, time1, time2: Time] = {
ENABLE Piece.Overflow => IF score = old THEN score ← new;
oldTS: TimeSignature ← [0, 0];
timeSig: TimeSignaturePTR ← NIL;
IF time1 > time2 THEN RETURN;
SetDirty[score, time1, time1+50];
remove old time signature
FOR i: CARDINAL IN [0..score.length) DO
event: EventPTR ~ score.event[i];
IF event.time > time2 THEN EXIT;
WITH event SELECT FROM
timeSig: TimeSignaturePTR => {
IF timeSig.time < time1 THEN oldTS ← timeSig.ts
ELSE {
SetDirty[score, time1, timeSig.time+50];
Piece.RemoveEvent[score, timeSig, TRUE];
};
};
ENDCASE;
ENDLOOP;
insert new time signature
IF ts.top # 0 AND ts.bottom # 0 THEN {
timeSig ← NEW[EventRec.timeSignature];
timeSig.time ← time1+10;
timeSig.ts ← ts;
Piece.AddEvent[score, timeSig];
};
IF oldTS.top # 0 AND time2 < EndOfScore[score] THEN {
timeSig ← NEW[EventRec.timeSignature];
timeSig.time ← time2+10;
timeSig.ts ← oldTS;
Piece.AddEvent[score, timeSig];
SetDirty[score, time1, time2];
};
score.cache.ts1 ← score.cache.ts2 ← NIL; -- invalidate cache
};
GetTimeSignature: PUBLIC PROC[score: ScorePTR, t: Time]
RETURNS[ts: TimeSignature ← [4, 4]] = {
IF score.cache.ts1 # NIL AND t >= score.cache.ts1.time AND
(score.cache.ts2 = NIL OR t < score.cache.ts2.time) THEN RETURN[score.cache.ts1.ts];
score.cache.ts1 ← score.cache.ts2 ← NIL;
FOR i: CARDINAL IN [0..score.length) DO
WITH score.event[i] SELECT FROM
timeSig: TimeSignaturePTR => {
IF timeSig.time > t THEN { score.cache.ts2 ← timeSig; EXIT };
score.cache.ts1 ← timeSig;
};
ENDCASE;
ENDLOOP;
IF score.cache.ts1 # NIL THEN ts ← score.cache.ts1.ts;
};
DrawTimeSignature: PUBLIC PROC[score: ScorePTR, ts: TimeSignature, time: Time] = {
x, y: INTEGER;
oldStaff: Staff;
context: Imager.Context ← score.sheet.context;
staves: StavesPTR = Sheet.FindStaves[score.sheet, time];
FOR i: CARDINAL IN [0..staves.length) DO
IF staves.staff[i] = oldStaff THEN LOOP;
oldStaff ← staves.staff[i];
[x, y] ← Sheet.Map[score.sheet, time, , i];
x ← x-5;
Utility.SetCP[context, x, y+8];
SELECT ts.bottom FROM
2 => DrawChar[context, 062C];
4 => DrawChar[context, 064C];
8 => DrawChar[context, 070C];
ENDCASE;
Utility.SetCP[context, x, y+24];
SELECT ts.top FROM
1 => DrawChar[context, 061C];
2 => DrawChar[context, 062C];
3 => DrawChar[context, 063C];
4 => DrawChar[context, 064C];
5 => DrawChar[context, 065C];
6 => DrawChar[context, 066C];
7 => DrawChar[context, 067C];
8 => DrawChar[context, 070C];
9 => DrawChar[context, 071C];
12 => {
Utility.SetCP[context, x-5, y+24];
DrawString[context, "12"]};
ENDCASE;
ENDLOOP;
};
****************************************************************************
procedures that change the staves attributes
****************************************************************************
SetStyle: PUBLIC PROC[score: ScorePTR, index: INTEGER, t1, t2: Time] = {
ENABLE Piece.Overflow => IF score = old THEN score ← new;
old: INTEGER ← -1;
style: StavesPTR ← NIL;
temp, sync: EventPTR ← NIL;
endOfScore: Time = EndOfScore[score];
IF index NOT IN [0..20] THEN {score.flash ← TRUE; RETURN};
IF score.style[index] = NIL THEN {score.flash ← TRUE; RETURN};
SetDirty[score, t1, EndOfScore[score]];
sync ← NearestMeasure[score, t1, 10];
temp ← NearestMeasure[score, t2, 10];
IF sync # NIL THEN t1 ← sync.time+1;
IF temp # NIL THEN t2 ← temp.time-1;
get the old style
FOR i: CARDINAL IN [0..score.length) DO
WITH score.event[i] SELECT FROM
staves: StavesPTR => IF staves.staves=style THEN {
IF staves.time >= t1-1 THEN EXIT; old ← staves.value; };
ENDCASE;
ENDLOOP;
change all of the current staves to the new style
FOR i: CARDINAL DECREASING IN [0..score.length) DO
event: EventPTR ~ score.event[i];
IF event.time > t2 THEN LOOP;
IF event.time < t1-5 THEN EXIT;
WITH event SELECT FROM
staves: StavesPTR => IF staves.staves=style THEN staves.value ← index;
ENDCASE;
ENDLOOP;
insert new staves
IF old # index THEN {
IF sync # NIL THEN Piece.RemoveEvent[score, sync, TRUE];
style ← NEW[EventRec.staves];
style^ ← score.style[index]^;
style.value ← index;
style.time ← t1;
Piece.AddEvent[score, style]};
IF old # index AND old # -1 AND t2 # endOfScore THEN {
IF temp # NIL THEN Piece.RemoveEvent[score, temp, TRUE];
style ← NEW[EventRec.staves];
style^ ← score.style[old]^;
style.value ← old;
style.time ← t2;
Piece.AddEvent[score, style]};
reset world
Sheet.Reset[score];
FOR i: CARDINAL IN [0..score.beamHeap.length) DO
Beam.SetStems[score.sheet, score.beamHeap.beam[i]];
ENDLOOP;
Selection.AddLine[score, t1, t2];
};
NearestMeasure: PROC[score: ScorePTR, time, delta: Time] RETURNS[EventPTR] = {
s: EventPTR ← NIL;
FOR i: CARDINAL IN [0..score.length) DO
IF ~Measure[score.event[i]] THEN LOOP;
IF ABS[score.event[i].time-time] > delta THEN LOOP;
delta ← ABS[score.event[i].time-time];
s ← score.event[i];
ENDLOOP;
RETURN[s];
};
style: PUBLIC ARRAY [0..20] OF Staves;
GetStyle: PUBLIC PROC[score: ScorePTR, time: Time] RETURNS[INTEGER] = {
equal: BOOL;
sheet: SheetPTR ← score.sheet;
staves: StavesPTR = sheet.section[Sheet.FindLine[sheet, time]].staves;
IF staves = NIL THEN RETURN[-1];
FOR i: CARDINAL IN [0..20] DO
IF score.style[i].length # staves.length THEN LOOP;
equal ← TRUE;
FOR j: CARDINAL IN [0..score.style[i].length) DO
IF score.style[i].staff[j].y # staves.staff[j].y THEN {equal ← FALSE; EXIT};
ENDLOOP;
IF equal THEN RETURN[i];
ENDLOOP;
RETURN[-1];
};
****************************************************************************
clefs switches
****************************************************************************
SetClef: PUBLIC PROC[score: ScorePTR, pitch, staff: INTEGER, time: Time] = {
ENABLE Piece.Overflow => IF score = old THEN score ← new;
staves: StavesPTR;
make up a sync
staves ← NEW[EventRec.staves];
Event.SetStave[score, staves, Sheet.FindStaves[score.sheet, time]];
staves.staff[staff].pitch ← pitch;
staves.staves ← clef;
staves.time ← time;
staves.value ← staff;
Piece.AddEvent[score, staves];
Sheet.Reset[score];
};
SetOctava: PUBLIC PROC[score: ScorePTR, pitch, staff, height: INTEGER, t1, t2: Time] = {
ENABLE Piece.Overflow => IF score = old THEN score ← new;
s: EventPTR ← NIL;
current: INTEGER;
section: CARDINAL;
staves: StavesPTR;
sheet: SheetPTR ← score.sheet;
normal: INTEGER = Sheet.NormalPitch[sheet, staff];
IF t1 >= t2 THEN RETURN;
check to make sure that the staff is clear
FOR i: CARDINAL IN [0..score.length) DO
event: EventPTR ~ score.event[i];
IF event.time < t1 THEN LOOP;
IF event.time > t2 THEN EXIT;
WITH event SELECT FROM
staves: StavesPTR => {
current ← staves.staff[staff].pitch;
IF current#normal THEN { score.flash ← TRUE; RETURN };
};
ENDCASE;
ENDLOOP;
section ← Sheet.FindSection[sheet, t1];
IF sheet.section[section].staves.staff[staff].pitch # normal THEN {score.flash ← TRUE; RETURN};
add the first sync
staves ← NEW[EventRec.staves];
Event.SetStave[score, staves, sheet.section[section].staves];
staves.staff[staff].pitch ← pitch;
staves.height ← Sheet.OctavaHeight[pitch, height];
staves.staves ← octava1;
staves.time ← t1;
staves.value ← staff;
Piece.AddEvent[score, staves];
add the second sync
staves ← NEW[EventRec.staves];
Event.SetStave[score, staves, Sheet.FindStaves[sheet, t2]];
staves.staff[staff].pitch ← normal;
staves.staves ← octava2;
staves.time ← t2;
staves.value ← staff;
Piece.AddEvent[score, staves];
Sheet.Reset[score];
};
END.