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