Where does the magic frequency 261.6255653006 come from?

Just checking that I haven’t changed my default synth setup:

SynthDescLib.global.synthDescs.at(\default);
// output 
-> SynthDesc 'default' 
Controls:
ControlName  P 0 out scalar 0.0
ControlName  P 1 freq control 440.0
ControlName  P 2 amp control 0.10000000149012
ControlName  P 3 pan control 0.0
ControlName  P 4 gate control 1.0

Also

SynthDescLib.global.synthDescs.at(\default).msgFunc.cs
// output
-> #{ arg out, freq, amp, pan;
	var	x812A0D17 = Array.new(8);
	out !? { x812A0D17.add('out').add(out) };
	freq !? { x812A0D17.add('freq').add(freq) };
	amp !? { x812A0D17.add('amp').add(amp) };
	pan !? { x812A0D17.add('pan').add(pan) };
	x812A0D17

So these seem the standard things, but…

x = Synth(\default);

(type: \set, id: x.nodeID, gate: 0).play

// output
-> ( 'amp': 0.1, 'gate': 0, 'server': localhost, 'freq': 261.6255653006, 
  'type': set, 'id': 1003 )

I know the above isn’t supposed to end/release x (because it misses the args: #[\gate] arg).

But my question is: why does the freq get set to the 261.625… value? Where does this (magic) constant come from? Is anyone else getting the same frequency-change behavior with that simple two-line code in their SC setup?

261.6255653006.cpsmidi // 60.0

This is middle C. Probably a default note value for an Event?

1 Like

Good point, but where could that possibly come from in a \set event? I tried a bit more debugging since set also looks at detune, but I’m not changing that either.

x = Synth(\default)

f = { [~freq, ~detune, ~detunedFreq].postln }
(type: \set, id: x.nodeID, gate: 0, finish: f, callback: f).play

Output

[ a Function, 0.0, a Function ]
[ 261.6255653006, 0.0, a Function ]
-> ( 'finish': a Function, 'callback': a Function, 'gate': 0, 'server': localhost, 
  'amp': 0.1, 'freq': 261.6255653006, 'id': 1010, 'type': set )

Actually, that’s more complicated as freq is itself a function, so ultimately that “60” indeed comes from midinote default value in Event

f = { ~midinote.value.postln }

x = Synth(\default)
(type: \set, id: x.nodeID, gate: 0, finish: f, callback: f).play

output

60.0
60.0
-> ( 'finish': a Function, 'callback': a Function, 'gate': 0, 'server': localhost, 
  'amp': 0.1, 'freq': 261.6255653006, 'id': 1012, 'type': set )
midinote: #{
	(((~note.value + ~gtranspose + ~root) /
		~scale.respondsTo(\stepsPerOctave).if(
			{ ~scale.stepsPerOctave },
			~stepsPerOctave) + ~octave - 5.0) *
		(12.0 * ~scale.respondsTo(\octaveRatio).if
			({ ~scale.octaveRatio }, ~octaveRatio).log2) + 60.0); // THIS
},
detunedFreq: #{
	~freq.value + ~detune
},
freq: #{
	(~midinote.value + ~ctranspose).midicps * ~harmonic;
},

By the way, amp is also “reset” this way in a set if not specified, it’s just not so obvious because the Event default amp value (0.1) coincidences with that of the \default synth (unlike for freq), but one can easily illustrate how amp gets “reset” too.

f = { ~amp.value.postln }
x = Synth(\default, [amp: 0.7])
(type: \set, id: x.nodeID, gate: 0, finish: f, callback: f).play

That’s why I personally recommend to use the set event type by specifying exactly which keys you want to be set: \args, #[name0, name1]. (\args itself has a default list of argument names, corresponding to the default SynthDef. That’s probably a minor blooper in the default event prototype.)

It’s possible to use the SynthDesc msgFunc in set as well but IMO this is not a good idea because, as you discovered, set will clobber your synth’s values with event defaults. I did, at one point in the past, think it would be better to use the msgFunc, but that was wrong.

Admittedly the documentation is not clear on this point.

hjh

In case anyone else is reading just this (and not the longer related threads), the quick way to enable auto-generation (via msgFunc) is to set args to a zero-size thing e.g. to [] or even (). That triggers an alternative code path in set. (However msgFunc is not pre-generated for some types of synths, e.g. those produced by Ndef/NodeProxy from a function–via ProxySynthDef.)

Thinking about this a bit more, on one hand it might be useful if set (and everything else in Event) could fetch not only the msgFunc from the synthDesc, which it does (if args are set to []), but perhaps also the default values for the synth controls… and then used those in preference to Event defaults if “nothing is specified” for some key in the event. Alas “nothing is specified” is tedious to detect in Event, as it does its own calculations which have a lot of inputs (esp. for the frequency stuff). So, one could implement such a policy more explicitly in a \finish callback that e.g. looks up in synthDesc only specific keys (say specified in a defArgs, possibly with [] meaning all found in the synthDesc) and sets only those if “not set” in event.

I almost started coding this idea, but I realised that’s actually inferior to just putting the synth defaults in another event/environment, and chaining that before the event… which is exactly what I usually do with a Pdef.envir. That approach, besides sidestepping the issue of detecting what’s “not set” in the Event, has an extra advantage that a persistent set of defaults, after being initialized with the synthdef ones, can be easily tweaked in a gui. When I find myself that I often change the default value of some control like that, I go back and change the synthdef default. Because of that workflow, in which I always have the defaults loaded in an chained envir, I didn’t even have much realization of Event defaults for things like amp, freq etc. And I didn’t need type: \set much until recently… for which getting the args constructed in that case is also rather easy as Pdef(...).controlKeys. Even without Pdefs, that’s straightforward (past loading those defaults into an event)

e = SynthDescLib.global.synthDescs.at(\default).controls.inject((), { |e, c| e[c.name] = c.defaultValue });
(instrument: \default, dur: 1, amp: 0.2).next(e).play

It sounds a bit to me like a choice between a simple way to tell the \set event type exactly what to do (args), vs a complicated and possibly breakable way to set things up so that the \set event type figures out what you wanted it to do.

I’ve been down this road of trying to get msgFunc to integrate nicely with \set, and I became convinced that it’s a dead-end.

hjh

1 Like

I see what you (probably) mean.