Tutorial

This browser does not have a Java Plug-in.
Get the latest Java Plug-in here.

Dynamic Updating - Callback variation for varied rhythmic timing Reload page to restart example
This tutorial demonstrates how the callback timing can be varied at runtime. In this example the score callback is varied at random between 0.5 and 1.0 beats in length. On each callback a note is played and piano-roll score display updated. Pitch is selected using the pitch class (pc) utilites and constants from SoundCipher. The score reflects the pitch, dynamic, and duration attributes of the note.

If the pitch goes very low on some sounds they may be hard to hear. The pcGaussianWalk() method does constrain pitches between 0 - 127 and so eventually the pitch will return into the soundable range for the instrument. Also, the default Processing sketch area size is 100 pixels square so the extreme pitches may be drawn outside the window.



The first three lines import SoundCipher then create an instance of the SoundCipher and SCScore classes. The sc instance will be used to play notes and access constants. The score instance will be used to control the timing, the score is repeated indefinitely while its length is dynamically varied.

Following this, a number of class variables are declared to control various parameters of the music and timing. Varying these settings and re-running is a good way of experimenting with what they do in the program.

In the setup() method, Processing's draw loop is halted using noLoop(). The drawing area background is cleared and the instrument (MIDI program change) for the sc instance is set using one of the SoundCipher constants - notice that SoundCipher constants are identifiable because they appear in UPPER CASE. The score's tempo is set and callbacks are registered, which requires that a handleCallbacks() method is written - see description below. The score is started with the -1 argument indicating that it should loop indenfinitely. and the draw() method is called once using redraw().

The draw() method checks first to see if the score drawing area needs to be refreshed. Then, using stroke(), it sets the drawing colour to be a shade of grey matching the note's dynamic value. A line is drawn reflecting the note attributes of pitch (height), dynamic (color), and duration (length). A note is played by SoundCipher based on the same variables.

The handleCallbacks() method is called each time a callback event occours during the score playback. In this case there is just one callback in the score - in fact the callback is the only object in the score, thus it is the last object in the score and so is used to indicate the end of the score. This method calls the draw() method - adding a line to the score and playing a note - then calulates the new pitch, dynamic and duration values for the next note. Finally, it resets the score values for its next loop, adding a callback event to correspond with the newly calulated duration.

Pitch calculation uses random and gaussian walks from the SoundCipher Unitilites class so that some reasonable melodic contour is established. The gaussian walk allows for larger leaps from time to time while the random walk produces a more consistent meandering. The pitch selection is taken from different pitch class sets (a scale for most notes and a triad for notes on the downbeat). The downbeat is calulated using modulo - beatCount%4.

Duration values ensure there is always a 1 beat note on the downbeat (to provide it with some emphasis) and otherwise a probabilistic choice between 1 and 0.5 beat values.

The dynamic value of the notes is controlled by a cosine function that uses the beatCount variable to ensure that the cosine frequency matches the musical beats. This results in a pulsing dynamic over a two beat cycle. This is a pretty useful technique for adding expression to the music.

This program may seem somewhat convoluted, but is the basis for an important callback programming pattern that is very useful for realtime generative media art where music and image need to be synchronised, but time needs to be in musically sensible units, such as beats and tempo.


See the DrawNotes tutorial for a simpler version of this method that uses Processing's draw() loop to detemine tempo.