2 classvar <dependantsDictionary, currentEnvironment, topEnvironment, <uniqueMethods;
6 *new { arg maxSize = 0;
9 // creates a new instance that can hold up to maxSize
10 // indexable slots. the indexed size will be zero.
11 // to actually put things in the object you need to
14 *newCopyArgs { arg ... args;
15 _BasicNewCopyArgsToInstVars
17 // creates a new instance that can hold up to maxSize
18 // indexable slots. the indexed size will be zero.
19 // to actually put things in the object you need to
23 // debugging and diagnostics
25 post { this.asString.post }
26 postln { this.asString.postln; }
27 postc { this.asString.postc }
28 postcln { this.asString.postcln; }
29 postcs { this.asCompileString.postln }
30 totalFree { _TotalFree }
31 largestFreeBlock { _LargestFreeBlock }
32 gcDumpGrey { _GCDumpGrey }
33 gcDumpSet { arg set; _GCDumpSet }
35 gcSanity { _GCSanity }
36 canCallOS { _CanCallOS }
44 do { arg function; function.value(this, 0) }
45 generate { arg function, state; this.do(function); ^state }
46 //reverseDo { arg function; function.value(this, 0) }
49 class { _ObjectClass; ^this.primitiveFailed }
50 isKindOf { arg aClass; _ObjectIsKindOf; ^this.primitiveFailed }
51 isMemberOf { arg aClass; _ObjectIsMemberOf; ^this.primitiveFailed }
52 respondsTo { arg aSymbol; _ObjectRespondsTo; ^this.primitiveFailed }
59 perform { arg selector ... args;
63 performList { arg selector, arglist;
68 // perform only if Function. see Function-functionPerformList
72 // super.perform(selector,arg) doesn't do what you might think.
73 // \perform would be looked up in the superclass, not the selector you are interested in.
74 // Hence these methods, which look up the selector in the superclass.
75 // These methods must be called with this as the receiver.
76 superPerform { arg selector ... args;
80 superPerformList { arg selector, arglist;
85 tryPerform { arg selector ... args;
86 ^if(this.respondsTo(selector),{
87 this.performList(selector,args)
90 multiChannelPerform { arg selector ... args;
91 ^flop([this, selector] ++ args).collect { |item|
92 performList(item[0], item[1], item[2..])
96 performWithEnvir { |selector, envir|
98 var method = this.class.findRespondingMethodFor(selector);
100 if(method.isNil) { ^this.doesNotUnderstand(selector) };
102 argNames = method.argNames.drop(1);
103 args = method.prototypeFrame.drop(1);
104 argNames.do { |name, i|
105 var val = envir[name];
106 val !? { args[i] = val };
109 ^this.performList(selector, args)
112 performKeyValuePairs { |selector, pairs|
113 ^this.performWithEnvir(selector, ().putPairs(pairs))
117 copy { ^this.shallowCopy }
118 contentsCopy { ^this.shallowCopy }
119 shallowCopy { _ObjectShallowCopy; ^this.primitiveFailed }
121 // if object is immutable then return a shallow copy, else return receiver.
122 _ObjectCopyImmutable;
123 ^this.primitiveFailed
128 ^this.primitiveFailed
131 ^Array.fill(n, { this.copy });
142 valueArrayEnvir { ^this }
144 // equality, identity
145 == { arg obj; ^this === obj }
146 != { arg obj; ^not(this == obj) }
147 === { arg obj; _Identical; ^this.primitiveFailed }
148 !== { arg obj;_NotIdentical; ^this.primitiveFailed }
149 equals { arg that, properties;
150 ^that.respondsTo(properties) and: {
151 properties.every { |selector| this.perform(selector) == that.perform(selector) }
154 compareObject { arg that, instVarNames;
155 if(this === that,{ ^true });
156 // possibly ok if one of us isKindOf the other
157 if(this.class !== that.class,{ ^false });
158 if(instVarNames.notNil,{
159 instVarNames.do({ |varname|
160 if(this.instVarAt(varname) != that.instVarAt(varname),{
165 this.instVarSize.do({ arg i;
166 if(this.instVarAt(i) != that.instVarAt(i),{ ^false });
171 instVarHash { arg instVarNames;
172 var indices, res = this.class.hash;
173 if(this.instVarSize == 0) {
176 indices = if(instVarNames.notNil) {
177 instVarNames.collect(this.slotIndex(_))
179 (0..this.instVarSize-1)
182 var obj = this.instVarAt(i);
183 res = res << 1 bitXor: obj.hash; // encode slot order by left shifting
188 basicHash { _ObjectHash; ^this.primitiveFailed }
189 hash { _ObjectHash; ^this.primitiveFailed }
190 identityHash { _ObjectHash; ^this.primitiveFailed }
192 // create an association
193 -> { arg obj; ^Association.new(this, obj) }
198 first { arg inval; this.reset; ^this.next(inval) }
199 iter { ^OneShotStream(this) }
203 removedFromScheduler { ^this }
205 embedInStream { ^this.yield; }
209 inval = this.embedInStream(inval);
218 item = this.next(inval);
219 if (item.isNil) { nil.alwaysYield };
225 repeat { arg repeats = inf; ^Pn(this, repeats).asStream }
226 loop { ^this.repeat(inf) }
231 composeEvents { arg event; ^event.copy }
243 ?? { arg obj; ^this }
244 !? { arg obj; ^obj.value(this) }
251 isSequenceableCollection { ^false }
252 isCollection { ^false }
255 containsSeqColl { ^false }
256 isValidUGenInput { ^false }
257 isException { ^false }
258 isFunction { ^false }
260 matchItem {|item| ^this === item }
263 ^this.trueAt(key).not
266 pointsTo { arg obj; _ObjectPointsTo; ^this.primitiveFailed }
267 mutable { _ObjectIsMutable; ^this.primitiveFailed }
268 frozen { _ObjectIsPermanent; ^this.primitiveFailed }
272 thisProcess.nowExecutingPath = nil;
278 PrimitiveFailedError(this).throw;
281 error(this.asString);
285 subclassResponsibility { arg method;
286 SubclassResponsibilityError(this, method, this.class).throw;
288 doesNotUnderstand { arg selector ... args;
289 DoesNotUnderstandError(this, selector, args).throw;
291 shouldNotImplement { arg method;
292 ShouldNotImplementError(this, method, this.class).throw;
294 outOfContextReturn { arg method, result;
295 OutOfContextReturnError(this, method, result).throw;
297 immutableError { arg value;
298 ImmutableError(this, value).throw;
301 deprecated { arg method, alternateMethod;
302 DeprecatedError(this, method, alternateMethod, this.class).throw;
305 mustBeBoolean { MustBeBooleanError(nil, this).throw; }
306 notYetImplemented { NotYetImplementedError(nil, this).throw; }
308 dumpBackTrace { _DumpBackTrace }
309 getBackTrace { _GetBackTrace }
311 if (Error.handling) {
312 error("throw during error handling!\n");
316 thisThread.handleError(this);
321 species { ^this.class }
322 asCollection { ^[this] }
323 asSymbol { ^this.asString.asSymbol }
324 asString { arg limit = 512;
327 string = String.streamContentsLimit({ arg stream; this.printOn(stream); }, limit);
328 if (string.size >= limit, { ^(string ++ "...etc..."); });
333 ^String.streamContents({ arg stream; this.storeOn(stream); });
336 cs { ^this.asCompileString }
338 printClassNameOn { arg stream;
340 title = this.class.name.asString;
341 stream << if((title @ 0).isVowel, { "an " }, { "a " }) << title;
343 printOn { arg stream;
344 this.printClassNameOn(stream);
346 storeOn { arg stream;
347 stream << this.class.name;
348 this.storeParamsOn(stream);
349 this.storeModifiersOn(stream);
351 storeParamsOn { arg stream;
352 var args = this.storeArgs;
354 stream << "(" <<<* this.simplifyStoreArgs(args) << ")";
359 simplifyStoreArgs { arg args;
360 var res = Array.new, newMethod, methodArgs;
361 newMethod = this.class.class.findRespondingMethodFor(\new);
362 methodArgs = newMethod.prototypeFrame.drop(1);
363 args.size.reverseDo { |i|
364 if(methodArgs[i] != args[i]) {
371 storeModifiersOn { arg stream;}
374 as { arg aSimilarClass; ^aSimilarClass.newFrom(this) }
375 dereference { ^this } // see Ref::dereference
376 reference { ^Ref.new(this) }
377 asRef { ^Ref.new(this) }
378 // asArray { ^Array.with(this) }
379 asArray { ^this.asCollection.asArray }
380 asSequenceableCollection { ^this.asArray }
384 deepCollect { arg depth, function, index = 0, rank = 0; ^function.value(this, index, rank) }
385 deepDo { arg depth, function, index = 0, rank = 0; function.value(this, index, rank) }
389 bubble { arg depth=0, levels=1;
390 if (levels <= 1) { ^[this] };
391 ^[this.bubble(depth,levels-1)]
394 // compatibility with sequenceable collection
396 obtain { arg index, default; ^if(index == 0) { this } { default } }
398 instill { arg index, item, default;
399 ^if(index == 0) { item } {
400 this.asArray.instill(index, item, default)
404 // FunctionList support
405 addFunc { arg ... functions;
406 ^FunctionList([this] ++ functions)
408 removeFunc { arg function; if(this === function) { ^nil } }
409 replaceFunc { arg find, replace; if(this === find) { ^replace } }
410 addFuncTo { arg variableName ... functions;
411 this.perform(variableName.asSetter, this.perform(variableName).addFunc(*functions))
413 removeFuncFrom { arg variableName, function;
414 this.perform(variableName).removeFunc(function)
419 // compiler magic: the compiler inlines the following loop
420 // thus an uninlinable while can be implemented using while itself
421 while({ this.value }, {
425 switch { arg ... cases;
426 cases.pairsDo { | test, trueFunc |
427 if (this == test.value) { ^trueFunc.value };
429 if (cases.size.odd) { ^cases.last.value };
436 ^this.primitiveFailed
440 ^this.primitiveFailed
442 yieldAndReset { arg reset = true;
443 _RoutineYieldAndReset
444 ^this.primitiveFailed
447 var time = thisThread.beats;
448 while { thisThread.beats - time < val } { this.value.yield }
451 // dependancy support
452 *initClass { dependantsDictionary = IdentityDictionary.new(4); }
454 ^dependantsDictionary.at(this) ?? { IdentitySet.new };
456 changed { arg what ... moreArgs;
457 dependantsDictionary.at(this).copy.do({ arg item;
458 item.update(this, what, *moreArgs);
461 addDependant { arg dependant;
463 theDependants = dependantsDictionary.at(this);
464 if(theDependants.isNil,{
465 theDependants = IdentitySet.new.add(dependant);
466 dependantsDictionary.put(this, theDependants);
468 theDependants.add(dependant);
471 removeDependant { arg dependant;
473 theDependants = dependantsDictionary.at(this);
474 if (theDependants.notNil, {
475 theDependants.remove(dependant);
476 if (theDependants.size == 0, {
477 dependantsDictionary.removeAt(this);
482 this.releaseDependants;
485 dependantsDictionary.removeAt(this);
487 update { arg theChanged, theChanger; // respond to a change in a model
491 // instance specific method support
492 addUniqueMethod { arg selector, function;
494 if(function.isKindOf(Function).not) {
495 Error("A method must be defined using a function").throw
497 if(uniqueMethods.isNil, { uniqueMethods = IdentityDictionary.new });
498 methodDict = uniqueMethods.at(this);
499 if (methodDict.isNil, {
500 methodDict = IdentityDictionary.new;
501 uniqueMethods.put(this, methodDict);
503 methodDict.put(selector, function);
505 removeUniqueMethods {
506 if (uniqueMethods.notNil, {
507 uniqueMethods.removeAt(this);
510 removeUniqueMethod { arg selector;
512 if (uniqueMethods.notNil, {
513 methodDict = uniqueMethods.at(this);
514 if (methodDict.notNil, {
515 methodDict.removeAt(selector);
516 if (methodDict.size < 1, {
517 uniqueMethods.removeAt(this);
523 inspect { ^this.inspectorClass.new(this) }
524 inspectorClass { ^ObjectInspector }
526 // finds the inspector for this object, if any.
527 ^Inspector.inspectorFor(this)
531 // virtual machine debugging...
532 crash { _HostDebugger } // for serious problems..
533 stackDepth { _StackDepth }
534 dumpStack { _DumpStack }
535 dumpDetailedBackTrace { _DumpDetailedBackTrace }
540 ^this.primitiveFailed
543 // Math protocol support
544 // translate these operators to names the code generator can safely generate in C++
545 & { arg that; ^bitAnd(this, that) }
546 | { arg that; ^bitOr(this, that) }
547 % { arg that; ^mod(this, that) }
548 ** { arg that; ^pow(this, that) }
549 << { arg that; ^leftShift(this, that) }
550 >> { arg that; ^rightShift(this, that) }
551 +>> { arg that; ^unsignedRightShift(this, that) }
552 <! { arg that; ^firstArg(this, that) }
554 asInt { ^this.asInteger }
556 blend { arg that, blendFrac = 0.5;
557 // blendFrac should be from zero to one
558 ^this + (blendFrac * (that - this));
561 blendAt { arg index, method='clipAt';
562 var iMin = index.roundUp.asInteger - 1;
563 ^blend(this.perform(method, iMin), this.perform(method, iMin+1), absdif(index, iMin));
566 blendPut { arg index, val, method='wrapPut';
567 var iMin = index.floor.asInteger;
568 var ratio = absdif(index, iMin);
569 this.perform(method, iMin, val * (1-ratio));
570 this.perform(method, iMin + 1, val * ratio);
574 fuzzyEqual { arg that, precision=1.0; ^max(0.0, 1.0 - (abs(this - that)/precision)) }
579 pair { arg that; ^[this, that] }
585 list = list.add(a.asArray ++ b)
593 awake { arg beats, seconds, clock;
595 time = seconds; // prevent optimization
598 beats_ { } // for PauseStream
599 clock_ { } // for Clock
601 // catch binary operators failure
602 performBinaryOpOnSomething { arg aSelector, thing, adverb;
603 if (aSelector === '==', {
606 if (aSelector === '!=', {
609 BinaryOpFailureError(this, aSelector, [thing, adverb]).throw;
612 performBinaryOpOnSimpleNumber { arg aSelector, thing, adverb;
613 ^this.performBinaryOpOnSomething(aSelector, thing, adverb)
615 performBinaryOpOnSignal { arg aSelector, thing, adverb;
616 ^this.performBinaryOpOnSomething(aSelector, thing, adverb)
618 performBinaryOpOnComplex { arg aSelector, thing, adverb;
619 ^this.performBinaryOpOnSomething(aSelector, thing, adverb)
621 performBinaryOpOnSeqColl { arg aSelector, thing, adverb;
622 ^this.performBinaryOpOnSomething(aSelector, thing, adverb)
624 performBinaryOpOnUGen { arg aSelector, thing, adverb;
625 ^this.performBinaryOpOnSomething(aSelector, thing, adverb)
628 writeDefFile { arg name, dir, overwrite = (true);
630 StartUp.defer { // make sure the synth defs are written to the right path
632 dir = dir ? SynthDef.synthDefDir;
633 if (name.isNil) { Error("missing SynthDef file name").throw } {
634 name = dir +/+ name ++ ".scsyndef";
635 if(overwrite or: { pathMatch(name).isEmpty })
637 file = File(name, "w");
639 AbstractMDPlugin.clearMetadata(name);
640 this.asArray.writeDef(file);
650 isInputUGen { ^false }
651 isOutputUGen { ^false }
652 isControlUGen { ^false }
654 asUGenInput { ^this }
655 asControlInput { ^this }
656 asAudioRateInput { ^if(this.rate != \audio) { K2A.ar(this) } { this } }
659 // these are the same as new and newCopyArgs, but should not be overridden by any class.
660 *prNew { arg maxSize = 0;
662 ^this.primitiveFailed
663 // creates a new instance that can hold up to maxSize
664 // indexable slots. the indexed size will be zero.
665 // to actually put things in the object you need to
668 *prNewCopyArgs { arg ... args;
669 _BasicNewCopyArgsToInstVars
670 ^this.primitiveFailed
671 // creates a new instance which holds the args as slots
675 // these are dangerous operations as they break encapsulation and
676 // can allow access to slots that should not be accessed because they are private to the
677 // virtual machine, such as Frame objects.
679 // see counterparts to these in ArrayedCollection
684 // index can be an integer or symbol.
685 ^this.instVarAt(index);
687 slotPut { arg index, value;
688 // index can be an integer or symbol.
689 ^this.instVarPut(index, value);
692 // index must be an integer.
693 ^this.class.instVarNames.at(index)
696 // key must be a symbol.
697 ^this.class.instVarNames.indexOf(key)
699 slotsDo { arg function;
700 this.slotSize.do {|i|
701 function.value(this.slotKey(i), this.slotAt(i), i);
704 slotValuesDo { arg function;
705 this.slotSize.do {|i|
706 function.value(this.slotAt(i), i);
710 // getSlots and setSlots will be used for a new implementation of asCompileString.
711 // getSlots stores the keys and values so that if the instance
712 // variable order changes, setSlots they will still set the right one.
715 array = Array.new(this.slotSize * 2);
716 this.slotSize.do {|i|
717 array.add(this.slotKey(i));
718 array.add(this.slotAt(i));
722 setSlots { arg array;
723 array.pairsDo {|key, value|
724 this.slotPut(key, value);
728 instVarSize { _InstVarSize; ^this.primitiveFailed }
729 instVarAt { arg index;
730 // index can be an integer or symbol.
732 ^this.primitiveFailed;
734 instVarPut { arg index, item;
735 // index can be an integer or symbol.
737 ^this.primitiveFailed;
740 //////////// ARCHIVING ////////////
742 writeArchive { arg pathname;
743 ^this.writeTextArchive(pathname)
745 *readArchive { arg pathname;
746 ^this.readTextArchive(pathname)
754 archiveAsCompileString { ^false }
755 archiveAsObject { ^this.archiveAsCompileString.not }
759 writeTextArchive { arg pathname;
760 var text = this.asTextArchive;
761 var file = File(pathname, "w");
767 MethodError("Could not open file % for writing".format(pathname.asCompileString), this).throw;
770 *readTextArchive { arg pathname;
774 var objects, list, stream, firsttime = true;
776 if (this.archiveAsCompileString) {
777 this.checkCanArchive;
778 ^this.asCompileString ++ "\n"
781 objects = IdentityDictionary.new;
783 this.getContainedObjects(objects);
785 stream = CollStream.new;
786 stream << "var o, p;\n";
788 list = List.newClear(objects.size);
789 objects.keysValuesDo {|obj, index| list[index] = obj };
794 if (i != 0) { stream << ", "; };
795 if ((i & 3) == 0) { stream << "\n\t" };
797 if (obj.archiveAsCompileString) {
798 stream << obj.asCompileString;
800 size = obj.indexedSize;
801 stream << obj.class.name << ".prNew";
803 stream << "(" << size << ")"
807 stream << "\n];\np = [";
812 if (obj.archiveAsCompileString.not) {
813 slots = obj.getSlots;
814 if (slots.size > 0) {
815 if (firsttime.not) { stream << ", "; };
817 stream << "\n\t// " << obj.class.name;
819 stream << i << ", [ ";
820 if (obj.isKindOf(ArrayedCollection)) {
823 if (j != 0) { stream << ", "; };
824 if ((j != 0) && ((j & 3) == 0)) { stream << "\n\t\t" };
825 index = objects[slot];
827 stream << slot.asCompileString;
829 stream << "o[" << index << "]";
833 slots.pairsDo {|key, slot, j|
835 if (j != 0) { stream << ", "; };
836 if ((j != 0) && ((j & 3) == 0)) { stream << "\n\t\t" };
837 stream << key << ": ";
838 index = objects[slot];
840 stream << slot.asCompileString;
842 stream << "o[" << index << "]";
852 stream << "prUnarchive(o,p);\n";
855 getContainedObjects { arg objects;
856 if (objects[this].notNil) {^this};
857 objects[this] = objects.size;
859 if (this.archiveAsCompileString.not) {
860 this.slotsDo {|key, slot|
861 if (slot.archiveAsObject) {
862 slot.getContainedObjects(objects);
868 // old binary archiving
869 // this will break if the instance vars change !
871 writeBinaryArchive { arg pathname;
873 ^this.primitiveFailed;
875 *readBinaryArchive { arg pathname;
877 ^this.primitiveFailed;
881 ^this.primitiveFailed;
887 // support for ViewRedirect
888 *classRedirect { ^this }
891 this.class.asString.help