Array parameters for SC custom synth

I have a SC custom synth that has some parameters that receive arrays:

(
SynthDef(\fmous, {
        //...
	var ratios = \ratios.kr(#[1,1,1,1,1,1]);
        //...
).add;

How would I declare and use these parameters in Tidal?

I tried

let ratios = pF "ratios"

d1 $ s "fmous" # ratios [1.0 2.0 3.0 4.0 5.0 6.0]

and got this error

 <interactive>:115:10: error:
  9     • Couldn't match expected type ‘Pattern Double’
  8                   with actual type ‘[t5]’
  7     • In the first argument of ‘ratios’, namely
  6         ‘[1.0 2.0 3.0 4.0 5.0 6.0]’
  5       In the second argument of ‘(#)’, namely
  4         ‘ratios [1.0 2.0 3.0 4.0 5.0 6.0]’
  3       In the second argument of ‘($)’, namely
  2         ‘n (segment 4 $ irand 7) # s "fmous"
  1          # ratios [1.0 2.0 3.0 4.0 5.0 6.0]’

Do I need to declare it as a different type than pF?
Should I put colons between elements in the array? I tried but got a different error.

It’s not really possible at the moment. I’m not sure how SuperCollider sends/receives arrays. Do you know?

I’m not sure what you mean by “how Supercollider sends/receives arrays”. To the server, you mean?

In Supercollider, for the SynthDef arguments to accept arrays, they are declared as literal arrays and their length is fixed.

SynthDef(\sound, { | ..., freqs = #[100,200,300], ...|
    //...
}).add;

The SynthDef documentation says:

literal arrays
Arguments which have literal arrays as default values (see Literals) result in multichannel controls, which can be set as a group with Node-setn or /n_setn. When setting such controls no bounds checking is done, so you are responsible for making sure that you set the correct number of arguments.

The miSCelaneous quark has a help file named “Event patterns and array args” that explains how it handles them with Supercollider patterns.

Does it help?

Maybe https://hackage.haskell.org/package/hsc3-0.17/docs/Sound-SC3-Server-Command-Generic.html#v:n_setn or its source if you don’t want to depend on the hsc3 package? Not tried it though…

Hm thanks, I think n_setn is for communicating with the supercollider synthesiser (scsynth) though. Tidal communicates only via the language (sclang). Skimming this issue it seems that the supercollider language doesn’t currently accept arrays: https://github.com/supercollider/supercollider/issues/2889

Does your synth accept a fixed number of elements in the list? Then you could send as separate numbers. What would be the disadvantage of doing that?

If you don’t want to pattern individual numbers, then you could send as a string, and add something in superdirt to convert that to a list of numbers.

Another possibility is to send the values in an OSC blob. Here’s how that’s done for MIDI SysEx, including patterning values within the list:

If you need arrays to be passed to superdirt then it would be probably be best to create an issue here:
https://github.com/musikinformatik/superdirt

Thanks for clarifying. That’s very helpful.

I do have a version of the same synth with separate numbers, but it has a good amount of them. It’s using the FM7 UGen, that mimmics Yamaha’s DX7. The UGen accepts two versions, one with the algorithm and then an array of 6 items, each holding [freq, phase, amp]. The other version accepts this array and a matrix of 6x6 for modulating ops between them. So you get an infinite amount of possible algorithms (which is the one I’m using). Something like this:

FM7.ar([
        // controls
	[freq * ratio1, 0, amp1],
	[freq * ratio2, 0, amp2],
	[freq * ratio3, 0, amp3],
	[freq * ratio4, 0, amp4],
	[freq * ratio5, 0, amp5],
	[freq * ratio6, 0, amp6],
],[
        // modulators
	[mod11, mod12, mod13, mod14, mod15, mod16],
	[mod21, mod22, mod23, mod24, mod25, mod26],
	[mod31, mod32, mod33, mod34, mod35, mod36],
	[mod41, mod42, mod43, mod44, mod45, mod46],
	[mod511, mod52, mod53, mod54, mod55, mod56],
	[mod61, mod62, mod63, mod64, mod65, mod66],
]);

in Tidal it would be faster an easier to live code something like this:

d1 $ s "fm7" 
# mods1 [1, 0.5, 0, 0, 0 0]
# mods2 [0, 0, 0.3, 0.5, 0, 1]
-- ...

than

d1 $ "fm7"
# mod11 "1"
# mod12 "0.5"
# mod23 "0.3"
# mod24 "0.5"
-- ...

hmmm… or maybe not. LOL

There is a way to specify multiple parameters together on the tidal side. You have probably seen it with sound "bd:2" being the same as sound "bd" # n 2

You could do this:

mods1 = grp [mF "mod11", mF "mod12", mF "mod13", mF "mod14", mF "mod15", mF "mod16"]

Or the same more compactly:

mods1 = grp (map (mF . ("mod"++) . show) [11 .. 16])

and then use it like this:

d1 $ s "fm7" # mods1 "1:0.5:0:0:0:0"`
3 Likes

You’d probably still want to define the individual parameters:

mods11 = pF "mods11"
mods12 = pF "mods12"
mods13 = pF "mods13"
mods14 = pF "mods14"
mods15 = pF "mods15"
mods16 = pF "mods16"

Then you could do e.g. this sort of thing:

d1 $ every 3 (# mods13 0.3) $ s "fm7" # mods1 "1:0.5:0:0:0:0" |* mods15 (slow 8 sine)`
2 Likes

If you don’t mind sharing the FM patch I’d love to play with this myself

I’d love to share it. I uploaded it to this Gist.

Please let me know your thoughts about it.

In case you are further interested, I made a repo for the FM synth. I updated the synth with an LFO with freq and depthparameters and fixed rate behaviour which was wrong.

I also added a couple of examples.

Is there a way I could add the synth permanently without having to declare the custom parameters every time I want to use it?

a quikc&dirty method you could try is to send data to sc as strings and then interpret it within the synthdef

something along the lines of…

~data="[1.0 2.0 3.0 4.0 5.0 6.0]".replace(" ",",");
~dada=~data.interpret;
~dada.value.postln;

Besides being quick and dirty, have you tried this within a SynthDef? It throws an error. I’m sure it has to do with how SynthDefs are compiled, and the order of interpretation of the code. Anyhow I think @yaxu’s solution is the closest we can get at the moment.

I had assumed something like this would work (but doesn’t) …

SynthDef(\test, { arg freq = 432, out = 0, data = "test";
    data.postln;
    Out.ar(out, SinOsc.ar(freq, 0, 0.2));
}).add;

So i guess SythDef can only contain Ugens. Looking closer, i’ve only used string.interpret with PBind or osc messages…

Yes, that’s the same reason why you can’t use if statements in SynthDefs. All these are sclang items, and the SynthDef (once interpreted) is not sclang, but scsynth which works in a complete different way.

You could add them to a custom BootTidal.hs file. Or you could submit the synth to the SuperDirt repo and we can add the parameters to tidal itself :slight_smile:

1 Like

This is so useful, now I can do my shortcuts for delays and such without the spaces in the middle that I’ve done as groups.

Can these also be patterned? Will try it out as soon as I can.