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