Introducing CsoundAC: Algorithmic Composition With Csound And Python

What happens when the world's most powerful waveform compiler meets one of the world's most popular programming languages ? Find out how one programmer makes it all work out in this introduction to CsoundAC.

Michael Gogins is a professional programmer and a major contributor to the development of Csound5, the modern face of the mighty and venerable Csound. Michael is deeply dedicated to the art of making music with computers. More specifically, his work is focused on the use of computers to create music that can not be produced by any other means. Over the past two decades this interest has led him to develop a variety of tools to assist him with his own compositions. His most recent toolkit is CsoundAC, a Python-based set of functions and routines specifically written for the purposes of algorithmic music composition with Csound.

CsoundAC requires the Python programming language and it assumes some experience with programming languages in general. It also assumes that you know how to program in Csound. However, both Python and Csound are relatively easy languages to learn, and the author advises the new reader to work his or her way through the tutorials at www.python.org before getting into CsoundAC.

After a brief introduction to basics Michael presents a fascinating series of exercises for CsoundAC in his Csound Algorithmic Composition Tutorial (PDF). The series is very cool, with Python/Csound code for the realization of pieces by Mozart, John Cage, LaMonte Young, Lejaren Hiller, Bill Schottstaedt, Terry Riley, and Charles Dodge. Again, I must advise the reader that the exercises are substantial chunks of code that will require previous knowledge of the languages involved for a full understanding of the processes. The efforts are worth the labor, and I congratulate the author for his decision to include works by well-known composers as tutorial material. Simply realizing the pieces to audio is instructive and fun, but the pieces are also sophisticated programming exercises through which the user can attain mastery over CsoundAC and its capabilities.

And what are those capabilities ? The laundry list of CsoundAC's salient features includes routines and functions for exploring the sonic possibilities of chaotic systems, image-to-score conversion, Lindenmayer systems, strange attractors, and other chancey and probabilistic realms. MIDI output is supported, and the author has thoughtfully included a large set of instruments and effects for audio realization of scores via Csound. Beyond its Python requirement CsoundAC is a self-contained system designed in which you can code, compile, and execute your programs, i.e. realize them as audio files.


Installation And Configuration

You must have a version of Csound5 that supports the Python interface. Thanks to the efforts of Csound users such Felipe Satelier some Linux distributions include an up-to-date Csound package which will include the csound binary and its various extension modules. If you want to build Csound yourself you might want to use some version of the following compile-time options :

useDouble=1 useOSC=1 buildPythonOpcodes=1 buildInterfaces=1 buildPythonWrapper=1 \
buildJavaWrapper=1 buildLoris=1 buildCsoundAC=1 pythonVersion=2.6 \
dynamicCsoundLibrary=1

You can leave out everything except the Python-specific options, but if you'd like to use Csound with a wider variety of programs you'll want the other options too. Also, you will need to install the binary and development packages for the FLTK graphics toolkit, the boost extension libraries for C++, and the SWIG interface generation software. Like Python and Csound, all these packages are free and open-source.

By the way, if you decide to build the system yourself you'll probably need to manually install the CsoundAC modules. On my current boxes the install.py script in the Csound source tree does not install everything so I had to make sure that /usr/lib/python2.6/site-packages included the following components :

  CsoundAC.py
  _CsoundAC.so
  _csnd.so
  csnd.py

You can perform a simple test to see if CsoundAC has been installed correctly. After starting the Python shell enter the following command :

  import CsoundAC

If you receive an error you should check your system's PYTHONPATH variable :

  echo $PYTHONPATH

If your Python version's site-packages directory isn't listed in the results you can add it with this command :

  export PYTHONPATH=$PYTHONPATH:/usr/lib/python2.6/site-packages

Correct for your Python version and path, then try importing the CsoundAC module into Python again. If the error persists you may need to re-install Python.

Figure 1. The SciTE editor hosts Python + CsoundAC

One thing more: The Linux version of CsoundAC is essentially a text-based environment. You can choose to work from the Python interpreter's prompt, but CsoundAC's author suggests using the SciTE programmer's editor (Figure 1) or a similar program that supports amenities such as random-access editing, syntax highlighting, and code execution.


Inside CsoundAC

At the heart of CsoundAC we find the concept of the music graph. A music graph represents a Csound score as "... a hierarchical tree of nodes, which can contain notes, score generators, score transforms, and other nodes", according to its definition in the Csound Manual. In fact, the relevant passage in that manual is such a good definition of CsoundAC that I'll quote it a bit further :

The coordinate system in CsoundAC is based on a Euclidean music space with dimensions {time, duration, event type, instrument number, pitch as MIDI key, loudness as MIDI velocity, phase, spatial X coordinate, spatial Y coordinate, spatial Z coordinate, pitch-class set, 1}. A point in music space can be a note, an inflection of a note, or even a grain of sound.

A music graph is a directed acyclical graph, or tree, of nodes in music space. These nodes are associated with local transformations of [the] coordinate system. There are nodes for containing scores or fragments of scores, for generating scores, and for transforming scores. In addition, any node may contain child nodes that inherit the parent's coordinate system.

Thus, it is possible to compose a musical score by containing or generating notes in lower level nodes, assembling them into a score using higher level nodes, and finally rendering the score with Csound. The process is strictly analogous to the construction of a 3-dimensional scene in computer graphics by generating primitive objects such as spheres, cones, and cubes and moving them around in space to assemble a scene...

Finally, it is possible to derive a new Node class in Python from any existing Node, in order to create new score generators and transforms as part of the composing process.

If any part of that description confuses you, fear not, it probably confuses me too. But in the end what matters will be the music that comes from all this definition and description, and CsoundAC is above all a music-maker's toolkit. You don't necessarily need exact knowledge of how a "chaotic dynamical system" works or even what it is. You do need to know how to run that code in the Python/Csound environment, and the code itself is a good place to start learning about such systems.


A Simple Exercise

Let's look at a simple exercise in CsoundAC. The following code is a slightly edited version of the first example in Michael's tutorial :

    import CsoundAC	; Bring CsoundAC functions and processes into Python.

    orchestra = '''	; Set the orchestra code block.
    sr = 44100		; These lines set the instrument's
    ksmps = 100		; sample rate, control rate, and
    nchnls = 2		; number of output channels.

                instr 1		; Define a Csound instrument, numbered 1.
		; Begin envelope design.
                ; Sharp attack stage, but not sharp enough to click.
    iattack     =           0.005
                ; Moderate decay stage.
    idecay      =           0.2
                ; Fast but gentle release stage.
    irelease    =           0.05
                ; Extend the total duration (p3 in the score) to include 
		; the attack, decay, and release.
    isustain    =           p3
    p3          =           iattack + idecay + isustain + irelease
                ; Exponential envelope.
    kenvelope    transeg       0.0, iattack, -3.0, 1.0, idecay, -3.0, 0.25, \
                               isustain, -3.0, 0.25, irelease, -3.0, 0.0
                ; Translate MIDI key number (p4) to frequency in cycles per second.
    ifrequency  =           cpsmidinn(p4)
                ; Translate MIDI velocity value (p5) to amplitude.
    iamplitude  =           ampdb(p5)
                ; Band-limited oscillator with integrated sawtooth wave.
    aout        vco2        iamplitude * kenvelope, ifrequency, 8
                ; Output stereo signal
                outs        aout, aout
                endin	; End the instrument definition.
    '''			; End the orchestra block.

    score = '''		; Begin score block.
    i 1 0 10 68 80	; A Csound score event with five parameters (p-fields).
    '''			; End score block.

    command = 'csound -RWfo toot1.wav toot1.orc toot1.sco' ; Sets command to
                                                           ; run the csound
                                                           ; binary with the
                                                           ; indicated options.
    model = CsoundAC.MusicModel()	; Sets the model to CsoundAC's MusicModel.
    model.setCsoundOrchestra(orchestra) ; Process the orchestra code.
    model.setCsoundScoreHeader(score)   ; Process the score code.
    model.setCsoundCommand(command)      ; Process the string defined in the
                                        ; command statement.

    model.render()			; Render the embedded Csound code.

The Csound parts are well-documented in the original example code. I've added some explanatory material for the Python-less among us.

The exercise embeds Csound code into a Python frame that will compile that code into a WAV file named toot1.wav. Experienced Csounders will recognize substantial portions of that example. Everything from the sr (sample rate) definition to the endin (end instrument definition) marker is unaltered Csound code. The single score line (i 1 0 10 68 80 is likewise pure Csound, while everything else in the example is Python code. The triple quotes (''') essentially convert the Csound code blocks to the variables defined as orchestra and score. Those variables are then supplied to the model code to produce a WAV-formatted file of a sawtooth waveform played at a pitch of 440 cycles per second (a.k.a. an A440) for 10 seconds (defined in the score line). By the way, if you've never programmed in Python you'll need to know that the language is sensitive to indentation and code block arrangement. The spaces are meaningful, ignore them at your peril.

Experienced Csounders might notice that it is possible to re-write the command definition to produce realtime output from the example. I'll leave that exercise to the industrious reader and proceed to the use of CsoundAC to create a MIDI file from some more ambitious code.


Advanced CsoundAC

The next example builds on the first exercise by adding a provision for writing an external score file. It also replaces the original score statement with a formula to create values produced by a chaotic phenomenon called a strange attractor. The bulk of this example has been absorbed from the previous exercise and is therefore left uncommented. Only the added parts are described in the following code :

    import CsoundAC
    import string	; Add facility for writing a text file.

    orchestra = '''
    sr = 44100	
    ksmps = 100	
    nchnls = 2	

                instr 1	
    iattack     =           0.005
    idecay      =           0.2
    irelease    =           0.05
    isustain    =           p3
    p3          =           iattack + idecay + isustain + irelease
    kenvelope   transeg     0.0, iattack, -3.0, 1.0, idecay, -3.0, 0.25, \
                            isustain, -3.0, 0.25, irelease, -3.0, 0.0
    ifrequency  =           cpsmidinn(p4)
    iamplitude  =           ampdb(p5)
    aout        vco2        iamplitude * kenvelope, ifrequency, 8
                outs        aout, aout
                endin	
    '''

    r = 3.974		; This block introduces the mathematics
    y = 0.5		; of the attractor. The values produced
    time_ = 0.0		; here are used to create each line
    duration = 0.25	; of the Csound score.
    istatements = []
    for i in xrange(1000):
        y = r * y * (1.0 - y)
        time_ = time_ + duration / 2.0
        midikey = int(36.0 + (y * 60.0))
        istatement = "i 1 %f %f %d 80\n" % (time_, duration, midikey) ; The Csound score event.                                                                                
        print istatement,
        istatements.append(istatement)

    score = string.join(istatements)	; Produce Csound score from events created 
                                        ; by the for loop above.

    command = 'csound -RWfo toot2.wav toot2.orc toot2.sco' 

    model = CsoundAC.MusicModel()
    model.setCsoundOrchestra(orchestra)
    model.setCsoundScoreHeader(score)  
    model.setCsoundCommand(command)    

    model.render()		

In this example the hard-coded score event in Exercise 1 has been replaced by a routine to generate a series of such events. The simple formula for the attractor provides values used in the generation of each line of the resultant Csound score. As in the previous example the score is then processed by the orchestra, and the command directive realizes the output as a WAV-formatted soundfile. You can listen to a sampling from the results at CsoundAC Tutorial #2 audio example.

To vary the output of this exercise you should re-run it with different values for the attractor's equation (r and y). For greater rhythmic variation you can edit the value for the duration definition or even replace it with a function to generate a different value for each event (the istatement in the for loop). And of course, be sure to study the relevant section of the Tutorial.


Documentation

It should be obvious by now that the way into CsoundAC is through Michael's Tutorial. That text is both a specific introduction to CsoundAC and a general introduction to the history and methods of algorithmic music composition. If you're clueless about Csound, the author has also written a general introduction to Csound that I recommend to new users.

The Tutorial is a fine introduction to the ways and means of CsoundAC, but I wish a greater number of simple examples had been supplied. Fortunately, it's easy to create such exercises yourself by editing the existing examples. As the student progresses into the system the Tutorial's greater value begins to show itself, particularly in its selection of representative pieces. Some of those pieces are quite famous - Mozart's Musikalisches Wuerfelspiel (musical dice game) and John Cage's Atlas Eclipticalis are well-known instances of music made by chance procedures - while others are based on notable pieces by other composers working with various algorithmic strategies.

Incidentally, I must apologize to Michael Gogins for appropriating so much material from his tutorial. I'll legitimize my lame excuse by pointing out that he is a fine writer - see his blog for the proof - and I simply felt that I could not improve upon his expression.


Happy R-Day, Dr. Vercoe !

Dr. Barry Vercoe, Csound's "founder of the feast", recently announced his retirement from the MIT Media Lab. It's hard to imagine my own musical life without Csound, and so on behalf of Csounders everywhere I send a gigantic "Thank you !" to Dr. Vercoe and his many students & colleagues for their invaluable work on what is surely the world's most advanced programming environment for sound and music production. Enjoy your retirement, Dr. V, and thanks again for Csound.


Outro

In my next article I'll continue this series with an introduction to Christopher Ariza's remarkable athenaCL. See you then !

Load Disqus comments