Next Chapter
Previous Chapter
Table of Contents

Working with Algorithms in Real-Time

This chapter introduces working with algorithms in "real-time". Real-time simply means that algorithms are synchronized to true clock time when they perform.

Scheduling Modes: Run-time vs. Real-time

Common Music normally processes objects in "run-time" mode, which means that objects receive their proper time stamp but time itself is running as fast as possible. For most applications, run-time mode is the best choice because it means that musical results are computed as quickly as possible. Real-time scheduling mode is useful when algorithms are to perform in parallel with real-world events occurring at the same time, for example when graphics and music are realized together or when algorithms perform together with a live performer.

With respect to designing algorithms, there is no conceptual difference between real-time and run-time modes. However, implementation details are likely to vary between modes due to two concerns of real-time composition: speed and memory usage. As an introduction to these topics, start out by opening MIDI with the scheduler set to real-time mode:

Stella [Top-Level]: open midi scheduling real-time
Warning: The current clock mode :SECONDS will result in floating point
calculations that may cause GC to affect real time processing.
Stream: #<Port: Midi to MODEM>
Stella [Top-Level]: 
The scheduling option enables any stream (not just the MIDI port) to process musical events in either run-time or real-time mode. A warning occurred because -- although the scheduling mode was set to real-time -- the system clock mode is currently seconds. This means that if event processing occurs, time will be calculated using floating point numbers. Floating point calculations involve temporary memory allocation; this in turn will probably cause Lisp to reclaim memory, or "garbage collect" (also called GC) during the real-time performance. Garbage collection will be audible as a dropout in the sound processing.

So the most important rule for real-time composition in Lisp is: avoid allocating temporary memory, or "ephemeral consing" as it is sometimes referred to. Since floating point numbers involve ephemeral consing, avoid using them in your algorithm code and don't force the system to use them either. The latter can be achieved by setting either the global system clock mode or a particular stream's clock mode to milliseconds. This next example fixes the warning by setting the clock mode of the MIDI port to milliseconds:

Stella [Top-Level]: open midi clock milliseconds
Stream: #<Port: Midi to MODEM>
Stella [Top-Level]: 
Although neither example demonstrates it, the scheduling and clock options can appear together in any order within a single open statement.

Clock Modes: Seconds vs. Milliseconds

Common Music supports two clock modes, seconds (the default mode) and milliseconds. Although milliseconds mode is slightly less intuitive (it means expressing time in quanta of 1/1000 of a second, for example, 250 means .25 seconds) it has two significant benefits for real-time work:

The global clock mode can be set using the (clock-mode) function and the clock mode of an individual stream can set using the clock option, as seen in the last example. For more information on clock modes, see About Time and clock-mode.

Real-Time Performance Tips

Here are a number of tips to keep in mind when implementing algorithms to run in real-time:

A First Example

The remainder of this chapter steps through a simple example to illustrate the basic concepts of reading and writing MIDI in real-time. The complete code listing can be found together with other real-time examples in rt.cm.

The example consists of two algorithms, named writer and reader. Writer plays random notes at random times for 20 seconds. The amplitude of each note depends on the value of the global velocity variable:

(defparameter velocity 64)

;;; Writer sends random notes at random times for 20 seconds. 
;;; The amplitude of each note is taken from Velocity. 

(algorithm writer midi-note (end 20000 duration 2000)
  (setf rhythm (between 50 500))
  (setf note (between 40 90))
  (setf amplitude velocity))
First listen to writer in run-time mode to hear what it does. Note that since the clock mode is milliseconds it is necessary to specify the start time offset to mix in the same format:
Stella [Top-Level]: open midi scheduling run-time clock milliseconds
Stream: #<Port: Midi to MODEM>
Stella [Top-Level]: mix writer 1000
Stella [Top-Level]:

The second algorithm is called reader. Reader's rhythm is set to .01 seconds; each time reader executes it reads all pending MIDI messages from the MIDI port and passes them to a function called message-handler:

;;; Reader reads all pending midi messages every 10 milliseconds.

(mute reader (rhythm 10 end 20000)
  (midi-read-messages #'message-handler))

;;; Send message out and update Velocity if a noteOn velocity is > 0.

(defun message-handler (msg now)
  (midi-write-message msg now)
  (message-case msg
    (KEY-DOWN (setf velocity (note-on-velocity msg)))))
Whenever reader calls midi-read-messages the pending MIDI input messages and their times are passed to message-handler in the order they are received. The first thing message-handler does is send whatever it receives out the MIDI port to the synthesizer, otherwise the input signal would not be heard. After sending the message, a message-case statement "filters out" all MIDI messages except a certain type of message called a "key-down". If a key-down occurs (a key-down is a MIDI note on message with a velocity greater than 0) then the global velocity variable is set to the velocity of that particular note on. This means that writer adjusts its amplitudes to the volume that the performer is currently playing at. For more information on reading messages see message-case and midi-read-messages.

Finally, reset the scheduling mode to real-time and mix reader and writer together. Offset the mix by 1 second so you have time to reach your MIDI keyboard.

Stella [Top-Level]: open midi scheduling real-time
Stream: #<Port: Midi to MODEM>
Stella [Top-Level]: mix reader,writer 1000
Stella [Top-Level]:

Next Chapter
Previous Chapter
Table of Contents