Switching between sample and non-sample based instruments in a pattern


#1

I want to do something like this:
p = Pbind(\instrument, Pseq([\default, Pfunc({|ev| ev[\buf] = [~b0, ~b1].choose; \bufPlayer}), \default], inf)).play;

It works up to the Pfunc but gets stuck there. I don’t understand why this happens. And yes, i know i can set the \buf key using a separate value pattern, but i really want to understand what’s happening here too. :slight_smile:


#2

Ah. Just discovered Pfuncn which seems to solve all problems in my life.


#3

Can you post your working code for the benefit of the audience? :slight_smile:
It took me a minute to see what the problem was, I’m sure other people as well.


#4

Of course. This works:

p = Pbind(\instrument, Pseq([\default, Pfuncn({|ev| ev[\buf] = [~b0, ~b1].choose; \bufPlayer}, 1), \default], inf)).play;

I still don’t really understand why the Pseq gets stuck at the Pfunc though. I thought that the nextFunc was evaluated once per event and binding the last value of the function to the key? But it seems i need to explicitly tell it to move on somehow? Maybe Pfunc isn’t intended to be nested in other value patterns but Pfuncn solves that?

The reason i do this kind of awkward thing btw is a little live-coding dialect i’m working on (using preProcessor). I want to write simple statements like [\default, \bd01] etc and it should just automagically do the needed pattern business.


#5

The reason it doesn’t work (I run into this gotcha regularly, and constantly have to remind myself…):

When you’re next-ing a pattern in a Pbind to get some kind of result, if you encounter another pattern, in general that pattern is immediately embedded. By embedded, I mean It effectively takes over returning values until it’s run out (at which point control falls back to outer pattern). So, in a case like:

Pseq([0, 1, 2, Pseq([3, 3, 3]), 4]);

you’ll get: 0, 1, 2, 3, 3, 3, 4, because when the nested Pseq is reached, it returns values until it runs out.

This is a particular gotcha with Pfunc because unlike most patterns, a basic Pfunc will usually return values forever (I believe until you explicitly return nil?). For most pattern types, encountering a nested Pfunc results in it being embedded, and then returning values forever and never returning control to e,g. the outer Pseq.

Pfuncn works because it has an extra argument specifying how many values it will return when it’s embedded - and the default is 1. The pattern class that behaves more in line with your intuitions is probably Ppatlace - this is similar to a Pseq - but if it encounters a nested pattern, it returns one value, holds on to the state of that pattern, and then moves on to the next. So you get:

Pseq([ Pseq([1, 2, 3]), Pseq([4, 5, 6]) ]).asStream.all;
-> [ 1, 2, 3, 4, 5, 6 ]

Ppatlace([ Pseq([1, 2, 3]), Pseq([4, 5, 6]) ], inf).asStream.all;
-> [ 1, 4, 2, 5, 3, 6 ]

Note that I did not specify a number of repeats for Pseq, because by default it moves through it’s list an exhausts every stream before it finished. With Ppatlace, repeats specifies how many times it loops through the list - so with the default repeats:1, you only get two numbers (one for each pattern in the list).


#6

Thanks for the explanation!


#7

If you intend to embed a random function as in your example you woudn’t need Pfunc at all:

~b0 = 0;
~b1 = 1;

p = Pbind(
            \buf, Prand([~b0, ~b1], inf),
            \instrument, Pseq([\default, \bufPlayer, \default], inf)
).trace.play;

The extra choice in the case of the default instrument can be neglected.

Sure, if you want a certain sequence to be continued only with the scond instrument you have to do it differently. You can e.g. use Pclutch (but note that by convention Pclutch starts with the first value anyway):

(
p = Pbind(
    \instrument, Pseq([\default, \bufPlayer, \default], inf),
    \buf, Pclutch(Pseq([~b0, ~b1], inf), Pkey(\instrument).collect(_ == \bufPlayer))
).trace.play;
)

Ppatlace can also be used with event patterns which is fine here:

(
q = Pbind(
    \instrument, \bufPlayer,
    \buf, Pseq([~b0, ~b1], inf)
);

p = Pbind(\instrument, \default);

r = Ppatlace([p, q, p], inf).trace.play;
)

#8

I guess the short example doesn’t really explain what i’m doing. The idea is to set several keys at once when i change between different kinds of instruments. From the live-coding interface the user should only need to bother with names, like this for example:

instr \rnd [\synthDefName, \bufferName, \inputBus, \midiDestination]

This should create a Pbindef that changes “instruments” (using a Prand) from the users perspective. In the Pbindef, however, if it’s something else than a \synthDefName, we need to consider and set several keys. My solution was that a line of code that starts with “instr” eventually gets routed (by the preProcessor) to the \instrument key in the Pbindef. But if it’s a \sampleName or anything else but an existing SynthDef name it gets wrapped in a Pfuncn that set the other necessary keys as well as swithching to the appropriate instrument.

Does this make sense?


#9

The intention sounds absolutely reasonable to me. Pbindef is great, but I always felt that its interface needs much typing, especially when using it in a life coding context.

I don’t know all options that you’re planning, but I’d also leave the opportunity to replace a pattern in its “original form” as well. E.g. at some point you would not only want \rnd and \seq etc but also combos and other stuff.

If you’re interested, miSCellaneous_lib contains classes PLbindef and PLbindefPar as interfaces for one or a number of parallel Pbindefs.

With optional activation of EventShortcuts you would write

PLbindef(\x, ...)

and then for instrument replacement e.g.

~x.i = PLseq([\beep, \buzz])

~x.i = PLrand([\beep, \buzz])

etc.

With some helper functions and/or preProcessor usage the pattern syntax could be shortened further.
Julian’s Steno might also be worth considering.