<> <> <> <> <> DIRECTORY Beam USING [SetStems], Convert USING [RopeFromInt], Event USING [GetScoreIndex, Invisible, SetStave], Imager USING [ScaleT], MusicDefs, Score, Sheet, Utility; SheetImplA: CEDAR PROGRAM IMPORTS Beam, Convert, Event, Imager, MusicDefs, Score, Sheet, Utility EXPORTS Sheet = BEGIN OPEN MusicDefs, Score, Sheet; FindStaves: PUBLIC PROC[sheet: SheetPTR, t: Time] RETURNS[StavesPTR] = { i: NAT ~ FindSection[sheet, t]; RETURN[sheet.section[i].staves] }; Last: PUBLIC PROC[sheet: SheetPTR, l: CARDINAL] RETURNS[CARDINAL] = { FOR i: CARDINAL IN [l..sheet.length-1) DO IF sheet.section[i].time # sheet.section[i+1].time THEN RETURN[i]; ENDLOOP; RETURN[sheet.length-1]; }; LineNumber: PUBLIC PROC[sheet: SheetPTR, l: CARDINAL] RETURNS[n: CARDINAL _ 1] = { FOR i: CARDINAL IN (0..l] DO IF sheet.section[i].y # sheet.section[i-1].y THEN n _ n+1; ENDLOOP; }; NextLine: PUBLIC PROC[sheet: SheetPTR, l: CARDINAL] RETURNS[CARDINAL] = { FOR i: CARDINAL IN (l..sheet.length) DO IF sheet.section[i].y = sheet.section[l].y THEN LOOP; RETURN[Last[sheet, i]]; ENDLOOP; RETURN[sheet.length-1]; }; NextStaff: PUBLIC PROC[sheet: SheetPTR, s: CARDINAL, t: Time] RETURNS[CARDINAL] = { staves: StavesPTR = FindStaves[sheet, t]; FOR i: CARDINAL IN (s..staves.length) DO IF staves.staff[i].y # staves.staff[s].y THEN RETURN[i]; ENDLOOP; RETURN[s]; }; NormalPitch: PUBLIC PROC[sheet: SheetPTR, staff: CARDINAL] RETURNS[INTEGER] = { RETURN[IF staff IN [0..1] THEN 48 ELSE 27] }; PriorLine: PUBLIC PROC[sheet: SheetPTR, l: CARDINAL] RETURNS[CARDINAL] = { FOR i: CARDINAL DECREASING IN [0..l) DO IF sheet.section[i].y # sheet.section[l].y THEN {l _ i; EXIT}; ENDLOOP; FOR i: CARDINAL DECREASING IN [0..l) DO IF sheet.section[i].y # sheet.section[l].y THEN RETURN[Last[sheet, i+1]]; ENDLOOP; RETURN[Last[sheet, 0]]; }; PriorStaff: PUBLIC PROC[sheet: SheetPTR, s: CARDINAL, t: Time] RETURNS[CARDINAL] = { staves: StavesPTR = FindStaves[sheet, t]; FOR i: CARDINAL DECREASING IN [0..s) DO IF staves.staff[i].y # staves.staff[s].y THEN RETURN[i]; ENDLOOP; RETURN[s]; }; OctavaHeight: PUBLIC PROC[pitch, height: INTEGER] RETURNS[INTEGER] = { IF pitch = 60 AND height < 40 THEN RETURN[40]; IF pitch = 60 AND height > 90 THEN RETURN[90]; IF pitch = 15 AND height < -40 THEN RETURN[-40]; IF pitch = 15 AND height > -8 THEN RETURN[-8]; RETURN[height]; }; <<****************************************************************************>> <> <<****************************************************************************>> New: PUBLIC PROC[length: CARDINAL] RETURNS[sheet: SheetPTR] = { sheet _ NEW[SheetRec[length] _ [section: ]]; sheet.length _ length; }; Free: PUBLIC PROC[sheet: SheetPTR] = { }; Reset: PUBLIC PROC[score: ScorePTR] = { sync: EventPTR _ NIL; staves: StavesPTR _ NIL; page, pitch: INTEGER _ 2; sheet: SheetPTR _ score.sheet; v, j, last, next: NAT _ 0; time, eol, priorEol, eventTime: Time _ 0; top, height, x, sc, priorHeight: INTEGER _ 0; Score.BuildCache[score, 0];-- ??? IF sheet.scale = 2 THEN sc _ 3 ELSE sc _ 2*sheet.scale; [staves, last, next] _ NextStyle[score, NIL, 0, 0, 100]; IF staves = NIL THEN RETURN; FOR i: NAT IN [0..score.length] DO IF i = score.length THEN eventTime _ LONG[sheet.width]*sheet.length ELSE {IF score.event[i].type # staves THEN LOOP; eventTime _ score.event[i].time}; WHILE time < eventTime OR eventTime = eol DO -- fill in all of the sections between IF j = sheet.length THEN EXIT; sheet[j].page _ 0; sheet[j].time _ time; IF j # 0 AND sheet[j].time = sheet[j-1].time THEN sheet[j].key _ sheet[j-1].key ELSE sheet[j].key _ Key[score, time, eol]; IF time < eol THEN x _ x+(time-sheet[j-1].time); IF time >= eol THEN { -- new line height _ height-priorHeight; x _ MAX[8, ABS[8*(sheet[j].key)]]; priorEol _ eol; eol _ priorEol+sheet.width-x; [staves, last, next] _ NextStyle[score, staves, last, next, eol-20]; priorHeight _ -staves.staff[staves.length-1].y+staves.offset; IF top-height > (650*sc)/2 THEN { -- new page top _ height; sheet[j].page _ page; page _ page+1}}; sheet[j].x _ x; sheet[j].y _ height; sheet[j].staves _ staves; time _ MIN[eol, eventTime]; j _ j + 1; ENDLOOP; IF i = score.length THEN EXIT; WITH score.event[i] SELECT FROM event: StavesPTR => staves _ event; ENDCASE => ERROR; IF staves.staves = style THEN LOOP; Event.SetStave[score, staves, staves]; IF staves.staves # clef THEN LOOP; IF Event.Invisible[score, i, priorEol] THEN time _ priorEol; ENDLOOP; IF sheet.scale = 2 THEN Paginate[sheet]; Sheet.SetBegin[sheet, sheet.begin]; Score.BuildCache[score, sheet.begin]; FOR i: NAT IN [0..score.beamHeap.length) DO IF score.beamHeap.beam[i].sync2.time < score.sheet.dirty1 THEN LOOP; IF score.beamHeap.beam[i].sync1.time > score.sheet.dirty2 THEN LOOP; Beam.SetStems[sheet, score.beamHeap.beam[i]]; ENDLOOP; }; NextStyle: PROC[score: ScorePTR, old: StavesPTR, last, next: NAT, eol: Time] RETURNS[new: StavesPTR, newLast, newNext: NAT] = { new _ old; newLast _ last; newNext _ next; FOR i: NAT IN[next..score.length) DO event: EventPTR ~ score.event[i]; IF event.time >= eol+40 THEN EXIT; WITH event SELECT FROM temp: StavesPTR => IF temp.staves=style THEN { newNext _ i; IF event.time >= eol THEN EXIT; IF temp = old THEN LOOP; IF new#NIL AND i#next AND new.staff[new.length-1].y < temp.staff[temp.length-1].y THEN LOOP; new _ temp; newLast _ i; }; ENDCASE; ENDLOOP; IF newLast=last AND old#NIL THEN RETURN[old, newLast, newNext]; Event.SetStave[score, old, new]; RETURN[new, newLast, newNext]; }; Key: PROC[score: ScorePTR, time, eol: Time] RETURNS[key: INTEGER _ 0] = { keySig: KeySignaturePTR _ NIL; key _ Score.GetKey[score, time]; FOR i: NAT IN [Event.GetScoreIndex[score, score.cache.key1]..score.length) DO event: EventPTR ~ score.event[i]; IF event.time >= time+score.sheet.width THEN EXIT; WITH event SELECT FROM k: KeySignaturePTR => { keySig _ k; IF time = eol THEN EXIT }; ENDCASE; ENDLOOP; IF keySig = NIL THEN RETURN; IF ~Event.Invisible[score, Event.GetScoreIndex[score, keySig], eol] THEN RETURN; RETURN[keySig.key]; }; limit: INTEGER _ 1050; Paginate: PROC[sheet: SheetPTR] = { min: INTEGER = 75; start: INTEGER = 40; page: NAT _ 2; height: INTEGER _ -start; line, last: NAT _ 1; count, first: NAT _ 0; FOR line _ 0, Sheet.NextLine[sheet, line] WHILE line # last DO height _ height-sheet[line].staves.staff[3].y+min; count _ count+1; IF height < limit THEN {last _ line; LOOP}; IF count # 1 THEN {-- subtract out the last line count _ count-1; height _ height+sheet[line].staves.staff[3].y-min}; <> LayoutPage[sheet, count, first, line, limit+count*min-start-height]; sheet[line].page _ page; page _ page+1; <> first _ line; line _ last; count _ 0; height _ -start; ENDLOOP; }; LayoutPage: PROC[sheet: SheetPTR, lines, first, last, space: NAT] = { oldHeight, height: INTEGER _ sheet[first].y; <> FOR i: NAT IN (first..sheet.length) DO IF sheet[i].y # oldHeight THEN {-- new line IF i > last THEN EXIT; oldHeight _ sheet[i].y; height _ sheet[i-1].y+sheet[i-1].staves.staff[3].y; height _ height-space/lines}; sheet[i].y _ height; sheet[i].page _ 0; ENDLOOP; }; FindLine: PUBLIC PROC[sheet: SheetPTR, t: Time] RETURNS[l: INTEGER] = { start: NAT _ 0; FOR i: NAT IN [0..sheet.length-1) DO IF sheet[i].time # sheet[i+1].time THEN {start _ i; EXIT}; ENDLOOP; l _ start; FOR i: NAT _ start, NextLine[sheet, i] DO IF sheet[i].time <= t THEN l _ i ELSE RETURN; IF i = sheet.length-1 THEN RETURN; ENDLOOP; }; FindSection: PUBLIC PROC[sheet: SheetPTR, t: Time] RETURNS[INTEGER] = { IF t >= sheet[sheet.current].time THEN { IF sheet.current = sheet.length-1 THEN RETURN[sheet.current]; WHILE t >= sheet[sheet.current+1].time DO sheet.current _ sheet.current+1; IF sheet.current = sheet.length-1 THEN RETURN[sheet.current]; ENDLOOP; RETURN[sheet.current]; } ELSE { WHILE sheet.current # 0 AND t < sheet[sheet.current].time DO sheet.current _ sheet.current -1; ENDLOOP; RETURN[sheet.current]; }; }; <<>> <<****************************************************************************>> <> <<****************************************************************************>> white: CARDINAL ~ 0; HiLite: PUBLIC PROC[sheet: SheetPTR, texture: ColorType, t1, t2: Time] = { x1, y1, x2, y2, lastY: INTEGER; offset: INTEGER _ IF texture = white THEN 58 ELSE 40; section1, section2, i, j: CARDINAL; IF t1 >= t2 THEN RETURN; <> Utility.SetBrush[sheet.context, texture, IF texture = white THEN opaque ELSE invert]; i _ Sheet.FindLine[sheet, t1]; j _ Sheet.PriorLine[sheet, i]; IF i = j THEN lastY _ 100 ELSE lastY _ sheet[j].y; j _ section1 _ FindSection[sheet, t1]; section2 _ FindSection[sheet, t2]; FOR i IN [section1..section2] DO IF sheet[i].time > sheet.endTime THEN EXIT; IF i # section2 AND sheet[i].time = sheet[i+1].time THEN LOOP; IF texture # white AND i # section2 AND sheet[i].y = sheet[i+1].y THEN LOOP; offset _ sheet[i].staves.offset; IF texture = white THEN offset _ offset/2 ELSE offset _ offset/4; [x1, y1] _ Sheet.Map[sheet, MAX[t1, sheet[j].time], , 0]; [x2, y2] _ Sheet.Map[sheet, MIN[t2, sheet[i+1].time-1], , sheet[i].staves.length-1]; y1 _ y1+offset+34; y2 _ y2-offset; IF x2 < sheet.width-1 OR texture = white THEN x2 _ x2+1 ELSE x2 _ x2+16; IF texture = white AND sheet[i].y # lastY AND x1 = sheet[i].x THEN x1 _ -25; lastY _ sheet[i].y; IF texture = white THEN Utility.SetBrush[sheet.context, white, opaque]; Utility.DrawBox[sheet.context, [x1, y1, x2, y2]]; IF texture = white THEN DrawSection[sheet, sheet[i], x1, x2]; j _ i+1; ENDLOOP; <> <<[] _ Graphics.SetPaintMode[sheet.context, transparent];>> }; Draw: PUBLIC PROC[sheet: SheetPTR] = { x: INTEGER; j: CARDINAL; staves: StavesPTR = sheet[0].staves; sheetHeight: INTEGER = staves.staff[staves.length-1].y; IF sheet.begin = 0 THEN Utility.DrawLine[sheet.context, -23, 0, -23, sheetHeight]; sheet.endTime _ sheet.begin; IF sheet.scale = 2 THEN x _ 3 ELSE x _ 2*sheet.scale; FOR j _ Sheet.FindLine[sheet, sheet.begin], Sheet.NextLine[sheet, j] DO IF sheet[j].y-sheet.top < (-650*x)/2 THEN {sheet.endTime _ sheet[j].time; EXIT}; DrawSection[sheet, sheet[j], -25, sheet.width]; ENDLOOP; }; DrawSection: PROC[sheet: SheetPTR, l: Section, start, length: INTEGER] = { sheetHeight: INTEGER _ l.staves.staff[l.staves.length-1].y; i: CARDINAL; y: INTEGER _ l.y-sheet.top; context: Utility.Context ~ sheet.context; oldStaff _ [0, 0]; Utility.SetColor[context, black]; IF start = -25 THEN Utility.DrawLine[context, start, y, start, y+sheetHeight]; IF start = -25 AND sheet.accidental THEN Sheet.DrawKey[sheet, l.key, 0, l.time]; IF l.page > 1 THEN { Utility.SetCP[context, sheet.width+10, y+30]; Utility.SetFont[context, text, 12]; Utility.DrawString[context, Convert.RopeFromInt[l.page]]; Utility.SetFont[context, music, 8]; }; IF sheet.scale > 3 THEN { <> IF length = sheet.width THEN Utility.DrawLine[context, length, y, length, y+sheetHeight]; IF NOT sheet.notehead THEN Utility.SetColor[context, grey]; Utility.DrawLine[context, start, y, length, y]; Utility.DrawLine[context, start, y+sheetHeight, length, y+sheetHeight]; Utility.SetColor[context, black]; <> } ELSE FOR i IN [0..l.staves.length) DO DrawStaff[sheet, l.staves.staff[i], y, start, length] ENDLOOP; }; DrawKey: PUBLIC PROC[sheet: SheetPTR, key, oldKey: INTEGER, time: Time] = { n: INTEGER = 8; oldStaff: Staff _ [0, 0]; x, y, offset: INTEGER; context: Utility.Context _ sheet.context; l: CARDINAL _ Sheet.FindLine[sheet, time]; staves: StavesPTR _ sheet[l].staves; IF NOT sheet.accidental THEN RETURN; IF time IN [sheet[l].time-10..sheet[l].time) THEN RETURN; IF time IN (sheet[l].time..sheet[l].time+15] THEN time _ sheet[l].time; x _ Sheet.MapHeight[sheet, time, 0].x; Utility.SetFont[context, music, 8]; FOR i: NAT IN [0..staves.length) DO IF staves.staff[i] = oldStaff THEN LOOP; oldStaff _ staves.staff[i]; offset _ Sheet.Height[sheet, time, oldStaff.pitch, i]; IF Mod[staves.staff[i].pitch, 12] = 3 THEN offset _ offset - 8; [x, y] _ Sheet.MapHeight[sheet, time, offset]; IF sheet[l].time = time THEN x _ 3; IF key > 0 THEN SlapDown[context, x , 32+y, sharp]; IF key > 1 THEN SlapDown[context, x+ n, 20+y, sharp]; IF key > 2 THEN SlapDown[context, x+2*n, 36+y, sharp]; IF key > 3 THEN SlapDown[context, x+3*n, 24+y, sharp]; IF key > 4 THEN SlapDown[context, x+4*n, 12+y, sharp]; IF key > 5 THEN SlapDown[context, x+5*n, 28+y, sharp]; IF key > 6 THEN SlapDown[context, x+6*n, 16+y, sharp]; IF key < 0 THEN SlapDown[context, x , 16+y, flat]; IF key < -1 THEN SlapDown[context, x+ n, 28+y, flat]; IF key < -2 THEN SlapDown[context, x+2*n, 12+y, flat]; IF key < -3 THEN SlapDown[context, x+3*n, 24+y, flat]; IF key < -4 THEN SlapDown[context, x+4*n, 8+y, flat]; IF key < -5 THEN SlapDown[context, x+5*n, 20+y, flat]; IF key < -6 THEN SlapDown[context, x+6*n, 4+y, flat]; ENDLOOP; }; SlapDown: PROC[context: Utility.Context, x, y: INTEGER, a: Accidental] = { Utility.SetCP[context, x, y]; SELECT a FROM flat => Utility.DrawChar[context, 111C]; natural => Utility.DrawChar[context, 112C]; sharp => Utility.DrawChar[context, 113C]; ENDCASE; }; DrawStaff: PROC[sheet: SheetPTR, s: Staff, y, start, length: INTEGER] = { w: REAL _ 1; staffY: INTEGER; context: Utility.Context _ sheet.context; IF s = oldStaff THEN RETURN; oldStaff _ s; y _ y + s.y; Utility.SetColor[context, IF sheet.notehead THEN black ELSE grey]; IF sheet.printing THEN w _ w/2; staffY _ y; FOR i: NAT IN [0..5) DO Utility.DrawBox[context, [start, staffY-w, length, staffY+w]]; staffY _ staffY + 8; ENDLOOP; Utility.SetColor[context, black]; <> IF (s.pitch = 27 OR s.pitch = 15) AND start < 0 THEN { Utility.SetCP[context, -21, y+16]; Utility.DrawChar[context, 121C]}; IF (s.pitch = 48 OR s.pitch = 60) AND start < 0 THEN { Utility.SetCP[context, -21, y+16]; Utility.DrawChar[context, 120C]}; <> }; oldStaff: Staff; d: CARDINAL = 8; DrawClef: PUBLIC PROC[sheet: SheetPTR, pitch, staff: INTEGER, time: Time] = { x, y: INTEGER; l: CARDINAL _ Sheet.FindLine[sheet, time]; IF time IN (sheet.section[l].time..sheet.section[l].time+15] THEN time _ sheet.section[l].time; [x, y] _ Sheet.Map[sheet, time, , staff]; IF sheet.section[l].time = time THEN x _ -21; Utility.SetCP[sheet.context, x, y+16]; IF sheet.voice # noVoice THEN Utility.SetColor[sheet.context, light]; SELECT TRUE FROM x = -21 AND pitch = 27 => Utility.DrawChar[sheet.context, 121C]; x = -21 AND pitch = 48 => Utility.DrawChar[sheet.context, 120C]; pitch = 27 => Utility.DrawChar[sheet.context, 131C]; pitch = 48 => Utility.DrawChar[sheet.context, 130C]; ENDCASE; }; DrawOctava: PUBLIC PROC[sheet: SheetPTR, pitch, staff, height: INTEGER, t1, t2: Time] = { x1, x2, y: INTEGER _ 0; section1, section2: CARDINAL; IF t1 > t2 THEN RETURN; IF pitch # 60 AND pitch # 15 THEN RETURN; height _ Sheet.OctavaHeight[pitch, height]; Utility.SetFont[sheet.context, text, 12]; section1 _ Sheet.FindSection[sheet, t1]; section2 _ Sheet.FindSection[sheet, t2]; IF sheet.voice # noVoice THEN Utility.SetColor[sheet.context, light]; FOR i: CARDINAL IN [section1..section2] DO IF i+1 = sheet.length THEN EXIT; IF sheet.section[i].time < sheet.begin THEN LOOP; IF sheet.section[i].time >= sheet.endTime THEN EXIT; IF sheet.section[i].time = sheet.section[i+1].time THEN LOOP; [x1, ] _ Sheet.Map[sheet, MAX[t1, sheet.section[i].time], , staff]; [x2, y] _ Sheet.Map[sheet, MIN[t2, sheet.section[i+1].time-1], , staff]; IF x1 > sheet.width-15 AND i = section1 THEN LOOP; x2 _ x2+1; y _ y+height; IF x1 < 10 OR i = section1 THEN { Utility.SetCP[sheet.context, x1, y-8]; Utility.DrawChar[sheet.context, '8]; x1 _ x1+7; }; IF x1 < x2 THEN DrawDottedLine[sheet, x1, y, x2, y]; IF i = section2 THEN Utility.DrawLine[sheet.context, x2, y, x2, y-(IF height > 0 THEN 7 ELSE -7)]; ENDLOOP; Utility.SetFont[sheet.context, music, 8]; }; DrawDottedLine: PROC[sheet: SheetPTR, x1, y, x2, y2: INTEGER] = { IF sheet.printing THEN { FOR x: INTEGER _ x1, x+5 WHILE x < x2 DO Utility.DrawLine[sheet.context, x, y, MIN[x+3, x2], y]; ENDLOOP; } ELSE { <> Utility.DrawLine[sheet.context, x1, y, x2, y]; <> }; }; <<****************************************************************************>> <> <<****************************************************************************>> <> SetBegin: PUBLIC PROC[sheet: SheetPTR, now: Time] = { l: Section _ sheet[Sheet.FindLine[sheet, now]]; sheet.begin _ l.time; sheet.top _ l.y; }; Scroll: PUBLIC PROC[sheet: SheetPTR, lines: INTEGER] = { section: CARDINAL _ Sheet.FindLine[sheet, sheet.begin]; IF lines < 0 THEN FOR i: CARDINAL IN [0..ABS[lines]) DO IF section = 0 THEN EXIT; section _ Sheet.PriorLine[sheet, section]; ENDLOOP ELSE FOR i: CARDINAL IN [0..ABS[lines]) DO section _ Sheet.NextLine[sheet, section]; IF section = sheet.length THEN RETURN; ENDLOOP; Sheet.SetBegin[sheet, sheet[section].time]; sheet.dirty1 _ 0; sheet.dirty2 _ LAST[Time]; }; Scale: PUBLIC PROC[score: ScorePTR, newScale: INTEGER] = { one: REAL = 1; two: REAL = 2; sheet: SheetPTR _ score.sheet; context: Utility.Context _ sheet.context; IF sheet.scale = newScale THEN RETURN; <> SELECT sheet.scale FROM 2 => Imager.ScaleT[context, 3/two]; 4 => Imager.ScaleT[context, 4]; ENDCASE; sheet.width _ 550; <> SELECT newScale FROM 1 => {sheet.scale _ 1; sheet.width _ sheet.width}; <> 2 => {sheet.scale _ 2; -- actually 3/2 sheet.width _ (3*sheet.width)/2; Imager.ScaleT[context, two/3]}; <> 4 => {sheet.scale _ 4; sheet.width _ 4*sheet.width; Imager.ScaleT[context, one/4]}; <> ENDCASE; Sheet.Reset[score]; <> sheet.dirty1 _ 0; sheet.dirty2 _ LAST[Time]; }; END.