-- Author: John Maxwell
-- Last Edited by: Maxwell, November 22, 1983 12:47 pm

DIRECTORY
	Beam USING [AddItem, Free, New, RemoveItem, SetStems], 
	Chord USING [AddNote, New, RemoveNote, SetDefaultStem, Sort], 
	Event USING [AddNote, Sync], 
   Graphics USING [DrawChar, SetCP], 
	Heuristic USING [MakeNTuplets], 
	MusicDefs, 
	Note USING [FindChord, Free, GetBackTie, InVoice], 
	Piece USING [AddEvent, CleanUpEvents, Overflow, Replace], 
	Real USING [FixI], 
	Selection USING [Clear, Draw, MakeBeam, MakeBeamOfBeams, RemoveNote], 
	Sheet USING [Height, HiLite, Map, MapNote, NextStaff];
	
SelectionImpl: PROGRAM
  IMPORTS Beam, Chord, Graphics, Heuristic, MusicDefs, Note, Piece, Real, Selection, Sheet, Event  
  EXPORTS Selection  = 
BEGIN
OPEN Graphics, MusicDefs;

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

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

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

selection: PUBLIC SelectionPTR ← zone.NEW[SelectionRec[100]];

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

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

AddNote: PUBLIC PROCEDURE[score: ScorePTR, n: NotePTR] = 
BEGIN
x, y: INTEGER;
IF selection.lineSelect THEN Clear[];
IF n = NIL OR score = NIL THEN RETURN;
IF selection.score # score THEN {selection.score ← score; selection.length ← 0};
IF ~Note.InVoice[n, score.sheet.voice] THEN RETURN;
IF selection.length = selection.max THEN RETURN;
FOR i: CARDINAL IN [0..selection.length) DO IF selection[i] = n THEN RETURN; ENDLOOP;
selection[selection.length] ← n;
selection.length ← selection.length+1;
selection.lineSelect ← FALSE;
[x, y] ← Sheet.MapNote[score.sheet, n];
Graphics.SetCP[score.sheet.context, x, y]; 
SetBrush[score, black, invert];
Graphics.DrawChar[score.sheet.context, 170C];
END;

RemoveNote: PUBLIC PROCEDURE[n: NotePTR] = 
BEGIN
x, y: INTEGER;
IF n = NIL THEN RETURN;
FOR i: CARDINAL IN [0..selection.length) DO 
    IF selection[i] # n THEN LOOP; 
    selection.length ← selection.length - 1;
    selection[i] ← selection[selection.length];
    selection[selection.length] ← NIL;
    SetBrush[selection.score, black, invert];
	 [x, y] ← Sheet.MapNote[selection.score.sheet, n];
    Graphics.SetCP[selection.score.sheet.context, x, y]; 
    Graphics.DrawChar[selection.score.sheet.context, 170C];
    EXIT; ENDLOOP;
END;

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

AddGreyLine: PUBLIC PROCEDURE[score: ScorePTR, time1, time2: Time] = 
BEGIN
IF selection.lineSelect AND selection.score2 # NIL 
	THEN Sheet.HiLite[selection.score2.sheet, grey, selection.greySelect1, selection.greySelect2] 
	ELSE Selection.Clear[];
selection.greySelect1 ← time1;
selection.greySelect2 ← time2;
selection.lineSelect ← TRUE;
IF score # NIL THEN Sheet.HiLite[score.sheet, grey, time1, time2];
END;

Draw: PUBLIC PROCEDURE = 
BEGIN
x, y: INTEGER;
IF selection.lineSelect THEN {
	IF selection.score # NIL THEN 
		Sheet.HiLite[selection.score.sheet, black, selection.select1, selection.select2];
	IF selection.score2 # NIL THEN 
		Sheet.HiLite[selection.score2.sheet, grey, selection.greySelect1, selection.greySelect2];
	RETURN};
IF selection.length = 0 OR selection.score = NIL THEN RETURN;
SetBrush[selection.score, black, invert];
FOR i: CARDINAL IN [0..selection.length) DO
    IF selection[i] = NIL THEN LOOP;
    IF ~Note.InVoice[selection[i], selection.score.sheet.voice] THEN LOOP;
    [x, y] ← Sheet.MapNote[selection.score.sheet, selection[i]];
    Graphics.SetCP[selection.score.sheet.context, x, y];
    Graphics.DrawChar[selection.score.sheet.context, 170C];
    ENDLOOP;
END;

Enumerate: PUBLIC PROCEDURE[proc: PROC[ScorePTR, NotePTR]] = 
BEGIN OPEN selection; -- ! ! !
IF score = NIL THEN RETURN;
IF lineSelect
	THEN FOR i: CARDINAL DECREASING IN [0..score.length) DO
		IF score[i].time < select1 OR score[i].time > select2 THEN LOOP;
		IF score[i].type # sync THEN LOOP;
	    FOR j: CARDINAL DECREASING IN [0..Event.Sync[score[i]].length) DO
			proc[score, Event.Sync[score[i]].note[j]];
			ENDLOOP;
		ENDLOOP
	ELSE FOR i: CARDINAL IN [0..selection.length) DO
	    IF ~Note.InVoice[selection[i], score.sheet.voice] THEN LOOP;
	    proc[score, selection[i]];
	    SetDirty[score, selection[i].sync.time, selection[i].sync.time];
	    ENDLOOP;
IF lineSelect THEN SetDirty[score, select1, select2];
END;

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

SetNoteValue: PUBLIC PROCEDURE[v: NoteValue, dots: INTEGER] = 
BEGIN
NoteValue: PROC[score: ScorePTR, n: NotePTR] = BEGIN
	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 RETURN;
	Beam.RemoveItem[score, n.beam, IF n.chord = NIL THEN [note[n]] ELSE [chord[n.chord]]];  
	END;
Enumerate[NoteValue];
END;

SetRest: PUBLIC PROCEDURE[rest: BOOLEAN] = 
BEGIN
Rest: PROC[score: ScorePTR, n: NotePTR] = BEGIN n.rest ← rest; END;
Enumerate[Rest];
END;

SetGrace: PUBLIC PROCEDURE[grace: BOOLEAN] = 
BEGIN
Grace: PROC[score: ScorePTR, n: NotePTR] = BEGIN n.grace ← grace; END;
Enumerate[Grace];
END;

SetStaff: PUBLIC PROCEDURE[staff: CARDINAL] = 
BEGIN 
Staff: PROC[score: ScorePTR, n: NotePTR] = BEGIN
	Count: PROCEDURE[s: CARDINAL, t: Time] RETURNS[j: CARDINAL] = INLINE {
	j ← 0; FOR i: CARDINAL IN [0..s) DO j ← Sheet.NextStaff[score.sheet, j, t]; ENDLOOP};
	n.staff ← Count[staff, n.sync.time];
	IF n.beam # NIL THEN Beam.SetStems[score.sheet, n.beam];
	END;		
IF selection.score = NIL THEN RETURN;
IF staff = 0 THEN {selection.score.flash ← TRUE; RETURN} ELSE staff ← staff-1;
Enumerate[Staff];
END;

SetStem: PUBLIC PROCEDURE[stemUp: BOOLEAN] = 
BEGIN
Stem: PROC[score: ScorePTR, n: NotePTR] = BEGIN
	b: BeamPTR;
	c: ChordPTR;
	x, y, xb, yb, d: INTEGER;
	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 RETURN;
	[x, y] ← Sheet.Map[score.sheet, n.sync.time, n.pitch, n.staff];
	[xb, yb] ← Sheet.Map[score.sheet, 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]);
	END;  
Enumerate[Stem];
END;

Transpose: PUBLIC PROCEDURE[halfsteps: INTEGER] = 
BEGIN
Pitch: PROC[score: ScorePTR, n: NotePTR] = BEGIN
	n.pitch ← n.pitch+halfsteps;
   IF (halfsteps MOD 12) # 0 THEN {n.spelled ← inKey; n.show ← FALSE};
	END;  
Enumerate[Pitch];
END;

Delete: PUBLIC PROC = 
BEGIN ENABLE Piece.Overflow => ERROR;
n: NotePTR;
IF selection.score = NIL THEN RETURN;
IF selection.lineSelect 
	THEN Piece.Replace[selection.score, NIL, selection.select1, selection.select2]
	ELSE FOR i: CARDINAL IN [0..selection.length) DO
		IF (n ← selection[i]) = NIL THEN LOOP;
		IF ~Note.InVoice[n, selection.score.sheet.voice] THEN LOOP;
		SetDirty[selection.score, n.sync.time, n.sync.time];
		Selection.RemoveNote[n];
		Note.Free[selection.score, n];
		ENDLOOP;
END;

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

MakeSync: PUBLIC PROCEDURE = 
BEGIN
n: NotePTR;
c: ChordPTR;
end: Time ← -1;
sync: SyncPTR ← NIL;
begin: Time ← LAST[Time];
IF selection.score = NIL THEN RETURN;
FOR i: CARDINAL IN [0..selection.length) 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 {selection.score.flash ← TRUE; RETURN};
FOR i: CARDINAL IN [0..selection.length) DO
	IF (n ← selection[i]) = NIL THEN LOOP;
	IF ~Note.InVoice[n, selection.score.sheet.voice] THEN LOOP;
	SetDirty[selection.score, n.sync.time, n.sync.time]; 
	IF sync = NIL THEN BEGIN sync ← n.sync; LOOP; END;
	c ← n.chord;
	FOR j: CARDINAL IN [0..(IF c = NIL THEN 1 ELSE c.length)) DO
		IF c = NIL AND j # 0 THEN EXIT;
		IF c # NIL THEN n ← c.note[j];
		IF n = NIL THEN EXIT;
		sync ← Event.AddNote[selection.score, sync, n];
		ENDLOOP;
	ENDLOOP;
Piece.CleanUpEvents[selection.score];
END;

ClearSync: PUBLIC PROCEDURE = 
BEGIN
Sync: PROC[score: ScorePTR, n: NotePTR] = BEGIN
	sync: SyncPTR;
	sync ← zone.NEW[EventRec.sync[10]];
	sync.time ← n.sync.time;
	sync ← Event.AddNote[score, sync, n];
	[] ← Piece.AddEvent[score, sync];
	END;
IF selection.score = NIL THEN RETURN;
Enumerate[Sync];
Piece.CleanUpEvents[selection.score];
END;

-- ******************************************************************
-- ties
-- ******************************************************************

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..selection.length) 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 {selection.score.flash ← TRUE; RETURN};
		IF ties[j].n2 # NIL THEN {selection.score.flash ← TRUE; RETURN};
		ties[j].n2 ← selection[i];
		EXIT; ENDLOOP;
	ENDLOOP;
IF ties[0].n1 = NIL THEN {selection.score.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 {selection.score.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[selection.score, ties[i].n1]).tie ← NIL;
		SetDirty[selection.score, n.sync.time, n.sync.time]};
	IF (n ← ties[i].n2.tie) # NIL THEN {
		n.tied ← FALSE;
		SetDirty[selection.score, n.sync.time, n.sync.time]};
	ties[i].n2.tie ← ties[i].n1;
	ties[i].n1.tied ← TRUE;
	ties[i].n2.tieHeight ← -10;
	SetDirty[selection.score, ties[i].n1.sync.time, ties[i].n2.sync.time];
	ENDLOOP;
END;

ClearTie: PUBLIC PROCEDURE = 
BEGIN
UnTie: PROC[score: ScorePTR, n: NotePTR] = BEGIN
	IF n.tie # NIL THEN n.tie.tied ← FALSE;
	n.tie ← NIL;
	END;
Enumerate[UnTie];
END;

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

MakeChord: PUBLIC PROC = 
BEGIN
n: NotePTR;
end: Time ← -1;
chord: ChordPTR;
beam: BeamPTR ← NIL;
sync: EventPTR ← NIL;
count: CARDINAL ← 0;
begin: Time ← LAST[Time];
-- is this a legal chording?
FOR i: CARDINAL IN [0..selection.length) DO
    IF (n ← selection[i]) = NIL THEN LOOP;
    IF ~Note.InVoice[n, selection.score.sheet.voice] 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 {selection.score.flash ← TRUE; RETURN};
-- make the new chord
chord ← Chord.New[selection.score, selection.length+2];
FOR i: CARDINAL IN [0..selection.length) DO
    IF (n ← selection[i]) = NIL THEN LOOP;
    IF ~Note.InVoice[n, selection.score.sheet.voice] THEN LOOP;
	 SetDirty[selection.score, n.sync.time, n.sync.time];
    chord ← Chord.AddNote[selection.score, chord, n];
    ENDLOOP;
Chord.SetDefaultStem[selection.score.sheet, chord];
Piece.CleanUpEvents[selection.score];
END;

ClearChord: PUBLIC PROCEDURE = 
BEGIN
UnChord: PROC[score: ScorePTR, n: NotePTR] = BEGIN
	c: ChordPTR ← Note.FindChord[n];
	IF c # NIL THEN Chord.RemoveNote[score, c, n];
	END;
Enumerate[UnChord];
END;

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

MakeBeam: PUBLIC PROCEDURE[beamed: BOOLEAN] = 
BEGIN
b: BeamPTR;
c: ChordPTR;
n, tmp: NotePTR;
score: ScorePTR ← selection.score;
sheet: SheetPTR ← score.sheet;
height, stem: INTEGER;
stemUp, first: BOOLEAN ← TRUE;
b ← Beam.New[selection.score, selection.length + 2];
b.beamed ← beamed;
FOR i: CARDINAL IN [0..selection.length) DO
   IF (n ← selection[i]) = NIL THEN LOOP;
   IF ~Note.InVoice[n, score.sheet.voice] 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.RemoveItem[score, n.beam, IF c = NIL THEN [note[n]] ELSE [chord[c]]];
   IF c # NIL THEN BEGIN
	    tmp ← c.note[0];
	    b ← Beam.AddItem[score, b, [chord[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[sheet, tmp.sync.time, tmp.pitch, tmp.staff] OR
	    NOT c.stemUp AND height > Sheet.Height[sheet, tmp.sync.time, tmp.pitch, tmp.staff]
		THEN height ← Sheet.Height[sheet, tmp.sync.time, tmp.pitch, tmp.staff];
	    END;
   IF c = NIL THEN BEGIN
	    b ← Beam.AddItem[score, b, [note[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[sheet, n.sync.time, n.pitch, n.staff]
	       OR NOT n.stemUp AND height > Sheet.Height[sheet, n.sync.time, n.pitch, n.staff]
		THEN height ← Sheet.Height[sheet, n.sync.time, n.pitch, n.staff];
	    END;
   ENDLOOP;
IF beamed THEN stem ← 34 ELSE stem ← 28;
b.height ← height-Sheet.Height[sheet, 0, , b.staff]+(IF stemUp THEN stem ELSE -stem);
IF b.chord[1] = endOfBeam THEN {Beam.Free[score, b]; RETURN};
SetDirty[score, b.sync1.time, b.sync2.time];
END;

MakeBeamOfBeams: PUBLIC PROCEDURE[beamed: BOOLEAN] = 
-- CONTROL b beams beams together
BEGIN
n: NotePTR;
c: ChordPTR;
oldBeam: BeamPTR;
score: ScorePTR ← selection.score;
b: BeamPTR ← Beam.New[score, 8];
b.beamed ← beamed;
FOR i: CARDINAL IN [0..selection.length) DO
	IF (n ← selection[i]) = NIL THEN LOOP;
	IF n.beam = NIL THEN LOOP;
	IF NOT n.beam.beamed THEN {score.flash ← TRUE; RETURN};
	IF n.beam.beam # NIL THEN {score.flash ← TRUE; RETURN};
	ENDLOOP;
FOR i: CARDINAL IN [0..selection.length) DO
	IF (n ← selection[i]) = NIL THEN LOOP;
	IF ~Note.InVoice[n, score.sheet.voice] THEN LOOP;
	oldBeam ← n.beam;
	IF oldBeam = b THEN LOOP;
	IF oldBeam # NIL THEN BEGIN
		b ← Beam.AddItem[score, b, [beam[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 b ← Beam.AddItem[score, b, [chord[c]]]
		ELSE b ← Beam.AddItem[score, b, [note[n]]];
	ENDLOOP;
IF beamed THEN FOR i: CARDINAL IN [0..b.length) DO
	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[score, b.sync1.time, b.sync2.time];
END;
 
ClearBeam: PUBLIC PROCEDURE = 
BEGIN
UnBeam: PROC[score: ScorePTR, n: NotePTR] = BEGIN
	IF n.beam # NIL THEN Beam.RemoveItem[score, n.beam, 
		IF n.chord # NIL THEN [chord[n.chord]] ELSE [note[n]]];
	END;
Enumerate[UnBeam];
END;

MakeNTuplet: PUBLIC PROCEDURE[n, a: INTEGER] = 
BEGIN
b: BeamPTR ← NIL;
newBeam: BOOLEAN ← FALSE;
IF selection.score = NIL THEN RETURN;
IF selection.lineSelect THEN {
	Heuristic.MakeNTuplets[selection.score, a, n, selection.select1, selection.select2]; 
	RETURN};
FOR i: CARDINAL IN [0..selection.length) DO
	IF ~Note.InVoice[selection[i], selection.score.sheet.voice] 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: CARDINAL IN [0..selection.length) DO
		IF ~Note.InVoice[selection[i], selection.score.sheet.voice]  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[selection.score, b.sync1.time, b.sync2.time];
END;

HideNTuplets: PUBLIC PROCEDURE[invisible: BOOLEAN] = 
BEGIN
Hide: PROC[score: ScorePTR, n: NotePTR] = BEGIN
	FOR b: BeamPTR ← n.beam, b.beam WHILE b # NIL DO
		IF b.ntuple = 0 THEN LOOP;
		b.invisible ← invisible;
		ENDLOOP;
	END;
Enumerate[Hide];
END;

MakeNTupletOfBeams: PUBLIC PROCEDURE[n, a: INTEGER] = 
BEGIN
t: NotePTR;
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: CARDINAL IN [0..selection.length) DO
	IF (t ← selection[i]) = NIL THEN LOOP;
	IF ~Note.InVoice[t, selection.score.sheet.voice] 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: CARDINAL IN [0..selection.length) DO
		IF selection[i] = NIL THEN LOOP;
		IF selection[i].beam = NIL THEN LOOP;
		IF ~Note.InVoice[selection[i], selection.score.sheet.voice] 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[selection.score, b.sync1.time, b.sync2.time];
END;

END.