PieceImplA.mesa
Copyright (C) 1983, 1984 Xerox Corporation. All rights reserved.
Author: John Maxwell
Last Edited by: Maxwell, November 22, 1983 1:00 pm
Last Edited by: Doug Wyatt, June 14, 1984 9:56:13 pm PDT
DIRECTORY
Event USING [GetOctava, GetScoreIndex, Octava],
MusicDefs USING [CacheRec, EventPTR, EventRec, NotePTR, ScorePTR, ScoreRec, SetDirty, SheetPTR, StavesPTR, StylePTR, StyleRec, SyncPTR, Time],
Note USING [Delta, Free, InVoice],
Piece USING [],
Score USING [GetKey, ShowPitch],
Sheet USING [FindLine, Height, NearestTime, New, NextLine];
PieceImplA: CEDAR PROGRAM
IMPORTS Event, MusicDefs, Note, Score, Sheet
EXPORTS Piece
= BEGIN OPEN MusicDefs;
dataStructureInFlux: PUBLIC BOOLEANFALSE;
****************************************************************************
data abstractions for a piece
CONSTRAINT: score.event[i-1].time <= score.event[i].time <= score.event[i+1].time
CONSTRAINT: score.event[i] # NIL for all i such that 0 <= i < score.length
CONSTRAINT: score.event[score.length] = NIL
CONSTRAINT: score.event[i] # score.event[j] for all i # j < score.length
****************************************************************************
AddEvent: PUBLIC PROC[score: ScorePTR, event: EventPTR] = {
any procedure that inserts syncs should process syncs in decreasing order
IF event = NIL THEN RETURN;
IF score.length+1 = score.max THEN score ← New[score.max+100, TRUE, score];
insert sync
dataStructureInFlux ← TRUE;
FOR i: NAT DECREASING IN[0..score.length) DO
IF score.event[i] = event THEN ERROR;
IF score.event[i].time < event.time THEN { score.event[i+1] ← event; event ← NIL; EXIT };
IF score.event[i].time = event.time AND ~LessThan[event, score.event[i]]
THEN {score.event[i+1] ← event; event ← NIL; EXIT};
score.event[i+1] ← score.event[i];
ENDLOOP;
IF event#NIL THEN score.event[0] ← event;
score.length ← score.length + 1;
dataStructureInFlux ← FALSE;
};
LessThan: PROC[a, b: EventPTR] RETURNS[BOOLEAN] = {
IsStyle: PROC[e: EventPTR] RETURNS[BOOL] ~ {
WITH e SELECT FROM
e: StavesPTR => RETURN[e.staves=style];
ENDCASE => RETURN[FALSE];
};
SELECT TRUE FROM
a.type = staves AND IsStyle[a] => RETURN[TRUE];
b.type = staves AND IsStyle[b] => RETURN[FALSE];
a.type = keySignature => RETURN[TRUE];
b.type = keySignature => RETURN[FALSE];
a.type = measure => RETURN[TRUE];
b.type = measure => RETURN[FALSE];
a.type = metronome => RETURN[TRUE];
b.type = metronome => RETURN[FALSE];
a.type = timeSignature => RETURN[TRUE];
b.type = timeSignature => RETURN[FALSE];
a.type = staves => RETURN[TRUE];
b.type = staves => RETURN[FALSE];
ENDCASE => RETURN[TRUE];
};
RemoveEvent: PUBLIC PROC[score: ScorePTR, event: EventPTR, free: BOOLEANFALSE] = {
length: CARDINAL ← 0;
octava: EventPTR ← NIL;
IF Event.Octava[event] AND free THEN octava ← Event.GetOctava[score, NARROW[event]];
FOR i: CARDINAL IN [0..score.length) DO
IF score.event[i] = event THEN LOOP;
IF i # length THEN score.event[length] ← score.event[i];
length ← length+1; ENDLOOP;
WITH event SELECT FROM
sync: SyncPTR => FOR i: NAT IN[0..sync.length) DO sync.note[i].sync ← NIL ENDLOOP;
ENDCASE;
score.length ← length;
score.event[score.length] ← NIL;
IF free THEN zone.FREE[@event];
IF free AND octava # NIL THEN {
SetDirty[score, octava.time, octava.time];
RemoveEvent[score, octava, FALSE];
zone.FREE[@octava]
};
};
nullScore: ScorePTR ← NEW[ScoreRec[0]];
New: PUBLIC PROC[length: CARDINAL, auxiliary: BOOL] RETURNS[score: ScorePTR] = {
score ← NEW[ScoreRec[length] ← [event: ]];
oldSize: CARDINAL;
nullSize: CARDINALSIZE[ScoreRec[0]];
score ← Utility.NewSegment[SIZE[ScoreRec[length]], length, nullSize-1];
IF old = NIL THEN old ← nullScore; -- gets the default values for us
oldSize ← SIZE[ScoreRec[old.length]];
Inline.LongCOPY[from: old, nwords: nullSize-1, to: score]; -- skip 'max'
Inline.LongCOPY[from: old+nullSize, nwords: oldSize-nullSize, to: score+nullSize];
IF score.max # length THEN ERROR; -- check that it worked.
IF old # nullScore THEN {
SetBackPointers[old, score];
Utility.FreeSegment[old]};
IF auxiliary THEN {
IF score.cache = NIL THEN score.cache ← NEW[CacheRec];
IF score.sheet = NIL THEN score.sheet ← Sheet.New[100];
IF score.style = NIL THEN score.style ← NewStyle[20];
};
};
NewStyle: PROC[length: NAT] RETURNS[StylePTR] = {
style: StylePTR ~ NEW[StyleRec[length]];
pianoroll
style[0] ← NEW[EventRec[staves] ← [variant: staves[
height: 0, offset: 60, length: 4,
staff: [[72,-32],[48,-88],[27,-136],[3,-196]]
]]];
one staff
style[1] ← NEW[EventRec[staves] ← [variant: staves[
height: 0, offset: 80, length: 4,
staff: [[48,-32],[48,-32],[48,-32],[48,-32]]
]]];
two staffs
style[2] ← NEW[EventRec[staves] ← [variant: staves[
height: 0, offset: 85, length: 4,
staff: [[48,-32],[48,-32],[27,-125],[27,-125]]
]]];
style[12] ← NEW[EventRec[staves] ← [variant: staves[
height: 0, offset: 85, length: 4,
staff: [[48,-32],[48,-32],[27,-150],[27,-150]]
]]];
three staffs
style[3] ← NEW[EventRec[staves] ← [variant: staves[
height: 0, offset: 75, length: 4,
staff: [[48,-32],[48,-32],[27,-110],[27,-185]]
]]];
style[13] ← NEW[EventRec[staves] ← [variant: staves[
height: 0, offset: 75, length: 4,
staff: [[48,-32],[48,-32],[27,-130],[27,-220]]
]]];
four staffs
style[4] ← NEW[EventRec[staves] ← [variant: staves[
height: 0, offset: 75, length: 4,
staff: [[48,-32],[48,-110],[27,-185],[27,-260]]
]]];
FOR i: NAT IN[0..length) DO
IF style[i]=NIL THEN style[i] ← NEW[EventRec[staves] ← [variant: staves[
height: 0, offset: 0, length: 0, staff: [[0, 0],[0, 0],[0, 0],[0, 0]]]]];
ENDLOOP;
style.length ← length;
RETURN[style];
};
Free: PUBLIC PROC[score: ScorePTR] =
{
IF score.beamHeap # NIL THEN { -- free all of the beams
FOR i: CARDINAL IN [0..score.beamHeap.length) DO
zone.FREE[@score.beamHeap[i]]; -- no need to carefully dismantle
ENDLOOP;
Utility.FreeSegment[score.beamHeap]};
IF score.chordHeap # NIL THEN { -- free all of the chords
FOR i: CARDINAL IN [0..score.chordHeap.length) DO
zone.FREE[@score.chordHeap[i]]; -- no need to carefully dismantle
ENDLOOP;
Utility.FreeSegment[score.chordHeap]};
FOR i: CARDINAL IN [0..score.length) DO
IF score.event[i] = NIL THEN LOOP;
IF score.event[i].type = sync THEN {
sync: SyncPTR = Event.Sync[score.event[i]];
FOR j: CARDINAL IN [0..sync.length) DO
zone.FREE[@sync.note[j]];
ENDLOOP};
zone.FREE[@score.event[i]];
ENDLOOP;
SetBackPointers[score, NIL];
Utility.FreeSegment[score];
};
SetBackPointers: PROC[old, new: ScorePTR] =
{
IF Selection.selection.score = old THEN Selection.selection.score ← new;
IF Selection.selection.score2 = old THEN Selection.selection.score2 ← new;
SIGNAL Overflow[old, new];
};
Sort: PUBLIC PROC[score: ScorePTR] = {
useful when going from physical to logical files
FOR i: CARDINAL IN [0..score.length) DO
FOR j: CARDINAL IN (i..score.length) DO
a: EventPTR ~ score.event[i];
b: EventPTR ~ score.event[j];
IF a.time > b.time THEN { score.event[i] ← b; score.event[j] ← a }
ELSE IF a.time = b.time THEN WITH a SELECT FROM
a: SyncPTR => WITH b SELECT FROM
b: SyncPTR => {
IF a.note[0]=NIL OR b.note[0]=NIL OR a.note[0].toc < b.note[0].toc THEN NULL
ELSE { score.event[i] ← b; score.event[j] ← a };
};
ENDCASE;
ENDCASE;
ENDLOOP;
ENDLOOP;
};
CleanUpEvents: PUBLIC PROC[score: ScorePTR] = {
length: CARDINAL ← 0;
FOR i: CARDINAL DECREASING IN [0..score.length) DO
WITH score.event[i] SELECT FROM
sync: SyncPTR => {
IF sync.length=0 THEN score.event[i] ← NIL
ELSE { score.event[length] ← score.event[i]; length ← length+1 };
};
ENDCASE;
ENDLOOP;
FOR i: CARDINAL IN [length..score.length) DO score.event[i] ← NIL; ENDLOOP;
score.length ← length;
score[score.length] ← NIL;
};
CleanUpNotes: PUBLIC PROC[score: ScorePTR] = {
FOR i: CARDINAL DECREASING IN [0..score.length) DO
WITH score.event[i] SELECT FROM
sync: SyncPTR => {
FOR j: CARDINAL DECREASING IN [0..sync.length) DO
IF sync.note[j].value # unknown THEN LOOP;
IF sync.note[j].duration # 0 THEN LOOP;
Note.Free[score, sync.note[j]]; -- may remove the sync from the score
ENDLOOP;
};
ENDCASE;
ENDLOOP;
CleanUpEvents[score];
};
****************************************************************************
what is being pointed at?
****************************************************************************
NearestEvent: PUBLIC PROC[score: ScorePTR, t: Time, syncsOnly: BOOLEANFALSE] RETURNS[index: CARDINAL] = {
delta: Time ← 10000;
index ← 0;
FOR binary: CARDINAL ← 8192, binary/2 UNTIL binary = 0 DO
IF index+binary >= score.length THEN LOOP;
IF score.event[index+binary].time <= t THEN index ← index+binary;
IF score.event[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, score.length]) DO
IF syncsOnly AND score.event[i].type # sync THEN LOOP;
IF ABS[score.event[i].time-t] > delta THEN LOOP;
index ← i; delta ← ABS[score.event[i].time-t];
ENDLOOP;
IF syncsOnly AND index # score.length AND score.event[index].type # sync
THEN index ← score.length;
};
NearestNote: PUBLIC PROC[score: ScorePTR, x, y: INTEGER] RETURNS[note: NotePTR] = {
n: NotePTR;
key: INTEGER;
sync: SyncPTR;
l, next: CARDINAL;
time, dt, t: Time ← 11;
sheet: SheetPTR ← score.sheet;
height, dy, show: INTEGER ← 8;
[time, height] ← Sheet.NearestTime[sheet, x, y];
next ← Sheet.FindLine[sheet, time];
time ← time - (sheet.width - sheet.section[next].x);
l ← Sheet.FindLine[sheet, time];
height ← height + (sheet.section[next].y - sheet.section[l].y);
FOR k: CARDINAL IN [0..2] DO
FOR i: CARDINAL IN [0..score.length) DO
IF score.event[i].type # sync THEN LOOP;
IF score.event[i].time < time - 40 THEN LOOP;
IF score.event[i].time > time + 40 THEN EXIT;
IF score.event[i].time < sheet.section[l].time THEN LOOP;
IF score.event[i].time >= sheet.section[next].time THEN EXIT;
sync ← NARROW[score.event[i]];
FOR j: CARDINAL IN [0..sync.length) DO
n ← sync.note[j];
IF ~Note.InVoice[n, score.sheet.voice] THEN LOOP;
t ← ABS[score[i].time+5+Note.Delta[score.sheet, n]-time];
key ← Score.GetKey[score, n.sync.time];
show ← Score.ShowPitch[score.sheet, n.pitch, n.spelled, key];
y ← ABS[height-Sheet.Height[sheet, 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;
note ← n;
ENDLOOP;
ENDLOOP;
time ← time+ (sheet.width - sheet.section[next].x);
height ← height- (sheet.section[next].y - sheet.section[l].y);
l ← next;
next ← Sheet.NextLine[sheet, l];
ENDLOOP;
IF dt > 10 OR dy > 7 THEN note ← NIL;
};
DeleteEvent: PUBLIC PROC[score: ScorePTR, event: EventPTR] = {
octava: StavesPTR ← NIL;
index: CARDINAL ← Event.GetScoreIndex[score, event];
IF Event.Octava[event] THEN octava ← Event.GetOctava[score, NARROW[event]];
RemoveEvent[score, event];
IF octava # NIL THEN {
SetDirty[score, octava.time, octava.time];
RemoveEvent[score, octava];
zone.FREE[@octava]
};
Utility.FreeEvent[@event];
};
END.
NearestObject: PUBLIC PROC[score: ScorePTR, x, y: INTEGER] RETURNS[obj: ObjectType, p: LONG POINTERNIL] = {
n: NotePTR;
l: CARDINAL;
key: INTEGER;
staves: StavesPTR;
time, dt, t: Time ← 11;
s, next: CARDINAL ← 0;
sheet: SheetPTR ← score.sheet;
height, dy, show: INTEGER ← 8;
[time, height] ← Sheet.NearestTime[sheet, x, y];
next ← Sheet.FindLine[sheet, time];
time ← time - (sheet.width - sheet.section[next].x);
l ← Sheet.FindLine[sheet, time];
height ← height + (sheet.section[next].y - sheet.section[l].y);
FOR k: CARDINAL IN [0..2] DO
left and right corners of a beam
FOR i: CARDINAL IN [0..score.beamHeap.length) DO
IF score.beamHeap.beam[i].beam # NIL AND score.beamHeap.beam[i].beam.beamed THEN LOOP;
IF score.beamHeap.beam[i].sync1.time-5 > time THEN LOOP;
IF score.beamHeap.beam[i].sync2.time+5 < time THEN LOOP;
IF ~Beam.InVoice[score.beamHeap.beam[i], score.sheet.voice] THEN LOOP;
y ← Beam.Height[sheet, score.beamHeap.beam[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 < (score.beamHeap.beam[i].sync1.time+score.beamHeap.beam[i].sync2.time+4)/2
THEN obj ← leftBeam
ELSE obj ← rightBeam;
p ← score.beamHeap.beam[i];
ENDLOOP;
measure sections, signatures and other events
FOR i: CARDINAL IN [s..score.length) DO
IF score.event[i].time < time - 300 THEN LOOP;
IF score.event[i].time > time + 300 THEN {s ← i; EXIT};
IF score.event[i].time < sheet.section[l].time THEN LOOP;
IF score.event[i].type = sync THEN { -- ties
sync: SyncPTR ← Event.Sync[score.event[i]];
FOR j: CARDINAL IN [0..sync.length) DO
n ← sync.note[j];
IF ~Note.InVoice[n, score.sheet.voice] 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[score, n.sync.time];
show ← Score.ShowPitch[score.sheet, n.pitch, n.spelled, key];
y ← ABS[height-(Sheet.Height[sheet, 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.event[i].time >= sheet[next].time THEN LOOP;
IF ABS[score.event[i].time-time] > 40 THEN LOOP;
IF score.event[i].type = staves THEN {
octava: StavesPTR ← NIL;
staves: StavesPTR ← Event.Staves[score.event[i]];
staff, top, bottom: INTEGER ← staves.value;
t ← staves.time;
IF staves.staves = 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 staves.staves = octava2 THEN staves ← Event.GetOctava[score, staves];
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[sheet, score.event[i].time, , staff]+top THEN LOOP;
IF height < Sheet.Height[sheet, score.event[i].time, , staff]+bottom THEN LOOP;
dt ← ABS[t-time]; dy ← 6;
p ← score.event[i];
obj ← measure;
LOOP};
IF score.event[i].type # sync THEN {
IF ABS[score.event[i].time-time]+6 > dt + dy THEN LOOP;
IF ABS[score.event[i].time-time]+6 = dt+dy AND score.event[i].time < time THEN LOOP;
IF score.event[i].type = staves AND score.event[i].time < 1 THEN LOOP; -- don't touch the first one
staves ← sheet.section[Sheet.FindSection[sheet, score[i].time]].staves;
IF height > Sheet.Height[sheet, score.event[i].time, , 0]+32 THEN LOOP;
IF height < Sheet.Height[sheet, score.event[i].time, , staves.length-1] THEN LOOP;
dt ← ABS[score.event[i].time-time]; dy ← 6;
p ← score.event[i];
obj ← measure;
LOOP};
IF score.event[i].type = sync THEN { -- notes
sync: SyncPTR ← Event.Sync[score.event[i]];
FOR j: CARDINAL IN [0..sync.length) DO
IF (n ← sync.note[j]) = NIL THEN EXIT;
IF ~Note.InVoice[n, score.sheet.voice] THEN LOOP;
t ← ABS[score.event[i].time+5+Note.Delta[score.sheet, n]-time];
y ← ABS[height-Sheet.Height[sheet, 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+ (sheet.width - sheet.section[next].x);
height ← height- (sheet.section[next].y - sheet.section[l].y);
l ← next;
next ← Sheet.NextLine[sheet, l];
ENDLOOP;
IF dt > 10 OR dy > 7 THEN p ← NIL;
};