External Control for Pbind

Hello,
I’m trying to send a realtime amplitude analysis from a buffer, which is stored inside a control Bus to a Pbind to control the amplitude level of the Synth.
In the example below I tried to control tha amp level inside of the Pbind with the In.kr signal but I can’t set the control signal inside UGen.

Any ideas how I can use that information in the control bus to manipulate the amp level inside of Patterns?

~controlbus = Bus.control(s,1);

~buffer = Buffer.read(s, Platform.resourceDir +/+ “sounds/a11wlk01.wav”);

(
~loudnessSynth = SynthDef(\amplitude, {
var in = PlayBuf.ar(2, ~buffer, loop:1);
var chain = FFT(LocalBuf(1024), in);
var lout = Loudness.kr(chain).poll(1);
Out.kr(~controlbus.index, lout);
}).add;
)

Synth(\amplitude)

(
~mainSynth = SynthDef(\SimpleSine, {|freq = 440, out, amp=0.5|
var env = EnvGen.kr(Env.perc(0.01), doneAction:2);
Out.ar(out, SinOsc.ar(freq, 0, 0.2) * env * amp) }).add;
)
(
Pbind(\instrument, \SimpleSine,
\dur, 0.1,
\freq, Pexprand(100,1000,inf),
\amp, (In.kr(~controlbus))
).play;
)

First, use triple-backtick ``` code blocks instead of “> …” quotes for code.

```
your code
```

Second, a couple of style recommendations.

IMO it’s better, in SynthDefs, to avoid hard coding references to external objects. It’s better to pass them in as arguments. Here, you have a SynthDef that can play one and only one buffer at output to one and only one bus – but it’s really no trouble to make it play any buffer and output to any kr bus.

Also, there isn’t really a good reason to assign the SynthDef to a variable, but you should assign the Synth to a variable. (Got those backward.)

~controlbus = Bus.control(s,1);

~buffer = Buffer.read(s, Platform.resourceDir +/+ “sounds/a11wlk01.wav”);

(
SynthDef(\amplitude, { |out, buffer|
	var in = PlayBuf.ar(2, buffer, loop: 1);
	var chain = FFT(LocalBuf(1024), in);
	var lout = Loudness.kr(chain).poll(1);
	Out.kr(out, lout);
}).add;
)

~loudnessSynth = Synth(\amplitude, [out: ~controlbus, buffer: ~buffer]);

Last, In.kr evaluates only in the server, but patterns evaluate in the language. In.kr can never give you a meaningful value for language-side calculations.

The two easiest choices are:

  • If you want the sine amplitude to follow the loudness curve moment-to-moment, you can map \amp to the bus: \amp, ~controlbus.asMap

  • Or, if you want the amplitude to be sampled-and-held per note, you could grab the bus value via shared memory: \amp, Pfunc { ~controlbus.getSynchronous }. (In this case, the time granularity for reading the control bus depends on your hardware buffer size.)

    • Variant: Write the sample-hold into the sine SynthDef, and map the bus. That would give you the most timing precision.
// variant
(
SynthDef(\SimpleSine, {|freq = 440, out, amp = 0.5|
	var env = EnvGen.kr(Env.perc(0.01), doneAction:2);
	// get amp once at start of synth and hold it forever
	amp = Latch.kr(amp, 1);
	Out.ar(out, SinOsc.ar(freq, 0, 0.2) * env * amp) }).add;
)

(
Pbind(\instrument, \SimpleSine,
	\dur, 0.1,
	\freq, Pexprand(100,1000,inf),
	\amp, ~controlbus.asMap
).play;
)

hjh

1 Like

Control strategies of this kind are also described in the miSCellaneous_lib tutorial “Event patterns and LFOs”