class library: Volume - remove debug message
[supercollider.git] / SCClassLibrary / Common / Core / Function.sc
blob91979f357bf96260e474f7a4ce32655633c0c85b
1 Function : AbstractFunction {
2         var <def, context;
3         // a Function is what you get when you write a FunctionDef in your code.
4         // it consists of the function's code and the variables in its defining context
6         *new { ^this.shouldNotImplement(thisMethod) }
8         isFunction { ^true }
9         isClosed { ^def.sourceCode.notNil }
11         storeOn { arg stream; stream << (def.sourceCode ? "{ \"open Function\" }"); }
12         archiveAsCompileString { ^true }
13         archiveAsObject { ^true }
14         checkCanArchive { if (def.sourceCode.isNil) { "cannot archive open Functions".warn } }
16         shallowCopy { ^this }
18         choose { ^this.value }
20         update { |obj, what ... args| this.value(obj, what, *args) }
23         // evaluation
24         value { arg ... args;
25                 _FunctionValue
26                 // evaluate a function with args
27                 ^this.primitiveFailed
28         }
29         valueArray { arg ... args;
30                 _FunctionValueArray
31                 // evaluate a function, if the last argument is an array it will be
32                 // expanded into separate args.
33                 ^this.primitiveFailed
34         }
36         valueEnvir { arg ... args;
37                 _FunctionValueEnvir
38                 // evaluate a function with args.
39                 // unsupplied argument names are looked up in the currentEnvironment
40                 ^this.primitiveFailed
41         }
42         valueArrayEnvir { arg ... args;
43                 _FunctionValueArrayEnvir
44                 // evaluate a function, if the last argument is an array it will be
45                 // expanded into separate args.
46                 // unsupplied argument names are looked up in the currentEnvironment
47                 ^this.primitiveFailed
48         }
49         functionPerformList { arg selector, arglist;
50                 _ObjectPerformList;
51                 ^this.primitiveFailed
52         }
54         valueWithEnvir { arg envir;
55                 var prototypeFrame;
56                 if(envir.isNil) { ^this.value };
57                 prototypeFrame = def.prototypeFrame.copy;
59                 def.argNames.do { |name,i|
60                         var val = envir[name];
61                         val !? { prototypeFrame[i] = val };
62                 };
63         //      postf("argNames: % prototypeFrame: %\n", def.argNames, prototypeFrame);
65                 // evaluate a function, using arguments from the supplied environment
66                 // slightly faster than valueEnvir and does not replace the currentEnvironment
67                 ^this.valueArray(prototypeFrame)
68         }
70         performWithEnvir { |selector, envir|
71                 if(selector === \value) { ^this.valueWithEnvir(envir) };
72                 ^super.performWithEnvir(selector, envir)
73         }
75         performKeyValuePairs { |selector, pairs|
76                 var envir;
77                 if(selector !== \value) {
78                         ^this.superPerform(\performKeyValuePairs, pairs)
79                 };
81                 envir = this.def.makeEnvirFromArgs;
82                 envir.putPairs(pairs);
84                 ^this.valueWithEnvir(envir)
85         }
87         numArgs { ^def.numArgs }                // return number of arguments to the function
88         numVars { ^def.numVars }                // return number of variables in the function
89         varArgs { ^def.varArgs }                // return boolean whether function has ellipsis argument
91         loop {
92                 // loop is supported magically by the compiler,
93                 // thus it can be implemented in terms of itself
94                 loop { this.value };
95         }
97         block {
98                 ^this.value {|val| ^val };
99         }
100 //      block {
101 //              var result;
102 //              try {
103 //                      result = this.value #{|val| Break(val).throw };
104 //              }{|error|
105 //                      if (error.class == Break) {
106 //                              ^error.value
107 //                      }{
108 //                              error.throw
109 //                      }
110 //              }
111 //              ^result
112 //      }
114         asRoutine {
115                 ^Routine.new(this)
116         }
118         dup { arg n = 2;
119                 ^Array.fill(n, this)
120         }
121         sum { arg n = 2;
122                 var sum = 0;
123                 n.do {|i| sum = sum + this.value(i) };
124                 ^sum
125         }
127         defer { arg delta = 0;
128                 if (delta == 0 and: {this.canCallOS}) {
129                         this.value
130                 }{
131                         AppClock.sched(delta, { this.value; nil })
132                 }
133         }
135         thunk { ^Thunk(this) }
137         // Pattern support
138         transformEvent { arg event;
139                 ^this.value(event)
140         }
142         // ControlView support
143         set { arg ... args; ^this.valueArray(args) }
144         get { arg prevVal; ^prevVal }
146         fork { arg clock, quant, stackSize;
147                 ^Routine(this, stackSize).play(clock, quant);
148         }
150         forkIfNeeded { arg clock, quant, stackSize;
151                 if(thisThread.isKindOf(Routine), this, { ^this.fork(clock, quant, stackSize) });
152                 ^thisThread;
153         }
156         awake { arg beats, seconds, clock;
157                 var time = seconds; // prevent optimization
158                 ^this.value(beats, seconds, clock)
159         }
161         cmdPeriod { this.value }
164         bench { arg print = true;
165                 var dt;
166                 var t0 = Main.elapsedTime;
167                 this.value;
168                 dt = Main.elapsedTime - t0;
169                 if (print) { Post << "time to run: " << dt << " seconds.\n"; }
170                 ^dt
171         }
173         protect { arg handler;
174                 var result;
175                 result = this.prTry;
176                 if (result.isException) {
177                         handler.value(result);
178                         result.throw;
179                 }{
180                         handler.value; // argument should be nil if there was no exception.
181                         ^result
182                 };
183         }
185         try { arg handler;
186                 var result = this.prTry;
187                 if (result.isException) { ^handler.value(result); }
188                         { ^result }
189         }
190         prTry {
191                 var result, thread = thisThread;
192                 var next = thread.exceptionHandler,
193                         wasInProtectedFunc = Exception.inProtectedFunction;
194                 thread.exceptionHandler = {|error|
195                         thread.exceptionHandler = next; // pop
196                         ^error
197                 };
198                 Exception.inProtectedFunction = true;
199                 result = this.value;
200                 Exception.inProtectedFunction = wasInProtectedFunc;
201                 thread.exceptionHandler = next; // pop
202                 ^result
203         }
205         handleError { arg error; ^this.value(error) }
207         case { arg ... cases;
208                 cases = [this] ++ cases;
209                 cases.pairsDo { | test, trueFunc |
210                         if (test.value) { ^trueFunc.value };
211                 };
212                 if (cases.size.odd) { ^cases.last.value };
213                 ^nil
214         }
216         r { ^Routine(this) }
217         p { ^Prout(this) }
219         matchItem { arg item;
220                 ^this.value(item)
221         }
223         // scale suppoert
225         performDegreeToKey { arg scaleDegree, stepsPerOctave = 12, accidental = 0;
226                 ^this.value(scaleDegree, stepsPerOctave, accidental)
227         }
229         // multichannel expand function return values
231         flop {
232                 if(def.argNames.isNil) { ^this };
233                 ^{ |... args| args.flop.collect(this.valueArray(_)) }
234         }
237         envirFlop {
238                 var func = this.makeFlopFunc;
239                 ^{ |... args|
240                         func.valueArrayEnvir(args).collect(this.valueArray(_))
241                 }
242         }
244         makeFlopFunc {
245                 if(def.argNames.isNil) { ^this };
247                 ^interpret(
248                                 "#{ arg " ++ " " ++ def.argumentString(true) ++ "; "
249                                 ++ "[ " ++ def.argumentString(false) ++ " ].flop };"
250                                 )
251         }
253                 // attach the function to a specific environment
254         inEnvir { |envir|
255                 envir ?? { envir = currentEnvironment };
256                 ^{ |... args| envir.use({ this.valueArray(args) }) }
257         }
260 Thunk : AbstractFunction {
261         // a thunk is an unevaluated value.
262         // it gets evaluated once and then always returns that value.
263         // also known as a "promise" in Scheme.
264         // thunks have no arguments.
265         var function, value;
267         *new { arg function;
268                 ^super.newCopyArgs(function)
269         }
270         value {
271                 ^value ?? { value = function.value; function = nil; value }
272         }
273         valueArray { ^this.value }
274         valueEnvir { ^this.value }
275         valueArrayEnvir { ^this.value }
278 UGenThunk : Thunk {
279         var ugens;
280         value {
281                 var res;
282                 ugens = ugens ?? { IdentityDictionary.new };
283                 res = ugens.at(UGen.buildSynthDef);
284                 if(res.isNil) { res = super.value; ugens.put(UGen.buildSynthDef, res) };
285                 ^res
286         }