Norns - SuperCollider Engines Basics

Hi,

I can’t seem to find any basic intros to writing SuperCollider engines, and integrating them with the Norns system.

Can anyone help me out?

5 Likes

we’re planning a tutorial/study, but in the meantime check the inline comments of these:

keep in mind that these are not a supercollider tutorial— sc knowledge is a prerequisite.

5 Likes

Cool, thanks!

I’m working on that one :wink:

Slowly…

Ah, so you have to define a “synth”, but it can be a self-playing SynthDef, or a more generic function.

Presumably if it was a function, it could do general-purpose sclang stuff, before instantiating a SynthDef to create/process audio.

I guess the synth def/function has to provide the means to integrate with Lua and the wider Norns environment via its arguments.

Can you pass data back from the engine to a Lua script (for example to update onscreen parameter values on preset change?

Does SC have access to the systems MIDI io if available?

Can a Lua script communicate with an engine via an internal MIDI bus?

So many questions…

Engines basically encapsulate arbitrary sclang code. you can use as many Synths, Busses, Groups, Patterns, Events, <blahblah> as you want. the only rules are:

  • you can’t use graphics stuff
  • you must free all server resources that you allocate in the free {} method (synths, buffers &c)
  • it’s probably a good idea to allocate all resources in your engine’s init {} method

and you can use the superclass addCommand() and addPoll() methods to define your engine’s interface to norns lua.

Does SC have access to the systems MIDI io if available?

yes, though it is entirely independent of the norns UI so it may be confusing to use or customize.

oops sorry as of 2021 I think thia is not actually true.

Can a Lua script communicate with an engine via an internal MIDI bus?

not without some pretty lightweight hacking of linux stuff.
update [december 2020]: a virtual MIDI device is now included out of the box.

Can you pass data back from the engine to a Lua script (for example to update onscreen parameter values on preset change?

yes, we call an sc->lua channel a “poll”, and a lua->sc channel a “command.” polls are less flexible, basically callback for a single float or a byte array. commands are more flexible, like remote procedure calls with different parameter types. see e.g. Glut engine for example of polls (it’s how grain positions are conveyed to lua.)

3 Likes

If I can help at all on this front please let me know – I’ve picked apart enough on my own to do mods of the templates @zebra and others have mentioned in other threads, but I figure if I can help with the tutorial I’ll also gain a deeper understanding of the system at large (and it will also help me keep up my SC chops)!

2 Likes

This is great info, thanks @zebra!

Could you recommend an example of this?

So better to do MIDI interface stuff in the Lua scripts, then?

Do you mean “heavyweight” there?

I’m planning to use busses to communicate parameter values to the percussion synth engine I’m working on, so I can use commands to write data to the busses on Norns, and write an alternative function to consume MIDI CCs to control the kr busses, in other contexts.

Now I need to keep banging my head against SuperCollider.

Then Lua.

1 Like

Could you recommend an example of this?

literally every engine. e.g.:

in alloc{} a synth is created. in free{} it is freed.

is there some specific thing that is not making sense?

not really, but on reflection i suppose i should have.

So better to do MIDI interface stuff in the Lua scripts, then?

IMHO, generally yes, for the reasons i’ve alluded to. but if you simply want to launch existing sclang code that is self-configuring or fixed-configuration, and don’t care about the norns UI, then that would be a notable exception.

What do “ii” and “if” mean, in this context (Engine_Glut.sc)?

this.addCommand("gate", "ii", { arg msg;
			var voice = msg[1] - 1;
			voices[voice].set(\gate, msg[2]);
		});

		this.addCommand("speed", "if", { arg msg;
			var voice = msg[1] - 1;
			voices[voice].set(\speed, msg[2]);
		});
1 Like

they are OSC message format strings. "ii" means that the command takes two integer arguments. (which are present in the msg argument array at locations [1] and [2] - location [0] is the name of the message, viz. "gate".)

likewise "if" is an integer argument followed by a float argument.

2 Likes

I asked in discord but having the answer searchable in lines would be better: is there a way of grabbing supercollider logs from the last supercollider crash?

I’m doing something in my nascent engine that’s causing the web ui to go back to “not connected to supercollider” (and audio stops, and I have to restart sc) very occasionally, and I think logs would help me debug.

from a shell session on norns you can do journalctl -u norns-sclang

3 Likes

Still learning how to use my own SC engines in norns. For this, I need to define how Lua can control the SC engine (amongst other things). This is done like this:

this.addCommand("amp", "f", {
arg msg;
synthDrone.set(\amp,msg[1]);
});

I am trying to understand what this means, and I believe I now understand this much: It will allow to set the parameter \amp of my SynthDef synthDrone to whatever value is being supplied by the Lua parameter called amp (which is of the float type, set by "f"). The “vehicle” to transmit these informations from Lua to Supercollider is an OSC message, that’s why msg is used as an argument in the function, and it is item [1] of the message part of the OSC message that is transmitting the value, while the invisble msg[0] part is the path of the OSC message. If this is correct, I have some follow-up questions:

  • It is my understanding that I have to specify the value type of a parameter, like “f” for float and “i” for integer. What would be the value type for an array? And where can I look up these value types?

  • If I want to use an array as a value type, it would still count as one element in the message, so if I only hand over an array, it would still be msg[1], correct?

  • I recently learned that instead of declaring an arg in a SynthDef, I can use NamedControls. Can this.addCommand address a NamedControl the same way as it addresses an arg, like \myNamedControlName?

Thanks much in advance!

2 Likes

someone might have a better answer, but my cave man solution has just been to add as many arguments to the command as needed, so sending an array from lua would look like this

engine.send_array(array[1], array[2], array[3], array[4])

in supercollider that would look like this:

this.addCommand("amp", "ffff", {
})

(just add as many type-letters as there are arguments)

(if you’re trying to send long or variable length arrays this method still works but I can expand on my answer to make the code less painful in this case)

yea the set syntax is the same for NamedControls and regular args

2 Likes

Thanks, so this means that in SC an array determining for example the frequencies of five partials would be like this, no?

this.addCommand("partial_freqs", "fffff", {
arg msg;
addSynth.set(\partial_freqs,msg[1]);
});

close ! this is probably best:

this.addCommand("partial_freqs", "fffff", {
arg msg;
addSynth.set(\partial_freqs, [ msg[1], msg[2], msg[3], msg[4], msg[5] ]);
});

msg is an array with the OSC path at 0 followed by all args

1 Like

Great, thanks, I’ll try that one!