.collect Array inside Ppar

hey, could someone please help me to collect the Array of Pborklund2 inside the Ppar? thanks a lot :slight_smile:

(
SynthDef(\testMono, {
	var trig = \trig.tr(1);
	var gainEnv = EnvGen.ar(Env.perc(0.01,0.3), trig);
	var sig = SinOscFB.ar(\freq.kr(150), 0.6);
	sig = sig * gainEnv * \amp.kr(0.25);
	Out.ar(\out.kr(0), sig);
}).add;
)

(
Pdef(\test,
	Ppar({|i|
		Pmono(\testMono,
			
			\a, Pseq([0.25, 1/3].flop, inf),
			//\a, [Pbjorklund2(5, 16, inf).asStream, Pbjorklund2(7, 16, inf).asStream],
			
			\dur, Pkey(\a).collect(_[i]),
			
			\amp, [0.25, 0.25].flop.collect(_[i]),
			\out, i,
	)} !2 )
).play(quant:1);
)

This strikes me as a design problem. It doesn’t quite make sense to produce parallel events with arrays of data, then extract only one column from these arrays.

Shorter, simpler approach:

(
var a = [Pbjorklund2(5, 16, inf), Pbjorklund2(7, 16, inf)];
var amp = [0.25, 0.25];

Pdef(\test,
	Ppar({|i|
		Pmono(\testMono,
			// note that a[i] is a pattern
			// so, referencing it in a Pbind/Pmono,
			// will behave just like writing a pattern into a Pbind/Pmono
			\dur, a[i],
			\amp, amp[i],
			\out, i,
		)
	} !2 )
).play(quant:1);
)

That is, we see a lot of Pbind(\xyz, Pthingamajig(...)) and then you might think that the only way to get patterns into Pbind is to write them within the parens. Not true – you can refer to patterns saved in variables or collections as well.

hjh

thanks for your help this makes totally sense.

the thought of using .collect comes from the idea of having the possibility to change the different arguments of the running synth with Envelopes inside a Task or Routine (with im not familiar with enough atm) on a macro level of composition. maybe you remember the ~transC function we once talked about in another thread. But with patterns this makes probably no sense.

(
~noteData = (
	Pbind(
		\dur, Pdefn(\durs, [0.5, 0.667]),
		\freq, Pdefn(\freqs, [125, 130])
	)
);

Pdef(\test,
	Ppar({|i|
		Pmono(\test,

			\dur, Pkey(\dur).collect(_[i]),
			\freq, Pkey(\freq).collect(_[i]),
			\out, i,

		)
	} !2) <> ~noteData;
);
)

(
r = Routine({
	
	Pdef(\test).play(quant:1);

	10.wait;

	loop {
		
		Pdefn(\freqs, Env([[125, 130], [145, 165]], 10));

		10.wait;
		
		Pdefn(\freqs, Env([[145, 165], [125, 130]], 10));

		10.wait;
	}

}).play(AppClock, quant:1)
)

The problem with this design is:

  • You have n patterns inside the Ppar.
  • Each individual pattern produces n values for one parameter, and then uses .collect to choose one.

This is O(n^2) to produce a number of values proportional to O(n) – very inefficient, and as n gets larger, the inefficiency will get worse at an ever increasing rate. Think carefully whether there’s really a good reason to do it this way – while O(n) is not always possible, O(n^2) is usually a bad sign in terms of efficiency.

You could easily have an array of patterns that are referring to the macro control data, and use one pattern per member of Ppar.

Or, if you needed to hot-swap some of the parameter patterns, you could have an array of pattern proxies and embed one proxy per Ppar member.

~durs = Array.fill(2, { PatternProxy(Pexprand(0.1, 0.5, inf)) });

Pdef(\test,
	Ppar({|i|
		Pmono(\test,
			\dur, ~durs[i],
			\freq, ... similar, etc...,
			\out, i,
		)
	} !2)
);

// now change the second one's rhythm
~durs[1].source = Pwhite(1, 7, inf) * 0.125;

hjh

thanks for the explanation. each Ppar will probably stay with a stereo pair (n=2) of Patterns inside it. But thanks for pointing out the ineffiency. I think some of the parameters should have a gradually transition via an Envelope from Pdef A to Pdef B and some are better hot-swapped so thanks for the PatternProxy idea.