Benjolin inspired instrument

Bumping an old thread. I’ve been using this instrument quite a bit lately (thanks, Alejandro!) and I’ve been wanting to extend it a bit further to mimic the hardware version. Basically, I want to be able to route each of the output signals (tri1, tr2, sq1, sq2, xor, filter) to their own bus so that I could then either reroute them back into the instrument recursively or into another instance of the same synth (butterfly benjolin-style). Is this something that is possible in JIT/Ndef?

I initially thought of using the .copy method to make a series of synths for each voice (tri1, tr2, sq1, sq2, xor) but I quickly realized how messy that approach is. Does anyone have any advice on how I might be able to achieve this?

Hi,

Here’s a simple example using Ndef: synth is your main synthesis operation, which in this case gives a 4-channel output; out is where you select which output to send to the speakers; and using freqmod you can send any of the outputs back as a modulation signal.

(
Ndef(\freqmod, { DC.ar(0) });
Ndef(\synth, { |freq = 100, freqmodamt = 0|
  freq = freq * (Ndef.ar(\freqmod) * freqmodamt).midiratio;
  [SinOsc.ar(freq), Pulse.ar(freq), Saw.ar(freq), VarSaw.ar(freq)];
}).addSpec(\freqmodamt, ControlSpec(-24, 24, default: 0)).gui;
Ndef(\out, { |which = 0| 
  LeakDC.ar(SelectX.ar(which, Ndef.ar(\synth))) ! 2;
}).addSpec(\which, ControlSpec(0, 3)).play.gui;
)


Ndef(\freqmod, { Ndef.ar(\synth)[1] })

(uses JITLibExtensions for Ndef:addSpec)

1 Like

Thanks for the reply Eric. I think this makes sense. I’ll play around with it this afternoon and see if I can get something cooking.

Hi Ian,
Allowing cross patching is a good idea for evolving the code. There are a few considerations that I can think. For example normalizing the inputs, so scaling the parameters to accept biploar (-1,1.0) or unipolar (0,1.0) signals. For example if you have the arguments freq1 and freq2 inside the instrument you could map or scale them with a variable:

	var freq1map = freq1.linlin(-1.0,1.0,20,10000);
	var freq2map = freq2.linlin(-1.0,1.0,1,10000); 

In this way, you can connect other signals to them without having to scale at each time. Check also the ranges and type (lin, exp, etc).

It is true that Ndef accept recursion as Eric said but you will need to expose the 7 outputs instead of

     LeakDC.ar(output * amp );

something like:

     LeakDC.ar([tri1, osc1, tri2, osc2, pwm, sh0, filt] * amp);

But this means that if you use hardware (sound interface) with multiple output channels, you will have all six outputs constantly producing sounds. So, your intuition about using buses is probably the cleaner way to do it, as you notice it you have to be more careful and tracking what goes where (and order of execution). I suggest using attenuators(volume control) for each output:

	Out.ar(outBusTri1, tri1*tri1Amp);
	Out.ar(outBusOsc1, osc1*osc1Amp);
	Out.ar(outBusTri2, tri2*tri2Amp);
	Out.ar(outBusOsc2, osc2*osc2Amp);
	Out.ar(outBusPwm, pwm*pwmAmp);
	Out.ar(outBusSh0, sh0*sh0Amp);
	Out.ar(outBusFilt, filt*filtAmp); 

Then at playing you will patch the output through a bus let’s say bus 10:

Ndef(\benjolis).set(\outBusTri1, 10);
Ndef(\benjolis).set(\tri1Amp, 0.5);
Ndef(\bus10, {In.ar(10)});
Ndef(\benjolis).map(\freq2, Ndef(\bus10));

Taking the LeakDCout allows you to use the outputs at very low (constant) frequencies.

If you prefer a more monolithic approach at the expenses of flexibility the route could be to wire all the feedbacks inside the instrument with recursive calls and multiplicators to control them or even better live coding it :wink:

Finally, opening up the modularity can be also interested, I mean breaking up the instrument into its different parts, which can be interesting for example for clocking the rungler with an external oscillator.

I am prototyping and experimenting with all the ideas above, I will share them if something meaningful comes out of it. But also let us know where do you end up.

Thanks for your reply, Alejandro. Some useful things to consider here. I have been playing around with the recursive method as described by Eric but nothing elegant enough to share yet. I think your point about exposing all the output channels to the hardware is an important distinction, so I’ll definitely work towards my initial intuition (using busses) moving forward. I hesitated on that approach initially because I thought it was discouraged to use Out Ugens in Ndefs, but I could be mistaken.

Thanks again for the help. If you make any progress I’d love to see what you come up with.