--Author: John Maxwell
--last modified: November 28, 1981 8:35 AM

DIRECTORY
Beam USING [Remove,SetSyncs,Sort,time],
Chord USING [Beam,Sort],
MusicDefs,
Real USING [FixI],
Sheet USING [Height],
Utility USING [FreeBeam];



BeamImplA: PROGRAM
IMPORTS Beam, Chord, MusicDefs, Real, Sheet, Utility
EXPORTS Beam, MusicDefs =

BEGIN
OPEN MusicDefs, Utility;

endOfBeam:PUBLIC VariousPTR←[note[NIL]]; --avoids a call to BlockEqualCodeLong


--**************************************************************************
--data abstractions for a
beam
--CONSTRAINT: IF b.chord[i]=NIL THEN b.chord[j]=NIL for all j>i
--CONSTRAINT: b.chord[i].sync.time<=b.chord[i+1].sync.time for all i
--**************************************************************************

AddNote
:PUBLIC PROCEDURE[b:BeamPTR,n:NotePTR] =
BEGIN
oldBeam:BeamPTR=n.beam;
i,j:CARDINAL←beamLength-1;
FOR i IN [0..beamLength) DO IF b.chord[i]=[note[n]] THEN RETURN; ENDLOOP;
IF b.sync1=NIL THEN b.sync1←n.sync;
IF b.sync2=NIL THEN b.sync2←n.sync;
IF n.sync.time < b.sync1.time THEN b.sync1 ← n.sync;
IF n.sync.time > b.sync2.time THEN b.sync2 ← n.sync;
FOR i IN [0..beamLength) DO
IF b.chord[i]=endOfBeam THEN BEGIN j ← i; EXIT; END;
IF Beam.time[b.chord[i]]>n.sync.time THEN j ← i;
IF j#beamLength-1 THEN EXIT;
ENDLOOP;
IF b.chord[beamLength-1]#endOfBeam THEN Overflow[beam];
FOR i DECREASING IN (j..beamLength) DO
b.chord[i] ← b.chord[i-1];
ENDLOOP;
b.chord[j] ← [note[n]];
n.beam ← b;
IF oldBeam#NIL AND oldBeam#b THEN Beam.Remove[n.beam,n,NIL,NIL];
IF b.beam#NIL THEN Beam.SetSyncs[b.beam];
END;

AddChord:PUBLIC PROCEDURE[b:BeamPTR,c:ChordPTR] =
BEGIN
i,j:CARDINAL←beamLength-1;
oldBeam:BeamPTR = Chord.Beam[c];
FOR i IN [0..beamLength) DO IF b.chord[i]=[chord[c]] THEN RETURN; ENDLOOP;
IF b.sync1=NIL THEN b.sync1←c.note[0].sync;
IF b.sync2=NIL THEN b.sync2←c.note[0].sync;
IF c.note[0].sync.time < b.sync1.time THEN b.sync1 ← c.note[0].sync;
IF c.note[0].sync.time > b.sync2.time THEN b.sync2 ← c.note[0].sync;
FOR i IN [0..beamLength) DO
IF b.chord[i]=endOfBeam THEN BEGIN j ← i; EXIT; END;
IF Beam.time[b.chord[i]]>c.note[0].sync.time THEN j ← i;
IF j#beamLength-1 THEN EXIT;
ENDLOOP;
IF b.chord[beamLength-1]#endOfBeam THEN Overflow[beam];
FOR i DECREASING IN (j..beamLength) DO
b.chord[i] ← b.chord[i-1];
ENDLOOP;
b.chord[j] ← [chord[c]];
FOR i IN [0..chordLength) DO
IF c.note[i]=NIL THEN EXIT;
c.note[i].beam ← b;
ENDLOOP;
IF oldBeam#NIL AND oldBeam#b THEN Beam.Remove[oldBeam,NIL,c,NIL];
IF b.beam#NIL THEN Beam.SetSyncs[b.beam];
END;


AddBeam:PUBLIC PROCEDURE[b:BeamPTR,new:BeamPTR] =
BEGIN
oldBeam:BeamPTR=b.beam;
i,j:CARDINAL←beamLength-1;
FOR i IN [0..beamLength) DO IF b.chord[i]=[beam[new]] THEN RETURN; ENDLOOP;
IF b.sync1=NIL THEN b.sync1←new.sync1;
IF b.sync2=NIL THEN b.sync2←new.sync2;
IF new.sync1.time < b.sync1.time THEN b.sync1 ← new.sync1;
IF new.sync2.time > b.sync2.time THEN b.sync2 ← new.sync2;
FOR i IN [0..beamLength) DO
IF b.chord[i]=endOfBeam THEN BEGIN j ← i; EXIT; END;
IF Beam.time[b.chord[i]]>new.sync1.time THEN j←i;
IF j#beamLength-1 THEN EXIT;
ENDLOOP;
IF b.chord[beamLength-1]#endOfBeam THEN Overflow[beam];
FOR i DECREASING IN (j..beamLength) DO
b.chord[i] ← b.chord[i-1];
ENDLOOP;
b.chord[j] ← [beam[new]];
new.beam ← b;
IF oldBeam#NIL AND oldBeam#b THEN Beam.Remove[oldBeam,NIL,NIL,new];
IF b.beam#NIL THEN Beam.SetSyncs[b.beam];
END;


Remove:PUBLIC PROCEDURE[beam:BeamPTR,n:NotePTR,c:ChordPTR,b:BeamPTR] =
BEGIN
i,j:CARDINAL;
found:BOOLEAN ← FALSE;
sync1,sync2:SyncPTR ← NIL;
IF beam=NIL THEN RETURN;
FOR i IN [0..beamLength) DO
IF beam.chord[i]=endOfBeam THEN EXIT;
IF beam.chord[i]#[beam[b]] AND beam.chord[i]#[chord[c]] AND
beam.chord[i]#[note[n]] THEN LOOP;
IF beam.chord[i]=[note[n]] THEN n.beam←NIL;
IF beam.chord[i]=[beam[b]] THEN b.beam←NIL;
IF beam.chord[i]=[chord[c]] THEN FOR j IN [0..chordLength) DO
IF c.note[j]=NIL THEN EXIT;
c.note[j].beam ← NIL;
ENDLOOP;
FOR j IN [i..beamLength-1) DO
beam.chord[j] ← beam.chord[j+1];
ENDLOOP;
beam.chord[beamLength-1] ← endOfBeam;
found ← TRUE;
ENDLOOP;
IF NOT found THEN RETURN;
IF beam.chord[1]=endOfBeam THEN {FreeBeam[@beam]; RETURN};
-- reset beamsyncs if needed
IF n#NIL THEN sync1 ← n.sync;
IF c#NIL THEN sync2 ← c.note[0].sync;
IF b#NIL THEN BEGIN sync1 ← b.sync1; sync2 ← b.sync2; END;
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];
END;

Sort:PUBLIC PROCEDURE[b:BeamPTR] =
BEGIN
i,j:CARDINAL;
temp:VariousPTR;
FOR i IN [0..beamLength) DO
IF b.chord[i] = endOfBeam THEN EXIT;
FOR j IN (i..beamLength) DO
IF b.chord[j] = endOfBeam THEN EXIT;
IF Beam.time[b.chord[i]] <= Beam.time[b.chord[j]] THEN LOOP;
temp ← b.chord[i];
b.chord[i] ← b.chord[j];
b.chord[j] ← temp;
ENDLOOP;
ENDLOOP;
END;

SetSyncs:PUBLIC PROCEDURE[b:BeamPTR] =
BEGIN
i:CARDINAL;
sync1,sync2:SyncPTR;
b.sync1 ←b.sync2 ← NIL;
FOR i IN [0..beamLength) DO
IF b.chord[i]=endOfBeam THEN EXIT;
sync1 ← sync2 ← NIL;
WITH ev:b.chord[i] SELECT FROM
note => sync1 ← ev.n.sync;
chord=> sync2 ← ev.c.note[0].sync;
beam => BEGIN sync1 ← ev.b.sync1; sync2 ← ev.b.sync2; END;
ENDCASE;
IF sync1=NIL THEN sync1 ← sync2;
IF sync2=NIL THEN sync2 ← sync1;
IF b.sync1=NIL THEN b.sync1 ← sync1;
IF b.sync2=NIL THEN b.sync2 ← sync2;
IF sync1=NIL OR sync2=NIL THEN LOOP;
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];
END;

SetStems:PUBLIC PROCEDURE[b:BeamPTR] =
--make the chord stems consistent with the position of the beam
BEGIN
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[b.sync1.time,,b.staff];
FOR i IN [0..beamLength) DO
IF b.chord[i]=endOfBeam THEN EXIT;
WITH ev:b.chord[i] SELECT FROM
note => {
heighti ← Sheet.Height[ev.n.sync.time,ev.n.pitch,ev.n.staff];
timei ← Beam.time[b.chord[i]];
height ← Real.FixI[heightb+b.tilt*(timei-time1)];
ev.n.stemUp ← (height > heighti);
b.staff ← ev.n.staff};
chord=> {
[] ← Chord.Sort[ev.c,FALSE];
heighti ← Sheet.Height[ev.c.note[0].sync.time,ev.c.note[0].pitch,ev.c.note[0].staff];
timei ← Beam.time[b.chord[i]];
height ← Real.FixI[heightb+b.tilt*(timei-time1)];
ev.c.stemUp ← (height > heighti);
b.staff ← ev.c.note[0].staff};
beam => {
ev.b.tilt ← b.tilt;
height ← Real.FixI[heightb+b.tilt*(ev.b.sync1.time-time1)];
ev.b.height ← height-Sheet.Height[0,,ev.b.staff];
SetStems[ev.b];
b.staff ← ev.b.staff};
ENDCASE;
ENDLOOP;
b.height ← heightb - Sheet.Height[b.sync1.time,,b.staff];
END;

--**************************************************************************
--beam attributes
--**************************************************************************

Grace:PUBLIC PROCEDURE[b:BeamPTR] RETURNS[BOOLEAN] =
BEGIN -- a grace beam doesn’t have ANY non-grace notes
i,j:CARDINAL;
FOR i IN [0..beamLength) DO
IF b.chord[i]=endOfBeam THEN EXIT;
WITH ev:b.chord[i] SELECT FROM
note => IF ~ev.n.grace THEN RETURN[FALSE];
chord => FOR j IN [0..chordLength) DO
IF ev.c.note[j]=NIL THEN EXIT;
IF ~ev.c.note[j].grace THEN RETURN[FALSE];
ENDLOOP;
beam => IF ~Grace[ev.b] THEN RETURN[FALSE];
ENDCASE;
ENDLOOP;
RETURN[TRUE];
END;

InVoice:PUBLIC PROCEDURE[b:BeamPTR,voice:CARDINAL] RETURNS[BOOLEAN] =
BEGIN
i,j:CARDINAL;
FOR i IN [0..beamLength) DO
IF b.chord[i]=endOfBeam THEN EXIT;
WITH ev:b.chord[i] SELECT FROM
note => IF ev.n.voice=voice THEN RETURN[TRUE];
chord => FOR j IN [0..chordLength) DO
IF ev.c.note[j]=NIL THEN EXIT;
IF ev.c.note[j].voice=voice THEN RETURN[TRUE];
ENDLOOP;
beam => IF InVoice[ev.b,voice] THEN RETURN[TRUE];
ENDCASE;
ENDLOOP;
RETURN[FALSE];
END;

END.