BeamImplA.mesa
Copyright (C) 1983, 1984 Xerox Corporation. All rights reserved.
Author: John Maxwell
Last Edited by: Maxwell, November 22, 1983 10:12 am
Last Edited by: Doug Wyatt, June 13, 1984 3:57:56 pm PDT
DIRECTORY
Beam USING [AddItem, Free, GetHeapIndex, RemoveItem, SetSyncs, Sort, time],
Chord USING [SetBeam, Sort],
MusicDefs USING [BeamHeapPTR, BeamPTR, BeamRec, ChordPTR, NotePTR, noVoice, ScorePTR, SheetPTR, SyncPTR, Time, VariousPTR],
Real USING [FixI],
Sheet USING [Height];
BeamImplA: CEDAR PROGRAM
IMPORTS Beam, Chord, Real, Sheet
EXPORTS Beam
= BEGIN OPEN MusicDefs;
**************************************************************************
data abstractions for a beam
CONSTRAINT: b.chord[i] # NIL for all i < b.length
CONSTRAINT: b.chord[i].sync.time <= b.chord[i+1].sync.time for all i < b.length
CONSTRAINT: b.sync1.time <= b.chord[i].sync.time for all i < b.length
CONSTRAINT: b.sync2.time >= b.chord[i].sync.time for all i < b.length
**************************************************************************
time: PUBLIC PROC[chord: VariousPTR] RETURNS[Time] ~ {
WITH chord SELECT FROM
n: NotePTR => RETURN[n.sync.time];
c: ChordPTR => RETURN[c.note[0].sync.time];
b: BeamPTR => { s: SyncPTR ~ GetSyncs[b].sync1; RETURN[IF s#NIL THEN s.time ELSE 0] };
ENDCASE => ERROR;
};
AddItem: PUBLIC PROC[score: ScorePTR, beam: BeamPTR, item: VariousPTR] = {
oldBeam: BeamPTR;
sync1, sync2: SyncPTR ← NIL;
FOR i: CARDINAL IN [0..beam.length) DO
IF beam.chord[i] = item THEN RETURN;
ENDLOOP;
IF NOT beam.length<beam.chord.max THEN {
old: Chords ~ beam.chord;
new: Chords ~ NEW[ChordsRec[old.max+4]];
FOR i: NAT IN[0..beam.length) DO new[i] ← old[i] ENDLOOP;
beam.chord ← new;
};
WITH item SELECT FROM
n: NotePTR => { sync1 ← sync2 ← n.sync; oldBeam ← n.beam; n.beam ← beam };
b: BeamPTR => { sync1 ← b.sync1; sync2 ← b.sync2; oldBeam ← b; b.beam ← beam };
c: ChordPTR => {
sync1 ← sync2 ← c.note[0].sync; oldBeam ← c.note[0].beam;
FOR i: NAT IN[0..c.length) DO c.note[i].beam ← beam ENDLOOP;
};
ENDCASE => ERROR;
IF oldBeam = beam THEN ERROR;
IF oldBeam#NIL THEN Beam.RemoveItem[score, oldBeam, item];
beam.chord[beam.length] ← item;
beam.length ← beam.length + 1;
IF sync1=NIL OR sync2=NIL THEN RETURN; -- for forward references during FileIn
IF beam.sync1=NIL THEN beam.sync1 ← sync1;
IF beam.sync2=NIL THEN beam.sync2 ← sync2;
IF sync1.time < beam.sync1.time THEN beam.sync1 ← sync1;
IF sync2.time > beam.sync2.time THEN beam.sync2 ← sync2;
Sort[beam];
IF beam.beam#NIL THEN Beam.SetSyncs[beam.beam];
};
RemoveItem: PUBLIC PROC[score: ScorePTR, beam: BeamPTR, item: VariousPTR,
free: BOOLEAN] = {
found: BOOLEANFALSE;
sync1, sync2: SyncPTR ← NIL;
IF beam=NIL THEN RETURN;
FOR i: NAT IN[0..beam.length) DO
IF beam.chord[i]=item THEN {
FOR j: NAT IN[i..beam.length) DO
beam.chord[j] ← beam.chord[j+1];
ENDLOOP;
beam.length ← beam.length - 1;
EXIT;
};
REPEAT FINISHED => RETURN;
ENDLOOP;
WITH item SELECT FROM
n: NotePTR => { sync1 ← sync2 ← n.sync; n.beam ← NIL };
c: ChordPTR => { sync1 ← sync2 ← c.note[0].sync; Chord.SetBeam[c, NIL] };
b: BeamPTR => { sync1 ← b.sync1; sync2 ← b.sync2; b.beam ← NIL };
ENDCASE => ERROR;
reset beam syncs if needed
IF beam.sync1 = sync1 OR beam.sync1 = sync2 THEN beam.sync1 ← NIL;
IF beam.sync2 = sync2 OR beam.sync2 = sync1 THEN beam.sync2 ← NIL;
IF beam.sync1 = NIL OR beam.sync2 = NIL THEN Beam.SetSyncs[beam];
IF free AND beam.length < 2 THEN Beam.Free[score, beam];
};
New: PUBLIC PROC[score: ScorePTR, length: CARDINAL] RETURNS[beam: BeamPTR] = {
beam ← NEW[BeamRec[length] ← [chord: ]];
IF score.beamHeap.length = score.beamHeap.max THEN { -- replace with a bigger one.
old: BeamHeapPTR = score.beamHeap;
nullSize: CARDINAL = SIZE[BeamHeapRec[0]];
oldSize: CARDINAL = SIZE[BeamHeapRec[score.beamHeap.max]];
newMax: CARDINAL = score.beamHeap.max+100;
new: BeamHeapPTR ← Utility.NewSegment[SIZE[BeamHeapRec[newMax]], newMax, nullSize-1];
Inline.LongCOPY[from: old, nwords: nullSize-1, to: new]; -- skip 'max'
Inline.LongCOPY[from: old+nullSize, nwords: oldSize-nullSize, to: new+nullSize];
IF new.max # newMax THEN ERROR;
score.beamHeap ← new;
Utility.FreeSegment[old]};
score.beamHeap[score.beamHeap.length] ← beam;
score.beamHeap.length ← score.beamHeap.length + 1;
};
Free: PUBLIC PROC[score: ScorePTR, b: BeamPTR] = {
changes all pointers to b to be NIL
a beam can be pointed to by notes, beams, the beamHeap, and the cache
index: NAT ~ Beam.GetHeapIndex[score.beamHeap, b];
score.beamHeap[index] ← NIL;
{ -- pack the beamHeap
score.beamHeap.length ← score.beamHeap.length - 1;
score.beamHeap[index] ← score.beamHeap[score.beamHeap.length]
};
IF b.beam#NIL THEN { -- put everything in the higher beam
highBeam: BeamPTR ← b.beam;
Beam.RemoveItem[score, highBeam, b, b.length = 0]; -- don't free beam if length > 0
FOR i: NAT DECREASING IN[0..b.length) DO -- add item to high beam
Beam.AddItem[score, highBeam, b.chord[i]];
ENDLOOP;
};
FOR i: NAT IN[0..b.length) DO
WITH b.chord[0] SELECT FROM
n: NotePTR => n.beam ← NIL;
c: ChordPTR => Chord.SetBeam[c, NIL];
b: BeamPTR => b.beam ← NIL;
ENDCASE;
ENDLOOP;
FOR i: NAT IN[0..3) DO
IF score.cache.beamQueue[i]=b THEN score.cache.beamQueue[i] ← NIL;
ENDLOOP;
FOR i: NAT DECREASING IN[0..score.cache.beamQueueLength) DO
IF score.cache.beamQueue[i] = b THEN {
score.cache.beamQueue[i] ← NIL;
{ -- pack the beamQueue
score.cache.beamQueueLength ← score.cache.beamQueueLength - 1;
score.cache.beamQueue[i] ← score.cache.beamQueue[score.cache.beamQueueLength];
};
};
ENDLOOP;
};
Sort: PUBLIC PROC[b: BeamPTR] = {
FOR i: CARDINAL IN [0..b.length) DO
FOR j: CARDINAL IN (i..b.length) DO
IF Beam.time[b.chord[i]]>Beam.time[b.chord[j]] THEN {
temp: VariousPTR ~ b.chord[i];
b.chord[i] ← b.chord[j];
b.chord[j] ← temp;
};
ENDLOOP;
ENDLOOP;
};
SetSyncs: PUBLIC PROC[b: BeamPTR] = {
sync1, sync2: SyncPTR;
b.sync1 ← b.sync2 ← NIL;
FOR i: NAT IN [0..b.length) DO
WITH b.chord[i] SELECT FROM
n: NotePTR => sync1 ← sync2 ← n.sync;
c: ChordPTR => sync1 ← sync2 ← c.note[0].sync;
b: BeamPTR => { sync1 ← b.sync1; sync2 ← b.sync2; };
ENDCASE;
IF b.sync1 = NIL THEN b.sync1 ← sync1;
IF b.sync2 = NIL THEN b.sync2 ← sync2;
IF b.sync1.time > sync1.time THEN b.sync1 ← sync1;
IF b.sync2.time <= sync2.time THEN b.sync2 ← sync2;
ENDLOOP;
Beam.Sort[b];
IF b.beam # NIL THEN SetSyncs[b.beam];
};
GetSyncs: PUBLIC PROC[b: BeamPTR] RETURNS[sync1, sync2: SyncPTR ← NIL] = {
IF b.length = 0 THEN RETURN[NIL, NIL];
WITH b.chord[0] SELECT FROM
n: NotePTR => sync1 ← n.sync;
c: ChordPTR => IF c.length > 0 THEN sync1 ← c[0].sync;
b: BeamPTR => sync1 ← GetSyncs[b].sync1;
ENDCASE => ERROR;
WITH b.chord[b.length-1] SELECT FROM
n: NotePTR => sync2 ← n.sync;
c: ChordPTR => IF c.length > 0 THEN sync2 ← c[0].sync;
b: BeamPTR => sync2 ← GetSyncs[b].sync2;
ENDCASE => ERROR;
};
SetStems: PUBLIC PROC[sheet: SheetPTR, b: BeamPTR] = {
make the chord stems consistent with the position of the beam
i: CARDINAL;
height, heighti, heightb: INTEGER;
time1, timei: Time ← b.sync1.time;
IF NOT b.beamed THEN RETURN;
determine the staff
heightb ← b.height+Sheet.Height[sheet, b.sync1.time, , b.staff];
FOR i IN [0..b.length) DO
WITH b.chord[i] SELECT FROM
n: NotePTR => {
heighti ← Sheet.Height[sheet, n.sync.time, n.pitch, n.staff];
timei ← Beam.time[b.chord[i]];
height ← Real.FixI[heightb+b.tilt*(timei-time1)];
n.stemUp ← (height > heighti);
b.staff ← n.staff;
};
c: ChordPTR => {
[] ← Chord.Sort[c, FALSE];
heighti ← Sheet.Height[sheet, c.note[0].sync.time, c.note[0].pitch, c.note[0].staff];
timei ← Beam.time[b.chord[i]];
height ← Real.FixI[heightb+b.tilt*(timei-time1)];
c.stemUp ← (height > heighti);
b.staff ← c.note[0].staff;
};
b: BeamPTR => {
b.tilt ← b.tilt;
height ← Real.FixI[heightb+b.tilt*(b.sync1.time-time1)];
b.height ← height-Sheet.Height[sheet, 0, , b.staff];
SetStems[sheet, b];
b.staff ← b.staff;
};
ENDCASE;
ENDLOOP;
b.height ← heightb - Sheet.Height[sheet, b.sync1.time, , b.staff];
};
**************************************************************************
beam attributes
**************************************************************************
Grace: PUBLIC PROC[b: BeamPTR] RETURNS[BOOL] = {
a grace beam doesn't have ANY non-grace notes
FOR i: NAT IN[0..b.length) DO
IF b.chord[i] = NIL THEN EXIT;
WITH b.chord[i] SELECT FROM
n: NotePTR => IF ~n.grace THEN RETURN[FALSE];
c: ChordPTR => FOR j: NAT IN [0..c.length) DO
IF ~c.note[j].grace THEN RETURN[FALSE];
ENDLOOP;
b: BeamPTR => IF ~Grace[b] THEN RETURN[FALSE];
ENDCASE;
ENDLOOP;
RETURN[TRUE];
};
InVoice: PUBLIC PROC[b: BeamPTR, voice: CARDINAL] RETURNS[BOOL] = {
IF voice = noVoice THEN RETURN[TRUE];
FOR i: NAT IN [0..b.length) DO
IF b.chord[i] = NIL THEN EXIT;
WITH b.chord[i] SELECT FROM
n: NotePTR => IF n.voice = voice THEN RETURN[TRUE];
c: ChordPTR => FOR j: NAT IN [0..c.length) DO
IF c.note[j].voice = voice THEN RETURN[TRUE];
ENDLOOP;
b: BeamPTR => IF InVoice[b, voice] THEN RETURN[TRUE];
ENDCASE;
ENDLOOP;
RETURN[FALSE];
};
GetHeapIndex: PUBLIC PROC[heap: BeamHeapPTR, p: BeamPTR] RETURNS[NAT] = {
FOR i: NAT IN[0..heap.length) DO
IF heap.beam[i] = p THEN RETURN[i];
ENDLOOP;
RETURN[heap.length];
};
Height: PUBLIC PROC[sheet: SheetPTR, b: BeamPTR, time: Time] RETURNS[h: INTEGER] = {
sync1: SyncPTR ← GetSyncs[b].sync1;
h ← b.height+Sheet.Height[sheet, sync1.time, , b.staff];
IF time # sync1.time THEN h ← h + Real.FixI[(time-sync1.time)*b.tilt];
};
END.