File: ProxySynthDef.sc

package info (click to toggle)
supercollider 1%3A3.13.0%2Brepack-1
  • links: PTS, VCS
  • area: main
  • in suites: bookworm
  • size: 80,292 kB
  • sloc: cpp: 476,363; lisp: 84,680; ansic: 77,685; sh: 25,509; python: 7,909; makefile: 3,440; perl: 1,964; javascript: 974; xml: 826; java: 677; yacc: 314; lex: 175; objc: 152; ruby: 136
file content (154 lines) | stat: -rw-r--r-- 5,276 bytes parent folder | download | duplicates (3)
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
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
ProxySynthDef : SynthDef {

	var <>rate, <>numChannels;
	var <>canReleaseSynth, <>canFreeSynth;
	classvar <>sampleAccurate=false;


	*new { | name, func, rates, prependArgs, makeFadeEnv = true, channelOffset = 0,
		chanConstraint, rateConstraint |
		var def, rate, numChannels, output, isScalar, envgen, canFree, hasOwnGate;
		var hasGateArg=false, hasOutArg=false;
		var outerBuildSynthDef = UGen.buildSynthDef;
		def = super.new(name, {
			var  out, outCtl;

			// build the controls from args
			output = SynthDef.wrap(func, rates, prependArgs);
			output = output.asUGenInput;

			// protect from user error
			if(output.isKindOf(UGen) and: { output.synthDef != UGen.buildSynthDef }) {
				Error("Cannot share UGens between NodeProxies:" + output).throw
			};

			// protect from accidentally returning wrong array shapes
			if(output.containsSeqColl) {
				// try first unbubble singletons, these are ok
				output = output.collect { |each| each.unbubble };
				// otherwise flatten, but warn
				if(output.containsSeqColl) {
					"Synth output should be a flat array.\n%\nFlattened to: %\n"
					"See NodeProxy helpfile:routing\n\n".format(output, output.flat).warn;
					output = output.flat;
				};
			};

			output = output ? 0.0;

			// determine rate and numChannels of ugen func
			numChannels = output.numChannels;
			rate = output.rate; // for an array, this will always result in the highest rate
			isScalar = rate === 'scalar';

			// check for out key. this is used by internal control.
			func.def.argNames.do { arg name;
				if(name === \out) { hasOutArg = true };
				if(name === \gate) { hasGateArg = true };
			};

			if(isScalar.not and: hasOutArg)
			{
				"out argument is provided internally!".error; // avoid overriding generated out
				^nil
			};

			// rate is only scalar if output was nil or if it was directly produced by an out ugen
			// this allows us to conveniently write constant numbers to a bus from the synth
			// if you want the synth to write nothing, return nil from the UGen function.

			if(isScalar and: { output.notNil } and: { UGen.buildSynthDef.children.last.isKindOf(AbstractOut).not }) {
				rate = 'control';
				isScalar = false;
			};

			//detect inner gates
			canFree = UGen.buildSynthDef.children.canFreeSynth;
			hasOwnGate = UGen.buildSynthDef.hasGateControl;
			makeFadeEnv = if(hasOwnGate and: { canFree.not }) {
				"The gate control should be able to free the synth!\n%".format(func).warn; false
			} {
				makeFadeEnv and: { (isScalar || canFree).not };
			};

			hasOwnGate = canFree && hasOwnGate; //only counts when it can actually free synth.
			if(hasOwnGate.not && hasGateArg) {
				"supplied gate overrides inner gate.".error;
				^nil
			};


			//"gate detection:".postln;
			//[\makeFadeEnv, makeFadeEnv, \canFree, canFree, \hasOwnGate, hasOwnGate].debug;

			// constrain the output to the right number of channels if supplied
			// if control rate, no channel wrapping is applied
			// and wrap it in a fade envelope
			envgen = if(makeFadeEnv) {
				EnvGate(i_level: 0, doneAction:2, curve: if(rate === 'audio') { 'sin' } { 'lin' })
			} { 1.0 };

			if(chanConstraint.notNil
				and: { chanConstraint < numChannels }
				and: { isScalar.not },
				{
					if(rate === 'audio') {
						postf("%: wrapped channels from % to % channels\n", NodeProxy.buildProxy, numChannels, chanConstraint);
						output = NumChannels.ar(output, chanConstraint, true);
						numChannels = chanConstraint;
					} {
						postf("%: kept first % channels from % channel input\n", NodeProxy.buildProxy, chanConstraint, numChannels);
						output = output.keep(chanConstraint);
						numChannels = chanConstraint;
					}

			});
			output = output * envgen;

			//"passed in rate: % output rate: %\n".postf(rateConstraint, rate);

			if(isScalar, {
				output
			}, {
				// rate adaption. \scalar proxy means neutral
				if(rateConstraint.notNil and: { rateConstraint != \scalar and: { rateConstraint !== rate }}) {
					if(rate === 'audio') {
						output = A2K.kr(output);
						rate = 'control';
						postf("%: adopted proxy output signal to control rate\n", NodeProxy.buildProxy);
					} {
						if(rateConstraint === 'audio') {
							output = K2A.ar(output);
							rate = 'audio';
							postf("%: adopted proxy output signal to audio rate\n", NodeProxy.buildProxy);
						}
					}
				};
				// because Out.ar will not accept a control rate ugen, we adapt any other rates
				if(rate == 'audio') {
					output.do { |x, i|
						if(x.rate != rate) {
							postf("%: adopted proxy output signal at index % to audio rate:\n%\n",  NodeProxy.buildProxy, i, x);
							output[i] = K2A.ar(x) // for efficiency in large outputs, we modify in place
						};
					};
				};
				outCtl = NamedControl.ir(\out, 0) + channelOffset;
				(if(rate === \audio and: { sampleAccurate }) { OffsetOut } { Out }).multiNewList([rate, outCtl] ++ output)
			})
		});

		UGen.buildSynthDef = outerBuildSynthDef;

		// set the synthDefs instvars, so they can be used later

		def.rate = rate;
		def.numChannels = numChannels;
		def.canReleaseSynth = makeFadeEnv || hasOwnGate;
		def.canFreeSynth = def.canReleaseSynth || canFree;
		//[\defcanReleaseSynth, def.canReleaseSynth, \defcanFreeSynth, def.canFreeSynth].debug;
		^def
	}


}