How to set oscillator signal in arg

Hello.
I’m trying to set oscillator signal into “arg” parameter in SynthDef.
But this code output some error…
Anyone can help?

s.boot;

(
SynthDef(\test, {
	arg freq = 440;
	var out = {SinOsc.ar(freq!2)};
	Out.ar(0, out);
}).add;
)

x = Synth(\test);
x.set(\freq, SinOsc.ar(440)); // error!
x.free;

console:

CALL STACK:
	Exception:reportError
		arg this = <instance of Error>
	Nil:handleError
		arg this = nil
		arg error = <instance of Error>
	Thread:handleError
		arg this = <instance of Thread>
		arg error = <instance of Error>
	Object:throw
		arg this = <instance of Error>
	UGen:asControlInput
		arg this = <instance of SinOsc>
	Object:asOSCArgEmbeddedArray
		arg this = <instance of SinOsc>
		arg array = [*1]
	< FunctionDef in Method SequenceableCollection:asOSCArgArray >
		arg e = <instance of SinOsc>
	ArrayedCollection:do
		arg this = [*2]
		arg function = <instance of Function>
		var i = 1
	SequenceableCollection:asOSCArgArray
		arg this = [*2]
		var array = [*1]
	Node:set
		arg this = <instance of Synth>
		arg args = [*2]
	Interpreter:interpretPrintCmdLine
		arg this = <instance of Interpreter>
		var res = nil
		var func = <instance of Function>
		var code = "x.set(\freq, SinOsc.ar(440));"
		var doc = nil
		var ideClass = <instance of Meta_ScIDE>
	Process:interpretPrintCmdLine
		arg this = <instance of Main>
^^ The preceding error dump is for ERROR: can't set a control to a UGen

Unfortunately you can’t do that… I wish it was that simple though :slight_smile:

You have two options :

Using buses

See http://doc.sccode.org/Tutorials/Getting-Started/11-Busses.html

SynthDef(\test, {
	Out.ar(\out.ar(0), SinOsc.ar(\freq.kr(440) ! 2));
}).add;

SynthDef(\modulator, {
	Out.kr(\out.kr(0), SinOsc.kr(\freq.kr(1)).range(440,600));
}).add;

~bus = Bus.control(s,1);
~testSynth = Synth(\test);
~modulatorSynth = Synth(\modulator, [out: ~bus]);
~testSynth.map(\freq, ~bus);

Using JITLib

The JIT lib makes the creation of buses invisible for this kind of use case.

See http://doc.sccode.org/Overviews/JITLib.html

Ndef(\test, {
	SinOsc.ar(\freq.kr(440) ! 2);
}).play;

Ndef(\modulator, {
	SinOsc.kr(1).range(440,600) 
});

Ndef(\test).map(\freq, Ndef(\modulator));

Good luck !

(
SynthDef(\test, {
arg freq = 440;
var out = SinOsc.ar(freq!2);
Out.ar(0, out);
}).add;
)

x = Synth(\test);
x.set(\freq, 220); // put only the frequency, (not SinOsc.ar)
x.free;

But I may not have understood the question correctly?

Thanks!!! I didn’t know “bus” and “JITLib”, and tutorial links are really helpful…
Its can work collectly like I thought, thank you​:sob::sob::sob:

@Geoffroy I am new to the language and still getting my head around ugens and and how they are instantiated. I have some questions about your solution. See comments below. There are two things I am confused about, the use of map and setting the control bus as an argument instead of directly in the UGen.

// This is rewritten how I am used to writting sc. It works.

SynthDef(\test, {
	arg freq = 440, out = 0;
	Out.ar(out, SinOsc.ar(freq ! 2));
}).add;

SynthDef(\modulator, {
	arg freq = 1, out = 0;
	Out.kr(out, SinOsc.kr(freq).range(440,600));
}).add;

~bus = Bus.control(s,1);
~testSynth = Synth(\test);
~modulatorSynth = Synth(\modulator, [\out, ~bus]);
~testSynth.map(\freq, ~bus);

// Now, there are a couple of things here that seem odd to me. The use
// of `map` and having to set `out` to bus as an argument after the
// synth is instantiated.

// First, if `~bus` is a global variable, why can't it be used in the
// `SynthDef` directly? This does not work. I am sure there is a good
// reason, I would just like to know why.

SynthDef(\test, {
	arg freq = 440, out = 0;
	Out.ar(out, SinOsc.ar(freq ! 2));
}).add;

SynthDef(\modulator, {
	arg freq = 1;
	Out.kr(~bus, SinOsc.kr(freq).range(440,600));
}).add;

~bus = Bus.control(s,1);
~testSynth = Synth(\test);
~modulatorSynth = Synth(\modulator);
~testSynth.map(\freq, ~bus);

// Second, the use of `map` over `set`. If I look up the help for map
// I see several help files. I assume this is the map for `NodeEvent`,
// but it's unclear why `set` doesn't work here. `set` is doing
// something, I'm not sure what...

SynthDef(\test, {
	arg freq = 440, out = 0;
	Out.ar(out, SinOsc.ar(freq ! 2));
}).add;

SynthDef(\modulator, {
	arg freq = 1, out = 0;
	Out.kr(out, SinOsc.kr(freq).range(440,600));
}).add;

~bus = Bus.control(s,1);
~testSynth = Synth(\test);
~modulatorSynth = Synth(\modulator, [\out, ~bus]);
~testSynth.set(\freq, ~bus);

// First, if ~bus is a global variable, why can’t it be used in the
// SynthDef directly? This does not work. I am sure there is a good
// reason, I would just like to know why.

My advice is to read Client vs Server | SuperCollider 3.12.2 Help and 10. SynthDefs and Synths | SuperCollider 3.12.2 Help

Basically, when you create a SynthDef, the value of the variables are evaluated (i.e. “freezed”) when you evaluate the SynthDef, and the synth definition is sent to the server.

In your code, ~bus does not exist when you instantiate the SynthDef, hence the error.

// Second, the use of map over set. If I look up the help for map
// I see several help files. I assume this is the map for NodeEvent,
// but it’s unclear why set doesn’t work here. set is doing
// something, I’m not sure what…

If you “set” the value of a Synth parameter, it sets a fixed value to the parameter.
If you “map” a Synth parameter to a bus, SuperCollider will know to get the value in the bus at each processing block to update the Synth parameter.

You can read that chapter of Nick Collins tutorial on control buses.

Hope it’s a bit more clear now :slight_smile:

2 Likes

@Geoffroy Great! Thanks for the clarifications!

Sooo, kinda offtopic, but, since we’re talking about control signals and busses, is there a way to .map a param of a synth instanciated by a pbind to a control bus??

Yep:

(
b = Bus.control(s, 1);
c = { Out.kr(b, LFDNoise3.kr(0.1).range(50, 90).midicps) }.play
)

(
p = Pbind(
    \freq, b.asMap,
    \dur, 0.3
).play
)


c.free;
p.stop;
1 Like

Awesome, now I understand those ¨c0¨ on Nick Collins tutorial

Sorry to necropost, but I came across this thread yesterday, and it answered a question I’ve been wondering about for years! I’ve got this working using the Ndef / JIT lib method, but I want to use a whole bunch of these, and I’m finding myself writing a lot of repetitive code and copy/pasting, so I’m wondering if there’s a way to do this with iteration. The way I’m doing this now looks like this (wherein ~timer is a TempoClock):

copy/pasty scLang
Ndef(\1, {
		SinOsc.kr(~timer.beatDur/rrand(2,16)).range(-1,1)
	});
	Ndef(\2, {
		SinOsc.kr(~timer.beatDur/rrand(2,16)).range(-1,1)
	});
	Ndef(\3, {
		SinOsc.kr(~timer.beatDur/rrand(2,16)).range(-1,1)
	});
	Ndef(\4, {
		SinOsc.kr(~timer.beatDur/rrand(2,16)).range(-1,1)
	});
	Ndef(\5, {
		SinOsc.kr(~timer.beatDur/rrand(2,16)).range(-1,1)
	});
	Ndef(\6, {
		SinOsc.kr(~timer.beatDur/rrand(2,16)).range(-1,1)
	});
	Ndef(\7, {
		SinOsc.kr(~timer.beatDur/rrand(2,16)).range(-1,1)
	});
	Ndef(\8, {
		SinOsc.kr(~timer.beatDur/rrand(2,16)).range(-1,1)
	});

When I try to do something like this:

iteration
8.do({
		arg i;
		Ndef(i, {
			SinOsc.kr(~timer.beatDur/rrand(2,16)).range(-1,1)
		})
	})

I get this error:

Error
ERROR: Message '++' not understood.
Perhaps you misspelled '+', or meant to call '++' on another receiver?
RECEIVER:
   Integer 0
ARGS:
   Integer -2134076441
PATH: /Users/spencerkingmangraham/Desktop/sc docs/superLeid.scd
CALL STACK:
	DoesNotUnderstandError:reportError
		arg this = <instance of DoesNotUnderstandError>
	Nil:handleError
		arg this = nil
		arg error = <instance of DoesNotUnderstandError>
	Thread:handleError
		arg this = <instance of Thread>
		arg error = <instance of DoesNotUnderstandError>
	Object:throw
		arg this = <instance of DoesNotUnderstandError>
	Object:doesNotUnderstand
		arg this = 0
		arg selector = '++'
		arg args = [*1]
	NodeProxy:generateUniqueName
		arg this = <instance of Ndef>
		var uniqueName = -2134076441
	Object:buildForProxy
		arg this = <instance of Function>
		arg proxy = <instance of Ndef>
		arg channelOffset = 0
		var channelConstraint = nil
		var rateConstraint = 'scalar'
		var argNames = nil
	SynthDefControl:build
		arg this = <instance of SynthDefControl>
		arg proxy = <instance of Ndef>
		arg orderIndex = 0
		var ok = nil
		var rate = nil
		var numChannels = nil
		var outerDefControl = nil
		var outerBuildProxy = nil
		var controlNames = nil
	NodeProxy:put
		arg this = <instance of Ndef>
		arg index = nil
		arg obj = <instance of Function>
		arg channelOffset = 0
		arg extraArgs = nil
		arg now = true
		var container = <instance of SynthDefControl>
		var bundle = <instance of MixedBundle>
		var oldBus = nil
	NodeProxy:source_
		arg this = <instance of Ndef>
		arg obj = <instance of Function>
	Meta_Ndef:new
		arg this = <instance of Meta_Ndef>
		arg key = 0
		arg object = <instance of Function>
		var res = <instance of Ndef>
		var server = <instance of Server>
		var dict = <instance of ProxySpace>
	Integer:do
		arg this = 8
		arg function = <instance of Function>
		var i = 0
	Interpreter:interpretPrintCmdLine
		arg this = <instance of Interpreter>
		var res = nil
		var func = <instance of Function>
		var code = "8.do({
		arg i;
		Ndef(i, {
..."
		var doc = nil
		var ideClass = <instance of Meta_ScIDE>
	Process:interpretPrintCmdLine
		arg this = <instance of Main>
^^ ERROR: Message '++' not understood.
Perhaps you misspelled '+', or meant to call '++' on another receiver?
RECEIVER: 0

Any ideas as to how to go about creating Ndefs with iteration? It seems like there must be a way, but I can’t seem to find it.

I think it’s just that Ndef wants a Symbol as a key? I.e.

{
	8.do { | i |
		Ndef(i.asSymbol) {
			SinOsc.ar(i + 1 * 220, 0) / 180
		}.play;
		1.wait
	}
}.fork

yes! This is working. I didn’t know about .asSymbol, and that was definitely the missing ingredient. Thanks so much! Here’s the working version of what I was trying to do above:

works!
//modulators
	48.do({
		arg i;
		if(
			i < 9,
			{
				Ndef(i.asSymbol, {
					SinOsc.kr(~timer.beatDur/rrand(2,16)).range(-1,1)
				})
			}
		);
		if(
			(i > 8) && (i < 17),
			{
				Ndef(i.asSymbol, {
					SinOsc.kr(~timer.beatDur/rrand(2,512)).range(500,15000)
				})
			}
		);
		if(
			(i > 16) && (i < 25),
			{
				Ndef(i.asSymbol, {
					SinOsc.kr(~timer.beatDur/rrand(2,512)).range(0,1)
				})
			}
		);
		if(
			i > 24,
			{
				Ndef(i.asSymbol, {
					SinOsc.kr(~timer.beatDur/rrand(2,512)).range(0.3,0.5)
				})
			}
		)
	});