Parallel processing with Ndefs

there is one thing that is disappointing with Ndefs - parallel processing requires additional Ndefs. which, if you have a typical thing that you use on all of your generators ends up with a messy Ndef setup.

Typical example is early reflections generator. This thing at best goes in parallel to reverberator. So you end up with 3 Ndefs for one sounding object’s image.
would it be possible to have parallel processing inside node proxy we could have 1 instead.

Did anybody had successful solutions to this with node proxies?

Maybe you could use helper functions?

var earlyRef = { |in| ... * \earlyAmp.kr(0.5) };
var reverberate = { |in| ... * \reverbAmp.kr(0.5) };
Ndef(\sound, {
  var sig = ... * \dryAmp.kr(0.5);
  (sig + earlyRef.(sig) + reverberate.(sig))
}).play;

If you don’t need to also use the dry sig for something…

Supernova requires you to model the signal dependencies explicitly using ParGroup and Group. So there are basically two choices with NodeProxy:

  • You hack the structure by moving the slots’ synths into a group structure that you decide.
  • Or, NodeProxy would need to be enhanced with features to specify the dependencies and auto-generate the group structure.

The second of these is probably not going to happen quickly.

Supernova parallel processing requires multiple nodes in a ParGroup, which you don’t get in your example.

hjh

Ah I didn’t read the question as being about supernova parallel, but as parallel vs sequential filters… Sorry for noise if so

Now that you mention it – you’re probably right, I assumed parallel processing but the question is probably not about that.

hjh

@jamshark70 @Eric_Sluyter yes, I’m about the parallel filters.
Eric’s approach has place, though yes one definitely might need something after dry (eq for percussions comes to mind). but maybe this also could be arranged with functions.
Though again, one will face potential control names clash.

I would just preface each control with a namespace, like \earlyAmp, \earlyDensity, \earlySize etc
As long as each function uses a different namespace it should all get along well

Eric, you mean hardcoding to the symbol values?

Basically yes, although I guess you could also automate that with a helper function, something like

(
// the namespace var will preface all controls created by kr.(...)
var namespace = '';
var kr = { |symbol, val, lag, spec|
  var str = symbol.asString;
  str[0] = str[0].toUpper;
  (namespace ++ str).asSymbol.kr(val, lag, spec: spec ? symbol);
};

// set the namespace before using the kr function
var earlyRef = { |in|  
  var sig;
  namespace = \early;
  sig = DelayC.ar(in, 1, kr.(\predelay, 0.02, 0.1));
  sig = AllpassC.ar(sig, 1, kr.(\density, 15, 0.1).reciprocal, kr.(\time, 0.2, 0.1));
  sig * kr.(\amp, 0.2);
};

// use the same control names here
var reverberate = { |in| 
  var sig;
  namespace = \reverb;
  sig = DelayC.ar(in, 1, kr.(\predelay, 0.03, 0.1));
  NHHall.ar(in, kr.(\time, 2), 1) * kr.(\amp, 0.5)
};

// add some control specs
ControlSpec.add(\predelay, [0.001, 0.5, \exp].asSpec);
ControlSpec.add(\density, [0.1, 40, \exp].asSpec);
ControlSpec.add(\time, [0.1, 10, \exp].asSpec);

Ndef(\sound, {
  var density, noise, freq, ffreq, sig;
  namespace = \sound;
  density = kr.(\density, 2);
  noise = LPF.ar(WhiteNoise.ar(0.5!2), 10000);
  freq = kr.(\freq, 500, 0.1);
  ffreq = 10.pow(Decay2.ar(Dust2.ar(density!2), 0.0001, 0.65)) * freq;
  sig = SVF.ar(noise, ffreq.clip(20, 20000), LFDNoise3.ar(1).range(0, 1));
  sig = sig * Decay2.ar(Dust2.ar(density), 0.001, 0.5) * kr.(\amp, 0.5);

  (sig + earlyRef.(sig) + reverberate.(sig))
}).play;
)

this makes controls that share the same named specs, but are namespaced so you can have several “density” controls in the same Ndef:

1 Like

hmm, that’s something new to me
so it’s clousure kind of thing?
there is a func and namespace coupled with it
and when you put that func in another namespace it takes original variable value from new namespace?

I think this is a closure kind of thing. It’s a variable, namespace, which is shared by several functions. The kr function is taking the symbol you give it, say ‘amp’, and making a new symbol, which is joined with whatever the namespace shared variable happens to be. Say the namespace is ‘reverb’ that would make ‘reverbAmp’. And that is what becomes the synth control name.

But all it’s doing is generating symbols, basically the same as doing it yourself by typing out all of the long forms like \earlyDensity.kr

(Basically the same except it looks up the control spec for ‘density’ if it can’t find ‘earlyDensity’)

that’s the confusing part, how it finds out about unconcatenated ‘old’ symbol when defining spec?

this is where it happens:

(namespace ++ str).asSymbol.kr(val, lag, spec: spec ? symbol);

for our demo, ‘reverb’ / ‘amp’ this winds up the same as writing

\reverbAmp.kr(val, lag, spec: spec ? 'amp');

But how does it find out that spec named Amp should be assigned to control named reverbAmp

because you called it with the symbol ‘amp’…

namespace = \reverb;
kr.(\amp, 0.5)

looks like it does not actually work


and that makes sense

weird, for me with the code I posted above and Ndef(\sound).gui:

can you try running Ndef(\sound).specs.postcs?
I get

( 
'soundDensity': ControlSpec(0.1, 40, 'exp', 0.0, 0.1, ""), 
'earlyDensity': ControlSpec(0.1, 40, 'exp', 0.0, 0.1, ""), 
'reverbTime': ControlSpec(0.1, 10, 'exp', 0.0, 0.1, ""),
'earlyPredelay': ControlSpec(0.001, 0.5, 'exp', 0.0, 0.001, ""), 
'earlyAmp': ControlSpec(0, 1, 'amp', 0, 0, ""), 
'reverbPredelay': ControlSpec(0.001, 0.5, 'exp', 0.0, 0.001, ""), 
'reverbAmp': ControlSpec(0, 1, 'amp', 0, 0, ""), 
'earlyTime': ControlSpec(0.1, 10, 'exp', 0.0, 0.1, ""), 
'soundAmp': ControlSpec(0, 1, 'amp', 0, 0, ""), 
'soundFreq': ControlSpec(20, 20000, 'exp', 0, 440, "Hz") 
)

Will check once near computer.
But as I recall your code did not add specs to the Ndef.

Ah no it adds the specs to the synthdef metadata, which the ndef should use


// (
// 'soundDensity':    ControlSpec(0.1,    40,     'exp',  0.0,  0.1,    ""),
// 'earlyDensity':    ControlSpec(0.1,    40,     'exp',  0.0,  0.1,    ""),
// 'reverbTime':      ControlSpec(0.1,    10,     'exp',  0.0,  0.1,    ""),
// 'earlyPredelay':   ControlSpec(0.001,  0.5,    'exp',  0.0,  0.001,  ""),
// 'earlyAmp':        ControlSpec(0,      1,      'amp',  0,    0,      ""),
// 'reverbPredelay':  ControlSpec(0.001,  0.5,    'exp',  0.0,  0.001,  ""),
// 'reverbAmp':       ControlSpec(0,      1,      'amp',  0,    0,      ""),
// 'earlyTime':       ControlSpec(0.1,    10,     'exp',  0.0,  0.1,    ""),
// 'soundAmp':        ControlSpec(0,      1,      'amp',  0,    0,      ""),
// 'soundFreq':       ControlSpec(20,     20000,  'exp',  0,    440,    "Hz")
// )

looks like it’s there, but gui does not pick it up