How to send the same audio from a Synth to two or more different busses

I’m often finding myself needing to send the output from a SynthDef to two or more separate buses on an as-needed basis - for example, for traditional Send FX busses, reverbs, etcetera, or when I want to record a bunch of specific individual instruments into separate files while also hearing them.

I know I can do this by defining an arrayed control in a SynthDef, like:

a = Bus.audio(s, 1);
b = Bus.audio(s, 1);

SynthDef(\test, {
	Out.ar(\out.kr([0, 1]), SinOsc.ar * 0.1)
});

Synth(\test, [\out, [a, b]]);

This sends a sine wave to buses a and b. But (if I’m understanding the documentation correctly), this Synthdef must now always have two output busses. If I run this:

Synth(\test, [\out, [a]]);

Then the signal still plays through my speakers, since it’s still playing on Bus 1. So I can’t use this same SynthDef for both single-output and double-output. I could work with this, but I’d have to define a new SynthDef for every time I want to send the audio from that SynthDef to multiple locations (for two, three, four outputs).

I can think of a couple ways I could maybe handle this but they all seem very hacky. Is there something I’m missing?

Hi,

you’re free to include several Out ugens in one SynthDef. Also you can define a bus with a max channel number. Taking these options together should enable building a SynthDef flexible enough for your needs. Sometimes a separate routing SynthDef can also be helpful.

best

Daniel

I like routing synthdefs, like I like analog and digital patchbays. Something nice about not having to prepare you synthdefs in any specific way and let another element handle the routing. Just my 2 cents.

A little tweak can suppress output to out-of-range bus numbers:

SynthDef(\test, {
	var maxbus = NumAudioBuses.ir - 1;
	var sig = SinOsc.ar * 0.1;
	\out.kr([0, -1]).do { |out|
		Out.ar(out, sig * InRange.kr(out, 0, maxbus))
	};
});

This doesn’t exactly roll off the tongue, but it could be written into a helper function.

But I agree that routing synths are better! Using the ddwMixerChannel quark:

m = MixerChannel(\source, s, 2, 2);
m.level = 0.15;

a = m.play {
	var trig = Impulse.ar(2.2);
	var freq = TExpRand.ar(200, 800, trig);
	var eg = Decay2.ar(trig, 0.02, 0.15);
	var pan = TRand.ar(-0.6, 0.6, trig);
	Pan2.ar(SinOsc.ar(freq), pan) * eg
};

r = MixerChannel(\reverb, s, 2, 2, completionFunc: { |chan|
	chan.playfx { |out|
		var sig = In.ar(out, 2);
		FreeVerb2.ar(sig[0], sig[1], 1, 0.97, 0.2)
	}
});

m.newPostSend(r, 0.6);

d = MixerChannel(\delay, s, 2, 2, completionFunc: { |chan|
	chan.playfx { |out|
		var sig = In.ar(out, 2);
		CombC.ar(sig, 1, Array.fill(2, { Rand(0.08, 0.14) }), 4)
	}
});

m.newPostSend(d, 0.75);

By the end of this, m’s signal is going to three places, just by adding routing synths. The source did not have to be rewritten to account for it.

Edit: You don’t strictly need specifically MixerChannel to implement sends. The quark packages the functionality in a convenient way, but under the hood, it’s only using synths and buses (and groups for maintaining evaluation order).

If you need different routing per-note, ddwPlug can do that too.

hjh

This is very helpful, thank you! I avoided using buses and groups at all for years mostly because of the pain of keeping order of operations straight. This MixerChannel class is really a breath of fresh air - took a day or two to figure it out but this is exactly what I was hoping for.

If you want to do this pretty easily with no libraries, you can just use a “patch” synth that takes in / out arguments and play as many as needed to send the same signal to different places:

SynthDef(\patch1, {|in, out|
Out.ar(out, In.ar(in, 1));
}).add;

a = Synth(\someeffect, [\in, someinbus]);
b = Synth(\somesound, [\out, somesoundbus]);

p = Synth.before(a, \patch1, [\in, somesoundbus, \out, someinbus]);
o = Synth.after(b, \patch1, [\in, somesoundbus, \out, 0]);

Etc.

So, every Synth has it’s own dedicated in and / or out busses, and you “patch” between them. Add another def with InFeedback for patching “up” the node tree.

Hope that makes sense. Typing on phone, so maybe a little sloppy.

checkout the Monitor class for another built-in option!

This is also a helpful break down: Bus routing: ddwMixerChannel's approach