-- Author: John Maxwell -- Last Edited by: Maxwell, November 22, 1983 12:29 pm DIRECTORY Beam USING [Drawn], Event USING [Adjust, Draw, GetScoreIndex, Sync], Graphics USING [Context, DrawBox, DrawChar, GetBounds, SetCP, SetStipple], MusicDefs, Note USING [DrawTie, InVoice], Piece USING [NearestEvent, Overflow], Score USING [Draw, GetKey, Justify, LogicalToPhysical, ScalePhysical, SetStyle, ShowPitch], Selection USING [Draw, selection], Sheet USING [default, Draw, FindSection, FindStaves, Height, HiLite, MapNote, NearestTime, Scale, ScreenPoint, SetBegin]; ScoreImplB: PROGRAM IMPORTS Beam, Graphics, MusicDefs, Note, Piece, Score, Selection, Sheet, Event EXPORTS Score = BEGIN OPEN MusicDefs; -- **************************************************************************** -- drawing the score -- **************************************************************************** Draw: PUBLIC PROCEDURE[score: ScorePTR, erase: BOOLEAN] = BEGIN sheet: SheetPTR ← score.sheet; Selection.Draw[]; -- remove selection from screen IF erase THEN { SetBrush[score, white, opaque]; Graphics.DrawBox[sheet.context, Graphics.GetBounds[sheet.context]]; SetBrush[score, black, transparent]; Sheet.Draw[sheet]}; DrawInterval[score, sheet.begin, sheet.endTime]; Selection.Draw[]; END; Redraw: PUBLIC PROCEDURE[score: ScorePTR, t1, t2: Time] = BEGIN x, y: INTEGER; select: BOOLEAN; selection: SelectionPTR ← Selection.selection; SetBrush[score, black, transparent]; SELECT score.sheet.scale FROM 1 => IF t2-t1 > 700 THEN {Score.Draw[score]; RETURN}; 2 => IF t2-t1 > 2000 THEN {Score.Draw[score]; RETURN}; 4 => IF t2-t1 > 8000 THEN {Score.Draw[score]; RETURN}; ENDCASE; select ← selection.lineSelect AND selection.select2 > t1-40 AND selection.select1 < t2+50; IF select THEN Selection.Draw[]; -- remove selection from screen Sheet.HiLite[score.sheet, white, t1-20, t2+30]; DrawInterval[score, t1-30, t2+40]; IF select THEN Selection.Draw[]; IF selection.lineSelect THEN RETURN; SetBrush[score, black, invert]; FOR i: CARDINAL IN [0..selection.length) DO IF selection.note[i] = NIL THEN LOOP; IF selection[i].sync.time > t2+40 OR selection[i].sync.time < t1-30 THEN LOOP; IF ~Note.InVoice[selection[i], score.sheet.voice] THEN LOOP; [x, y] ← Sheet.MapNote[score.sheet, selection[i]]; Graphics.SetCP[score.sheet.context, x, y]; Graphics.DrawChar[score.sheet.context, 170C]; ENDLOOP; END; DrawInterval: PROCEDURE[score: ScorePTR, t1, t2: Time] = BEGIN n: NotePTR; staves: StavesPTR; j: CARDINAL ← 0; sheet: SheetPTR ← score.sheet; [] ← Beam.Drawn[score, NIL]; -- clears the beam cache SetBrush[score, black, transparent]; -- draw octava markings first staves ← Sheet.FindStaves[sheet, t1]; FOR i: CARDINAL IN [0..staves.length) DO IF staves.staff[i].pitch # 15 AND staves.staff[i].pitch # 60 THEN LOOP; FOR j: CARDINAL DECREASING IN [0..score.length) DO IF score.event[j].time >= t1 THEN LOOP; IF score.event[j].type # staves THEN LOOP; WITH ev: score.event[j] SELECT FROM staves => {IF ev.staves NOT IN [octava1..octava2] THEN LOOP; IF ev.value # i THEN LOOP; IF ev.staves = octava2 THEN EXIT}; ENDCASE => ERROR; Event.Draw[score, score.event[j]]; EXIT; ENDLOOP; ENDLOOP; -- pre-Adjust the first 10 syncs (beams may draw notes in syncs downstream) j ← 0; IF score.sheet.display = graphical THEN 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 = sync THEN { IF j = 10 THEN EXIT ELSE j ← j+1; Event.Adjust[score, Event.Sync[score.event[i]]]}; ENDLOOP; -- draw the syncs j ← 0; FOR i: CARDINAL IN [0..score.length) DO IF score.event[i].time < t1 THEN LOOP; IF score.event[i].time > t2 THEN {j ← i; EXIT}; IF score.sheet.display = graphical THEN { plus10: CARDINAL ← MIN[i+10, score.length-1]; IF score.event[plus10].type = sync THEN Event.Adjust[score, Event.Sync[score.event[plus10]]]}; Event.Draw[score, score.event[i]]; IF NOT sheet.printing AND AnyBug[] THEN EXIT; ENDLOOP; -- draw ties that go off the end of the section IF j # 0 THEN FOR i: CARDINAL IN [j..score.length) DO IF score.event[i].time > t2+200 THEN EXIT; IF score.event[i].type = sync THEN { sync: SyncPTR ← Event.Sync[score.event[i]]; FOR j: CARDINAL IN [0..sync.length) DO IF (n ← sync.note[j]) = NIL THEN EXIT; IF n.tie = NIL THEN LOOP; IF n.tie.sync.time > t2 THEN LOOP; IF ~Note.InVoice[n, score.sheet.voice] THEN Graphics.SetStipple[score.sheet.context, light] ELSE Graphics.SetStipple[score.sheet.context, black]; Note.DrawTie[score, n]; ENDLOOP}; ENDLOOP; [] ← Beam.Drawn[score, NIL]; END; -- **************************************************************************** -- printing the score -- **************************************************************************** Print: PUBLIC PROCEDURE[score: ScorePTR, splines: BOOLEAN] = {}; -- **************************************************************************** -- changing the view -- **************************************************************************** Look: PUBLIC PROCEDURE[score: ScorePTR, look: LookCommand, switch: BOOLEAN, n: INTEGER] = BEGIN ENABLE Piece.Overflow => IF score = old THEN score ← new; draw: BOOLEAN ← TRUE; SELECT look FROM accidental => score.sheet.accidental ← switch; hardcopy => { Sheet.Scale[score, IF switch THEN 2 ELSE 1]; score.sheet.hardcopy ← switch}; justified => { score.sheet.density ← n; Score.Justify[score, Selection.selection.select1, Selection.selection.select2]}; logical => Score.LogicalToPhysical[score, Selection.selection.select1, Selection.selection.select2]; noCarry => score.sheet.noCarry ← switch; notehead => score.sheet.notehead ← switch; overview => Overview[score, switch]; physical => Score.ScalePhysical[score, 512/n]; style => Score.SetStyle[score, n, 0, EndOfScore[score]]; sync => score.sheet.sync ← switch; voice => {score.sheet.voice ← IF ~switch THEN noVoice ELSE n}; ENDCASE; END; Overview: PROCEDURE[score: ScorePTR, switch: BOOLEAN] = BEGIN time: Time; x, y: INTEGER; SELECT TRUE FROM score.sheet.scale < 4 AND ~switch => {score.flash ← TRUE; RETURN}; score.sheet.scale > 2 AND switch => {score.flash ← TRUE; RETURN}; switch => {Sheet.Scale[score, 4]; Sheet.SetBegin[score.sheet, 0]}; YellowBug[] => { [x, y] ← Sheet.ScreenPoint[score.sheet]; time ← Sheet.NearestTime[score.sheet, x, y].time; Sheet.Scale[score, IF score.sheet.hardcopy THEN 2 ELSE 1]; Sheet.SetBegin[score.sheet, time]}; ENDCASE => { Sheet.Scale[score, IF score.sheet.hardcopy THEN 2 ELSE 1]; Sheet.SetBegin[score.sheet, 0]}; END; -- **************************************************************************** -- getting accidentals -- **************************************************************************** phi: ARRAY [0..12) OF INTEGER = [6, 1, -4, 3, -2, 5, 0, 7, 2, -3, 4, -1]; flatHeight: ARRAY[0..12) OF INTEGER = [0, 4, 8, 8, 12, 12, 16, 16, 20, 24, 24, 28]; sharpHeight: ARRAY[0..12) OF INTEGER = [0, 4, 4, 8, 8, 12, 12, 16, 20, 20, 24, 24]; GetAccidental: PUBLIC PROCEDURE[score: ScorePTR, n: NotePTR] RETURNS[Accidental] = BEGIN -- hack to set n.shown n.shown ← GetAccInternal[score, n]; RETURN[n.shown]; END; GetAccInternal: PROCEDURE[score: ScorePTR, n: NotePTR] RETURNS[Accidental] = INLINE BEGIN a: Accidental; prior: NotePTR; index: CARDINAL; pitch, key: INTEGER; normal: Accidental; noteHeight: INTEGER; IF NOT score.sheet.accidental THEN RETURN[inKey]; IF n.tie # NIL AND n.tie.pitch = n.pitch THEN RETURN[inKey]; IF n.show AND n.spelled = inKey THEN Error["show inKey?"]; IF n.show THEN RETURN[n.spelled]; IF score.sheet.noCarry THEN { IF n.spelled # inKey THEN RETURN[n.spelled]; a ← DefaultAcc[0, n.pitch]; IF a = natural THEN a ← inKey; RETURN[a]}; key ← Score.GetKey[score, n.sync.time]; pitch ← Score.ShowPitch[score.sheet, n.pitch, n.spelled, key]; noteHeight ← Sheet.Height[score.sheet, n.sync.time, pitch, n.staff]; index ← Event.GetScoreIndex[score, n.sync]; IF index = score.length THEN index ← EventIndex[score, n.sync.time]; prior ← PriorNote[score, pitch, noteHeight, index]; normal ← NormalAcc[key, pitch]; -- no prior note IF prior = NIL THEN SELECT TRUE FROM normal = inKey => RETURN[n.spelled]; n.spelled = inKey => RETURN[normal]; ENDCASE => RETURN[n.spelled]; -- n.spelled = normal => RETURN[inKey]; -- n.spelled # normal => RETURN[n.spelled]; -- ENDCASE; -- prior note IF prior.pitch = n.pitch THEN RETURN[inKey]; -- an accidental MUST be asserted IF n.spelled # inKey THEN RETURN[n.spelled]; IF normal # inKey THEN RETURN[normal]; IF Mod[n.pitch, 12] = 7 AND key < -5 THEN RETURN[flat]; IF Mod[n.pitch, 12] = 0 AND key < -6 THEN RETURN[flat]; IF Mod[n.pitch, 12] = 1 AND key > 5 THEN RETURN[sharp]; IF Mod[n.pitch, 12] = 8 AND key > 6 THEN RETURN[sharp]; normal ← NormalAcc[0, n.pitch]; IF normal = inKey THEN normal ← natural; IF key < 0 AND normal = sharp THEN normal ← flat; RETURN[normal]; END; EventIndex: PROCEDURE[score: ScorePTR, time: Time] RETURNS[s: CARDINAL] = INLINE BEGIN s ← Piece.NearestEvent[score, time]; IF score.event[s] # NIL AND score.event[s].time >= time THEN RETURN[s]; FOR i: CARDINAL DECREASING IN [0..s+10) DO IF score.event[i] = NIL THEN LOOP; IF score.event[i].time > time THEN LOOP; RETURN[i+1]; ENDLOOP; END; PriorNote: PUBLIC PROCEDURE[score: ScorePTR, pitch, height: INTEGER, index: CARDINAL] RETURNS[NotePTR] = BEGIN n: NotePTR; sync: SyncPTR; newPitch, key: INTEGER; FOR i: CARDINAL DECREASING IN [0..index) DO IF Measure[score.event[i]] THEN RETURN[NIL]; IF score.event[i].type # sync THEN LOOP; key ← Score.GetKey[score, score.event[i].time]; sync ← Event.Sync[score.event[i]]; FOR j: CARDINAL IN [0..sync.length) DO IF (n ← sync.note[j]) = NIL THEN EXIT; IF n.rest THEN LOOP; newPitch ← Score.ShowPitch[score.sheet, n.pitch, n.spelled, key]; IF NOT newPitch IN [pitch-2..pitch+2] THEN LOOP; IF Sheet.Height[score.sheet, score.event[i].time, newPitch, n.staff] # height THEN LOOP; RETURN[n]; ENDLOOP; ENDLOOP; RETURN[NIL]; END; assert: Accidental = LOOPHOLE[LOOPHOLE[LAST[Accidental], CARDINAL]+1]; NormalAcc: PUBLIC PROCEDURE[key, pitch: INTEGER] RETURNS[Accidental] = BEGIN strangeness: INTEGER ← phi[Mod[pitch, 12]]; -- I bet you don't really believe this works. SELECT TRUE FROM Mod[(key-strangeness), 12] > 4 => RETURN[inKey]; strangeness > 0 => RETURN[natural]; key < 0 => RETURN[flat]; ENDCASE => RETURN[sharp]; END; DefaultAcc: PROCEDURE[key, pitch: INTEGER] RETURNS[Accidental] = BEGIN SELECT NormalAcc[0, pitch] FROM inKey => RETURN[natural]; sharp => RETURN[IF key < 0 THEN flat ELSE sharp]; ENDCASE => ERROR; END; AddToPitch: PUBLIC PROCEDURE[key, pitch, delta: INTEGER] RETURNS[INTEGER] = BEGIN i: INTEGER; IF delta > 0 THEN FOR i IN (pitch..90] DO IF NormalAcc[key, i] = inKey THEN delta ← delta-1; IF delta = 0 THEN RETURN[i]; ENDLOOP ELSE FOR i DECREASING IN [0..pitch) DO IF NormalAcc[key, i] = inKey THEN delta ← delta+1; IF delta = 0 THEN RETURN[i]; ENDLOOP; ERROR; END; KeyHeight: PUBLIC PROCEDURE[key, pitch: INTEGER] RETURNS[INTEGER] = BEGIN i: CARDINAL ← Mod[pitch, 12]; SELECT TRUE FROM i = 7 AND key < -5 => RETURN[flatHeight[8]]; i = 0 AND key < -6 => RETURN[flatHeight[1]]; key < 0 => RETURN[flatHeight[i]]; i = 1 AND key > 5 => RETURN[sharpHeight[0]]; i = 8 AND key > 6 => RETURN[sharpHeight[7]]; ENDCASE => RETURN[sharpHeight[i]]; END; END . . . Print: PUBLIC PROCEDURE[splines: BOOLEAN] = BEGIN printAll: BOOLEAN; oldBegin: Time ← begin; ph: POINTER TO PressDefs.PressFileDescriptor; device: Device.Handle ← Utility.OpenPressDevice[splines]; ph ← LOOPHOLE[device.data, PressDeviceImpl.DataRef].pressHandle; printAll ← ~splines AND scale = 2; IF printAll THEN Sheet.SetBegin[0]; FOR i: CARDINAL IN [0..scoreLength) DO Event.Adjust[score.event[i]]; ENDLOOP; WHILE TRUE DO Sheet.Draw[]; DrawInterval[begin, endTime]; IF endTime > EndOfScore[]-40 THEN EXIT; IF printAll THEN {Sheet.SetBegin[endTime]; PressDefs.WritePage[ph]} ELSE EXIT; ENDLOOP; Utility.ClosePressDevice[@device]; Sheet.SetBegin[oldBegin]; END;