Common Music Reference Manual
 

Top Objects Processes IO Scales Data Patterns Plotter Utilities Index

2  Musical Processes

A process is a LISP function that runs in an environment called a scheduler. The scheduler is like a conductor, its job is to insure that musical events occur in their proper order and at their correct time in a score. The scheduler acomplishes this task by maintaining the objects under its control in a time ordered scheduling queue. The scheduler runs each object at its appropriate time by removing it from the head of the queue, processing it in some score specific manner, and then possibly reinserting the object back into the queue to be run again at a later time.

The scheduler runs a process object by evaluating it as a LISP function. While there is no restriction on what a process function can do, typical actions include creating and outputting musical events, sprouting musical structure and determining the next run time of the process. The scheduler will continue to evaluate a process until the process signals it should be stopped. The stop signal can be done explicitly -- by calling the stop function inside the process definition -- or implictly -- by defining the process using the process macro. Regardless of which method is used there must be some condition that causes a process to stop running or it will continue to execute as long as LISP is running.

2.1 Defining Musical Processes

The defprocess macro is the easiest way to create a process. Its syntax is the same as defun. Statements in the body of the macro define the lexical environment for the definition of a process runtime function. Defprocess should return a process function as its value.

defprocess name parameters {form}* [Macro]

Defines a process object and a lisp function that creates a process runtime function. name is the name of the process. parameters is a lambda parameter list or nil. Following the parameters comes one or more form that define the body of the process. The last form in the body should be a process definition. The process definition defines that portion of the code that is evaluated by the scheduler during event processing:

Example 2-1

;; a process definition with no lambda parameters

(defprocess simp ()
  (process repeat 100                   ; iterate 100 times
           for x = (now)                ; get current time
           for k = (between 32 90)      ; pick a random keynum
           output (new midi time x      ; output a midi note
                       keynum k amplitude .3)
           wait (pick .2 .6 .8)))      ; pick next run time
#<process: Simp>    

;; the process can be invoked using its object or function call:

(events #!simp "/tmp/foo.midi")
(events (simp) "/tmp/foo.midi")

;; a recursive process with lambda parameters:

(defprocess sierpinski (keynum ints dur amp depth)
  (process with len = (length ints)
           for i in ints
           for k = (transpose keynum i)
           output (new midi time (now) duration dur 
                       amplitude amp keynum k)
           when (> depth 1)
           sprout (sierpinski (transpose k 12) ints
                              (/ dur len) amp (1- depth))
           wait dur))
#<process: "sierpinski">

;; the process is invoked by passing it input values.

(events (sierpinski 'a0 '(0 7 5 ) 3 .5 4)
        "/tmp/sier.midi")

process {clause}+ [Macro]

The process macro supports iterative process description using the same syntax as the loop macro. process iteration is defined as a series of clauses, each clause begins with a clause operator. There are four broad categories of clause operators:

Iteration and action clauses are executed in the same order in which they appear in the code. If more than one iteration clause is specified the process terminates as soon as the earliest clause completes its iteration.

Example 2-2

(defprocess drunkard (num)
  (process with tempo = '(0 1 .5 .5 1 1)
           and amp = (between .3 .5) 
           and pat = (new random q (e weight 2)
                          (new cycle s for 2)) 
           for i below num
           for k = 60 then (drunk k 6 :low 30 :high 90 :mode ':reset)
           for d = (* (rhythm (next pat) 120)
                      (interpl (/ i num) tempo))
           output (new midi time (now) keynum k
                        duration d amplitude amp)
           when (odds .4)
           output (new midi time (now)
                       keynum (odds .5 (+ k 11) (- k 13))
                       duration (* d 1.5)
                       amplitude amp)      
           wait d
           finally 
           (print 'all-done!)))

(events (drunkard 100) "midi.port")

Initialization and Finalization Clauses

with var [= {form}] {and var2 [= {form2}]}*

Declares local process variables and their optional initial values. The forms in a with clause are processed one time only immediately before process iteration starts. Each variable declaration in the with clause can be specified as either:

  • the name of the variable.
  • the name of the variable and an initial value separated by =.
If an initial value is not specified a variable is initialized to nil. Use and to separate variables if more than one variable is declared. Declarations are processed in left to right order so a = value may reference variables declared to the left.
Example clause syntax:
with a and b = 32 and c = (* (random b) 4)
initially {form}

Evaluates form when the process is created and before the process starts running.
Example clause syntax:

initially (print 'initializing!)
finally {form}

Evaluates form immediately after the process stops.
Example clause syntax:

do ...
finally (print 'all-done!)

Iteration Clauses

repeat {form}

Causes the process to run form number of times and then stop.
Example clause syntax:

repeat 23
for var = {form} [then {form2}]

Sets var to the value of form on each iteration. If then is supplied var is set to form on the first iteration and to form2 on subsequent iterations.
Example clause syntax:

for k = (now)
for p = 60 then (+ p (random 10))
for var [from {form}] [to | below {form}] [by {form}]

Iterates var over a range of numbers. If the sequence is bounded the process stops once the last number has been reached.

  • from sets the starting value ofvar, defaults to 0.
  • to establishes an inclusive ceiling on the iteration and stops the process once the value has been reached.
  • below sets an exclusive ceiling on the iteration and stops the process just before the value has been reached.
  • by provides an optional increment for var, defaults to 1.

Example clause syntax:
for k below 10
for p from 10 to 100 by 5 
for var {in | on} list [by function]

Sets var to successive elements or tails of list and stops process when iteration is done.

  • in maps var over successive elements in list.
  • on maps var over successive tails of list.
  • by specifies an optional stepping function. Defaults to cdr.

Example clause syntax:
for k in '(c4 d4 ef5 fs)

Action Clauses

do {form}+

Introduces one or more Lisp forms as part of the process definition.

output {object} [to {io}]

Outputs object to an open output stream. If to is is not specified the default open output stream is used. The Lisp function output is available for use inside a do clause definition.

wait {seconds}

Causes the process to wait seconds amount of time before running again. The Lisp function wait is available for use inside a do clause definition.

sprout {object} [at {time}]

Inserts object into the currently running scheduling queue at an optional time. If the at time is not specified the object is inserted at the current time. The Lisp function sprout is available for use inside a do clause definition.

Conditional Clauses

while {test} {action}

Triggers action clause as long as test is true otherwise stops process.
Example clause syntax:

while (< (now) end-time)
do ...
until {test} {action}

Triggers action clause as long as test is false otherwise stops process.
Example clause syntax:

until (= freq 440)
do ...
when {test} {action}

Triggers action clause whenever test is true.

unless {test} {action}

Triggers action clause whenever test is false.

if {test} [{action} {and {action}}*] [else {action} {and {action}}*]

Triggers one or more action clauses if test is true otherwise triggers one or more optional else action clauses.

2.2 Process Utilities

There are a number of utility functions and macros that may be used inside the body of a process definition.

now &optional absolute [Function]

Returns the current process time. If absolute is nil (the default) then the value returned represents the elapsed time since the processes began. If absolute is t then the true process score time is returned.

wait delta [Function]

Sets process to run again delta seconds later.

wait-until time [Function]

Sets process to run again at (local) time.

stop [Function]

Immediately terminates process.

output object &optional ahead [Function]

Outputs object to the open output destination (file, port, display, etc).

input source [Function]

Inputs events from the open source stream (file, port, etc.) The exact behavior of input depends the class of source.
input midi-port
Returns a list of all available messages and their times from the open midi-port. Each message and time is encoded as a 64 bit integer; the upper 32 bits contain the millisecond time and the lower 32 bits contain the midi message.

sprout object time [Function]

Schedules object to start running at time. The file processes.cm contains many examples of defprocess and process.

doeach (var data) {forms}* [Macro]

If data is a list doeach iterates over the list and evaluates forms with var to successive elements from the list. If data is not a list then forms are evaluated with var bound to the data itself. doeach is useful for processing both lists and non-lists alike, for example, to output pattern data that contains both chords and single notes.


Common Music Homepage  |  © 2002 Heinrich Taube Last Modified: 20 May 2002