FilerImpl.mesa
Copyright (C) 1983, 1984 Xerox Corporation. All rights reserved.
Author: John Maxwell
Last Edited by: Maxwell, November 22, 1983 10:13 am
Last Edited by: Doug Wyatt, June 15, 1984 2:14:10 pm PDT
DIRECTORY
Beam USING [AddItem, SetSyncs],
Event USING [SetStave],
FS USING [GetName, OpenFileFromStream, StreamOpen],
IO USING [Close, EndOfStream, STREAM, UnsafeGetBlock, UnsafePutBlock],
MusicDefs USING [BeamHeapPTR, BeamHeapRec, BeamPTR, BeamRec, ChordHeapPTR, ChordHeapRec, ChordPTR, ChordRec, EventPTR, EventRec, KeySignaturePTR, MeasurePTR, MetronomePTR, NotePTR, NoteRec, ScorePTR, StavesPTR, SyncPTR, Time, TimeSignaturePTR, VariousPTR],
MusicFileDefs USING [BeamFormat, beams, ChordFormat, chords, EventFormat, ID, NoteFormat, RelativePTR, SheetFormat, StaffFormat, TieFormat, ViewFormat],
Piece USING [New],
Rope USING [ROPE],
Score USING [FileStats, Look, SetKey, SetMetronome],
Sheet USING [Reset, SetBegin];
FilerImpl: CEDAR PROGRAM
IMPORTS Beam, Event, FS, IO, Piece, Score, Sheet
EXPORTS Score
= BEGIN OPEN MusicDefs, MusicFileDefs;
STREAM: TYPE ~ IO.STREAM;
ROPE: TYPE ~ Rope.ROPE;
Error: ERROR ~ CODE;
****************************************************************************
word-size stream operations
****************************************************************************
Get: PROC[stream: STREAM] RETURNS[data: INTEGER] = TRUSTED {
IF stream.UnsafeGetBlock[[base: LOOPHOLE[LONG[@data]], count: 2]]<2 THEN
ERROR IO.EndOfStream[stream]
ELSE RETURN[data]
};
GetBlock: UNSAFE PROC[stream: STREAM, p: LONG POINTER, length: CARDINAL] = UNCHECKED {
count: INT ~ length*2; -- bytes
IF stream.UnsafeGetBlock[[base: LOOPHOLE[p], count: count]]<count THEN
ERROR IO.EndOfStream[stream];
};
Put: PROC[stream: STREAM, data: INTEGER] = TRUSTED {
stream.UnsafePutBlock[[base: LOOPHOLE[LONG[@data]], count: 2]];
};
PutBlock: PROC[stream: STREAM, p: LONG POINTER, length: CARDINAL] = TRUSTED {
stream.UnsafePutBlock[[base: LOOPHOLE[p], count: length*2]];
};
****************************************************************************
filein
****************************************************************************
minVersion: INTEGER ← 2;
skipBeams: BOOLFALSE;
skipView: BOOLFALSE;
dontSort: BOOLTRUE;
FileIn: PUBLIC PROC[fileName: ROPE] RETURNS[score: ScorePTR] = {
stream: STREAM ~ FS.StreamOpen[fileName, $read];
GetID: PROC[stream: STREAM] RETURNS[ID] ~ INLINE { RETURN[LOOPHOLE[Get[stream]]] };
version: INTEGER ~ Get[stream];
IF version<minVersion THEN ERROR Error; -- too old
score ← Piece.New[3000, TRUE];
score.name ← FS.GetName[FS.OpenFileFromStream[stream]].fullFName;
IF GetID[stream]#scoreID THEN ERROR Error
ELSE { -- read the score
score.length ← Get[stream]; -- number of events following
FOR i: NAT IN[0..score.length) DO
event: EventPTR ~ ReadEvent[stream, score];
score[i] ← event;
IF event.time=-1 THEN WITH event SELECT FROM
staves: StavesPTR => IF staves.staves=style THEN { -- sort the score
IF dontSort THEN ERROR;
FOR j: NAT DECREASING IN[0..i) DO score[j+1] ← score[j]; ENDLOOP;
score[0] ← staves; staves.time ← 0;
};
ENDCASE;
ENDLOOP;
};
IF GetID[stream]#chordHeapID THEN ERROR Error
ELSE { -- read the chord heap
length: NAT ~ Get[stream]; -- number of chords in file
chordHeap: ChordHeapPTR ~ NEW[ChordHeapRec[length+100] ← [chord: ]];
chordHeap.length ← length;
FOR i: NAT IN[0..chordHeap.length) DO
chordHeap[i] ← ReadChord[stream, score];
ENDLOOP;
score.chordHeap ← chordHeap;
};
IF GetID[stream]#beamHeapID THEN ERROR Error
ELSE { -- read the beam heap
length: NAT ~ Get[stream]; -- number of beams in file
beamHeap: BeamHeapPTR ~ NEW[BeamHeapRec[length+100] ← [beam: ]];
beamHeap.length ← length;
FOR i: NAT IN[0..beamHeap.length) DO
beamHeap[i] ← NEW[BeamRec[12] ← [chord: ]];
ENDLOOP;
score.beamHeap ← beamHeap;
FOR i: NAT IN[0..beamHeap.length) DO
ReadBeam[stream, score, score.beamHeap[i]];
ENDLOOP;
FOR i: NAT IN[0..beamHeap.length) DO
Beam.SetSyncs[score.beamHeap[i]];
ENDLOOP;
};
THROUGH [0..9) DO -- nine spares
IF GetID[stream]#nullID THEN ERROR Error;
ENDLOOP;
ReadView[stream, score, version]; -- read view
stream.Close[];
score.command ← TRUE;
Score.FileStats[score];
Sheet.SetBegin[score.sheet, 0];
};
ReadEvent: PROC[stream: STREAM, score: ScorePTR] RETURNS[event: EventPTR] = {
format: EventFormat;
TRUSTED{GetBlock[stream, @format, SIZE[EventFormat]]};
IF format.identifier#eventID THEN ERROR Error;
SELECT format.type FROM
notes => {
sync: SyncPTR ~ NEW[EventRec[sync][MAX[format.length, 10]] ← [
time: format.time, variant: sync[length: format.length, note: ]]];
FOR j: NAT IN[0..format.length) DO
note: NotePTR ~ ReadNote[stream, score];
sync.note[j] ← note; note.sync ← sync;
ENDLOOP;
RETURN[sync];
};
keySignature => {
event: KeySignaturePTR ~ NEW[EventRec[keySignature] ← [
time: format.time, variant: keySignature[key: format.value]]];
RETURN[event];
};
timeSignature => {
event: TimeSignaturePTR ~ NEW[EventRec[timeSignature] ← [
time: format.time, variant: timeSignature[ts: LOOPHOLE[format.value]]]];
RETURN[event];
};
metronome => {
event: MetronomePTR ~ NEW[EventRec[metronome] ← [
time: format.time, variant: metronome[metronome: format.value]]];
RETURN[event];
};
IN[measure..timeSignature) => {
event: MeasurePTR ~ NEW[EventRec[measure] ← [
time: format.time, variant: measure[]]];
event.measure ← SELECT format.type FROM
measure => $measure, repeat1 => $repeat1, repeat2 => $repeat2,
endMeasure => $endMeasure, doubleMeasure => $doubleMeasure,
m5 => $m5, ENDCASE => ERROR;
RETURN[event];
};
IN[staves..spare5) => {
staves: StavesPTR ~ NEW[EventRec[staves] ← [
time: format.time, variant: staves[]]];
staves.staves ← SELECT format.type FROM
staves => $style, clef => $clef, octava1 => $octava1,
octava2 => $octava2, ENDCASE => ERROR;
ReadSheet[stream, staves];
IF format.value=-1 THEN FindStyle[score, staves]
ELSE staves.value ← format.value;
RETURN[staves];
};
ENDCASE => ERROR;
};
ReadNote: PROC[stream: STREAM, score: ScorePTR] RETURNS[note: NotePTR] = {
format: NoteFormat;
TRUSTED{GetBlock[stream, @format, SIZE[NoteFormat]]};
IF format.identifier#noteID THEN ERROR Error;
note ← 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;
IF format.tie THEN ReadTie[stream, score, note];
score.maxVoice ← MAX[note.voice, score.maxVoice];
};
ReadTie: PROC[stream: STREAM, score: ScorePTR, note: NotePTR] = {
format: TieFormat;
TRUSTED{GetBlock[stream, @format, SIZE[TieFormat]]};
IF format.identifier#tieID THEN ERROR Error;
WITH score[format.heap] SELECT FROM
sync: SyncPTR => note.tie ← sync.note[format.index];
ENDCASE => ERROR;
note.tieHeight ← format.height;
note.tie.tied ← TRUE;
};
ReadSheet: PROC[stream: STREAM, staves: StavesPTR] = {
format: SheetFormat;
TRUSTED{GetBlock[stream, @format, SIZE[SheetFormat]]};
staves.height ← format.height;
staves.offset ← format.offset;
staves.length ← format.sl+1;
FOR i: NAT IN[0..staves.length) DO
staff: StaffFormat ~ format.staff[i];
staves.staff[i] ← [pitch: staff.pitch, y: staff.y];
ENDLOOP;
};
FindStyle: PROC[score: ScorePTR, staves: StavesPTR] = {
index: INTEGER ← 1;
FOR i: NAT IN[1..staves.length) DO
IF staves.staff[i].y#staves.staff[i-1].y THEN 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]];
};
ReadChord: PROC[stream: STREAM, score: ScorePTR] RETURNS[chord: ChordPTR] = {
format: ChordFormat;
TRUSTED{GetBlock[stream, @format, SIZE[ChordFormat]]};
IF format.identifier#chordID THEN ERROR Error;
chord ← NEW[ChordRec[format.length+2] ← [note: ]];
chord.stemUp ← format.stemUp;
chord.length ← format.length;
FOR j: NAT IN[0..chord.length) DO
pointer: RelativePTR;
TRUSTED{GetBlock[stream, @pointer, SIZE[RelativePTR]]};
WITH score[pointer.heap] SELECT FROM
sync: SyncPTR => {
note: NotePTR ~ sync.note[pointer.index];
chord.note[j] ← note; note.chord ← chord;
};
ENDCASE => ERROR;
ENDLOOP;
};
ReadBeam: PROC[stream: STREAM, score: ScorePTR, beam: BeamPTR] = {
format: BeamFormat;
TRUSTED{GetBlock[stream, @format, SIZE[BeamFormat]]};
IF format.identifier#beamID THEN ERROR 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;
FOR i: NAT IN[0..format.length) DO
vp: VariousPTR ~ ReadBeamEntry[stream, score, beam];
Beam.AddItem[score, beam, vp];
ENDLOOP;
};
ReadBeamEntry: PROC[stream: STREAM, score: ScorePTR, beam: BeamPTR]
RETURNS[vp: VariousPTR] = {
pointer: RelativePTR;
TRUSTED{GetBlock[stream, @pointer, SIZE[RelativePTR]]};
SELECT pointer.heap FROM
beams => RETURN[score.beamHeap[pointer.index]];
chords => RETURN[score.chordHeap[pointer.index]];
ENDCASE;
WITH score[pointer.heap] SELECT FROM
sync: SyncPTR => RETURN[sync.note[pointer.index]];
ENDCASE => ERROR;
};
SkipBeam: PROC[stream: STREAM] = {
format: BeamFormat;
TRUSTED{GetBlock[stream, @format, SIZE[BeamFormat]]};
IF format.identifier#beamID THEN ERROR Error;
FOR i: NAT IN[0..format.length) DO SkipBeamEntry[stream] ENDLOOP;
};
SkipBeamEntry: PROC[stream: STREAM] = {
pointer: RelativePTR;
TRUSTED{GetBlock[stream, @pointer, SIZE[RelativePTR]]};
};
ReadView: PROC[stream: STREAM, score: ScorePTR, version: INTEGER] = {
view: ViewFormat;
TRUSTED{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.SetMetronome[score, 0, LAST[Time], view.speed];
Sheet.Reset[score];
};
SkipView: PROC[stream: STREAM] = {
view: ViewFormat;
TRUSTED{GetBlock[stream, @view, SIZE[ViewFormat]]};
};
END.
****************************************************************************
fileout
****************************************************************************
FileOut: PUBLIC PROC[score: ScorePTR, fileName: STRING]
RETURNS[BOOLEAN] = {
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 => {
pointer.heap ← Event.GetScoreIndex[score, ev.n.sync];
pointer.index ← Note.GetSyncIndex[Event.Sync[score[pointer.heap]], ev.n];
};
chord => {
pointer.heap ← chords;
pointer.index ← Chord.GetHeapIndex[score.chordHeap, ev.c];
};
beam => {
pointer.heap ← beams;
pointer.index ← Beam.GetHeapIndex[score.beamHeap, ev.b];
};
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];
};
WriteEvent: PROC[data: EventPTR, format: POINTER TO EventFormat] = {
Add: PROC[a, b: UNSPECIFIED] RETURNS[UNSPECIFIED] = INLINE
{ RETURN[LOOPHOLE[a, CARDINAL] + LOOPHOLE[b, CARDINAL]]; };
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;
metronome => format.value ← Event.Metronome[data].metronome;
staves => {
format.type ← Add[format.type, Event.Staves[data].staves];
format.ignore ← TRUE};
ENDCASE => ERROR;
[] ← PutBlock[stream, @format, SIZE[EventFormat]];
};
WriteNote: PROC[note: NotePTR, format: POINTER TO NoteFormat] = {
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]];
};
WriteTie: PROC[score: ScorePTR, note: NotePTR, format: POINTER TO TieFormat] = {
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]];
};  
WriteSheet: PROC[staves: StavesPTR, sheet: POINTER TO SheetFormat] = {
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]];
};
WriteChord: PROC[chord: ChordPTR, format: POINTER TO ChordFormat] = {
format.stemUp ← chord.stemUp;
format.length ← chord.length;
[] ← PutBlock[stream, @format, SIZE[ChordFormat]];
};
WriteBeam: PROC[beam: BeamPTR, format: POINTER TO BeamFormat] = {
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]];
};
WriteView: PROC[score: ScorePTR, view: POINTER TO ViewFormat] = {
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]];
};