Using the 220b Programming Environment

The Big Picture

Music 220b uses a Common Lisp based environment for algorithmic composition and sound rendering. Common Music ("CM") extends the Common Lisp computer language to provide functions for algorithmic music composition. In comes an algorithm, out comes a score (in many different possible formats). Common Lisp Music ("CLM") extends Common Lisp so that it can do sound rendering. That means that you put in your program and a sound file comes out:

This is very different from real-time synthesis (for example, MIDI synthesizers, Pd, etc.), where the control comes continuously and the synthesis happens just in time for the sound to be played out the loudspeaker. The main advantage of the sound rendering model is that you can use as much computer processing time as you need to synthesize your piece. If you want sixteen thousand notes in twenty seconds, you just have to wait while the computer synthesizes them.

Layers of Alphabet Soup

If you are familiar with Pd, you know that it is a single program. Pd is many things:

An environment for sound synthesis, with built-in oscillators, filters, etc.

A programming language (sort of), like C or Java

A user interface for editing Pd programs

In 220b, every one of these functions (and a few more) is its own separate program or software package. So, unfortunately, you can't just "run CLM" in the same way that you just "run Pd". (Likewise, you can't "learn CLM" in the same sense that you "learn Pd".)

Here's the big picture of what you will use in 220b:

CLM, CM, and CMN

We use three music-specific software environments (all of which were developed at CCRMA):

Common Music ("CM"), a "music composition environment" providing scales and tuning systems, rhythm in the form of tempo, eighth notes, etc., and a cool way to do algorithmic composition with "streams". (See the CM "Dictionary" for the complete contents.)

Common Music Notation ("CMN") can create and display traditional western music scores consisting of clefs, staves, bars, notes, etc. It uses the basic rendering model of CLM and CM, except that the output is generally a Postscript file (suitable for printing) rather than a sound file.

These three environments work well together. For example, you can write a note-generating algorithm in CM and have the resulting notes be synthesized in CLM as well as typeset into a score in CMN. In my opinion they work together too well---it can be hard to remember which function belongs to which package when you're looking for documentation.

Common Lisp

All three of these environments are implemented on top of Common Lisp. That is to say, none of CLM, CM, or CMN provides basic things like arithmetic, control flow, data structures, procedures, etc. All of them are extensions to Common Lisp and to use them you must write Common Lisp programs that call the functions they provide. For Winter 2006 we've switched to the totally free SBCL (which stands for "Steel Bank Common Lisp".)

The comprehensive reference for Common Lisp is the book "Common Lisp the Language, 2nd edition" by Guy Steele; the full text is available online. The index is a good place to start if you want documentation on a specific function, macro, constant, etc.

Emacs

Common Lisp plus CLM, CM, and CMN is everything you need to turn programs into sound files and printed scores. If you never made mistakes you could type your program into the Common Lisp interpreter and it would always run perfectly, producing the sound file(s) and/or score(s) you intended.

In the real world, we need a development environment that helps us build and test our programs bit by bit. In addition, it's much easier to program in Lisp (with all the (nested) parentheses) if your text editor is aware of programs' syntax and helps you match up parentheses, indent properly, etc. There are also lots of advantages to running the Lisp interpreter from within the text editor itself, because it lets you do things like execute bits of your program directly from the text editor, scroll back through previous things you typed into Lisp, and even have the editor take you automatically to the part of your code that just caused an error.

For this course you will use Emacs, the Rolls-Royce of text editors. Emacs was first developed in the 1970s and has been used actively ever since; they're up to version 21.3! Emacs can do just about anything; it has its own built-in Lisp interpreter that you can use to customize its behavior.

Here are some Emacs resources:

The extremely easy, helpful, and informative Emacs tutorial, which you run from within Emacs:

SLIME

The last software package you need to know about is SLIME, the "Superior Lisp Interaction Mode for Emacs". SLIME is the system that runs Lisp from within Emacs and does all kinds of wonderful fancy things to integrate your Lisp interpreter with your text editor. The only SLIME documentation we know about is the SLIME user manual, which you can get as a PDF file from the web. There's also a copy on the CCRMA filesystem at /usr/share/doc/slime-1.2.1/slime.pdf which you can look at with the Linux "acroread" program. I recommend that you start learning SLIME by following the tutorial below; once you've gotten through that and are comfortable with Emacs then the SLIME user manual will make a lot more sense.

Tutorial

0) Get to Unix

Get a CCRMA account, log into a CCRMA Linux machine, and open a terminal window.

1) Learn Emacs

Learn at least enough to move the cursor around, open and save files, follow the instructions below (which are in terms of the "Control" and "Meta" keys), and get out of trouble if you get confused.

I recommend the built-in Emacs tutorial or some of the other resources listed above.

2) Install SLIME

You need to add some mumbo-jumbo to a file called ~/.emacs whose purpose is to customize Emacs

Winter 2006: if you have previously customized emacs you will have to merge this into your existing .emacs configuration file, otherwise just copy this example .emacs file over the existing one.

Minus 10 points if you retyped those 133 characters into Emacs! I would have done it by selecting that text in the Mozilla window (with left-click and drag), and then pasting it into the Emacs window (with middle-click).

2) Make a directory for the course and put some goodies in it

Make yourself a directory called 220b, go into it, and make a subdirectory for this tutorial:

cdmkdir 220b
cd 220b
mkdir tutorial

Make a symbolic link giving you easy access to the CLM instruments we'll use in the course, then copy the fm-violin instrument from there into your tutorial directory, then go into that directory:

ln -s /usr/share/common-lisp/source/clm .
cp clm/v.ins tutorial
cd !$

3) Basic Interaction with SLIME

Start Emacs, then type M-X slime to start SLIME. You'll see that the Emacs window is now labeled "slime-repl"; that stands for "Read-Eval-Print Loop", meaning that in this window SLIME continuously reads what you type, evaluates it, and prints the result. That's the basic mode of interaction with any Lisp interpreter.

Type a number like 234 and hit return. As soon as you hit return, SLIME changes what you typed to bold face to show that it was passed to Common Lisp. Below that, SLIME prints 234 again, this time not in bold. That's the "eval-print" part; it means that (according to Common Lisp) the value of 234 is 234. Deep, huh?

Now we'll intentionally cause an error so you can get used to dealing with them. Type "hello" and hit enter. The screen will split in two, with the "slime-repl" window you just typed into at the top, and a new "sldb" window at the bottom. First of all, notice that in the upper window, "hello" is now in bold, showing that it was sent to Lisp, but there's nothing below it, meaning that "hello" has no value, i.e., Lisp was unable to evaluate it.

Now let's look at the "sldb" window. I believe this stands for "SLime DeBugger." Unfortunately, like most "debuggers," this one doesn't actually remove your bugs; it just lets you poke around and try to figure out for yourself what the problem was.

The most important thing is the error message itself, which came from Lisp:

the variable HELLO is unbound.
[Condition of type UNBOUND-VARIABLE]

That word "unbound" appears two times, so it must be important. Indeed, it means that Lisp was trying to interpret something (in this case, "hello"), as a variable but that variable doesn't exist, i.e., there's no value "bound" to it.

The next text in the SLDB window is a list of options for "restarts". They're called "restarts" because they're your options for dealing with the error and getting back to work. Sometimes when you make an error you might be able to correct it directly from the SLDB window and then carry on with the rest of what your program was doing. So you're going to see more "Restart" options for other errors. In this case, there are only two:

To bail out completely, select "Return to Top-Level". This is always a good idea if you want to just start over and try something else; it will take you out of the debugger and back to the normal mode of interaction. Move the cursor to that line and hit enter. Now the SLDB window goes away and you're back to just the slime-REPL window filling the whole screen. Now, below the bold "hello", you'll see this:

; Evaluation aborted

That's SLIME's way of reminding you that you aborted the evaluation of the expression "hello". You'll see another prompt, so now you're back to the read-eval-print loop. (The semicolon is a comment character.)

To finish our tour of the SLDB window, let's cause the same error again but in a different context. Type this:

(+ hello 2)

(By the way, before you hit return, notice that when you type the close parenthesis, the cursor briefly flashed to the corresponding open parenthesis. For complicated Lisp programs this "blink" feature is extremely helpful.)

Once again the SLDB window will open, with the same error message and list of two possible restarts. At the bottom of the SLDB window is a section called "Backtrace":

The cursor is on the first of these lines. It's telling you that the immediate context in which the error occured was trying to evaluate the expression HELLO. Below that you can see that Lisp was trying to evaluate the expression HELLO because it was trying to evaluate the expression (+ HELLO 2). You can imagine that this kind of "what was going on when the error occured?" information could be useful in debugging more complicated programs; it's called a "backtrace" because it traces backwards from the error through all the stages of what the program was trying to do when the error occurred.

If you hit enter on any of these backtrace lines then you get even more information about what was going on when the error occurred. For this simple example that information isn't very useful, but it's nice to know it's there.

You can always type the "q" key to skip out of the debugger.

Let's make a different kind of error. Ask Lisp to spell out all the numbers from one to a billion:

(loop for x from 1 to 1000000000 do (format t "~R~%" x))

Well, it's very impressive, but this is obviously going to take too long to complete. So interrupt Lisp by typing C-c C-b, which will take you back to the debugger, where you can abort this program.

4) Make a sound with CLM

Tell Common Lisp to use the CM/CLM/CMN package:

(in-package :cm)

Tell Common Lisp to compile and load the fm-violin instrument that you copied into this directory. (What do I mean by "this" directory? Common Lisp has a notion of the "current" directory, just like the Unix shell. So does Emacs. When you start Emacs, Emacs' current directory is set to the current directory of the Unix shell in which you typed "emacs". Likewise, when you start Common Lisp from Emacs (via SLIME), Common Lisp's current directory is set to Emacs' current directory. So since you were in your ~/220b/tutorial directory when you launched Emacs, and since you didn't change Emacs' current directory before starting SLIME, Common Lisp is "in" your ~/220b/tutorial directory too.)

(compile-file "v.ins")
(load "v")

Invoke the fm-violin instrument:

(with-sound () (fm-violin 0 1 440 0.1))

You should hear a one-second long note with pitch 440 Hertz (A above middle C).

5) Fancy Interactive SLIME

Let's enjoy some of the cool features of SLIME.

Completion

Type only the beginning of a call to fm-violin:

(fm

Now hit the tab key. A new Emacs window named "completions" comes up listing all the Lisp names in the current package (which includes CM, CLM, and CMN) that begin with the letters "fm". Type the next two characters, "-v":

(fm-v

Now hit the tab key again. SLIME knows that everything whose name begins "fm-v" actually begins with "fm-violin", so it automatically filled in the "iolin" then showed you everything whose name begins with "fm-violin". Nice, huh?

Automatic documentation retrieval

Now type space, and an Emacs window pops up at the bottom of the screen listing all the parameters to fm-violin, like this:

Of course you don't know what all of these mean yet, but the point is that it's telling you all the possible arguments to fm-violin. The first four, startime, dur, frequency, and amplitude, are regular arguments and must appear every time you call fm-violin. The rest are optional "keyword" arguments. Don't worry about all this detail now, just appreciate the way SLIME is trying to help you.

History of expressions sent to Lisp

The other thing I love about SLIME is scrolling through previous inputs to Lisp. Get back to a top-level REPL prompt and type M-p. It calls up the "p"revious thing that you sent to Lisp. Each time you type M-p it goes back through the history of what you sent Lisp. M-n goes to the "n"ext one. These are just like C-p and C-n for moving up and down by lines, except that they move up and down by expressions you sent to Lisp.

Now type "(w" at a top-level REPL prompt and then M-p. It goes back to the last expression from your history that begins with "(w", which is probably the previous call to with-sound.

Automatic Indentation

Say you want to synthesize a "song" with three notes. You could type them in all on one line, but that's ugly and hard to read:

Instead, type just "(with-sound ()" and then hit return. Notice that SLIME automatically indents your cursor to be underneath the letter "i", so you can see visually that what you're about to type will be inside the body of the call to with-sound. Notice also that what you've typed so far has not turned bold, meaning that it hasn't been sent to Lisp (because it's not yet a complete expression (because the parentheses aren't balanced yet, like this parenthetical comment.

Now type the first call to fm-violin on the second line and hit return again. Keep going, and you'll end up with this much more attractive and easy-to-understand formatting:

Experienced Lisp programmers rarely count parentheses; they rely on Emacs to indent their code properly and learn to read the indentation level to see what's inside what.

6) Writing Programs in Emacs

Split Emacs into two vertical windows with C-X 2. In one window, use C-X C-F to open a new file called mysound.lisp. Because the name of the file you're working on ends in ".lisp", Emacs figured out that you're going to type a Lisp program, and so the mode of that buffer is "(Lisp Slime)". Even though this isn't the SLIME REPL buffer where you talk to Lisp, you'll get a lot of the nice features of SLIME, including paren blinking, automatic indentation, automatic documentation retrieval, and tab completion.

Type your own little Lisp program into this buffer, with a call to with-sound and one or more calls to fm-violin.

When you're ready to try your program, put the cursor inside it somewhere and type C-M-x. This will send your program to Lisp. If there's an error it will bring up the debugger as before. Otherwise, your program will run, and you'll see the return value (which will probably be the string "test.snd") at the very bottom of Emacs.

Another way to run your program is to save the file (C-X s) and then load it into Lisp:

(load "mysound.lisp")

You can also explicitly copy and paste between the buffer that contains your file and the SLIME REPL buffer, M-C-k is super useful, because it cuts an entire Lisp expression (i.e., a complete set of balanced parentheses and everything between them).

7) Managing Emacs buffers

So far we've used many different Emacs buffers:

* slime-repl[1]*, SLIME's interactive read-eval-print loop

sldb, SLIME's debugger

*completions*, where Emacs listed all the things you might want to type that began with the letters "fm".

*Messages*, where SLIME shows you the arguments to functions

The buffer in which you're editing the file mysound.lisp

Behind the scenes, the buffer *inferior-lisp*, where CMUCL is actually running

For the most part, SLIME does a good job of popping up these buffers in Emacs windows when you need them, then making them go away when you're done with them. But eventually you're going to have to manage these buffers yourself. I get by with these five commands:

C-x o: move the cursor to the "o"ther window.

C-x 1: Make there be only one window (the one with the cursor in it)

C-x 2: Split the current window vertically into two windows

C-x b: Switch the current window to a different buffer, whose name I will type. (Space does name completion.)

C-x C-b: Show a list of all the buffers (along with some other information like what Emacs mode each one is in, what file is loaded into each one, etc.). Moving the cursor over one and hitting enter loads that buffer into the current window.

Also, if you click on the name of the buffer (which is in bold at the bottom of a window), it cycles through all the buffers.

8) Loading and Modifying Programs We Provide

This simple program is based on the idea of "forking" a note; at each time step each note becomes two notes, one higher and one lower:

Don't type the whole filename; after the letter "i" hit tab to get filename completion. You don't have to compile it because it's not an instrument; it's just a program that calls the fm-violin instrument.

Loading the file into Lisp is not the same thing as loading it into Emacs. What you've done so far is just make Lisp aware of one new function called violin-forker. To look at the file yourself, edit it, etc, you should load it into Emacs with C-X C-F.

In your violin-forker.lisp Emacs buffer, scroll down to "Some examples". Evaluate these to run my procedure.

I hope you will be inspired to modify the violin-forker function to do something different.