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);
99 if(method.isNil) { ^this.doesNotUnderstand(selector) };
101 envir = method.makeEnvirFromArgs.putAll(envir);
103 argNames = method.argNames.drop(1);
104 args = envir.atAll(argNames);
105 ^this.performList(selector, args)
108 performKeyValuePairs { |selector, pairs|
109 ^this.performWithEnvir(selector, ().putPairs(pairs))
113 copy { ^this.shallowCopy }
114 contentsCopy { ^this.shallowCopy }
115 shallowCopy { _ObjectShallowCopy; ^this.primitiveFailed }
117 // if object is immutable then return a shallow copy, else return receiver.
118 _ObjectCopyImmutable;
119 ^this.primitiveFailed
124 ^this.primitiveFailed
127 ^Array.fill(n, { this.copy });
138 valueArrayEnvir { ^this }
140 // equality, identity
141 == { arg obj; ^this === obj }
142 != { arg obj; ^not(this == obj) }
143 === { arg obj; _Identical; ^this.primitiveFailed }
144 !== { arg obj;_NotIdentical; ^this.primitiveFailed }
145 equals { arg that, properties;
146 ^that.respondsTo(properties) and: {
147 properties.every { |selector| this.perform(selector) == that.perform(selector) }
150 compareObject { arg that, instVarNames;
151 if(this === that,{ ^true });
152 // possibly ok if one of us isKindOf the other
153 if(this.class !== that.class,{ ^false });
154 if(instVarNames.notNil,{
155 instVarNames.do({ |varname|
156 if(this.instVarAt(varname) != that.instVarAt(varname),{
161 this.instVarSize.do({ arg i;
162 if(this.instVarAt(i) != that.instVarAt(i),{ ^false });
167 instVarHash { arg instVarNames;
168 var res = this.class.hash;
169 var indices = if(instVarNames.notNil) {
170 instVarNames.collect(this.slotIndex(_))
172 (0..this.instVarSize-1)
175 var obj = this.instVarAt(i);
176 res = res bitXor: obj.hash;
181 basicHash { _ObjectHash; ^this.primitiveFailed }
182 hash { _ObjectHash; ^this.primitiveFailed }
183 identityHash { _ObjectHash; ^this.primitiveFailed }
185 // create an association
186 -> { arg obj; ^Association.new(this, obj) }
191 first { arg inval; this.reset; ^this.next(inval) }
192 iter { ^OneShotStream(this) }
196 removedFromScheduler { ^this }
198 embedInStream { ^this.yield; }
202 inval = this.embedInStream(inval);
211 item = this.next(inval);
212 if (item.isNil) { nil.alwaysYield };
218 repeat { arg repeats = inf; ^Pn(this, repeats).asStream }
219 loop { ^this.repeat(inf) }
224 composeEvents { arg event; ^event.copy }
233 ?? { arg obj; ^this }
234 !? { arg obj; ^obj.value(this) }
241 isSequenceableCollection { ^false }
244 containsSeqColl { ^false }
245 isValidUGenInput { ^false }
246 isException { ^false }
247 isFunction { ^false }
249 matchItem {|item| ^this === item }
252 ^this.trueAt(key).not
255 pointsTo { arg obj; _ObjectPointsTo; ^this.primitiveFailed }
256 mutable { _ObjectIsMutable; ^this.primitiveFailed }
257 frozen { _ObjectIsPermanent; ^this.primitiveFailed }
261 thisProcess.nowExecutingPath = nil;
267 PrimitiveFailedError(this).throw;
270 error(this.asString);
274 subclassResponsibility { arg method;
275 SubclassResponsibilityError(this, method, this.class).throw;
277 doesNotUnderstand { arg selector ... args;
278 DoesNotUnderstandError(this, selector, args).throw;
280 shouldNotImplement { arg method;
281 ShouldNotImplementError(this, method, this.class).throw;
283 outOfContextReturn { arg method, result;
284 OutOfContextReturnError(this, method, result).throw;
286 immutableError { arg value;
287 ImmutableError(this, value).throw;
290 deprecated { arg method, alternateMethod;
291 DeprecatedError(this, method, alternateMethod, this.class).throw;
294 mustBeBoolean { MustBeBooleanError(nil, this).throw; }
295 notYetImplemented { NotYetImplementedError(nil, this).throw; }
297 dumpBackTrace { _DumpBackTrace }
298 getBackTrace { _GetBackTrace }
300 if (Error.handling) {
301 error("throw during error handling!\n");
305 thisThread.handleError(this);
310 species { ^this.class }
311 asCollection { ^[this] }
312 asSymbol { ^this.asString.asSymbol }
313 asString { arg limit = 512;
316 string = String.streamContentsLimit({ arg stream; this.printOn(stream); }, limit);
317 if (string.size >= limit, { ^(string ++ "...etc..."); });
322 ^String.streamContents({ arg stream; this.storeOn(stream); });
325 cs { ^this.asCompileString }
327 printClassNameOn { arg stream;
329 title = this.class.name.asString;
330 stream << if((title @ 0).isVowel, { "an " }, { "a " }) << title;
332 printOn { arg stream;
333 this.printClassNameOn(stream);
335 storeOn { arg stream;
336 stream << this.class.name;
337 this.storeParamsOn(stream);
338 this.storeModifiersOn(stream);
340 storeParamsOn { arg stream;
341 var args = this.storeArgs;
343 stream << "(" <<<* this.simplifyStoreArgs(args) << ")";
348 simplifyStoreArgs { arg args;
349 var res = Array.new, newMethod, methodArgs;
350 newMethod = this.class.class.findRespondingMethodFor(\new);
351 methodArgs = newMethod.prototypeFrame.drop(1);
352 args.size.reverseDo { |i|
353 if(methodArgs[i] != args[i]) {
360 storeModifiersOn { arg stream;}
363 as { arg aSimilarClass; ^aSimilarClass.newFrom(this) }
364 dereference { ^this } // see Ref::dereference
365 reference { ^Ref.new(this) }
366 asRef { ^Ref.new(this) }
367 // asArray { ^Array.with(this) }
368 asArray { ^this.asCollection.asArray }
369 asSequenceableCollection { ^this.asArray }
373 deepCollect { arg depth, function; ^function.value(this, 0) }
377 bubble { arg depth=0, levels=1;
378 if (levels <= 1) { ^[this] };
379 ^[this.bubble(depth,levels-1)]
382 // compatibility with sequenceable collection
384 obtain { arg index, default; ^if(index == 0) { this } { default } }
386 instill { arg index, item, default;
387 ^if(index == 0) { item } {
388 this.asArray.instill(index, item, default)
392 // FunctionList support
393 addFunc { arg ... functions;
394 ^FunctionList([this] ++ functions)
396 removeFunc { arg function; if(this === function) { ^nil } }
397 replaceFunc { arg find, replace; if(this === find) { ^replace } }
398 addFuncTo { arg variableName ... functions;
399 this.perform(variableName.asSetter, this.perform(variableName).addFunc(*functions))
401 removeFuncFrom { arg variableName, function;
402 this.perform(variableName).removeFunc(function)
407 // compiler magic: the compiler inlines the following loop
408 // thus an uninlinable while can be implemented using while itself
409 while({ this.value }, {
413 switch { arg ... cases;
414 cases.pairsDo { | test, trueFunc |
415 if (this == test.value) { ^trueFunc.value };
417 if (cases.size.odd) { ^cases.last.value };
424 ^this.primitiveFailed
428 ^this.primitiveFailed
430 yieldAndReset { arg reset = true;
431 _RoutineYieldAndReset
432 ^this.primitiveFailed
435 var time = thisThread.beats;
436 while { thisThread.beats - time < val } { this.value.yield }
439 // dependancy support
440 *initClass { dependantsDictionary = IdentityDictionary.new(4); }
442 ^dependantsDictionary.at(this) ?? { IdentitySet.new };
444 changed { arg what ... moreArgs;
445 dependantsDictionary.at(this).copy.do({ arg item;
446 item.update(this, what, *moreArgs);
449 addDependant { arg dependant;
451 theDependants = dependantsDictionary.at(this);
452 if(theDependants.isNil,{
453 theDependants = IdentitySet.new.add(dependant);
454 dependantsDictionary.put(this, theDependants);
456 theDependants.add(dependant);
459 removeDependant { arg dependant;
461 theDependants = dependantsDictionary.at(this);
462 if (theDependants.notNil, {
463 theDependants.remove(dependant);
464 if (theDependants.size == 0, {
465 dependantsDictionary.removeAt(this);
470 this.releaseDependants;
473 dependantsDictionary.removeAt(this);
475 update { arg theChanged, theChanger; // respond to a change in a model
479 // instance specific method support
480 addUniqueMethod { arg selector, function;
482 if(function.isKindOf(Function).not) {
483 Error("A method must be defined using a function").throw
485 if(uniqueMethods.isNil, { uniqueMethods = IdentityDictionary.new });
486 methodDict = uniqueMethods.at(this);
487 if (methodDict.isNil, {
488 methodDict = IdentityDictionary.new;
489 uniqueMethods.put(this, methodDict);
491 methodDict.put(selector, function);
493 removeUniqueMethods {
494 if (uniqueMethods.notNil, {
495 uniqueMethods.removeAt(this);
498 removeUniqueMethod { arg selector;
500 if (uniqueMethods.notNil, {
501 methodDict = uniqueMethods.at(this);
502 if (methodDict.notNil, {
503 methodDict.removeAt(selector);
504 if (methodDict.size < 1, {
505 uniqueMethods.removeAt(this);
511 inspect { ^this.inspectorClass.new(this) }
512 inspectorClass { ^ObjectInspector }
514 // finds the inspector for this object, if any.
515 ^Inspector.inspectorFor(this)
519 // virtual machine debugging...
520 crash { _HostDebugger } // for serious problems..
521 stackDepth { _StackDepth }
522 dumpStack { _DumpStack }
523 dumpDetailedBackTrace { _DumpDetailedBackTrace }
528 ^this.primitiveFailed
531 // Math protocol support
532 // translate these operators to names the code generator can safely generate in C++
533 & { arg that; ^bitAnd(this, that) }
534 | { arg that; ^bitOr(this, that) }
535 % { arg that; ^mod(this, that) }
536 ** { arg that; ^pow(this, that) }
537 << { arg that; ^leftShift(this, that) }
538 >> { arg that; ^rightShift(this, that) }
539 +>> { arg that; ^unsignedRightShift(this, that) }
540 <! { arg that; ^firstArg(this, that) }
542 asInt { ^this.asInteger }
544 blend { arg that, blendFrac = 0.5;
545 // blendFrac should be from zero to one
546 ^this + (blendFrac * (that - this));
549 blendAt { arg index, method='clipAt';
550 var iMin = index.roundUp.asInteger - 1;
551 ^blend(this.perform(method, iMin), this.perform(method, iMin+1), absdif(index, iMin));
554 blendPut { arg index, val, method='wrapPut';
555 var iMin = index.floor.asInteger;
556 var ratio = absdif(index, iMin);
557 this.perform(method, iMin, val * (1-ratio));
558 this.perform(method, iMin + 1, val * ratio);
562 fuzzyEqual { arg that, precision=1.0; ^max(0.0, 1.0 - (abs(this - that)/precision)) }
567 pair { arg that; ^[this, that] }
573 list = list.add(a.asArray ++ b)
581 awake { arg beats, seconds, clock;
583 time = seconds; // prevent optimization
586 beats_ { } // for PauseStream
587 clock_ { } // for Clock
589 // catch binary operators failure
590 performBinaryOpOnSomething { arg aSelector, thing, adverb;
591 if (aSelector === '==', {
594 if (aSelector === '!=', {
597 BinaryOpFailureError(this, aSelector, [thing, adverb]).throw;
600 performBinaryOpOnSimpleNumber { arg aSelector, thing, adverb;
601 ^this.performBinaryOpOnSomething(aSelector, thing, adverb)
603 performBinaryOpOnSignal { arg aSelector, thing, adverb;
604 ^this.performBinaryOpOnSomething(aSelector, thing, adverb)
606 performBinaryOpOnComplex { arg aSelector, thing, adverb;
607 ^this.performBinaryOpOnSomething(aSelector, thing, adverb)
609 performBinaryOpOnSeqColl { arg aSelector, thing, adverb;
610 ^this.performBinaryOpOnSomething(aSelector, thing, adverb)
612 performBinaryOpOnUGen { arg aSelector, thing, adverb;
613 ^this.performBinaryOpOnSomething(aSelector, thing, adverb)
616 writeDefFile { arg name, dir, overwrite = (true);
618 StartUp.defer { // make sure the synth defs are written to the right path
620 dir = dir ? SynthDef.synthDefDir;
621 if (name.isNil) { error("missing SynthDef file name") } {
622 name = dir +/+ name ++ ".scsyndef";
623 if(overwrite or: { pathMatch(name).isEmpty })
625 file = File(name, "w");
627 AbstractMDPlugin.clearMetadata(name);
628 this.asArray.writeDef(file);
638 isInputUGen { ^false }
639 isOutputUGen { ^false }
640 isControlUGen { ^false }
642 asUGenInput { ^this }
643 asControlInput { ^this }
644 asAudioRateInput { ^if(this.rate != \audio) { K2A.ar(this) } { this } }
647 // these are the same as new and newCopyArgs, but should not be overridden by any class.
648 *prNew { arg maxSize = 0;
650 ^this.primitiveFailed
651 // creates a new instance that can hold up to maxSize
652 // indexable slots. the indexed size will be zero.
653 // to actually put things in the object you need to
656 *prNewCopyArgs { arg ... args;
657 _BasicNewCopyArgsToInstVars
658 ^this.primitiveFailed
659 // creates a new instance which holds the args as slots
663 // these are dangerous operations as they break encapsulation and
664 // can allow access to slots that should not be accessed because they are private to the
665 // virtual machine, such as Frame objects.
667 // see counterparts to these in ArrayedCollection
672 // index can be an integer or symbol.
673 ^this.instVarAt(index);
675 slotPut { arg index, value;
676 // index can be an integer or symbol.
677 ^this.instVarPut(index, value);
680 // index must be an integer.
681 ^this.class.instVarNames.at(index)
684 // key must be a symbol.
685 ^this.class.instVarNames.indexOf(key)
687 slotsDo { arg function;
688 this.slotSize.do {|i|
689 function.value(this.slotKey(i), this.slotAt(i), i);
692 slotValuesDo { arg function;
693 this.slotSize.do {|i|
694 function.value(this.slotAt(i), i);
698 // getSlots and setSlots will be used for a new implementation of asCompileString.
699 // getSlots stores the keys and values so that if the instance
700 // variable order changes, setSlots they will still set the right one.
703 array = Array.new(this.slotSize * 2);
704 this.slotSize.do {|i|
705 array.add(this.slotKey(i));
706 array.add(this.slotAt(i));
710 setSlots { arg array;
711 array.pairsDo {|key, value|
712 this.slotPut(key, value);
716 instVarSize { _InstVarSize; ^this.primitiveFailed }
717 instVarAt { arg index;
718 // index can be an integer or symbol.
720 ^this.primitiveFailed;
722 instVarPut { arg index, item;
723 // index can be an integer or symbol.
725 ^this.primitiveFailed;
728 //////////// ARCHIVING ////////////
730 writeArchive { arg pathname;
731 ^this.writeTextArchive(pathname)
733 *readArchive { arg pathname;
734 ^this.readTextArchive(pathname)
742 archiveAsCompileString { ^false }
743 archiveAsObject { ^this.archiveAsCompileString.not }
747 writeTextArchive { arg pathname;
748 var text = this.asTextArchive;
749 var file = File(pathname, "w");
755 MethodError("Could not open file % for writing".format(pathname.asCompileString), this).throw;
758 *readTextArchive { arg pathname;
762 var objects, list, stream, firsttime = true;
764 if (this.archiveAsCompileString) {
765 this.checkCanArchive;
766 ^this.asCompileString ++ "\n"
769 objects = IdentityDictionary.new;
771 this.getContainedObjects(objects);
773 stream = CollStream.new;
774 stream << "var o, p;\n";
776 list = List.newClear(objects.size);
777 objects.keysValuesDo {|obj, index| list[index] = obj };
782 if (i != 0) { stream << ", "; };
783 if ((i & 3) == 0) { stream << "\n\t" };
785 if (obj.archiveAsCompileString) {
786 stream << obj.asCompileString;
788 size = obj.indexedSize;
789 stream << obj.class.name << ".prNew";
791 stream << "(" << size << ")"
795 stream << "\n];\np = [";
800 if (obj.archiveAsCompileString.not) {
801 slots = obj.getSlots;
802 if (slots.size > 0) {
803 if (firsttime.not) { stream << ", "; };
805 stream << "\n\t// " << obj.class.name;
807 stream << i << ", [ ";
808 if (obj.isKindOf(ArrayedCollection)) {
811 if (j != 0) { stream << ", "; };
812 if ((j != 0) && ((j & 3) == 0)) { stream << "\n\t\t" };
813 index = objects[slot];
815 stream << slot.asCompileString;
817 stream << "o[" << index << "]";
821 slots.pairsDo {|key, slot, j|
823 if (j != 0) { stream << ", "; };
824 if ((j != 0) && ((j & 3) == 0)) { stream << "\n\t\t" };
825 stream << key << ": ";
826 index = objects[slot];
828 stream << slot.asCompileString;
830 stream << "o[" << index << "]";
840 stream << "prUnarchive(o,p);\n";
843 getContainedObjects { arg objects;
844 if (objects[this].notNil) {^this};
845 objects[this] = objects.size;
847 if (this.archiveAsCompileString.not) {
848 this.slotsDo {|key, slot|
849 if (slot.archiveAsObject) {
850 slot.getContainedObjects(objects);
856 // old binary archiving
857 // this will break if the instance vars change !
859 writeBinaryArchive { arg pathname;
861 ^this.primitiveFailed;
863 *readBinaryArchive { arg pathname;
865 ^this.primitiveFailed;
869 ^this.primitiveFailed;
875 // support for ViewRedirect
876 *classRedirect { ^this }