Repeat N_times a pattern || Newbie Question

Hi everyone,
this one should be super simple…
I was wondering if there is a way, I’m sure there is … and which one is the best possible…
to choose how many time to repeat a Pbindef,
so when I have to schedule or recall the pattern using a midi controller I can just write the name of the func or element of a dictionary or Pbindef and the number of time I want to listen to the pattern…

// DOESN’T WORK

   (
    ~ph0func = { arg n;
    	Pbindef(\ph0,
        \instrument, \default,
        \note,Pseq([0,5,1,4,2,3],n.postln),
    	\dur,0.25
    ).play;
    };)

    ~ph0func.value(12);  // prints 12

// DOESN’T WORK

(
SynthDef(\instr,{|n =1,out =0, freq = 440|
	 Out.ar(out,SinOsc.ar(freq) * Line.kr(1, 0, 0.25, doneAction: Done.freeSelf))
}).add;
)

a = Synth(\instr);
(
    	Pbindef(\ph,
        \instrument, \instr,
	    \n, 4,
	\freq,Pseq([100,200,300],Pkey(\n)),
    	\dur,0.25
    ).play;
)
Pbindef(\ph, \n, 12).play;

Pbindef(\ph).stop;
Pbindef(\ph).clear;

Really thanks in advance for your time

evaluating ~ph0func over here prints:

12
-> Pbindef('ph0', 'instrument', 'default', 'note', Pseq([ 0, 5, 1, 4, 2, 3 ], 12), 'dur', 0.25)

and plays the sequence 12 times.

1 Like

Also note that you can use Pn, which applies to value and event patterns:

Pn(Pbind(\note, Pseq([0, 1]), \dur, 0.2), 5).play

2 Likes

Sorry,
my fault, I was in a hurry …
and you know in these cases what can happen

Thank you very much for the replies

Hey, I just found out there’s also this nice “shortcut” for Pn:

Pbindef(\test).repeat(2).play
1 Like

Thanks!
Supercool! :+1:
Exactly what I was looking for!

Beware that with Pdef/Pbindef this approach will generate a new player, and the Pdef/Pbindef is not in control of it.

Same happens if you do Pdef(\n).trace.play or any application of a FilterPattern on a PatternProxy, AFAICT.

So, if you do Pdef(\n).trace.play multiple times you will get multiple independent players, none of which can be stopped with Pdef(\n).stop

It looks like if you want to keep both Pbindef “single-player” and dynamically change repeats, you could probably setup a \repeat key and set it from outside.

Question: Is there any other recommended approach for FilterPatterning P(bin)defs?

1 Like

I do something like

Pdef(\top, Pchain(Pdef(\filter), Pdef(\sounds)))

With that you can change \filter to e.g. rep n times, even while \top is playing.

Pdef(\sounds, Pbind(\instrument, \default, \degree, Pwhite(1, 7)))
Pdef(\sounds).set(\dur, 0.5)

Pdef(\filter, Pbind()) // pass-through initially

Pdef(\top).play

Pdef(\filter, Pstutter(3, Pbind())) // start repeating each 3 times

Of course the above is trivially done in some other ways. But I do much more complicated filtering with the general structure from above, e.g. my filters merge some keys using custom merge functions… and the merged values are derived by blending presets stored in “sub-dictionaries” of Pdef.envirs. Currently I implement those “sub-dictionaries” with prefixes for the keys, e.g. p0_freq is the freq for preset 0, so that multiple presets can be displayed in the same EnvirGui. (These are partial presets, i.e. not every param needs to be defined in every preset, only what’s different/modded, that saves [a lot of] gui space.) I don’t have most of my framework encapsulated in classes to publish though because of the nuisance with recompiling the class library on Windows. (Except for a mini-DSL for specifying preset merging e.g. 0 %> 2 @% {33--67} means merge presets 0 and 2 [themselves defined by “sub-dictionaries”] with a blendFrac uniformly randomly generated between 0.33 and 0.67) So I only put in classes stuff that requires very few changes/debugging, presently, the rest is “some big Prouts”.

The empty Pbind above is where the presets are merged, e.g. you can do something like

Pdef(\filter, Pstutter(3, Pbind(\ctranspose, 24)))

just with the above, so it already works like a Pbindef in some ways, but merging only by overriding doesn’t get one very far.

(My beef with Pbindef [actually that happens in PbindProxy] is that it becomes conceptually obscure quickly if you [implicitly] add new keys with it. The new keys are added to the end of PbindProxy.pairs. But if you want to precalculate something in a new key and then set an existing key to that value… good luck doing that with Pbindef/PbindProxy. If you have Pchain of Pdefs instead, it’s much easier to keep the order of changes clear.)

2 Likes

Chaining: Pn(Pbind(),3) <> Pbind(\note,Pseq((0..2))) still doesn’t repeat those 3 notes 3 times. And I guess this is because Pchain is one level above the pattern Pn repeats. So, Pn sees only its empty Pbind, and not our note Pbind, which is merged after Pn has processed.

To me it looks like there’s no other way than enclosing our note Pbind in a Pn, which is btw what .repeat() does.

Yeah, actually I also think Pdefs are clearer than Pbindefs. They keep it simple to see which keys you put inside patterns… so I totally agree with you on this point … but the OP is using Pbindefs, hence my question. :slight_smile:

1 Like

That can’t work. You need a Plazy wapper if you want to read the repeats from the event itself because Pseq doesn’t accept a pattern for the repeats (and Pkey is a pattern). Alternatively, use PLseq. See more discussion/examples here. The whole list is being changed there, but the same applies to the repeat parameter.

Here’s the Plazy example adapted to something meaningful here (two Pseqs with different repeats):

(Pbindef(\twoseqs,
	\instrument, \default,
	\dur, 0.5, \reps1, 1, \reps2, 1,
	\midinote, Pn(PlazyEnvir({|reps1, reps2| Pseq([Pseq([44, 55], reps1), Pseq([66, 77], reps2)])}))));

Pbindef(\twoseqs).play;

Pbindef(\twoseqs, \reps1, 2);
Pbindef(\twoseqs, \reps2, 2);

I’m assuming the whole point of using Pbindef is that you want to be able to change the repeats “on the fly”, as the pattern is playing, but with your initial example that doesn’t make much sense, as you only have one sequence being repeated and you seem to want to change just how many times it plays. You don’t need Pbindef or Plazy for that since you decide from the start how many repeats there are, which I’m guessing why you’ve accepted @elgiano’s answer. The last example I gave is more of an “advanced version” of doing this change on the fly and/or as a “pattern filter”; it would work the same with chained Pdefs…

Setting just an initial value for reps is in fact much simpler, using a Pdef function… which gets evald in the event envir, i.e. basically/implicitly Plazy-like

(Pdef(\oneseq, { Pbind( // note the brace!
	\instrument, \default,
	\dur, Pseq(0.5!2, ~reps),
	\midinote, Pseq([44, 55], ~reps)) }));

Pdef(\oneseq).set(\reps, 2)
Pdef(\oneseq).play

Pdef(\oneseq).set(\reps, 3)
Pdef(\oneseq).play

Pdef(\oneseq).gui // because reps is read from the Pdef.evir, it shows up in gui

// same Pdef as above but using Pn for the reps:
(Pdef(\oneseq, { Pn(Pbind(
	\instrument, \default,
	\dur, Pseq(0.5!2),
	\midinote, Pseq([44, 55])),  ~reps) }));

// you can also set the reps on each play, without saving them in the envir
Pdef(\oneseq).set(\reps, nil)
Pdef(\oneseq).play(protoEvent: (reps: 2))

If you look in the source code of EventPatternProxy, the relevant superclass of Pdef, you’ll that it does

	source_ { arg obj;
		if(obj.isKindOf(Function)) // allow functions to be passed in
		{ pattern = PlazyEnvirN(obj) }

which is how the “brace magic” works. Note that without that (brace or Plazy) ~reps would get read from the current environment immediately when the Pdef is created, rather than when it is played.

1 Like

I use Pbindef (), only and exclusively because, in another project I had found myself well. Btw if it makes more sense to use Pdef (), why not… the important thing from my point of view is to be able to make the project works .
I was convinced that Pdef () were extremely designed only for those who do livecoding, which is not what interests me, at least for now.
I’m interested in interfacing with some midi controllers.

Note that Pbindef in itself doesn’t buy you anything extra in terms of functionality here, i.e. merely for play-time reps initialization. You can’t pass a function to Pbindef’s constructor, even though it inherits from Pdef, because Pbindef overrides the constructor inherited (which Pdef inherits from PatternProxy actually):

(Pbindef(\oneseq, { Pbind( // ERR with the brace

You have to write something more explicit instead like

(Pbindef(\oneseq,
	\instrument, \default,
	\dur, PlazyEnvir({Pseq(0.5!2, ~reps)}),
	\midinote, PlazyEnvir({Pseq([44, 55], ~reps)})));

Pdef(\oneseq).set(\reps, 3)
Pdef(\oneseq).play
1 Like

Hi @RFluff ,
let me say that this is a newbie question, I’m studying sc, using a real project, so I can face real problems. My goal is just to find a way to change the paramenters inside patterns “on the fly” while I’m defining them for “musical” arrangement purposes, not to play them live(livecoding).
And I use Pbindef just out of habit, that I can change ofc.

About miSCellaneous, GitHub - dkmayer/miSCellaneous_lib: SuperCollider extensions and tutorials: patterns, fx sequencing, granulation, demand rate controlled (half) wavesets, wave folding, sieves, combined lang and server gui control, live coding, single sample feedback, ordinary differential equation audification, generalized functional iteration synthesis. it’s something that I would love to start learning, but I can’t do it before I find myself really confortable with the native Pattern library.
Anyway, it’s my next goal.

(
(Pbindef(\oneseq,
	\instrument, \default,
	\dur, PlazyEnvir({Pseq(0.15!2, ~reps.postln)}),
	\midinote, PlazyEnvir({Pseq([57, 55], ~reps)})));
)
Pdef(\oneseq).set(\reps, 58)
Pdef(\oneseq).play;
Pdef(\oneseq).stop;
Pdef(\oneseq).clear;

In this way it works and I can change \midinote while is playing etc…

thank you very much !

Even Pstutter breaks with a finite-length Pseq on the right, event tough it works (without stopping) in my Pdef example (which uses an infinite stream on the right.)

r = (Pn(Pbind(),3) <> Pbind(\note,Pseq((0..2)))).asStream

r.nextN(9, ()) // -> [ ( 'note': 0 ), ( 'note': 1 ), ( 'note': 2 ), nil, nil, nil, nil, nil, nil ]

r = (Pstutter(3, Pbind()) <> Pbind(\note, Pseq((0..2)))).asStream

r.nextN(9, ()) // -> [ ( 'note': 0 ), ( 'note': 0 ), ( 'note': 0 ), nil, nil, nil, nil, nil, nil ]

I think it’s Pchain which throws in the towel here once the rightmost stream puts out a nil. And I also think the way Pchain works is that it pulls an element from the rightmost stream even when the leftmost one doesn’t need a new element. So you can only do 1:1 filtering like this. Actually, you can shorten the length of a finite pattern with Pchain, but not lengthen it.

Here’s proof that it actually works that way:

r = (Pstutter(3, Pbind()) <> p { (0..2) do: { |x| (x+"pulled!").postln; (foo: x).yield; } } ).asStream
r.nextN(9, ())

Outputs

0 pulled!
1 pulled!
2 pulled!
-> [ ( 'foo': 0 ), ( 'foo': 0 ), ( 'foo': 0 ), nil, nil, nil, nil, nil, nil ]

Ideally, a derived/modified version of Pchain could a be bit smarter and only pull new elements from the stream(s) to the right when the stream(s) to left actually pull them. But I’m not sure how to implement that at the moment. You’d need to build something like the AST that UGens build for the sever-side use… which is pretty difficult to get/make; probably needs compiler support.

One catch here is that the right-hand side of the Pchain in that example is outside of the sphere of influence of Pstutter. Actually, the entire Pchain is outside of the Pstutter, thus not subject to Pstutter’s control.

If it’s Pstutter(..., Pchain(...)) then it wouldn’t draw from the right for every event.

At the moment, I don’t see a way, with this object model, to represent the idea of skipping stream nodes conditionally but outside of the object that decides whether to pull or skip. It may not require compiler support but it would need a different object model. Currently dependencies between operations are represented by nested parent-child relationships; the feature you’re suggesting needs a different way to establish the dependency that’s independent of the parent-child design.

hjh

One can do something like below instead chaining, which essentially just replaces the arguments:

Pdef(\topn, Pdef(\filtered)) // no reps yet

Pdef(\filtered, Pstutter(3, Pdef(\sounds)))

Pdef(\sounds, \instrument, \default, \dur, 0.2, \degree, Pseq((0..4)))

Pdef(\topn).play

// replace filtered before it finishes playing
// not the the sequence restarts though
Pdef(\filtered, Pstutter(2, Pdef(\sounds)))

// likewise for re-reps (try while it's playing)
Pdef(\topn, Pn(Pdef(\filtered), 2))

This avoids pull problems with Pchaining, but the catch is that whenever you do a replacement like this the stream for that level of the AST restarts. E.g. for Pn (topn) it starts to count again from “the beginning”.