«NodeProxy PlayControl for parallel sounds» by LFSaw
on 04 Aug'22 11:38 inNodeProxy rule to create parallel versions of the same sound definition.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134
( // AbstractPlayControl-par // // create parallel streams of the provided function filling the NodeProxy channels. // requires the functions output channels to be a multiple of the NodeProxy's numChannels. AbstractPlayControl.proxyControlClasses.put(\par, SynthDefControl); AbstractPlayControl.buildMethods.put(\par, #{ | func, proxy, channelOffset = 0, index | var funcNumChannels; func.isKindOf(ArrayedCollection).if({ funcNumChannels = func[1]; func = func.first; }, { try({ funcNumChannels = (func.value.shape ?? {[1]}).first; },{|exeption| (exeption.selector == \addKr).if({ // assume mono "NodeProxy: NamedControl in function, please provide numChannels explicitely:".postln; "\t \par -> [{...}, <numChannels>]".postln; funcNumChannels = 1; }, { exeption.throw }) }) }); if ((proxy.numChannels % funcNumChannels) != 0) { Error("NodeProxy input (par): number of proxy channels need to be multiple of function output channels").throw; }; { | out | var env, ctl = NamedControl.kr(("mix" ++ (index ? 0)).asSymbol, 1.0); var funcControls, outSnd; // create named controls '<argname>s' whilst retaining order of function args funcControls = func.def.keyValuePairsFromArgs.clump(2).collect{|args, i| var key, val; #key, val = args; // \idx arg is used to hand index into function if (key == \idx, { Array.series((proxy.numChannels/funcNumChannels).asInteger) }, { NamedControl.kr( name: (key ++ $s ++ (index ? 0)), values: (val ? 0).dup(proxy.numChannels/funcNumChannels) ) }) // // NamedControl.kr( // (args[0] ++ $s ++ (index ? 0)), // (args[1] ? 0).dup(proxy.numChannels/funcNumChannels) // ) }; // embed function synthesis if (funcControls.isEmpty, { // no args given outSnd = (proxy.numChannels/funcNumChannels).asInteger.collect{ SynthDef.wrap(func, nil); } }, { // args given outSnd = funcControls.flop.collect{|args, i| // args.postln; SynthDef.wrap(func, nil, args); }.flatten; }); if(proxy.rate === 'audio') { env = ctl * EnvGate(i_level: 0, doneAction: 2, curve: \sin); Out.ar(out, env * outSnd) } { env = ctl * EnvGate(i_level: 0, doneAction: 2, curve: \lin); Out.kr(out, env * outSnd) }; }.buildForProxy( proxy, channelOffset, index ) }); // specs (0..1000).do{|i| Spec.add( ("wet"++i).asSymbol, [0, 1].asSpec ); Spec.add( ("mix"++i).asSymbol, [0, 1].asSpec ); } ) ////////////// example usage // make n 8-channel Ndef Ndef(\a).ar(8) // show edit window Ndef(\a).edit(30) // play on two channels (this wraps the 8 channels onto 2) Ndef(\a).play(0, numChannels: 2, vol: 0.2) // create an 8-channel sound by instantiating 8 times the below monosound Ndef(\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 }; // set some arbitrary parameters of the sound Ndef(\a).setn(\freqs0, {exprand(100, 400)}!8); Ndef(\a).setn(\lpFreqs0, {exprand(1000, 4000)}!8); Ndef(\a).setn(\lpRqs0, {exprand(1, 0.1)}!8); // add a variation of the first sound in 2nd slot Ndef(\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 }; // set its mix param (amplitude) Ndef(\a).set(\mix1, 0.3); // set some arbitrary parameters of the 2nd sound Ndef(\a).setn(\freqs1, {exprand(1000, 4000)}!8); Ndef(\a).setn(\lpFreqs1, {exprand(1000, 4000)}!8); Ndef(\a).setn(\lpRqs1, {exprand(1, 0.1)}!8); // filter sound globally Ndef(\a)[10] = \filter -> {|in, lpFreq = 1000, lpRq = 0.1| RLPF.ar(in, lpFreq, lpRq)} Ndef(\a).release Ndef(\a).clear
reception
comments