ChordImpl.mesa
Copyright (C) 1981, 1984 Xerox Corporation. All rights reserved.
Author: John Maxwell
last modified: October 23, 1981 12: 58 PM
Edited by Doug Wyatt, June 14, 1984 3:35:28 pm PDT
DIRECTORY
Beam USING [AddChord, Remove],
Chord USING [default, InVoice, RemoveNote, Sort],
Graphics USING [DrawRectangle, MoveTo, SetTexture],
MusicDefs,
Note USING [DrawHead, FindChord, Width],
Sheet USING [Map, MapNote],
Sync USING [AddNote],
Utility USING [DrawChar, DrawLine, FreeChord];
ChordImpl: CEDAR PROGRAM
IMPORTS Beam, Chord, Graphics, MusicDefs, Note, Sheet, Sync, Utility
EXPORTS Chord
= BEGIN OPEN Chord, MusicDefs, Graphics, Utility;
Beam: PUBLIC PROC[c: ChordPTR] RETURNS[BeamPTR] =
{RETURN[IF c.note[0]#NIL THEN c.note[0].beam ELSE NIL]};
GetHeapIndex: PUBLIC PROC[c: ChordPTR] RETURNS[CARDINAL] =
{FOR i: CARDINAL IN [0..chordHeapLength) DO
IF chordHeap[i]=c THEN RETURN[i];
ENDLOOP;
RETURN[chordHeapLength]};
Grace: PUBLIC PROC[c: ChordPTR] RETURNS[BOOL] =
{FOR i: CARDINAL IN [0..chordLength) DO
IF c.note[i]=NIL THEN EXIT;
IF ~c.note[i].grace THEN RETURN[FALSE];
ENDLOOP;
RETURN[TRUE]};
InVoice: PUBLIC PROC[c: ChordPTR, voice: CARDINAL] RETURNS[BOOL] =
{FOR i: CARDINAL IN [0..chordLength) DO
IF c.note[i]=NIL THEN EXIT;
IF c.note[i].voice=voice THEN RETURN[TRUE];
ENDLOOP;
RETURN[FALSE]};
Length: PUBLIC PROC[c: ChordPTR] RETURNS[CARDINAL] =
{FOR i: CARDINAL IN [0..chordLength] DO
IF i=chordLength THEN RETURN[i];
IF c.note[i]=NIL THEN RETURN[i];
ENDLOOP;
ERROR};
Width: PUBLIC PROC[c: ChordPTR] RETURNS[INTEGER] =
{RETURN[IF Grace[c] THEN 5 ELSE 8]};
****************************************************************************
data abstractions for a chord
CONSTRAINT: c.note[i].sync = c.note[j].sync for all i, j
CONSTRAINT: c.note[i].beam = c.note[j].beam for all i, j
****************************************************************************
AddNote: PUBLIC PROC[c: ChordPTR, n: NotePTR] = {
oldBeam: BeamPTR = n.beam;
oldChord: ChordPTR = Note.FindChord[n];
chordSync: SyncPTR = IF c.note[0]#NIL THEN c.note[0].sync ELSE n.sync;
chordBeam: BeamPTR = IF c.note[0]#NIL THEN c.note[0].beam ELSE NIL;
check for aborted attempts
IF c=oldChord THEN RETURN;
IF c.note[chordLength-1]#NIL THEN Overflow[chord];
put the note in the chord
FOR i: CARDINAL IN [0..chordLength) DO
IF c.note[i]=NIL THEN {c.note[i] ← n; EXIT};
ENDLOOP;
n.chord ← c;
n.beam ← chordBeam;
IF chordBeam=NIL AND oldBeam#NIL THEN Beam.AddChord[oldBeam, c];
IF n.sync#chordSync THEN Sync.AddNote[chordSync, n];
remove the note from old places
IF oldChord#NIL THEN Chord.RemoveNote[oldChord, n];
IF oldBeam#NIL AND oldChord=NIL AND oldBeam#n.beam THEN Beam.Remove[oldBeam, n, NIL, NIL];
};
RemoveNote: PUBLIC PROC[c: ChordPTR, n: NotePTR] = {
i, k: CARDINAL ← chordLength;
IF n=NIL OR c=NIL THEN RETURN;
keep the notes packed;
FOR i DECREASING IN [0..chordLength) DO
IF c.note[i]=NIL THEN k ← i;
IF c.note[i]#n THEN LOOP;
IF n.chord=c THEN n.chord ← NIL;
n.beam ← NIL; -- removing a note from a chord also removes it from the beam
c.note[i] ← c.note[k-1];
c.note[k-1] ← NIL;
ENDLOOP;
IF c.note[1]=NIL THEN Utility.FreeChord[@c];
};
Sort: PUBLIC PROC[c: ChordPTR, up: BOOL] RETURNS[n: CARDINAL] = {
i, j: CARDINAL;
temp: NotePTR;
FOR i IN [0..chordLength) DO
IF c.note[i]=NIL THEN { n ← i; EXIT; };
FOR j IN (i..chordLength) DO
IF c.note[j]=NIL THEN EXIT;
IF NOT up AND c.note[i].pitch<c.note[j].pitch
OR up AND c.note[i].pitch>c.note[j].pitch
THEN { temp← c.note[i]; c.note[i]← c.note[j]; c.note[j]← temp; };
ENDLOOP;
ENDLOOP;
};
SetDefaultStem: PUBLIC PROC[c: ChordPTR] = {
n: CARDINAL;
x0, xn, y0, yn, middle0, middlen: INTEGER;
n ← Chord.Sort[c, TRUE];
[x0, y0] ← Sheet.Map[c.note[0].sync.time, c.note[0].pitch, c.note[0].staff];
[xn, yn] ← Sheet.Map[c.note[n-1].sync.time, c.note[n-1].pitch, c.note[n-1].staff];
middle0 ← Sheet.Map[c.note[0].sync.time,, c.note[0].staff].y+16;
middlen ← Sheet.Map[c.note[n-1].sync.time,, c.note[n-1].staff].y+16;
SELECT TRUE FROM
(yn+y0)/2 > middlen => c.stemUp ← FALSE;
(yn+y0)/2 < middle0 => c.stemUp ← TRUE;
(yn+y0)/2 > (middlen+middle0)/2 => c.stemUp ← TRUE;
ENDCASE => c.stemUp ← FALSE;
};
******************************************************************
procedures for drawing chords on the screen
******************************************************************
Draw: PUBLIC PROC[c: ChordPTR, stem: INTEGER] = {
n: NotePTR;
flag: NoteValue;
two: REAL = 2;
dotX: INTEGER ← 0;
inVoice: BOOL;
xWide, yWide: INTEGER;
nonGrace: BOOLFALSE;
minStaff, maxStaff: CARDINAL;
x, y, yMin, yMax, mid, width: INTEGER;
inVoice ← ~(voice AND NOT Chord.InVoice[c, selectedVoice]); -- draw the note heads
FOR i: CARDINAL IN [0..chordLength) DO
IF c.note[i]=NIL THEN EXIT ELSE n ← c.note[i];
IF ~n.dotted THEN LOOP;
[x, y] ← Sheet.MapNote[n];
dotX ← MAX[dotX, x+Note.Width[n]+2];
ENDLOOP;
mid ← Sheet.Map[n.sync.time,, n.staff].y+16;
FOR i: CARDINAL IN [0..chordLength) DO
IF c.note[i]=NIL THEN EXIT ELSE n ← c.note[i];
[x, y] ← Sheet.MapNote[n];
Note.DrawHead[n, x, y, dotX];
IF i=0 THEN {yMin ← yMax ← y; width ← 0};
width ← MAX[width, Note.Width[n]];
IF n.delta#0 THEN {
xWide ← MIN[xWide, x];
yWide ← IF y>mid THEN MIN[yWide, y] ELSE MAX[yWide, y]};
IF ~n.grace THEN nonGrace ← TRUE;
IF y<=yMin THEN {yMin ← y; minStaff ← n.staff};
IF y>=yMax THEN {yMax ← y; maxStaff ← n.staff};
ENDLOOP;
IF NOT show.notehead THEN RETURN;
draw the ledger lines
Graphics.SetTexture[context, IF inVoice THEN black ELSE light];
IF show.display=graphical THEN x ← x - n.delta; --subtract off x offset before drawing stem
FOR i: INTEGER IN [3..9] WHILE yMax-mid >= i*d DO
IF nonGrace
THEN DrawRectangle[context,[x-3, mid+1+i*d],[x+width+3, mid-1+i*d]]
ELSE DrawRectangle[context,[x-3, mid+1/two+i*d],[x+width+3, mid-1/two+i*d]];
ENDLOOP;
FOR i: INTEGER IN [3..9] WHILE mid-yMin >= i*d DO
IF nonGrace
THEN DrawRectangle[context,[x-3, mid+1-i*d],[x+width+3, mid-1-i*d]]
ELSE DrawRectangle[context,[x-3, mid+1/two-i*d],[x+width+3, mid-1/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 c.stemUp THEN stem+1 ELSE stem-1); flag ← quarter}
ELSE SELECT TRUE FROM
nonGrace AND c.stemUp => stem ← MAX[yMax+(7*d)/2, mid];
nonGrace AND ~c.stemUp => stem ← MIN[yMin-(7*d)/2, mid];
c.stemUp => stem ← yMax+2*d;
~c.stemUp => stem ← yMin-2*d;
ENDCASE;
IF c.stemUp THEN DrawLine[x+width, yMin, x+width, stem] ELSE DrawLine[x, yMax, x, stem];
draw the flag
IF nonGrace
THEN MoveTo[context,[x, IF c.stemUp THEN stem-24 ELSE stem+24]]
ELSE MoveTo[context,[x, IF c.stemUp THEN stem-17 ELSE stem+17]];
SELECT TRUE FROM
~nonGrace => IF c.stemUp
THEN DrawChar[context, 133C]
ELSE DrawChar[context, 134C];
flag=eighth => IF c.stemUp
THEN DrawChar[context, 153C]
ELSE DrawChar[context, 163C];
flag=sixteenth => IF c.stemUp
THEN DrawChar[context, 152C]
ELSE DrawChar[context, 162C];
flag=thirtysecond => IF c.stemUp
THEN DrawChar[context, 151C]
ELSE DrawChar[context, 161C];
flag=sixtyfourth => IF c.stemUp
THEN DrawChar[context, 151C]
ELSE DrawChar[context, 161C];
ENDCASE;
};
d: CARDINAL=8;
****************************************************************************
look graphical
****************************************************************************
Adjust: PUBLIC PROC[c: ChordPTR] = {
n: NotePTR;
pad: ScratchPad;
delta: INTEGER;
last: BOOLFALSE;
i, length: CARDINAL;
build the pad, initialize
FOR i IN [0..chordLength) DO
IF (n ← c.note[i])=NIL THEN { length ← i; EXIT; };
[, pad[i].y] ← Sheet.MapNote[n];
pad[i].n ← n;
n.delta ← 0;
ENDLOOP;
SortPad[c.stemUp,@pad];
c.delta ← 0;
determine the deltas on the notes
IF c.stemUp THEN delta ← 8 ELSE delta ← -8;
FOR i IN [1..length) DO
IF last THEN { last ← FALSE; LOOP; };
IF ABS[pad[i-1].y-pad[i].y]>= 8 THEN LOOP;
pad[i].n.delta ← delta;
last ← TRUE;
ENDLOOP;
};
SortPad: PROC[ascending: BOOL, pad: POINTER TO ScratchPad] = {
i, j: CARDINAL;
temp: Scratch;
FOR i IN [0..syncLength) DO
IF pad[i].n=NIL THEN EXIT;
FOR j IN (i..syncLength) DO
IF pad[j].n=NIL THEN EXIT;
IF NOT ascending AND pad[i].y<pad[j].y
OR ascending AND pad[i].y>pad[j].y
THEN { temp← pad[i]; pad[i]← pad[j]; pad[j]← temp; };
ENDLOOP;
ENDLOOP;
};
Delta: PROC[n: NotePTR] RETURNS[INTEGER] = {
c: ChordPTR ← Note.FindChord[n];
IF c=NIL THEN RETURN[n.delta]
ELSE RETURN[n.delta+c.delta];
};
ScratchPad: TYPE= ARRAY [0..syncLength) OF Scratch;
Scratch: TYPE = RECORD[x, y, stem: INTEGER ← 0, push: CARDINAL ← syncLength,
acc: Accidental, stemUp: BOOL,
c: ChordPTR, n: NotePTR ← NIL];
END.