1 ProxySynthDef : SynthDef {
3 var <>rate, <>numChannels;
4 var <>canReleaseSynth, <>canFreeSynth;
5 classvar <>sampleAccurate=false;
8 *new { arg name, func, rates, prependArgs, makeFadeEnv=true, channelOffset=0,
9 chanConstraint, rateConstraint;
10 var def, rate, numChannels, output, isScalar, envgen, canFree, hasOwnGate;
11 var hasGateArg=false, hasOutArg=false;
12 def = super.new(name, {
15 // build the controls from args
16 output = SynthDef.wrap(func, rates, prependArgs);
17 output = output.asUGenInput;
19 // determine rate and numChannels of ugen func
21 isScalar = rate === 'scalar';
22 numChannels = output.numChannels;
23 // check for out key. this is used by internal control.
24 func.def.argNames.do { arg name;
25 if(name === \out) { hasOutArg = true };
26 if(name === \gate) { hasGateArg = true };
29 if(isScalar.not and: hasOutArg)
31 "out argument is provided internally!".error; // avoid overriding generated out
36 canFree = UGen.buildSynthDef.children.canFreeSynth;
37 hasOwnGate = UGen.buildSynthDef.hasGateControl;
38 makeFadeEnv = if(hasOwnGate && canFree.not) {
39 "warning: gate does not free synth!".inform; false
41 makeFadeEnv and: { (isScalar || canFree).not };
44 hasOwnGate = canFree && hasOwnGate; //only counts when it can actually free synth.
45 if(hasOwnGate.not && hasGateArg) {
46 "supplied gate overrides inner gate.".error;
51 //"gate detection:".postln;
52 //[\makeFadeEnv, makeFadeEnv, \canFree, canFree, \hasOwnGate, hasOwnGate].debug;
54 // constrain the output to the right number of channels if supplied
55 // if control rate, no channel wrapping is applied
56 // and wrap it in a fade envelope
57 envgen = if(makeFadeEnv) {
58 EnvGate(1, nil, nil, 2, if(rate === 'audio') { 'sin' } { 'lin' })
61 if(chanConstraint.notNil
62 and: { chanConstraint < numChannels }
63 and: { isScalar.not },
65 if(rate === 'audio') {
67 "wrapped channels from" + numChannels
68 + "to" + chanConstraint + "channels");
69 output = NumChannels.ar(output, chanConstraint, true);
70 numChannels = chanConstraint;
72 postln("kept first" + chanConstraint + "channels from"
73 + numChannels + "channel input");
74 output = output.keep(chanConstraint);
75 numChannels = chanConstraint;
79 output = output * envgen;
81 //"passed in rate: % output rate: %\n".postf(rateConstraint, rate);
86 // rate adaption. \scalar proxy means neutral
87 if(rateConstraint != \scalar and: { rateConstraint !== rate }) {
88 if(rate === 'audio') {
89 output = A2K.kr(output);
91 "adopted proxy input to control rate".postln;
93 if(rateConstraint === 'audio') {
94 output = K2A.ar(output);
96 "adopted proxy input to audio rate".postln;
100 outCtl = Control.names(\out).ir(0) + channelOffset;
101 if(rate === \audio and: { sampleAccurate }) { OffsetOut } { Out } .multiNewList([rate, outCtl]++output)
105 // set the synthDefs instvars, so they can be used later
108 def.numChannels = numChannels;
109 def.canReleaseSynth = makeFadeEnv || hasOwnGate;
110 def.canFreeSynth = def.canReleaseSynth || canFree;
111 //[\defcanReleaseSynth, def.canReleaseSynth, \defcanFreeSynth, def.canFreeSynth].debug;