1class::AbstractFunction
2summary::An object which responds to a set of messages that represent mathematical functions
3categories::Core>Kernel
4related::Classes/UGen,Classes/Pattern,Classes/Function,Overviews/Operators
5
6description::
7
8An AbstractFunction is an object which responds to a set of messages that represent
9mathematical functions. Subclasses override a smaller set of messages to respond
10to the mathematical functions.
11
12The intent is to provide a mechanism for functions that do not calculate values directly but instead compose structures for calculating (lazy evaluation).
13
14Function, Pattern, Stream and UGen are subclasses of AbstractFunction.
15For example, if you multiply two UGens together the receiver responds by returning a new
16instance of class BinaryOpUGen which has the two operands as inputs.
17
18code::
19{ var a, b; a = LFSaw.ar(220); b = LFPulse.ar(1442); [a, b, a * b] }.plot;
20::
21
22For an overview of common operators, see link::Overviews/Operators::, for specific examples, see also e.g. link::Classes/Function::, link::Classes/UGen::, link::Classes/Pattern::.
23To see which classes implement a specific method, see that method in the generated link::Overviews/Methods:: overview.
24
25instanceMethods::
26
27subsection::Unary Messages
28
29The following messages return an object which represents a delayed unary operation, i.e. an operation on one object. For example, the reciprocal of a function will result in a new function that, when called, returns the reciprocal of the evaluation of the operand.
30
31All of the following messages send the message composeUnaryOp to the receiver with the
32unary message selector as an argument.
33See link::Classes/UnaryOpFunction::.
34
35method::neg
36code::
37a = { 10.rand.postln }; b = a.neg; b.value;
38// Patterns, Streams, UGens, and Proxies are AbstractFunctions, too:
39a = Pgeom(1, 2, 5).neg; a.asStream.nextN(8);
40{ a = LFNoise1.ar(1500); [a, a.neg] }.plot;
41::
42method::reciprocal
43code::
44a = { 10.rand.postln }; b = a.reciprocal; b.value;
45a = Pgeom(1, 2, 5).reciprocal; a.asStream.nextN(8);
46{ a = LFNoise1.ar(1500) + 2; [a, a.reciprocal] }.plot;
47::
48method::bitNot
49Bitwise integer negation.
50method::abs
51Absolute value
52code::
53a = { 10.rand - 10.rand }; b = a.abs; b.value;
54a = Pseries(3, -1.8, inf).abs; a.asStream.nextN(8);
55{ a = LFNoise1.ar(1500); [a, a.abs] }.plot;
56::
57method::asFloat
58code::
59a = { "123.471".scramble }; b = a.asFloat; b.value;
60::
61method::asInt
62Deprecated. Use code::asInteger:: instead.
63method::asInteger
64code::
65a = { "123471".scramble }; b = a.asInteger; b.value;
66::
67method::ceil, floor, frac
68code::
69a = { 10.0.rand2.postln }; b = a.ceil; b.value;
70a = { 10.0.rand2.postln }; b = a.floor; b.value;
71a = Pgeom(1, 1.2, inf).ceil; a.asStream.nextN(8);
72a = Pgeom(1, 1.2, inf).floor; a.asStream.nextN(8);
73{ a = SinOsc.ar(150) * 1.5; [a, a.ceil, a.floor, a.frac] }.plot.superpose_(true);
74::
75method::sign
76Returns a function that returns -1 if receiver returns a negative number, 1 if positive, and 0 if zero.
77code::
78a = { 10.0.rand2.postln }; b = a.sign; b.value;
79{ a = LFNoise1.ar(1500) * 1.5; [a, a.sign] }.plot;
80::
81method::squared
82code::
83a = { |x| x + 1 }; b = a.squared; [a.value(1), b.value(1)];
84a = Pseries(0, 1, inf).squared; a.asStream.nextN(8);
85{ a = LFNoise1.ar(1500); [a, a.squared] }.plot;
86::
87method::cubed
88code::
89a = { |x| x + 1 }; b = a.cubed; [a.value(1), b.value(1)];
90a = Pseries(0, 1, inf).cubed; a.asStream.nextN(8);
91{ a = LFNoise1.ar(1500); [a, a.cubed] }.plot;
92::
93method::sqrt
94code::
95a = { |x| x + 1 }; b = a.sqrt; [a.value(1), b.value(1)];
96a = Pseries(0, 1, inf).sqrt; a.asStream.nextN(8);
97{ a = LFNoise1.ar(1500); [a, a.sqrt] }.plot;
98::
99method::exp
100Returns e to the power of this.
101code::
102a = { |x| x + 1 }; b = a.exp; [a.value(1), b.value(1)];
103a = Pseries(0, 0.25, inf).exp; a.asStream.nextN(8);
104{ a = LFNoise1.ar(1500); [a, a.exp] }.plot;
105::
106method::midicps
107Converts midinote into cycles per seconds (Hz).
108code::
109a = { |x, root = 60| x + root }; b = a.midicps; [a.value(9), b.value(9)];
110a = Pseries(60, 1, inf).midicps; a.asStream.nextN(12);
111{ a = LFNoise1.ar(1) * 5 + 60; Pulse.ar(a.round.midicps) * 0.1 }.play;
112::
113method::cpsmidi
114Converts cycles per seconds (Hz) into midinote.
115code::
116a = { |x| #[440, 720, 801, 1020.2].at(x) }; b = a.cpsmidi; [a.value(3), b.value(3)];
117a = Pseries(220, 220, inf).cpsmidi; a.asStream.nextN(12); // overtone series as midinotes
118// follow but round to next midinote
119{ a = Pitch.kr(SoundIn.ar).at(1); Pulse.ar(a.cpsmidi.round.midicps) * 0.1 }.play;
120::
121method::midiratio
122method::ratiomidi
123method::ampdb
124method::dbamp
125method::octcps
126method::cpsoct
127method::log
128method::log2
129method::log10
130method::sin
131method::cos
132method::tan
133method::asin
134method::acos
135method::atan
136method::sinh
137method::cosh
138method::tanh
139method::rand
140method::rand2
141method::linrand
142method::bilinrand
143method::sum3rand
144method::distort
145method::softclip
146method::coin
147method::even
148method::odd
149method::isPositive
150method::isNegative
151method::isStrictlyPositive
152method::rho
153method::theta
154
155subsection::Binary Messages
156
157The following messages return an object which represents a delayed binary operation, i.e. an operation between two objects. For example, adding two functions will result in a new function that, when called, adds the results of the evaluation of the two operands.
158
159All of the following messages send the message composeBinaryOp to the receiver with the
160binary message selector and the second operand as arguments.
161See: link::Classes/BinaryOpFunction::.
162
163
164Examples:
165code::
166(
167// Add two functions:
168var x = { |x| x + 1000 } + { |x| x * 100 };
169// Evaluate the result, passing in one argument:
170x.value(2); // posts 1202
171)
172// either operand can be another object:
173(
174// Add two functions:
175var x = 1871 + { |x| x * 12 };
176x.value(12);
177)
178::
179code::
180(
181// Add two UGens
182{
183 SinOsc.ar(440, 0, 0.2) + PinkNoise.ar(0.1);
184}.play
185)
186::
187// Add two Patterns
188code::
189(Pseq([1, 2, 3, 4]) + Prand([0, 0.1, -0.1], inf)).asStream.nextN(5);
190::
191// Add two NodeProxies
192code::
193Ndef(\x, { SinOsc.ar(440, 0, 0.2) });
194Ndef(\y, { PinkNoise.ar(0.1) });
195Ndef(\z, Ndef(\x) + Ndef(\y)).play;
196::
197
198method::+
199code::
200({ |x| x.squared } + 3).value(2);
201::
202method::-
203code::
204({ |x| x.squared } - 3).value(2);
205::
206method::*
207code::
208({ |x| x.squared } * { |x| x.squared }).value(2);
209::
210method::/
211code::
212({ |x| x.squared } / 4).value(2);
213::
214method::div
215code::
216({ |x| x.squared } div: 3).value(2);
217::
218method::%
219code::
220({ |x| x.squared } % 3).value(2);
221::
222method::**
223code::
224({ |x| x.squared } ** 3).value(2);
225::
226method::min
227code::
228({ |x| x.squared } min: 0).value(2);
229::
230method::max
231code::
232({ |x| x.squared } max: 0).value(2);
233::
234method::<
235code::
236({ |x| x.squared } < 3).value(2);
237::
238method::<=
239code::
240({ |x| x.squared } <= 3).value(2);
241::
242method::>
243code::
244({ |x| x.squared } > 3).value(2);
245::
246method::>=
247code::
248({ |x| x.squared } >= 3).value(2);
249::
250method::&
251code::
252a = { |min, max| ({ rrand(min, max) } ! 4).postln };
253(a & a).value(0, 8);
254::
255method::|
256code::
257a = { |min, max| ({ rrand(min, max) } ! 4).postln };
258(a | a).value(0, 8);
259::
260method::lcm
261code::
262a = { |min, max| rrand(min, max).postln };
263(a lcm: a).value(0, 8);
264::
265method::gcd
266code::
267a = { |min, max| rrand(min, max).postln };
268(a gcd: a).value(0, 8);
269::
270method::round
271code::
272a = { |max| max.rand.postln };
273(a round: 0.5).value(1.0);
274::
275method::trunc
276code::
277a = { |max| max.rand.postln };
278(a trunc: 2).value(10);
279::
280method::atan2
281code::
282a = { 1.0.rand2 };
283a.atan2.dup(10);
284::
285method::hypot
286code::
287a = { 1.0.rand2 };
288a.hypot.dup(10);
289::
290method::hypotApx
291code::
292a = { 1.0.rand2 };
293a.hypotApx.dup(10);
294::
295method::>>
296code::
297a = { [2r10010, 2r101011, 2r11100].choose.postln };
298b = a >> 2;
299b.value.asBinaryDigits.join;
300::
301method::+>>
302code::
303a = { [2r10010, 2r101011, 2r11100].choose.postln };
304b = a +>> 2;
305b.value.asBinaryDigits.join;
306::
307method::ring1
308
309(a * b) + a
310
311code::
312({ [5, 6, 2].choose.postln } ring1: { [2, -1, 3].choose.postln }).value
313
314// UGens are also abstract functions
315(
316{ a = SinOsc.ar(335); b = SinOsc.ar(MouseX.kr(1, 1000, 1));
317ring1(a, b) * 0.1 }.play;
318)
319::
320method::ring2
321
322((a*b) + a + b)
323
324code::
325({ [5, 6, 2].choose.postln } ring2: { [2, -1, 3].choose.postln }).value
326
327(
328{ a = SinOsc.ar(335); b = SinOsc.ar(MouseX.kr(1, 1000, 1));
329ring2(a, b) * 0.1 }.play;
330)
331::
332method::ring3
333
334(a * a * b)
335
336code::
337({ [5, 6, 2].choose.postln } ring3: { [2, -1, 3].choose.postln }).value
338
339(
340{ a = SinOsc.ar(335); b = SinOsc.ar(MouseX.kr(1, 1000, 1));
341ring3(a, b) * 0.1 }.play;
342)
343::
344method::ring4
345
346((a*a *b) - (a*b*b))
347
348code::
349({ [5, 6, 2].choose.postln } ring4: { [2, -1, 3].choose.postln }).value
350
351(
352{ a = SinOsc.ar(335); b = SinOsc.ar(MouseX.kr(1, 1000, 1));
353ring4(a, b) * 0.1 }.play;
354)
355::
356method::difsqr
357
358(a*a) - (b*b)
359
360code::
361({ [5, 6, 2].choose.postln } difsqr: { [2, -1, 3].choose.postln }).value
362
363(
364{ a = SinOsc.ar(335); b = SinOsc.ar(MouseX.kr(1, 1000, 1));
365difsqr(a, b) * 0.1 }.play;
366)
367::
368method::sumsqr
369
370(a*a) + (b*b)
371
372code::
373({ [5, 6, 2].choose.postln } sumsqr: { [2, -1, 3].choose.postln }).value
374
375(
376{ a = SinOsc.ar(335); b = SinOsc.ar(MouseX.kr(1, 1000, 1));
377sumsqr(a, b) * 0.1 }.play;
378)
379::
380method::sqrdif
381
382(a - b) ** 2
383
384code::
385({ [5, 6, 2].choose.postln } sqrdif: { [2, -1, 3].choose.postln }).value
386
387(
388{ a = SinOsc.ar(335); b = SinOsc.ar(MouseX.kr(1, 1000, 1));
389ring4(a, b) * 0.1 }.play;
390)
391::
392method::sqrsum
393
394(a + b) ** 2
395
396code::
397({ [5, 6, 2].choose.postln } sqrsum: { [2, -1, 3].choose.postln }).value
398
399(
400{ a = SinOsc.ar(335); b = SinOsc.ar(MouseX.kr(1, 1000, 1));
401sqrsum(a, b) * 0.1 }.play;
402)
403::
404method::absdif
405
406(a - b).abs
407
408code::
409({ [5, 6, 2].choose.postln } absdif: { [2, -1, 3].choose.postln }).value
410
411(
412{ a = SinOsc.ar(335); b = SinOsc.ar(MouseX.kr(1, 1000, 1));
413absdif(a, b) * 0.1 }.play;
414)
415::
416
417method::moddif
418absolute difference in modulo arithmetics.
419
420method::amclip
4210 when b <= 0, a*b when b > 0
422
423method::scaleneg
424a * b when a < 0, otherwise a.
425
426method::clip2
427clips receiver to +/- aNumber
428
429method::excess
430Returns the difference of the receiver and its clipped form.
431method::<!
432method::rrand
433code::
434a = { |x| sin(x) } rrand: { |x| sin(x) *  -1 };
435(0..1000).normalize(0, 5pi).collect(a).plot;
436
437(
438{ a = SinOsc.ar(335); b = SinOsc.ar(MouseX.kr(1, 1000, 1));
439rrand(a, b) * 0.1 }.play;
440)
441::
442method::exprand
443method::rotate
444method::dist
445method::bitAnd
446method::bitOr
447method::bitXor
448method::bitHammingDistance
449method::@
450
451subsection:: Messages with more arguments (n-ary Operators)
452
453The following messages return an object which represents a delayed n-ary operation, i.e. an operation between several objects (often three). For example, rescaling a function with linlin will result in a new function that, when called, scales the results of the evaluation of all operands.
454
455All of the following messages send the message code::composeNAryOp:: to the receiver with the
456binary message selector and the other operands as arguments.
457See link::Classes/NAryOpFunction::.
458
459method::clip
460method::wrap
461method::fold
462method::blend
463method::linlin
464method::linexp
465method::explin
466method::expexp
467
468subsection:: other
469
470method::applyTo
471
472Interface that allows us to combine selectors (Symbols) and Functions. Sends valueArray(args) to this.
473discussion::
474code::
475// example:
476
477f = [{ |a, b| a * b * 100.rand }, { |a, b| sin(a) * sin(b) }, '*', '/'];
478f.choose.postcs.applyTo(3, 4);
479
480// this is used in SequenceableCollection reduce:
481(1..10).reduce('+');
482(1..10).reduce({ |a, b| a * b * 1.0.rand });
483::
484
485method::asUGenInput
486
487returns:: the result of sending the value(for) message to this.
488discussion::
489code::
490// example:
491(
492var f, g, product;
493f = { SinOsc.ar(400) };
494g = { LFPulse.kr(8)  };
495product = f * g * 0.1;
496{ Pan2.ar(product, SinOsc.kr(0.3)) }.play;
497)
498::
499
500method::sampled
501Sample a function.
502discussion::
503code::
504//sample a function
505f = { |x| sin(3*x)*cos(8*x) }
506f.plotGraph(from:0,to:2);
507f.sampled(10,0,2).plotGraph(from:0,to:2);
508f.sampled(80,0,2).plotGraph(from:0,to:2);
509
510//on complicated functions a sampled function is less cpy heavy.
511f = { |x| 60.collect{ 2**((x-rrand(0.0,1.0))) }.sum/60 };
512f.plotGraph(from:0,to:1);
513g = f.sampled(200);
514g.plotGraph(from:0,to:1);
515{ 200.collect{ f.(rand(0.0,1.0)) } }.bench;
516{ 200.collect{ g.(rand(0.0,1.0)) } }.bench;
517::
518
519subsection::Function Composition
520
521When unary, binary or n-ary operators are applied to an abstract function, it returns an object that represents
522this operation, without evaluating the function: link::Classes/UnaryOpFunction::, link::Classes/BinaryOpFunction::, link::Classes/NAryOpFunction::.
523Note that different subclasses like link::Classes/Pattern:: or link::Classes/UGen:: have their own composition scheme analogous to the one of AbstractFunction itself. For more about functions, see link::Classes/Function::.
524
525code::
526// compose a function that will return an array of random length
527a = { |n| { 16.rand } ! n } <> { |x, y| rrand(4, 8) };
528a.value;
529// compose a function from a that selects only odd values
530b = { |x| x.select(_.odd) } <> a;
531b.value;
532::
533
534
535examples::
536
537code::
538// examples
539
540a = { 1.0.rand } + 8;
541a.value;
542
543
544y = { 8 } + { 1.0.rand };
545y.value;
546::
547
548code::
549// arguments are passed into both functions
550
551y = { |x=0| x } + { 1.0.rand };
552y.value(10);
553
554
555y = { |x=0| x * 3 } + { |x=0| x + 1.0.rand };
556y.value(10);
557
558y.postcs;
559
560y = { |x=0| x * 3 } + { |x=0| x + 1.0.rand } * { |x=0| [50, 100].choose + x } + 1.0;
561y.value(10);
562::
563
564code::
565// environments can be used as a lookup with valueEnvir:
566
567(
568Environment.use {
569	~y = 10;
570	~x = 2;
571	~z = { |x=8| x } + { |y=0| y + 1.0.rand };
572	~z.valueEnvir;
573}
574)
575::
576
577code::
578// n-ary operators:
579
580a = blend({ 3.0.rand }, { 1000.rand }, { |frac| frac });
581a.value(0.5);
582
583a.value((0, 0.06..1)); // creates a range of values..
584::
585
586