<> <> <> <> <> DIRECTORY Event USING [GetOctava, GetScoreIndex, Octava], MusicDefs USING [CacheRec, EventPTR, EventRec, NotePTR, ScorePTR, ScoreRec, SetDirty, SheetPTR, StavesPTR, StylePTR, StyleRec, SyncPTR, Time], Note USING [Delta, Free, InVoice], Piece USING [], Score USING [GetKey, ShowPitch], Sheet USING [FindLine, Height, NearestTime, New, NextLine]; PieceImplA: CEDAR PROGRAM IMPORTS Event, MusicDefs, Note, Score, Sheet EXPORTS Piece = BEGIN OPEN MusicDefs; dataStructureInFlux: PUBLIC BOOLEAN _ FALSE; <<****************************************************************************>> <> <> <> <> <> <<****************************************************************************>> AddEvent: PUBLIC PROC[score: ScorePTR, event: EventPTR] = { <> IF event = NIL THEN RETURN; <> <> dataStructureInFlux _ TRUE; FOR i: NAT DECREASING IN[0..score.length) DO IF score.event[i] = event THEN ERROR; IF score.event[i].time < event.time THEN { score.event[i+1] _ event; event _ NIL; EXIT }; IF score.event[i].time = event.time AND ~LessThan[event, score.event[i]] THEN {score.event[i+1] _ event; event _ NIL; EXIT}; score.event[i+1] _ score.event[i]; ENDLOOP; IF event#NIL THEN score.event[0] _ event; score.length _ score.length + 1; dataStructureInFlux _ FALSE; }; LessThan: PROC[a, b: EventPTR] RETURNS[BOOLEAN] = { IsStyle: PROC[e: EventPTR] RETURNS[BOOL] ~ { WITH e SELECT FROM e: StavesPTR => RETURN[e.staves=style]; ENDCASE => RETURN[FALSE]; }; SELECT TRUE FROM a.type = staves AND IsStyle[a] => RETURN[TRUE]; b.type = staves AND IsStyle[b] => RETURN[FALSE]; a.type = keySignature => RETURN[TRUE]; b.type = keySignature => RETURN[FALSE]; a.type = measure => RETURN[TRUE]; b.type = measure => RETURN[FALSE]; a.type = metronome => RETURN[TRUE]; b.type = metronome => RETURN[FALSE]; a.type = timeSignature => RETURN[TRUE]; b.type = timeSignature => RETURN[FALSE]; a.type = staves => RETURN[TRUE]; b.type = staves => RETURN[FALSE]; ENDCASE => RETURN[TRUE]; }; RemoveEvent: PUBLIC PROC[score: ScorePTR, event: EventPTR, free: BOOLEAN _ FALSE] = { length: CARDINAL _ 0; octava: EventPTR _ NIL; IF Event.Octava[event] AND free THEN octava _ Event.GetOctava[score, NARROW[event]]; FOR i: CARDINAL IN [0..score.length) DO IF score.event[i] = event THEN LOOP; IF i # length THEN score.event[length] _ score.event[i]; length _ length+1; ENDLOOP; WITH event SELECT FROM sync: SyncPTR => FOR i: NAT IN[0..sync.length) DO sync.note[i].sync _ NIL ENDLOOP; ENDCASE; score.length _ length; score.event[score.length] _ NIL; <> IF free AND octava # NIL THEN { SetDirty[score, octava.time, octava.time]; RemoveEvent[score, octava, FALSE]; <> }; }; nullScore: ScorePTR _ NEW[ScoreRec[0]]; New: PUBLIC PROC[length: CARDINAL, auxiliary: BOOL] RETURNS[score: ScorePTR] = { score _ NEW[ScoreRec[length] _ [event: ]]; <> <> <> <> <> <> <> <> <> <> <> IF auxiliary THEN { IF score.cache = NIL THEN score.cache _ NEW[CacheRec]; IF score.sheet = NIL THEN score.sheet _ Sheet.New[100]; IF score.style = NIL THEN score.style _ NewStyle[20]; }; }; NewStyle: PROC[length: NAT] RETURNS[StylePTR] = { style: StylePTR ~ NEW[StyleRec[length]]; <> style[0] _ NEW[EventRec[staves] _ [variant: staves[ height: 0, offset: 60, length: 4, staff: [[72,-32],[48,-88],[27,-136],[3,-196]] ]]]; <> style[1] _ NEW[EventRec[staves] _ [variant: staves[ height: 0, offset: 80, length: 4, staff: [[48,-32],[48,-32],[48,-32],[48,-32]] ]]]; <> style[2] _ NEW[EventRec[staves] _ [variant: staves[ height: 0, offset: 85, length: 4, staff: [[48,-32],[48,-32],[27,-125],[27,-125]] ]]]; style[12] _ NEW[EventRec[staves] _ [variant: staves[ height: 0, offset: 85, length: 4, staff: [[48,-32],[48,-32],[27,-150],[27,-150]] ]]]; <> style[3] _ NEW[EventRec[staves] _ [variant: staves[ height: 0, offset: 75, length: 4, staff: [[48,-32],[48,-32],[27,-110],[27,-185]] ]]]; style[13] _ NEW[EventRec[staves] _ [variant: staves[ height: 0, offset: 75, length: 4, staff: [[48,-32],[48,-32],[27,-130],[27,-220]] ]]]; <> style[4] _ NEW[EventRec[staves] _ [variant: staves[ height: 0, offset: 75, length: 4, staff: [[48,-32],[48,-110],[27,-185],[27,-260]] ]]]; FOR i: NAT IN[0..length) DO IF style[i]=NIL THEN style[i] _ NEW[EventRec[staves] _ [variant: staves[ height: 0, offset: 0, length: 0, staff: [[0, 0],[0, 0],[0, 0],[0, 0]]]]]; ENDLOOP; style.length _ length; RETURN[style]; }; Free: PUBLIC PROC[score: ScorePTR] = { <> <> <> <> <> <> <> <> <> <> <> <> <> <> <> <> <> <> <> <> <> }; <> <<{>> <> <> <> <<};>> <<>> Sort: PUBLIC PROC[score: ScorePTR] = { <> FOR i: CARDINAL IN [0..score.length) DO FOR j: CARDINAL IN (i..score.length) DO a: EventPTR ~ score.event[i]; b: EventPTR ~ score.event[j]; IF a.time > b.time THEN { score.event[i] _ b; score.event[j] _ a } ELSE IF a.time = b.time THEN WITH a SELECT FROM a: SyncPTR => WITH b SELECT FROM b: SyncPTR => { IF a.note[0]=NIL OR b.note[0]=NIL OR a.note[0].toc < b.note[0].toc THEN NULL ELSE { score.event[i] _ b; score.event[j] _ a }; }; ENDCASE; ENDCASE; ENDLOOP; ENDLOOP; }; CleanUpEvents: PUBLIC PROC[score: ScorePTR] = { length: CARDINAL _ 0; FOR i: CARDINAL DECREASING IN [0..score.length) DO WITH score.event[i] SELECT FROM sync: SyncPTR => { IF sync.length=0 THEN score.event[i] _ NIL ELSE { score.event[length] _ score.event[i]; length _ length+1 }; }; ENDCASE; ENDLOOP; FOR i: CARDINAL IN [length..score.length) DO score.event[i] _ NIL; ENDLOOP; score.length _ length; score[score.length] _ NIL; }; CleanUpNotes: PUBLIC PROC[score: ScorePTR] = { FOR i: CARDINAL DECREASING IN [0..score.length) DO WITH score.event[i] SELECT FROM sync: SyncPTR => { FOR j: CARDINAL DECREASING IN [0..sync.length) DO IF sync.note[j].value # unknown THEN LOOP; IF sync.note[j].duration # 0 THEN LOOP; Note.Free[score, sync.note[j]]; -- may remove the sync from the score ENDLOOP; }; ENDCASE; ENDLOOP; CleanUpEvents[score]; }; <<>> <<****************************************************************************>> <> <<****************************************************************************>> NearestEvent: PUBLIC PROC[score: ScorePTR, t: Time, syncsOnly: BOOLEAN _ FALSE] RETURNS[index: CARDINAL] = { delta: Time _ 10000; index _ 0; FOR binary: CARDINAL _ 8192, binary/2 UNTIL binary = 0 DO IF index+binary >= score.length THEN LOOP; IF score.event[index+binary].time <= t THEN index _ index+binary; IF score.event[index].time = t THEN EXIT; ENDLOOP; <> FOR i: CARDINAL IN [(IF index < 5 THEN 0 ELSE index-5)..MIN[index+5, score.length]) DO IF syncsOnly AND score.event[i].type # sync THEN LOOP; IF ABS[score.event[i].time-t] > delta THEN LOOP; index _ i; delta _ ABS[score.event[i].time-t]; ENDLOOP; IF syncsOnly AND index # score.length AND score.event[index].type # sync THEN index _ score.length; }; NearestNote: PUBLIC PROC[score: ScorePTR, x, y: INTEGER] RETURNS[note: NotePTR] = { n: NotePTR; key: INTEGER; sync: SyncPTR; l, next: CARDINAL; time, dt, t: Time _ 11; sheet: SheetPTR _ score.sheet; height, dy, show: INTEGER _ 8; [time, height] _ Sheet.NearestTime[sheet, x, y]; next _ Sheet.FindLine[sheet, time]; time _ time - (sheet.width - sheet.section[next].x); l _ Sheet.FindLine[sheet, time]; height _ height + (sheet.section[next].y - sheet.section[l].y); FOR k: CARDINAL IN [0..2] DO FOR i: CARDINAL IN [0..score.length) DO IF score.event[i].type # sync THEN LOOP; IF score.event[i].time < time - 40 THEN LOOP; IF score.event[i].time > time + 40 THEN EXIT; IF score.event[i].time < sheet.section[l].time THEN LOOP; IF score.event[i].time >= sheet.section[next].time THEN EXIT; sync _ NARROW[score.event[i]]; FOR j: CARDINAL IN [0..sync.length) DO n _ sync.note[j]; IF ~Note.InVoice[n, score.sheet.voice] THEN LOOP; t _ ABS[score[i].time+5+Note.Delta[score.sheet, n]-time]; key _ Score.GetKey[score, n.sync.time]; show _ Score.ShowPitch[score.sheet, n.pitch, n.spelled, key]; y _ ABS[height-Sheet.Height[sheet, n.sync.time, show, n.staff]]; IF y+t > dy+dt THEN LOOP; IF y+t = dy+dt AND t > dt THEN LOOP; dt _ t; dy _ y; note _ n; ENDLOOP; ENDLOOP; time _ time+ (sheet.width - sheet.section[next].x); height _ height- (sheet.section[next].y - sheet.section[l].y); l _ next; next _ Sheet.NextLine[sheet, l]; ENDLOOP; IF dt > 10 OR dy > 7 THEN note _ NIL; }; DeleteEvent: PUBLIC PROC[score: ScorePTR, event: EventPTR] = { octava: StavesPTR _ NIL; index: CARDINAL _ Event.GetScoreIndex[score, event]; IF Event.Octava[event] THEN octava _ Event.GetOctava[score, NARROW[event]]; RemoveEvent[score, event]; IF octava # NIL THEN { SetDirty[score, octava.time, octava.time]; RemoveEvent[score, octava]; <> }; <> }; END. NearestObject: PUBLIC PROC[score: ScorePTR, x, y: INTEGER] RETURNS[obj: ObjectType, p: LONG POINTER _ NIL] = { n: NotePTR; l: CARDINAL; key: INTEGER; staves: StavesPTR; time, dt, t: Time _ 11; s, next: CARDINAL _ 0; sheet: SheetPTR _ score.sheet; height, dy, show: INTEGER _ 8; [time, height] _ Sheet.NearestTime[sheet, x, y]; next _ Sheet.FindLine[sheet, time]; time _ time - (sheet.width - sheet.section[next].x); l _ Sheet.FindLine[sheet, time]; height _ height + (sheet.section[next].y - sheet.section[l].y); FOR k: CARDINAL IN [0..2] DO <> FOR i: CARDINAL IN [0..score.beamHeap.length) DO IF score.beamHeap.beam[i].beam # NIL AND score.beamHeap.beam[i].beam.beamed THEN LOOP; IF score.beamHeap.beam[i].sync1.time-5 > time THEN LOOP; IF score.beamHeap.beam[i].sync2.time+5 < time THEN LOOP; IF ~Beam.InVoice[score.beamHeap.beam[i], score.sheet.voice] THEN LOOP; y _ Beam.Height[sheet, score.beamHeap.beam[i], time]; IF ABS[y-height]+4 > dt+dy THEN LOOP; IF ABS[y-height]+4 = dt+dy THEN IF y > height THEN LOOP; dt _ 4; dy _ ABS[y-height]; IF time < (score.beamHeap.beam[i].sync1.time+score.beamHeap.beam[i].sync2.time+4)/2 THEN obj _ leftBeam ELSE obj _ rightBeam; p _ score.beamHeap.beam[i]; ENDLOOP; <> FOR i: CARDINAL IN [s..score.length) DO IF score.event[i].time < time - 300 THEN LOOP; IF score.event[i].time > time + 300 THEN {s _ i; EXIT}; IF score.event[i].time < sheet.section[l].time THEN LOOP; IF score.event[i].type = sync THEN { -- ties sync: SyncPTR _ Event.Sync[score.event[i]]; FOR j: CARDINAL IN [0..sync.length) DO n _ sync.note[j]; IF ~Note.InVoice[n, score.sheet.voice] THEN LOOP; IF n.tie = NIL THEN LOOP; IF n.sync.time < time OR n.tie.sync.time > time THEN LOOP; t _ ABS[(n.sync.time+n.tie.sync.time)/2-time]; key _ Score.GetKey[score, n.sync.time]; show _ Score.ShowPitch[score.sheet, n.pitch, n.spelled, key]; y _ ABS[height-(Sheet.Height[sheet, n.sync.time, show, n.staff]+n.tieHeight/2)]; IF y+t > dy+dt THEN LOOP; IF y+t = dy+dt THEN IF t > dt THEN LOOP; dt _ t; dy _ y; obj _ tie; p _ n; ENDLOOP}; IF score.event[i].time >= sheet[next].time THEN LOOP; IF ABS[score.event[i].time-time] > 40 THEN LOOP; IF score.event[i].type = staves THEN { octava: StavesPTR _ NIL; staves: StavesPTR _ Event.Staves[score.event[i]]; staff, top, bottom: INTEGER _ staves.value; t _ staves.time; IF staves.staves = clef THEN t _ t+6; IF ABS[t-time]+6 > dt + dy THEN LOOP; IF ABS[t-time]+6 = dt+dy AND t < time THEN LOOP; IF staves.staves = octava2 THEN staves _ Event.GetOctava[score, staves]; <> <> SELECT staves.staff[staff].pitch FROM 60, 15 => {top _ staves.height+5; bottom _ staves.height-5}; ENDCASE => {top _ 32; bottom _ 0}; IF height > Sheet.Height[sheet, score.event[i].time, , staff]+top THEN LOOP; IF height < Sheet.Height[sheet, score.event[i].time, , staff]+bottom THEN LOOP; dt _ ABS[t-time]; dy _ 6; p _ score.event[i]; obj _ measure; LOOP}; IF score.event[i].type # sync THEN { IF ABS[score.event[i].time-time]+6 > dt + dy THEN LOOP; IF ABS[score.event[i].time-time]+6 = dt+dy AND score.event[i].time < time THEN LOOP; IF score.event[i].type = staves AND score.event[i].time < 1 THEN LOOP; -- don't touch the first one staves _ sheet.section[Sheet.FindSection[sheet, score[i].time]].staves; IF height > Sheet.Height[sheet, score.event[i].time, , 0]+32 THEN LOOP; IF height < Sheet.Height[sheet, score.event[i].time, , staves.length-1] THEN LOOP; dt _ ABS[score.event[i].time-time]; dy _ 6; p _ score.event[i]; obj _ measure; LOOP}; IF score.event[i].type = sync THEN { -- notes sync: SyncPTR _ Event.Sync[score.event[i]]; FOR j: CARDINAL IN [0..sync.length) DO IF (n _ sync.note[j]) = NIL THEN EXIT; IF ~Note.InVoice[n, score.sheet.voice] THEN LOOP; t _ ABS[score.event[i].time+5+Note.Delta[score.sheet, n]-time]; y _ ABS[height-Sheet.Height[sheet, n.sync.time, n.pitch, n.staff]]; IF y+t > dy+dt THEN LOOP; IF y+t = dy+dt AND t > dt THEN LOOP; dt _ t; dy _ y; p _ n; obj _ note; ENDLOOP}; ENDLOOP; -- end of this line, go to next time _ time+ (sheet.width - sheet.section[next].x); height _ height- (sheet.section[next].y - sheet.section[l].y); l _ next; next _ Sheet.NextLine[sheet, l]; ENDLOOP; IF dt > 10 OR dy > 7 THEN p _ NIL; };