Delay two Pbind

Hello, I am new on SC, I would like to know how can I delay two Pseq, in thoses different ways:
1/ Pbind(x) start 0.5 sec after Pbind(w)
2/ Pbind(x) start 0.5 sec after Pbind(w) and then it delays itself of 0.05 sec more each time it’s repeating the note sequence ?
3/ The same but with a kind of a “loopable time ramp”: the delay from Pbind(x) to Pbind(w) goes from 0.1 sec to 10 sec in one minute and at the end of the ramp replay it again

Thanks!

( 
var w,x;

w=Pbind(
\type, \midi,
	\midicmd, \noteOn,\midiout, m,\chan,0,\midinote,Pseq([[72,69,64,60],[72,69,64,59],[72,69,62,57],[71,64,62,59]],inf),\dur,Pseq([2,1,2,1],inf));
x=Pbind(
\type, \midi,
	\midicmd, \noteOn,\midiout, m,\chan,1,\midinote,Pseq([[72,69,64,60],[72,69,64,59],[72,69,62,57],[71,64,62,59]],inf),\dur,Pseq([2,1,2,1],inf));

Ppar([w, x]).play;
)

Hello and welcome,

ad 1)

Ptpar([0, w, 0.5, x]).play;

ad 2)

e.g.

...
	\lag, Pstutter(4, Pseries(0, 0.05)),
	\midinote,Pseq([[72,69,64,60],[72,69,64,59],[72,69,62,57],[71,64,62,59]],inf),
...

Obviously here Pstutter’s repetition number is based on the fixed loop length. A dynamic loop length would require a slight adaption, i.e. coupling of those params.

ad 3)

Essentially Pseg should do what you want, e.g.

...
	\lag, Pstutter(4, Pseg(Pseq([0, 10], inf), Pseq([60, 10], inf))),
...

For a jump back e.g.

...
	\lag, Pstutter(4, Pseg(Pseq([0, 10], inf), Pseq([60, 0], inf))),
...

Thanks a lot! that’s really clear.
I have another difficulty dealing with several Pbind, here, I have 4 voices playing a 7/4 bar:

(
w=Pbind(
\type, \midi,
	\midicmd, \noteOn,\midiout, m,\chan,~chanw,\midinote, Pseq([71,71,70],inf),\sustain,Pseq([1,4,2],inf),\dur,Pseq([1,4,2],inf),\stretch,~stretch);

x=Pbind(
\type, \midi,
	\midicmd, \noteOn,\midiout, m,\chan,~chanx,\midinote, Pseq([67,66],inf),\sustain,Pseq([3,4],inf),\dur,Pseq([3,4],inf),\stretch,~stretch);

y=Pbind(
\type, \midi,
	\midicmd, \noteOn,\midiout, m,\chan,~chany,\midinote, Pseq([62,64,61],inf),\sustain,Pseq([1,2,4],inf),\dur,Pseq([1,2,4],inf),\stretch,~stretch);

z=Pbind(
\type, \midi,
	\midicmd, \noteOn,\midiout, m,\chan,~chanz,\midinote, Pseq([55,52,54],inf),\sustain,Pseq([1,2,4],inf),\dur,Pseq([1,2,4],inf),\stretch,~stretch);

Ppar([w,x,y,z]).play(TempoClock(0.7));
)

1/ Would it be possible to use one single Pbind for thoses 4 voices, using as a example [\midinote, \dur] ? My problem to find the good syntax is that each voice has its own note duration…

2/ If I want to randomise the lenght of the last quarter note (for all the 4 voices) how I have to proceed?
ex: on every new loop the 7th quarter note freeze randomly between 2,3,4,5 quarter note more, the overall time signature become a 8/4, 9/4 or 10/4 etc…

3/ Finally, using Ppar what is the best way to open and close each Pbinds on the fly?

Thanks!

Hm, I see some conflicting requirements – treat the four voices as a unit and be able to turn them on and off individually, and have separate voices for 7 beats and then unified at the end. Definitely makes things less than obvious.

Would it be possible to use one single Pbind for thoses 4 voices, using as a example [\midinote, \dur] ?

If you do that, then you can’t turn the individual voices on and off. So it’s probably better if you just forget this idea.

using Ppar what is the best way to open and close each Pbinds on the fly?

I’d use Pdefn to “inject” rests.

Pdefn(\dummyW, 0);  // and for x y z

w = Pbind(......, \dummy, Pdefn(\dummyW));

Pdefn(\dummyW, Rest(0));  // disable
Pdefn(\dummyW, 0);  // enable

In Ppar, you can’t remove and re-add a Pbind, but you can silence a running Pbind by getting a Rest object into it. The Pdefn is just a hook to let you change the value on demand.

If I want to randomise the length of the last quarter note (for all the 4 voices) how I have to proceed?

Possible but tricky. I have an idea but, without testing, I’m not sure it’ll work. I’m not at the computer right now, so I’ll have to try later.

hjh

ad 1) It’s not possible to ‘multi-node-expand’ the Pattern with an array value for ‘dur’. For good reason, because it would lead to ambiguities. The syntax [\midinote, \dur] is useful for something else: if you want to couple certain midinotes with certain durations, e.g. with Ptuple.

ad 2) As the answer to (1) is no, a different strategy has to be taken: data sharing http://doc.sccode.org/Tutorials/A-Practical-Guide/PG_06g_Data_Sharing.html

(
x = Pbind(
	\dur, Pseq([2, 1, 1, Prand([1/2, 1/3, 2/3], 1).collect { |x| ~a = x } ], 50) / 4,
	// shorter with partial application:
	// \dur, Pseq([2, 1, 1, Prand([1/2, 1/3, 2/3], 1).collect(~a=_) ], inf) / 4,
	\midinote, Pn(Pseries(55, Pwhite(1, 2), 12)),
	\legato, 0.5
);

y = Pbind(
	\dur, Pseq([1, 2, 1, Pfuncn { ~a }], 50) / 4, // Pfuncn defaults to length 1
	\midinote, Pn(Pseries(62, Pwhite(3, 6)/3, 12)),
	\legato, 0.5
);

z = Pbind(
	\dur, Pseq([1, 1, 2, Pfuncn { ~a }], 50) / 4,
	\midinote, Pn(Pseries(71, Pwhite(1, 2), 12)),
	\legato, 0.5
);
)

(
// ensure order of evaluation by slight time-shift, you could equalize with lag, but can be neglected with 0.0001 sec
d = 0.0001;
v = Ptpar([0, x, d*2, y, d*3, z]).play
)

ad 3) the premise must be changed: if you want to stop and resume Pbinds (to be exact: EventStreamPlayers) on the fly you should avoid Ppar / Ptpar! Instead you can start and stop single players, if you want to sync them you can play them on a grid by using the quant arg of method ‘play’.

(
p = Pbind(
	\dur, Pseq([2, 1, 1], 50) / 4,
	\midinote, Pn(Pseries(55, Pwhite(1, 2), 12)),
	\legato, 0.5
);

q = Pbind(
	\dur, Pseq([1, 2, 1], 50) / 4,
	\midinote, Pn(Pseries(62, Pwhite(3, 6)/3, 12)),
	\legato, 0.5
);

r = Pbind(
	\dur, Pseq([1, 1, 2], 50) / 4,
	\midinote, Pn(Pseries(71, Pwhite(1, 2), 12)),
	\legato, 0.5
);
)

(
x = p.play(quant: 1);
y = q.play(quant: 2);
z = r.play(quant: 1);
)

y.stop;

y.play(quant: 1);

z.stop;

z.play(quant: 1);

x.stop;
y.stop;
z.stop;

Already in this example you see that “measures” are synchronised, but not upward phrases.
But the even more tricky task in your use case would be to synchronise measures of dynamic length. I have no general solution for this at hand and today I’m to tired to think about it.
However there is another workaround: you could introduce mute variables, muting would not really stop the players but save you from sync quirks, in this case again Ppar would be fine again.

(
~mute1 = 0;
~mute2 = 0;
~mute3 = 0;

x = Pbind(
	\dur, Pseq([2, 1, 1, Prand([1/2, 1/3, 2/3], 1).collect { |x| ~a = x } ], 50) / 4,
	// shorter with partial application:
	// \dur, Pseq([2, 1, 1, Prand([1/2, 1/3, 2/3], 1).collect(~a=_) ], inf) / 4,
	\midinote, Pn(Pseries(55, Pwhite(1, 2), 12)),
	\amp, 0.1 * Pfunc { 1 - ~mute1 },
	\legato, 0.5
);

y = Pbind(
	\dur, Pseq([1, 2, 1, Pfuncn { ~a }], 50) / 4,
	\midinote, Pn(Pseries(62, Pwhite(3, 6)/3, 12)),
	\amp, 0.1 * Pfunc { 1 - ~mute2 },
	\legato, 0.5
);

z = Pbind(
	\dur, Pseq([1, 1, 2, Pfuncn { ~a }], 50) / 4,
	\midinote, Pn(Pseries(71, Pwhite(1, 2), 12)),
	\amp, 0.1 * Pfunc { 1 - ~mute3 },
	\legato, 0.5
);
)

(
// ensure order of evaluation by slight time-shift, you could equalize with lag, but can be neglected with 0.0001 sec
d = 0.0001;
v = Ptpar([0, x, d*2, y, d*3, z]).play
)



~mute2 = 1

~mute1 = 1

~mute2 = 0

~mute1 = 0

v.stop

Ah, haven’t seen your answer James, but this all gives a broad picture hopefully.

Agreed about data sharing. I’d wanted to check if Ppar would handle it without the offset, but now that I think about it, I’m pretty sure that the way you cited from the pattern guide is necessary. (If it hadn’t been necessary, I think I would have written the pattern guide example differently.)

I still prefer Rests over muting the amplitude, to stop the synths from playing completely. Same basic idea (external reference to control the pattern’s data) but different implementation of the “shush” behavior :wink:

hjh

Thanks both of you, I made a big step on Patterns. I still have a major problem, I only manage to make this data sharing works if the different Pbinds have the same number of steps (in \mininote and \dur) no matter if the global seq time is the same
It works:

(
w=Pbind(
\midinote, Pseq([74,76,75],inf),\dur, Pseq([1,3,Prand([3,5,7],1).collect { |x| ~a = x } ],inf));

x=Pbind(
\midinote, Pseq([[67,70,62],[67,70,62],[72,67,60]],inf),\dur,Pseq([2,2,Pfuncn({ ~a },1)],inf));
 
~run=Ppar([w,x]).play(TempoClock(0.7));
)

But I would like (same timing but just two events for x)

(
w=Pbind(\midinote, Pseq([74,76,75],inf),\dur, Pseq([1,3,Prand([3,5,7],1).collect { |x| ~a = x } ],inf));

x=Pbind(\midinote, Pseq([[67,70,62],[72,67,60]],inf),\dur,Pseq([4,Pfuncn({ ~a },1)],inf));

~run=Ppar([w,x]).play(TempoClock(0.7));
)

Thanks

I’d suggest you refer to the last example under http://doc.sccode.org/Tutorials/A-Practical-Guide/PG_06g_Data_Sharing.html#Communicating%20values%20between%20separate%20event%20patterns – this example uses Ptpar (not Ppar!) and a timing offset parameter to guarantee that the source of the shared data evaluates first.

With Ppar, (I think) you can’t be completely certain which of the subpatterns evaluates first. If the Pfunc dur “reader” happens to evaluate first, it will get old data from the last cycle, and then you’re out of sync.

Note that Daniel already told you about this:

// ensure order of evaluation by slight time-shift, you could equalize with lag, but can be neglected with 0.0001 sec
d = 0.0001;
v = Ptpar([0, x, d*2, y, d*3, z]).play

… but that seems to have been overlooked. (Understandable, as there are a lot of details here.)

hjh

ok now it works, the irregularities were coming from the sustain of the midi source, thanks

Glad to see it’s working.

I had already written this so I’m going to post it anyway.

If the code is not doing what you think it should be doing, then put in some debug or trace calls to get more visibility. (Also, here I’ve reformatted the code to make it friendlier to read.)

(
~a = nil;

w = Pbind(
	\midinote, Pseq([74, 76, 75], inf),
	\dur, Pseq([1, 3, Prand([3, 5, 7], 1).collect { |x| ~a = x }], inf).trace(prefix: "w dur: ")
);

x = Pbind(
	\midinote, Pseq([[67, 70, 62], [72, 67, 60]], inf),
	\dur, Pseq([4, Pfuncn({ ~a.debug("x retrieved") }, 1)], inf).trace(prefix: "x dur: ")
);

~run = Ppar([w, x]).play;
)

w dur: 1
x dur: 4
w dur: 3
x retrieved: nil  << HERE
w dur: 5

So… you’re expecting that if w and x are scheduled for the same time, then w will always wake up first, and populate ~a to be ready for x. But when the number of items is different, the debugging information shows this is not guaranteed.

Ptpar is the solution for this.

(
~a = nil;

w = Pbind(
	\midinote, Pseq([74, 76, 75], inf),
	\dur, Pseq([1, 3, Prand([3, 5, 7], 1).collect { |x| ~a = x }], inf).trace(prefix: "w dur: "),
	\timingOffset, 0.01
);

x = Pbind(
	\midinote, Pseq([[67, 70, 62], [72, 67, 60]], inf),
	\dur, Pseq([4, Pfuncn({ ~a.debug("x retrieved") }, 1)], inf).trace(prefix: "x dur: ")
);

~run = Ptpar([0, w, 0.01, x]).play;
)

w dur: 1
x dur: 4
w dur: 3
w dur: 7
x retrieved: 7  << 'w' did its bit first so now we are fine
x dur: 7

hjh

Just as a side-note, as you know with MIDI you don’t have the precision related to time-stamped messaging. However in the context of VSTPlugins Christof is thinking about implementing an analogue mechanism.

Last thing, I know to generate time ramped values on the server side (line, sweep, LFDNoise etc…) but how I do that on the language side? As an instance I would like to ramp the midi bend.
1 From 16383 to 100 in 10 sec
2 From 16383 to 100 in 10 sec, then from 100 to 8000 in 5 sec, then from 8000 to 15000 in 2 sec and so on
Thanks

(
//chord
x=Pbind(
\type, \midi,
	\midicmd,\noteOn,\midiout, m,\chan,0,\midinote, Pseq([[67,68,72,73]],inf),\dur,10).play;
//pitchbend for the chord
y=Pbind(
\type, \midi,
	\midicmd,\bend,\midiout, m,\chan,0,\dur,10,\val,//from 16383 to 100 in 10 sec
 ).play;

Ppar([x,y]).play(TempoClock(0.7));
)

I’ve already answered your originally 3rd question in my first post of this thread, you can use Pseg:

see also

I’ve also written a tutorial on this topic proposing a number of alternatives, see “Event patterns and LFOs” from the miSCellaneous_lib quark.

yes yes yes, sorry and thank you, so much information :slight_smile:

No worrys, pattern-land is huge :slight_smile:

To play a chord transposition sequence ?
This chord

 \midinote, Pseq([[ 74,73,69]],inf)  

In order to have:

\midinote, Pseq([ thischord*transposed one key down, thischord*transposed half a key up, and so on],inf);

Thanks

You can add patterns to patterns as well as to arrays:

Pbind(
	\midinote, [74, 73, 69] + Pseq([-2, 1], inf),
	\dur, 0.2
).play


Pbind(
	\midinote, Pstutter(3, Pseq((60..65), inf)) + [0, 7, 14] + Pseq([0, -3, 3], inf),
	\dur, 0.2
).play

Pstutter(3, Pseq((60..65), inf)) + [0, 7, 14] + Pseq([0, -3, 3], inf),

Or, I’m pretty sure this is equivalent:

(Pseq((60..65), inf) +.x Pseq([0, -3, 3], inf)) + [0, 7, 14],

hjh

Not exactly, the cross adverb has to be applied to finite streams, to get the same we can e.g. do

(
Pbind(
	\midinote, Pn(Pseq((60..65)) +.x Pseq([0, -3, 3])) + [0, 7, 14],
	\dur, 0.2
).play
)

I omitted this in order to avoid another layer of mysteriousness, but it’s nice indeed.

1 Like