PieceImplB.mesa
Copyright (C) 1983, 1984 Xerox Corporation. All rights reserved.
Author: John Maxwell
Last Edited by: Maxwell, November 22, 1983 1:53 pm
Last Edited by: Doug Wyatt, June 12, 1984 1:33:14 pm PDT
DIRECTORY
Chord USING [Free],
Event USING [AddNote, AddTimes, Free],
MusicDefs,
Note USING [Free, InVoice],
Piece USING [AddEvent, CleanUpEvents, Free, NearestEvent],
Real USING [Fix],
Score USING [GetKey, GetStyle],
Sheet USING [FindSection, Reset],
Selection USING [AddLine, selection],
Voice USING [Correct];
PieceImplB: CEDAR PROGRAM
IMPORTS Chord, Event, MusicDefs, Note, Piece, Real, Score, Sheet, Selection, Voice
EXPORTS Piece
= BEGIN OPEN MusicDefs;
****************************************************************************
section manipulation
****************************************************************************
Merge: PUBLIC PROC[score, new: ScorePTR, begin, end: Time] = {
ENABLE Piece.Overflow => IF score = old THEN score ← new;
l: REAL = 1;
scale: REAL;
sBegin: Time;
sync: BOOL;
pBegin, pEnd: Time;
pmLast, smLast: Time ← 0;
pmFirst, smFirst: Time ← 0;
tolerance: CARDINAL = 10;
start, stop: CARDINAL ← 0;
pStop, sStop: CARDINAL ← 0;
pStart, sStart: CARDINAL ← 0;
measuresP, measuresS: INTEGER ← -1;
length: CARDINAL = new.length;
SetDirty[score, begin, end];
IF length = 0 THEN RETURN;
IF score.sheet.display = physical THEN {
MergeSection[score, new, begin, 0, length];
Piece.Free[new];
RETURN};
where does the score start and stop?
[start, stop] ← SelectMeasures[score, begin, end];
IF start # stop THEN begin ← score.event[start].time;
IF start # stop THEN end ← score.event[MIN[stop, score.length-1]].time+1;
Selection.AddLine[score, begin, end];
how are the measures in new distributed?
sync ← FALSE;
FOR i: CARDINAL IN [0..length) DO
IF new.event[i].type = sync THEN sync ← TRUE;
IF NOT Measure[new.event[i]] THEN LOOP;
IF measuresP = -1 THEN {
pmFirst ← new.event[i].time-new.event[0].time;
IF sync THEN pmFirst ← MAX[pmFirst, tolerance+1]};
sync ← FALSE;
pmLast ← new.event[length-1].time-new.event[i].time;
measuresP ← measuresP + 1;
ENDLOOP;
IF sync THEN pmLast ← MAX[pmLast, tolerance+1];
how are the measures in the score distributed?
sync ← FALSE;
FOR i: CARDINAL IN [start..stop) DO
IF NOT Measure[score.event[i]] THEN LOOP;
IF measuresS = -1 THEN {
smFirst ← score.event[i].time-score.event[start].time;
IF sync THEN smFirst ← MAX[smFirst, tolerance+1]};
sync ← FALSE;
smLast ← score.event[MIN[stop, score.length-1]].time-score.event[i].time;
measuresS ← measuresS + 1;
ENDLOOP;
IF sync THEN smLast ← MAX[smLast, tolerance+1];
count the partial measures
IF pmFirst > tolerance THEN measuresP ← measuresP+1;
IF pmLast > tolerance THEN measuresP ← measuresP+1;
IF smFirst > tolerance THEN measuresS ← measuresS+1;
IF smLast > tolerance THEN measuresS ← measuresS+1;
IF smFirst <= tolerance THEN
Selection.AddLine[score, Selection.selection.select1+smFirst+1, Selection.selection.select2];
IF smLast <= tolerance THEN
Selection.AddLine[score, Selection.selection.select1, Selection.selection.select2-smLast-1];
IF measuresS < 1 THEN measuresS ← 1;
IF measuresP < 1 THEN measuresP ← 1;
IF measuresS # 0 AND measuresS # measuresP THEN {
score.flash ← TRUE;
Piece.Free[new];
RETURN};
merge new into the score measure by measure
pEnd ← new.event[length-1].time;
pStart ← length; sStart ← stop;
IF Measure[new.event[length-1]] THEN pStart ← length-1;
FOR i: INTEGER DECREASING IN [0..measuresP) DO
find corresponding measures
pStop ← pStart;
sStop ← sStart;
FOR j: CARDINAL DECREASING IN [0..pStop) DO -- find next measure
IF NOT Measure[new.event[j]] THEN LOOP;
IF pmLast IN [1..tolerance] THEN {pmLast ← 0; pStop ← pStart ← j; LOOP};
pStart ← j; EXIT;
ENDLOOP;
IF pStart = pStop THEN pStart ← 0;
FOR j: CARDINAL DECREASING IN [0..sStop) DO -- find next measure
IF NOT Measure[score.event[j]] THEN LOOP;
IF smLast IN [1..tolerance] THEN {smLast ← 0; sStop ← sStart ← j; LOOP};
sStart ← j; EXIT;
ENDLOOP;
IF sStart = sStop THEN sStart ← start;
IF pStop = pStart THEN LOOP;
scale new's measure and merge it into the score
IF i = 0 THEN sBegin ← begin ELSE sBegin ← score.event[sStart].time;
IF i # measuresP-1 THEN end ← score.event[sStop].time;
pBegin ← (IF pStart = 0 THEN 0 ELSE new.event[pStart].time);
scale ← (l*MAX[end-sBegin, 10])/(MAX[pEnd-pBegin, 10]);
scale ← MIN[scale, 1];
pEnd ← pBegin;
FOR j: CARDINAL IN [pStart..pStop) DO
new.event[j].time ← Real.Fix[scale*(new.event[j].time-pEnd)];
ENDLOOP;
MergeSection[score, new, sBegin, pStart, pStop];
ENDLOOP;
Piece.Free[new];
Voice.Correct[score, Selection.selection.select1, Selection.selection.select2];
Sheet.Reset[score];
};
SelectMeasures: PROC[score: ScorePTR, begin, end: Time] RETURNS[start, stop: CARDINAL] = {
find the start
start ← score.length; stop ← 0;
FOR i: CARDINAL DECREASING IN [0..score.length) DO
IF score.event[i].time < begin AND Measure[score.event[start]] AND start # 0 THEN EXIT;
IF score.event[i].time < begin AND score.event[i].type = sync THEN EXIT;
IF score.event[i].time < begin-40 THEN EXIT;
IF Measure[score.event[i]] OR score.event[i].type = sync THEN start ← i;
ENDLOOP;
find the stop
FOR i: CARDINAL IN [0..score.length] DO
IF i = score.length THEN {stop ← i; EXIT};
IF score.event[i].time > end AND Measure[score.event[stop]] AND stop # 0 THEN EXIT;
IF score.event[i].time > end AND score.event[i].type = sync THEN EXIT;
IF score.event[i].time > end+40 THEN EXIT;
IF Measure[score.event[i]] OR score.event[i].type = sync THEN stop ← i;
ENDLOOP;
IF start = score.length THEN start ← stop;
IF stop = 0 THEN stop ← start;
};
MergeSection: PROC[score, new: ScorePTR, begin: Time, start, stop: CARDINAL] = {
start and stop are indices into new, begin is the time where it goes in score
FOR i: CARDINAL IN [start..stop) DO
new.event[i].time ← new.event[i].time+begin;
ENDLOOP;
FOR i: CARDINAL IN [start..stop) DO
newEvent: EventPTR ← new.event[i];
WITH newEvent SELECT FROM
newSync: SyncPTR => {
nearest: EventPTR ~ score.event[Piece.NearestEvent[score, newSync.time, TRUE]];
WITH nearest SELECT FROM
oldSync: SyncPTR => IF ABS[oldSync.time-newSync.time]<=1 THEN {
FOR j: NAT DECREASING IN[0..newSync.length) DO
Event.AddNote[score, oldSync, newSync.note[j]];
ENDLOOP;
newEvent ← NIL;
};
ENDCASE;
};
staves: StavesPTR => IF staves.staves=style THEN newEvent ← NIL;
ENDCASE => newEvent ← NIL;
IF newEvent#NIL THEN Piece.AddEvent[score, newEvent];
new.event[i] ← NIL;
ENDLOOP;
};
Replace: PUBLIC PROC[score: ScorePTR, new: ScorePortionPTR, delete1, delete2: Time] = {
ENABLE Piece.Overflow => IF score = old THEN score ← new;
replaces [delete1..delete2) with the score portion.
portion: ScorePortionPTR ~ NEW[ScorePortionRec ← []];
IF score.sheet.voice # noVoice THEN { DeleteSection[score, delete1, delete2, 0]; RETURN};
portion.score ← score;
SetDirty[score, delete1, LAST[Time]];
GetScoreState[portion, delete1, delete2];
IF delete1 # delete2 THEN DeleteSection[score, delete1, delete2, portion.toc];
toc ← MaxToc[score, 0, begin, FALSE]-MinToc[score, end, 1000000];
IF score.sheet.display = physical THEN toc ← (delete2-delete1)*score.sheet.density;
IF new # NIL THEN {
InsertSection[score, new.score, delete1, new.duration, new.toc];
SetScoreDifferences[score, portion.ss1, new.ss1, delete1];
SetScoreDifferences[score, new.ss2, portion.ss2, delete1];
zone.FREE[@new]
};
Sheet.Reset[score];
Selection.AddLine[score, delete1, delete1];
};
DeleteSection: PUBLIC PROC[score: ScorePTR, delete1, delete2, toc: Time] = {
voice: BOOL ← score.sheet.voice # noVoice;
SetDirty[score, delete1, IF voice THEN delete2 ELSE LAST[Time]];
free sync and invalidate syncs
FOR i: CARDINAL DECREASING IN [0..score.length) DO
event: EventPTR ~ score.event[i];
IF event.time >= delete1 THEN LOOP;
IF event.time < delete2 THEN EXIT;
WITH event SELECT FROM
sync: SyncPTR => {
FOR j: NAT DECREASING IN[0..sync.length) DO
n: NotePTR ← sync.note[j];
IF voice THEN {IF ~Note.InVoice[n, score.sheet.voice] THEN LOOP}
ELSE { -- speed up deletion with some efficiency hacks
IF n.chord # NIL THEN Chord.Free[score, n.chord];
n.sync ← NIL}; -- so Free won't waste time removing n from sync
Note.Free[score, n]; -- removes all back pointers to n
ENDLOOP;
};
ENDCASE;
IF ~voice AND i # 0 THEN Event.Free[score, event];
ENDLOOP;
IF ~voice THEN FOR i: CARDINAL IN [0..score.length) DO -- move everything to the left
IF score.event[i].time < delete2 THEN LOOP;
Event.AddTimes[score.event[i], delete1-delete2, -toc];
ENDLOOP;
Piece.CleanUpEvents[score];
};
InsertSection: PROC[score, new: ScorePTR, begin, duration, toc: Time] = {
anything at begin ends up after new
start: CARDINAL ← 0;
length: CARDINAL = new.length;
move the trailing part of the score to the right
IF begin = 0 THEN begin ← 1;
[size, toc] ← Size[new, length];
FOR i: CARDINAL DECREASING IN [0..score.length) DO
IF score.event[i].time < begin THEN {start ← i+1; EXIT};
Event.AddTimes[score.event[i], duration, toc];
score.event[i+length] ← score.event[i];
ENDLOOP;
insert the new score
IF score.sheet.display = physical
THEN toc ← begin*score.sheet.density
ELSE toc ← MaxToc[score, 0, begin, TRUE];
FOR i: CARDINAL IN[start..start+length) DO
score.event[i] ← new.event[i-start];
WITH score.event[i] SELECT FROM
sync: SyncPTR => Event.AddTimes[sync, begin, toc];
ENDCASE;
new.event[i-start] ← NIL; -- so Piece.Free doesn't deallocate the syncs
ENDLOOP;
score.length ← score.length + length;
score[score.length] ← NIL;
Piece.CleanUpEvents[score];
Piece.Free[new];
Sheet.Reset[score];
IF Selection.selection.lineSelect THEN Selection.AddLine[score, begin, begin+duration];
SetDirty[score, begin, LAST[Time]];
};
Size: PROC[score: ScorePTR, length: CARDINAL] RETURNS[size, toc: Time ← 0] = {
grey: Time = Selection.selection.greySelect2-Selection.selection.greySelect1;
FOR i: CARDINAL IN [(IF length > 10 THEN length-10 ELSE 0)..length) DO
size ← MAX[size, score.event[i].time-score.event[0].time];
WITH score.event[i] SELECT FROM
sync: SyncPTR => {
FOR j: NAT IN[0..sync.length) DO
n: NotePTR ~ sync.note[j];
IF n=NIL THEN EXIT;
IF n.toc = 0 THEN LOOP;
toc ← MAX[toc, n.toc+n.duration];
IF score.sheet.max-n.toc > 64000 THEN EXIT;
ENDLOOP;
};
ENDCASE;
ENDLOOP;
IF ABS[grey-size] < 30 THEN size ← grey ELSE size ← size+10; -- use the grey selection if applicable
IF score.sheet.display = physical THEN toc ← size*score.sheet.density;
};
MaxToc: PUBLIC PROC[score: ScorePTR, time1, time2: Time, duration: BOOL] RETURNS[Time] = {
max: Time ← 0;
noteTime: Time ← end;
IF score.sheet.display = physical THEN RETURN[time2*score.sheet.density];
FOR i: CARDINAL DECREASING IN [0..score.length) DO
event: EventPTR ~ score.event[i];
IF event.time >= time2 THEN LOOP;
IF event.time <= time1 THEN EXIT;
WITH event SELECT FROM
sync: SyncPTR => {
FOR j: NAT IN[0..sync.length) DO
n: NotePTR ~ sync.note[j];
IF n = NIL THEN EXIT;
IF n.toc = 0 THEN LOOP;
IF n.toc > ABS[noteTime+3000] THEN LOOP;
max ← MAX[max, n.toc+(IF duration THEN n.duration ELSE 0)];
noteTime ← n.toc;
IF max-noteTime > 64000 THEN RETURN[max];
ENDLOOP;
};
ENDCASE;
ENDLOOP;
RETURN[max];
};
end: Time = 10000000;
MinToc: PROC[score: ScorePTR, time1, time2: Time] RETURNS[Time] = {
min: Time ← end;
noteTime: Time;
IF score.sheet.display = physical AND time1 # 0 THEN min ← time1*score.sheet.density;
FOR i: CARDINAL IN [0..score.length) DO
event: EventPTR ~ score.event[i];
IF event.time < time1 THEN LOOP;
IF event.time > time2 THEN EXIT;
WITH event SELECT FROM
sync: SyncPTR => {
FOR j: NAT IN[0..sync.length) DO
n: NotePTR ~ sync.note[j];
IF n = NIL THEN EXIT;
IF n.toc = 0 OR n.duration = 0 THEN LOOP;
min ← MIN[min, n.toc];
noteTime ← n.toc;
IF noteTime-min > 64000 THEN RETURN[min];
ENDLOOP;
};
ENDCASE;
ENDLOOP;
RETURN[min];
};
********************************************************
making score attributes consistent as you cut and paste
********************************************************
GetScoreState: PROC[ss: ScorePortionPTR, t1, t2: Time] = {
ss.duration ← t2-t1;
IF ss.score.sheet.display = physical
THEN ss.toc ← (t2-t1)*ss.score.sheet.density
ELSE ss.toc ← MinToc[ss.score, t1, t2];
ss.ss1.key ← Score.GetKey[ss.score, t1];
ss.ss2.key ← Score.GetKey[ss.score, t2];
ss.ss1.style ← Score.GetStyle[ss.score, t1];
ss.ss2.style ← Score.GetStyle[ss.score, t2];
ss.ss1.staff ← ss.score.sheet.section[Sheet.FindSection[ss.score.sheet, t1]].staves.staff;
ss.ss2.staff ← ss.score.sheet.section[Sheet.FindSection[ss.score.sheet, t2]].staves.staff;
};
SetScoreDifferences: PROC[score: ScorePTR, ss1, ss2: ScoreStateRec, time: Time] = {
IF ss1.key # ss2.key THEN {
keySig: KeySignaturePTR ~ NEW[EventRec[keySignature]];
keySig.time ← time;
keySig.key ← ss2.key;
Piece.AddEvent[score, keySig];
};
IF ss1.style # ss2.style THEN {
style: StavesPTR ~ NEW[EventRec[staves]];
style.time ← time;
style.staves ← style;
style.value ← ss2.style;
Piece.AddEvent[score, style];
};
FOR i: CARDINAL IN [0..4) DO
staves: StavesPTR;
IF i > 0 AND ss2.staff[i].y = ss2.staff[i-1].y THEN LOOP;
IF ss1.staff[i].pitch = ss2.staff[i].pitch THEN LOOP;
staves ← NEW[EventRec[staves]];
staves.time ← time;
staves.value ← i;
staves.staff ← ss2.staff;
SELECT ss2.staff[i].pitch FROM
15, 60 => staves.staves ← octava1;
27 => IF ss1.staff[i].pitch = 48
THEN staves.staves ← clef
ELSE staves.staves ← octava2;
48 => IF ss1.staff[i].pitch = 27
THEN staves.staves ← clef
ELSE staves.staves ← octava2;
ENDCASE;
Piece.AddEvent[score, staves];
ENDLOOP;
};
END.
************************************************************************
making a copy of a portion of the score
************************************************************************
BeamMapPTR: TYPE = REF BeamMapRec;
BeamMapRec: TYPE = RECORD[length: CARDINAL ← 0,
array: SEQUENCE max: CARDINAL OF RECORD[old, new: BeamPTR ← NIL]];
ChordMapPTR: TYPE = REF ChordMapRec;
ChordMapRec: TYPE = RECORD[length: CARDINAL ← 0,
array: SEQUENCE max: CARDINAL OF RECORD[old, new: ChordPTR ← NIL]];
NoteMapPTR: TYPE = REF NoteMapRec;
NoteMapRec: TYPE = RECORD[length: CARDINAL ← 0,
array: SEQUENCE max: CARDINAL OF RECORD[old, new: NotePTR ← NIL]];
Copy: PUBLIC PROC[score: ScorePTR, time1, time2: Time] RETURNS[copy: ScorePortionPTR] = {
ENABLE Piece.Overflow => {IF copy.score = old THEN copy.score ← new; RESUME};
copies [time1..time2)
tieMap: NoteMapPTR ← zone.NEW[NoteMapRec[10]];
beamMap: BeamMapPTR ← zone.NEW[BeamMapRec[10]];
chordMap: ChordMapPTR ← zone.NEW[ChordMapRec[10]];
copy ← zone.NEW[ScorePortionRec];
copy.score ← score;
GetScoreState[copy, time1, time2];
copy.score ← Piece.New[100, FALSE];
IF score.sheet.voice # noVoice AND score.sheet.display # physical THEN {
start, stop: CARDINAL;
[start, stop] ← SelectMeasures[score, time1, time2];
IF start # stop THEN time1 ← score.event[start].time;
IF stop # start THEN time2 ← score.event[MIN[stop, score.length-1]].time+1;
Selection.AddGreyLine[score, time1, time2]};
FOR i: CARDINAL IN [0..score.length) DO
newEvent: EventPTR;
IF score.event[i].time < time1 THEN LOOP;
IF score.event[i].time >= time2 THEN EXIT;
IF score.sheet.voice # noVoice AND ~Measure[score.event[i]]
AND score.event[i].type # sync THEN LOOP;
IF Selection.selection.lineSelect AND score.event[i].type # sync THEN
newEvent ← CopyEvent[score.event[i]];
IF score.event[i].type = sync THEN {
n: NotePTR;
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;
IF ~Selection.Includes[n] THEN LOOP;
CopyNote[score, n, newEvent, beamMap, chordMap, tieMap];
ENDLOOP};
UnSave[score.event[i], beamMap, chordMap];
IF newEvent = NIL THEN LOOP;
Event.AddTimes[newEvent, -time1, -copy.toc+500];
copy.score ← Piece.AddEvent[copy.score, newEvent];
ENDLOOP;
FOR i: CARDINAL DECREASING IN [0..copy.score.beamHeap.length) DO
IF copy.score.beamHeap[i].length > 1 THEN LOOP;
Beam.Free[copy.score, copy.score.beamHeap[i]];
ENDLOOP;
FOR i: CARDINAL DECREASING IN [0..copy.score.chordHeap.length) DO
IF copy.score.chordHeap[i].length > 1 THEN LOOP;
Chord.Free[copy.score, copy.score.chordHeap[i]];
ENDLOOP;
zone.FREE[@tieMap];
zone.FREE[@chordMap];
zone.FREE[@beamMap];
RETURN[copy];
};
CopyEvent: PROC[event: EventPTR] RETURNS[copy: EventPTR] = {
WITH ev: event SELECT FROM
sync => ERROR;
measure => {
copy ← zone.NEW[EventRec.measure];
Event.Measure[copy]^ ← ev};
timeSignature => {
copy ← zone.NEW[EventRec.timeSignature];
Event.TimeSignature[copy]^ ← ev};
keySignature => {
copy ← zone.NEW[EventRec.keySignature];
Event.KeySignature[copy]^ ← ev};
metrenome => {
copy ← zone.NEW[EventRec.metrenome];
Event.Metrenome[copy]^ ← ev};
staves => {
copy ← zone.NEW[EventRec.staves];
Event.Staves[copy]^ ← ev};
ENDCASE => ERROR;
};
CopyNote: PROC[score: ScorePTR, old: NotePTR, newEvent: EventPTR, beamMap: BeamMapPTR, chordMap: ChordMapPTR, tieMap: NoteMapPTR] = {
new: NotePTR;
b, oldBeam: BeamPTR;
c, oldChord: ChordPTR;
new ← zone.NEW[NoteRec];
new^ ← old^;
oldBeam ← new.beam;
new.beam ← NIL;
IF new.tied THEN {SaveTie[tieMap, old, new]; new.tied ← FALSE};
IF new.tie # NIL THEN {
new.tie ← Find[new.tie];
RemoveTie[tieMap, new.tie];
IF new.tie # NIL THEN new.tie.tied ← TRUE};
oldChord ← Note.FindChord[old];
IF oldChord # NIL THEN {
c ← Find[oldChord];
IF c = NIL THEN {
c ← Chord.New[score, oldChord.max];
SaveChord[chordMap, oldChord, c];
};
c.stemUp ← oldChord.stemUp;
c ← Chord.AddNote[score, c, new];
};
IF newEvent = NIL THEN {
newEvent ← zone.NEW[EventRec.sync[old.sync.max]];
newEvent.time ← old.sync.time};
newEvent ← Event.AddNote[score, Event.Sync[newEvent], new];
IF oldBeam = NIL THEN RETURN;
b ← Find[oldBeam];
IF b = NIL THEN {
b ← Beam.New[score, oldBeam.max];
SaveBeam[beamMap, oldBeam, b];
Inline.LongCOPY[oldBeam, SIZE[BeamRec[0]], b]; -- copy all but chords
b.beam ← NIL;
b.sync1 ← b.sync2 ← NIL;
};
CopyBeam[score, beamMap, oldBeam, b, old, new];
};
CopyBeam: PROC[score: ScorePTR, beamMap: BeamMapPTR, oldBeam, newBeam: BeamPTR, oldNote, newNote: NotePTR] = {
highBeam: BeamPTR;
oldChord, newChord: ChordPTR;
newNote.beam ← newBeam;
FOR i: CARDINAL IN [0..oldBeam.length) DO
oldChord ← Note.FindChord[oldNote];
newChord ← Note.FindChord[newNote];
IF oldBeam.chord[i] = [note[oldNote]]
THEN newBeam ← Beam.AddItem[score, newBeam, [note[newNote]]];
IF oldBeam.chord[i] = [chord[oldChord]]
THEN newBeam ← Beam.AddItem[score, newBeam, [chord[newChord]]];
ENDLOOP;
IF oldBeam.beam = NIL OR newBeam.beam # NIL THEN RETURN;
highBeam ← Find[oldBeam.beam];
IF highBeam = NIL THEN {
highBeam ← Beam.New[score, oldBeam.max];
SaveBeam[beamMap, oldBeam.beam, highBeam];
Inline.LongCOPY[oldBeam, SIZE[BeamRec[0]], highBeam]; -- copy all but chords
highBeam.beam ← NIL;
highBeam.chord ← ALL[endOfBeam];
highBeam.sync1 ← highBeam.sync2 ← NIL;
};
highBeam ← Beam.AddItem[score, highBeam, [beam[newBeam]]];
};
SaveBeam: PROC[map: BeamMapPTR, old, new: BeamPTR] = {
i: CARDINAL;
FOR i IN [0..map.length) DO
IF map[i].old # NIL THEN LOOP;
map[i] ← [old, new];
RETURN;
ENDLOOP;
IF map.length = map.max THEN ERROR;
map[map.length] ← [old, new];
map.length ← map.length + 1;
};
SaveChord: PROC[map: ChordMapPTR, old, new: ChordPTR] = {
i: CARDINAL;
FOR i IN [0..map.length) DO
IF map[i].old # NIL THEN LOOP;
map[i] ← [old, new];
RETURN;
ENDLOOP;
IF map.length = map.max THEN ERROR;
map[map.length] ← [old, new];
map.length ← map.length + 1;
};
SaveTie: PROC[map: NoteMapPTR, old, new: NotePTR] = {
i: CARDINAL;
FOR i IN [0..map.length) DO
IF map[i].old # NIL THEN LOOP;
map[i] ← [old, new];
RETURN;
ENDLOOP;
IF map.length = map.max THEN ERROR;
map[map.length] ← [old, new];
map.length ← map.length + 1;
};
RemoveTie: PROC[map: NoteMapPTR, new: NotePTR] = {
i: CARDINAL;
FOR i IN [0..map.length) DO
IF map[i].new # new THEN LOOP;
map[i] ← [NIL, NIL];
RETURN;
ENDLOOP;
ERROR;
};
UnSave: PROC[s: EventPTR, beamMap: BeamMapPTR, chordMap: ChordMapPTR] = {
IF s = NIL THEN RETURN;
FOR i: CARDINAL IN [0..beamMap.length) DO
IF beamMap[i].old # NIL AND beamMap[i].old.sync2 = s
THEN beamMap[i] ← [NIL, NIL];
ENDLOOP;
FOR i: CARDINAL IN [0..chordMap.length) DO
IF chordMap[i].old # NIL AND chordMap[i].old.note[0].sync = s
THEN chordMap[i] ← [NIL, NIL];
ENDLOOP;
};
Find: PROC[old: LONG POINTER] RETURNS[LONG POINTER] = {
i: CARDINAL;
FOR i IN [0..listLength) DO
IF beamList[i].old = old THEN RETURN[beamList[i].new];
IF chordList[i].old = old THEN RETURN[chordList[i].new];
IF tieList[i].old = old THEN RETURN[tieList[i].new];
ENDLOOP;
RETURN[NIL];
};
listLength: CARDINAL = 10;
beamList: ARRAY [0..listLength) OF RECORD[old, new: BeamPTR];
chordList: ARRAY [0..listLength) OF RECORD[old, new: ChordPTR];
tieList: ARRAY [0..listLength) OF RECORD[old, new: NotePTR];
************************************************************************
Delete: PUBLIC PROC[begin, end: Time] = {
toc: Time;
n: NotePTR;
j: CARDINAL;
sync: EventPTR;
staves: StavesPTR;
newSheet: BOOL;
key, index: INTEGER;
staff: ARRAY [0..4) OF Staff;
SetDirty[begin, IF voice THEN end ELSE EndOfScore[]];
key ← Score.GetKey[end];
staves ← sheet[Sheet.FindSection[begin]].staves;
free sync and invalidate syncs
FOR i: CARDINAL DECREASING IN [0..score.length) DO
IF score.event[i].time >= end THEN LOOP;
IF score.event[i].time < begin THEN EXIT;
IF score.event[i].type = sync THEN FOR j DECREASING IN [0..syncLength) DO
IF (n ← score.event[i].event[j]) = NIL THEN LOOP;
IF voice AND n.voice # selectedVoice THEN LOOP;
-- Piece.-- RemoveNote[n];
Note.Free[@n]; -- takes n out of n.beam, n.chord, etc.
ENDLOOP;
IF voice THEN LOOP;
IF score.event[i].type IN SheetSwitch
THEN score.event[i].time ← end
ELSE score.event[i].type ← sync; -- deleted by CleanUpEvents
ENDLOOP;
invalidate useless sheet switches
newSheet ← FALSE;
staff ← ALL[NIL];
IF ~voice THEN FOR i: CARDINAL DECREASING IN [0..score.length) DO
IF score.event[i].time # end THEN LOOP;
IF score.event[i].type NOT IN SheetSwitch THEN LOOP;
IF score.event[i].type = staves THEN {
IF newSheet THEN score.event[i].type ← sync ELSE newSheet ← TRUE; LOOP};
index ← Index[score.event[i], score.event[i].value]; -- get the normalized index
SELECT score.event[i].type FROM
clef => IF staff[index] # NIL
THEN score.event[i].type ← sync
ELSE staff[index] ← score.event[i];
octava1 => IF staff[index] # NIL
THEN {IF staff[index].type # octava2 THEN Error;
score.event[i].type ← sync;
staff[index].type ← sync;
staff[index] ← NIL}
ELSE staff[index] ← score.event[i];
octava2 => IF staff[index] # NIL AND staff[index].type = octava1
THEN {score.event[i].type ← sync;
staff[index].type ← sync;
staff[index] ← NIL}
ELSE staff[index] ← score.event[i];
ENDCASE;
ENDLOOP;
invalidate duplicate sheet switches
IF ~voice THEN FOR i: CARDINAL IN [0..6) DO
IF staff[i] = NIL THEN LOOP;
IF staves.staff[i].pitch # Event.GetStaff[staff[i], i].pitch THEN LOOP;
staff[i].type ← sync;
ENDLOOP;
Piece.CleanUpEvents[score]; -- frees invalid syncs
put back any key signature
IF Score.GetKey[end] # key THEN {
sync ← zone.NEW[EventRec.keySignature];
sync.time ← end;
sync.type ← keySignature;
sync.value ← key;
Piece.AddEvent[score, sync]};
slide everything over
toc ← MaxToc[score, 0, begin]-MinToc[end, 1000000];
IF ~voice THEN FOR i: CARDINAL IN [0..score.length) DO
IF score.event[i].time < end THEN LOOP;
Event.AddTimes[score.event[i], begin-end, toc];
ENDLOOP;
Sheet.Reset[];
Selection.AddLine[begin, begin];
};
Index: PROC[s: EventPTR, i: CARDINAL] RETURNS[index: INTEGER] =
{IF s.type NOT IN SheetSwitch THEN Error;
index ← s.value;
FOR j: CARDINAL DECREASING IN [0..4) DO
IF Event.GetStaff[s, j].y = Event.GetStaff[s, index].y THEN index ← j;
ENDLOOP};
InsertScore: PUBLIC PROC[new: ScorePTR, size: Time] = {
i, j, k, pLength: CARDINAL ← 0;
maxTime, offset: Time ← 0;
delta: Time ← select2-select1+20;
FOR i IN [0..maxPieceLength) DO
IF new.event[i] = NIL THEN { pLength ← i; EXIT; };
ENDLOOP;
FOR i IN [0..pLength) DO maxTime ← MAX[maxTime, new.event[i].time+10]; ENDLOOP;
IF size # 0 THEN maxTime ← size;
IF pLength+score.length >= maxScoreLength THEN ERROR;
DeleteSelection[];
offset ← MaxTime[new, 0, end];
IF show.display = physical THEN offset ← MAX[offset, maxTime*TF];
FOR i DECREASING IN [0..score.length) DO
IF score.event[i].time <= select1 THEN { j ← i+1; EXIT; };
Event.AddTimes[score.event[i], maxTime, offset];
score.event[i+pLength] ← score.event[i];
ENDLOOP;
offset ← MaxTime[score, 0, select1];
FOR i IN [j..j+pLength) DO
score.event[i] ← new.event[i-j];
Event.AddTimes[score.event[i], select1, offset];
ENDLOOP;
score.length ← score.length + pLength;
SystemDefs.FreeSegment[new];
CleanUpEvents[];
min ← select1;
max ← EndOfScore[]+MAX[delta, maxTime];
};
Replace: PUBLIC PROC[score, score2: ScorePTR, delete1, delete2, copy1, copy2: Time] = {
ENABLE Piece.Overflow => {
IF score = old THEN score ← new; IF score2 = old THEN score2 ← new};
copy: ScorePTR;
cs1, cs2, ds1, ds2: ScoreState;
IF score2 # NIL THEN {
GetScoreState[score, @ds1, MAX[delete1-1, 0]];
GetScoreState[score, @ds2, delete2];
GetScoreState[score2, @cs1, MAX[copy1-1, 0]];
GetScoreState[score2, @cs2, copy2];
copy ← Copy[score2, copy1, copy2]};
Delete[score, delete1, delete2];
IF copy = NIL THEN RETURN;
IF score.sheet.voice # noVoice
THEN Piece.Merge[score, copy, delete1, delete2]
ELSE {
Insert[score, copy, delete1];
SetScoreDifferences[score, @ds1, @cs1, delete1];
SetScoreDifferences[score, @cs2, @ds2, delete1+copy2-copy1];
Sheet.Reset[score]};
};