Events in a Pchain

As I understand, it’s possible to use a standalone Event in a Pchain, to be combined with a Pbind:

p = Pbind(\a, 1);
x = Pchain((b: 2), p).asStream;
); // -> ( 'a': 1, 'b': 2 )

But, when using the <> syntax shortcut, the previous example returns nil:

p = Pbind(\a, 1);
x = ((b: 2) <> p).asStream;
); // -> nil

What seems even stranger is that the <> example works normally if the two terms are swapped:

p = Pbind(\a, 1);
x = (p <> (b: 2)).asStream;
); // -> ( 'a': 1, 'b': 2 )

But of course, order can be significant in the context of pattern composition. Can anyone explain this behavior?


I think the operator <> is defined for a Pattern but not for an Event.
So p <> ... means something, but (b: 2) <> ... doesn’t.

That definitely makes sense, thanks. I suppose you can always use a Pbind instead of an Event, so it doesn’t seem like a huge deal:

p = Pbind(\a, 1);
x = (Pbind(\b, 2) <> p).asStream;

In case you didn’t know…
Event (or rather dictionary) gives another of doing this with the merge method — here you supply a function that is run for overlapping keys, meaning you could wrap them in another pattern, play them both, or something… Its a super set of this operator.

1 Like

Interesting. But this approach seems to be exclusively for combining two Dictionaries (or its subclasses). How would merge be used to blend two Pbinds? Or one Pbind and one Event?

There may be other shortcuts out there but:

you can make a Pbind into an IdentityDictionary by calling myPbind.patternpairs.asDict

and to make a dictionary into a Pbind you can do Pbind( *yourDict.asPairs)

I like the shortcut .p for Pbind(*p) (I think it is in wslib?) because it encourages me to manipulate Pbind data with Array methods.

and then you could write myDict.asPairs.p

Very interesting, thanks. Always more to be learned. I found the .p method you describe, it looks like it’s actually part of the miSCellaneous lib, rather than wslib.

This is because of events-as-prototypes:

p = <>;
-> nil

p = false) <>;
^^ ERROR: Message '<>' not understood.

// OK then, if it's not understood, then
// let's have the event implement the operation
var event = (
	'<>': { |self, that| Pchain(self, that) }

p = event <>;
-> a Pchain

But… I don’t see that event <> pattern is a particularly useful incantation.

I do use pattern <> event quite often. The event provides fixed default values, and the pattern can override those with variable values.

Reversing them means that the pattern provides variable values, and then the event clobbers them with unchanging values.

var event = (
	'<>': { |self, that| Pchain(self, that) },
	pan: 0,

p = event <> Pbind(
	\degree, Pwhite(-7, 7, inf),
	\pan, Pwhite(-1.0, 1.0, inf).trace(prefix: "generated pan value: ")

p.asStream.nextN(3, ()).do(_.postln); ""

generated pan value: -0.46429038047791
generated pan value: 0.46915698051453
generated pan value: -0.22278738021851
( 'degree': -1, '<>': a Function, 'pan': 0 )
( 'degree': 2, '<>': a Function, 'pan': 0 )
( 'degree': 0, '<>': a Function, 'pan': 0 )

… where I’m pretty sure, over 20 years, that I never encountered a need for this behavior.


Makes perfect sense. Thanks James.

@semiquaver Just curious, what sorts of array methods do you use for Pbind manipulation? Are there any techniques that are difficult/impossible to do without converting data to an array format?

maybe “array methods” sounds fancier that I intended! I just meant that it can be nice to just manipulate the pairs, whether as Arrays or Dictionaries directly ,rather than trying to figure out just the right wizardly combo of filter Patterns/Pfuncs etc.

Sone random uses in my world: I have a set of “tune” Arrays, one for each section - the arrays have midinote and dur pairs So I can write `

~tunes[section].pp` to hear the \deafult Synth play the tune.or

( ~tunes[section] ++ [instrument: [\a, \b], amp: [0.2, 0.3]) _.pp )

to have two instruments play the tune at different levels.

check if the high midinote in the phrase is above 60 and if so double lower

( ~tunes[section] ++ ( ~tunes[section].asEvent.midinote.list.max > 60 ).if{ [ctranspose: -12]}{[]} ) ).pp etc

I also can pass the tune Array to a function that builds a SynthesizerV AI voice project.

I have some functions that alter the performance/register/etc of these “voices” (again using not using Streams) and I can compose them back into pbinds if I want to check them or forward them on to SynthesizerV.

…I have Multi-Voice writing Class to represent contrapuntal voices - it has a pbinds method that builds an Array of Arrays and then collect into Pbinds. Again manipulation is easier for me with data in Arrays and Dicts than Patterns.

The point is that I try not to have to agonize about which obscure combo of Ptuple and Pif and what not will get what is often a very uncomplicated disideratum!