- [Function]
(
rts
[objects] [to] [start] [end])
Starts the "real time" scheduler according to optional arguments. Returns no values.
rts
supports the following optional arguments:
- objects {objects | false}
- An optional object or list of objects to place in the scheduling queue. If the value is false then the scheduler starts running with no pending objects. While the scheduler is running objects can always be added "interactively" by calling sprout from the REPL or from an active receiver hook.
- to {stream | false}
- An optional destination output stream, defaults to current-output-stream. If the value is a stream then it must already be open and initialized. If the value is false then no output stream is assumed and the caller is expected to manage stream IO themselves.
- ahead number
- An optional start time offset, defaults to 0.
- end {number | boolean}
-
If end is a number then
rts
stops running when that time has been reached in the score or when no objects remain pending in the scheduler, whichever comes first. If end is boolean true thenrts
stops running when no more objects are pending. If end is false then rts will run (even if the queue is empty) until rts-stop is called to terminate scheduling. The default value is boolean true if objects are specified else boolean false.
rts
and events are "source
compatible" scheduling environments, meaning
process definitions do not have to be
modified to run in either environment, and scheduling functions such
as now, output
and sprout behave identically in both
schedulers. The most important differences between the two schedulers
are:
- the REPL is not blocked by
rts
while the scheduler is running. rts
can be started without objects and, if end is false,rts
will continue to run until it is explicitly stopped, even if its queue is empty.- sprout can be called from the REPL
or a receive hook to add objects
to
rts
"interactively", as long asrts
is running. - The values returned by now reflect the current real time in seconds.
Although process definitions do not have to be changed to run under
rts
, real time scheduling does place timing demands on
software that non-real time scheduling does not. Process definitions
should avoid consing and creating lots of temporary structure or being
otherwise wasteful and inefficient. Consider reusing events rather
than creating new one for each output statement. If you are working
with MIDI, consider using low-level,
consless MIDI messages in
place of midi objects.
Implementation:
CM uses either native threads or a 1 millisecond periodic task to
implement rts
. The availability of either method depends
on the underlying Lisp implementation:
Table 1. Implementation
of rts
scheduling in the supported Lisp implementations
(most current release). NT=native threads, PP=periodic polling.
Linux | OS X | Windows | |
---|---|---|---|
CMUCL | PP | ||
OpenMCL | NT, PP | ||
SBCL | NT, PP |
Notes:
- Both threaded and periodic versions of
rts
are supported in OpenMCL and SBCL. The default implementation is threaded. To try out the periodic version set*rts-type*
to:periodic
. - Periodic polling has several consequences:
- Using the GUI at the same time as periodic rts or periodic receiving will currently not work.
- Periodic tasks are pushed onto a task list; if
rts
is periodic be sure to start it running before calling set-receiver! otherwiserts
will not be able to process what the receiver places into its queue until the next tick. - All receiving and process tasks must happen within the 1 millisecond tick or things will drift.
-
How close
rts
actually gets to accurate real time rendering depends on a number of factors, including how accurate the Lisp implementation's thread sleeping/periodic tasks are, how short the time deltas between musical events are, whether process code is compiled or interpreted, how much garbage is generated, and so on.
Examples:
Example 1. Starting rts
and sprouting from the REPL.
(define *pm* (portmidi-open :output 3 :latency 0)) (define (zzz len lb ub wai amp) (process repeat len output (new midi :time (now) :duration .1 :amplitude amp :keynum (between lb ub)) wait wai)) ;;; fire it up for 25 seconds (rts (list (zzz 100 60 90 .25 .5) (zzz 50 20 50 .5 .5)) *pm*) ;;; add stuff while zzz plays (let ((k (between 20 100)) (n (pick 3 5 7 11))) (sprout (zzz (* n (pick 2 3 4)) k (+ k 7) (/ 1 n) .75)))
Example 2. Endless Fluff. Stops only when rts-stop is called. (Todd Ingalls)
(define fluff '(60 62 64 67 72 65 69 48 50)) (define (endless-fluff num dur knums) (process repeat num for i from 0 output (new midi :time (now) :duration (* 2 dur) :amplitude .5 :keynum (pickl fluff)) wait (pick dur (/ dur 2) (/ dur 4)) when (= i (1- num)) sprout (process repeat 4 output (new midi :time (now) :duration 5 :amplitude .5 :keynum (pickl knums))) and sprout (endless-fluff 20 1 knums))) (rts (endless-fluff 20 1 fluff) *pm* 0 #f) ;;; stop when you are done. (rts-stop)
Example 3. Sprouting into the scheduler from a receive hook.
(define *pm* (portmidi-open :input 1 :output 3 :latency 0)) (define (major-thirds len knum) (process with d = .1 for i below len for k = (+ knum (* (mod i 5) 4)) output (make-note-on 0 k 90) at (now) sprout (make-note-off 0 k 125) at (+ (now) .5) wait d)) (rts #f *pm*) (set-receiver! (lambda (mm ms) ms (if (note-on-p mm) (sprout (major-thirds 20 (note-on-key mm))))) *pm*) ;;; stop receiving and scheduling. (remove-receiver! *pm*) (rts?) ⇒ #t (rts-stop) (rts?) ⇒ #f
See also:
rts?
[Function]rts-stop
[Function]set-receiver!
[Function]sprout
[Function]