0

How do I send FFT information of phases and amplitudes to an oscillator bank in SuperCollider? It seems that I've misunderstood something but can't figure out what is wrong in my code:

    msg = osc_message_builder.OscMessageBuilder(address = "/s_new 100 1 1 oscBank512")
    msg.add_arg(" amplitude ", amplitudes)
    msg.add_arg(" phase ",phases)
    msg.add_arg(" attackSynth ", 0.1)
    msg.add_arg(" releaseSynth", 0.5)
    msg = msg.build()
    client.send(msg)

So my oscillator bank has 256 SinOscs and I'd want to send that amplitude and phase information for them, they are in those arrays(amplitudes, phases). However python-osc doesn't allow me to do that. It says:

ValueError: arg_type must be one of ('f', 'i', 'b', 's', 'T', 'F')

So how would I construct that OSC message properly? Python-osc tutorial is really minimal and couldn't find any help online. I'd use pyOSC or OSC but pip wasn't able to install them so I'm stuck with python-osc.

SuperCollider code for the oscillator bank:

    SynthDef.new(\oscBank512, {
    arg attackSynth=0.1, releaseSynth=0.1;
    var dmplitudeReceive = \dmplitudeReceive.kr( 0.0!256 );
    var phaseReceive = \phaseReceive.kr( 0.0!256 );
    var osc1, envelopeSynth, sig, amp, freq, phases, amps,out;

    freq = Array.fill(256, {
        arg i, j=(22050/256), k=(j/2);
        if(i==0){i*j}{(i*j)-k};
    });
    amp = EnvGen.kr(
            envelope: Env.perc(
                attackTime: attackSynth,
                releaseTime: releaseSynth,
                level: 1,
                curve: -4),
            doneAction: 2);

    sig = SinOsc.ar(freq:freq,  mul: dmplitudeReceive, phase: phaseReceive);
    sig = sig*amp;
    sig = Limiter.ar(in:Mix.new(sig), level:1);


    Out.ar(0, Pan2.ar(sig));
}).add;

The dmplitudeReceive is named with d on purpose.

Biffen
  • 6,249
  • 6
  • 28
  • 36
bnc
  • 57
  • 2
  • 14
  • This is not a SuperCollider question. – Dan Stowell Feb 24 '17 at 08:19
  • You are right, I removed the SuperCollider tag. – bnc Feb 24 '17 at 11:56
  • I think the supercollider tag would have been appropriate; The message formatting here is specific to creating synths in supercollider's synthesis server (scsynth), but not supercollider's language (sclang). – Charles Martin Feb 27 '17 at 09:04
  • @buoyancy Could you please post in the synthdef for "oscBank512" - it would be easier to find a solution with that information – Charles Martin Feb 28 '17 at 08:39
  • Great! The current behaviour is to put each SinOsc in a different output channel. If you want to mix them all together, you should put `Mix(sig)` in the third last line instead of `Pan2.ar(sig)`. – Charles Martin Feb 28 '17 at 11:04
  • Oh, I thought Pan2 would take care of the multichannel expansion. Will fix it, thanks! About the one synth/voice method: how is it possible to send 256 /s_new messages for the server simultaneously? I'm just wondering how the OSC can deal with that amount of messages, if there's a delay between the synths then it would make a melody, instead of a one sound. This ended up being more difficult than I thought, that lack of arrays in py-osc is pretty annoying. – bnc Feb 28 '17 at 17:13
  • pyOSC would send the messages sequentially, not in parallel, but the message sending time would be very small - maybe less than milliseconds? It definitely wouldn't make a melody, but might mess up the phase of your sounds if you're trying to resynthesises some FFT data accurately. You could also create all the synths but not have them play right away, trigger them all as a group on the server after sending the phase and amplitude settings - this might be the better plan anyway for best performance? You might have to try a couple of ways and see how the results sound! – Charles Martin Mar 01 '17 at 13:51
  • I did quite extensive searching for this problem. Apparently you can send arrays to SC server by using $[ as a starter tag and $] as a closer. "This message now supports array type tags ($[ and $]) in the control/value component of the OSC message." from: http://doc.sccode.org/Reference/Server-Command-Reference.html Also found out a right way to set arrays as arguments in side a synth def, I updated the post for the correct version of the synth. I still have problems that the values are not set correctly, when I poll the values for amps and phases they are all just 0. – bnc Mar 06 '17 at 19:22

2 Answers2

1

The address in this message should be just "/s_new", everything else is a separate argument. The keys and values go into the message sequentially and SuperCollider should be able to sort them out.

This code adds in the argument one at a time with their type. I would have thought that each amplitude and phase would have a different name in your synthdef that you would have to address separately. If not, they might end up with a different integer index that you can address.

from pythonosc import osc_message_builder
from pythonosc import udp_client

client = udp_client.SimpleUDPClient('localhost', 57110)

amplitude = 0.5 # just adding one amplitude
phase = 0.5 # just adding one phase

msg = osc_message_builder.OscMessageBuilder(address = '/s_new')
msg.add_arg(100, arg_type='i')
msg.add_arg(1, arg_type='i')
msg.add_arg(1, arg_type='i')
msg.add_arg('oscBank512', arg_type='s')
msg.add_arg('amplitude', arg_type='s')
msg.add_arg(amplitude, arg_type='f')
msg.add_arg('phase', arg_type='s')
msg.add_arg(phase, arg_type='f')
msg.add_arg('attackSynth', arg_type='s')
msg.add_arg(0.1, arg_type='f')
msg.add_arg('releaseSynth', arg_type='s')
msg.add_arg(0.5, arg_type='f')
msg = msg.build()
client.send(msg)

Unfortunately python-osc doesn't support the array type for OSC messages which SuperCollider uses to send arrays of arguments to the synthesis server. I'm not aware of a Python OSC library that does support arrays in messages, so you might have to think of another way to achieve what you want.

One option might be to have a much simpler synthdef with one oscillator and generate 256 of them (or more!) as needed. That way you don't have to try to send 256 values when creating one synthdef.

Charles Martin
  • 338
  • 2
  • 13
  • Yeah that seems to be right, and I was able to send stuff over to the server. I still wasn't able to sort out the array problem how to send the phases, I did it by adding the phases and amplitudes with for-loops one by one. But I'm not sure if it should add word 'phase' before every value, or does it compile to kind of list in the server, so I should address the values by index. Apparently many uGens were created but not any sound coming out. Thank you for your answer! I also commented your comment below, sorry for inconvenience, noticed this answer after commenting :) – bnc Feb 27 '17 at 19:40
  • OK, I found out that the array type in OSC messages is pretty rare and not supported in python-osc. (It's listed as an unsupported type in the [OSC 1.0 spec](http://opensoundcontrol.org/spec-1_0)). Maybe it would be better to avoid using arrays altogether - just make 256 individual synth, rather than one with 256 voices. – Charles Martin Feb 28 '17 at 11:02
0

Look at the arguments to the add_arg function: arg_value, arg_type=None. You're trying to add a "key" and a "value" but there's no way to add a "key".

You simply have to add the values "anonymously". When you receive them, you decode them by knowing which order you added them.

Dan Stowell
  • 4,618
  • 2
  • 20
  • 30
  • Thank you for your answer. I'm trying to create a new instance of the Synth every time I send the information to the server. I've done this before with Max/MSP and ableton. How does the server now know to which argument to allocate the arrays of phases and amplitudes as I can't assign any keys? With my Max solution I just sent everything as a big string. So just to clarify, I'm trying to send the message straight to the server, not the language, so how do I decode it that way? – bnc Feb 24 '17 at 12:26
  • I see. The [SuperCollider server command reference](http://doc.sccode.org/Reference/Server-Command-Reference.html) tells you that the `s_new` command DOES accept keys as well as values, even though the OSC doesn't "know" that's what's happening. So, add the key, add the val, add the key, add the val, until you're done. However I don't remember how you deal with arrays of data. – Dan Stowell Feb 25 '17 at 13:11
  • Can you really have one key and then a number of control values in the `s_new` message? [The docs](http://doc.sccode.org/Reference/Server-Command-Reference.html) suggest to me that the arguments must alternate between control names/indexes and control values. Surely, even if you hadn't named each amplitude and phase separately, it would have a different index. – Charles Martin Feb 27 '17 at 09:29
  • That is probably true as I don't have a working version of this thing yet, probably because of this exact thing. If I've got it right, it is possible to do additive synthesis by having an array as a frequency, so for the 256 bins I've it like this: freq = Array.fill(256, { arg i, j=(22050/256), k=(j/2); if(i==0){i*j}{(i*j)-k}; }); But I've no idea how to do it for the phases, I had this version at one point, but it didn't produce a working solution: phases = Array.fill(256, {phaseReceive}); Where phaseReceive is the key, so I'm not sure if might work with phaseReceive[i]? – bnc Feb 27 '17 at 19:33
  • Ok that looks like supercollider code - I thought the question here was how to control scsynths using just OSC messages to the server. There's not necessarily a clear parallel between how you would achieve something with sclang and OSC even if the output is the same. – Charles Martin Feb 28 '17 at 08:44