-- Author: John Maxwell -- Last Edited by: Maxwell, November 22, 1983 10:12 am DIRECTORY Beam USING [AddItem, Free, GetHeapIndex, RemoveItem, SetSyncs, Sort, time], Chord USING [SetBeam, Sort], Inline USING [LongCOPY], MusicDefs, Real USING [FixI], Sheet USING [Height], Utility USING [FreeSegment, NewSegment]; BeamImplA: PROGRAM IMPORTS Beam, Chord, Inline, MusicDefs, Real, Sheet, Utility EXPORTS Beam = BEGIN OPEN MusicDefs, Utility; Overflow: PUBLIC SIGNAL[old, new: BeamPTR] = CODE; -- ************************************************************************** -- 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 -- ************************************************************************** AddItem: PUBLIC PROCEDURE[score: ScorePTR, beam: BeamPTR, item: VariousPTR] RETURNS[new: BeamPTR] = BEGIN oldBeam: BeamPTR; sync1, sync2: SyncPTR ← NIL; FOR i: CARDINAL IN [0..beam.length) DO IF beam.chord[i] = item THEN RETURN[beam]; ENDLOOP; IF beam.length >= beam.max THEN beam ← New[score, beam.max+4, beam]; WITH vp: item SELECT FROM note => {sync1 ← sync2 ← vp.n.sync; oldBeam ← vp.n.beam; vp.n.beam ← beam}; beam => {sync1 ← vp.b.sync1; sync2 ← vp.b.sync2; oldBeam ← vp.b; vp.b.beam ← beam}; chord => { sync1 ← sync2 ← vp.c.note[0].sync; oldBeam ← vp.c.note[0].beam; FOR i: CARDINAL IN [0..vp.c.length) DO vp.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 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]; RETURN[beam]; END; RemoveItem: PUBLIC PROCEDURE[score: ScorePTR, beam: BeamPTR, item: VariousPTR, free: BOOLEAN] = BEGIN found: BOOLEAN ← FALSE; sync1, sync2: SyncPTR ← NIL; IF beam = NIL THEN RETURN; FOR i: CARDINAL IN [0..beam.length) DO IF beam.chord[i] # item THEN LOOP; FOR j: CARDINAL IN [i..beam.length) DO beam.chord[j] ← beam.chord[j+1]; ENDLOOP; beam.length ← beam.length - 1; found ← TRUE; EXIT; ENDLOOP; IF NOT found THEN RETURN; WITH vp: item SELECT FROM note => {sync1 ← sync2 ← vp.n.sync; vp.n.beam ← NIL}; chord => {sync1 ← sync2 ← vp.c.note[0].sync; Chord.SetBeam[vp.c, NIL]}; beam => {sync1 ← vp.b.sync1; sync2 ← vp.b.sync2; vp.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]; END; New: PUBLIC PROCEDURE[score: ScorePTR, length: CARDINAL, old: BeamPTR] RETURNS[beam: BeamPTR] = BEGIN beam ← zone.NEW[BeamRec[length]]; IF old # NIL THEN { nullSize: CARDINAL = SIZE[BeamRec[0]]; oldSize: CARDINAL = SIZE[BeamRec[old.length]]; IF old.length > length THEN ERROR; Inline.LongCOPY[from: old, nwords: nullSize-1, to: beam]; -- skip 'max' Inline.LongCOPY[from: old+nullSize, nwords: oldSize - nullSize, to: beam+nullSize]; SetBackPointers[score, beam, old]; zone.FREE[@old]; RETURN}; 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; END; Free: PUBLIC PROCEDURE[score: ScorePTR, b: BeamPTR] = BEGIN SetBackPointers[score, b, NIL]; zone.FREE[@b]; END; SetBackPointers: PROC[score: ScorePTR, b, new: BeamPTR] = BEGIN -- changes all pointers to b to be pointers to new -- a beam can be pointed to by notes, beams, the beamHeap, and the cache index: CARDINAL ← Beam.GetHeapIndex[score.beamHeap, b]; score.beamHeap[index] ← new; IF new = NIL THEN { -- pack the beamHeap score.beamHeap.length ← score.beamHeap.length - 1; score.beamHeap[index] ← score.beamHeap[score.beamHeap.length]}; IF b.beam # NIL AND new # NIL THEN { -- replace beam with new in the higher beam highBeam: BeamPTR ← b.beam; Beam.RemoveItem[score, highBeam, [beam[b]], FALSE]; -- don't free beam! highBeam ← Beam.AddItem[score, highBeam, [beam[new]]]}; IF b.beam # NIL AND new = NIL THEN { -- put everything in the higher beam highBeam: BeamPTR ← b.beam; Beam.RemoveItem[score, highBeam, [beam[b]], b.length = 0]; -- don't free beam if length > 0 FOR i: CARDINAL DECREASING IN [0..b.length) DO -- add item to high beam highBeam ← Beam.AddItem[score, highBeam, b.chord[i]]; ENDLOOP}; FOR i: CARDINAL IN [0..b.length) DO WITH ev: b.chord[0] SELECT FROM note => ev.n.beam ← new; chord => Chord.SetBeam[ev.c, new]; beam => ev.b.beam ← new; ENDCASE; ENDLOOP; FOR i: CARDINAL DECREASING IN [0..score.cache.beamQueueLength) DO IF score.cache.beamQueue[i] = b THEN { score.cache.beamQueue[i] ← new; IF new = NIL THEN { -- pack the beamQueue score.cache.beamQueueLength ← score.cache.beamQueueLength - 1; score.cache.beamQueue[i] ← score.cache.beamQueue[score.cache.beamQueueLength]}}; ENDLOOP; END; Sort: PUBLIC PROCEDURE[b: BeamPTR] = BEGIN temp: VariousPTR; 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 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..b.length) DO WITH ev: b.chord[i] SELECT FROM note => sync1 ← sync2 ← ev.n.sync; chord => sync2 ← sync2 ← ev.c.note[0].sync; beam => BEGIN sync1 ← ev.b.sync1; sync2 ← ev.b.sync2; END; 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]; END; GetSyncs: PUBLIC PROCEDURE[b: BeamPTR] RETURNS[sync1, sync2: SyncPTR ← NIL] = BEGIN IF b.length = 0 THEN RETURN[NIL, NIL]; WITH chord: b.chord[0] SELECT FROM note => sync1 ← chord.n.sync; chord => IF chord.c.length > 0 THEN sync1 ← chord.c[0].sync; beam => sync1 ← GetSyncs[chord.b].sync1; ENDCASE => ERROR; WITH chord: b.chord[b.length-1] SELECT FROM note => sync2 ← chord.n.sync; chord => IF chord.c.length > 0 THEN sync2 ← chord.c[0].sync; beam => sync2 ← GetSyncs[chord.b].sync2; ENDCASE => ERROR; END; SetStems: PUBLIC PROCEDURE[sheet: SheetPTR, 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[sheet, b.sync1.time, , b.staff]; FOR i IN [0..b.length) DO WITH ev: b.chord[i] SELECT FROM note => { heighti ← Sheet.Height[sheet, 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[sheet, 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[sheet, 0, , ev.b.staff]; SetStems[sheet, ev.b]; b.staff ← ev.b.staff}; ENDCASE; ENDLOOP; b.height ← heightb - Sheet.Height[sheet, 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..b.length) 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..ev.c.length) DO 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 IF voice = noVoice THEN RETURN[TRUE]; FOR i: CARDINAL IN [0..b.length) 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: CARDINAL IN [0..ev.c.length) DO 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.