Actually, on second thought, partitioning would be harder than it seems. (Say you want the random numbers between 3.0 and 3.2, and the total to be 10.0. If there are three random numbers, the largest you could get is 9.6; if there are four, the smallest would be 12 – so, there exists at least one constraint that is impossible to solve – therefore, a solution can’t be guaranteed, not without some heuristics to go outside the strict bounds.)
But then I thought of an alternate design.
“I would like to change the \revtime parameter in parallel, but with different Pwhites”
I finally realized that, effectively, this is dividing the timeline into blocks. Trying to have each parameter independently transition from one block to the next is messy – it would have been manageable for multiple parameters following the same rhythm, but when you introduced the idea of rhythmic polyphony, then it became extremely difficult.
Instead, you could have one parameter that identifies the parameter set for the current time block. Then, each parameter independently looks up the right source pattern based on that ID.
(
// to show the logic more clearly,
// I'll abstract the parameters out
// also scaling by 1/4 b/c I don't want to wait that long
var synthDurs = [Pwhite(1.5, 2, inf), Pwhite(2.5, 6, inf)] * 0.25;
var synthAmps = [Pwhite(0.05, 0.2, inf), Pwhite(0.6, 0.9, inf)];
var fxDurs = [Pwhite(3.0, 6.0, inf), Pwhite(6.0, 9.0, inf)] * 0.25;
// introduce another parameter so the shifts are easier to hear
var octaves = [5, 6];
p = Pchain(
Ppar([
// synth
Pbind(
\dur, Pswitch1(synthDurs, Pkey(\phraseIndex)),
\amp, Pswitch1(synthAmps, Pkey(\phraseIndex)),
\octave, Pswitch1(octaves, Pkey(\phraseIndex)),
\degree, Pwhite(-7, 0, inf),
\pan, -0.7
),
// fx - for laziness in the example,
// I'll just run another synth in the other channel
Pbind(
\dur, Pswitch1(fxDurs, Pkey(\phraseIndex)),
\amp, 0.15,
\octave, Pswitch1(octaves, Pkey(\phraseIndex)),
\degree, Pwhite(0, 7, inf),
\pan, 0.7
)
], 1),
// current time block is 0 or 1
Pbind(\phraseIndex, Pstep(Pseq([0, 1], inf), 5))
).play;
)
This sometimes happens in programming – the most convenient solution for a smaller-scale problem (in this case, time-constraining one stream of random durations) could scale up slightly (to the case of time-constraining two streams according to the same rhythm), but simply cannot scale up to a more general problem (time-constraining any number of streams with any number of simultaneous rhythms).
There’s not really any way out except for a redesign – but the benefit is that this new design is not conceptually more complex for 3 streams vs 300.
If you didn’t want to wrap them up in Ppar, you could also run a Pdef(\phraseControl, Pbind(\phraseIndex, Pseq(...), \dur, 20).collect { |ev| Pdefn(\phraseIndex, ev[\phraseIndex]); ev })
and then use Pdefn(\phraseIndex)
in any independently running pattern, anywhere.
hjh