Improving Documentation - Pfunc and Pfuncn [SOLVED]

mod note: topic moved to the questions category.

the Learn to Contribute category is intended for learning resources that pertain to software development.

from the help docs “Pattern Guide 02: Basic Vocabulary”:

Pfunc(nextFunc, resetFunc)
The next value is the return value from evaluating nextFunc. If .reset is called on a stream made from this pattern, resetFunc is evaluated. The stream will run indefinitely until nextFunc returns nil.


this is not possible with patterns - you will need to modify the default SynthDef in order to achieve a sinusoidal vibrato. If you look at Event.sc, you can see SynthDef for the default synth:

SynthDef(\default, { arg out=0, freq=440, amp=0.1, pan=0, gate=1;
			var z;
			z = LPF.ar(
				Mix.new(VarSaw.ar(freq + [0, Rand(-0.4,0.0), Rand(0.0,0.4)], 0, 0.3, 0.3)),
				XLine.kr(Rand(4000,5000), Rand(2500,3200), 1)
			) * Linen.kr(gate, 0.01, 0.7, 0.3, 2);
			OffsetOut.ar(out, Pan2.ar(z, pan, amp));
		}, [\ir]).add;

you could modify this to incorporate some vibrato arguments:

SynthDef(\default, { arg out=0, freq=440, amp=0.1, pan=0, gate=1, vibrato_width=0, vibrato_rate=0;
			var z;
			z = freq  * (2 ** (SinOsc.ar (vibrato_rate) * vibrato_width / 12);
			z = LPF.ar(
				Mix.new(VarSaw.ar(z + [0, Rand(-0.4,0.0), Rand(0.0,0.4)], 0, 0.3, 0.3)),
				XLine.kr(Rand(4000,5000), Rand(2500,3200), 1)
			) * Linen.kr(gate, 0.01, 0.7, 0.3, 2);
			OffsetOut.ar(out, Pan2.ar(z, pan, amp));
		}, [\ir]).add;

and then executing:

Synth (\default, [ \vibrato_width, 0.3, \vibrato_rate, 1.5 ])

where vibrato_width is a value of semitones.

alternatively, you could use a time-based pattern to alter the pitch of the sequence of notes according to a sinusoid:

(
Pbind (
	\instrument, \default,
	\delta, 0.2,
	\freq, 440 * (2 ** (sin (Ptime () * 1.5 * 2pi) * 0.3 / 12)),
).play;
)

just bear in mind that in this case the notes themselves are not actually vibrato-ing. each note holds its frequency, while frequencies passed to the synths oscillate up and down from 440 Hz, by 0.3 semitones, at a rate of 1.5cps.

edit: por che no los dos?

(
Pbind (
	\instrument, \default,
	\delta, 0.2,
	\freq, Pseq ([440, 550, 660], inf) * (2 ** (sin (Ptime() * 17.reciprocal * 2pi) * 0.3 / 12)),
	\vibrato_width, sin (Ptime () * 13.reciprocal * 2pi) * 0.3,
	\vibrato_rate, 2.1 + (2 * (sin (Ptime () * 9.reciprocal * 2pi))),
).play;
)

Thanks! Got the vibrato stuff.

Yeah, I do understand what it does, the description is clear, although the example does not help. Passing a exponential function inside it is the same of using Pexprand… Can you point out a really useful/usual utilization for Pfunc and Pfuncn?

from the help documentation, “Pattern Guide Cookbook 03: External Control”:

Control of parameters by MIDI or HID

The best approach is to save an incoming value into a variable, and then use Pfunc to access the variable for each event.

(
~legato = 1;
c = MIDIFunc.cc({ |value, num, chan, src|
   ~legato = value.linlin(0, 127, 0.1, 2.5)
}, 1);    // 1 means modwheel
)

(
p = Pbind(
   \degree, Pwhite(-7, 12, inf),
   \dur, Pwrand([0.25, Pn(0.125, 2)], #[0.8, 0.2], inf),
   \legato, Pfunc { ~legato }    // retrieves value set by MIDI control
).play;
)

p.stop;
c.free;

each time the Pbind assembles a note event, Pfunc directs it to whatever is currently inside ~legato, so you can control that parameter in realtime by live coding, using a midi / osc listener, etc.

1 Like

The way to think about Pfunc is this: It allows you to take any function and turn it into a pattern. The reset function is there so you can write patterns that have a state (e.g. if you write a pattern that always starts from 1) - you can make sure it always starts from the place it’s supposed to start. That’s probably a bad example as Prout with a routine would be a better way of doing that, but hopefully you get the idea.

Pfuncn only works for stateless functions, and just calls the function ‘n’ times before ending.

I often use Pfunc and Prout as a way to create templates for patterns I use a lot.

1 Like

you can use it to live code! for example:

(
SynthDef (\folded_sine) {
	arg lag = 0;
	var sig;
	sig = SinOsc.ar (\freq.kr (330, lag));
	sig = (sig * \fold.kr (0, lag)).fold2 (1);
	sig = Pan2.ar (sig, \pan.kr (0, lag), \amp.kr (0.1, lag));
	Out.ar (0, sig);
}.add;

~pattern = Pmono (\folded_sine,
	\delta, Pfunc { ~delta.choose },
	\freq, Pfunc { ~chord.choose.midicps * ~octaves.choose },
	\fold, Pfunc { ~fold.choose },
	\lag, Pfunc { ~lag },
	\pan, Pwhite (-1.0, 1.0),
);

~delta = [ 2 / 7 ];
~chord = [ 0, 3, 7, 10, 14, 17 ] + 62;
~fold = [ 1 ];
~octaves = [ 1 ];
~lag = 0;
)

and then evaluate the following, line by line:

~play = Ppar (~pattern!6, inf).play
~lag = 0.2
~fold = (1..3)
~delta = (2..4) / 7
~octaves = 2 ** (-1..1)
~octaves = [ 1 ]
~chord = [ 0, 3, 7, 10, 14, 17 ] + 62
~chord = [ 0, 4, 7, 14, 18, 21 ] + 61
~chord = [ 0, 3, 7, 10, 14, 17 ] + 60
~chord = [ 0, 4, 7, 14, 18, 21 ] + 59
~delta = (1..4).nthPrime.reciprocal
~fold = [ 1 ]

~play.stop

by constantly directing the pattern to an environment variable, Pfunc lets you control pattern parameters in real time, ie. without having to rebuild the pattern.

1 Like

I am more used to do this with Pdef, Pbind, Pbindf and Pbindef. Why exactly do you prefer to use Pmono instead of glueing everything with Pbind? Is it to have faster independent control of each key-value pairs (somehow typing less) or is it just a matter of preference?

I got the point of the MIDI example, and there seems to me an usage that is strictly unique to Pfunc.

Just to be 100% sure, any other example where one could only use Pfunc or Pfuncn? (especially something that does not involves MIDI)

Pmono is for when you want to just use the one continuous synthesiser that lives on the server, and the pattern changes its parameters by sending it .set messsages.

Pbind is for when you want to instantiate a separate synth for each note event (if the tail of the previous note overlaps the start of the next note, for example).

edit 2:

oop yup no I see what you mean.

I’m not sure if there is a unique case that couldn’t be done in any other way - I think it is more a convenience thing, letting you access environment variables and non-pattern specific functions etc.

Hum, I see. From what I’ve found it seems that people use it as a way to avoid patterns abstraction and use conditional, loops and iteration like some commercial programming languages (I also find that using Pif resembles a really strange visual code ). I guess that’s why David Cottle put this example on his book:

(
SynthDef("SimpleTone",
{arg midinote = 60, amp = 0.9, dur = 1, pan = 0, legato = 0.8;
Out.ar(0,
Pan2.ar(
SinOsc.ar(midinote.midicps, mul: amp)
*
EnvGen.kr(Env.perc(0, dur*legato), doneAction: 2),
pan
)
)
}).add;
)

(
f = 100;
Pbind(
\instrument, "SimpleTone",
\midinote, Pfunc({		
f = ([3/2, 4/3].choose) * f;
if(f > 1000, {f = f/8}); //.fold or .wrap didn't do what I wanted
f.cpsmidi
}),
\dur, 0.2
).play
)

I often see people using both Pfunc and Pkey within Pbind for different pairs (especially when they what to access some keys of the current event), but I don’t see any good reason for doing so, despite of convenience or style. Is there any optimization or memory allocation issue?

Is this doing exactly the same or is there any problem/constrain involved with one of the methods?

(
Pbind(
	\degree, Pfunc({|e| (e.dur * 4).postln;}) , //could be any enviroment variable a, b, c, d, e, f....
).play;
)

(
Pbind(
	\degree, (Pkey(\dur)*4 ).trace, 
).play;
)

are convenience or style not reason enough?

I would say probably not, but I am not sure.

\degree, Pfunc({|e| (e.dur * 4).postln;}) , //could be any enviroment variable a, b, c, d, e, f....

although this is not mentioned in the help file, looks like Pfunc passes in new events created by the Pbind into its function as an argument.

I would say using an environment variable here confuses the issue: e is being used within the scope of Pfunc as a container for the event being passed in - you may as well use a more descriptive name like event or note_event or this_event, and leave the environmental variables for uses that require the larger scope.

but yes - the two techniques look pretty equivalent to me.

Sure! I just wonder if this could be clear in the documentation. I will pull a request on github.

Just to clarify: a,b,c,d (when not defined locally) are interpreter variables, not environmental variables (see Interpreter help file). Only in a restricted sense they are global - they cannot be used from class code. But in this context e is a variable local to the function.
A help file describing and comparing different variable types in SC would be valuable IMO (I know, should do, not complain).

Daniel

1 Like

It is mentioned in the help file:

nextFunc Stream function. In an event stream receives the current Event as argument.

Yes, but Pfunc is more versatile. There have been numerous requests in the past, where Pkey was not applicable for various reasons.

… but granted, regarding the importance of this feature this is extremely tight, an example would be appropriate.

1 Like

right you are! been reading with my eyes closed again

TIL interpreter variables

It’s worth pointing out that SuperCollider doesn’t have any global variables. If you change the environment (which can happen quite easily in ways that are not obvious to inexperienced users) they will no longer hold the same values.

This is a common (and to be honest quite reasonable) misunderstanding.

1 Like

Can you remember one of them here?

E.g. see this thread, granted a quite exceptional case, but there have often been requests concerning such special situations where Pkey is not or hardly applicable, Pfunc is always possible in some way.

https://www.listarc.bham.ac.uk/lists/sc-users-2013/msg28267.html

A FAQ is the the combination of Pif, Pkey and ‘==’, which needs Pbinop. This is not Pkey’s fault but it forces an unintuitive syntax. The Pfunc variant is not shorter and also not intuitive per se, but it follows its standard scheme.

Pbind(
    \midinote, Pwhite(60, 62),
    \dur, 0.2,
    \amp, Pif(Pbinop('==', Pkey(\midinote), 61), 0.5, 0.05)
).play


Pbind(
    \midinote, Pwhite(60, 62),
    \dur, 0.2,
    \amp, Pfunc { |e| (e[\midinote] == 61).if { 0.5 }{ 0.05 } }
).play
2 Likes

Indeed. And that’s a useful way to delete keys from an event in a stream because Pbind itself cannot do that; if you assign a nil to a key in a Pbind, it just nils/ends the whole event stream.

And yeah, you can’t do that (delete a key) with Pkey.


Another (related) example: blend an event with another while dropping non-common parts. Easily done with Pfunc, somewhat more tedious (but feasible) with Pnaryop

// blends via AbstractFunction, which assumes max 2 args for blend, whereas
// Dictionary.blend and subclasses take 4 args
(blend(Pbind(\foo, 2),  Pbind(\foo, 1, \bar, 3), fill: false)).asStream.next(())
// -> WARNING: keyword arg 'fill' not found in call to AbstractFunction:blend
// -> ( 'bar': 3, 'foo': 1.5 )

// can't give args by name in Pnaryop; so to give 4th, need give the 3rd
Pnaryop(\blend, Pbind(\foo, 2), [Pbind(\foo, 1, \bar, 3), 0.5, false]).asStream.next(())
// -> ( 'foo': 1.5 )

// more "natural"?
(Pfunc({|ev| (foo: 2).blend(ev, fill: false)}) <> Pbind(\foo, 1, \bar, 3)).asStream.next(())
// -> ( 'foo': 1.5 )

This gets “even more natural” in a Pfunc because there’s a nasty gotcha using Dictionary.blend and merge for Events (these have a parent that is dropped by blend/merge in the superclass implementation.)

// parent (useful: 42) is lost here;
// this is normally Event.default.parent in a .play -- ouch
e = ().parent_((useful: 42));
(Pnaryop(\blend, Pbind(\foo, 2), [Pbind(\foo, 1, \bar, 3), 0.5, false])
.asStream.next(e).parent) // -> nil

// what you really want
((Pfunc({|ev| ev.putAll((foo: 2).blend(ev, fill: false))})
	<> Pbind(\foo, 1, \bar, 3)).asStream.next(e).parent)
// -> ( 'useful': 42 )

You can probably get this latter Pfunc done with nested Pnaryops (maybe in combo with Plazy if you don’t want to duplicate streams)… really terrible to read though and left as exercise to the reader.

(For the sake of this last example being entirely equivalent with the fill: false, you’d also have to also delete keys in ev not found in (foo: 2), but in practice a “directional” merge or blend as done above is more useful. And you don’t get that “directional flavor” from Dictionary.blend (or Dictionary.merge) in any (direct) parametrization of those methods, besides the “parent drop” issue. So you’d need multiple Pnaryops to implement one Pfunc.)

If you extend the (right) class with just the right method, then you can alway use just one Pnaryop. :grimacing: And with Events/Environments you do that most of the time (with the “object prototyping” know trick, on by default.)

~bf = {|self, other, blendFrac=0.5| self.putAll(self.blend(other, blendFrac, false)) }
(blendFrom: ~bf, foo: 2).blendFrom((foo: 1)) // -> ( 'blendFrom': a Function, 'foo': 1.5 )

You can even hide those “methods” in the parent (that’s what Event.default.parent is mostly used for):

(foo: 3).parent_((blendFrom: ~bf)).blendFrom((foo: 2)) // -> ( 'foo': 2.5 )

So yeah, with this hack it seems you can use just one Pnaryop instead of any given Pfunc, but you have to define the function somewhere separately (which actually may be a good thing).

e = ().parent_((useful: 42, blendFrom: ~bf));
(Pnaryop(\blendFrom, Pbind(\foo, 2), [Pbind(\foo, 1, \bar, 3), 0.5, false])
.asStream.next(e).postln.parent)
// posts ( 'foo': 1.5 )
// -> ( 'blendFrom': a Function, 'useful': 42 )

Fairly contrived (in the sense that a simple \blend would do here) musical example like that, just to show how to get a custom pseudo-method called “in play”.

e = ().parent_(Event.default.parent.next((blendFrom: ~bf)))
(Pnaryop(\blendFrom, Pbind(\dur, 1, \degree, 1),
	[Pbind(\dur, 0.1, \degree, 8), Pseq(0.1*(1..9)), false])
.trace.play(protoEvent: e))

Aside: I have Event.<> (nil by default) defined via a class extension to be the same as next, which is the same as composeEvents, so I can write just

e = ().parent_(Event.default.parent <> (blendFrom: ~bf))

(Sometimes I forget that and give examples written like that that don’t work for other people.)

Actually, you can also use “the other parent” of an Event, namely proto here for the same effect. Unlike parent which gets set to Event.default.parent during play (only) if it’s nil, proto isn’t set by “the (Event) system”, but it is used, so you don’t have to worry about chaining that one:

e = ().proto_((blendFrom: ~bf))

Besides, I guess ZeCraum hadn’t discovered Prout. Everything is “redundant” next to that, but of course there’s the matter of conciseness. With Prout you have to explicitly yield and also explicitly loop; Pfunc is more useful for simpler code that doesn’t have one-time (stream-start) initialization and “auto-yields” the last expression in an “auto-loop”. (Prout is very useful for prototyping new Pattern classes without recompiling the class lib, as it “takes the same inner code” as an embedInStream would.)

Pfunc { |ev| something.(ev) }

Prout { |ev| init_stuff; loop { ev = yield(something.(ev)) } }

But sometimes it’s “the other way around”, Prout can be “less hairy” when you need local state, since there are no C-style “static vars” in SC (because you have co-Routines)

Prout{ 4.yield; 5.yield; }.asStream.nextN(3) // -> [ 4, 5, nil ]

~cnt = 0; Pfunc{ switch(~cnt = ~cnt + 1, 1, {4}, 2, {5}, {nil}) }.asStream.nextN(3)

// or
~cnt = 0; Pfunc{ if((~cnt = ~cnt + 1) < 3, {~cnt + 3}, {nil}) }.asStream.nextN(3)

You can encapsulate the “local state” in Plazy too.

Plazy { init_stuff; Pfunc { |ev| something.(ev) } } 

is roughly equivalent to a Prout (with a loop), but still more tedious sometimes if you don’t want the Pfunc “auto-loop” that you need to escape from (with a nil-return).

Plazy{ var cnt = 0; Pfunc{ if((cnt = cnt + 1) < 3, {cnt + 3}, {nil}) } }.asStream.nextN(3)

(See also Penvir and PlazyEnvir for some other/related ways to initialization, which may even setup data sharing. There are some threads here on that.)

And no, despite the trivial examples I gave here, Prout is not redundant to Pseq. :face_with_raised_eyebrow:

That’s the point of most classes in the Pattern library, to make some constructs simpler to write than having to dish out a Prout (or Pfunc). But there isn’t a pre-packaged Pattern class written for every conceivable function or Routine.

I’m not sure if this was changed/fixed since 2018, but I recall it was always the case that they differed. The point of the n-class is to allow a reps arg, which by default is 1, so unlike Pfunc it doesn’t “loop forever by default” (as a stream):

~cnt = 0; Pfuncn({(~cnt = ~cnt + 1) + 3}, 2).asStream.nextN(3)
// -> [ 4, 5, nil ]

Instead of Pfuncn’s repeat count, Pfunc also allows a custom reset function to be called when the stream resets, but I haven’t really used that (besides some experimenting) because resets don’t propagate properly. You’re better off with an initialization section in a Prout or Plazy to do the “reset logic”. (Alternatively, you can use cleanups, preferably after the double-execution bugs are fixed; but cleanups don’t work, propagation-wise, for non-event streams “by design” because they use a “reserved” field/key in the event itself for cleanup-messaging between streams… although that could be changed with a more generic Halo-type approach to cleanups, but that’s not “in the works” right now.)