UtilityImpl.mesa
Copyright (C) 1983, 1984 Xerox Corporation. All rights reserved.
Author: John Maxwell
Last Edited by: Maxwell, November 22, 1983 10:23 am
Last Edited by: Doug Wyatt, June 18, 1984 12:59:04 pm PDT
DIRECTORY
Beam USING [Free],
Chord USING [Free],
Event USING [GetScoreIndex, GetOctava, Staves, Sync],
Font USING [CreateScaled, FONT],
Imager,
IO USING [int, Put, PutChar, PutRope, STREAM],
MusicDefs,
Rope USING [ROPE],
Score USING [],
Utility USING [Box],
ViewerIO USING [CreateViewerStreams];
UtilityImpl: CEDAR PROGRAM
IMPORTS Beam, Chord, Event, Font, Imager, IO, ViewerIO
EXPORTS Score, Utility
= BEGIN OPEN MusicDefs;
Context: TYPE ~ Imager.Context;
FONT: TYPE ~ Font.FONT;
****************************************************************************
graphics procedures
****************************************************************************
DrawLine: PUBLIC PROC[dc: Context, x1, y1, x2, y2: INTEGER] = {
Imager.MaskVector[dc, [x1, y1], [x2, y2]];
};
DrawCubic: PUBLIC PROC[dc: Context, x1, y1, x2, y2, height: INTEGER] = {
path: Imager.Trajectory ← Imager.MoveTo[[x1, y1]];
path ← Imager.CurveTo[path,
[(4*x1+x2)/5, height+y1], [(x1+4*x2)/5, height+y1], [x2, y2]];
path ← Imager.LineTo[path, [x2, y2-width1]];
path ← Imager.CurveTo[path,
[(x1+4*x2)/5, height-width2+y1], [(4*x1+x2)/5, height-width2+y1], [x1, y1-width1]];
Imager.MaskFill[dc, path];
};
DrawBox: PUBLIC PROC[dc: Context, box: Utility.Box] ~ {
Imager.MaskRectangle[dc, box.xmin, box.ymin, box.xmax-box.xmin, box.ymax-box.ymin];
};
SetCP: PUBLIC PROC[dc: Context, x, y: REAL] ~ {
Imager.SetXY[dc, [x, y]];
};
width1: INTEGER ← 0;
width2: INTEGER ← 3;
music5: FONT ~ Font.CreateScaled["Xerox/PressFonts/Music/MRR", 5, $Screen];
music8: FONT ~ Font.CreateScaled["Xerox/PressFonts/Music/MRR", 8, $Screen];
text10: FONT ~ Font.CreateScaled["Xerox/PressFonts/TimesRoman/MRR", 10, $Screen];
text12: FONT ~ Font.CreateScaled["Xerox/PressFonts/TimesRoman/MRR", 12, $Screen];
SetFont: PUBLIC PROC[dc: Imager.Context, font: FontType, size: INTEGER] = { -- a hack
SELECT font FROM
music => SELECT size FROM
5 => Imager.SetFont[dc, music5];
8 => Imager.SetFont[dc, music8];
ENDCASE => ERROR;
text => SELECT size FROM
10 => Imager.SetFont[dc, text10];
12 => Imager.SetFont[dc, text12];
ENDCASE => ERROR;
ENDCASE => ERROR;
};
DrawChar: PUBLIC PROC[dc: Context, c: CHAR] = { Imager.ShowChar[dc, c] };
DrawString: PUBLIC PROC[dc: Context, s: Rope.ROPE] = { Imager.ShowCharacters[dc, s] };
light: PUBLIC CARDINAL ← 102041B;
greyColor: Imager.ConstantColor ~ Imager.MakeGray[0.5];
lightColor: Imager.ConstantColor ~ Imager.MakeGray[0.3];
SetColor: PUBLIC PROC[dc: Context, color: ColorType] ~ {
SELECT color FROM
black => Imager.SetColor[dc, Imager.black];
grey => Imager.SetColor[dc, greyColor];
light => Imager.SetColor[dc, lightColor];
white => Imager.SetColor[dc, Imager.white];
ENDCASE => ERROR;
};
SetBrush: PUBLIC PROC[dc: Context, color: ColorType, mode: PaintMode] ~ {
SetColor[dc, color]; -- ignores mode
};
PointSize: PROC[sheet: SheetPTR, n: INTEGER] RETURNS[INTEGER] = INLINE {
SELECT sheet.scale FROM
1 => RETURN[n];
2 => RETURN[2*n/3];
4 => RETURN[n/4];
ENDCASE;
RETURN[n];
};
**********************************************************
printing the score
**********************************************************
print: PUBLIC BOOLFALSE;
printChar: BOOLFALSE;
****************************************************************************
filestats, consistency checking
****************************************************************************
FileStats: PUBLIC PROC[score: ScorePTR] = {
OPEN Utility;
memory: LONG INTEGER;
highWater, notes: INTEGER ← 0;
WriteLine[""];
memory ← SIZE[ScoreRec[score.length]];
memory ← memory+ LONG[score.length]*SIZE[EventRec];
WriteString["scoreLength = "];
WriteNumber[score.length, [10, FALSE, TRUE, 4]];
FOR i: CARDINAL IN [0..score.length) DO
sync: SyncPTR;
IF score.event[i].type # sync THEN LOOP;
sync ← Event.Sync[score.event[i]];
notes ← notes + sync.length;
highWater ← MAX[highWater, sync.length];
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[score.chordHeap.length, [10, FALSE, TRUE, 3]];
FOR i: CARDINAL IN [0..score.chordHeap.length) DO
highWater ← MAX[highWater, score.chordHeap.chord[i].length];
ENDLOOP;
WriteString["; maxUsageOfChords = "];
WriteNumber[highWater, [10, FALSE, TRUE, 2]];
memory ← memory + SIZE[ChordRec[score.chordHeap.length]];
memory ← memory + SIZE[BeamRec[score.beamHeap.length]];
WriteLine[""];
highWater ← 0;
WriteString[" # beams = "];
WriteNumber[score.beamHeap.length, [10, FALSE, TRUE, 3]];
FOR i: CARDINAL IN [0..score.beamHeap.length) DO
highWater ← MAX[highWater, score.beamHeap.beam[i].length];
ENDLOOP;
WriteString["; maxUsageOfBeams = "];
WriteNumber[highWater, [10, FALSE, TRUE, 2]];
WriteLine[""];
WriteString["event = "];
WriteNumber[SIZE[EventRec], [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 = "];
WriteNumber[memory, [10, FALSE, TRUE, 8]];
WriteLine[""];
};
Test: PUBLIC PROC[score: ScorePTR] RETURNS[BOOL] = {
n: NotePTR;
s: EventPTR;
lastBeam: BeamPTR ← NIL;
beamFound: BOOL;
sync1, sync2: BOOL;
dataError ← FALSE;
FOR i: CARDINAL IN [0..score.max) DO
IF score.event[i] # NIL AND i >= score.length THEN
WriteError[sync, i, -1, "beyond scoreLength"];
IF score.event[i] = NIL AND i < score.length THEN WriteError[sync, i, -1, " = NIL"];
IF score.event[i] = NIL THEN LOOP;
IF i # 0 AND score[i-1].time > score[i].time THEN
WriteError[sync, i, -1, "out of place"];
WITH score.event[i] SELECT FROM
ev: StavesPTR => IF ev.staves IN [octava1..octava2] AND
Event.GetOctava[score, Event.GetOctava[score, Event.Staves[score.event[i]]]] # score.event[i]
THEN WriteError[sync, i, -1, " = octava with no matching end."];
ev: SyncPTR => {
IF ev.length = 0 THEN WriteError[sync, i, -1, "empty"];
FOR j: CARDINAL IN [0..ev.max) DO
n ← ev.note[j];
IF n # NIL AND j >= ev.length THEN WriteError[sync, i, j, "beyond syncLength"];
IF n = NIL AND j < ev.length THEN WriteError[sync, i, j, " = NIL"];
IF n = NIL THEN LOOP;
IF n.sync # score.event[i] THEN WriteError[sync, i, j, "n.sync # score.event[i]"];
IF n.beam = NIL THEN LOOP;
IF n.beam = lastBeam THEN LOOP;
beamFound ← FALSE;
FOR k: CARDINAL IN [0..score.beamHeap.length) DO
IF score.beamHeap.beam[k] # n.beam THEN LOOP;
beamFound ← TRUE;
lastBeam ← n.beam;
EXIT; ENDLOOP;
IF NOT beamFound THEN WriteError[sync, i, j, "non-existant beam"];
ENDLOOP};
ENDCASE;
ENDLOOP;
FOR i: CARDINAL IN [0..score.chordHeap.max) DO
IF score.chordHeap.chord[i] # NIL AND i >= score.chordHeap.length THEN
WriteError[chord, i, -1, "beyond score.chordHeapLength"];
IF score.chordHeap.chord[i] = NIL AND i < score.chordHeap.length THEN
WriteError[chord, i, -1, " = NIL"];
IF score.chordHeap.chord[i] = NIL THEN LOOP;
IF score.chordHeap.chord[i].length = 0 THEN WriteError[chord, i, -1, "empty"];
s ← NIL;
FOR j: CARDINAL IN [0..score.chordHeap.length) DO
chord: ChordPTR = score.chordHeap.chord[i];
n ← chord.note[j];
IF n # NIL AND j >= chord.length THEN WriteError[chord, i, j, "beyond chordLength"];
IF n = NIL AND j < chord.length THEN WriteError[chord, i, j, " = NIL"];
IF n = NIL THEN LOOP;
IF s = NIL THEN s ← n.sync;
IF s # n.sync THEN WriteError[chord, i, j, "wrong sync"];
ENDLOOP;
ENDLOOP;
FOR i: CARDINAL IN [0..score.beamHeap.length) DO
IF score.beamHeap.beam[i] # NIL AND i >= score.beamHeap.length THEN
WriteError[beam, i, -1, "beyond score.beamHeapLength"];
IF score.beamHeap.beam[i] = NIL AND i < score.beamHeap.length THEN
WriteError[beam, i, -1, " = NIL"];
IF score.beamHeap.beam[i] = NIL THEN LOOP;
IF score.beamHeap.beam[i].length = 0 THEN WriteError[beam, i, -1, "empty"];
TestBeam[score, i];
sync1 ← Event.GetScoreIndex[score, score.beamHeap.beam[i].sync1] # score.length;
sync2 ← Event.GetScoreIndex[score, score.beamHeap.beam[i].sync2] # score.length;
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];
};
TestBeam: PROC[score: ScorePTR, i: CARDINAL] = {
beam: BeamPTR ← score.beamHeap.beam[i];
sync1, sync2: BOOLFALSE;
FOR j: CARDINAL IN [0..beam.max) DO
IF beam.chord[j] # endOfBeam AND j >= beam.length THEN
WriteError[beam, i, j, "beyond beamLength"];
IF beam.chord[j] = endOfBeam AND j < beam.length THEN
WriteError[beam, i, j, " = NIL"];
IF beam.chord[j] = endOfBeam THEN LOOP;
WITH beam.chord[j] SELECT FROM
n: NotePTR =>  {
IF n.beam # beam THEN WriteError[beam, i, j, "wrong beam"];
IF n.sync = beam.sync1 THEN sync1 ← TRUE;
IF n.sync = beam.sync2 THEN sync2 ← TRUE;
};
c: ChordPTR => {
IF c.note[0].sync = beam.sync1 THEN sync1 ← TRUE;
IF c.note[0].sync = beam.sync2 THEN sync2 ← TRUE;
FOR k: CARDINAL IN [0..c.length) DO
IF c.note[k] = NIL THEN LOOP;
IF c.note[k].beam # beam THEN WriteError[beam, i, j, "wrong beam"];
ENDLOOP;
};
b: BeamPTR =>  {
IF b.beam # beam THEN WriteError[beam, i, j, "wrong beam"];
IF b.sync1 = beam.sync1 THEN sync1 ← TRUE;
IF b.sync2 = beam.sync2 THEN sync2 ← TRUE;
};
ENDCASE;
ENDLOOP;
IF NOT sync1 THEN WriteError[beam, i, -1, "bad sync1"];
IF NOT sync2 THEN WriteError[beam, i, -1, "bad sync2"];
};
WriteError: PROC[t: Type, i, j: INTEGER, s: Rope.ROPE] = {
SELECT t FROM
sync => {
WriteString["sync"];
WriteNumber[i, [10, FALSE, TRUE, 4]];
IF j > -1 THEN
{
WriteString[", event"];
WriteNumber[j, [10, FALSE, TRUE, 2]];
};
};
chord => {
WriteString["chord"];
WriteNumber[i, [10, FALSE, TRUE, 4]];
IF j > -1 THEN
{
WriteString[", note"];
WriteNumber[j, [10, FALSE, TRUE, 2]];
};
};
beam => {
WriteString["beam"];
WriteNumber[i, [10, FALSE, TRUE, 4]];
IF j > -1 THEN
{
WriteString[", chord"];
WriteNumber[j, [10, FALSE, TRUE, 2]];
};
};
ENDCASE;
dataError ← TRUE;
WriteString[": "];
WriteLine[s];
};
Type: TYPE = {sync, chord, beam, none};
dataError: BOOLFALSE;
CleanUp: PROC[score: ScorePTR] = {
FOR i: CARDINAL IN [0..score.length) DO
IF score.event[i].type # sync THEN LOOP;
IF Event.Sync[score.event[i]].length = 0 THEN score.event[i] ← NIL;
ENDLOOP;
FOR i: CARDINAL IN [0..score.chordHeap.length) DO
IF score.chordHeap.chord[i].length = 0
THEN Chord.Free[score, score.chordHeap.chord[i]];
ENDLOOP;
FOR i: CARDINAL IN [0..score.beamHeap.length) DO
IF score.beamHeap.beam[i].length = 0
THEN Beam.Free[score, score.beamHeap.beam[i]];
ENDLOOP;
};
**********************************************************
interface to Cedar for output
**********************************************************
log: IO.STREAMNIL;
NumberFormat: TYPE = RECORD[base: INTEGER, x, y: BOOL, width: INTEGER];
CreateLog: PROC ~ {
log ← ViewerIO.CreateViewerStreams[name: "Mockingbird", backingFile: "Mockingbird.log"].out;
};
WriteLine: PROC[s: Rope.ROPE] = {IF log#NIL THEN { log.PutRope[s]; log.PutChar['\n] }};
WriteString: PROC[s: Rope.ROPE] = {IF log#NIL THEN log.PutRope[s]};
WriteNumber: PROC[n: INT, format: NumberFormat] = { IF log#NIL THEN log.Put[IO.int[n]] };
END.
CleanUpSheets: PROC =
{
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.FreeEvent[@score[i]]; EXIT;
ENDLOOP;
ENDLOOP;
};
**********************************************************
printing the score
**********************************************************
print: PUBLIC BOOLFALSE;
olddc: Graphics.Context;
printChar: BOOLFALSE;
DeviceObject: PUBLIC TYPE = Device.Object; -- exported to OpaqueDevice
device: Device.Handle;
OpenPressDevice: PUBLIC PROC[splines: BOOL] RETURNS[Device.Handle] =
{
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];
};
ClosePressDevice: PUBLIC PROC[device: POINTER TO Device.Handle] =
{
tncp: BcplFontFileDefs.SplineCommandPtr;
Graphics.FreeContext[@context];
Device.Free[device];
IF NOT printChar THEN {
BcplFontFileDefs.CloseSDFontFile[];
UNTIL ncp = NIL DO
tncp ← ncp.next;
SystemDefs.FreeHeapNode[ncp];
ncp ← tncp;
ENDLOOP;
};
context ← olddc;
print ← FALSE;
};
****************************************************************************
graphics procedures
****************************************************************************
SetFont: PUBLIC PROC[dc: Context, font: FontRef, size: INTEGER] =
{
OPEN PressDeviceImpl;
l: REAL ← 1;
Graphics.SetFont[dc, font, size];
IF NOT print THEN {Graphics.SetFont[dc, font, size]; RETURN};
IF printChar THEN {
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];
}
ELSE {
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]];
};
};
DrawCubic: PUBLIC PROC[x1, y1, x2, y2, height: INTEGER] =
{
b1, b2: Cubic.Bezier;
c1, c2: Cubic.Coeffs;
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];
};
DrawString: PUBLIC PROC[dc: Context, s: STRING] =
{
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};
};
DrawChar: PUBLIC PROC[dc: Graphics.Context, c: CHARACTER] =
{
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 { DrawChar[dc, c]; RETURN; };
IF printChar THEN {
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]];
}
ELSE {
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]];
};
};
ndp: BcplFontFileDefs.SplineDataPtr ← NIL;
ncp: BcplFontFileDefs.SplineCommandPtr ← NIL;
DoSDChar: PROC[char: CHARACTER,
Move: PROC[v: POINTER TO Vec],
Draw: PROC[v: POINTER TO Vec],
DCubic: PROC[c: POINTER TO Cubic.Coeffs]] =
{
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;
};