Pattern/Pkey Question

Hi,

for some reason i cant get the \degree values with a Pkey(\freq) without crashing sc if i use lists. With single values it works fine. This bothers me for a while but i cant figure it out. Im on 3.12.2

Have a good day!

Post:
Server ‘localhost’ exited with exit code 0.
server ‘localhost’ disconnected shared memory interface

(
SynthDef(\demo,{|freq,freq2,freq3|
	var osc1,osc2,osc3,mix;
	
	osc1 = SinOsc.ar(freq);
	osc2 = SinOsc.ar(freq2,osc1);
	osc3 = SinOsc.ar(freq3,osc2);
	
	mix = osc3 * EnvGen.ar(Env.perc(0.001,2),doneAction:2) !2;
	
	Out.ar(0,mix * 0.333)
}
).add;
)

(
Pbind(\instrument,\demo,
	\degree,[0,2,8],
	\freq2,Pkey(\freq)*3,
	\freq3,Pkey(\freq2)*2,
	\dur,1.5
).play
)

Hi Flo.

I like this solution:

(
Pdef(0,
	Pbind(\instrument, \demo,
		\degree,[2, 4, 5],
		\frq, Pfunc{|ev|ev.use{ev.freq}},
		\freq2, Pkey(\frq) * 3,
		\freq3, Pkey(\freq2) * 2,
		\dur, 1.5,
	)
).play;
)

I would love it if someone smarter could show an easier way also.

1 Like

HI,
thank you for the reply. What i do normally is to use \freq2 etc as frequency ratios avoiding the problem.
I think \degree is converted to \freq anyways and the “problem” lies in the conversion.

(
SynthDef(\demo,{|freq,freq2,freq3|
	var osc1,osc2,osc3,mix;
	
	osc1 = SinOsc.ar(freq);
	osc2 = SinOsc.ar(freq*freq2,osc1);
	osc3 = SinOsc.ar(freq*freq3,osc2);
	
	mix = osc3 * EnvGen.ar(Env.perc(0.001,2),doneAction:2) !2;
	
	Out.ar(0,mix * 0.333)
}
).add;
)

(
Pbind(\instrument,\demo,
	\degree,Pwhite(-14,14!2),
	\freq2,3,
	\freq3,4,
	\dur,0.125
).play
)

Event processing takes place in two stages:

  1. Putting data into the event (often/usually with Pbind). Pkey evaluates at this stage – so, properly, Pkey should refer only to keys that have already been put into the event as part of the pattern.

  2. Event .play (or really, playAndDelta). \freq, \delta, \amp etc. conversions are done at this stage – after Pkey.

So the first thing is that this pattern design shouldn’t have been expected to work, and the proper solution is to model it a different way so that the pattern calculations don’t depend on future event calculations. Sending frequency ratios and letting the SynthDef multiply Hz * ratio is a good way (I do it myself).

It seems to work for single values because of some last-minute conversion that is done on the argument list, just before sending.

  1. \degree gets a single value, like 0.

  2. Pkey(\freq) finds, in the parent event, the freq function. So \freq2 gets { that function } * 2 which becomes a BinaryOpFunction.

    (degree: 0, freq2: a BinaryOpFunction)

    Same for freq3.

  3. When playing the event, \freq gets calculated.

  4. The arg list then has [freq: 261.xxxxx, freq2: a BinaryOpFunction, ...].

  5. If needed, the arg list gets multichannel-expanded.

  6. a BinaryOpFunction can’t be sent to the server. So there’s a last pass, just before sending, that loops over the arg list – the main reason is to embed $[ and $] array-enclosure values.

  7. This loop also calls asControlInput on every item. Any Function “asControlInput” evaluates the function. So it’s at this time that you get the concrete freq2 and freq3.

  8. Now the arg list has [key: number, key: number] as expected.

In the array case, step 5 multichannel-expands freq but – here’s the problem – you don’t have the concrete freq2 and freq3 values (3 for each). So these are not multichannel-expanded – this means, even if the server didn’t crash, you would still get the wrong result. This problem, I don’t think there’s a solution. Multichannel expansion will work only if it has all of the concrete values before final arglist processing. So it will (AFAICS) never be valid to stick a function into an Event when you’re going to need multichannel expansion at play time.

And the crash is because the arglist then looks like:

  • [freq: 261.xxx, freq2: [784.8766959018, 988.88267073861, 1761.9886075044], freq3: […]]
  • [freq: 329.xxx, freq2: [784.8766959018, 988.88267073861, 1761.9886075044], freq3: […]]
  • etc.

… and the freq2 / freq3 arrays take the server down.

So I guess the issue here is that the single-value non-crash leads to an expectation that this is a supported feature – but it can’t be.

hjh

1 Like

On second thought – we could move the .asControlInput bit before the multichannel expansion. That would add a cycle through the loop but would get a more intuitive result.

I’ve used “functions-in-events” to “post-resolve” some values that needed preparation in the event type function, but always felt it was a bit fragile. Probably better to avoid it unless there’s really no other way.

hjh

1 Like