Basic Coding Help!

Hello,
I am relatively new to SuperCollider. There is a very basic understanding that I am missing. It is probably simple but I don’t know what I don’t know.
In the code below, if I enclose the two synth creation arguments (m =… and x =…) in the same region/code block as the synth definitions, I get no sound. If instead, I run them separately after the SynthDef code block runs, all works well. Can anyone please tell me what I am missing? I would like to run all code at once rather than having to start multiple code blocks ‘by hand’.
Thank you in advance!

(
~masterBus = Bus.audio(s, 1);

SynthDef(\master, {
	| vol = 0.25 |
	Out.ar(0, In.ar(~masterBus)*vol); // <- In reads off the ~masterBus
}).add;

SynthDef(\osc, {
	arg out = 0, vol = 0.25; // <- NB! default output is arg
	var snd = SinOsc.ar(440);
	snd = snd * vol;
	Out.ar(out, snd)}
).add;
)  // <- if this is moved after 'm = Synth(\master...etc' there is no sound

x = Synth(\osc, [\out, ~masterBus], addAction: 'addToHead'); // <- NB! sets output to the master
m = Synth(\master, [\vol, 0.1], addAction: 'addToTail');

// <- if ')' is moved to here

Firstly, you need to enclose code you post in the backtick: ` not a quotation mark :slight_smile: - you can go back to edit your post using the pencil icon.

I added some comments to your code to explain the chain of events that cause you to not hear anything in this case:

(

~masterBus = Bus.audio(s, 1);
// A bus is allocated. There are a fixed number of buses on the server, so this is effectively instantaneous, as simple as picking the next free bus number. 

SynthDef(\master, {
	| vol = 0.25 |
	Out.ar(0, In.ar(~masterBus)*vol); // ← In reads off the ~masterBus
}).add;
// A SynthDef called \master is defined, and sent to the server. It can take time for the server to load this def, so master is NOT AVAILABLE RIGHT AWAY.

SynthDef(\osc, {
	arg out = 0, vol = 0.25; // ← NB! default output is arg
	var snd = SinOsc.ar(440);
	snd = snd * vol;
	Out.ar(out, snd)}
).add;
// Same with \master, this is sent to the server but not loaded immediately.

x = Synth(\osc, [\out, ~masterBus], addAction: \addToHead);
// A new \osc synth is created on the server. If you did NOT have SynthDef(\osc) already defined on the server, this will fail since the server has not loaded it yet.

m = Synth(\master, [\vol, 0.1], addAction: \addToTail);
// A new \master synth is created on the server. You JUST created and sent a \master SynthDef in this same block - because it's not instantaneous, it will not have been loaded yet. So: this Synth will EITHER point to the LAST \master you defined, or nothing if you haven't yet defined one. Importantly, since you are creating a new Bus each time you execute this code, your old \master is pointing to the OLD bus from the last time you ran it: you're always one step behind.
)

Here are three things you can do to improve this code. You don’t need to do ALL of these things, but they represent good practices and will avoid all kinds of subtle problems.

(
fork {
	// BUSES
	~masterBus = ~masterBus ?? { Bus.audio(s, 1) };

	// SYNTH DEFS
	SynthDef(\master, {
		|in, vol = 0.25|
		Out.ar(0, In.ar(in) * vol);
	}).add;
	
	SynthDef(\osc, {
		arg out = 0, vol = 0.25; // ← NB! default output is arg
		var snd = SinOsc.ar(440);
		snd = snd * vol;
		Out.ar(out, snd);
	}).add;
	
	s.sync;
	
	x = Synth(\osc, [\out, ~masterBus], addAction: \addToHead);
	m = Synth(\master, [\in, ~masterBus, \vol, 0.1], addAction: \addToTail);
}
)
  1. Only allocate a Bus if you have not already (?? only does the second half if ~masterBus is nil).
  2. Enclose your code in fork {} and run s.sync after you define your SynthDefs. This ensures that the audio server is synchronized, meaning it’s loaded everything you’ve requested.
  3. Make the argument to In a synth control like your \out control. This way, you aren’t baking your Bus INTO the SynthDef itself, but rather setting it as an argument when you create the Synth.

Finally: since you only need to allocate a Bus and define your SynthDefs once, but you may want to start and stop the Synths many times, it’s actually very reasonable to separate these into two chunks of code.

Thank you! I did go back and put the back tics in so the code looks correct. I understand that it takes some time for the server to recognize the SynthDef additions. Do I always have to wait and then create the Synths?

Wow! Thank you ! This is a whole new level that I was completely unaware of. I have some research to do… Thanks again.

Yes, you should definitely assume that you need to wait. There are cases where you may not - but, not waiting can produce very subtle bugs, so it’s much better to be very rigorous about this. EITHER separate your code into two parts - one chunk that you run a single time per launch of the audio Server, and one chunk that actually runs your Synths - and then execute them separately. OR, combine these two into a single block, make sure you don’t re-allocate Buses etc. using the ~foo = ~foo ?? { Bus.audio } pattern, and add a s.sync after all of your initial asynchronous load things.

Thank you again! This is great advice.