1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188
|
// you must not make any change at all to the order or number of
// instance variables in these classes!
// You should also not muck with the contents of the instance
// variables unless you are sure you know what you are doing.
// You may add methods.
// Thread inherits from Stream for the benefit of its subclass Routine which can
// behave like a Stream. Thread itself is not used like a Stream.
Thread : Stream {
var <state=0, func, stack, method, block, frame, ip=0, sp=0;
var numpop=0, receiver, numArgsPushed=0;
var <parent, terminalValue;
var <primitiveError=0, <primitiveIndex=0, randData=0;
var <beats=0.0, <seconds=0.0, <clock, <nextBeat, <>endBeat, <>endValue;
var environment;
var <>exceptionHandler, >threadPlayer;
var <executingPath, <oldExecutingPath;
var rescheduledTime;
*new { arg func, stackSize = (512);
^super.new.init(func, stackSize)
}
init { arg argFunc, argStackSize = 512;
_Thread_Init
^this.primitiveFailed
}
copy { ^this } // sorry cannot copy
clock_ { arg inClock;
clock = inClock;
beats = clock.secs2beats(seconds);
}
seconds_ { arg inSeconds; seconds = inSeconds; beats = clock.secs2beats(inSeconds); }
beats_ { arg inBeats; beats = inBeats; seconds = clock.beats2secs(inBeats); }
isPlaying { ^state == 5 }
threadPlayer { ^threadPlayer ?? { this.findThreadPlayer } }
findThreadPlayer {
var parent = this.parent;
^if(parent.notNil and: { parent !== thisProcess.mainThread }) {
parent.threadPlayer
} {
this
}
}
deferAwayFrom { |func, delta = 0|
if(this === thisThread or: { delta > 0 }) {
func.defer(delta)
} {
func.value
}
}
randSeed_ { arg seed;
// You supply an integer seed.
// This method creates a new state vector and stores it in randData.
// A state vector is an Int32Array of three 32 bit words.
// SuperCollider uses the taus88 random number generator which has a
// period of 2**88, and passes all standard statistical tests.
// Normally Threads inherit the randData state vector from the Thread that created it.
_Thread_RandSeed
^this.primitiveFailed;
}
randData_ {
arg data;
_Thread_SetRandData
^this.primitiveFailed
}
randData {
_Thread_GetRandData
^this.primitiveFailed
}
failedPrimitiveName {
_PrimName
^this.primitiveFailed
}
handleError { arg error;
(exceptionHandler ? parent).handleError(error)
}
// these make Thread act like an Object not like Stream.
next { ^this }
value { ^this }
valueArray { ^this }
*primitiveError {
_PrimitiveError
^this.primitiveFailed
}
*primitiveErrorString {
_PrimitiveErrorString
^this.primitiveFailed
}
storeOn { arg stream; stream << "nil"; }
archiveAsCompileString { ^true }
checkCanArchive { "cannot archive Threads".warn }
}
Routine : Thread {
*run { arg func, stackSize, clock, quant;
var routine = super.new(func, stackSize);
^routine.play(clock ? SystemClock, quant);
}
// resume, next, value, run are synonyms
next { arg inval;
_RoutineResume
^this.primitiveFailed
}
value { arg inval;
_RoutineResume
^this.primitiveFailed
}
resume { arg inval;
_RoutineResume
^this.primitiveFailed
}
reschedule { arg argClock, quant;
deferAwayFrom(this) {
// Thread:isPlaying only answers if the thread is waiting
// It *doesn't* confirm that it is actually scheduled on a clock
if(this.nextBeat.isNil) {
Error("% can't be rescheduled when idle; use 'play' instead".format(this.class.name)).throw;
};
rescheduledTime = quant.asQuant.nextTimeOnGrid(clock, this.nextBeat);
if(argClock.isNil) { argClock = clock };
if(argClock !== clock) {
// convert to new clock's time
rescheduledTime = argClock.secs2beats(clock.beats2secs(rescheduledTime));
};
clock = argClock;
}
}
run { arg inval;
_RoutineResume
^this.primitiveFailed
}
valueArray { arg inval;
^this.value(inval)
}
reset {
_RoutineReset
^this.primitiveFailed
}
// the _RoutineStop primitive can't stop the currently running Routine
// but a user should be able to use .stop anywhere
stop {
if(this === thisThread) { nil.alwaysYield }
{ this.prStop };
}
prStop {
_RoutineStop
^this.primitiveFailed
}
p { ^Prout(func) }
storeArgs { ^[func] }
storeOn { arg stream;
stream << this.class.name;
this.storeParamsOn(stream);
this.storeModifiersOn(stream);
}
// PRIVATE
awake { arg inBeats, inSeconds, inClock;
if(rescheduledTime.isNil) {
clock = inClock;
^this.next(inBeats)
} {
// rescheduling, possibly on a new clock
clock.schedAbs(rescheduledTime, this);
rescheduledTime = nil;
^nil
}
}
prStart { arg inval;
func.value(inval);
// if the user's function returns then always yield nil
nil.alwaysYield;
}
}
|