[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 then rts 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:

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:
  1. 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.
  2. 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! otherwise rts 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.
  3. 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:


$Name: $
($Revision: 1.9 $, $Date: 2005/08/13 00:19:29 $)