-- Author: John Maxwell
-- Last Edited by: Maxwell, November 22, 1983 10:13 am

DIRECTORY
	Beam USING [AddItem, GetHeapIndex, New, SetSyncs], 
	Chord USING [GetHeapIndex], 
	Directory USING [Lookup], 
	Event USING [GetScoreIndex, KeySignature, Measure, Metrenome, SetStave, Staves, Sync, TimeSignature], 
	FileStream USING [Create], 
	MusicDefs, 
	MusicFileDefs, 
	Note USING [GetSyncIndex], 
	Piece USING [New, Overflow], 
	Score USING [FileStats, Look, SetKey, SetMetrenome, Test], 
	Sheet USING [Reset, SetBegin], 
	Stream USING [Delete, GetBlock, Handle, PutBlock], 
	Utility USING [NewSegment];

FilerImpl: MONITOR
  IMPORTS Beam, Chord, Directory, FileStream, MusicDefs, Note, Piece, Score, Sheet, Stream, Event, Utility 
  EXPORTS Score = 

BEGIN
OPEN MusicDefs, MusicFileDefs;


Error: SIGNAL;

-- ****************************************************************************
-- buffer for stream
-- ****************************************************************************

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

Get: PROCEDURE[stream: Stream.Handle] RETURNS[data: UNSPECIFIED] = 
INLINE BEGIN GetBlock[stream, @data, 1]; END;

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

Put: PROCEDURE[stream: Stream.Handle, data: INTEGER] = INLINE {PutBlock[stream, @data, 1]};

PutBlock: PROCEDURE[stream: Stream.Handle, p: POINTER, length: CARDINAL] = 
BEGIN
array: POINTER TO ARRAY[0..1000) OF INTEGER ← LOOPHOLE[p];
IF length = 0 THEN IF bufferIndex # 0 THEN {
	[] ← Stream.PutBlock[stream, [@buffer, 0, 2*bufferIndex]];
	bufferIndex ← 0};
FOR i: CARDINAL IN [0..length) DO
	IF bufferIndex >= bufferLength THEN {
		[] ← Stream.PutBlock[stream, [@buffer, 0, 2*bufferIndex]];
		bufferIndex ← 0};
	buffer[bufferIndex] ← array[i];
	bufferIndex ← bufferIndex + 1;
	ENDLOOP;
END;

-- ****************************************************************************
-- filein
-- ****************************************************************************

version: INTEGER;

FileIn: PUBLIC ENTRY PROCEDURE[fileName: STRING] RETURNS[score: ScorePTR] = 
BEGIN ENABLE Piece.Overflow => {IF score = old THEN score ← new; RESUME};
view: ViewFormat;
newMax: CARDINAL;
stream ← FileStream.Create[Directory.Lookup[fileName] -- Stream.Read -- ];
GetBlock[stream, NIL, 0]; -- pre-fills the buffer
version ← Get[stream];
IF version # versionID THEN Error; -- need a different error here
score ← Piece.New[3000];
-- Utility.InitStorage[score]; 
-- format in score
IF Get[stream] # scoreID THEN Error;
score.length ← Get[stream];
FOR i: CARDINAL IN [0..score.length) DO
	event: EventFormat; -- local storage
	score[i] ← ReadEvent[@event];
	SELECT score[i].type FROM
		sync => {
			sync: SyncPTR ← Event.Sync[score[i]];
			FOR j: CARDINAL IN [0..sync.length) DO
				tie: TieFormat;
				note: NoteFormat;
				sync.note[j] ← ReadNote[@note];
				sync.note[j].sync ← sync;
				score.maxVoice ← MAX[note.voice, score.maxVoice];
				IF note.tie THEN ReadTie[score, sync.note[j], @tie];
				ENDLOOP};
		staves => {
			format: SheetFormat;
			staves: StavesPTR ← Event.Staves[score[i]];
			ReadSheet[score, staves, @format];
			IF event.type = staves AND event.value = -1 
				THEN FindStyle[score, staves]
				ELSE staves.value ← event.value;
			};
		ENDCASE;
	IF score[i].time = -1 AND score[i].type = staves 
		AND Event.Staves[score[i]].staves = style THEN { -- sort the score
			temp: EventPTR = score[i];
			FOR j: CARDINAL DECREASING IN [0..i) DO score[j+1] ← score[j]; ENDLOOP;
			score[0] ← temp;
			score[0].time ← 0};
	ENDLOOP;
-- format in chordheap
IF Get[stream] # chordHeapID THEN Error;
newMax ← Get[stream]+100;
score.chordHeap ← Utility.NewSegment[SIZE[ChordHeapRec[newMax]], newMax, SIZE[ChordHeapRec[0]]-1];
FOR i: CARDINAL IN [0..score.chordHeap.length) DO
	chord: ChordFormat;
	score.chordHeap[i] ← ReadChord[@chord];
	FOR j: CARDINAL IN [0..chord.length) DO
		pointer: RelativePTR;
		GetBlock[stream, @pointer, SIZE[RelativePTR]];
		score.chordHeap[i].note[j] ← Event.Sync[score[pointer.heap]].note[pointer.index];
		score.chordHeap[i].note[j].chord ← score.chordHeap[i];
		ENDLOOP;
	ENDLOOP;
-- format in beamheap
IF Get[stream] # beamHeapID THEN Error;
newMax ← Get[stream]+100;
score.beamHeap ← Utility.NewSegment[SIZE[BeamHeapRec[newMax]], newMax, SIZE[BeamHeapRec[0]]-1];
FOR i: CARDINAL IN [0..score.beamHeap.length) DO 
	score.beamHeap[i] ← Beam.New[score, 4]; 
	ENDLOOP;
FOR i: CARDINAL IN [0..score.beamHeap.length) DO
	beamFormat: BeamFormat;
	beam: BeamPTR ← score.beamHeap[i];
	ReadBeam[beam, @beamFormat];
	FOR j: CARDINAL IN [0..beamFormat.length) DO
		beam ← Beam.AddItem[score, beam, ReadBeamEntry[score, beam]];
		ENDLOOP;
	ENDLOOP;
FOR i: CARDINAL IN [0..score.beamHeap.length) DO 
	Beam.SetSyncs[score.beamHeap[i]]; 
	ENDLOOP;
-- format in spares
IF Get[stream] # nullID THEN Error;
IF Get[stream] # nullID THEN Error;
IF Get[stream] # nullID THEN Error;
IF Get[stream] # nullID THEN Error;
IF Get[stream] # nullID THEN Error;
IF Get[stream] # nullID THEN Error;
IF Get[stream] # nullID THEN Error;
IF Get[stream] # nullID THEN Error;
IF Get[stream] # nullID THEN Error;
ReadView[score, @view];
-- clean up and return
bufferIndex ← 0;
Stream.Delete[stream];
score.command ← TRUE;
Score.FileStats[score];
Sheet.SetBegin[score.sheet, 0];
END;

ReadEvent: PROCEDURE[format: POINTER TO EventFormat] RETURNS[event: EventPTR] = 
BEGIN
Subtract: PROC[a, b: UNSPECIFIED] RETURNS[UNSPECIFIED] = INLINE
	BEGIN RETURN[LOOPHOLE[a, CARDINAL] - LOOPHOLE[b, CARDINAL]]; END;
GetBlock[stream, @format, SIZE[EventFormat]];
IF format.identifier # eventID THEN Error;
SELECT format.type FROM
	notes => event ← zone.NEW[EventRec.sync[format.length + 2]];
	keySignature => {
		event ← zone.NEW[EventRec.keySignature];
		Event.KeySignature[event].key ← format.value};
	timeSignature => {
		event ← zone.NEW[EventRec.timeSignature];
		Event.TimeSignature[event].ts ← LOOPHOLE[format.value]};
	metrenome => {
		event ← zone.NEW[EventRec.metrenome];
		Event.Metrenome[event].metrenome ← format.value};
	IN [measure..timeSignature) => {
		measure: MusicFileDefs.EventTypeFormat ← measure;
		event ← zone.NEW[EventRec.measure];
		Event.Measure[event].measure ← Subtract[format.type, measure]};
	IN [staves.. spare14) => {
		staves: MusicFileDefs.EventTypeFormat ← staves;
		event ← zone.NEW[EventRec.staves];
		Event.Staves[event].staves ← Subtract[format.type, staves]};
	ENDCASE => ERROR;
event.time ← format.time;
END; 

ReadNote: PROCEDURE[format: POINTER TO NoteFormat] RETURNS[note: NotePTR] = 
BEGIN
GetBlock[stream, @format, SIZE[NoteFormat]];
IF format.identifier # noteID THEN Error;
note ← zone.NEW[NoteRec];
note.pitch ← format.pitch;
note.voice ← format.voice;
note.value ← format.value;
note.spelled ← format.spelled;
note.rest ← format.rest;
note.dotted ← format.dotted;
note.doubleDotted ← format.doubleDotted;
note.grace ← format.grace;
note.stemUp ← format.stemUp;
note.embellish ← format.embellish;
note.staff ← format.staff;
note.toc ← format.toc;
note.duration ← format.duration;
END; 

ReadTie: PROC[score: ScorePTR, note: NotePTR, format: POINTER TO TieFormat] = 
BEGIN
GetBlock[stream, @format, SIZE[TieFormat]];
IF format.identifier # tieID THEN Error;
note.tie ← Event.Sync[score[format.heap]].note[format.index];
note.tieHeight ← format.height;
note.tie.tied ← TRUE;
END;

ReadSheet: PROCEDURE[score: ScorePTR, staves: StavesPTR, format: POINTER TO SheetFormat] = 
BEGIN
-- old.event ← LOOPHOLE[staves↑];
-- staves ← LOOPHOLE[@old.event];
-- IF version < 3 THEN ConvertSheet2[old];
GetBlock[stream, @format, SIZE[SheetFormat]];
staves.height ← format.height;
staves.offset ← format.offset;
staves.length ← format.sl-1;
FOR i: CARDINAL IN [0..staves.length) DO
	staves.staff[i] ← format.staff[i];
	ENDLOOP;
END;

FindStyle: PROCEDURE[score: ScorePTR, staves: StavesPTR] = 
BEGIN
index: INTEGER ← 1;
FOR i: CARDINAL IN [1..staves.length) 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;
staves.value ← index;
Event.SetStave[score, staves, score.style[index]];
END; 

ReadChord: PROCEDURE[format: POINTER TO ChordFormat] RETURNS[chord: ChordPTR] = 
BEGIN
IF format.identifier # chordID THEN Error;
GetBlock[stream, @format, SIZE[ChordFormat]];
chord ← zone.NEW[ChordRec[format.length + 2]];
chord.stemUp ← format.stemUp;
chord.length ← format.length;
END; 

ReadBeam: PROCEDURE[beam: BeamPTR, format: POINTER TO BeamFormat] = 
BEGIN
GetBlock[stream, @format, SIZE[BeamFormat]];
IF format.identifier # beamID THEN Error;
beam.tilt ← format.tilt;
beam.beamed ← format.beamed;
beam.ntuple ← format.ntuple;
beam.against ← format.against;
beam.height ← format.height;
beam.invisible ← format.invisible;
beam.staff ← format.staff;
beam.length ← format.length;
END; 

ReadBeamEntry: PROC[score: ScorePTR, beam: BeamPTR] RETURNS[vp: VariousPTR] = 
BEGIN
pointer: RelativePTR;
GetBlock[stream, @pointer, SIZE[RelativePTR]];
SELECT pointer.heap FROM
	beams => BEGIN
		b: BeamPTR ← score.beamHeap[pointer.index];
		b.beam ← beam;
		vp ← [beam[b]];
		END; 
	chords => BEGIN
		c: ChordPTR ← score.chordHeap[pointer.index];
		FOR k: CARDINAL IN [0..c.length) DO
			c.note[k].beam ← beam;
			ENDLOOP;
		vp ← [chord[c]];
		END; 
	ENDCASE => BEGIN
		n: NotePTR ← Event.Sync[score[pointer.heap]].note[pointer.index];
		n.beam ← beam;
		vp ← [note[n]];
		END;
END;

ReadView: PROCEDURE[score: ScorePTR, view: POINTER TO ViewFormat] = 
BEGIN ENABLE Piece.Overflow => IF score = old THEN score ← new;
GetBlock[stream, @view, SIZE[ViewFormat]];
IF view.display = physical 
	THEN score.sheet.density ← view.scale
	ELSE score.sheet.justification ← view.scale;
SELECT view.sheet FROM -- convert from old indices to new ones
	1   => Score.Look[score, style, , 0];
	6, 7 => Score.Look[score, style, , 1];
	2   => Score.Look[score, style, , 2];
	4, 5 => Score.Look[score, style, , 3];
	3   => Score.Look[score, style, , 4];
	ENDCASE;
score.sheet.accidental ← view.accidental;
score.sheet.sync ← view.sync;
score.sheet.notehead ← view.notehead;
score.sheet.display ← view.display;
score.sheet.noCarry ← view.noCarry;
Score.Look[score, hardcopy, view.hardcopy, ];
IF version = 1 THEN Score.SetKey[score, 0, LAST[Time], view.key];
IF version = 1 THEN Score.SetMetrenome[score, 0, LAST[Time], view.speed];
Sheet.Reset[score];
END;

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

FileOut: PUBLIC ENTRY PROCEDURE[score: ScorePTR, fileName: STRING] 
	RETURNS[BOOLEAN] = 
BEGIN
OPEN FileStream;
beam: BeamFormat ← []; -- fills in default values
view: ViewFormat ← [];
sheet: SheetFormat ← [];
event: EventFormat ← [];
chord: ChordFormat ← [];
IF Score.Test[score] THEN RETURN[FALSE]; 
stream ← FileStream.Create[Directory.Lookup[fileName] -- Stream.Write+Stream.Append-- ];
-- stream.reset[stream];
-- format out score
Put[stream, versionID];
Put[stream, scoreID];
Put[stream, score.length];
FOR i: CARDINAL IN [0..score.length) DO
	sync: SyncPTR;
	tie: TieFormat ← [];
	note: NoteFormat ← [];
	WriteEvent[score[i], @event];
	IF score[i].type = staves THEN WriteSheet[Event.Staves[score[i]], @sheet];
	IF score[i].type # sync THEN LOOP;
	sync ← Event.Sync[score[i]];
	FOR j: CARDINAL IN [0..sync.length) DO
		WriteNote[sync[j], @note];
		IF sync[j].tie # NIL THEN WriteTie[score, sync[j], @tie];
		ENDLOOP;
	ENDLOOP;
-- format out chordheap
Put[stream, chordHeapID];
Put[stream, score.chordHeap.length];
FOR i: CARDINAL IN [0..score.chordHeap.length) DO
	pointer: RelativePTR;
	WriteChord[score.chordHeap[i], @chord];
	pointer.heap ← Event.GetScoreIndex[score, score.chordHeap[i].note[0].sync];	    
	FOR j: CARDINAL IN [0..score.chordHeap[i].length) DO
		note: NotePTR ← score.chordHeap[i].note[j];
		pointer.index ← Note.GetSyncIndex[note.sync, note];
		[] ← PutBlock[stream, @pointer, SIZE[RelativePTR]];
		ENDLOOP;
	ENDLOOP;
-- format out beamheap
Put[stream, beamHeapID];
Put[stream, score.beamHeap.length];
FOR i: CARDINAL IN [0..score.beamHeap.length) DO
	WriteBeam[score.beamHeap[i], @beam];
	[] ← PutBlock[stream, @beam, SIZE[BeamFormat]];
	FOR j: CARDINAL IN [0..score.beamHeap[i].length) DO
		pointer: RelativePTR;
		WITH ev: score.beamHeap[i].chord[j] SELECT FROM
			note => BEGIN
				pointer.heap ← Event.GetScoreIndex[score, ev.n.sync];
				pointer.index ← Note.GetSyncIndex[Event.Sync[score[pointer.heap]], ev.n];
				END;
			chord => BEGIN
				pointer.heap ← chords;
				pointer.index ← Chord.GetHeapIndex[score.chordHeap, ev.c];
				END;
			beam => BEGIN
				pointer.heap ← beams;
				pointer.index ← Beam.GetHeapIndex[score.beamHeap, ev.b];
				END;
			ENDCASE;
		[] ← PutBlock[stream, @pointer, SIZE[RelativePTR]];
		ENDLOOP;
	ENDLOOP;
-- format out nine spares
Put[stream, nullID];
Put[stream, nullID];
Put[stream, nullID];
Put[stream, nullID];
Put[stream, nullID];
Put[stream, nullID];
Put[stream, nullID];
Put[stream, nullID];
Put[stream, nullID];
-- format out view
WriteView[score, @view];
-- clean up and return
[] ← PutBlock[stream, NIL, 0]; -- clean up buffer
Stream.Delete[stream];
score.command ← TRUE;
RETURN[TRUE];
END;

WriteEvent: PROCEDURE[data: EventPTR, format: POINTER TO EventFormat] = 
BEGIN
Add: PROC[a, b: UNSPECIFIED] RETURNS[UNSPECIFIED] = INLINE
	BEGIN RETURN[LOOPHOLE[a, CARDINAL] + LOOPHOLE[b, CARDINAL]]; END;
format.time ← data.time;
format.type ← LOOPHOLE[data.type];
format.ignore ← FALSE;
format.value ← 0;
format.length ← 0;
SELECT data.type FROM
    measure => format.type ← Add[format.type, Event.Measure[data].measure];
    sync => format.length ← Event.Sync[data].length;
    timeSignature => format.value ← LOOPHOLE[Event.TimeSignature[data].ts];
    keySignature => format.value ← Event.KeySignature[data].key;
	 metrenome => format.value ← Event.Metrenome[data].metrenome;
	 staves => {
	 	format.type ← Add[format.type, Event.Staves[data].staves]; 
	 	format.ignore ← TRUE};
	 ENDCASE => ERROR;
[] ← PutBlock[stream, @format, SIZE[EventFormat]];
END; 

WriteNote: PROCEDURE[note: NotePTR, format: POINTER TO NoteFormat] = 
BEGIN
format.pitch ← note.pitch;
format.voice ← note.voice;
format.value ← note.value;
format.spelled ← note.spelled;
format.rest ← note.rest;
format.dotted ← note.dotted;
format.doubleDotted ← note.doubleDotted;
format.grace ← note.grace;
format.stemUp ← note.stemUp;
format.embellish ← note.embellish;
format.tie ← note.tie # NIL;
format.staff ← note.staff;
format.toc ← note.toc;
format.duration ← note.duration;
[] ← PutBlock[stream, @format, SIZE[NoteFormat]];
END; 

WriteTie: PROC[score: ScorePTR, note: NotePTR, format: POINTER TO TieFormat] = 
BEGIN
format.height ← note.tieHeight;
format.heap ← Event.GetScoreIndex[score, note.tie.sync];
format.index ← Note.GetSyncIndex[note.tie.sync, note.tie];
[] ← PutBlock[stream, @format, SIZE[TieFormat]];
END;		

WriteSheet: PROCEDURE[staves: StavesPTR, sheet: POINTER TO SheetFormat] = 
BEGIN
sheet.height ← staves.height;
sheet.offset ← staves.offset;
sheet.sl ← staves.length+1;
FOR i: CARDINAL IN [0..staves.length) DO
	sheet.staff[i] ← staves.staff[i];
	ENDLOOP;
[] ← PutBlock[stream, @sheet, SIZE[SheetFormat]];
END;

WriteChord: PROCEDURE[chord: ChordPTR, format: POINTER TO ChordFormat] = 
BEGIN
format.stemUp ← chord.stemUp;
format.length ← chord.length;
[] ← PutBlock[stream, @format, SIZE[ChordFormat]];
END; 

WriteBeam: PROCEDURE[beam: BeamPTR, format: POINTER TO BeamFormat] = 
BEGIN
format.tilt ← beam.tilt;
format.beamed ← beam.beamed;
format.ntuple ← beam.ntuple;
format.against ← beam.against;
format.height ← beam.height;
format.invisible ← beam.invisible;
format.staff ← beam.staff;
format.length ← beam.length;
[] ← PutBlock[stream, @format, SIZE[BeamFormat]];
END; 

WriteView: PROCEDURE[score: ScorePTR, view: POINTER TO ViewFormat] = 
BEGIN
IF score.sheet.display = physical
	THEN view.scale ← score.sheet.density
	ELSE view.scale ← score.sheet.justification;
view.accidental ← score.sheet.accidental;
view.notehead ← score.sheet.notehead;
view.sync ← score.sheet.sync;
view.display ← score.sheet.display;
view.noCarry ← score.sheet.noCarry;
view.hardcopy ← score.sheet.hardcopy;
[] ← PutBlock[stream, @view, SIZE[ViewFormat]];
END;

 
END...








PutReals: PROCEDURE = 
BEGIN
i: CARDINAL;
FOR i IN [0..score.beamHeap.length) 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;
i, length: CARDINAL ← 0;
pn: PhysicalNote;
note: NotePTR;
sync: EventPTR ← 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[@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
	     AddEventToPiece[sync, score];
	     oldToc ← note.toc;
	     sync ← NewEvent[];
	     END;
     sync.time ← note.toc/256;
     AddToEvent[sync, note];
     i ← i + 1;
     IF i = length THEN EXIT;
     ENDLOOP;
AddEventToPiece[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[0];
inputStream.put[0];
FOR i IN [0..score.length) DO
    FOR j IN [0..syncLength) DO
	IF (n ← score[i][j]) = NIL THEN EXIT;
	IF score[i][j].rest THEN LOOP;
	IF n.duration = 0 THEN LOOP;
	pNote ← [n.pitch, n.duration, 50, n.toc];
        [] ← WriteBlock[@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[]; 
score.length ← inputStream.get[inputStream]+start;
maxVoice ← 0;
FOR i IN [start..score.length) DO score[i] ← NewEvent[]; ENDLOOP;
FOR i IN [start..score.length) DO
    [] ← ReadBlock[inputStream, score[i], SIZE[Event]];
    score[i].time ← score[i].time + offset;
    FOR j IN [0..syncLength) DO
	IF score[i][j] = NIL THEN EXIT;
	[] ← ReadBlock[inputStream, score[i][j] ← NewNote[], SIZE[Note]];
	score[i][j].sync ← score[i];
	IF skipBeams THEN score[i][j].beam ← NIL;
	maxVoice ← MAX[maxVoice, score[i][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];
KeyIndexToEvent[];
END;

KeyIndexToEvent: 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 ← score.length;
bstart ← score.beamHeap.length;
cstart ← score.chordHeap.length;
IF score.length # 0 THEN offset ← score[score.length-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][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][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 format if you save temp.logical somewhere.
inputStream ← NewWordStream[s, Write+Append];
inputStream.reset[inputStream];
inputStream.put[inputStream, score.length];
FOR i IN [0..score.length) DO
    [] ← WriteBlock[inputStream, score[i], SIZE[Event]];
    FOR j IN [0..syncLength) DO
	IF score[i][j] = NIL THEN EXIT;
	[] ← WriteBlock[inputStream, score[i][j], SIZE[Note]];
	ENDLOOP;
    ENDLOOP;
inputStream.put[inputStream, score.chordHeap.length];
FOR i IN [0..score.chordHeap.length) DO
    rchord ← RelativeChord[chordHeap[i]];
    [] ← WriteBlock[inputStream, @rchord, SIZE[RChord]];
    ENDLOOP;
inputStream.put[inputStream, score.beamHeap.length];
FOR i IN [0..score.beamHeap.length) 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;
KeyEventToIndex[];
[] ← 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]];
KeyIndexToEvent[];
END;

KeyEventToIndex: PROCEDURE = 
BEGIN
i, k: CARDINAL;
syncFound: BOOLEAN;
FOR k IN [0..keyLength) DO
    syncFound ← FALSE;
    FOR i IN [0..score.length] 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..score.length) 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][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..score.length) 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] > score.length THEN ERROR;
IF LOOPHOLE[r.beam.sync2, CARDINAL] > score.length 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..score.chordHeap.length) 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..score.beamHeap.length) 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..score.length) 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][j] = ev.n 
			   THEN r.n[i].index ← j;
		  	ENDLOOP;
		  END;
	ENDCASE;
    ENDLOOP;
RETURN[r];
END;

ConvertSheet2: PROCEDURE[old: EventPTR] = 
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..score.length) 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;

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