-- ScoreImplA.mesa
-- Author: John Maxwell
-- Last Edited by: Maxwell, November 22, 1983 12:23 pm
DIRECTORY
Beam USING [SetStems],
-- Device USING [Handle],
Event USING [SetStave, Staves],
Graphics USING [Context, MakeFont, SetCP, SetFat],
MusicDefs,
Piece USING [AddEvent, Overflow, RemoveEvent],
-- PressDefs USING [PressFileDescriptor, WritePage],
-- PressDeviceImpl USING [DataRef],
Score USING [GetKey, Look],
Selection USING [AddLine],
Sheet USING [FindLine, FindSection, FindStaves, Map, NormalPitch, OctavaHeight, Reset],
String USING [AppendDecimal],
Utility;
ScoreImplA: PROGRAM
IMPORTS Beam, Event, Graphics, MusicDefs, Piece, Score, Selection, Sheet, String, Utility
EXPORTS MusicDefs, Score
-- SHARES PressDeviceImpl -- =
BEGIN
OPEN Graphics, MusicDefs, Score, Utility;
Error: PUBLIC SIGNAL[s: STRING] = CODE;
-- ****************************************************************************
-- cache
-- ****************************************************************************
-- cache: PUBLIC ARRAY [0..maxCacheLength) OF EventPTR;
-- cacheLength: PUBLIC CARDINAL ← 0;
-- currentKey: INTEGER ← 0;
-- keyStart, keyStop: Time ← 0;
BuildCache: PUBLIC PROCEDURE[score: ScorePTR, time: Time] =
BEGIN
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];
[] ← GetMetrenome[score, time];
END;
Initialize: PUBLIC PROC[score: ScorePTR, context: Graphics.Context] =
BEGIN
score.sheet.context ← context;
[] ← Graphics.SetFat[context, TRUE];
music ← Graphics.MakeFont["music5"];
text ← Graphics.MakeFont["TimesRoman10"];
Utility.SetFont[context, music, 8];
-- pianoroll
score.style[0] ← zone.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] ← zone.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] ← zone.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] ← zone.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] ← zone.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] ← zone.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] ← zone.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];
END;
-- ****************************************************************************
-- key
-- ****************************************************************************
SetKey: PUBLIC PROCEDURE[score: ScorePTR, key: INTEGER, time1, time2: Time] =
BEGIN ENABLE Piece.Overflow => IF score = old THEN score ← new;
oldKey: INTEGER;
keySig: LONG POINTER TO EventRec.keySignature ← 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 ← zone.NEW[EventRec.keySignature];
keySig.time ← time1;
keySig.key ← key;
score ← Piece.AddEvent[score, keySig]};
-- put back the old signature
IF oldKey # key AND time2 < EndOfScore[score] THEN {
keySig ← zone.NEW[EventRec.keySignature];
keySig.time ← time2;
keySig.key ← oldKey;
score ← Piece.AddEvent[score, keySig]};
score.cache.key1 ← score.cache.key2 ← NIL; -- invalidate cache
SetDirty[score, time1, LAST[Time]];
Sheet.Reset[score]; -- rebuild sheet
END;
GetKey: PUBLIC PROCEDURE[score: ScorePTR, t: Time] RETURNS[key: INTEGER ← 0] =
BEGIN -- 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
IF score.event[i].type # keySignature THEN LOOP;
IF score.event[i].time > t THEN {score.cache.key2 ← LOOPHOLE[score.event[i]]; EXIT};
score.cache.key1 ← LOOPHOLE[score.event[i]];
ENDLOOP;
IF score.cache.key1 # NIL THEN key ← score.cache.key1.key;
END;
-- ****************************************************************************
-- metrenome
-- ****************************************************************************
SetMetrenome: PUBLIC PROCEDURE[score: ScorePTR, m: INTEGER, time1, time2: Time] =
BEGIN ENABLE Piece.Overflow => IF score = old THEN score ← new;
metrenome: LONG POINTER TO EventRec.metrenome ← NIL;
IF time1 > time2 THEN RETURN;
SetDirty[score, time1, time1+50];
-- remove old metrenomes
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 # metrenome THEN LOOP;
SetDirty[score, time1, score.event[i].time+50];
Piece.RemoveEvent[score, score.event[i], TRUE];
-- Utility.FreeEvent[@sync];
ENDLOOP;
-- insert new one
metrenome ← zone.NEW[EventRec.metrenome];
metrenome.time ← time1;
metrenome.metrenome ← m;
score ← Piece.AddEvent[score, metrenome];
score.cache.met1 ← score.cache.met2 ← NIL; -- invalidates cache
END;
GetMetrenome: PUBLIC PROCEDURE[score: ScorePTR, t: Time] RETURNS[metrenome: INTEGER ← 128] =
BEGIN
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.metrenome];
score.cache.met1 ← score.cache.met2 ← NIL;
FOR i: CARDINAL IN [0..score.length) DO
IF score.event[i].type # metrenome THEN LOOP;
IF score.event[i].time > t THEN {score.cache.met2 ← LOOPHOLE[score.event[i]]; EXIT};
score.cache.met1 ← LOOPHOLE[score.event[i]];
ENDLOOP;
IF score.cache.met1 # NIL THEN metrenome ← score.cache.met1.metrenome;
END;
DrawMetrenome: PUBLIC PROCEDURE[score: ScorePTR, metrenome: INTEGER, time: Time] =
BEGIN
x, y: INTEGER;
string: STRING ← [10];
context: Graphics.Context ← score.sheet.context;
IF score.sheet.printing THEN RETURN;
[x, y] ← Sheet.Map[score.sheet, time, , 0];
Graphics.SetCP[context, x, y+40];
DrawChar[context, 't];
String.AppendDecimal[string, metrenome];
Utility.SetFont[context, text, 12];
DrawChar[context, '= ];
DrawString[context, string];
Utility.SetFont[context, music, 8];
END;
-- ****************************************************************************
-- time signature
-- ****************************************************************************
SetTimeSignature: PUBLIC PROCEDURE[score: ScorePTR, ts: TimeSignature, time1, time2: Time] =
BEGIN ENABLE Piece.Overflow => IF score = old THEN score ← new;
oldTS: TimeSignature ← [0, 0];
timeSig: LONG POINTER TO EventRec.timeSignature;
IF time1 > time2 THEN RETURN;
SetDirty[score, time1, time1+50];
-- remove old time signature
FOR i: CARDINAL IN [0..score.length) DO
IF score.event[i].time > time2 THEN EXIT;
IF score.event[i].type # timeSignature THEN LOOP;
IF score.event[i].time < time1 THEN {
timeSig ← LOOPHOLE[score.event[i]];
oldTS ← timeSig.ts; LOOP};
SetDirty[score, time1, score.event[i].time+50];
Piece.RemoveEvent[score, score.event[i], TRUE];
-- Utility.FreeEvent[@sync];
ENDLOOP;
-- insert new time signature
IF ts.top # 0 AND ts.bottom # 0 THEN {
timeSig ← zone.NEW[EventRec.timeSignature];
timeSig.time ← time1+10;
timeSig.ts ← ts;
score ← Piece.AddEvent[score, timeSig]};
IF oldTS.top # 0 AND time2 < EndOfScore[score] THEN {
timeSig ← zone.NEW[EventRec.timeSignature];
timeSig.time ← time2+10;
timeSig.ts ← oldTS;
score ← Piece.AddEvent[score, timeSig];
SetDirty[score, time1, time2]};
score.cache.ts1 ← score.cache.ts2 ← NIL; -- invalidate cache
END;
GetTimeSignature: PUBLIC PROCEDURE[score: ScorePTR, t: Time] RETURNS[ts: TimeSignature ← [4, 4]] =
BEGIN
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
IF score.event[i].type # timeSignature THEN LOOP;
IF score.event[i].time > t THEN {score.cache.ts2 ← LOOPHOLE[score.event[i]]; EXIT};
score.cache.ts1 ← LOOPHOLE[score.event[i]];
ENDLOOP;
IF score.cache.ts1 # NIL THEN ts ← score.cache.ts1.ts;
END;
DrawTimeSignature: PUBLIC PROCEDURE[score: ScorePTR, ts: TimeSignature, time: Time] =
BEGIN
x, y: INTEGER;
oldStaff: Staff;
context: Graphics.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;
Graphics.SetCP[context, x, y+8];
SELECT ts.bottom FROM
2 => DrawChar[context, 062C];
4 => DrawChar[context, 064C];
8 => DrawChar[context, 070C];
ENDCASE;
Graphics.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 => {
Graphics.SetCP[context, x-5, y+24];
DrawString[context, "12"]};
ENDCASE;
ENDLOOP;
END;
-- ****************************************************************************
-- procedures that change the staves attributes
-- ****************************************************************************
SetStyle: PUBLIC PROCEDURE[score: ScorePTR, index: INTEGER, t1, t2: Time] =
BEGIN 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
IF score.event[i].type # staves THEN LOOP;
IF Event.Staves[score.event[i]].staves # style THEN LOOP;
IF score.event[i].time >= t1-1 THEN EXIT;
old ← Event.Staves[score.event[i]].value;
ENDLOOP;
-- change all of the current staves to the new style
FOR i: CARDINAL DECREASING IN [0..score.length) DO
IF score.event[i].time > t2 THEN LOOP;
IF score.event[i].time < t1-5 THEN EXIT;
IF score.event[i].type # staves THEN LOOP;
IF Event.Staves[score.event[i]].staves # style THEN LOOP;
Event.Staves[score.event[i]].value ← index;
ENDLOOP;
-- insert new staves
IF old # index THEN {
IF sync # NIL THEN Piece.RemoveEvent[score, sync, TRUE];
style ← zone.NEW[EventRec.staves];
style↑ ← score.style[index]↑;
style.value ← index;
style.time ← t1;
score ← Piece.AddEvent[score, style]};
IF old # index AND old # -1 AND t2 # endOfScore THEN {
IF temp # NIL THEN Piece.RemoveEvent[score, temp, TRUE];
style ← zone.NEW[EventRec.staves];
style↑ ← score.style[old]↑;
style.value ← old;
style.time ← t2;
score ← 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];
END;
NearestMeasure: PROCEDURE[score: ScorePTR, time, delta: Time] RETURNS[EventPTR] =
BEGIN
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];
END;
-- style: PUBLIC ARRAY [0..20] OF Staves;
GetStyle: PUBLIC PROCEDURE[score: ScorePTR, time: Time] RETURNS[INTEGER] =
BEGIN
equal: BOOLEAN;
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];
END;
-- ****************************************************************************
-- clefs switches
-- ****************************************************************************
SetClef: PUBLIC PROCEDURE[score: ScorePTR, pitch, staff: INTEGER, time: Time] =
BEGIN ENABLE Piece.Overflow => IF score = old THEN score ← new;
staves: StavesPTR;
-- make up a sync
staves ← zone.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;
score ← Piece.AddEvent[score, staves];
Sheet.Reset[score];
END;
SetOctava: PUBLIC PROCEDURE[score: ScorePTR, pitch, staff, height: INTEGER, t1, t2: Time] =
BEGIN 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
IF score.event[i].time < t1 THEN LOOP;
IF score.event[i].time > t2 THEN EXIT;
IF score.event[i].type # staves THEN LOOP;
staves ← Event.Staves[score.event[i]];
current ← staves.staff[staff].pitch;
IF current = normal THEN LOOP;
score.flash ← TRUE; RETURN;
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 ← zone.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;
score ← Piece.AddEvent[score, staves];
-- add the second sync
staves ← zone.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;
score ← Piece.AddEvent[score, staves];
Sheet.Reset[score];
END;
END..