Switch with OSC msg to get different sample according to data


#1

Hi!
I’m new to SC and the whole music programming thing in general.
I’ve done a python app, that reads a text and sends word by word to SC through OSC.
The text is only the words ‘miau’ and ‘guau’ repeated for fun and to try it out.

The OSC communication is working, but I hear no sound. I’ve played my buffers separately, and they are working.

The SC code is this:

s.boot;

~b0 = Buffer.read(s, "/path/to/bd/BT0A0A7.wav")
~b1 =Buffer.read(s, "/path/to/hh/000_hh3closedhh.wav")

~b0.play; 

(
SynthDef.new(\playbuf, {|amp=1, out=0, buf, da=2, rate =1|
var sig;
sig = PlayBuf.ar(2, buf, BufRateScale.kr(buf) * rate, doneAction:da);
sig = sig*amp;
Out.ar(out, sig);
}).add;
)

Synth.new(\playbuf, [\buf, ~b1.bufnum]);

(
OSCdef.new("texto",{
	|msg, time, addr, port|
	msg[1].postln;
	switch(msg[1],
		"miau", {Synth.new(\playbuf, [\buf, ~b1.bufnum])},
		"guau", {Synth.new(\playbuf, [\buf, ~b0.bufnum])}
		);

},
'/supercollider',
)
)

When I play the buffers or the SynthDef I can hear the samples being played, so I assume the issue is on the switch.
Another thing kind of weird happening, is that on the SC console I get three times the same word than on the text and on terminal (that python prints each word). So for each ‘miau’ on the txt/terminal, I get ‘miau miau miau’ on the SC console.

Thank you (:


#2

Your code looks more or less fine, you’re totally on the right track —

One very subtle gotcha, and the reason you’re probably getting multiple posts… OSCdef's are unique per name - they get defined if they don’t already exist, and the old one is overwritten if it does. BUT - they are intended to be used with a symbol as a name, e.g. \texto, not a string like "textto". Two separate usages of a \symbol point to the same global, unique object - but two usages of "string"s are just two different objects that happen to have the same content. This rarely matters, but it does matter for OSCdef and other dictionary-like objects (Ndef, Pdef, etc), which expect a \symbol for a name and use the identity of that name to store them in an IdentityDictionary. Each time you define your OSCdef, it’s seeing a new string, and creating and storing a NEW OSCdef based on the identity of that string (but not the characters inside of it).

There’s more info in the help file for Symbol, but the short answer is: use a \symbol for the name of the OSCdef and you’ll never need to worry about this again.

It’s hard to see from the code why the switch doesn’t work? What’s does the entire message look like, e.g. msg.postln? Or msg.asCompileString.postln for that matter?


#3

You already explained the reason – switch checks for identity, not equality.

We can use \symbols for matching in switch.

We cannot use "strings" for matching in switch.

FWIW all strings arriving in OSC messages render into Symbols. Currently, in SC, you will never get a String object in an OSC message array.

	switch(msg[1],
		\miau, {Synth.new(\playbuf, [\buf, ~b1.bufnum])},
		\guau, {Synth.new(\playbuf, [\buf, ~b0.bufnum])}
	);  // fixed indentation

(Also, scztt is correct about naming your OSCdefs with Symbols – always write OSCdef(\name...), never OSCdef("name"...).)

hjh


#4

I thought when I was writing my response originally that switch matches on identity, but I double checked it and this is actually not true:

(
a = "test-a";
switch(a,
	"test-a", { "a".postln },
	"test-b", { "b".postln }
)
)
// Or, try something with a more complex ==, just to make sure we're not getting lucky somehow
(
a = [1, 2];
switch(a,
	[4, 3], { "a".postln },
	[1, 2], { "b".postln }
)
)

But I did NOT actually know that all strings in OSC messages are turned into symbols - THIS is why the switch isn’t working. @jamshark70’s last code snippet should do the trick.

It turns out that there are still buried gotchas around symbols and identity in SC for both of us. :slight_smile:


#5

Oh, OK, something must have changed at some point.

The compiler can do one of two things with a switch:

  • Push all the arguments as given and call the normal switch method. Object:switch does use ==.

  • Or, build a hash table of matching values --> “jump forward” indices – an optimized form which, for big switch statements, is a lot faster.

I thought the compiler would optimize all switch statements where all of the values are literals, including string literals, but it doesn’t. It will optimize numeric, character and symbol literals, but not strings.

// symbol literal: "ControlOpcode" indicates the optimized form
{ switch(a) { \a } { 1 } }.def.dumpByteCodes
BYTECODES: (10)
  0   12       PushInstVar 'a'
  1   40       PushLiteral instance of Array (0x55ceabdc1658, size=8, set=3)
  2   8F 1C    ControlOpcode
  4   64       PushSpecialValue 1
  5   FC 00 01 JumpFwd 1  (9)
  8   6E       PushSpecialValue nil
  9   F2       BlockReturn

// string literal: "SendSpecialMsg" indicates a normal method call
{ switch(a) { "a" } { 1 } }.def.dumpByteCodes
BYTECODES: (9)
  0   12       PushInstVar 'a'
  1   04 00    PushLiteralX instance of FunctionDef - closed
  3   04 01    PushLiteralX instance of FunctionDef - closed
  5   B0       TailCallReturnFromFunction
  6   C3 11    SendSpecialMsg 'switch'
  8   F2       BlockReturn

One catch is that the compiler optimizes Float literals, but these are not necessarily OK for the optimized form.

switch(0.1 + 0.1 + 0.1) { 0.3 } { "yep" } { "nope" }
-> nope

hjh


#6

Hi! It totally works now, thank you for your throughtful response, I learnt a lot from this conversation (:


#7

Hi! Thank you for your response, it works now (: I learnt a lot from this conversation, good to read people so invested in explaining things so carefully!