NoteImpl.mesa
Copyright (C) 1983, 1984 Xerox Corporation. All rights reserved.
Author: John Maxwell
Last Edited by: Maxwell, November 22, 1983 11:43 am
Last Edited by: Doug Wyatt, June 19, 1984 10:22:21 am PDT
DIRECTORY
Beam USING [RemoveItem],
Chord USING [RemoveNote],
Event USING [GetScoreIndex, RemoveNote, Sync],
MusicDefs,
Note USING [default, Width],
Real USING [Fix],
Score USING [ShowPitch],
Selection USING [RemoveNote],
Sheet USING [FindLine, Height, Map, MapNote],
Utility USING [DrawBox, DrawChar, DrawCubic, DrawLine, SetColor, SetCP];
NoteImpl: CEDAR PROGRAM
IMPORTS Beam, Chord, Event, MusicDefs, Note, Real, Score, Selection, Sheet, Utility
EXPORTS Note
= BEGIN OPEN MusicDefs;
****************************************************************************
note procedures
****************************************************************************
Free: PUBLIC PROC[score: ScorePTR, n: NotePTR] = {
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, n];
IF n.tie # NIL THEN {n.tie.tied ← FALSE; n.tie ← NIL};
IF n.tied THEN {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];
};
SetAccidental: PUBLIC PROC[score: ScorePTR, n: NotePTR, a: Accidental] = {
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];
};
SetEmbellishment: PUBLIC PROC[score: ScorePTR, n: NotePTR, e: Embellishment] = {
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];
};
Duration: PUBLIC PROC[n: NotePTR, metrenome: INTEGER] RETURNS[d: Time] = {
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;
};
GetBackTie: PUBLIC PROC[score: ScorePTR, n: NotePTR] RETURNS[t: NotePTR] = {
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;
};
GetSyncIndex: PUBLIC PROC[s: SyncPTR, p: NotePTR] RETURNS[NAT] = {
FOR i: NAT IN[0..s.length) DO
IF s.note[i] = p THEN RETURN[i];
ENDLOOP;
ERROR;
};
****************************************************************************
drawing notes
****************************************************************************
d: CARDINAL = 8;
Draw: PUBLIC PROC[score: ScorePTR, n: NotePTR, stem: INTEGER] = {
w: REAL = 1; -- 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];
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 Utility.DrawBox[sheet.context, [x-3, mid+w+i*d, x+width+3, mid-w+i*d]]
ELSE Utility.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 Utility.DrawBox[sheet.context, [x-3, mid+w-i*d, x+width+3, mid-w-i*d]]
ELSE Utility.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#Note.default THEN { stem ← (IF n.stemUp THEN stem+1 ELSE stem-1); flag ← quarter }
ELSE IF n.grace THEN { IF n.stemUp THEN stem ← y+2*d ELSE stem ← y-2*d }
ELSE { IF n.stemUp THEN stem ← MAX[y+(7*d)/2, mid] ELSE stem ← MIN[y-(7*d)/2, mid] };
IF n.stemUp
THEN Utility.DrawLine[sheet.context, x+width, y, x+width, stem]
ELSE Utility.DrawLine[sheet.context, x, y, x, stem];
draw the flag
IF n.grace
THEN Utility.SetCP[sheet.context, x, IF n.stemUp THEN stem-17 ELSE stem+17]
ELSE Utility.SetCP[sheet.context, x, IF n.stemUp THEN stem-24 ELSE stem+24];
SELECT TRUE FROM
flag = quarter => NULL;
n.grace => Utility.DrawChar[sheet.context, IF n.stemUp THEN 133C ELSE 134C];
flag = eighth => Utility.DrawChar[sheet.context, IF n.stemUp THEN 153C ELSE 163C];
flag = sixteenth => Utility.DrawChar[sheet.context, IF n.stemUp THEN 152C ELSE 162C];
flag = thirtysecond => Utility.DrawChar[sheet.context, IF n.stemUp THEN 151C ELSE 161C];
flag = sixtyfourth => Utility.DrawChar[sheet.context, IF n.stemUp THEN 151C ELSE 161C];
ENDCASE;
};
DrawHead: PUBLIC PROC[score: ScorePTR, n: NotePTR, x, y, dotX: INTEGER] = {
inVoice: BOOL;
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;  
Utility.SetColor[sheet.context, IF inVoice THEN black ELSE light];
IF NOT sheet.notehead THEN {DrawPhysical[sheet, n, x, y]; RETURN};
IF n.tie # NIL THEN DrawTie[score, n];
Utility.SetCP[sheet.context, x, y];
IF n.grace
THEN IF n.rest
THEN SELECT n.value FROM
ENDCASE => Utility.DrawChar[sheet.context, 172C]
ELSE SELECT n.value FROM
ENDCASE => Utility.DrawChar[sheet.context, 132C]
ELSE IF n.rest
THEN SELECT n.value FROM
whole => {Utility.SetCP[sheet.context, x, y+(IF sheet.printing THEN -5 ELSE 3)] ;
Utility.DrawChar[sheet.context, 145C]};
half => {IF sheet.printing THEN Utility.SetCP[sheet.context, x, y-9];
Utility.DrawChar[sheet.context, 145C]};
quarter => Utility.DrawChar[sheet.context, 144C];
eighth => Utility.DrawChar[sheet.context, 143C];
sixteenth => Utility.DrawChar[sheet.context, 142C];
thirtysecond => Utility.DrawChar[sheet.context, 141C];
ENDCASE => Utility.DrawChar[sheet.context, 114C]
ELSE SELECT n.value FROM
whole => Utility.DrawChar[sheet.context, 156C];
half => Utility.DrawChar[sheet.context, 155C];
ENDCASE => Utility.DrawChar[sheet.context, 154C];
width ← Note.Width[n];
IF DottedOnLine[sheet, n] THEN dotY ← y+4 ELSE dotY ← y;
IF n.dotted THEN {
Utility.SetCP[sheet.context, dotX, dotY];
Utility.DrawChar[sheet.context, 056C];
dotX ← dotX+3;
};
IF n.doubleDotted THEN {
Utility.SetCP[sheet.context, dotX, dotY]; Utility.DrawChar[sheet.context, 056C]; dotX ← dotX+3;
Utility.SetCP[sheet.context, dotX, dotY]; Utility.DrawChar[sheet.context, 056C];
};
IF n.embellish # none THEN {
sy: INTEGER;
sy ← MAX[y, Sheet.Map[sheet, n.sync.time, , n.staff].y+28];
IF n.embellish = trill
THEN Utility.SetCP[sheet.context, x, sy+10]
ELSE Utility.SetCP[sheet.context, x-2, sy+10];
SELECT n.embellish FROM
trill => Utility.DrawChar[sheet.context, 'U];
mordent1 => Utility.DrawChar[sheet.context, 'V];
mordent2 => Utility.DrawChar[sheet.context, 'W];
ENDCASE;
};
IF n.rest OR NOT sheet.accidental THEN RETURN;
x ← x-9+(IF sheet.display = graphical THEN n.accDelta ELSE 0);
Utility.SetCP[sheet.context, x, y];
SELECT n.shown FROM -- n.shown is set by EventImpl.Adjust's GetAccidental
doubleFlat => {Utility.SetCP[sheet.context, x-4, y]; Utility.DrawChar[sheet.context, 110C]};
flat => Utility.DrawChar[sheet.context, 111C];
natural => Utility.DrawChar[sheet.context, 112C];
sharp => Utility.DrawChar[sheet.context, 113C];
doubleSharp => Utility.DrawChar[sheet.context, 114C];
ENDCASE;
};
DottedOnLine: PROC[sheet: SheetPTR, n: NotePTR] RETURNS[BOOL] = INLINE {
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];
};
Mod: PROC[k, m: INTEGER] RETURNS[n: INTEGER] = { n ← k MOD m; IF n < 0 THEN n ← n+m; };
DrawPhysical: PROC[sheet: SheetPTR, n: NotePTR, x, y: INTEGER] = {
duration: INTEGER;
IF n.rest THEN RETURN;
IF sheet.display # physical THEN duration ← 7*(Duration[n, 128]/8)/sheet.density;
IF sheet.display = physical OR n.value = unknown THEN duration ← n.duration/sheet.density;
Utility.DrawBox[sheet.context, [x, y-2, MIN[x+duration, sheet.width+12], y+1]];
};
DrawTie: PUBLIC PROC[score: ScorePTR, n: NotePTR] = {
temp: NotePTR;
oneTie: BOOL;
sheet: SheetPTR ← score.sheet;
x1, x2, y1, y2, xe, ye: INTEGER;
IF n.sync.time < n.tie.sync.time THEN { -- swap the notes
temp ← n.tie;
IF n.tied THEN GetBackTie[score, n].tie ← n.tie;
temp.tied ← n.tied; n.tied ← TRUE;
n.tie ← temp.tie; temp.tie ← n;
n ← temp;
};
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 { oneTie ← FALSE; xe ← sheet.width; ye ← y1; }
ELSE { oneTie ← TRUE; xe ← x2-2; ye ← y2; };
x1 ← x1 + 10;
IF n.tie.sync.time >= sheet.begin THEN
Utility.DrawCubic[sheet.context, x1, y1, xe, ye, n.tieHeight];
IF oneTie THEN RETURN;
IF n.sync.time <= sheet.endTime THEN {
xe ← sheet.section[Sheet.FindLine[sheet, n.sync.time]].x-6;
ye ← y2;
Utility.DrawCubic[sheet.context, xe, ye, x2, y2, n.tieHeight];
};
};
END.
****************************************************************************
DrawStem: PUBLIC PROC[x, y1, y2, stem, staff: INTEGER, stemUp, grace: BOOL, flag: NoteValue] =
{
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
Utility.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
Utility.DrawBox[context, [x-3, middle+line-n*d, x+w, middle-line-n*d]];
ENDLOOP;
IF flag = whole OR flag = unknown THEN RETURN;
Utility.SetCP[context, x, IF stemUp THEN tail-24 ELSE tail+24];
SELECT flag FROM
eighth => IF stemUp THEN Utility.DrawChar[context, 153C]
ELSE Utility.DrawChar[context, 163C];
sixteenth => IF stemUp THEN Utility.DrawChar[context, 152C]
ELSE Utility.DrawChar[context, 162C];
thirtysecond => IF stemUp THEN Utility.DrawChar[context, 151C]
ELSE Utility.DrawChar[context, 161C];
ENDCASE;
IF stemUp THEN x ← x+8;
Utility.DrawLine[x, head, x, tail];
};
Vector: TYPE = RECORD[x, y: INTEGER];
DisplayAcc: PROC[x, y: INTEGER, n: NotePTR] =
{
c: Accidental;
Utility.SetCP[context, x, y];
c ← GetAccidental[n];
SELECT c FROM
doubleFlat => { Utility.SetCP[context, x-4, y]; Utility.DrawChar[context, 110C]; };
flat => Utility.DrawChar[context, 111C];
natural => Utility.DrawChar[context, 112C];
sharp => Utility.DrawChar[context, 113C];
doubleSharp => Utility.DrawChar[context, 114C];
ENDCASE;
};