<> <> <> <> <> 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; <<****************************************************************************>> <> <<****************************************************************************>> 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] }; <> 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]; }; <<**********************************************************>> <> <<**********************************************************>> print: PUBLIC BOOL _ FALSE; printChar: BOOL _ FALSE; <<****************************************************************************>> <> <<****************************************************************************>> 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; < score[i].time THEN>> <> 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: BOOL _ FALSE; 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: BOOL _ FALSE; 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; }; <<**********************************************************>> <> <<**********************************************************>> log: IO.STREAM _ NIL; 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; }; <<**********************************************************>> <> <<**********************************************************>> print: PUBLIC BOOL _ FALSE; olddc: Graphics.Context; printChar: BOOL _ FALSE; 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; }; <<****************************************************************************>> <> <<****************************************************************************>> 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; };