<> <> <> <> <> DIRECTORY Basics USING [LongMult], Event USING [AddNote, Sync], MusicDefs, Note USING [Duration], Piece USING [AddEvent, CleanUpEvents], Score USING [GetTimeSignature], Selection USING [Enumerate], Sheet USING [HiLite], Voice USING [max, State, StatePTR]; VoiceImpl: CEDAR PROGRAM IMPORTS Basics, Event, MusicDefs, Note, Piece, Score, Selection, Sheet EXPORTS Voice = BEGIN OPEN MusicDefs, Voice; <> <> <> Set: PUBLIC PROC[score: ScorePTR, voice: CARDINAL] = { SetVoice: PROC[score: ScorePTR, n: NotePTR] = {n.voice _ voice}; Selection.Enumerate[SetVoice]; score.maxVoice _ MAX[voice, score.maxVoice]; }; Check: PUBLIC PROC[score: ScorePTR] = { vs: StatePTR ~ NEW[State]; sum, sumTS: Time _ 0; begin, end: CARDINAL _ 0; FOR i: CARDINAL IN [0..score.length) DO WITH score[i] SELECT FROM timeSig: TimeSignaturePTR => { sumTS _ Basics.LongMult[timeSig.ts.top*256, 64/timeSig.ts.bottom] }; ENDCASE; IF ~Measure[score[i]] THEN LOOP; begin _ end; end _ i; IF begin = end THEN LOOP; sum _ Sum[score, begin, end, score.sheet.voice # noVoice, vs]; IF sum = 0 THEN LOOP; IF ABS[sum-sumTS] > 8 THEN Sheet.HiLite[score.sheet, light, score[begin].time, score[end].time]; ENDLOOP; }; <> Sum: PROC[score: ScorePTR, begin, end: CARDINAL, separate: BOOLEAN, ss: StatePTR] RETURNS[sum: Time] = { sum _ 0; ClearState[ss]; FOR i: CARDINAL IN (begin..end) DO IF score[i].type # sync THEN LOOP; [] _ SetState[ss, score[i], 128, separate]; ENDLOOP; [] _ SetState[ss, score[end], 128, separate]; FOR v: CARDINAL IN [0..Voice.max) DO IF separate AND v # score.sheet.voice THEN LOOP; sum _ MAX[sum, ss[v].sum+ss[v].duration]; ENDLOOP; }; <<*****************************************************************>> <> <<*****************************************************************>> ClearState: PUBLIC PROC[vs: StatePTR] ~ { vs^ _ ALL[[FALSE, FALSE, FALSE, FALSE, 0, 0]]; }; <<>> SetState: PUBLIC PROC[vs: StatePTR, event: EventPTR, m: INTEGER, separate: BOOLEAN] RETURNS[max: Time] = { <> <> <> <> n: NotePTR; i: CARDINAL _ 0; duration, offset: Time; sync: SyncPTR _ NIL; IF event.type = sync THEN sync _ Event.Sync[event]; FOR i: CARDINAL IN [0..10) DO vs[i].found _ vs[i].graced _ FALSE; ENDLOOP; IF sync # NIL THEN FOR i: CARDINAL IN [0..sync.length) DO IF (n _ sync.note[i]) = NIL THEN EXIT; IF n.value = unknown THEN LOOP; IF vs[n.voice].grace THEN vs[n.voice].graced _ TRUE; IF n.grace THEN { vs[n.voice].found _ TRUE; vs[n.voice].sum _ vs[n.voice].sum+ vs[n.voice].duration-10; vs[n.voice].duration _ 10; LOOP}; duration _ Note.Duration[n, m]; IF vs[n.voice].found = FALSE THEN { vs[n.voice].sum _ vs[n.voice].sum+ vs[n.voice].duration; vs[n.voice].duration _ duration; vs[n.voice].found _ TRUE; IF n.rest AND n.value = whole THEN { vs[n.voice].sum _ vs[n.voice].sum+ vs[n.voice].duration/2; vs[n.voice].duration _ vs[n.voice].duration/2}}; IF vs[n.voice].duration > duration THEN vs[n.voice].duration _ duration; ENDLOOP; <<"grace" is set separately to avoid having a second grace note appear to be "graced" by the first.>> IF sync # NIL THEN FOR i: CARDINAL IN [0..Voice.max) DO IF (n _ sync.note[i]) = NIL THEN EXIT; vs[n.voice].grace _ n.grace; ENDLOOP; <> IF Measure[event] THEN FOR i: CARDINAL IN [0..Voice.max) DO vs[i].found _ TRUE; vs[i].sum _ vs[i].sum+vs[i].duration; vs[i].duration _ 0; <> <> <> ENDLOOP; <> max _ 0; FOR i: CARDINAL IN [0..Voice.max) DO IF NOT vs[i].found AND vs[i].duration # 0 THEN offset _ 10 ELSE offset _ 0; max _ MAX[max, vs[i].sum+ offset]; ENDLOOP; IF max = 0 THEN RETURN; <> IF ~separate THEN FOR i: CARDINAL IN [0..Voice.max) DO IF vs[i].found THEN vs[i].sum _ max; ENDLOOP; }; <<*****************************************************************>> <> <<*****************************************************************>> Correct: PUBLIC PROC[score: ScorePTR, time1, time2: Time] = { < IF old = score THEN score _ new;>> begin, end: CARDINAL _ 0; sumTS, sum: Time _ 0; ts: TimeSignature; vs: StatePTR ~ NEW[State]; FOR i: CARDINAL DECREASING IN [0..score.length) DO IF Measure[score[i]] AND begin = 0 THEN {begin _ i; LOOP}; IF NOT Measure[score[i]] THEN LOOP; end _ begin; begin _ i; IF score[begin].time < time1-1 OR score[end].time > time2+1 THEN LOOP; <> end _ SeparateGraceNotes[score, begin, end]; -- may update end sum _ Sum[score, begin, end, FALSE, vs]; ts _ Score.GetTimeSignature[score, score[end].time]; sumTS _ Basics.LongMult[ts.top*256, 64/ts.bottom]; IF sum-sumTS < 9 THEN LOOP; -- no complaints [] _ Sum[score, begin, end, TRUE, vs]; FOR v: CARDINAL IN [0..Voice.max) DO vs[v].full _ ABS[vs[i].sum-sumTS] < 9; ENDLOOP; end _ BreakUpEvents[score, begin, end]; -- may update end ReconstructVoices[score, vs, begin, end]; ENDLOOP; Piece.CleanUpEvents[score]; }; SeparateGraceNotes: PROC[score: ScorePTR, start, stop: CARDINAL] RETURNS[newStop: CARDINAL] = { n: NotePTR; sync, new: SyncPTR; skip: CARDINAL _ 0; grace, normal: BOOLEAN; FOR i: CARDINAL IN [start..score.length) DO IF i = stop THEN EXIT; IF skip > 0 THEN {skip _ skip-1; LOOP}; -- skip the syncs that we added IF score[i].type # sync THEN LOOP; sync _ Event.Sync[score[i]]; <> grace _ normal _ FALSE; FOR j: CARDINAL IN [0..sync.length) DO IF (n _ sync.note[j]) = NIL THEN EXIT; IF n.grace THEN grace _ TRUE ELSE normal _ TRUE; IF NOT (grace AND normal) THEN LOOP; new _ NEW[EventRec[sync][sync.length] _ [variant: sync[note: ]]]; -- separate new.time _ sync.time; FOR k: CARDINAL DECREASING IN [0..sync.length) DO IF (n _ sync.note[k]) = NIL THEN LOOP; IF ~n.grace THEN LOOP; Event.AddNote[score, new, n]; ENDLOOP; Piece.AddEvent[score, new]; stop _ stop+1; skip _ skip+1; EXIT; ENDLOOP; ENDLOOP; RETURN[stop]; }; BreakUpEvents: PROC[score: ScorePTR, start, stop: CARDINAL] RETURNS[newStop: CARDINAL] = { vs: StatePTR ~ NEW[State]; n: NotePTR; sync, new: SyncPTR; skip: CARDINAL _ 0; ClearState[vs]; FOR i: CARDINAL IN (start..score.length) DO IF i = stop THEN EXIT; IF skip > 0 THEN {skip _ skip-1; LOOP}; -- skip the syncs that we added IF score[i].type # sync THEN LOOP; sync _ Event.Sync[score[i]]; <> [] _ SetState[vs, score[i], 128, TRUE]; FOR v1: CARDINAL IN [0..Voice.max) DO IF NOT (vs[v1].full AND vs[v1].found) THEN LOOP; FOR v2: CARDINAL IN (v1..Voice.max) DO IF NOT (vs[v2].full AND vs[v2].found) THEN LOOP; IF ABS[vs[v1].sum-vs[v2].sum] < 9 THEN LOOP; -- same time (close enough) new _ NEW[EventRec[sync][sync.length] _ [variant: sync[note: ]]]; -- separate new.time _ score[i].time; FOR j: CARDINAL DECREASING IN [0..sync.length) DO IF (n _ sync.note[j]) = NIL THEN LOOP; IF n.voice # v1 THEN LOOP; Event.AddNote[score, new, n]; ENDLOOP; Piece.AddEvent[score, new]; stop _ stop +1; skip _ skip+1; ENDLOOP; ENDLOOP; ENDLOOP; RETURN[stop]; }; ReconstructVoices: PROC[score: ScorePTR, vs: StatePTR, start, stop: CARDINAL] = { <> FOR v1: CARDINAL IN [0..Voice.max) DO IF NOT vs[v1].full THEN LOOP; FOR v2: CARDINAL IN (v1..Voice.max) DO IF vs[v2].full THEN AdjustVoices[score, v1, v2, start, stop]; ENDLOOP; ENDLOOP; }; AdjustVoices: PROC[score: ScorePTR, a, b: CARDINAL, start, stop: CARDINAL] = { <> <> A: StatePTR ~ NEW[State]; B: StatePTR ~ NEW[State]; n: NotePTR; time: Time _ 0; temp: EventPTR; as, bs: CARDINAL _ start; getNextA, getNextB: BOOLEAN; ClearState[A]; ClearState[B]; WHILE as # stop AND bs # stop DO getNextA _ A[a].sum <= B[b].sum; getNextB _ B[b].sum <= A[a].sum; IF getNextA THEN FOR i: CARDINAL IN (as..stop+1] DO IF i = stop+1 THEN ERROR; -- catches infinite loops [] _ SetState[A, score[i], 128, TRUE]; IF score[i].type # sync AND i # stop THEN LOOP; IF ~A[a].found THEN LOOP; as _ i; EXIT; ENDLOOP; IF getNextB THEN FOR i: CARDINAL IN (bs..stop+1] DO IF i = stop+1 THEN ERROR; -- catches infinite loops [] _ SetState[B, score[i], 128, TRUE]; IF score[i].type # sync AND i # stop THEN LOOP; IF ~B[b].found THEN LOOP; bs _ i; EXIT; ENDLOOP; IF ABS[A[a].sum-B[b].sum] < 5 THEN A[a].sum _ B[b].sum _ MAX[A[a].sum, B[b].sum]; <> <> IF A[a].sum < B[b].sum AND bs < as THEN { temp _ score[as]; FOR i: CARDINAL DECREASING IN [bs..as) DO score[i+1] _ score[i]; ENDLOOP; score[bs] _ temp; as _ bs; bs _ bs+ 1; }; IF A[a].sum > B[b].sum AND bs > as THEN { temp _ score[bs]; FOR i: CARDINAL DECREASING IN [as..bs) DO score[i+1] _ score[i]; ENDLOOP; score[as] _ temp; bs _ as; as _ as+ 1; }; <> <> IF A[a].sum = B[b].sum AND as # bs THEN IF bs < as THEN FOR j: CARDINAL DECREASING IN [0..Event.Sync[score[as]].length) DO IF (n _ Event.Sync[score[as]].note[j]) = NIL THEN LOOP; IF n.voice # a THEN LOOP; Event.AddNote[score, Event.Sync[score[bs]], n]; <> ENDLOOP ELSE FOR j: CARDINAL DECREASING IN [0..Event.Sync[score[bs]].length) DO IF (n _ Event.Sync[score[bs]].note[j]) = NIL THEN LOOP; IF n.voice # b THEN LOOP; Event.AddNote[score, Event.Sync[score[as]], n]; ENDLOOP; IF A[a].sum = B[b].sum AND bs < as THEN as _ bs; IF A[a].sum = B[b].sum AND as < bs THEN bs _ as; <> <> ENDLOOP; }; END.