1 // These Unit Generators are instantiated by math operations on UGens
6 // writeName { arg file;
8 // name = this.class.name.asString;
9 // opname = operator.asString;
10 // file.putInt8(name.size + opname.size + 1);
11 // file.putString(name);
13 // file.putString(opname);
17 specialIndex = operator.specialIndex;
18 if(specialIndex < 0) {
19 Error("Operator '%' applied to a UGen is not supported in scsynth".format(operator)).throw
23 argNamesInputsOffset { ^2 }
24 argNameForInputAt { arg i;
25 var method = this.class.class.findMethod('new');
26 if(method.isNil or: {method.argNames.isNil},{ ^nil });
27 ^method.argNames.at(i + this.argNamesInputsOffset)
31 (" operator:" + operator).postln;
32 inputs.do({ arg in,ini;
33 (" " ++ (this.argNameForInputAt(ini) ? ini.asString)++":" + in + in.class).postln
38 ^synthIndex.asString ++ "_" ++ this.operator
42 UnaryOpUGen : BasicOpUGen {
44 *new { arg selector, a;
45 ^this.multiNew('audio', selector, a)
48 init { arg theOperator, theInput;
49 this.operator = theOperator;
51 inputs = theInput.asArray;
55 super.performDeadCodeElimination
59 BinaryOpUGen : BasicOpUGen {
60 *new { arg selector, a, b;
61 ^this.multiNew('audio', selector, a, b)
64 determineRate { arg a, b;
65 if (a.rate == \demand, { ^\demand });
66 if (b.rate == \demand, { ^\demand });
67 if (a.rate == \audio, { ^\audio });
68 if (b.rate == \audio, { ^\audio });
69 if (a.rate == \control, { ^\control });
70 if (b.rate == \control, { ^\control });
73 *new1 { arg rate, selector, a, b;
75 // eliminate degenerate cases
76 if (selector == '*', {
77 if (a == 0.0, { ^0.0 });
78 if (b == 0.0, { ^0.0 });
79 if (a == 1.0, { ^b });
80 if (a == -1.0, { ^b.neg });
81 if (b == 1.0, { ^a });
82 if (b == -1.0, { ^a.neg });
84 if (selector == '+', {
85 if (a == 0.0, { ^b });
86 if (b == 0.0, { ^a });
88 if (selector == '-', {
89 if (a == 0.0, { ^b.neg });
90 if (b == 0.0, { ^a });
92 if (selector == '/', {
93 if (b == 1.0, { ^a });
94 if (b == -1.0, { ^a.neg });
97 ^super.new1(rate, selector, a, b)
100 init { arg theOperator, a, b;
101 this.operator = theOperator;
102 rate = this.determineRate(a, b);
107 //this.constantFolding;
110 if (super.performDeadCodeElimination) {
114 if (operator == '+') {
119 if (operator == '-') {
127 // create a Sum3 if possible.
128 optimizedUGen = this.optimizeToSum3;
130 // create a Sum4 if possible.
131 if (optimizedUGen.isNil) {
132 optimizedUGen = this.optimizeToSum4
135 // create a MulAdd if possible.
136 if (optimizedUGen.isNil) {
137 optimizedUGen = this.optimizeToMulAdd
140 // optimize negative additions
141 if (optimizedUGen.isNil) {
142 optimizedUGen = this.optimizeAddNeg
145 if (optimizedUGen.notNil) {
146 synthDef.replaceUGen(this, optimizedUGen);
147 optimizedUGen.optimizeGraph
155 if (b.isKindOf(UnaryOpUGen) and: { b.operator == 'neg' }) {
156 // a + b.neg -> a - b
157 if (b.descendants.size == 1) {
158 buildSynthDef.removeUGen(b);
160 b.descendants.remove(this);
165 if (a.isKindOf(UnaryOpUGen) and: { a.operator == 'neg' }) {
166 // a.neg + b -> b - a
167 if (a.descendants.size == 1) {
168 buildSynthDef.removeUGen(a);
170 a.descendants.remove(this);
181 if (a.isKindOf(BinaryOpUGen) and: { a.operator == '*'
182 and: { a.descendants.size == 1 }})
184 if (MulAdd.canBeMulAdd(a.inputs[0], a.inputs[1], b)) {
185 buildSynthDef.removeUGen(a);
186 ^MulAdd.new(a.inputs[0], a.inputs[1], b);
189 if (MulAdd.canBeMulAdd(a.inputs[1], a.inputs[0], b)) {
190 buildSynthDef.removeUGen(a);
191 ^MulAdd.new(a.inputs[1], a.inputs[0], b)
195 if (b.isKindOf(BinaryOpUGen) and: { b.operator == '*'
196 and: { b.descendants.size == 1 }})
198 if (MulAdd.canBeMulAdd(b.inputs[0], b.inputs[1], a)) {
199 buildSynthDef.removeUGen(b);
200 ^MulAdd.new(b.inputs[0], b.inputs[1], a)
203 if (MulAdd.canBeMulAdd(b.inputs[1], b.inputs[0], a)) {
204 buildSynthDef.removeUGen(b);
205 ^MulAdd.new(b.inputs[1], b.inputs[0], a)
215 if (a.isKindOf(BinaryOpUGen) and: { a.operator == '+'
216 and: { a.descendants.size == 1 }}) {
217 buildSynthDef.removeUGen(a);
218 ^Sum3(a.inputs[0], a.inputs[1], b);
221 if (b.isKindOf(BinaryOpUGen) and: { b.operator == '+'
222 and: { b.descendants.size == 1 }}) {
223 buildSynthDef.removeUGen(b);
224 ^Sum3(b.inputs[0], b.inputs[1], a);
233 if (a.isKindOf(Sum3) and: { a.descendants.size == 1 }) {
234 buildSynthDef.removeUGen(a);
235 ^Sum4(a.inputs[0], a.inputs[1], a.inputs[2], b);
238 if (b.isKindOf(Sum3) and: { b.descendants.size == 1 }) {
239 buildSynthDef.removeUGen(b);
240 ^Sum4(b.inputs[0], b.inputs[1], b.inputs[2], a);
246 var a, b, replacement;
249 if (b.isKindOf(UnaryOpUGen) and: { b.operator == 'neg' }) {
250 // a - b.neg -> a + b
251 if (b.descendants.size == 1) {
252 buildSynthDef.removeUGen(b);
254 b.descendants.remove(this);
256 replacement = BinaryOpUGen('+', a, b.inputs[0]);
258 synthDef.replaceUGen(this, replacement);
259 replacement.optimizeGraph
265 var a, b, aa, bb, cc, dd, temp, ac_ops, value;
267 // associative & commutative operators
268 ac_ops = #['+','*','min','max','&&','||'];
270 if (ac_ops.includes(operator).not) { ^this };
273 if (a.isKindOf(BinaryOpUGen) and: { operator == a.operator
274 and: { b.isKindOf(BinaryOpUGen) and: { operator == b.operator } }}) {
277 if (aa.isKindOf(SimpleNumber)) {
278 if (cc.isKindOf(SimpleNumber)) {
280 this.inputs[0] = aa.perform(operator, cc);
281 synthDef.removeUGen(a);
283 if (dd.isKindOf(SimpleNumber)) {
285 this.inputs[0] = aa.perform(operator, dd);
286 synthDef.removeUGen(a);
289 if (bb.isKindOf(SimpleNumber)) {
290 if (cc.isKindOf(SimpleNumber)) {
292 this.inputs[0] = bb.perform(operator, cc);
293 synthDef.removeUGen(a);
295 if (dd.isKindOf(SimpleNumber)) {
297 this.inputs[0] = bb.perform(operator, dd);
298 synthDef.removeUGen(a);
304 if (a.isKindOf(BinaryOpUGen) and: { operator == a.operator }) {
306 if (b.isKindOf(SimpleNumber)) {
307 if (aa.isKindOf(SimpleNumber)) {
308 buildSynthDef.removeUGen(a);
309 this.inputs[0] = aa.perform(operator, b);
313 if (bb.isKindOf(SimpleNumber)) {
314 buildSynthDef.removeUGen(a);
315 this.inputs[0] = bb.perform(operator, b);
320 // percolate constants upward so that a subsequent folding may occur
321 if (aa.isKindOf(SimpleNumber)) {
325 if (bb.isKindOf(SimpleNumber)) {
331 if (b.isKindOf(BinaryOpUGen) and: { operator == b.operator }) {
333 if (a.isKindOf(SimpleNumber)) {
334 if (cc.isKindOf(SimpleNumber)) {
335 buildSynthDef.removeUGen(b);
336 this.inputs[0] = a.perform(operator, cc);
340 if (dd.isKindOf(SimpleNumber)) {
341 buildSynthDef.removeUGen(b);
342 this.inputs[0] = a.perform(operator, dd);
347 // percolate constants upward so that a subsequent folding may occur
348 if (cc.isKindOf(SimpleNumber)) {
352 if (dd.isKindOf(SimpleNumber)) {
358 if (a.isKindOf(SimpleNumber) and: { b.isKindOf(SimpleNumber) }) {
359 synthDef.replaceUGen(this, a.perform(operator, b));
360 synthDef.removeUGen(this);
366 *new { arg in, mul = 1.0, add = 0.0;
367 var rate = [in, mul, add].rate;
368 ^this.multiNew(rate, in, mul, add)
370 *new1 { arg rate, in, mul, add;
371 var minus, nomul, noadd;
373 // eliminate degenerate cases
374 if (mul == 0.0, { ^add });
378 if (nomul && noadd, { ^in });
379 if (minus && noadd, { ^in.neg });
380 if (noadd, { ^in * mul });
381 if (minus, { ^add - in });
382 if (nomul, { ^in + add });
384 if (this.canBeMulAdd(in, mul, add)) {
385 ^super.new1(rate, in, mul, add)
387 if (this.canBeMulAdd(mul, in, add)) {
388 ^super.new1(rate, mul, in, add)
392 init { arg in, mul, add;
393 inputs = [in, mul, add];
397 *canBeMulAdd { arg in, mul, add;
398 // see if these inputs satisfy the constraints of a MulAdd ugen.
399 if (in.rate == \audio, { ^true });
400 if (in.rate == \control
401 and: { mul.rate == \control || { mul.rate == \scalar }}
402 and: { add.rate == \control || { add.rate == \scalar }},
411 *new { arg in0, in1, in2;
412 ^this.multiNew(nil, in0, in1, in2)
415 *new1 { arg dummyRate, in0, in1, in2;
416 var argArray, rate, sortedArgs;
417 if (in2 == 0.0) { ^(in0 + in1) };
418 if (in1 == 0.0) { ^(in0 + in2) };
419 if (in0 == 0.0) { ^(in1 + in2) };
421 argArray = [in0, in1, in2];
422 rate = argArray.rate;
423 sortedArgs = argArray.sort {|a b| a.rate < b.rate};
425 ^super.new1(rate, *sortedArgs)
430 *new { arg in0, in1, in2, in3;
431 ^this.multiNew(nil, in0, in1, in2, in3)
434 *new1 { arg dummyRate, in0, in1, in2, in3;
435 var argArray, rate, sortedArgs;
437 if (in0 == 0.0) { ^Sum3.new1(nil, in1, in2, in3) };
438 if (in1 == 0.0) { ^Sum3.new1(nil, in0, in2, in3) };
439 if (in2 == 0.0) { ^Sum3.new1(nil, in0, in1, in3) };
440 if (in3 == 0.0) { ^Sum3.new1(nil, in0, in1, in2) };
442 argArray = [in0, in1, in2, in3];
443 rate = argArray.rate;
444 sortedArgs = argArray.sort {|a b| a.rate < b.rate};
446 ^super.new1(rate, *sortedArgs)