Question about handling time

I am trying to reconcile two ways of dealing with (musical) time. The context is real-time performance instrument + SC. I have some algorithmic process that generate sounds which I implement as a Task with a loop. The general outline of the loop is:

  • calculate some values
  • Synth(\makesound [val1, val2…]
  • wait

But, sometimes, I want this process to respond to an external event. This event would be an OSCdef triggered either from the instrument (e.g. onset detection) or from outside supercollider. For example, I would like the algorithm to move to the next event in the loop (i.e. stop waiting). In addition i would like to also be able to schedule such an interruption to happen in the future. e.g. in 2 seconds trigger the next event in the loop.
Any thoughts how to integrate these two modes of handling time?
thanks
Oded

There’s a trick to reschedule a Task: assign the Task’s routine into a new task.

t = Task { loop { your stuff, with wait calls etc } }.play;

// to reschedule (do this in the OSCdef)
(
// transfer the stream into a new Task wrapper
// and play it Right Now -- this will interrupt the prior timing
var new = Task.new.stream_(t.stream).play;

// break the connection between the old Task and its stream
// the old timing is forgotten at this point
t.stop;

// and remember the new Task wrapper for next time
t = new;
)

It’s a unique feature of Task / PauseStream; you can’t do this with Routine directly (because of the way Routine handles its playing/stopped status). You also can’t do it with a single, persistent Task – but you can do it by disposing of Task objects after breaking their normal timing.

hjh

thanks. I must be doing something wrong because I am getting an error. I defined this task

t = Task({
	20.do({|i|
		i.postln;
		3.wait
	})
})

t.play

But when I try reassigning it

x= Task.new.stream_(t.stream).play
i get an error:

ERROR: Thread function arg not a Function.
ERROR: Primitive 'Thread_Init’ failed.
Wrong type.
RECEIVER:
Instance of Routine { (0x5598e8879ec8, gc=F4, fmt=00, flg=00, set=05)
instance variables [27]
state : Integer 0
func : nil
stack : nil
method : nil
block : nil
frame : nil
ip : Integer 0
sp : Integer 0
numpop : Integer 0
receiver : nil
numArgsPushed : Integer 0
parent : nil
terminalValue : nil
primitiveError : Integer 0
primitiveIndex : Integer 0
randData : Integer 0
beats : Float 0.000000 00000000 00000000
seconds : Float 0.000000 00000000 00000000
clock : nil
nextBeat : nil
endBeat : nil
endValue : nil
environment : nil
exceptionHandler : nil
threadPlayer : nil
executingPath : nil
oldExecutingPath : nil
}
CALL STACK:
MethodError:reportError
arg this =
Nil:handleError
arg this = nil
arg error =
Thread:handleError
arg this =
arg error =
Object:throw
arg this =
Object:primitiveFailed
arg this =
Meta_Task:new
arg this =
arg func = nil
arg clock = nil
< closed FunctionDef > (no arguments or variables)
Interpreter:interpretPrintCmdLine
arg this =
var res = nil
var func =
var code = "x= Task.new.stream
(t.stream);"
var doc = nil
var ideClass =
Process:interpretPrintCmdLine
arg this =
^^ The preceding error dump is for ERROR: Primitive ‘_Thread_Init’ failed.
Wrong type.
RECEIVER: a Routine

That’s strange. It worked on my machine.

You can use PauseStream then.

x = PauseStream(t.stream, t.clock).play;

… and the rest should be the same.

It’s approaching midnight in China, so I won’t be available for further questions until 8 or so hours later… good night.

hjh

Ah, I see now – Task code changed about 3 years ago (i.e., your SC is not the most up-to-date). – Nov 2021: Classlib: Put 'protect' in PauseStreams (update state on error) · supercollider/supercollider@8e912f9 · GitHub

Looking at the code again, I’d change my recommendation slightly:

(
var new = Task.new(nil, t.clock).stream_(t.originalStream).play;
// or var new = PauseStream(t.originalStream, t.clock).play;
t.stop;
t = new;
)

hjh

1 Like