Passing input arguments to Pfunc?

I cannot seem to figure this out.

Say I have a function, {arg chord, deg; chord[deg]}, that returns (or manipulates) the requested degree from a triad. In simple function form I can do that fine. But if I try to use that in a pattern, say with Pfunc, then I have no idea how to pass on “chord” and “deg” to it. And I cannot find any examples anywhere either. Is this because patterns are supposed to be thoroughly determined from the moment you initialize them, like Synths are? Or is there something else going on?

In the context of a Pfunc at least, the first argument to the function is the Event currently being processed. So you can do:

(
Pdef(\funcs, Pbind(
	\octave, 3,
	\legato, 3, 
	\chord, [0, 3, 4, 7],
	\deg, Pseq([0, 1, 2, 3, 2, 1, 0], inf),
	\degree, Pfunc({
		|event|
		event[\chord][event[\deg]].postln
	})
)).play
)

Gotchas:

  • Event keys are processed in order, so if you only specify \chord and \deg after where they are used in the Pbind, they’ll be missing.

There’s a slightly simpler formulation, where you can just use a function:

(
Pdef(\funcs, Pbind(
	\octave, 3,
	\legato, 3, 
	\chord, [0, 3, 4, 7],
	\deg, Pseq([0, 1, 2, 3, 2, 1, 0], inf),
	\degree, {
		~chord[~deg].postln
	}
)).play
)

The subtlety here: Pfunc is executed when the Pbind is evaluating all its keys. A raw function like this is executed only when the Event actually plays, and only for keys that are actually synth parameters (or “special” parameters like \degree). Two advantages:

  • When this is executed, the Event is set to the the current Environment, so you can use ~tildeVariables instead.
  • When this is executed, every Event keys is specified - so if you added or changed your \chord key later, the later one will be used. In the Pfunc case, you’ll always be using the \chord specified in that Pbind.

One more example that might be interesting - since you can set specific default keys in a Pdef, you can set the chord from the outside like this:

(
Pdef(\funcs, Pbind(
	\octave, 3,
	\legato, 3, 
	\deg, Pseq([0, 1, 2, 3, 2, 1, 0], inf),
	\degree, {
		~chord[~deg].postln
	}
)).play;

Pdef(\funcs).set(\chord, [0, 3, 5, 9])
)

FINALLY: if you’re only interested in indexing into a particular chord/scale, the event system already does this for you, using the \degree and \scale keys. Using this mechanism:

(
Pdef(\funcs, Pbind(
	\octave, 3,
	\legato, 3, 
	\degree, Pseq([0, 1, 2, 3, 2, 1, 0], inf), // indexes into \scale
)).play;

Pdef(\funcs).set(
	\scale, #[0, 5, 9, 16]
);
)
3 Likes

This is fantastic! I would have never been able to figure it out. Thanks so much.

if you’re only interested in indexing into a particular chord/scale, the event system already does this for you, using the \degree and \scale keys. Using this mechanism

I am actually trying to do inversions on the fly, i.e. set a chord, do a few different inversions of it, move on to the next chord. Your solution does exactly what I needed. I owe you a beer :+1:

Happy to help :+1:

Fwiw if you’re working with note/pitch pattern stuff, it’s worth reading the section of the Event docs called pitchEvent keys. This describes all of the various default pitch/note related keys and how they are used to calculate the freq that is ultimately sent to the synth.

You can override any of these keys EITHER in your Pbind or by defining your own custom Event type, to provide customized note behavior. For example, you could override the behavior of the \note key so that it optionally inverts the scale depending on another \invert key, and then control the inversion with a pattern as well.

1 Like

That’s very helpful. I should have gone over that document sooner. Thanks.