--Author: John Maxwell
--last modified: December 18, 1981 8:45 AM

DIRECTORY
BcplFontFileDefs USING [OpenSDFontFile, CloseSDFontFile,
SplineCommandPtr,SplineDataPtr,GetSplineCommands],
Beam USING [AddBeam,AddChord,AddNote,GetHeapIndex,Remove],
Chord USING [GetHeapIndex,RemoveNote],
Cubic USING [Bezier,BezierToCoeffs,Coeffs],
Device USING [Free, Handle, Object],
Graphics USING [DisplayContext, DisplayChar, DisplayString, DrawArea, DrawTo, EnterCubic, EnterPoint, FontId, FreeContext, GetPosition, Map, MoveTo, NewBoundary, NewContext, RelMoveTo, Scale, SetFont, SetLineWidth, SetColor, StartAreaPath, Texture, Translate, UserToScreen, Vec],
IODefs USING [WriteLine,WriteNumber,WriteString],
Memory USING [NewZone],
MusicDefs,
Note USING [GetBackTie],
OpaqueDevice,
Piece USING [Length,RemoveSync],
PressDefs USING [PressFileDescriptor,PutText,SetFont],
PressDevice USING [NewPressDevice],
PressDeviceImpl USING [DataRef],
Real USING [FixC],
Score USING [],
Sync USING [GetScoreIndex, Octava, RemoveNote],
SystemDefs USING [AllocateHeapNode,AllocateSegment,FreeHeapNode,FreeSegment],
StringDefs USING [AppendLongNumber],
Utility USING [FreeBeam, FreeChord, FreeSync, SetFont];

UtilityImpl: PROGRAM
IMPORTS BcplFontFileDefs, Beam, Chord, Cubic, Device, Graphics, IODefs, Memory, MusicDefs, Note, Piece, PressDefs, PressDevice, PressDeviceImpl, Real, StringDefs, Sync, SystemDefs, Utility
EXPORTS MusicDefs, OpaqueDevice, Score, Utility
SHARES PressDeviceImpl =

BEGIN
OPEN Graphics,MusicDefs;

--****************************************************************************
--graphics procedures
--****************************************************************************

context
:PUBLIC DisplayContext;
text,music:PUBLIC FontId;
light:PUBLIC Graphics.Texture ← 102041B;

DrawLine: PUBLIC PROCEDURE[x1,y1,x2,y2:INTEGER] =
BEGIN
MoveTo[context,[x1,y1]];
DrawTo[context,[x2,y2]];
END;

DrawCubic:PUBLIC PROC[x1,y1,x2,y2,height:INTEGER] =
BEGIN
c:Cubic.Coeffs;
b1,b2:Cubic.Bezier;
StartAreaPath[context];
EnterPoint[context,[x1,y1]];
b1 ← [[x1,y1],[(4*x1+x2)/5,height+y1],[(x1+4*x2)/5,height+y1],[x2,y2]];
c ← Cubic.BezierToCoeffs[b1];
EnterCubic[context,@c];
EnterPoint[context,[x2,y2-width1]];
b2 ← [[x2,y2-width1],[(x1+4*x2)/5,height-width2+y1],
[(4*x1+x2)/5,height-width2+y1],[x1,y1-width1]];
c ← Cubic.BezierToCoeffs[b2];
EnterCubic[context,@c];
DrawArea[context];
END;

width1:INTEGER ← 0;
width2:INTEGER ← 3;

SetFont:PUBLIC PROCEDURE[dc:DisplayContext,font:FontId,size:INTEGER]=
BEGIN
OPEN PressDeviceImpl;
l:REAL←1;
Graphics.SetFont[dc,font,size];
IF NOT print THEN {Graphics.SetFont[dc,font,size]; RETURN};
IF printChar THEN BEGIN
fontname:STRING←[16];
ph:POINTER TO PressDefs.PressFileDescriptor;
ptsize,face,rotation:CARDINAL←0;
ph←LOOPHOLE[device.data,PressDeviceImpl.DataRef].pressHandle;
IF font=music THEN fontname←"MOCKINGBIRD" ELSE fontname←"TIMESROMAN";
ptsize ← IF font=music THEN LOOPHOLE[-24] ELSE 8;
face ← 0; --PressDefs.EncodeFace[’n,’n,’n]
PressDefs.SetFont[ph,fontname,ptsize,face,rotation];
END
ELSE BEGIN
BcplFontFileDefs.CloseSDFontFile[];
IF font=music THEN BcplFontFileDefs.OpenSDFontFile["music8.sd"]
ELSE BcplFontFileDefs.OpenSDFontFile["timesroman.sd"];
IF font=text THEN Scale[context,[12,12]] ELSE Scale[context,[l/12,l/12]];
END;
END;

DrawString:PUBLIC PROCEDURE[dc:DisplayContext,s:STRING] =
BEGIN
screen,pos:Vec←GetPosition[context];
IF NOT print THEN {DisplayString[dc,s]; RETURN};
IF printChar THEN {
ph:POINTER TO PressDefs.PressFileDescriptor;
ph←LOOPHOLE[device.data,PressDeviceImpl.DataRef].pressHandle;
screen ← UserToScreen[context,pos];
IF screen.x<0 OR screen.y<0 THEN RETURN;
PressDefs.PutText[ph,s,Real.FixC[screen.x],Real.FixC[screen.y]]}
ELSE {
FOR i:CARDINAL IN [0..s.length) DO
DrawChar[dc,s[i]];
RelMoveTo[dc,[5,0]];
ENDLOOP};
END;

DrawChar:PUBLIC PROCEDURE[dc:Graphics.DisplayContext,c:CHARACTER]=
BEGIN
screen,pos:Vec←GetPosition[context];
Move: PROC[v: POINTER TO Vec] = { NewBoundary[dc]; EnterPoint[dc,v↑] };
Draw: PROC[v: POINTER TO Vec] = { EnterPoint[dc,v↑] };
ECubic: PROC[c: POINTER TO Cubic.Coeffs] = { EnterCubic[dc,c] };
IF NOT print THEN BEGIN DisplayChar[dc,c]; RETURN; END;
IF printChar THEN BEGIN
s:STRING←[1];
ph:POINTER TO PressDefs.PressFileDescriptor;
ph←LOOPHOLE[device.data,PressDeviceImpl.DataRef].pressHandle;
s.length←1; s[0]←c;
screen ← UserToScreen[context,pos];
IF screen.x<0 OR screen.y<0 THEN RETURN;
PressDefs.PutText[ph,s,Real.FixC[screen.x],Real.FixC[screen.y]];
END
ELSE BEGIN
Translate[context,pos];
StartAreaPath[context,IF c=’P OR c=’X THEN FALSE ELSE TRUE];
DoSDChar[c,Move,Draw,ECubic];
DrawArea[context];
Translate[context,[-pos.x,-pos.y]];
END;
END;

ndp:BcplFontFileDefs.SplineDataPtr←NIL;
ncp:BcplFontFileDefs.SplineCommandPtr←NIL;

DoSDChar: PROCEDURE[char: CHARACTER,
Move: PROC[v: POINTER TO Vec],
Draw: PROC[v: POINTER TO Vec],
DCubic: PROC[c: POINTER TO Cubic.Coeffs]] =
BEGIN
pos: Vec←[0,0];
tscp,scp:BcplFontFileDefs.SplineCommandPtr;
sdp:BcplFontFileDefs.SplineDataPtr;
IF char=154C AND ndp#NIL THEN {sdp←ndp; scp←ncp}
ELSE [sdp,scp]←BcplFontFileDefs.GetSplineCommands[char,
SystemDefs.AllocateHeapNode];
IF char=154C AND ndp=NIL THEN {ndp←sdp; ncp←scp};
tscp←scp;
UNTIL scp=NIL DO
WITH scp SELECT FROM
MoveTo => {pos←[x,y]; Move[@pos]};
DrawTo => {pos←[x,y]; Draw[@pos]};
DrawCurve => {
c: Cubic.Coeffs←[c3:[x2,y2],c2:[x1,y1],c1:[x0,y0],c0:pos];
DCubic[@c]; pos←[pos.x+x0+x1+x2, pos.y+y0+y1+y2];
};
NewObject => NULL;
EndDefinition => EXIT;
ENDCASE;
scp←scp.next;
ENDLOOP;
IF char#154C THEN UNTIL (scp←tscp)=NIL DO
tscp←scp.next;
SystemDefs.FreeHeapNode[scp];
ENDLOOP;
END;

PointSize:PROCEDURE[n:INTEGER] RETURNS[INTEGER] =
INLINE BEGIN
SELECT scale FROM
1 => RETURN[n];
2 => RETURN[2*n/3];
4 => RETURN[n/4];
ENDCASE;
RETURN[n];
END;

--**********************************************************
--
printing the score
--**********************************************************

print:PUBLIC BOOLEAN←FALSE;
olddc:Graphics.DisplayContext;
printChar:BOOLEAN←FALSE;
DeviceObject: PUBLIC TYPE = Device.Object; -- exported to OpaqueDevice
device:Device.Handle;

OpenPressDevice:PUBLIC PROCEDURE[splines:BOOLEAN] RETURNS[Device.Handle] =
BEGIN
l:REAL←1;
pos:Graphics.Vec;
printChar ← ~splines AND scale=2;
print←TRUE;
olddc←context;
device←PressDevice.NewPressDevice["music.press"];
context←Graphics.NewContext[device];
pos ← Graphics.Map[olddc,context,[0,0]];
pos.x ← pos.x+8;
pos.y ← pos.y+28;
Graphics.Translate[context,pos];
Graphics.SetLineWidth[context,1];
IF scale=2 THEN Scale[context,[(2*l)/3,(2*l)/3]] ELSE Scale[context,[l/scale,l/scale]];
SetColor[context,[0,0,0]];
IF NOT printChar THEN {
Graphics.Scale[context,[12,12]]; -- to offset bug in something
BcplFontFileDefs.OpenSDFontFile["music8.sd"];
[ndp,ncp]←BcplFontFileDefs.GetSplineCommands[0154C,SystemDefs.AllocateHeapNode]};
Utility.SetFont[context,music,8];
RETURN[device];
END;

ClosePressDevice:PUBLIC PROCEDURE[device:POINTER TO Device.Handle]=
BEGIN
tncp:BcplFontFileDefs.SplineCommandPtr;
Graphics.FreeContext[@context];
Device.Free[device];
IF NOT printChar THEN BEGIN
BcplFontFileDefs.CloseSDFontFile[];
UNTIL ncp=NIL DO
tncp←ncp.next;
SystemDefs.FreeHeapNode[ncp];
ncp ← tncp;
ENDLOOP;
END;
context ← olddc;
print←FALSE;
END;



--****************************************************************************
--Basic Allocation Procedures
--****************************************************************************

zone:UNCOUNTED ZONE = Memory.NewZone["musicZone"];

chordHeap:PUBLIC POINTER TO ARRAY [0..maxChordHeapLength) OF ChordPTR←NIL;
chordHeapLength:PUBLIC CARDINAL;

beamHeap:PUBLIC POINTER TO ARRAY [0..maxBeamHeapLength) OF BeamPTR←NIL;
beamHeapLength:PUBLIC CARDINAL;

InitStorage:PUBLIC PROCEDURE =
BEGIN
i,j:CARDINAL;
IF beamHeap#NIL THEN FOR i IN [0..beamHeapLength) DO
IF beamHeap[i]#NIL THEN zone.FREE[@beamHeap[i]];
ENDLOOP;
IF beamHeap=NIL THEN BEGIN
beamHeap← SystemDefs.AllocateSegment[maxBeamHeapLength*SIZE[BeamPTR]];
beamHeap↑ ← ALL[NIL]; END;
beamHeapLength ← 0;
IF chordHeap#NIL THEN FOR i IN [0..chordHeapLength) DO
IF chordHeap[i]#NIL THEN zone.FREE[@chordHeap[i]];
ENDLOOP;
IF chordHeap=NIL THEN BEGIN
chordHeap← SystemDefs.AllocateSegment[maxChordHeapLength*SIZE[ChordPTR]];
chordHeap↑ ← ALL[NIL]; END;
chordHeapLength ← 0;
IF score#NIL THEN FOR i IN [0..scoreLength) DO
FOR j IN [0..syncLength) DO
IF score[i].event[j]=NIL THEN EXIT;
zone.FREE[@score[i].event[j]];
ENDLOOP;
zone.FREE[@score[i]];
ENDLOOP;
IF score=NIL THEN score ← NewPiece[maxScoreLength];
scoreLength ← 0;
selectionLength ← 0;
selection ← ALL[NIL];
END;

NewPiece:PUBLIC PROCEDURE[length:CARDINAL] RETURNS[p:PiecePTR] =
BEGIN
p ← LOOPHOLE[SystemDefs.AllocateSegment[length*SIZE[SyncPTR]]];
FOR i:CARDINAL IN [0..length) DO p[i] ← NIL; ENDLOOP;
END;

FreePiece
:PUBLIC PROCEDURE[p:POINTER TO PiecePTR] =
BEGIN
FOR i:CARDINAL DECREASING IN [0..Piece.Length[p↑]) DO
IF p[i]=NIL THEN LOOP;
IF p[i].type=notes THEN FOR j:CARDINAL IN [0..syncLength) DO
IF p[i].event[j]=NIL THEN EXIT;
p[i].event[j].sync ← NIL; -- so FreeNote doesn’t free p[i]
p[i].event[j].tied ← FALSE;
FreeNote[@p[i].event[j],p↑];
ENDLOOP;
FreeSync[@p[i]];
ENDLOOP;
SystemDefs.FreeSegment[p↑];
p↑ ← NIL;
END;

NewSync:PUBLIC PROCEDURE RETURNS[p:SyncPTR] =
BEGIN
p ← zone.NEW[SyncRec];
p↑ ← []; --
defaults to the values listed in MusicDefs
RETURN;
END;

FreeSync
:PUBLIC PROCEDURE[s:LONG POINTER TO SyncPTR] =
BEGIN
IF s↑.type=notes THEN FOR i:CARDINAL IN [0..syncLength) DO
IF s↑.event[i]=NIL THEN EXIT;
IF s↑.event[i]#NIL THEN ERROR;
ENDLOOP;
zone.FREE[s];
END;

NewChord:PUBLIC PROCEDURE RETURNS[p:ChordPTR] =
BEGIN
p ← zone.NEW[ChordRec];
p↑ ← [,0,ALL[NIL]];
chordHeap[chordHeapLength] ← p;
chordHeapLength ← chordHeapLength + 1;
RETURN;
END;

FreeChord:PUBLIC PROCEDURE[c:LONG POINTER TO ChordPTR] =
BEGIN
IF c.note[1]#NIL THEN {zone.FREE[c]; RETURN}; -- don’t need to carefully dismantle
IF c.note[0]#NIL THEN {
c.note[0].chord ← NIL;
IF c.note[0].beam#NIL THEN FOR i:CARDINAL IN [0..beamLength) DO
IF c.note[0].beam.chord[i]#[chord[c↑]] THEN LOOP;
c.note[0].beam.chord[i] ← [note[c.note[0]]];
EXIT; ENDLOOP};
chordHeapLength ← chordHeapLength - 1;
chordHeap[Chord.GetHeapIndex[c↑]]←chordHeap[chordHeapLength];
chordHeap[chordHeapLength] ← NIL;
zone.FREE[c];
END;

NewBeam:PUBLIC PROCEDURE RETURNS[p:BeamPTR] =
BEGIN
IF beamHeapLength=maxBeamHeapLength THEN ERROR;
p ← zone.NEW[BeamRec];
p↑ ← [0,TRUE,0,0,NIL,NIL,NIL,0,FALSE,0,1,ALL[endOfBeam]];
beamHeap[beamHeapLength] ← p;
beamHeapLength ← beamHeapLength + 1;
END;

FreeBeam:PUBLIC PROCEDURE[b:LONG POINTER TO BeamPTR] =
BEGIN
IF b.chord[1]#endOfBeam THEN {zone.FREE[b]; RETURN}; -- don’t need to carefully dismantle
IF b.chord[0]#endOfBeam THEN WITH ev:b.chord[0] SELECT FROM
note => {ev.n.beam ← NIL; IF b.beam#NIL THEN Beam.AddNote[b.beam,ev.n]};
chord=> {FOR j:CARDINAL IN [0..chordLength) DO
IF ev.c.note[j]=NIL THEN EXIT;
ev.c.note[j].beam ← NIL;
ENDLOOP;
IF b.beam#NIL THEN Beam.AddChord[b.beam,ev.c]};
beam => {ev.b.beam ← NIL; IF b.beam#NIL THEN Beam.AddBeam[b.beam,ev.b]};
ENDCASE;
IF b↑.beam#NIL THEN Beam.Remove[b↑.beam,NIL,NIL,b↑];
beamHeapLength ← beamHeapLength -1;
beamHeap[Beam.GetHeapIndex[b↑]]←beamHeap[beamHeapLength];
beamHeap[beamHeapLength] ← NIL;
zone.FREE[b];
END;

NewNote:PUBLIC PROCEDURE RETURNS[p:NotePTR] =
BEGIN
p ← zone.NEW[NoteRec];
p↑ ← []; --
defaults to the values listed in MusicDefs
RETURN;
END;

FreeNote:PUBLIC PROCEDURE[n:LONG POINTER TO NotePTR,p:PiecePTR] =
BEGIN
s:SyncPTR ← n.sync;
IF n↑.tied THEN Note.GetBackTie[n↑].tie←NIL;
IF n↑.tie#NIL THEN n↑.tie.tied←FALSE;
IF n↑.chord#NIL THEN Chord.RemoveNote[n↑.chord,n↑];
IF n↑.beam#NIL THEN Beam.Remove[n↑.beam,n↑,NIL,NIL];
IF n↑.sync#NIL THEN Sync.RemoveNote[n↑.sync,n↑];
IF s#NIL AND s.event[0]=NIL THEN {
Piece.RemoveSync[p,s];
FreeSync[@s]};
zone.FREE[n];
END;

--****************************************************************************
--
filestats, consistency checking
--****************************************************************************

FileStats:PUBLIC PROCEDURE =
BEGIN
OPEN IODefs, Utility;
i,j:CARDINAL;
memory:LONG INTEGER;
highWater,notes:INTEGER ← 0;
number:STRING ← [20];
WriteLine[""];
memory ← 2*maxBeamHeapLength+2*maxChordHeapLength+
2*maxScoreLength;
memory ←memory+ LONG[scoreLength]*SIZE[SyncRec];
WriteString["scoreLength= "];
WriteNumber[scoreLength,[10,FALSE,TRUE,4]];
FOR i IN [0..scoreLength) DO
FOR j IN [0..syncLength) DO
IF score[i].event[j] =NIL
THEN BEGIN highWater ← MAX[highWater,j]; EXIT; END
ELSE notes ← notes + 1;
ENDLOOP;
ENDLOOP;
memory ← memory + LONG[notes]*SIZE[NoteRec];
WriteString["; #notes= "];
WriteNumber[notes,[10,FALSE,TRUE,5]];
WriteString["; maxUsageOfSyncs= "];
WriteNumber[highWater,[10,FALSE,TRUE,2]];
WriteLine[""];
highWater ← 0;
WriteString[" #chords="];
WriteNumber[chordHeapLength,[10,FALSE,TRUE,3]];
FOR i IN [0..chordHeapLength) DO
FOR j IN [0..chordLength) DO
IF chordHeap[i].note[j] #NIL THEN LOOP;
highWater ← MAX[highWater,j];
EXIT;
ENDLOOP;
ENDLOOP;
WriteString["; maxUsageOfChords= "];
WriteNumber[highWater,[10,FALSE,TRUE,2]];
memory ← memory + LONG[chordHeapLength]*SIZE[ChordRec];
memory ← memory + LONG[beamHeapLength]*SIZE[BeamRec];
WriteLine[""];
highWater ← 0;
WriteString[" #beams ="];
WriteNumber[beamHeapLength,[10,FALSE,TRUE,3]];
FOR i IN [0..beamHeapLength) DO
FOR j IN [0..beamLength) DO
IF beamHeap[i].chord[j] #endOfBeam THEN LOOP;
highWater ← MAX[highWater,j];
EXIT;
ENDLOOP;
ENDLOOP;
WriteString["; maxUsageOfBeams= "];
WriteNumber[highWater,[10,FALSE,TRUE,2]];
WriteLine[""];
WriteString["sync="];
WriteNumber[SIZE[SyncRec],[10,FALSE,TRUE,2]];
WriteString["; note="];
WriteNumber[SIZE[NoteRec],[10,FALSE,TRUE,2]];
WriteString["; chord="];
WriteNumber[SIZE[ChordRec],[10,FALSE,TRUE,2]];
WriteString["; beam="];
WriteNumber[SIZE[BeamRec],[10,FALSE,TRUE,2]];
WriteString["; memory="];
StringDefs.AppendLongNumber[number,memory,10];
WriteString[number];
WriteLine[""];
END;

Test:PUBLIC PROCEDURE RETURNS[BOOLEAN] =
BEGIN
i,j,k:CARDINAL;
n:NotePTR;
s:SyncPTR;
lastBeam:BeamPTR←NIL;
nilFound,beamFound:BOOLEAN;
sync1,sync2:BOOLEAN;
dataError ← FALSE;
FOR i IN [0..maxScoreLength) DO
IF score[i]#NIL AND i>=scoreLength THEN
WriteError[sync,i,-1,"beyond scoreLength"];
IF score[i]=NIL AND i<scoreLength THEN WriteError[sync,i,-1,"=NIL"];
IF score[i]=NIL THEN LOOP;
-- IF i#0 AND score[i-1].time>score[i].time THEN
-- WriteError[sync,i,-1,"out of place"];
IF score[i].type IN [octava1..octava2] AND Sync.Octava[Sync.Octava[score[i]]]#score[i] THEN
WriteError[sync,i,-1,"=octava with no matching end."];
IF score[i].type#notes AND score[i].event[0]#NIL THEN
WriteError[sync,i,-1,"measure with note attached!"];
IF score[i].type#notes THEN LOOP;
IF score[i].type=notes AND score[i].event[0]=NIL THEN
WriteError[sync,i,-1,"empty"];
nilFound ← FALSE;
FOR j IN [0..syncLength) DO
n ← score[i].event[j];
IF n#NIL AND nilFound THEN WriteError[sync,i,j,"not compact"];
IF n=NIL THEN BEGIN nilFound←TRUE; LOOP; END;
IF n.sync#score[i] THEN WriteError[sync,i,j,"n.sync#score[i]"];
IF n.beam=NIL THEN LOOP;
IF n.beam=lastBeam THEN LOOP;
beamFound ← FALSE;
FOR k IN [0..beamHeapLength) DO
IF beamHeap[k]#n.beam THEN LOOP;
beamFound ← TRUE;
lastBeam←n.beam;
EXIT;
ENDLOOP;
IF NOT beamFound THEN WriteError[sync,i,j,"non-existant beam"];
ENDLOOP;
ENDLOOP;
FOR i IN [0..maxChordHeapLength) DO
IF chordHeap[i]#NIL AND i>=chordHeapLength THEN
WriteError[chord,i,-1,"beyond chordHeapLength"];
IF chordHeap[i]=NIL AND i<chordHeapLength THEN
WriteError[chord,i,-1,"=NIL"];
IF chordHeap[i]=NIL THEN LOOP;
IF chordHeap[i].note[0]=NIL THEN WriteError[chord,i,-1,"empty"];
nilFound ← FALSE;
s ← NIL;
FOR j IN [0..chordLength) DO
n ← chordHeap[i].note[j];
IF n#NIL AND nilFound THEN WriteError[chord,i,j,"not compact"];
IF n=NIL THEN BEGIN nilFound ← TRUE; LOOP; END;
IF s=NIL THEN s ← n.sync;
IF s#n.sync THEN WriteError[chord,i,j,"wrong sync"];
ENDLOOP;
ENDLOOP;
FOR i IN [0..maxBeamHeapLength) DO
IF beamHeap[i]#NIL AND i>=beamHeapLength THEN
WriteError[beam,i,-1,"beyond beamHeapLength"];
IF beamHeap[i]=NIL AND i<beamHeapLength THEN
WriteError[beam,i,-1,"=NIL"];
IF beamHeap[i]=NIL THEN LOOP;
IF beamHeap[i].chord[0]=endOfBeam THEN WriteError[beam,i,-1,"empty"];
TestBeam[i,beamHeap[i]];
sync1 ← Sync.GetScoreIndex[beamHeap[i].sync1]#scoreLength;
sync2 ← Sync.GetScoreIndex[beamHeap[i].sync2]#scoreLength;
IF NOT sync1 THEN WriteError[beam,i,-1,"sync1 not in score"];
IF NOT sync2 THEN WriteError[beam,i,-1,"sync2 not in score"];
ENDLOOP;
RETURN[dataError];
END;

TestBeam:PROCEDURE[i:CARDINAL,pointer:BeamPTR] =
BEGIN
j,k:CARDINAL;
beam:BeamPTR ← beamHeap[i];
nilFound,sync1,sync2:BOOLEAN ← FALSE;
FOR j IN [0..beamLength) DO
IF beam.chord[j]#endOfBeam AND nilFound THEN
WriteError[beam,i,j,"not compact"];
IF beam.chord[j]=endOfBeam THEN BEGIN
nilFound ← TRUE; LOOP; END;
WITH ev:beam.chord[j] SELECT FROM
note =>BEGIN
IF ev.n.beam#pointer THEN WriteError[beam,i,j,"wrong beam"];
IF ev.n.sync=pointer.sync1 THEN sync1←TRUE;
IF ev.n.sync=pointer.sync2 THEN sync2←TRUE;
END;
chord=> BEGIN
IF ev.c.note[0].sync=pointer.sync1 THEN sync1←TRUE;
IF ev.c.note[0].sync=pointer.sync2 THEN sync2←TRUE;
FOR k IN [0..chordLength) DO
IF ev.c.note[k]=NIL THEN LOOP;
IF ev.c.note[k].beam#pointer THEN
WriteError[beam,i,j,"wrong beam"];
ENDLOOP;
END;
beam =>BEGIN
IF ev.b.beam#pointer THEN WriteError[beam,i,j,"wrong beam"];
IF ev.b.sync1=pointer.sync1 THEN sync1←TRUE;
IF ev.b.sync2=pointer.sync2 THEN sync2←TRUE;
END;
ENDCASE;
ENDLOOP;
IF NOT sync1 THEN WriteError[beam,i,-1,"bad sync1"];
IF NOT sync2 THEN WriteError[beam,i,-1,"bad sync2"];
END;

WriteError:PROCEDURE[t:Type,i,j:INTEGER,s:STRING] =
BEGIN
OPEN IODefs;
SELECT t FROM
sync => BEGIN
WriteString["sync"];
WriteNumber[i,[10,FALSE,TRUE,4]];
IF j>-1 THEN
BEGIN
WriteString[",event"];
WriteNumber[j,[10,FALSE,TRUE,2]];
END;
END;
chord=> BEGIN
WriteString["chord"];
WriteNumber[i,[10,FALSE,TRUE,4]];
IF j>-1 THEN
BEGIN
WriteString[",note"];
WriteNumber[j,[10,FALSE,TRUE,2]];
END;
END;
beam => BEGIN
WriteString["beam"];
WriteNumber[i,[10,FALSE,TRUE,4]];
IF j>-1 THEN
BEGIN
WriteString[",chord"];
WriteNumber[j,[10,FALSE,TRUE,2]];
END;
END;
ENDCASE;
dataError ← TRUE;
WriteString[": "];
WriteLine[s];
END;

Type:TYPE = {sync,chord,beam,none};
dataError:BOOLEAN;

CleanUp:PROCEDURE =
BEGIN
i,j,k:CARDINAL;
n:NotePTR;
s:SyncPTR;
nilFound,beamFound:BOOLEAN;
sync1,sync2:BOOLEAN;
FOR i IN [0..maxScoreLength) DO
IF score[i]#NIL AND i>=scoreLength THEN
WriteError[sync,i,-1,"beyond scoreLength"];
IF score[i]=NIL AND i<scoreLength THEN WriteError[sync,i,-1,"=NIL"];
IF score[i]=NIL THEN LOOP;
IF i#0 AND score[i-1].time>score[i].time THEN
WriteError[sync,i,-1,"out of place"];
IF score[i].type#notes AND score[i].event[0]#NIL THEN
WriteError[sync,i,-1,"measure with note attached!"];
IF score[i].type#notes THEN LOOP;
IF score[i].type=notes AND score[i].event[0]=NIL THEN
WriteError[sync,i,-1,"empty"];
nilFound ← FALSE;
FOR j IN [0..syncLength) DO
n ← score[i].event[j];
IF n#NIL AND nilFound THEN WriteError[sync,i,j,"not compact"];
IF n=NIL THEN BEGIN nilFound←TRUE; LOOP; END;
IF n.sync#score[i] THEN n.sync ← score[i];
IF n.beam=NIL THEN LOOP;
beamFound ← FALSE;
FOR k IN [0..beamHeapLength) DO
IF beamHeap[k]#n.beam THEN LOOP;
beamFound ← TRUE;
EXIT;
ENDLOOP;
IF NOT beamFound THEN score[i].event[j].beam ← NIL;
ENDLOOP;
ENDLOOP;
FOR i IN [0..maxChordHeapLength) DO
IF chordHeap[i]#NIL AND i>=chordHeapLength THEN
WriteError[chord,i,-1,"beyond chordHeapLength"];
IF chordHeap[i]=NIL AND i<chordHeapLength THEN
WriteError[chord,i,-1,"=NIL"];
IF chordHeap[i]=NIL THEN LOOP;
IF chordHeap[i].note[0]=NIL THEN Utility.FreeChord[@chordHeap[i]];
nilFound ← FALSE;
s ← NIL;
FOR j IN [0..chordLength) DO
n ← chordHeap[i].note[j];
IF n#NIL AND nilFound THEN WriteError[chord,i,j,"not compact"];
IF n=NIL THEN BEGIN nilFound ← TRUE; LOOP; END;
IF s=NIL THEN s ← n.sync;
IF s#n.sync THEN chordHeap[i].note[j].sync ← s;
ENDLOOP;
ENDLOOP;
FOR i IN [0..maxBeamHeapLength) DO
IF beamHeap[i]#NIL AND i>=beamHeapLength THEN
WriteError[beam,i,-1,"beyond beamHeapLength"];
IF beamHeap[i]=NIL AND i<beamHeapLength THEN
WriteError[beam,i,-1,"=NIL"];
IF beamHeap[i]=NIL THEN LOOP;
IF beamHeap[i].chord[1]=endOfBeam THEN Utility.FreeBeam[@beamHeap[i]];
CleanUpBeam[i,beamHeap[i]];
sync1 ← sync2 ← FALSE;
FOR j IN [0..scoreLength) DO
IF score[j]=beamHeap[i].sync1 THEN sync1 ← TRUE;
IF score[j]=beamHeap[i].sync2 THEN sync2 ← TRUE;
ENDLOOP;
IF NOT sync1 THEN WriteError[beam,i,-1,"sync1 not in score"];
IF NOT sync2 THEN WriteError[beam,i,-1,"sync2 not in score"];
ENDLOOP;
END;

CleanUpBeam:PROCEDURE[i:CARDINAL,pointer:BeamPTR] =
BEGIN
j,k:CARDINAL;
beam:BeamPTR ← beamHeap[i];
nilFound:BOOLEAN ← FALSE;
FOR j IN [0..beamLength) DO
IF beam.chord[j]#endOfBeam AND nilFound THEN
WriteError[beam,i,j,"not compact"];
IF beam.chord[j]=endOfBeam THEN BEGIN
nilFound ← TRUE; LOOP; END;
WITH ev:beam.chord[j] SELECT FROM
note =>IF ev.n.beam#pointer THEN ev.n.beam ← pointer;
chord=> FOR k IN [0..chordLength) DO
IF ev.c.note[k]=NIL THEN LOOP;
IF ev.c.note[k].beam#pointer THEN
ev.c.note[k].beam ← pointer;
ENDLOOP;
ENDCASE;
ENDLOOP;
END;

CleanUpSheets:PROCEDURE =
BEGIN
sheet:Staves;
FOR i:CARDINAL DECREASING IN [0..scoreLength) DO
IF score[i].type NOT IN SheetSwitch THEN LOOP;
sheet ← LOOPHOLE[score[i].event];
FOR j:CARDINAL IN [0..sheet.sl] DO
IF sheet.staff[j].pitch#0 THEN LOOP;
Utility.FreeSync[@score[i]]; EXIT;
ENDLOOP;
ENDLOOP;
END;

END..