Multichannel DynKlang

Hi all! I made a synthdef around DynKlang which i’ll be controlling from an openframeworks application using the ofxsupercollider library.

The synth should be 8 channels, but because DynKlang already needs arrays for its frequencies and amplitudes parameters, i’m struggling to achieve the multichannel expansion… I’m very novice in SC, and there’s many fundamental things i still don’t know. Sorry if my questions are irritating!

This is what i have so far:

(
SynthDef(\dynklang, {
	arg out=0;
	var freqs, amps,transpose,sig;
	var partials=40;
	transpose=\transpose.kr(36!8).midicps;
	freqs=(\freqarray.kr(220!partials)+transpose).lag(0.05);
	amps=\amparray.kr(0.5!partials).lag(0.05);
	sig=DynKlang.ar(`[freqs, amps, nil]!8);
	Out.ar(out, sig)
}).add
)

but this doesn’t behave the way i would expect.

The idea is:

-Be able to receive a 2D array of 8*40 \freqarray and \amparray
-Be able to receive an array of 8 values to \transpose

So that on each of the 8 channels there are different values for frequencies and amplitudes that feed DynKlang.

I’ve been flying around this problem in circles for days, but i haven’t found the right direction to solve it…

Any idea?
Thanks in advance for your patience and wisdom…

You have to multichannel expand your DynKlang Ugen or iterate over the Ugen, not the array reference argument.
But also I think this Ugen is going to just make you mad…

PS be careful with your transpose function and your use of the ! operator.

You don’t need to do most of the multichannel expansion you have in this synthdef.

1 Like

This is pretty ridiculous to have this many inputs… but here you go! Also, DynKlang is a wrapper around SinOsc … don’t use it if it doesn’t suit your use case.

s.options.numWireBufs = 1024; // won't work on default, too big.

s.boot;

SynthDef(\large, {
	var columns = 40;
	var rows = 8;
	var freqs = \freqs.kr(220.dup(columns*rows)).reshape(columns, rows);
	var amps = \amps.kr(1.0.dup(columns*rows)).reshape(columns, rows);
	var transpose = \transpose.kr(0!rows);
	var largeSig = SinOsc.ar(freqs + transpose, mul: amps);
	var flat8 = Mix.ar(largeSig);
	var stereo = Splay.ar(flat8) * 0.001;
	Out.ar(0, stereo);
}).add

x = Synth(\large)
x.set(\freqs, { rrand(50, 18000) }.dup(40*8) )
x.set(\amps, { rrand(0.0, 1.0) }.dup(40*8) )
x.set(\transpose, { rrand(-10, 10) }.dup(8) )

Thats because this is a bad problem, its very difficult to specify and control 320 frequencies and 320 amplitudes. Why not break this down into 8 synths, or use a more complex waveform to begin with? That is, assuming you want to control this musically (like with a pattern), if you’ve already got a bunch of data that you are trying to sonify then this might make sense.

1 Like

ahaha yeah! it’s a ridiculous amount of data! :sweat_smile:

But as you suspected it’s intended for sonification of pixel luminance and motor angle of a kinetic installation (https://www.flickr.com/photos/playmodes/albums/72177720299743152)
We already did something similar here, using max msp:
https://www.playmodes.com/home/horizon/

ok, i’m going to study your answers and come back with results soon!!!

Thank you very much for your help, there’s lot for me to understand here (ooo i see…reshape…)!!

It takes two flat arrays of 320 elements, then reshapes it internally to get the interface you wanted. This might illustrate better what I’ve done better, I think I’ve understood you correct?

var freqs = [
   [f101, f102 ... f140],
   [f201, f202 ... f240],
   ...
   [f801, f802 ... f840]
];

var amps = [
   [a101, a102 ... a140],
   [a201, f802 ... a240],
   ...
   [a801, a802 ... f840]
];

var transpose = [t1, t2 ... t8 ];

var S = {|freq, amp| SinOsc.ar(freq, mul: amp) };

var largeSig = [
   [S.(f101 + t1, a101), S.(f102 + t1, a102) ... S.(f140 + t1, a140)],
   [S.(f201 + t2, a201), S.(f202 + t2, a202) ... S.(f240 + t2, a240)]
   ...
   [S.(f801 + t8, a801), S.(f802 + t8, f802) ... S.(f840 + t8, f840)]
];

var flat8 = [
   largeSig[0].sum,
   largeSig[1].sum,
   ...
   largeSig[8].sum
];


Yes, that’s exactly what i ment. Each of the 8 outputs includes 40 partials.

I’m trying to integrate all this with the OF pipeline to test, but still no luck… at this stage of development is still a tedious and opaque work…

I understand that my OSC messages for freqs and amps should be 1D arrays of 320 elements, which “reshape” will put in 2D format, right?

thanks again for your help!

I’d also be a bit careful using the oF addon for supercolllider. Seems like an old and unmaintained project. Might be simpler and better just to go for raw osc messages instead.

1 Like

For the moment we are working with ofxsupercollider, and it seems to adhere correctly to scsynth server command reference. We have made some additions to the original code to add some missing functions. You can find our fork here:

We still need to take a look on the bidirectional socket connection between both, but at the moment is giving us good results and performance.

Our final synthdef ended looking like this:

(
SynthDef(\dynklang, {
	arg out=0;
	var partials = 40;
	var chans = 8;
	var freqs = \freqarray.kr(220.dup(partials*chans)).reshape(chans, partials).flop;
	var amps = \amparray.kr(1.0.dup(partials*chans)).reshape(chans, partials).flop;
	var transpose = \transpose.kr(0!chans).dup(partials).midicps;
	var largeSig = SinOsc.ar(freqs + transpose, mul: amps);
	var flat8 = Mix.ar(largeSig);
	Out.ar(out, flat8);
}).add
)

Thank you all very much for your unvaluable help.
We have now this working as expected, and having the best fun with zillions of oscillators.

That’s great news about the fork of the of extension! I usually control of from supercollider, not the other way around, but now I will definitely take a look into how to use the extension for some sonification ideas I have :blush:

1 Like