--Author: John Maxwell
--last modified: December 16, 1981 7:58 AM

DIRECTORY
Beam USING [GetHeapIndex,Length,SetSyncs],
Chord USING [GetHeapIndex,Length],
MusicDefs,
MusicFileDefs,
Note USING [GetSyncIndex],
Piece USING [MaxToc],
Score USING [FileStats,Look,SetKey,SetMetrenome,Test],
Sheet USING [Reset, SetBegin],
StreamDefs USING [Append, DiskHandle, NewWordStream, Read, ReadBlock, WriteBlock, Write],
Sync USING [AddTimes, GetScoreIndex, Length],
Utility USING [InitStorage, NewBeam, NewChord, NewNote, NewSync];

FilerImpl: PROGRAM
IMPORTS Beam, Chord, MusicDefs, Note, Piece, Score, Sheet, StreamDefs, Sync, Utility
EXPORTS Score =

BEGIN
OPEN MusicDefs, MusicFileDefs;


Error:SIGNAL;

--****************************************************************************
--
filein, fileout
--****************************************************************************

version:INTEGER;

FileIn:PUBLIC PROCEDURE[s:STRING,append:BOOLEAN] =
BEGIN
tie:TieFormat;
temp:SyncPTR;
note:NoteFormat;
view:ViewFormat;
event:EventFormat;
beam:BeamFormat;
chord:ChordFormat;
pointer:RelativePTR;
inputStream:StreamDefs.DiskHandle;
n:NotePTR; c:ChordPTR; last,lastP:Time←0;
i,j,k,length,oldBHLength,oldSLength,oldCHLength:CARDINAL;
inputStream←StreamDefs.NewWordStream[s,StreamDefs.Read];
inputStream.reset[inputStream];
[] ← GetBlock[inputStream,NIL,0]; --
signal to fill up buffer
version ← Get[inputStream];

--prepare for append
IF NOT append THEN Utility.InitStorage[];
oldBHLength ← beamHeapLength;
oldSLength ← scoreLength;
oldCHLength ← chordHeapLength;
IF scoreLength#0 THEN last ← score[scoreLength-1].time+20;
IF scoreLength#0 THEN lastP ← Piece.MaxToc[score,last-50,last,TRUE];
--file in score
IF Get[inputStream]#scoreID THEN Error;

length ← Get[inputStream];
FOR i IN [oldSLength..oldSLength+length) DO
[] ← GetBlock[inputStream,@event,SIZE[EventFormat]];
BuildEvent[(score[i]←Utility.NewSync[]),@event];
FOR j IN [0..event.length) DO
[] ← GetBlock[inputStream,@note,SIZE[NoteFormat]];
BuildNote[(score[i].event[j]←Utility.NewNote[]),@note];
score[i].event[j].sync ← score[i];
IF NOT note.tie THEN LOOP;
[] ← GetBlock[inputStream,@tie,SIZE[TieFormat]];
score[i].event[j].tie ← score[tie.heap+oldSLength].event[tie.index];
score[i].event[j].tieHeight ← tie.height;
score[i].event[j].tie.tied ← TRUE;
ENDLOOP;
IF event.exception THEN
BEGIN
sheet:Staves←[,0,0,0,,];
[] ← GetBlock[inputStream,@sheet,SIZE[SheetFormat]];
BuildSheet[score[i],@sheet];
END;
temp ← score[i];
IF score[i].time=-1 AND score[i].type=staves THEN { -- sort the score
FOR j IN [oldSLength..i) DO score[j+1]← score[j]; ENDLOOP;
score[oldSLength] ← temp;
score[oldSLength].time ← 0};
IF append THEN Sync.AddTimes[temp,last,lastP];
scoreLength ← i+1;
ENDLOOP;
--file in chordheap
IF Get[inputStream]#chordHeapID THEN Error;

length ← Get[inputStream];
FOR i IN [oldCHLength..oldCHLength+length) DO
[] ← GetBlock[inputStream,@chord,SIZE[ChordFormat]];
BuildChord[(chordHeap[i]←Utility.NewChord[]),@chord];
FOR j IN [0..chord.length) DO
[] ← GetBlock[inputStream,@pointer,SIZE[RelativePTR]];
chordHeap[i].note[j] ← score[pointer.heap+oldSLength].event[pointer.index];
chordHeap[i].note[j].chord ← chordHeap[i];
ENDLOOP;
ENDLOOP;
--file in beamheap
IF Get[inputStream]#beamHeapID THEN Error;

length ← Get[inputStream];
FOR i IN [oldBHLength..oldBHLength+length) DO beamHeap[i] ← Utility.NewBeam[]; ENDLOOP;
FOR i IN [oldBHLength..oldBHLength+length) DO
[] ← GetBlock[inputStream,@beam,SIZE[BeamFormat]];
FOR j IN [0..beam.length) DO
[] ← GetBlock[inputStream,@pointer,SIZE[RelativePTR]];
SELECT pointer.heap FROM
beams => BEGIN
beamHeap[i].chord[j] ← [beam[beamHeap[pointer.index+oldBHLength]]];
beamHeap[pointer.index+oldBHLength].beam ← beamHeap[i];
END;
chords =>BEGIN
c ← chordHeap[pointer.index+oldCHLength];
beamHeap[i].chord[j] ← [chord[c]];
FOR k IN [0..chordLength) DO
IF c.note[k] = NIL THEN EXIT;
c.note[k].beam ← beamHeap[i];
ENDLOOP;
END;
ENDCASE=>BEGIN
n ← score[pointer.heap+oldSLength].event[pointer.index];
beamHeap[i].chord[j]←[note[n]];
n.beam ← beamHeap[i];
END;
BuildBeam[beamHeap[i],@beam];
ENDLOOP;
ENDLOOP;
FOR i IN [oldBHLength..oldBHLength+length) DO
Beam.SetSyncs[beamHeap[i]];
ENDLOOP;
--file in spares
IF Get[inputStream]#nullID THEN Error;
IF Get[inputStream]#nullID THEN Error;
IF Get[inputStream]#nullID THEN Error;
IF Get[inputStream]#nullID THEN Error;
IF Get[inputStream]#nullID THEN Error;
IF Get[inputStream]#nullID THEN Error;
IF Get[inputStream]#nullID THEN Error;
IF Get[inputStream]#nullID THEN Error;
IF Get[inputStream]#nullID THEN Error;
--file in view
[] ← GetBlock[inputStream,@view,SIZE[ViewFormat]];
BuildView[@view];
--clean up and return
bufferIndex←0;
inputStream.destroy[inputStream];
command ← TRUE;
Score.FileStats[];
Sheet.SetBegin[0];
END;

Get:PROCEDURE[s:StreamDefs.DiskHandle] RETURNS[data:UNSPECIFIED] =
INLINE BEGIN []←GetBlock[s,@data,1]; END;

GetBlock:PROCEDURE[s:StreamDefs.DiskHandle,p:POINTER,length:CARDINAL] RETURNS[INTEGER] =
--
a hack to speed up disk transfers by batching them together
BEGIN
i:CARDINAL;
array:POINTER TO ARRAY[0..1000) OF INTEGER ← LOOPHOLE[p];
IF p=NIL THEN BEGIN
[] ← StreamDefs.ReadBlock[s,@buffer,bufferLength];
bufferIndex ← 0;
RETURN[0];
END;
FOR i IN [0..length) DO
array[i] ← buffer[bufferIndex];
bufferIndex←bufferIndex+1;
IF bufferIndex< bufferLength THEN LOOP;
[] ← StreamDefs.ReadBlock[s,@buffer,bufferLength];
bufferIndex←0;
ENDLOOP;
RETURN[0];
END;

BuildEvent:PROCEDURE[old:SyncPTR,new:POINTER TO EventFormat] =
BEGIN
IF new.identifier#eventID THEN Error;
old.time ← new.time;
SELECT version FROM
1 => old.type ← LOOPHOLE[LOOPHOLE[new.type,CARDINAL]/4];
ENDCASE => old.type ← new.type;
IF old.type=timeSignature THEN old.ts ← LOOPHOLE[new.value]
ELSE old.value← new.value;
END;

BuildSheet:PROCEDURE[old:SyncPTR,staves:StavesPTR] =
BEGIN
old.event ← LOOPHOLE[staves↑];
staves ← LOOPHOLE[@old.event];
IF version<3 THEN ConvertSheet2[old];
IF old.type=staves AND old.value=-1 THEN FindStyle[old];
END;

ConvertSheet2:PROCEDURE[old:SyncPTR] =
BEGIN
staves:StavesPTR ← LOOPHOLE[@old.event];
IF old.value=-1 THEN {old.type←staves; RETURN};
IF staves.staff[old.value].pitch=15 THEN {
old.type←octava1;
staves.height ← -30;
RETURN};
IF staves.staff[old.value].pitch=60 THEN {
old.type←octava1;
staves.height ← 60;
RETURN};
FOR i:CARDINAL DECREASING IN [0..scoreLength) DO
--is the present clef change the latter part of an octava pair?
IF score[i].type NOT IN SheetSwitch THEN LOOP;
staves ← LOOPHOLE[@score[i].event];
SELECT staves.staff[old.value].pitch FROM
15 => old.type ← octava2;
60 => old.type ← octava2;
ENDCASE => old.type ← clef;
RETURN; ENDLOOP;
END;

FindStyle:PROCEDURE[old:SyncPTR] =
BEGIN
staves:StavesPTR ← LOOPHOLE[@old.event];
index:INTEGER←1;
FOR i:CARDINAL IN [1..staves.sl] DO
IF staves.staff[i].y=staves.staff[i-1].y THEN LOOP;
index←index+1;
ENDLOOP;
IF index=4 AND staves.staff[0].pitch=72 THEN index←0;
old.value ← index;
staves↑ ← style[index];
END;

BuildNote:PROCEDURE[old:NotePTR,new:POINTER TO NoteFormat] =
BEGIN
IF new.identifier#noteID THEN Error;
old.pitch ← new.pitch;
old.value ← new.value;
old.embellish ← new.embellish;
old.grace ← new.grace;
old.voice ← new.voice;
maxVoice ← MAX[old.voice,maxVoice];
old.spelled ← new.spelled;
old.rest ← new.rest;
old.dotted ← new.dotted;
old.doubleDotted ← new.doubleDotted;
old.stemUp ← new.stemUp;
old.staff ← new.staff;
old.toc ← new.toc;
old.duration ← new.duration;
END;

BuildChord:PROCEDURE[old:ChordPTR,new:POINTER TO ChordFormat] =
BEGIN
IF new.identifier#chordID THEN Error;
old.stemUp ← new.stemUp;
END;

BuildBeam:PROCEDURE[old:BeamPTR,new:POINTER TO BeamFormat] =
BEGIN
IF new.identifier#beamID THEN Error;
old.tilt ← new.tilt;
old.beamed ← new.beamed;
old.invisible ← new.invisible;
old.ntuple ← new.ntuple;
old.against ← new.against;
old.height ← new.height;
old.staff ← new.staff;
Beam.SetSyncs[old];
END;

BuildView:PROCEDURE[view:POINTER TO ViewFormat] =
BEGIN
TF ← view.scale;
IF view.display#physical AND TF>10 THEN TF←3; --
temporary hack
SELECT view.sheet FROM -- convert from old indices to new ones
1 => Score.Look[sheet,,0];
6,7 => Score.Look[sheet,,1];
2 => Score.Look[sheet,,2];
4,5 => Score.Look[sheet,,3];
3 => Score.Look[sheet,,4];
ENDCASE;
show.accidental ← view.accidental;
show.sync ← view.sync;
show.notehead ← view.notehead;
show.display ← view.display;
show.noCarry ← view.noCarry;
Score.Look[hardcopy,view.hardcopy,];
IF version=1 THEN Score.SetKey[0,EndOfScore[],view.key];
IF version=1 THEN Score.SetMetrenome[0,EndOfScore[],view.speed];
Sheet.Reset[];
END;

--****************************************************************************
--
fileout
--****************************************************************************

FO:PROCEDURE RETURNS[STRING] =
BEGIN
RETURN[IF FileOut["temp.music"]
THEN "filed out on temp.music"
ELSE "fileout aborted"];
END;

buffer:ARRAY [0..bufferLength) OF INTEGER;
bufferIndex:CARDINAL←0;
bufferLength:CARDINAL = 500;

FileOut:PUBLIC PROCEDURE[s:STRING] RETURNS[BOOLEAN] =
BEGIN
OPEN StreamDefs;
i,j:CARDINAL;
outputStream:StreamDefs.DiskHandle;
event:EventFormat;
note:NoteFormat;
chord:ChordFormat;
beam:BeamFormat;
tie:TieFormat;
view:ViewFormat;
pointer:RelativePTR;
n:NotePTR;
IF Score.Test[] THEN RETURN[FALSE];
outputStream←StreamDefs.NewWordStream[s,StreamDefs.Write+StreamDefs.Append];
outputStream.reset[outputStream];
--file out score
Put[outputStream,versionID];

Put[outputStream,scoreID];
Put[outputStream,scoreLength];
FOR i IN [0..scoreLength) DO
ConvertEvent[score[i],@event];
[] ← PutBlock[outputStream,@event,SIZE[EventFormat]];
IF event.exception THEN
BEGIN
sheet:Staves ← LOOPHOLE[score[i].event];
[]←PutBlock[outputStream,@sheet,SIZE[SheetFormat]];
END;
FOR j IN [0..syncLength) DO
IF (n←score[i].event[j])=NIL THEN EXIT;
ConvertNote[n,@note];
note.tie ← n.tie#NIL;
[] ← PutBlock[outputStream,@note,SIZE[NoteFormat]];
IF NOT note.tie THEN LOOP;
tie.heap ← Sync.GetScoreIndex[n.tie.sync];
tie.index ← Note.GetSyncIndex[n.tie.sync,n.tie];
tie.height ← score[i].event[j].tieHeight;
[] ← PutBlock[outputStream,@tie,SIZE[TieFormat]]
ENDLOOP;
ENDLOOP;
--file out chordheap
Put[outputStream,chordHeapID];

Put[outputStream,chordHeapLength];
FOR i IN [0..chordHeapLength) DO
ConvertChord[chordHeap[i],@chord];
[] ← PutBlock[outputStream,@chord,SIZE[ChordFormat]];
pointer.heap ← Sync.GetScoreIndex[chordHeap[i].note[0].sync];

FOR j IN [0..chordLength) DO
IF chordHeap[i].note[j] = NIL THEN EXIT;
pointer.index ← Note.GetSyncIndex[score[pointer.heap],chordHeap[i].note[j]];
[] ← PutBlock[outputStream,@pointer,SIZE[RelativePTR]];
ENDLOOP;
ENDLOOP;
--file out beamheap
Put[outputStream,beamHeapID];

Put[outputStream,beamHeapLength];
FOR i IN [0..beamHeapLength) DO
ConvertBeam[beamHeap[i],@beam];
[] ← PutBlock[outputStream,@beam,SIZE[BeamFormat]];
FOR j IN [0..beamLength) DO
IF beamHeap[i].chord[j] = endOfBeam THEN EXIT;
WITH ev:beamHeap[i].chord[j] SELECT FROM
note => BEGIN
pointer.heap ← Sync.GetScoreIndex[ev.n.sync];
pointer.index← Note.GetSyncIndex[score[pointer.heap],ev.n];
END;
chord=> BEGIN
pointer.heap ← chords;
pointer.index← Chord.GetHeapIndex[ev.c];
END;
beam => BEGIN
pointer.heap ← beams;
pointer.index← Beam.GetHeapIndex[ev.b];
END;
ENDCASE;
[] ← PutBlock[outputStream,@pointer,SIZE[RelativePTR]];
ENDLOOP;
ENDLOOP;
--file out nine spares
Put[outputStream,nullID];
Put[outputStream,nullID];
Put[outputStream,nullID];
Put[outputStream,nullID];
Put[outputStream,nullID];
Put[outputStream,nullID];
Put[outputStream,nullID];
Put[outputStream,nullID];
Put[outputStream,nullID];
--file out view
ConvertView[@view];
[] ← PutBlock[outputStream,@view,SIZE[ViewFormat]];
--clean up and return
[] ← PutBlock[outputStream,NIL,0]; --
clean up buffer
outputStream.destroy[outputStream];
command ← TRUE;
RETURN[TRUE];
END;

Put:PROCEDURE[s:StreamDefs.DiskHandle,data:INTEGER] =
INLINE BEGIN []←PutBlock[s,@data,1]; END;

PutBlock:PROCEDURE[s:StreamDefs.DiskHandle,p:POINTER,length:CARDINAL] RETURNS[INTEGER] =
--
a hack to speed up disk transfers by batching them together
BEGIN
i:CARDINAL;
array:POINTER TO ARRAY[0..1000) OF INTEGER ← LOOPHOLE[p];
IF length=0 THEN BEGIN
IF bufferIndex=0 THEN RETURN[0];
[] ← StreamDefs.WriteBlock[s,@buffer,bufferIndex];
bufferIndex←0;
RETURN[0]; END;
FOR i IN [0..length) DO
buffer[bufferIndex] ← array[i];
bufferIndex←bufferIndex+1;
IF bufferIndex< bufferLength THEN LOOP;
[] ← StreamDefs.WriteBlock[s,@buffer,bufferIndex];
bufferIndex←0;
ENDLOOP;
RETURN[0];
END;

ConvertEvent:PROCEDURE[data:SyncPTR,file:POINTER TO EventFormat] =
BEGIN
file↑ ← []; --
defaults to the values listed in musicFileDefs
file.time ← data.time;
file.type ← data.type;
SELECT data.type FROM
notes => file.length←Sync.Length[data];
timeSignature => file.value ← LOOPHOLE[data.ts];
ENDCASE => file.value ← data.value;
IF data.type IN SheetSwitch THEN file.exception ← TRUE;
END;

ConvertNote:PROCEDURE[old:NotePTR,new:POINTER TO NoteFormat] =
BEGIN
new.pitch ← old.pitch;
new.value ← old.value;
new.embellish ← old.embellish;
new.voice ← old.voice;
new.spelled ← old.spelled;
new.rest ← old.rest;
new.dotted ← old.dotted;
new.doubleDotted ← old.doubleDotted;
new.grace ← old.grace;
new.stemUp ← old.stemUp;
new.staff ← old.staff;
new.toc ← old.toc;
new.duration ← old.duration;
END;

ConvertChord:PROCEDURE[old:ChordPTR,new:POINTER TO ChordFormat] =
BEGIN
new.stemUp ← old.stemUp;
new.length ← Chord.Length[old];
END;

ConvertBeam:PROCEDURE[old:BeamPTR,new:POINTER TO BeamFormat] =
BEGIN
new.tilt ← old.tilt;
new.beamed ← old.beamed;
new.invisible ← old.invisible;
new.ntuple ← old.ntuple;
new.against ← old.against;
new.height ← old.height;
new.staff ← old.staff;
new.length ← Beam.Length[old];
END;

ConvertView:PROCEDURE[view:POINTER TO ViewFormat] =
BEGIN
view.scale ← TF;
view.sheet ← 0;
view.speed ← 128;
view.accidental ← show.accidental;
view.sync ← show.sync;
view.notehead ← show.notehead;
view.display ← show.display;
view.noCarry ← show.noCarry;
view.hardcopy ← hardcopy;
END;


END...








ConvertReals:PROCEDURE =
BEGIN
i:CARDINAL;
FOR i IN [0..beamHeapLength) DO
IF beamHeap[i]=NIL THEN LOOP;
beamHeap[i].tilt ← RealConvert.Mesa5ToIeee[beamHeap[i].tilt];
ENDLOOP;
END;

--****************************************************************************
--
filein, fileout pianorolls
--****************************************************************************

FileInOld:PUBLIC PROCEDURE[s:STRING] =
BEGIN
OPEN DataDefs, StreamDefs;
i,length:CARDINAL ← 0;
pn:PhysicalNote;
note:NotePTR;
sync:SyncPTR←NIL;
oldToc:Time;
inputStream:DiskHandle;
inputStream←NewWordStream[s,Read];
inputStream.reset[inputStream];
length ← inputStream.get[inputStream];
[] ← inputStream.get[inputStream];
NewScore[];
WHILE NOT inputStream.endof[inputStream] DO
[] ← ReadBlock[inputStream,@pn,SIZE[PhysicalNote]];
IF pn.duration<4 THEN LOOP;
IF pn.pitch>80 OR pn.pitch<-7 THEN LOOP;
note ← NewNote[];
note↑ ← []; --
defaults to the values listed in musicDefs
note.toc ← pn.toc;
note.duration ← pn.duration;
note.pitch ← pn.pitch;
SELECT TRUE FROM
note.pitch IN [67..80] => BEGIN note.stemUp←FALSE; note.staff←0; END;
note.pitch IN [56..66] => BEGIN note.stemUp← TRUE; note.staff←0; END;
note.pitch IN [43..55] => BEGIN note.stemUp←FALSE; note.staff←1; END;
note.pitch IN [32..42] => BEGIN note.stemUp← TRUE; note.staff←1; END;
note.pitch IN [22..31] => BEGIN note.stemUp←FALSE; note.staff←2; END;
note.pitch IN [10..21] => BEGIN note.stemUp← TRUE; note.staff←2; END;
note.pitch IN [ 0.. 9] => BEGIN note.stemUp←FALSE; note.staff←3; END;
note.pitch IN [-7..-1] => BEGIN note.stemUp← TRUE; note.staff←3; END;
ENDCASE;
IF ABS[note.toc - oldToc] > 200
THEN BEGIN
AddSyncToPiece[sync,score];
oldToc ← note.toc;
sync ← NewSync[];
END;
sync.time ← note.toc/256;
AddToSync[sync,note];
i ← i + 1;
IF i=length THEN EXIT;
ENDLOOP;
AddSyncToPiece[sync,score];
inputStream.destroy[inputStream];
ActionDefs.Look[accidental,FALSE,];
ActionDefs.Look[sheet,,1];
ActionDefs.Look[notehead,FALSE,];
selectionLength ← 0;
SortScore[];
UtilityDefs.DrawScore[TRUE];
END;

FileOutOld:PUBLIC PROCEDURE[s:STRING] =
BEGIN
OPEN StreamDefs;
i,j:CARDINAL;
pNote:PhysicalNote;
n:NotePTR;
inputStream:DiskHandle;
inputStream←NewWordStream[s,Write+Append];
inputStream.reset[inputStream];
inputStream.put[inputStream,0];
inputStream.put[inputStream,0];
FOR i IN [0..scoreLength) DO
FOR j IN [0..syncLength) DO
IF (n←score[i].event[j])=NIL THEN EXIT;
IF score[i].event[j].rest THEN LOOP;
IF n.duration = 0 THEN LOOP;
pNote ← [n.pitch,n.duration,50,n.toc];
[]←WriteBlock[inputStream,@pNote,SIZE[PhysicalNote]];
ENDLOOP;
ENDLOOP;
inputStream.destroy[inputStream];
UtilityDefs.DrawScore[TRUE];
END;


END....
--************************************************************************
--
filein, fileout old .logical format
--************************************************************************

FileInOld:PUBLIC PROCEDURE[s:STRING] =
BEGIN
OPEN StreamDefs;
i,j,noChords,noBeams:CARDINAL;
rchord:RChord;
rbeam:RBeam;
convert:BOOLEAN ← FALSE;
inputStream:DiskHandle;
inputStream←NewWordStream[s,Read];
inputStream.reset[inputStream];
IF append THEN InitAppend[] ELSE NewScore[];
scoreLength ← inputStream.get[inputStream]+start;
maxVoice ← 0;
FOR i IN [start..scoreLength) DO score[i]←NewSync[]; ENDLOOP;
FOR i IN [start..scoreLength) DO
[] ← ReadBlock[inputStream,score[i],SIZE[Sync]];
score[i].time ← score[i].time + offset;
FOR j IN [0..syncLength) DO
IF score[i].event[j]=NIL THEN EXIT;
[] ← ReadBlock[inputStream,score[i].event[j]←NewNote[],SIZE[Note]];
score[i].event[j].sync ← score[i];
IF skipBeams THEN score[i].event[j].beam ← NIL;
maxVoice ← MAX[maxVoice,score[i].event[j].voice];
ENDLOOP;
ENDLOOP;
noChords ← inputStream.get[inputStream];
FOR i IN [0..noChords) DO
[] ← ReadBlock[inputStream,@rchord,SIZE[RChord]];
RestoreChord[@rchord,NewChord[]];
ENDLOOP;
noBeams ← inputStream.get[inputStream];
FOR i IN [0..noBeams) DO
[] ← ReadBlock[inputStream,@rbeam,SIZE[RBeam]];
IF skipBeams THEN LOOP;
RestoreBeam[@rbeam,NewBeam[]];
IF rbeam.beam.tilt> 3 OR rbeam.beam.tilt<-3 THEN convert ← TRUE;
ENDLOOP;
IF NOT inputStream.endof[inputStream] THEN FileInSheet[inputStream];
inputStream.destroy[inputStream];
IF convert THEN Convert[];
UtilityDefs.ResetSheet[];
--UtilityDefs.DrawScore[TRUE];
END;

FileInSheet:PROCEDURE[stream:StreamDefs.DiskHandle]=
BEGIN
OPEN StreamDefs;
[] ← ReadBlock[stream,@keyArray,2*keyLength];
[] ← ReadBlock[stream,@show,SIZE[DocumentProfile]];
[] ← ReadBlock[stream,@metrenome,SIZE[INTEGER]];
[] ← ReadBlock[stream,@TF,SIZE[CARDINAL]];
[] ← ReadBlock[stream,@sheet,SIZE[Staves]];
sheet.staff[4] ← [0,0]; sheet.staff[5] ← [0,0];
KeyIndexToSync[];
END;

KeyIndexToSync:PROCEDURE =
BEGIN
k:CARDINAL;
FOR k IN [0..keyLength) DO
keyArray[k].sync ← score[LOOPHOLE[keyArray[k].sync,CARDINAL]];
ENDLOOP;
END;


skipBeams:BOOLEAN ← FALSE;
bstart,cstart:CARDINAL←0;
offset:Time←0;
append:BOOLEAN ← FALSE;

InitAppend:PROCEDURE =
BEGIN
start ← scoreLength;
bstart ← beamHeapLength;
cstart ← chordHeapLength;
IF scoreLength#0 THEN offset ← score[scoreLength-1].time+10;
END;

RestoreChord:PROCEDURE[r:POINTER TO RChord,chord:ChordPTR] =
BEGIN
i:CARDINAL;
chord↑ ← [r.stem,0,ALL[NIL]];
FOR i IN [0..chordLength) DO
IF r.n[i]=100 THEN EXIT;
chord.note[i] ← score[r.s+start].event[r.n[i]];
ENDLOOP;
END;

RestoreBeam:PROCEDURE[r:POINTER TO RBeam,beam:BeamPTR]=
BEGIN
i,j:CARDINAL;
beam↑ ← r.beam;
beam.sync1 ← score[LOOPHOLE[beam.sync1,CARDINAL]+start];
beam.sync2 ← score[LOOPHOLE[beam.sync2,CARDINAL]+start];
FOR i IN [0..beamLength) DO
IF r.n[i].type = null THEN EXIT;
SELECT r.n[i].type FROM
note => beam.chord[i] ←
[note[score[r.n[i].sync+start].event[r.n[i].index]]];
chord=> beam.chord[i] ← [chord[chordHeap[r.n[i].index+cstart]]];
beam => beam.chord[i] ← [beam[beamHeap[r.n[i].index+bstart]]];
null => beam.chord[i] ← endOfBeam;
ENDCASE =>NULL; --later ERROR
ENDLOOP;
FOR i IN [0..beamLength) DO
WITH ev:beam.chord[i] SELECT FROM
note => IF ev.n#NIL THEN ev.n.beam ← beam;
chord=> FOR j IN [0..chordLength) DO
IF ev.c.note[j]#NIL THEN ev.c.note[j].beam ← beam;
ENDLOOP;
beam => SetBeamPTRToBeam[ev.b,beam];
ENDCASE;
ENDLOOP;
END;

RChord:TYPE = RECORD[s:CARDINAL,stem:BOOLEAN,
n:ARRAY [0..chordLength) OF CARDINAL];
RBeam:TYPE = RECORD[beam:BeamedSet,n:ARRAY [0..beamLength) OF VPointer];
VPointer:TYPE = RECORD[type:{note,chord,beam,null},sync:CARDINAL,index:CARDINAL];



FileOutOld
:PUBLIC PROCEDURE[s:STRING] =
BEGIN
OPEN StreamDefs;
i,j:CARDINAL;
rchord:RChord;
rbeam:RBeam;
inputStream:DiskHandle;
IF UtilityDefs.Test[] THEN ERROR; --
PLEASE save MESA.TYPESCRIPT for me to examine!
-- to try to recover, SEt Root & SEt Module to utility,
-- Interpret Procedure: cleanUp,
-- SEt Root & Module to MusicData,
-- Interpret Procedure: fo (for "FileOut"),
-- save the mesa.typescript somewhere(like ivy) and then
-- reinvoke mockingbird and filein temp.logical.
-- If this doesn’t work, I may still be able to recover
-- part of the file if you save temp.logical somewhere.
inputStream←NewWordStream[s,Write+Append];
inputStream.reset[inputStream];
inputStream.put[inputStream,scoreLength];
FOR i IN [0..scoreLength) DO
[] ← WriteBlock[inputStream,score[i],SIZE[Sync]];
FOR j IN [0..syncLength) DO
IF score[i].event[j]=NIL THEN EXIT;
[] ← WriteBlock[inputStream,score[i].event[j],SIZE[Note]];
ENDLOOP;
ENDLOOP;
inputStream.put[inputStream,chordHeapLength];
FOR i IN [0..chordHeapLength) DO
rchord ← RelativeChord[chordHeap[i]];
[] ← WriteBlock[inputStream,@rchord,SIZE[RChord]];
ENDLOOP;
inputStream.put[inputStream,beamHeapLength];
FOR i IN [0..beamHeapLength) DO
rbeam ← RelativeBeam[beamHeap[i]];
[] ← WriteBlock[inputStream,@rbeam,SIZE[RBeam]];
ENDLOOP;
FileOutSheet[inputStream];
inputStream.destroy[inputStream];
command ← TRUE;
UtilityDefs.DrawScore[TRUE];
END;

FileOutSheet:PROCEDURE[stream:StreamDefs.DiskHandle]=
BEGIN
OPEN StreamDefs;
KeySyncToIndex[];
[] ← WriteBlock[stream,@keyArray,2*keyLength];
[] ← WriteBlock[stream,@show,SIZE[DocumentProfile]];
[] ← WriteBlock[stream,@metrenome,SIZE[INTEGER]];
[] ← WriteBlock[stream,@TF,SIZE[CARDINAL]];
[] ← WriteBlock[stream,@sheet,SIZE[Staves]];
KeyIndexToSync[];
END;

KeySyncToIndex:PROCEDURE =
BEGIN
i,k:CARDINAL;
syncFound:BOOLEAN;
FOR k IN [0..keyLength) DO
syncFound ← FALSE;
FOR i IN [0..scoreLength] DO
IF score[i]#keyArray[k].sync THEN LOOP;
syncFound ← TRUE;
keyArray[k].sync ← LOOPHOLE[i];
EXIT;
ENDLOOP;
IF NOT syncFound THEN ERROR;
ENDLOOP;
END;

RelativeChord:PROCEDURE[c:ChordPTR] RETURNS[RChord]=
BEGIN
i,j:CARDINAL;
r:RChord ← [0,c.stemUp,ALL[100]];
FOR i IN [0..scoreLength) DO
IF score[i]=c.note[0].sync THEN BEGIN r.s←i; EXIT; END;
ENDLOOP;
FOR i IN [0..chordLength) DO
IF c.note[i]=NIL THEN EXIT;
FOR j IN [0..syncLength) DO
IF score[r.s].event[j] # c.note[i] THEN LOOP;
r.n[i] ← j; EXIT;
ENDLOOP;
ENDLOOP;
RETURN[r];
END;

RelativeBeam:PROCEDURE[b:BeamPTR] RETURNS[RBeam]=
BEGIN
i,j:CARDINAL;
r:RBeam;
r.beam ← b↑;
r.n ← ALL[[null,0,0]];
FOR i IN [0..scoreLength) DO
IF r.beam.sync1=score[i] THEN r.beam.sync1←LOOPHOLE[i];
IF r.beam.sync2=score[i] THEN r.beam.sync2←LOOPHOLE[i];
ENDLOOP;
IF LOOPHOLE[r.beam.sync1,CARDINAL]>scoreLength THEN ERROR;
IF LOOPHOLE[r.beam.sync2,CARDINAL]>scoreLength THEN ERROR;
FOR i IN [0..beamLength) DO
IF b.chord[i]=endOfBeam THEN EXIT;
WITH ev:b.chord[i] SELECT FROM
chord => BEGIN
r.n[i].type ← chord;
FOR j IN [0..chordHeapLength) DO
IF chordHeap[j]=ev.c THEN r.n[i].index←j;
ENDLOOP;
END;
beam => BEGIN
r.n[i].type ← beam;
FOR j IN [0..beamHeapLength) DO
IF beamHeap[j]=ev.b THEN r.n[i].index←j;
ENDLOOP;
END;
note => BEGIN
r.n[i].type ← note;
FOR j IN [0..scoreLength) DO
IF score[j]=ev.n.sync THEN r.n[i].sync ← j;
ENDLOOP;
FOR j IN [0..syncLength) DO
IF score[r.n[i].sync].event[j]=ev.n
THEN r.n[i].index←j;
ENDLOOP;
END;
ENDCASE;
ENDLOOP;
RETURN[r];
END;