-- 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..