-- Author: John Maxwell -- Last Edited by: Maxwell, November 22, 1983 11:43 am DIRECTORY Beam USING [RemoveItem], Chord USING [RemoveNote], Event USING [GetScoreIndex, RemoveNote, Sync], Graphics USING [SetCP, DrawBox, SetStipple], Inline USING [LowHalf], MusicDefs, Note USING [default, DrawHead, DrawTie, Duration, GetBackTie, Width], Real USING [Fix], Score USING [ShowPitch], Selection USING [RemoveNote], Sheet USING [FindLine, Height, Map, MapNote], Utility USING [DrawChar, DrawCubic, DrawLine]; NoteImpl: PROGRAM IMPORTS Beam, Chord, Graphics, Inline, MusicDefs, Note, Real, Score, Selection, Sheet, Event, Utility EXPORTS Note = BEGIN OPEN Graphics, MusicDefs, Note, Utility; Error: SIGNAL; -- **************************************************************************** -- note procedures -- **************************************************************************** Free: PUBLIC PROCEDURE[score: ScorePTR, n: NotePTR] = BEGIN -- NILs out all pointers to n. -- a note can be pointed at by a sync, a chord, a beam, a tie, and the selection IF n.chord # NIL THEN Chord.RemoveNote[score, n.chord, n]; -- sets n.beam to NIL IF n.beam # NIL THEN Beam.RemoveItem[score, n.beam, [note[n]]]; IF n.tie # NIL THEN {n.tie.tied _ FALSE; n.tie _ NIL}; IF n.tied THEN {Note.GetBackTie[score, n].tie _ NIL; n.tied _ FALSE}; Event.RemoveNote[score, n.sync, n, TRUE]; -- frees s if s.length = 0. Selection.RemoveNote[n]; zone.FREE[@n]; END; SetAccidental: PUBLIC PROCEDURE[score: ScorePTR, n: NotePTR, a: Accidental] = BEGIN IF score.sheet.voice # noVoice AND n.voice # score.sheet.voice THEN RETURN; score.flash _ TRUE; SELECT n.pitch MOD 12 FROM 0 => IF a = doubleFlat OR a = sharp THEN RETURN; 1 => IF a = flat OR a = doubleSharp THEN RETURN; 2 => IF a = doubleFlat OR a = natural THEN RETURN; 3 => IF a = flat OR a = sharp THEN RETURN; 4 => IF a = doubleFlat OR a = natural OR a = doubleSharp THEN RETURN; 5 => IF a = flat OR a = sharp THEN RETURN; 6 => IF a = natural OR a = doubleSharp THEN RETURN; 7 => IF a = doubleFlat OR a = sharp THEN RETURN; 8 => IF a = flat OR a = doubleSharp THEN RETURN; 9 => IF a = doubleFlat OR a = natural THEN RETURN; 10 => IF a = flat OR a = sharp THEN RETURN; 11 => IF a = natural OR a = doubleSharp THEN RETURN; ENDCASE; score.flash _ FALSE; n.spelled _ a; SetDirty[score, n.sync.time, n.sync.time]; END; SetEmbellishment: PUBLIC PROCEDURE[score: ScorePTR, n: NotePTR, e: Embellishment] = BEGIN IF score.sheet.voice # noVoice AND n.voice # score.sheet.voice THEN RETURN; IF n.rest THEN RETURN; n.embellish _ e; SetDirty[score, n.sync.time, n.sync.time]; END; Duration: PUBLIC PROCEDURE[n: NotePTR, metrenome: INTEGER] RETURNS[d: Time] = BEGIN m, t, rem: Time; l: REAL _ 1; b: BeamPTR; IF metrenome # 128 THEN m _ Real.Fix[128*(l*256/metrenome)] ELSE m _ 256; d _ SELECT n.value FROM whole => 64, half => 32, quarter => 16, eighth => 8, sixteenth => 4, thirtysecond => 2, ENDCASE => 1; IF n.dotted THEN d _ (d*3)/2; d _ d*m; FOR b _ n.beam, b.beam WHILE b # NIL DO IF b.ntuple = 0 THEN LOOP; t _ (d*b.against)/b.ntuple; rem _ d*(b.against)-t*b.ntuple; IF b.sync2.time = n.sync.time THEN d _ t+rem ELSE d _ t; ENDLOOP; END; GetBackTie: PUBLIC PROCEDURE[score: ScorePTR, n: NotePTR] RETURNS[t: NotePTR] = BEGIN sync: SyncPTR; IF n = NIL THEN RETURN[NIL]; IF n.sync = NIL THEN RETURN[NIL]; IF ~n.tied THEN RETURN[NIL]; FOR i: CARDINAL IN [Event.GetScoreIndex[score, n.sync]..score.length) DO IF score.event[i].type # sync THEN LOOP; sync _ Event.Sync[score.event[i]]; FOR j: CARDINAL IN [0..sync.length) DO IF sync.note[j].tie = n THEN RETURN[sync.note[j]]; ENDLOOP; ENDLOOP; Error; END; -- **************************************************************************** -- drawing notes -- **************************************************************************** Draw: PUBLIC PROCEDURE[score: ScorePTR, n: NotePTR, stem: INTEGER] = BEGIN w: REAL = 0; -- width of ledger line two: REAL = 2; flag: NoteValue; sheet: SheetPTR = score.sheet; x, y, mid, width: INTEGER _ 0; width _ Note.Width[n]; [x, y] _ Sheet.MapNote[sheet, n]; Note.DrawHead[score, n, x, y, x+width+2]; -- includes x delta IF n.rest OR NOT sheet.notehead THEN RETURN; -- draw the ledger lines mid _ Sheet.Map[sheet, n.sync.time, , n.staff].y+16; FOR i: INTEGER IN [3..9] WHILE y-mid >= i*d DO IF ~n.grace -- note above mid THEN DrawBox[sheet.context, [x-3, mid+w+i*d, x+width+3, mid-w+i*d]] ELSE DrawBox[sheet.context, [x-3, mid+w/two+i*d, x+width+3, mid-w/two+i*d]] ENDLOOP; FOR i: INTEGER IN [3..9] WHILE mid-y >= i*d DO IF ~n.grace -- note below mid THEN DrawBox[sheet.context, [x-3, mid+w-i*d, x+width+3, mid-w-i*d]] ELSE DrawBox[sheet.context, [x-3, mid+w/two-i*d, x+width+3, mid-w/two-i*d]] ENDLOOP; IF n.value = whole OR n.value = unknown THEN RETURN; -- draw the stem flag _ n.value; IF stem # default -- drawing part of or beam THEN {stem _ (IF n.stemUp THEN stem+1 ELSE stem-1); flag _ quarter} ELSE SELECT TRUE FROM n.grace AND n.stemUp => stem _ y+2*d; n.grace AND ~n.stemUp => stem _ y-2*d; n.stemUp => stem _ MAX[y+(7*d)/2, mid]; ~n.stemUp => stem _ MIN[y-(7*d)/2, mid]; ENDCASE; IF n.stemUp THEN DrawLine[sheet.context, x+width, y, x+width, stem] ELSE DrawLine[sheet.context, x, y, x, stem]; -- draw the flag IF n.grace THEN SetCP[sheet.context, x, IF n.stemUp THEN stem-17 ELSE stem+17] ELSE SetCP[sheet.context, x, IF n.stemUp THEN stem-24 ELSE stem+24]; SELECT TRUE FROM flag = quarter => NULL; n.grace => IF n.stemUp THEN DrawChar[sheet.context, 133C] ELSE DrawChar[sheet.context, 134C]; flag = eighth => IF n.stemUp THEN DrawChar[sheet.context, 153C] ELSE DrawChar[sheet.context, 163C]; flag = sixteenth => IF n.stemUp THEN DrawChar[sheet.context, 152C] ELSE DrawChar[sheet.context, 162C]; flag = thirtysecond => IF n.stemUp THEN DrawChar[sheet.context, 151C] ELSE DrawChar[sheet.context, 161C]; flag = sixtyfourth => IF n.stemUp THEN DrawChar[sheet.context, 151C] ELSE DrawChar[sheet.context, 161C]; ENDCASE; END; DrawHead: PUBLIC PROCEDURE[score: ScorePTR, n: NotePTR, x, y, dotX: INTEGER] = BEGIN inVoice: BOOLEAN; width, dotY: INTEGER; sheet: SheetPTR _ score.sheet; -- draw notehead (physical, rest, or normal), dot, and accidental inVoice _ score.sheet.voice = noVoice OR n.voice = score.sheet.voice; IF inVoice THEN Graphics.SetStipple[sheet.context, black] ELSE Graphics.SetStipple[sheet.context, light]; IF NOT sheet.notehead THEN {DrawPhysical[sheet, n, x, y]; RETURN}; IF n.tie # NIL THEN Note.DrawTie[score, n]; SetCP[sheet.context, x, y]; IF n.grace THEN IF n.rest THEN SELECT n.value FROM ENDCASE => DrawChar[sheet.context, 172C] ELSE SELECT n.value FROM ENDCASE => DrawChar[sheet.context, 132C] ELSE IF n.rest THEN SELECT n.value FROM whole => {SetCP[sheet.context, x, y+(IF sheet.printing THEN -5 ELSE 3)] ; DrawChar[sheet.context, 145C]}; half => {IF sheet.printing THEN SetCP[sheet.context, x, y-9]; DrawChar[sheet.context, 145C]}; quarter => DrawChar[sheet.context, 144C]; eighth => DrawChar[sheet.context, 143C]; sixteenth => DrawChar[sheet.context, 142C]; thirtysecond => DrawChar[sheet.context, 141C]; ENDCASE => DrawChar[sheet.context, 114C] ELSE SELECT n.value FROM whole => DrawChar[sheet.context, 156C]; half => DrawChar[sheet.context, 155C]; ENDCASE => DrawChar[sheet.context, 154C]; width _ Note.Width[n]; IF DottedOnLine[sheet, n] THEN dotY _ y+4 ELSE dotY _ y; IF n.dotted THEN BEGIN SetCP[sheet.context, dotX, dotY]; DrawChar[sheet.context, 056C]; dotX _ dotX+3; END; IF n.doubleDotted THEN BEGIN SetCP[sheet.context, dotX, dotY]; DrawChar[sheet.context, 056C]; dotX _ dotX+3; SetCP[sheet.context, dotX, dotY]; DrawChar[sheet.context, 056C]; END; IF n.embellish # none THEN BEGIN sy: INTEGER; sy _ MAX[y, Sheet.Map[sheet, n.sync.time, , n.staff].y+28]; IF n.embellish = trill THEN SetCP[sheet.context, x, sy+10] ELSE SetCP[sheet.context, x-2, sy+10]; SELECT n.embellish FROM trill => DrawChar[sheet.context, 'U]; mordent1 => DrawChar[sheet.context, 'V]; mordent2 => DrawChar[sheet.context, 'W]; ENDCASE; END; IF n.rest OR NOT sheet.accidental THEN RETURN; x _ x-9+(IF sheet.display = graphical THEN n.accDelta ELSE 0); SetCP[sheet.context, x, y]; SELECT n.shown FROM -- n.shown is set by EventImpl.Adjust's GetAccidental doubleFlat => {SetCP[sheet.context, x-4, y]; DrawChar[sheet.context, 110C]}; flat => DrawChar[sheet.context, 111C]; natural => DrawChar[sheet.context, 112C]; sharp => DrawChar[sheet.context, 113C]; doubleSharp => DrawChar[sheet.context, 114C]; ENDCASE; END; DottedOnLine: PROCEDURE[sheet: SheetPTR, n: NotePTR] RETURNS[BOOLEAN] = INLINE BEGIN IF NOT n.dotted THEN RETURN[FALSE]; RETURN[Mod[Sheet.Height[sheet, n.sync.time, Score.ShowPitch[sheet, n.pitch, n.spelled, 0], n.staff]- Sheet.Height[sheet, n.sync.time , , n.staff ], 8] = 0]; END; Mod: PROCEDURE[k, m: INTEGER] RETURNS[n: INTEGER] = BEGIN n _ k MOD m; IF n < 0 THEN n _ n+m; END; DrawPhysical: PROCEDURE[sheet: SheetPTR, n: NotePTR, x, y: INTEGER] = BEGIN duration: INTEGER; IF n.rest THEN RETURN; IF sheet.display # physical THEN duration _ Inline.LowHalf[7*(Note.Duration[n, 128]/8)/sheet.density]; IF sheet.display = physical OR n.value = unknown THEN duration _ n.duration/sheet.density; DrawBox[sheet.context, [x, y-2, MIN[x+duration, sheet.width+12], y+1]]; END; DrawTie: PUBLIC PROCEDURE[score: ScorePTR, n: NotePTR] = BEGIN temp: NotePTR; oneTie: BOOLEAN; sheet: SheetPTR _ score.sheet; x1, x2, y1, y2, xe, ye: INTEGER; IF n.sync.time < n.tie.sync.time THEN BEGIN -- swap the notes temp _ n.tie; IF n.tied THEN Note.GetBackTie[score, n].tie _ n.tie; temp.tied _ n.tied; n.tied _ TRUE; n.tie _ temp.tie; temp.tie _ n; n _ temp; END; x1 _ Sheet.Map[sheet, n.tie.sync.time, n.tie.pitch, n.tie.staff].x; x2 _ Sheet.Map[sheet, n.sync.time, n.pitch, n.staff].x; [xe, y1] _ Sheet.MapNote[sheet, n.tie]; x1 _ MAX[x1, xe]; [xe, y2] _ Sheet.MapNote[sheet, n]; x2 _ MIN[x2, xe]; IF n.tieHeight > 0 THEN y1 _ y1+2 ELSE y1 _ y1-2; IF n.tieHeight > 0 THEN y2 _ y2+2 ELSE y2 _ y2-2; IF ABS[n.sync.time-n.tie.sync.time] < 28 THEN { x1 _ x1-5; x2 _ x2+5; IF n.tieHeight > 0 THEN y1 _ y1+4 ELSE y1 _ y1-4; IF n.tieHeight > 0 THEN y2 _ y2+4 ELSE y2 _ y2-4}; IF x1 > x2 THEN BEGIN oneTie _ FALSE; xe _ sheet.width; ye _ y1; END ELSE BEGIN oneTie _ TRUE; xe _ x2-2; ye _ y2; END; x1 _ x1 + 10; IF n.tie.sync.time >= sheet.begin THEN DrawCubic[sheet.context, x1, y1, xe, ye, n.tieHeight]; IF oneTie THEN RETURN; IF n.sync.time <= sheet.endTime THEN BEGIN xe _ sheet.section[Sheet.FindLine[sheet, n.sync.time]].x-6; ye _ y2; DrawCubic[sheet.context, xe, ye, x2, y2, n.tieHeight]; END; END; d: CARDINAL = 8; END.. DrawStem: PUBLIC PROCEDURE[x, y1, y2, stem, staff: INTEGER, stemUp, grace: BOOLEAN, flag: NoteValue] = BEGIN -- x is the left edge of the note, -- y1 & y2 are the y coordinates of the top and bottom notes of a chord -- stem is the y coordinate (stem = default is a request for the default stem) n: INTEGER; line: REAL _ 1; IF stem # default -- drawing part of or beam THEN stem _ (IF stemUp THEN stem+1 ELSE stem-1) ELSE {[, middle] _ Sheet.Map[n.sync.time, 1000, n.staff]+16; IF stemUp THEN stem _ MAX[y+(7*d)/2, middle] ELSE stem _ MIN[y-(7*d)/2, middle]}; FOR n IN [3..9] WHILE MAX[head, head2]-middle >= n*d DO -- note above middle DrawBox[context, [x-3, middle+line+n*d, x+w, middle-line+n*d]]; ENDLOOP; FOR n IN [3..9] WHILE middle-MIN[head, head2] >= n*d DO -- note below middle DrawBox[context, [x-3, middle+line-n*d, x+w, middle-line-n*d]]; ENDLOOP; IF flag = whole OR flag = unknown THEN RETURN; SetCP[context, x, IF stemUp THEN tail-24 ELSE tail+24]; SELECT flag FROM eighth => IF stemUp THEN DrawChar[context, 153C] ELSE DrawChar[context, 163C]; sixteenth => IF stemUp THEN DrawChar[context, 152C] ELSE DrawChar[context, 162C]; thirtysecond => IF stemUp THEN DrawChar[context, 151C] ELSE DrawChar[context, 161C]; ENDCASE; IF stemUp THEN x _ x+8; DrawLine[x, head, x, tail]; END; Vector: TYPE = RECORD[x, y: INTEGER]; DisplayAcc: PROCEDURE[x, y: INTEGER, n: NotePTR] = BEGIN c: Accidental; SetCP[context, x, y]; c _ GetAccidental[n]; SELECT c FROM doubleFlat => BEGIN SetCP[context, x-4, y]; DrawChar[context, 110C]; END; flat => DrawChar[context, 111C]; natural => DrawChar[context, 112C]; sharp => DrawChar[context, 113C]; doubleSharp => DrawChar[context, 114C]; ENDCASE; END; \3b8B604i17I157i17I