Synchronizing patterns (again)

Dear community,

It’s a recurring question, but each case offering a different solution, so I’d like to submit this one:


///Two sequences

(Pbindef(\synt1,
	\dur, 0.7,
	\midinote, Pseq((60, 62..88), inf),
	\amp, 0.5,
	\pan, -1,
);
Pbindef(\synt2,
	\dur, 0.7,
	\midinote, Pseq((60, 62..86)+1, inf),
	\amp, 0.5,
	\pan, 1,
))

///Start synchronisation

(
Pbindef(\synt1).play(quant: 0);
Pbindef(\synt2).play(quant: 0)
)

///De-correlate both synths
(
Pbindef(\synt1, \dur, 0.6);
Pbindef(\synt2, \dur, 0.8)
)

// How to synchronize them again without using stop/play in order not to interumpt the stream of notes ?

(
Pbindef(\synt1, \dur, 0.8);
Pbindef(\synt2, \dur, 0.8);
)

//Something like that ? But is it possible to not have a so long pause ?

// 1. pause
(Routine({
	(Pbindef(\synt1).pause;
		Pbindef(\synt2).pause;
	);

	0.8.wait;

	(Pbindef(\synt1, \dur, 0.5).resume;
		Pbindef(\synt2, \dur, 0.5).resume
	)
}).play)

Thanks for your advice !

You can also update the quant and set it back, I think this ignores the initial quant though.

f = {|u|
	var old = Pbindef(\synt1).quant;
	Pbindef(\synt1).quant = u;
	Pbindef(\synt2).quant = u;
	Pbindef(\synt1, \dur, u);
	Pbindef(\synt2, \dur, u);
	Pbindef(\synt1).quant = old;
	Pbindef(\synt2).quant = old;
}

f.(1)
1 Like

Thank you jordan, but this doesn’t seem to work, I have the same result as using:

(
Pbindef(\synt1, \dur, 1);
Pbindef(\synt2, \dur, 1);
)

Both synths remain decorrelated. Perhaps I missed something in your suggestion ?

They should be rhythmically synchronous, but the midi notes might end up being out of sync.

I’m not sure to understand the reason, but sometimes it works, sometimes not, depending the values:

(
Routine ({
	Pbindef(\synt1).play(quant: 0);
	Pbindef(\synt2).play(quant: 0);
	
	3.wait;
	
	Pbindef(\synt1, \dur, 0.6);
	Pbindef(\synt2, \dur, 0.8);
	
	3.wait;
	
	f = {|u|
		var old = Pbindef(\synt1).quant;
		Pbindef(\synt1).quant = u;
		Pbindef(\synt2).quant = u;
		Pbindef(\synt1, \dur, u);
		Pbindef(\synt2, \dur, u);
		Pbindef(\synt1).quant = old;
		Pbindef(\synt2).quant = old;
	};
	3.wait;
	
	f.(0.2); ////Works
	
	5.wait;
	
	Pbindef(\synt1, \dur, 1);
	Pbindef(\synt2, \dur, 1);
	
	5.wait;
	
	Pbindef(\synt1, \dur, 0.6);
	Pbindef(\synt2, \dur, 0.8);
	
	5.wait;
	
	f.(0.5); ////Doesn't work
	
	3.wait;
	
	f = {|u|
		var old = Pbindef(\synt1).quant;
		Pbindef(\synt1).quant = u;
		Pbindef(\synt2).quant = u;
		Pbindef(\synt1, \dur, u);
		Pbindef(\synt2, \dur, u);
		Pbindef(\synt1).quant = old;
		Pbindef(\synt2).quant = old;
	};
	
	3.wait;
	
	f.(0.5); ////Doesn't work
	
}).play

)

Would this be an option, or its important not to stop the pbindef, even it is unnoticeable?

// to re-sync
(
Pbindef(\synt1, \dur, 0.8).stop.play(quant: 0);
Pbindef(\synt2, \dur, 0.8).stop.play(quant: 0);
)

That’s actually the challenge, to synchronize and desynchronize a stream (in this example, simple ascending scales) without interrupting it. :slightly_smiling_face:

I am not sure I understand what you mean by synchronize, synchronize to what? The pattern keeps changing because the uneven number of steps, so you mean just sync the durations but keep going from where you are in the respective patterns or start from the beginning (like my code)?

That’s it. Sorry if it wasn’t clear enough.

synchronize and desynchronize several streams without interrupting them

And what do you mean by synchronize?

Well, be synchronous… Play simultaneously, at the same time, like two musical voices in homorhythm.

You could do something like this:

(
Pdef.removeAll;
Pdef(\s1,
	Pbindef(\synt1,
		\dur, 1,
		\midinote, Pseq((60, 62..88), inf),
		\amp, 0.5,
		\pan, -1,
	)
);
Pdef(\s2,
	Pchain(
		Prout({|ev|
			var lastDur = ev.dur;
			while { ev.notNil }
			{
				if ( (ev.dur != 1) && (t.beats%1).equalWithPrecision(0).not )
				{ ev.dur = t.beats.roundUp - t.beats };
				ev = ev.yield
			}
		}),
		Pbindef(\synt2,
			\dur, 1,
			\midinote, Pseq((60, 62..86)+1, inf),
			\amp, 0.5,
			\pan, 1,
		)
	)
)
)

///Start synchronisation
(
t = TempoClock(1/0.8);
Pdef(\s1).play(t);
Pdef(\s2).play(t)
)

// desync

Pbindef(\synt2, \dur, 0.8/0.6)

// resync
Pbindef(\synt2, \dur, 1)

// change tempo of clock and new synt1/synt2 ratio
(
t.tempo = 1.4;
Pbindef(\synt2, \dur, 0.23)
)

// resync
Pbindef(\synt2, \dur, 1)

Pdef.removeAl

l

1 Like

It’s really nice of you to propose this solution, thanks ! The Prout code is a bit difficult for me. But I ran it, programming the two synths in a similar way. The behavior is a little strange, the rhythm becoming ternary.

(
Pdef.removeAll;
Pdef(\s1,
	Pchain(
		Prout({|ev|
			var lastDur = ev.dur;
			while { ev.notNil }
			{
				if ( (ev.dur != 1) && (t.beats%1).equalWithPrecision(0).not )
				{ ev.dur = t.beats.roundUp - t.beats };
				ev = ev.yield
			}
		}),
	Pbindef(\synt1,
		\dur, 1,
		\midinote, Pseq((60, 62..88), inf),
		\amp, 0.5,
		\pan, -1,
	)
));
Pdef(\s2,
	Pchain(
		Prout({|ev|
			var lastDur = ev.dur;
			while { ev.notNil }
			{
				if ( (ev.dur != 1) && (t.beats%1).equalWithPrecision(0).not )
				{ ev.dur = t.beats.roundUp - t.beats };
				ev = ev.yield
			}
		}),
		Pbindef(\synt2,
			\dur, 1,
			\midinote, Pseq((60, 62..86)+1, inf),
			\amp, 0.5,
			\pan, 1,
		)
	)
)
)

///Start synchronisation
(
t = TempoClock(1/0.8);
Pdef(\s1).play(t);
Pdef(\s2).play(t)
)

// desync
(
Pbindef(\synt1, \dur, 0.6);
Pbindef(\synt2, \dur, 0.8);
)

// resync
(
Pbindef(\synt2, \dur, 0.7);
Pbindef(\synt1, \dur, 0.7)
)

So in my first example the dur of synt1 should always remain 1 and instead of chaning the dur you would change the tempo of the clock ‘t’. Here as mod’ed version where you can freely express the dur values like in your initial example. Synt2 resyncs to synt1 whenever they have the same dur value.

I don’t see any other option than Prout for this, so better make friends with it:) The syntax is a little special, with the ev = ev.yield at the end and the while { ev.notnil } { }.

(
~synt1Dur = 0.7;
~nextSynt1Time = 0.7;
Pdef.removeAll;
Pdef(\s1,
	Pbindef(\synt1,
	\dur, 0.7,
	\report, Pfunc{|ev| ~synt1Dur = ev.dur; ~nextSynt1Time = SystemClock.beats + ev.dur },
	\midinote, Pseq((60, 62..88), inf),
	\amp, 0.5,
	\pan, -1,
));
Pdef(\s2,
	Pchain(
		Prout({|ev|
			while { ev.notNil }
			{
				var dur = ~nextSynt1Time - SystemClock.beats;
				if (  (ev.dur == ~synt1Dur) && (dur > 0.01) )
				{ ev.dur = dur };
				ev = ev.yield
			}
		}),
		Pbindef(\synt2,
			\dur, 0.7,
			\midinote, Pseq((60, 62..86)+1, inf),
			\amp, 0.5,
			\pan, 1,
		)
	)
);
Pdef(\run, Ptpar([0, Pdef(\s1), 0, Pdef(\s2)], inf)).play
)

// sync both to new dur with quant 1 (the default)
(
Pbindef(\synt1, \dur, 0.8);
Pbindef(\synt2, \dur, 0.8)
)

// de-sync 
(
Pbindef(\synt2, \dur, 0.8);
Pbindef(\synt2, \dur, 0.6)
)

// resync with quant = 0, does not affect synt1, might affect how fast synt2 catches up
(
Pbindef(\synt1, \dur, 0.8).quant = 0;
Pbindef(\synt2, \dur, 0.8).quant = 0
)
1 Like

Here’s an alternate proposal - I’m just thinking about this from the perspective of having auto triggers de-sync and re-sync, and the solution for me is always to think about it in terms of modulating the PHASE of one of the triggers rather than the frequency, since you have a guarantee that if the phases and frequencies are the same you will be resynced. With that in mind, you could do something like:

(
    Pdefn(\phaseA, 0);
    Pdefn(\phaseB, Pseg([0, 6, 0], [60, 60]).repeat); // drift 6 beats in/out of alignment

    Pdef(\play, Ppar([
        Pbind(
            \scale, Scale.hexSus,
            \dur, 1/4, \legato, 1/4,
            \octave, 3, \degree, Pseq([0, 0, -1, 0, -2], inf),
            \lag, Pdefn(\phaseA).trace
        ),
        Pbind(
            \scale, Scale.hexSus,
            \dur, 3/4, \legato, 1/4,
            \octave, 3, \degree, Pseq([0, 0, -1, 0, -2], inf),
            \lag, Pdefn(\phaseB).trace
        ),
    ])).play
)
2 Likes

That seems like a neat trick, although I am not sure we are talking about the exact same thing.

Lets say we start 2 sequences with un-correlated durations, like \dur, 0.33 and \dur, 0.432. Then at some point we want to change both to the same dur, e.q \dur, 0.5. Now, how do we ensure they play in sync? I couldn’t figure out how to do that with your example.

Any difference in periodicity (in this case, duration) has to be translated to an increasing phase (lag). so:

Pbind(\dur, 1.0);
Pbind(\dur, 1.5;

is:

Pbind(\dur, 1.0 + 0.0);
Pbind(\dur, 1.0 + 0.5);

which is:

Pbind(\dur, 1.0, \lag, 0.0);
Pbind(\dur, 1.0, \lag, 0.5 * Pseg([0, 1000], [0, 1000])); // lag increases by 0.5 every 1 beat.

These two will be in phase at any point where EITHER both lags are the same value, OR where lag mod the period are the same value (in my above example, where (lagA % 1.0) == (lagB % 1.0)). This calculation gets complicated if they have different durations, or the durations are not fixed values.

I think this approach is the most straightforward if you want smaller out-of-sync drift behavior like my original example, and where it’s important that you end up with the same number of events output by both patterns - with this technique, if one pattern lags behind, you can have it “catch up” and play all the notes it missed, which may be musically desirable or not.

Yes, I see the advantage of the simplicity in your code, but as you said, the calculations can get complicated pretty fast if you change durations a lot. On the other hand I think my last example can handle any setting of the durations at any time without extra calculations of any type, and I don’t think it is that complicated, everything is handled by one Prout. I guess it is a matter of taste and needs:)

The original code example at the top of the page was like this:

  1. Both durations = 0.7
  2. Both durations = 0.8
  3. dur1 = 0.8, dur2 = 0.6 (but what if was not a ‘nice ratio’?)
  4. both durations = 0.8 - this is where the resync needs to happen

Wouldn’t this require a lot of calculations for every step in your example?

Thank you very much for your great suggests. scztt, the “phasing” works wonderfully, I keep this elegant idea !

Indeed, my question concerned about sync and desync between several voices following tempi evolutions, \dur being treated in this case as a metronome evolving differently for each voice. Something like this, following the last program proposed by Thor_Madsen :

(
Pbindef(\synt1, \dur, Env(#[0.5, 0.1, 0.3, 0.3, 0.1, 0.2, 0.5], #[5], \lin));
Pbindef(\synt2, \dur, Env(#[0.5, 0.7, 0.3, 0.3, 0.7, 0.2, 0.5], #[5], \lin));
)

(notice the little jump at the end :slight_smile: )

I will study this code and how integrate it into multiple streams…and I will offer a drink to Prout (and his partner Pchain) in order to make friend with them :upside_down_face:

1 Like