Norns: SuperCollider

Hi all.

Not sure if this is the best place to post this, but I’m struggling with getting SC code to run on the norns. I write in the SC IDE and then manually migrate over using the example engines. The following code throws an error after a reset, but I can’t see why. Any pointers would be very welcome.

// Engine_nornsBasicEngine

Engine_nornsBasicEngine : CroneEngine {
	var <synth;

*new { arg context, doneCallback;
	^super.new(context, doneCallback);
}

alloc {
	SynthDef("nornsBasicEngine", {
		arg out, hz=220, amp=0.5, amplag=0.02, hzlag=0.01;
		
		var amp_, hz_;

		amp_ = Lag.ar(K2A.ar(amp), amplag);
		hz_ = Lag.ar(K2A.ar(hz), hzlag);

		Out.ar(out, (SinOsc.ar(hz_) + (50 * SinOsc.kr([50,51]))).dup);
	}.play(args: [\out, context.out_b], target: context.xg);

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

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

free {
	synth.free;
}

}

Before adding the second SinOsc (kr) it worked fine, so I’m guessing it’s something to do with that.

Please help, I’m desperate to start making my own sounds :slight_smile:

1 Like

Would be good to include the error, maybe in a [details] box?

here, you’re adding the output of an audio rate UGen (SinOsc.ar) to the output of a control rate UGen (SinOsc.kr), which is weird and will maybe cause an error (from channel mismatch if nt rate mismatch - the modulator is stereo due to the array arg)

i’m guessing you want to add the .kr ugen to the frequency argument of the first sinewave, inside the parens. the stereo modulator will then cause the .ar sinewave to expand to stereo and you probably won’t want .dup

1 Like

I don’t get any error details, just the audio engine error on the norns main screen.

Is there a way to get deeper details on SC errors? I’ll tweak as you suggest and report back.

Lua is starting to make sense. SC though… it’s a puzzling creature.

you should be able to see SC output from the SC tab in maiden. since you’re sending the synthdef on the fly at engine alloc time, if the error is there it should show.

i’d try
Out.ar(out, SinOsc.ar(hz_ + (50 * SinOsc.kr([50,51]))) * amp_)

1 Like

When resetting the norns after making changes to the engine (that is still a thing right?) the norns displays (error: AUDIO ENGINE) and the SC tab in maiden shows nothing. Your code makes sense but hasn’t changed this error.

if you have an error in your engine class that prevents compilation, rather than just an error in the synthdef, then SC will not start at all.

you can diagnose this by manually starting sclang from a serial or ssh connection (just type sclang) and getting the resulting errors on the terminal

i’d recommend developing your engine on desktop first. we’ve written a lot about this on the existing norns dev/help threads.

2 Likes

Ahh, perfect. Thank you that solved it. I’d missed a ) while making the SynthDef, after the } and before .play.

sorry, i should have spotted that.

goes to show, really, it’s better to use an IDE that checks braces, auto-indents &c, esp. as SC is relatively verbose. i like scel b/c it works nicely with tramp-mode to remote into norns directly. (but other editors have similar features too, andit’s probably helpful to use ScIDE when you’re getting started - has the best docs integration, autocomplete &c)

1 Like

After transferring the engine into norns via sftp is debugging as easy as launching /usr/bin/sclang in a ssh shell? Does it check all the engines in all the folders?

SC recursively scans these locations in addition to the defaults:

if you add class files, you have to restart the sclang interpreter for them to be compiled. there are a few options:

  • kill and restart sclang from a terminal (yea, afaik that’s the right exec path). this puts sclang I/O on the terminal. (be aware that the lua process might be confused after this, since it wants to initiate handshake with sclang on start.)

  • use ;restart from maiden SC REPL these put sclang I/O on websocket for use with maiden. (you should get compile errors on the SC REPL in maiden, but i think there are some cases where you won’t.)

  • equivalently, restart the norns-sclang systemd service and maybe also norns-matron if you want lua stuff to keep working (the crone and JACK processes should be fine.)

  • to further debug compilation without terminal access, try executing thisProcess.recompile from SC REPL (again, lua process will be confused.)

  • just reset the norns

2 Likes

Superb, thank you :slight_smile:

Now I have it working I’m having a blast, so thank you for your help. Next share will be a drone generation program, have some strange sounds coming out the speakers. It’s like the aliens have landed.

One thing, synth.free doesn’t free the synth for me if I declare a synthdef with a name, but does if I just use synth = {…

I wonder what the benefit of having names synths is?

dunno, SynthDef.new(...).play should return a Synth that can then assigned to a variable, freed/paused/modulated/&c. (the {...}.play method for Functions is a shorthand that adds some extra stuff to the def, like a gate argument.) so i’m not sure exactly what problem you ran into. (i think you mean that you expected SynthDef.new(\foo, {...}) to create a clif.ent-side variable named foo, which it doesn’t - t he id foo is a property of the synthdef object, and it will be registered with the synthdef on the server side. so it should be unique among all synthdefs.)

if you haven’t yet, it would be a good time to look over the SC docs that introduce client/server architecture and all this stuff

i don’t use SynthDef.play() much. the only time it seems better than reusing a synthdef is if you are somehow generating the synthesis graph on the fly.

it’s often both cleaner and more efficient to define, compile, and re-use a synthdef.

here’s an example where we make a single synthdef, then make a bunch of randomized instances of it, then manage the lifecycle of those synth objects.

// do everyting in a Routine so we can do timing stuff
Routine {
	
	// define a synthdef for later use. this is a basic 2-operator sinewave FM sound
	~def = SynthDef.new(\fm2, { arg out=0, amp=0.25, hz=110, rat=1.5, idx=2.0, detune = 0.25;
		var car, mod;
		mod = SinOsc.ar(hz * rat);
		car = SinOsc.ar(hz * (1 + (mod * idx)) + [detune, detune * -1]);
		Out.ar(out, car * (Lag.kr(amp)));
	});
	
	// send the def to the server for immediate compilation
	~def.send(s);
	
	// wait for the server to be done with async commands (like compiling the def)
	s.sync;
	
	// make a bunch of synths, each using the def
	n = 10;
	~synths = Array.fill(n, {
		var params = [\amp, 0.05, \hz, 110 * (10.rand), \rat, 2 ** 2.0.rand2, \idx, 4.0.rand, \detune, 1.0.rand];
		params.postln;
// this is where we supply the name of the def we made
		Synth.new(\fm2, params, s);
	});
	
	// wait, then free the synths one at a time
	2.0.wait;
	n.do({ |i|
		~synths[i].free;
		(1 + 2.0.rand).wait;
	});
}.play; // play the routine
4 Likes