File: Thread.sc

package info (click to toggle)
supercollider 1%3A3.13.0%2Brepack-1
  • links: PTS, VCS
  • area: main
  • in suites: bookworm
  • size: 80,292 kB
  • sloc: cpp: 476,363; lisp: 84,680; ansic: 77,685; sh: 25,509; python: 7,909; makefile: 3,440; perl: 1,964; javascript: 974; xml: 826; java: 677; yacc: 314; lex: 175; objc: 152; ruby: 136
file content (188 lines) | stat: -rw-r--r-- 4,715 bytes parent folder | download | duplicates (3)
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;
	}
}