PieceImplA.mesa
Copyright (C) 1981, 1984 Xerox Corporation. All rights reserved.
Author: John Maxwell
last modified: December 15, 1981 4: 15 PM
Edited by Doug Wyatt, June 14, 1984 1:07:52 pm PDT
DIRECTORY
Beam USING [Height, InVoice],
MusicDefs,
Note USING [Delete, Delta, FindChord],
Piece USING [CleanUpSyncs, Length, RemoveSync],
Real USING [FixI],
Score USING [GetKey, ShowPitch],
Sheet USING [FindLine, FindSection, Height, NearestTime, NextLine],
Sync USING [GetScoreIndex, Octava],
Utility USING [FreeSync];
PieceImplA: CEDAR PROGRAM
IMPORTS Beam, MusicDefs, Note, Piece, Real, Score, Sheet, Sync, Utility
EXPORTS MusicDefs, Piece
= BEGIN OPEN MusicDefs;
dataStructureInFlux: PUBLIC BOOLFALSE;
Length: PROC[p: PiecePTR] RETURNS[NAT] = {
IF p=NIL THEN RETURN[0];
IF p=MusicDefs.score THEN RETURN[scoreLength];
FOR i: NAT IN [0..maxPieceLength] DO
IF i=maxPieceLength THEN RETURN[i];
IF p[i]=NIL THEN RETURN[i];
ENDLOOP;
ERROR;
};
****************************************************************************
data abstractions for a piece
CONSTRAINT: piece[i-1].time <= piece[i].time <= piece[i+1].time
CONSTRAINT: piece[i]#NIL for all i such that 0 <= i < pieceLength
CONSTRAINT: piece[pieceLength]=NIL
CONSTRAINT: a sync may be entered at most one time
****************************************************************************
AddSync: PUBLIC PROC[p: PiecePTR, s: SyncPTR] = {
any procedure that inserts syncs should process syncs in decreasing order
length: CARDINAL=Piece.Length[p];
IF s=NIL THEN RETURN;
check for overflow
IF p=score AND length+1=maxScoreLength THEN Overflow[score];
IF p#score AND length+1=maxPieceLength THEN Overflow[piece];
insert sync
IF p=score THEN dataStructureInFlux ← TRUE;
FOR i: CARDINAL DECREASING IN [0..length) DO
IF p[i]=NIL THEN LOOP;
IF p[i].time < s.time THEN {p[i+1] ← s; s ← NIL; EXIT};
IF p[i].time = s.time AND ~LessThan[s.type, p[i].type]
THEN {p[i+1] ← s; s ← NIL; EXIT};
p[i+1] ← p[i];
ENDLOOP;
IF s#NIL THEN p[0] ← s;
IF p=score THEN scoreLength ← scoreLength+1;
IF p=score THEN dataStructureInFlux ← FALSE;
};
LessThan: PROC[a, b: EventType] RETURNS[BOOL] = INLINE {
SELECT TRUE FROM
a=staves => RETURN[TRUE];
b=staves => RETURN[FALSE];
a=keySignature => RETURN[TRUE];
b=keySignature => RETURN[FALSE];
Measure[a] => RETURN[TRUE];
Measure[b] => RETURN[FALSE];
a=metrenome => RETURN[TRUE];
b=metrenome => RETURN[FALSE];
a=timeSignature => RETURN[TRUE];
b=timeSignature => RETURN[FALSE];
a IN SheetSwitch => RETURN[TRUE];
b IN SheetSwitch => RETURN[FALSE];
ENDCASE => RETURN[TRUE];
};
RemoveSync: PUBLIC PROC[p: PiecePTR, s: SyncPTR] = {
DOES NOT free notes or itself!
any procedure that removes syncs should process syncs in decreasing order
i, j: CARDINAL ← 0;
length: CARDINAL ← Piece.Length[p];
FOR i IN [0..length) DO
IF p[i]=s THEN LOOP;
IF i#j THEN p[j] ← p[i];
j ← j+1; ENDLOOP;
FOR i IN [0..syncLength) DO
IF s.event[i]=NIL THEN EXIT;
s.event[i].sync ← NIL;
ENDLOOP;
length ← j;
p[length] ← NIL;
IF p=score THEN scoreLength ← length;
};
DeleteSync: PUBLIC PROC[s: SyncPTR] = {
octava: SyncPTR ← NIL;
index: CARDINAL ← Sync.GetScoreIndex[s];
IF s.type IN [octava1..octava2] THEN octava ← Sync.Octava[s];
Piece.RemoveSync[score, s];
IF octava#NIL THEN {
SetDirty[octava.time, octava.time];
Piece.RemoveSync[score, octava];
Utility.FreeSync[@octava]};
Utility.FreeSync[@s];
};
Sort: PUBLIC PROC[p: PiecePTR] =
useful when going from physical to logical files {
i, j: CARDINAL;
temp: SyncPTR;
length: CARDINAL = Piece.Length[p];
FOR i IN [0..length) DO
FOR j IN (i..length) DO
IF p[j] = NIL THEN LOOP;
IF p[i] = NIL THEN {p[i] ← p[j]; p[j] ← NIL; LOOP};
IF p[i].time > p[j].time THEN {temp ← p[i]; p[i] ← p[j]; p[j] ← temp};
IF p[i].time= p[j].time THEN
{IF p[i].event[0]=NIL OR p[j].event[0]=NIL THEN LOOP;
IF p[i].event[0].toc<p[j].event[0].toc THEN LOOP;
temp ← p[i]; p[i] ← p[j]; p[j] ← temp};
ENDLOOP;
ENDLOOP;
};
CleanUpSyncs: PUBLIC PROC[p: PiecePTR] = {
next: CARDINAL ← 0;
length: CARDINAL ← Piece.Length[p];
FOR i: CARDINAL IN [0..length) DO
IF p[i].type=notes AND p[i].event[0]=NIL
THEN Utility.FreeSync[@p[i]]
ELSE {p[next] ← p[i]; next ← next+1};
ENDLOOP;
FOR i: CARDINAL IN [next..length) DO p[i] ← NIL; ENDLOOP;
IF p = score THEN scoreLength ← next;
};
CleanUpNotes: PUBLIC PROC[p: PiecePTR] = {
i, j: CARDINAL;
n: NotePTR;
FOR i DECREASING IN [0..Piece.Length[p]) DO
IF p[i].type#notes THEN LOOP;
FOR j DECREASING IN [0..syncLength) DO
IF p[i]=NIL THEN LOOP;
IF (n ← p[i].event[j])=NIL THEN LOOP;
IF n.value#unknown THEN LOOP;
IF n.duration#0 THEN LOOP;
Note.Delete[n, TRUE];
ENDLOOP;
ENDLOOP;
Piece.CleanUpSyncs[p];
};
****************************************************************************
what is being pointed at?
****************************************************************************
NearestSync: PUBLIC PROC[p: PiecePTR, t: Time, notesOnly: BOOLFALSE] RETURNS[index: CARDINAL]= {
delta: Time ← 10000;
length: CARDINAL = Piece.Length[p];
index ← 0;
FOR binary: CARDINAL ← 8192, binary/2 UNTIL binary=0 DO
IF index+binary>=length THEN LOOP;
IF p[index+binary].time<=t THEN index ← index+binary;
IF p[index].time=t THEN EXIT;
ENDLOOP;
the above will fail when the p isn't strictly ordered
FOR i: CARDINAL IN [(IF index<5 THEN 0 ELSE index-5)..MIN[index+5, length]) DO
IF notesOnly AND p[i].type#notes THEN LOOP;
IF ABS[p[i].time-t] > delta THEN LOOP;
index ← i; delta ← ABS[p[i].time-t];
ENDLOOP;
IF notesOnly AND index#length AND p[index].type#notes THEN index ← length;
};
NearestNote: PUBLIC PROC[x, y: INTEGER] RETURNS[p: NotePTR] = {
n: NotePTR;
key: INTEGER;
time, dt, t: Time ← 11;
i, j, k, l, next: CARDINAL;
height, dy, show: INTEGER ← 8;
[time, height] ← Sheet.NearestTime[x, y];
next ← Sheet.FindLine[time];
time ← time - (staffLength - sheet[next].x);
l ← Sheet.FindLine[time];
height ← height + (sheet[next].y - sheet[l].y);
FOR k IN [0..2] DO
FOR i IN [0..scoreLength) DO
IF score[i].time< time - 40 THEN LOOP;
IF score[i].time> time + 40 THEN EXIT;
IF score[i].time<sheet[l].time THEN LOOP;
IF score[i].time>=sheet[next].time THEN EXIT;
FOR j IN [0..syncLength) DO
IF (n ← score[i].event[j]) = NIL THEN EXIT;
IF voice AND n.voice#selectedVoice THEN LOOP;
t ← ABS[score[i].time+5+Note.Delta[n]-time];
key ← Score.GetKey[n.sync.time];
show ← Score.ShowPitch[n.pitch, n.spelled, key];
y ← ABS[height-Sheet.Height[n.sync.time, show, n.staff]];
IF y+t>dy+dt THEN LOOP;
IF y+t=dy+dt AND t>dt THEN LOOP;
dt ← t; dy ← y;
p ← n;
ENDLOOP;
ENDLOOP;
time ← time+ (staffLength - sheet[next].x);
height ← height- (sheet[next].y - sheet[l].y);
l ← next;
next ← Sheet.NextLine[l];
ENDLOOP;
IF dt>10 OR dy>7 THEN p ← NIL;
};
NearestObject: PUBLIC PROC[x, y: INTEGER] RETURNS[obj: ObjectType, p: UnspecifiedPTR ← NIL] = {
n: NotePTR;
l: CARDINAL;
key: INTEGER;
staves: StavesPTR;
i, j, k, s, next: CARDINAL ← 0;
time, dt, t: Time ← 11;
height, dy, show: INTEGER ← 8;
[time, height] ← Sheet.NearestTime[x, y];
next ← Sheet.FindLine[time];
time ← time - (staffLength - sheet[next].x);
l ← Sheet.FindLine[time];
height ← height + (sheet[next].y - sheet[l].y);
FOR k IN [0..2] DO
left and right corners of a beam
FOR i IN [0..beamHeapLength) DO
IF beamHeap[i].beam#NIL AND beamHeap[i].beam.beamed THEN LOOP;
IF beamHeap[i].sync1.time-5 > time THEN LOOP;
IF beamHeap[i].sync2.time+5 < time THEN LOOP;
IF voice AND ~Beam.InVoice[beamHeap[i], selectedVoice] THEN LOOP;
y ← Beam.Height[beamHeap[i], time];
IF ABS[y-height]+4 > dt+dy THEN LOOP;
IF ABS[y-height]+4 = dt+dy THEN IF y>height THEN LOOP;
dt ← 4; dy ← ABS[y-height];
IF time < (beamHeap[i].sync1.time+beamHeap[i].sync2.time+4)/2
THEN obj ←leftBeam
ELSE obj ←rightBeam;
p ← beamHeap[i];
ENDLOOP;
measure sections, signatures and other events
FOR i IN [s..scoreLength) DO
IF score[i].time< time - 300 THEN LOOP;
IF score[i].time> time + 300 THEN {s ← i; EXIT};
IF score[i].time<sheet[l].time THEN LOOP;
FOR j IN [0..syncLength) DO --ties
IF (n ← score[i].event[j]) = NIL THEN EXIT;
IF voice AND n.voice#selectedVoice THEN LOOP;
IF n.tie=NIL THEN LOOP;
IF n.sync.time<time OR n.tie.sync.time>time THEN LOOP;
t ← ABS[(n.sync.time+n.tie.sync.time)/2-time];
key ← Score.GetKey[n.sync.time];
show ← Score.ShowPitch[n.pitch, n.spelled, key];
y ← ABS[height-(Sheet.Height[n.sync.time, show, n.staff]+n.tieHeight/2)];
IF y+t>dy+dt THEN LOOP;
IF y+t=dy+dt THEN IF t>dt THEN LOOP;
dt ← t; dy ← y;
obj ← tie; p ← n;
ENDLOOP;
IF score[i].time>=sheet[next].time THEN LOOP;
IF ABS[score[i].time-time]>40 THEN LOOP;
IF score[i].type IN SheetSwitch AND score[i].type#staves THEN {
octava: SyncPTR ← NIL;
staff, top, bottom: INTEGER ← score[i].value;
t ← score[i].time;
IF score[i].type=clef THEN t ← t+6;
IF ABS[t-time]+6> dt + dy THEN LOOP;
IF ABS[t-time]+6=dt+dy AND t<time THEN LOOP;
IF score[i].type=octava2 THEN octava ← Sync.Octava[score[i]];
IF octava#NIL THEN staves ← LOOPHOLE[@octava.event]
ELSE staves ← LOOPHOLE[@score[i].event];
SELECT staves.staff[staff].pitch FROM
60, 15 => {top ← staves.height+5; bottom ← staves.height-5};
ENDCASE => {top ← 32; bottom ← 0};
IF height>Sheet.Height[score[i].time,, staff]+top THEN LOOP;
IF height<Sheet.Height[score[i].time,, staff]+bottom THEN LOOP;
dt ← ABS[t-time]; dy ← 6;
p ← score[i];
obj ← measure;
LOOP};
IF score[i].type#notes THEN {
IF ABS[score[i].time-time]+6> dt + dy THEN LOOP;
IF ABS[score[i].time-time]+6=dt+dy AND score[i].time<time THEN LOOP;
IF score[i].type=staves AND score[i].time<1 THEN LOOP; -- don't touch the first one
staves ← sheet[Sheet.FindSection[score[i].time]].staves;
IF height>Sheet.Height[score[i].time,, 0]+32 THEN LOOP;
IF height<Sheet.Height[score[i].time,, staves.sl] THEN LOOP;
dt ← ABS[score[i].time-time]; dy ← 6;
p ← score[i];
obj ← measure;
LOOP};
FOR j IN [0..syncLength) DO --notes
IF (n ← score[i].event[j]) = NIL THEN EXIT;
IF voice AND n.voice#selectedVoice THEN LOOP;
t ← ABS[score[i].time+5+Note.Delta[n]-time];
y ← ABS[height-Sheet.Height[n.sync.time, n.pitch, n.staff]];
IF y+t>dy+dt THEN LOOP;
IF y+t=dy+dt AND t>dt THEN LOOP;
dt ← t; dy ← y;
p ← n; obj ← note;
ENDLOOP;
ENDLOOP; -- end of this line, go to next
time ← time+ (staffLength - sheet[next].x);
height ← height- (sheet[next].y - sheet[l].y);
l ← next;
next ← Sheet.NextLine[l];
ENDLOOP;
IF dt>10 OR dy>7 THEN p ← NIL;
};
END.