{
   "name" : "NodeProxy PlayControl for parallel sounds",
   "author" : "LFSaw",
   "ancestor_list" : [],
   "description" : "NodeProxy rule to create parallel versions of the same sound definition.",
   "labels" : [
      "jitlib",
      "recipe"
   ],
   "code" : "(\r\n// AbstractPlayControl-par\r\n//\r\n// create parallel streams of the provided function filling the NodeProxy channels.\r\n// requires the functions output channels to be a multiple of the NodeProxy's numChannels.\r\nAbstractPlayControl.proxyControlClasses.put(\\par, SynthDefControl);\r\nAbstractPlayControl.buildMethods.put(\\par, #{ | func, proxy, channelOffset = 0, index |\r\n\tvar funcNumChannels;\r\n\r\n\tfunc.isKindOf(ArrayedCollection).if({\r\n\t\tfuncNumChannels = func[1];\r\n\t\tfunc = func.first;\r\n\t}, {\r\n\t\ttry({\r\n\t\t\tfuncNumChannels = (func.value.shape ?? {[1]}).first;\r\n\t\t},{|exeption|\r\n\t\t\t(exeption.selector == \\addKr).if({\r\n\t\t\t\t// assume mono\r\n\t\t\t\t\"NodeProxy: NamedControl in function, please provide numChannels explicitely:\".postln;\r\n\t\t\t\t\"\\t \\par -> [{...}, <numChannels>]\".postln;\r\n\t\t\t\tfuncNumChannels = 1;\r\n\t\t\t}, {\r\n\t\t\t\texeption.throw\r\n\t\t\t})\r\n\t\t})\r\n\t});\r\n\r\n\tif ((proxy.numChannels % funcNumChannels) != 0) {\r\n\t\tError(\"NodeProxy input (par): number of proxy channels need to be multiple of function output channels\").throw;\r\n\t};\r\n\r\n\t{ | out |\r\n\t\tvar env, ctl = NamedControl.kr((\"mix\" ++ (index ? 0)).asSymbol, 1.0);\r\n\t\tvar funcControls, outSnd;\r\n\r\n\t\t// create named controls '<argname>s' whilst retaining order of function args\r\n\t\tfuncControls = func.def.keyValuePairsFromArgs.clump(2).collect{|args, i|\r\n\t\t\tvar key, val;\r\n\t\t\t#key, val = args;\r\n\r\n\t\t\t// \\idx arg is used to hand index into function\r\n\t\t\tif (key == \\idx, {\r\n\t\t\t\tArray.series((proxy.numChannels/funcNumChannels).asInteger)\r\n\t\t\t}, {\r\n\t\t\t\tNamedControl.kr(\r\n\t\t\t\t\tname: (key ++ $s  ++ (index ? 0)),\r\n\t\t\t\t\tvalues: (val ? 0).dup(proxy.numChannels/funcNumChannels)\r\n\t\t\t\t)\r\n\t\t\t})\r\n\r\n\t\t\t//\r\n\t\t\t// NamedControl.kr(\r\n\t\t\t// \t(args[0] ++ $s ++ (index ? 0)),\r\n\t\t\t// \t(args[1] ? 0).dup(proxy.numChannels/funcNumChannels)\r\n\t\t\t// )\r\n\t\t};\r\n\r\n\t\t// embed function synthesis\r\n\t\tif (funcControls.isEmpty, {\r\n\t\t\t// no args given\r\n\t\t\toutSnd = (proxy.numChannels/funcNumChannels).asInteger.collect{\r\n\t\t\t\tSynthDef.wrap(func, nil);\r\n\t\t\t}\r\n\t\t}, {\r\n\t\t\t// args given\r\n\t\t\toutSnd = funcControls.flop.collect{|args, i|\r\n\t\t\t\t// args.postln;\r\n\t\t\t\tSynthDef.wrap(func, nil, args);\r\n\t\t\t}.flatten;\r\n\t\t});\r\n\r\n\r\n\t\tif(proxy.rate === 'audio') {\r\n\t\t\tenv = ctl * EnvGate(i_level: 0, doneAction: 2, curve: \\sin);\r\n\t\t\tOut.ar(out, env * outSnd)\r\n\t\t} {\r\n\t\t\tenv = ctl * EnvGate(i_level: 0, doneAction: 2, curve: \\lin);\r\n\t\t\tOut.kr(out, env * outSnd)\r\n\t\t};\r\n\t}.buildForProxy( proxy, channelOffset, index )\r\n});\r\n\r\n// specs\r\n\r\n(0..1000).do{|i|\r\n\tSpec.add(\r\n\t\t(\"wet\"++i).asSymbol, [0, 1].asSpec\r\n\t);\r\n\tSpec.add(\r\n\t\t(\"mix\"++i).asSymbol, [0, 1].asSpec\r\n\t);\r\n}\r\n)\r\n\r\n\r\n////////////// example usage\r\n\r\n// make n 8-channel Ndef\r\nNdef(\\a).ar(8)\r\n\r\n\r\n// show edit window\r\nNdef(\\a).edit(30)\r\n\r\n\r\n// play on two channels (this wraps the 8 channels onto 2)\r\nNdef(\\a).play(0, numChannels: 2, vol: 0.2)\r\n\r\n// create an 8-channel sound by instantiating 8 times the below monosound\r\nNdef(\\a)[0] = \\par -> {|freq = 444, amp = 0.1, lpFreq = 1200, lpRq = 0.1| RLPF.ar(Blip.ar(freq.lag(4), 5), lpFreq.lag(4), lpRq.lag(4)) * amp };\r\n\r\n// set some arbitrary parameters of the sound\r\nNdef(\\a).setn(\\freqs0, {exprand(100, 400)}!8);\r\nNdef(\\a).setn(\\lpFreqs0, {exprand(1000, 4000)}!8);\r\nNdef(\\a).setn(\\lpRqs0, {exprand(1, 0.1)}!8);\r\n\r\n\r\n// add a variation of the first sound in 2nd slot\r\nNdef(\\a)[1] = \\par -> {|freq = 444, amp = 0.1, lpFreq = 1200, lpRq = 0.1| RLPF.ar(Blip.ar(freq.lag(4), 5), lpFreq.lag(4), lpRq.lag(4)) * amp };\r\n\r\n// set its mix param (amplitude)\r\nNdef(\\a).set(\\mix1, 0.3);\r\n\r\n// set some arbitrary parameters of the 2nd sound\r\nNdef(\\a).setn(\\freqs1, {exprand(1000, 4000)}!8);\r\nNdef(\\a).setn(\\lpFreqs1, {exprand(1000, 4000)}!8);\r\nNdef(\\a).setn(\\lpRqs1, {exprand(1, 0.1)}!8);\r\n\r\n\r\n// filter sound globally\r\nNdef(\\a)[10] = \\filter -> {|in, lpFreq = 1000, lpRq = 0.1| RLPF.ar(in, lpFreq, lpRq)}\r\n\r\nNdef(\\a).release\r\nNdef(\\a).clear",
   "is_private" : null,
   "id" : "1-5gn"
}
