--Author: John Maxwell
--last modified: January 16, 1982 12:05 PM

DIRECTORY
Beam USING [AddBeam,AddChord,AddNote,Remove,SetStems],
Chord USING [AddNote,RemoveNote,SetDefaultStem,Sort],
Graphics USING [DisplayChar,MoveTo],
Heuristic USING [MakeNTuplets],
MusicDefs,
Note USING [FindChord,GetBackTie],
Piece USING [AddSync,CleanUpSyncs,Delete],
Real USING [FixI],
Selection USING [Clear,Draw,MakeBeam,MakeBeamOfBeams,RemoveNote],
Sheet USING [Height,HiLite,Map,MapNote, NextStaff],
Sync USING [AddNote],
Utility USING [FreeBeam,FreeNote,NewBeam,NewChord,NewSync];



SelectionImpl:PROGRAM
IMPORTS Beam, Chord, Graphics, Heuristic, MusicDefs, Note, Piece, Real, Selection, Sheet, Sync, Utility
EXPORTS Selection, MusicDefs =
BEGIN
OPEN Graphics, MusicDefs;

lineSelect
:PUBLIC BOOLEAN;
select1,greySelect1:PUBLIC Time ← 1;
greySelect2,select2:PUBLIC Time ← 0;

selection:PUBLIC ARRAY [0..maxSelectionLength) OF NotePTR;
selectionLength:PUBLIC CARDINAL←0;

min,max:PUBLIC Time ← 1;
command:PUBLIC BOOLEAN ← FALSE;
flash:PUBLIC BOOLEAN ← FALSE;

--***************************************************************************
--
selection manipulation
--***************************************************************************

Clear
:PUBLIC PROCEDURE =
BEGIN
Selection.Draw[];
greySelect1 ← select1 ← 1;
greySelect2 ← select2 ← 0;
selectionLength ← 0;
END;

AddNote:PUBLIC PROCEDURE[n:NotePTR] =
BEGIN
i:CARDINAL;
x,y:INTEGER;
IF lineSelect THEN Clear[];
IF n=NIL THEN RETURN;
IF voice AND n.voice#selectedVoice THEN RETURN;
IF selectionLength=maxSelectionLength THEN RETURN;
FOR i IN [0..selectionLength) DO IF selection[i]=n THEN RETURN; ENDLOOP;
selection[selectionLength]←n;
selectionLength ← selectionLength+1;
lineSelect ← FALSE;
[x,y] ← Sheet.MapNote[n];
Graphics.MoveTo[context,[x,y]];
SetBrush[black,invert];
Graphics.DisplayChar[context,170C];
END;

RemoveNote:PUBLIC PROCEDURE[n:NotePTR] =
BEGIN
x,y:INTEGER;
IF n=NIL THEN RETURN;
SetBrush[black,invert];
FOR i:CARDINAL IN [0..selectionLength) DO
IF selection[i]#n THEN LOOP;
selection[i] ← NIL;
FOR j:CARDINAL DECREASING IN [0..selectionLength) DO
IF selection[j]#NIL THEN EXIT;
selectionLength ← j;
ENDLOOP;
[x,y] ← Sheet.MapNote[n];
Graphics.MoveTo[context,[x,y]];
Graphics.DisplayChar[context,170C];
RETURN; ENDLOOP;
END;

AddLine:PUBLIC PROCEDURE[time1,time2:Time] =
BEGIN
IF lineSelect
THEN Sheet.HiLite[black,select1,select2]
ELSE Selection.Clear[];
Sheet.HiLite[black,time1,time2];
select1 ← time1;
select2 ← time2;
lineSelect ← TRUE;
END;

AddGreyLine:PUBLIC PROCEDURE[time1,time2:Time] =
BEGIN
IF lineSelect
THEN Sheet.HiLite[grey,greySelect1,greySelect2]
ELSE Selection.Clear[];
Sheet.HiLite[grey,time1,time2];
greySelect1 ← time1;
greySelect2 ← time2;
lineSelect ← TRUE;
END;

Draw:PUBLIC PROCEDURE =
BEGIN
x,y:INTEGER;
IF lineSelect THEN {
Sheet.HiLite[black,select1,select2];
Sheet.HiLite[grey,greySelect1,greySelect2];
RETURN};
SetBrush[black,invert];
FOR i:CARDINAL IN [0..selectionLength) DO
IF selection[i]=NIL THEN LOOP;
IF voice AND selection[i].voice#selectedVoice THEN LOOP;
[x,y] ← Sheet.MapNote[selection[i]];
Graphics.MoveTo[context,[x,y]];
Graphics.DisplayChar[context,170C];
ENDLOOP;
END;

--****************************************************************************
--
procedures that take the current selection as an implicit parameter
--****************************************************************************

SetNoteValue:PUBLIC PROCEDURE[v:NoteValue,dots:INTEGER] =
BEGIN
i,j:CARDINAL;
n:NotePTR;
IF lineSelect
THEN FOR i IN [0..scoreLength) DO
IF score[i]=NIL THEN LOOP;
IF score[i].time < select1 OR score[i].time > select2 THEN LOOP;
FOR j IN [0..syncLength) DO
IF (n←score[i].event[j])=NIL THEN EXIT;
IF voice AND n.voice#selectedVoice THEN LOOP;
n.value← v;
IF dots>1 THEN n.doubleDotted ← TRUE ELSE n.doubleDotted←FALSE;
IF dots MOD 2=1 THEN n.dotted←TRUE ELSE n.dotted←FALSE;
IF v>quarter OR n.beam=NIL OR NOT n.beam.beamed THEN LOOP;
Beam.Remove[n.beam,n,Note.FindChord[n],NIL];
ENDLOOP;
ENDLOOP
ELSE FOR i IN [0..selectionLength) DO
IF (n←selection[i])=NIL THEN LOOP;
IF voice AND n.voice#selectedVoice THEN LOOP;
SetDirty[n.sync.time,n.sync.time];
n.value← v;
IF dots>1 THEN n.doubleDotted ← TRUE ELSE n.doubleDotted←FALSE;
IF dots MOD 2=1 THEN n.dotted←TRUE ELSE n.dotted←FALSE;
IF v>quarter OR n.beam=NIL OR NOT n.beam.beamed THEN LOOP;
Beam.Remove[n.beam,n,Note.FindChord[n],NIL];
ENDLOOP;
IF lineSelect THEN SetDirty[select1,select2];
END;

SetRest:PUBLIC PROCEDURE[rest:BOOLEAN] =
BEGIN
i,j:CARDINAL;
n:NotePTR;
IF lineSelect
THEN FOR i IN [0..scoreLength) DO
IF score[i]=NIL THEN LOOP;
IF score[i].time < select1 OR score[i].time > select2 THEN LOOP;
FOR j IN [0..syncLength) DO
IF (n←score[i].event[j])=NIL THEN EXIT;
IF voice AND n.voice#selectedVoice THEN LOOP;
n.rest ← rest;
ENDLOOP;
ENDLOOP
ELSE FOR i IN [0..selectionLength) DO
IF (n←selection[i])=NIL THEN LOOP;
IF voice AND n.voice#selectedVoice THEN LOOP;
SetDirty[n.sync.time,n.sync.time];
n.rest← rest;
ENDLOOP;
IF lineSelect THEN SetDirty[select1,select2];
END;

SetStaff:PUBLIC PROCEDURE[staff:CARDINAL] =
BEGIN
n:NotePTR;
Count:PROCEDURE[s:CARDINAL,t:Time] RETURNS[j:CARDINAL] = {
j←0; FOR i:CARDINAL IN [0..s) DO j←Sheet.NextStaff[j,t]; ENDLOOP};
IF staff=0 THEN {flash←TRUE; RETURN} ELSE staff←staff-1;
IF lineSelect
THEN FOR i:CARDINAL IN [0..scoreLength) DO
IF score[i]=NIL THEN LOOP;
IF score[i].time < select1 OR score[i].time > select2 THEN LOOP;
FOR j:CARDINAL IN [0..syncLength) DO
IF (n←score[i].event[j])=NIL THEN EXIT;
IF voice AND n.voice#selectedVoice THEN LOOP;
n.staff ← Count[staff,n.sync.time];
IF n.beam#NIL THEN Beam.SetStems[n.beam];
ENDLOOP;
ENDLOOP
ELSE FOR i:CARDINAL IN [0..selectionLength) DO
IF (n←selection[i])=NIL THEN LOOP;
IF voice AND n.voice#selectedVoice THEN LOOP;
SetDirty[n.sync.time,n.sync.time];
n.staff ← Count[staff,n.sync.time];
IF n.beam#NIL THEN Beam.SetStems[n.beam];
ENDLOOP;
IF lineSelect THEN SetDirty[select1,select2];
END;

SetStem:PUBLIC PROCEDURE[stemUp:BOOLEAN] =
BEGIN
i,j:CARDINAL;
x,y,xb,yb:INTEGER;
d:INTEGER;
n:NotePTR;
c:ChordPTR;
b:BeamPTR;
IF lineSelect
THEN FOR i IN [0..scoreLength) DO
IF score[i]=NIL THEN LOOP;
IF score[i].time < select1 OR score[i].time > select2 THEN LOOP;
FOR j IN [0..syncLength) DO
IF (n←score[i].event[j])=NIL THEN EXIT;
IF voice AND n.voice#selectedVoice THEN LOOP;
n.stemUp←stemUp;
c ← Note.FindChord[n];
IF c#NIL THEN c.stemUp ← stemUp;
IF (b←n.beam)=NIL OR NOT n.beam.beamed THEN LOOP;
[x,y] ← Sheet.Map[n.sync.time,n.pitch,n.staff];
[xb,yb]←Sheet.Map[b.sync1.time,,b.staff];
yb←yb+b.height;
d ← Real.FixI[yb+b.tilt*(x-xb)-y];
b.height←b.height-(IF stemUp THEN MIN[0,d-28] ELSE MAX[0,d+28]);
ENDLOOP;
ENDLOOP
ELSE FOR i IN [0..selectionLength) DO
IF (n←selection[i])=NIL THEN LOOP;
IF voice AND n.voice#selectedVoice THEN LOOP;
SetDirty[n.sync.time,n.sync.time];
n.stemUp ← stemUp;
c ← Note.FindChord[n];
IF c#NIL THEN c.stemUp ← stemUp;
IF (b←n.beam)=NIL OR NOT n.beam.beamed THEN LOOP;
[x,y] ← Sheet.Map[n.sync.time,n.pitch,n.staff];
[xb,yb]←Sheet.Map[b.sync1.time,,b.staff];
yb ← yb+b.height;
d ← Real.FixI[yb+n.beam.tilt*(x-xb)-y];
b.height←b.height-(IF stemUp THEN MIN[0,d-28] ELSE MAX[0,d+28]);
ENDLOOP;
IF lineSelect THEN SetDirty[select1,select2];
END;

SetGrace:PUBLIC PROCEDURE[grace:BOOLEAN] =
BEGIN
i,j:CARDINAL;
n:NotePTR;
IF lineSelect
THEN FOR i IN [0..scoreLength) DO
IF score[i]=NIL THEN LOOP;
IF score[i].time < select1 OR score[i].time > select2 THEN LOOP;
FOR j IN [0..syncLength) DO
IF (n←score[i].event[j])=NIL THEN EXIT;
IF voice AND n.voice#selectedVoice THEN LOOP;
n.grace ← grace;
ENDLOOP;
ENDLOOP
ELSE FOR i IN [0..selectionLength) DO
IF (n←selection[i])=NIL THEN LOOP;
IF voice AND n.voice#selectedVoice THEN LOOP;
SetDirty[n.sync.time,n.sync.time];
n.grace← grace;
ENDLOOP;
IF lineSelect THEN SetDirty[select1,select2];
END;

Transpose:PUBLIC PROCEDURE[halfsteps:INTEGER] =
BEGIN
i,j:CARDINAL;
n:NotePTR;
IF lineSelect
THEN FOR i IN [0..scoreLength) DO
IF score[i]=NIL THEN LOOP;
IF score[i].time < select1 OR score[i].time > select2 THEN LOOP;
FOR j IN [0..syncLength) DO
IF (n←score[i].event[j])=NIL THEN EXIT;
IF voice AND n.voice#selectedVoice THEN LOOP;
n.pitch←n.pitch+halfsteps;
IF (halfsteps MOD 12)#0 THEN {n.spelled ← inKey; n.show←FALSE};
ENDLOOP;
ENDLOOP
ELSE FOR i IN [0..selectionLength) DO
IF (n←selection[i])=NIL THEN LOOP;
IF voice AND n.voice#selectedVoice THEN LOOP;
SetDirty[n.sync.time,n.sync.time];
n.pitch ← n.pitch+halfsteps;
IF (halfsteps MOD 12)#0 THEN {n.spelled ← inKey; n.show←FALSE};
ENDLOOP;
IF lineSelect THEN SetDirty[select1,select2];
END;

--**************************************************************************
--syncs
--**************************************************************************

MakeSync
:PUBLIC PROCEDURE =
BEGIN
sync:SyncPTR←NIL;
c:ChordPTR;
n:NotePTR;
i,j:CARDINAL;
begin,end:Time;
begin ← EndOfScore[];
end ← -1;
FOR i IN [0..selectionLength) DO
IF (n←selection[i])=NIL THEN LOOP;
begin ← MIN[begin,n.sync.time];
end ← MAX[end,n.sync.time];
ENDLOOP;
IF ABS[end-begin]>20 THEN {flash←TRUE; RETURN};
FOR i IN [0..selectionLength) DO
IF (n←selection[i])=NIL THEN LOOP;
IF voice AND n.voice#selectedVoice THEN LOOP;
SetDirty[n.sync.time,n.sync.time];
IF sync=NIL THEN BEGIN sync←n.sync; LOOP; END;
c ← Note.FindChord[n];
FOR j IN [0..chordLength) DO
IF c=NIL AND j#0 THEN EXIT;
IF c#NIL THEN n ← c.note[j];
IF n=NIL THEN EXIT;
Sync.AddNote[sync,n];
ENDLOOP;
ENDLOOP;
Piece.CleanUpSyncs[score];
END;

ClearSync:PUBLIC PROCEDURE =
BEGIN
n:NotePTR;
sync:SyncPTR;
i,j:CARDINAL;
first:BOOLEAN ← TRUE;
IF lineSelect THEN FOR i IN [0..scoreLength) DO
IF score[i].time<select1 OR score[i].time>select2 THEN LOOP;
FOR j DECREASING IN [0..syncLength) DO
IF score[i].event[j]=NIL THEN EXIT;
sync ← Utility.NewSync[];
sync.time ← score[i].time;
Piece.AddSync[score,sync];
Sync.AddNote[sync,score[i].event[j]];
ENDLOOP;
ENDLOOP;
IF NOT lineSelect THEN FOR i IN [0..selectionLength) DO
IF (n←selection[i])=NIL THEN LOOP;
IF voice AND n.voice#selectedVoice THEN LOOP;
SetDirty[n.sync.time,n.sync.time];
sync ← Utility.NewSync[];
sync.time ← n.sync.time;
Piece.AddSync[score,sync];
Sync.AddNote[sync,n];
ENDLOOP;
Piece.CleanUpSyncs[score];
IF lineSelect THEN SetDirty[select1,select2];
END;

MakeTie:PUBLIC PROCEDURE =
BEGIN
n:NotePTR;
ties:ARRAY [0..10) OF RECORD[n1,n2:NotePTR];
ties ← ALL[[NIL,NIL]];
--build up matching pairs
FOR i:CARDINAL IN [0..selectionLength) DO
IF selection[i]=NIL THEN LOOP;
FOR j:CARDINAL IN [0..10) DO
IF ties[j].n1=NIL THEN {ties[j].n1←selection[i]; EXIT};
IF ties[j].n1.pitch#selection[i].pitch THEN LOOP;
IF ties[j].n1.sync=selection[i].sync THEN {flash←TRUE; RETURN};
IF ties[j].n2#NIL THEN {flash←TRUE; RETURN};
ties[j].n2 ← selection[i];
EXIT; ENDLOOP;
ENDLOOP;
IF ties[0].n1=NIL THEN {flash←TRUE; RETURN};
-- if there are only two notes, we ignore pitch constraints
IF ties[0].n2=NIL AND ties[1].n2=NIL AND ties[2].n1=NIL THEN {
ties[0].n2 ← ties[1].n1;
ties[1].n1 ← NIL};
-- check that there are an even number of pitches
FOR i:CARDINAL IN [0..10) DO
IF ties[i].n1=NIL THEN EXIT;
IF ties[i].n2=NIL THEN {flash←TRUE; RETURN};
ENDLOOP;
-- tie the pairs together
FOR i:CARDINAL IN [0..10) DO
IF ties[i].n1=NIL THEN EXIT;
IF ties[i].n1.sync.time>ties[i].n2.sync.time THEN
{n←ties[i].n1; ties[i].n1←ties[i].n2; ties[i].n2←n};
IF ties[i].n1.tied THEN {
(n←Note.GetBackTie[ties[i].n1]).tie←NIL;
SetDirty[n.sync.time,n.sync.time]};
IF (n←ties[i].n2.tie)#NIL THEN {
n.tied←FALSE;
SetDirty[n.sync.time,n.sync.time]};
ties[i].n2.tie ← ties[i].n1;
ties[i].n1.tied ← TRUE;
ties[i].n2.tieHeight ← -10;
SetDirty[ties[i].n1.sync.time,ties[i].n2.sync.time];
ENDLOOP;
END;

ClearTie:PUBLIC PROCEDURE =
BEGIN
n:NotePTR;
i,j:CARDINAL;
IF lineSelect
THEN FOR i IN [0..scoreLength) DO
IF score[i].time<select1 THEN LOOP;
IF score[i].time>select2 THEN EXIT;
FOR j IN [0..syncLength) DO
IF (n←score[i].event[j])=NIL THEN EXIT;
IF n.tie=NIL THEN LOOP;
n.tie.tied ← FALSE;
n.tie ← NIL;
ENDLOOP;
ENDLOOP
ELSE FOR i IN [0..selectionLength) DO
IF (n←selection[i])=NIL THEN LOOP;
IF n.tie=NIL THEN LOOP;
SetDirty[n.sync.time,n.sync.time];
SetDirty[n.tie.sync.time,n.tie.sync.time];
n.tie.tied ← FALSE;
n.tie ← NIL;
ENDLOOP;
IF lineSelect THEN SetDirty[select1,select2];
END;

--******************************************************************
--chords
--******************************************************************

MakeChord
:PUBLIC PROCEDURE =
BEGIN
n:NotePTR;
begin,end:Time;
chord:ChordPTR;
beam:BeamPTR←NIL;
sync:SyncPTR←NIL;
count:CARDINAL←0;
--is this a legal chording?
begin ← EndOfScore[];
end ← -1;
FOR i:CARDINAL IN [0..selectionLength) DO
IF (n←selection[i])=NIL THEN LOOP;
IF voice AND n.voice#selectedVoice THEN LOOP;
begin ← MIN[begin,n.sync.time];
end ← MAX[end,n.sync.time];
count ← count+1;
ENDLOOP;
IF ABS[end-begin]>20 OR count<2 THEN {flash←TRUE; RETURN};
--make the new chord
chord ← Utility.NewChord[];
FOR i:CARDINAL IN [0..selectionLength) DO
IF (n←selection[i])=NIL THEN LOOP;
IF voice AND n.voice#selectedVoice THEN LOOP;
SetDirty[n.sync.time,n.sync.time];
Chord.AddNote[chord,n];
ENDLOOP;
Chord.SetDefaultStem[chord];
Piece.CleanUpSyncs[score];
END;

ClearChord:PUBLIC PROCEDURE =
BEGIN
n:NotePTR;
c:ChordPTR;
i,j:CARDINAL;
IF lineSelect THEN {
FOR i DECREASING IN [0..scoreLength) DO
IF score[i].time < select1 OR score[i].time > select2 THEN LOOP;
FOR j IN [0..syncLength) DO
IF (n←score[i].event[j])=NIL THEN EXIT;
IF voice AND n.voice#selectedVoice THEN LOOP;
c ← Note.FindChord[n];
IF c#NIL THEN Chord.RemoveNote[c,n]; -- NIL’s n.beam
ENDLOOP;
ENDLOOP};
IF NOT lineSelect THEN {
FOR i IN [0..selectionLength) DO
IF (n←selection[i])=NIL THEN LOOP;
IF voice AND n.voice#selectedVoice THEN LOOP;
SetDirty[n.sync.time,n.sync.time];
c ← Note.FindChord[n];
IF c#NIL THEN Chord.RemoveNote[c,n]; -- NIL’s n.beam
ENDLOOP};
IF lineSelect THEN SetDirty[select1,select2];
END;

--****************************************************************************
--
beams and n-tuplets
--****************************************************************************

MakeBeam
:PUBLIC PROCEDURE[beamed:BOOLEAN] =
BEGIN
b:BeamPTR ← Utility.NewBeam[];
c:ChordPTR;
n,tmp:NotePTR;
i:CARDINAL;
height,stem:INTEGER;
stemUp,first:BOOLEAN←TRUE;
FOR i IN [0..selectionLength) DO
IF (n←selection[i])=NIL THEN LOOP;
IF voice AND n.voice#selectedVoice THEN LOOP;
--
don’t beam quarters or above
IF beamed AND n.value<eighth THEN LOOP;
IF n.beam = b THEN LOOP; --
beaming a note in a chord already beamed
c ←Note.FindChord[n];
IF n.beam#NIL THEN Beam.Remove[n.beam,n,c,NIL];
IF c#NIL THEN BEGIN
tmp ← c.note[0];
Beam.AddChord[b,c];
IF first THEN BEGIN stemUp← c.stemUp; first ← FALSE;
[] ← Chord.Sort[c,NOT stemUp];
height←(IF stemUp THEN -1000 ELSE 1000);
b.staff ← c.note[0].staff; END;
IF beamed THEN c.stemUp←stemUp;
[] ← Chord.Sort[c,NOT stemUp];
tmp ← c.note[0];
IF c.stemUp AND height<Sheet.Height[tmp.sync.time,tmp.pitch,tmp.staff] OR
NOT c.stemUp AND height>Sheet.Height[tmp.sync.time,tmp.pitch,tmp.staff]
THEN height←Sheet.Height[tmp.sync.time,tmp.pitch,tmp.staff];
END;
IF c=NIL THEN BEGIN
Beam.AddNote[b,n];
IF first THEN BEGIN stemUp← n.stemUp; first ← FALSE;
height←(IF stemUp THEN -1000 ELSE 1000);
b.staff ← n.staff; END;
IF beamed THEN n.stemUp ← stemUp;
IF n.stemUp AND height<Sheet.Height[n.sync.time,n.pitch,n.staff]
OR NOT n.stemUp AND height>Sheet.Height[n.sync.time,n.pitch,n.staff]
THEN height ← Sheet.Height[n.sync.time,n.pitch,n.staff];
END;
ENDLOOP;
IF beamed THEN stem←34 ELSE stem←28;
b.height ← height-Sheet.Height[0,,b.staff]+(IF stemUp THEN stem ELSE -stem);
IF b.chord[1]=endOfBeam THEN {Utility.FreeBeam[@b]; RETURN};
SetDirty[b.sync1.time,b.sync2.time];
END;

MakeBeamOfBeams:PUBLIC PROCEDURE[beamed:BOOLEAN] =
--
CONTROL b beams beams together
BEGIN
n:NotePTR;
c:ChordPTR;
oldBeam:BeamPTR;
b:BeamPTR ← Utility.NewBeam[];
FOR i:CARDINAL IN [0..selectionLength) DO
IF (n←selection[i])=NIL THEN LOOP;
IF n.beam=NIL THEN LOOP;
IF NOT n.beam.beamed THEN {flash←TRUE; RETURN};
IF n.beam.beam#NIL THEN {flash←TRUE; RETURN};
ENDLOOP;
FOR i:CARDINAL IN [0..selectionLength) DO
IF (n←selection[i])=NIL THEN LOOP;
IF voice AND n.voice#selectedVoice THEN LOOP;
oldBeam ← n.beam;
IF oldBeam=b THEN LOOP;
IF oldBeam#NIL THEN BEGIN
Beam.AddBeam[b,oldBeam];
b.staff ← oldBeam.staff;
b.height ← oldBeam.height;
LOOP; END;
IF beamed AND n.value<eighth THEN LOOP;
IF (c←Note.FindChord[n])#NIL
THEN Beam.AddChord[b,c]
ELSE Beam.AddNote[b,n];
ENDLOOP;
IF beamed THEN FOR i:CARDINAL IN [0..beamLength) DO
IF b.chord[i]=endOfBeam THEN EXIT;
WITH ev:b.chord[i] SELECT FROM
beam => BEGIN
ev.b.height ← b.height;
ev.b.staff ← b.staff;
ev.b.tilt ← 0;
END;
ENDCASE;
ENDLOOP;
b.tilt ← 0;
SetDirty[b.sync1.time,b.sync2.time];
END;

ClearBeam:PUBLIC PROCEDURE =
BEGIN
c:ChordPTR;
b:BeamPTR;
n:NotePTR;
i,j:CARDINAL;
IF lineSelect THEN {
SetDirty[select1,select2];
FOR i IN [0..scoreLength) DO
IF score[i].time < select1 OR score[i].time > select2 THEN LOOP;
FOR j IN [0..syncLength) DO
IF (n←score[i].event[j])=NIL THEN EXIT;
IF voice AND n.voice#selectedVoice THEN EXIT;
c ←Note.FindChord[n];
b ← n.beam;
IF b#NIL THEN Beam.Remove[b,n,c,NIL];
ENDLOOP;
ENDLOOP};
IF NOT lineSelect THEN {
FOR i IN [0..selectionLength) DO
IF (n←selection[i])=NIL THEN LOOP;
IF voice AND n.voice#selectedVoice THEN LOOP;
SetDirty[n.sync.time,n.sync.time];
c ←Note.FindChord[n];
b ← n.beam;
IF b=NIL THEN LOOP;
Beam.Remove[b,n,c,NIL];
ENDLOOP};
END;

MakeNTuplet:PUBLIC PROCEDURE[n,a:INTEGER] =
BEGIN
i:CARDINAL;
b:BeamPTR←NIL;
newBeam:BOOLEAN ← FALSE;
IF lineSelect THEN {Heuristic.MakeNTuplets[a,n,select1,select2]; RETURN};
FOR i IN [0..selectionLength) DO
IF selection[i]=NIL THEN LOOP;
IF voice AND selection[i].voice#selectedVoice THEN LOOP;
IF b=NIL THEN b←selection[i].beam;
IF b=NIL OR selection[i].beam#b THEN newBeam ← TRUE;
ENDLOOP;
IF newBeam THEN {
Selection.MakeBeam[FALSE];
FOR i IN [0..selectionLength) DO
IF selection[i]=NIL THEN LOOP;
IF voice AND selection[i].voice#selectedVoice THEN LOOP;
b←selection[i].beam;
EXIT; ENDLOOP;
IF b#NIL THEN b.beamed←FALSE};
IF b=NIL THEN RETURN;
b.against ← n;
b.ntuple ← a;
b.invisible←FALSE;
SetDirty[b.sync1.time,b.sync2.time];
END;

HideNTuplets
:PUBLIC PROCEDURE[invisible:BOOLEAN] =
BEGIN
b:BeamPTR;
n:NotePTR;
i,j:CARDINAL;
IF lineSelect
THEN FOR i IN [0..scoreLength) DO
IF score[i]=NIL THEN LOOP;
IF score[i].time < select1 OR score[i].time > select2 THEN LOOP;
FOR j IN [0..syncLength) DO
IF (n←score[i].event[j])=NIL THEN EXIT;
IF voice AND n.voice#selectedVoice THEN LOOP;
FOR b←n.beam, b.beam WHILE b#NIL DO
IF b.ntuple=0 THEN LOOP;
b.invisible ← invisible;
ENDLOOP;
ENDLOOP;
ENDLOOP
ELSE FOR i IN [0..selectionLength) DO
IF (n←selection[i])=NIL THEN LOOP;
IF voice AND n.voice#selectedVoice THEN LOOP;
SetDirty[n.sync.time,n.sync.time];
FOR b←n.beam, b.beam WHILE b#NIL DO
IF b.ntuple=0 THEN LOOP;
b.invisible ← invisible;
ENDLOOP;
ENDLOOP;
IF lineSelect THEN SetDirty[select1,select2];
END;

MakeNTupletOfBeams
:PUBLIC PROCEDURE[n,a:INTEGER] =
BEGIN
t:NotePTR;
i:CARDINAL;
b:BeamPTR←NIL;
newBeam:BOOLEAN ← FALSE;
TopBeam:PROCEDURE[b:BeamPTR] RETURNS[BeamPTR] =
{IF b#NIL AND b.beam#NIL THEN RETURN[b.beam] ELSE RETURN[b]};
FOR i IN [0..selectionLength) DO
IF (t←selection[i])=NIL THEN LOOP;
IF voice AND t.voice#selectedVoice THEN LOOP;
IF b=NIL THEN b←TopBeam[t.beam];
IF b=NIL OR TopBeam[t.beam]#b THEN newBeam ← TRUE;
ENDLOOP;
IF newBeam THEN {
Selection.MakeBeamOfBeams[FALSE];
FOR i IN [0..selectionLength) DO
IF selection[i]=NIL THEN LOOP;
IF selection[i].beam=NIL THEN LOOP;
IF voice AND selection[i].voice#selectedVoice THEN LOOP;
b←TopBeam[selection[i].beam];
EXIT; ENDLOOP;
IF b#NIL THEN b.beamed←FALSE};
IF b=NIL THEN RETURN;
b.against ← n;
b.ntuple ← a;
b.invisible←FALSE;
SetDirty[b.sync1.time,b.sync2.time];
END;

Delete:PUBLIC PROCEDURE =
BEGIN
n:NotePTR;
IF lineSelect
THEN Piece.Delete[select1,select2]
ELSE FOR i:CARDINAL IN [0..selectionLength) DO
IF (n←selection[i])=NIL THEN LOOP;
IF voice AND n.voice#selectedVoice THEN LOOP;
SetDirty[n.sync.time,n.sync.time];
Selection.RemoveNote[n];
Utility.FreeNote[@n];
ENDLOOP;
END;

END.