Lisp as a Second Language, 
Chapter ? 
Object-Oriented Style, Part I 

Peter Desain 

Nijmegen Institute for Cognition and Information
Nijmegen University
P.O.Box 9104
6500 HE Nijmegen
The Netherlands

Draft JULI  1995

Next to Smalltalk, Lisp has been one of the earliest languages to support an object-oriented style, and many ideas, that can now be found in object- oriented extensions of traditional languages (like C++) have developed over the years within the Lisp community. The ideas have come to a consistent and mature definition in CLOS, the Common Lisp Object System. This language replaces many of the precursors (Flavors, Common Loops) and has become widely accepted by users of Common Lisp. It contains, next to standard object-oriented mechanisms (instantiation, inheritance, etc.) more sophisticated concepts (like multiple inheritance, method combination, multi-methods and the meta object protocol) that can become powerful tools in constructing computer music systems and representing musical knowledge. The complexity of the full object oriented extension to Common Lisp can be quite overwhelming, especially when one tries to learn it directly from a concise reference such as Steele (1990). Therefore this paper takes a step by step approach with many small examples, which are constructed for the sake of clarity. The programs presented are not intended as a computer music composition system. They rather show programming techniques that can be used in writing your own. The expose of CLOS constructs is divided into three parts. This first part covers all basic constructs needed for defining classes and creating objects and simple methods that can act on them. It contains enough explanation of the mundane issues for the reader to learn to program in a simple object oriented style. The second part will treat more advanced topics like multi-methods and method combination which makes CLOS so much more powerful than other object-oriented languages and that provide many good tools for structuring and factoring musical knowledge. After mastering this part the reader will be familiar will all aspects of CLOS that one needs for programming in this language. The third part will show how in CLOS the language itself can be extended. This meta approach makes it possible to achieve elegant solutions even when the constructs and mechanisms (e.g. the kind of inheritance) built into the language cannot be used directly for representing the domain in question. Although everyday pedestrian use of CLOS does not often entail working at this level, the concepts of the meta object protocol are of such beauty and power that it will be worthwhile to get acquainted with them a bit. these texts a basic knowledge of Lisp is assumed. Basic list processing, function definitions, recursion, functional arguments, let binding and the loop macro are topics that will be used but not explained here. Most of this can be found in Desain (1990) or e.g. Tatar (1987). 

Defining classes 
When one uses a general data structure like a list to store information, one has to program a data abstraction barrier. This set of selector functions and constructor functions provides access to parts of the data structure or builds one and prevents the programmer from having to remember what e.g the caddar of a certain list represents. In CLOS the main data structure is the object, a record-like concept, and the access functions for objects are automatically defined by the system when the type of such an object is defined. There is a rigorous division between objects (instances) and the types of those objects (classes). In a class definition the programmer declares what fields (slots) an object of that class has and how they are to be accessed. the next example a class note is defined for the representation of a simple note of a specific pitch and duration. It has two slots, a duration (in arbitrary units, e.g. seconds or quarter notes) and a key number (a MIDI key number between 0 and 127, counting in semitones, with 60 defined as middle-C). Consider the choice of this type of primitive musical object here as an arbitrary one, we just need one such object in our examples. Of course the particular choice in a real program should be made on esthetic grounds - it expresses your idea of the atomic musical object and its parameters. Later we will show the mechanisms to extend the simple note concept implemented here. A class definition is made with a defclass form. Like all def... forms this form has some syntax to remember. First the name of the class to be defined is given in this case note. Following is a list of classes to inherit from (inheritance will be described later), in this case it is empty. After that, a list of slot descriptors follows. Each slot descriptor is a list of the slot name and some keyword-value pairs that define the name of the accessor function, the name of the initialization keyword by which the slot can be given its initial value and the documentation string describing the slot. The last component of the defclass form are the class options. In this case the only class option is a documentation string that describes the class. 

(defclass note ()
  ((duration :accessor duration 
             :initarg :duration 
             :documentation "Duration in time units")
   (pitch :accessor pitch 
          :initarg :pitch 
          :documentation "Pitch in MIDI numbers"))
  (:documentation "Class of note objects with pitch and duration attributes"))

Creating objects 
After having defined the class note there is no note object yet - we will have to make one by instantiating the class using the function make‑instance. The first argument to make‑instance is the name of a class and the initialization arguments. Thus we can supply values for each attribute, each slot of the note, by listing an initialization keyword supplied in the slot descriptor of the class definition, and a value for that slot. 

(make-instance 'note :duration 2 :pitch 60)

The expression above will return an object that represents a middle-C note of 2 time units. This object has its two slots initialized accordingly. If the expression above is too elaborate for your taste or, a better reason, if you want to create notes in your program without fixing yourself to this particular class definition and initialization protocol yet, a constructor function has to be defined. A first attempt looks like: 

(defun note (&key duration pitch)
  "Return a note object with a specified duration and pitch"
  (make-instance 'note :duration duration :pitch pitch))

This note constructor function passes its two arguments to make‑instance. But to flexibly allow for later extensions, we will define a constructor function that passes its whole argument list, whatever that may be, directly to make‑instance. It uses the &rest lambda list keyword by which an arbitrary number of arguments in the function call are gathered in a list. apply takes care to feed the elements of this list as separate arguments to make‑instance. 

(defun note (&rest attributes)
  "Return a note object with specified attributes (keyword value pairs)"
  (apply #'make-instance 'note attributes))

The fact that note is used both as the name of a user defined Lisp function, and of a user defined CLOS class does not cause name clashes: it is always clear from the context which construct is intended. Now a note object can be created by calling the note function. This will yield an object of the note class (printed in a not very readable format). 

(note :duration 2 :pitch 60) 
-> 
#<NOTE #x3F5BC1>

In the example above, the arrow (->) means "returns" or "evaluates to". 

Later we will show how we can program a play function that can transmit the note over MIDI to any connected synthesizer and make it audible. 

Reading slots 
A note object can be accessed (its slots read) by the accessor functions named in the class definition of note. The next example shows the creation of such a note and some subsequent slot access to print its duration and pitch attributes. 

(let ((object (note :duration 3 :pitch 65)))
  (print (duration object))
  (print (pitch object))
  object)
=>
3
65
->
#<NOTE #x3F5FB9>

In the example above, the double arrow (=>) means "outputs by side effect" or "prints". avoid having to print attributes of notes all the time for inspection of their values one can define an external representation of notes in a readable format that directly shows the values of the slots. Here we define a function called show‑note. It uses the accessor functions to translate the note and the values of its slots into a readable list. We arbitrarily choose to let it yield the same simple list format that happens to be the construction call. We will need to call this function explicitly for the moment, to show the results of our programming. However, we will tie this function  more closely into the system later, such that notes will be printed automatically in this format. 

(defun show-note (object)
  "Return a list describing a note object with its attributes"
  (list 'note :duration (duration object) :pitch (pitch object)))

(show-note (note :duration 2 :pitch 61)) -> (note :duration 2 :pitch 61)

More classes 
We will enrich our domain of musical objects a bit and, as example, add some classes that handle the representation of time order. You might have noticed that a note object has no slot specifying its onset in time. We will indeed opt for a relative specification of time: the actual onset of a note will be dictated by its context. There are two obvious time relations that two musical objects can have: they can start at the same time or one can start after the other. Specifying the time relations between objects as being either parallel or sequential and building larger musical objects in that way is an alternative to the use of absolute start times found in most composition systems, but the choice is made here quite arbitrarily - just to illustrate the definition and use of these constructs. 

(defclass sequential ()
  ((elements :accessor elements 
             :initarg :elements 
             :documentation "The successive components"))
  (:documentation "A group of musical objects, one after another"))

(defclass parallel ()
  ((elements :accessor elements 
             :initarg :elements 
             :documentation "The simultaneous components"))
  (:documentation "A group of musical objects, starting at the same time"))

After the class definitions we can define the constructor functions for these compound musical objects. 

(defun sequential (&rest elements)
  "Return a sequential musical object consisting of the specified components"
  (make-instance 'sequential :elements elements))

(defun parallel (&rest elements)
  "Return a parallel musical object consisting of the specified components"
  (make-instance 'parallel :elements elements))

Now we can construct more elaborate musical objects like a low long note parallel to a sequence of three higher notes. 

(defun example ()
  "Returns a simple structured musical object"
  (parallel (sequential (note :duration 1 :pitch 60)
                        (note :duration 2 :pitch 63)
                        (note :duration 1 :pitch 61))
            (note :duration 4 :pitch 55)))

(play (example) :time-unit 1/4 :tempo 60) => 

Figure 1. Score of the example.

The score  of this example is given in Figure 1. A piano-roll notation of the same musical object is given in Figure 2. On the horizontal axis time runs from left to right, in time units. On the vertical axis pitch is represented. Each rectangle represents a note. 

Figure 2. Piano-roll notation of the example.

When the body of example is run, three note objects will be created and collected in a list that is stored in the elements slot of an encompassing sequential object. A list of this sequential object together with a fourth lower note is stored in the elements slot of the enclosing parallel object. Thus a structured (parallel or sequential) musical object has access to its parts: they are listed as elements. The musical structures define a part-of hierarchy of linked musical objects. This is illustrated in Figure 3 which constitutes an alternative graphical representation of the same example. 

Figure 3. The objects and their links in the example.

In this tree diagram the arrow represents the "has-part" relation. It reflects the fact that a sequential or parallel object can get access to its parts via its elements slot, but not vise versa. 

Naming pitches 
To make the pitches in our examples a bit easier to read we can translate pitch names to MIDI numbers and vise versa. The two functions pitch‑name‑to‑midi‑number and midi‑number‑to‑pitch‑name are defined in the full code listing in the final section. They do simple string processing and constitute nothing interesting from the point of CLOS constructs. Let us first check their working. 

(pitch-name-to-midi-number "C#4") -> 73
(midi-number-to-pitch-name 60) -> "C3"

The show‑note function can easily be adapted to do the translation and show pitches by name. We can integrate the translation into the note function by adding the new pitch argument to the initialization argument list passed to make‑instance. This new argument will be used because it occurs in the argument list before the old one. The small effort needed to define a constructor function for notes now already has proven its worth by enabling this addition. 

(defun note (&rest attributes)
  "Return a note object with specified attributes (keyword value pairs)"
  (if (stringp (getf attributes :pitch))
    (apply #'make-instance 'note 
           :pitch (pitch-name-to-midi-number (getf attributes :pitch))
           attributes)
    (apply #'make-instance 'note attributes)))

(defun show-note (object)
  "Return a list describing a note object with its attributes"
  (list 'note 
        :duration (duration object) 
        :pitch (midi-number-to-pitch-name (pitch object))))

(show-note (note :duration 2 :pitch "C#3")) 
-> 
(note :duration 2 :pitch "C#3")

One might wonder why we didn't store pitch names directly in the pitch slot of the note. There wouldn't be anything fundamentally wrong with this, except for the fact that the calculations on pitches that we are going to write later (like transpositions) are much easier to implement on a numeric representation. 

Methods 
To be able to show the new objects in a readable form on the output ads well, one could be tempted to define show‑sequential and show‑parallel functions to construct the appropriate lists. In calling these functions the programmer then has to be aware of the type of musical objects to show and select the proper function. E.g. the show‑parallel function has to be called when the result of the example function has to be shown. Another approach would be to write a function that can be applied to any type of musical object and which obtains the type (class) of its argument and dispatches accordingly to the appropriate function. This approach is illustrated in the next example. 

(defun show-object (object)
  "Return a list describing a musical object with its components or attributes"
  (typecase object
    (note (show-note object))
    (sequential (show-sequential object))
    (parallel (show-parallel object))))

And indeed the construction of a so called polymorphic or generic show function that can be applied to many types of musical objects is part of the answer. Only it should not have to be written by the programmer, but should be supplied by the system. One of the central ideas of object-oriented programming is indeed the notion of automatic type-driven dispatching, in which the system assembles definitions like the one above from the functions supplied by the user. How does this work? The user defines a set of functions all with the same name, but with added type information for the arguments. These are called methods. Thus instead of the show‑note, show‑sequential and show‑parallel functions three different show methods are defined by the programmer, all have the same name but they can be distinguished because their argument is declared to be of a different type. 

(defmethod show ((object note))
  "Return a list describing a note object with its attributes"
  (list 'note 
        :duration (duration object) 
        :pitch (midi-number-to-pitch-name (pitch object))))

(defmethod show ((object sequential))
  "Return a list describing a sequential musical object with its components"
  (cons 'sequential 
        (mapcar #'show (elements object))))

(defmethod show ((object parallel))
  "Return a list describing a parallel musical object with its components"
  (cons 'parallel 
        (mapcar #'show (elements object))))

Silently the system has in the mean time assembled a so called generic function show similar to the definition of show‑object given above, but now calling the appropriate user-defined show methods. The three show methods are said to be specialized to the note, sequential and parallel class respectively. The show method for a note returns a list with the attributes, with the atom note in front. The show method for a sequential or parallel object collects the result of showing its elements in a list and places the proper atom in front. 

Generic functions vs. message passing 
Note how the show methods for sequential and parallel objects call themselves recursively using a functional argument to mapcar to calculate the result for each element. Because in CLOS the individual methods are combined into a generic function automatically, the well-known functional programming techniques like recursion and passing functions as arguments to other functions can be applied directly for use on methods. This unification of functional and object oriented programming using generic functions is a large advantage over the message passing style. In the latter paradigm objects are being send messages (a special language construct) by other objects. This means that all facilities and constructs developed for functions (mapping, combinators, tracing etc.) have to be redefined to be useful for messages. In CLOS methods are applied to objects just like functions are applied to arguments, and all the functional paraphernalia works as well for methods. should test the show method now, which is done more easily by pretty-printing its result. 

(pprint (show (example)))
=> 
(parallel (sequential (note :duration 1 :pitch "C3")
                      (note :duration 2 :pitch "D#3")
                      (note :duration 1 :pitch "C#3"))
          (note :duration 4 :pitch "G2"))

Defining an object representing a rest 
In a call to show the so called applicable method is selected by the system, on the basis of the class of the argument. This has the added advantage that when a program grows and more classes are added, the programmer does not have to modify the functions to handle these data types as well, only additional methods have to be defined. We will demonstrate this by adding a class for a rest and the constructor for it (which will be called pause to prevent a name clash with the Lisp rest function that gives the cdr of lists). 

(defclass pause ()
  ((duration :accessor duration 
             :initarg :duration 
             :documentation "Duration in time units"))
  (:documentation "Class of rest objects with a duration attribute"))

(defun pause (&rest attributes)
  "Return a rest object with specified attributes (keyword value pairs)"
  (apply #'make-instance 'pause attributes))

A rest only needs a duration attribute. Adding the following simple method definition will enable compound musical objects that contain rests to be shown as well: 

(defmethod show ((object pause))
  "Return a list describing a rest object with its attributes"
  (list 'pause :duration (duration object)))

Transformations 
Having set up a simple system of musical objects, a way to create them, and a way to print them textually, we can proceed in defining some useful musical transformations on these objects. The next example defines a method that takes a note as argument and creates a new one with a pitch that is transposed by an interval. This method is specialized in one argument, the object to transpose, the second argument of the transpose method, the interval, is not typed. In the example a C# is transposed by a whole tone. 

(defmethod transpose ((object note) interval)
  "Return a note with a pitch raised by interval"
  (note :duration (duration object)
        :pitch (+ (pitch object) interval)))

(show (transpose (note :duration 2 :pitch "C#") 2)) -> (note :duration 2 :pitch "D#")

The example above did instantiate a new note object with its duration slot copied directly from the original. This is a proper (non destructive) functional style of programming. But when the original is no longer needed it may make sense to alter one slot in the original directly instead of creating a new object. And indeed the imperative style of programming in which objects are modified destructively is often used in object oriented programming. In this style one has to take care that an object that is altered is not accessible from parts of the program that assume that its state is invariant. 

Writing slots 
The slots of an object can be destructively written by the generalized setf assignment construct using the same accessors as for reading. The next example transposes a note by altering its pitch slot. 

(defmethod transpose ((object note) interval)
  "Raise the pitch of a note by an interval"
  (setf (pitch object) (+ (pitch object) interval)))

Of course the predefined modification macro's that are based on setf work as well for slots. 

(defmethod transpose ((object note) interval)
  "Raise the pitch of a note by an interval"
  (incf (pitch object) interval))

To illustrate the workings of the transpose method we show a note before and after transposition. 

(let ((object (note :duration 2 :pitch "C#3")))
  (print (show object))
  (transpose object 3)
  (print (show object))
  t) 
=>
(note :duration 2 :pitch "C#3") 
(note :duration 2 :pitch "E3")

Because transpose works by side-effect it is not really necessary to make it return a value. But often a nicer style of code can result when these destructive transformations do return their modified argument as result. This is a specific style of writing destructively: a function may destroy its arguments but cannot be trusted to update them correctly. However, it returns a proper value. This style resembles the protocol that may Common Lisp functions use (e.g. sort). It stands in contrast to the style that uses procedures to modify arguments directly, and whose return value will be ignored. 

(defmethod transpose ((object note) interval)
  "Raise the pitch of a note by an interval"
  (incf (pitch object) interval)
  object)

(show (transpose (note :duration 2 :pitch "C#") 3)) 
-> 
(note :duration 2 :pitch "E3")

Transposing whole musical objects 
Now, of course, we can define the other methods for transpose as well, using recursion to transpose the components of sequential and parallel musical objects. 

(defmethod transpose ((object pause) interval)
  "Transpose a rest, do nothing to it"
  object)

(defmethod transpose ((object sequential) interval)
  "Transpose a sequential musical structure, by transposing its components"
  (loop for element in (elements object) 
        do (transpose element interval))
  object)

(defmethod transpose ((object parallel) interval)
  "Transpose a parallel musical structure, by transposing its components"
  (loop for element in (elements object) 
        do (transpose element interval))
  object)

The compiler will warn that in the transpose method for pause the interval argument is not used. A declaration can instruct the compiler, to ignore this parameter and will suppress the warning. 

(defmethod transpose ((object pause) interval)
  "Transpose a rest, do nothing to it"
  (declare (ignore interval))
  object)

With our generic definition of transpose and the show facility we can now check its working. 

(pprint (show (transpose (example) 2)))
=>
(parallel (sequential (note :duration 1 :pitch "D3")
                      (note :duration 2 :pitch "F3")
                      (note :duration 1 :pitch "D#3"))
          (note :duration 4 :pitch "A2"))

This transformation can of course also be checked by playing the results using the play function. In Figure 4 the original example plus its transposition is shown, collected together in a sequential order. In this case the transformation cannot destructively affect the original because a fresh copy is made by each call to the example function. Figure 5 gives the corresponding piano roll notation. 

(play (original-and-transform
       (example) 
       (pause :duration 1)
       #'transpose 2)) => 

Figure 4. Score of the example and a transposition over a whole tone.

Figure 5. The example and its transposition over a whole tone.

Inheritance 
You may have wondered if sequential and parallel need their own definitions of transpose, even though the body of the method is exactly the same. The same holds for the duplicated definition of the elements slot in the sequential and parallel class. In the object-oriented style one strives towards sharing duplicated code and behavior as much as possible. This is done is by making the sharable part explicit as a new abstract class that we will call compound‑musical‑object. It has its own slot definition and a transpose method defined for it. 

(defclass compound-musical-object ()
  ((elements :accessor elements 
             :initarg :elements 
             :documentation "The components"))
  (:documentation "A group of musical objects ordered in time"))

(defmethod transpose ((object compound-musical-object) interval)
  "Transpose a compound musical structure, by transposing its components"
  (loop for element in (elements object) 
        do (transpose element interval))
  object)

An abstract class is not intended for direct initialization, but it groups the shared behavior (in this case a slot and the transpose method) together, such that other classes can inherit this behavior without defining it themselves. Inheriting this class’ behavior is done by supplying the class in the list of direct superclasses in the defclass form. This is the first list after the method's name, the list that unto now has been left empty. 

(defclass sequential (compound-musical-object) ()
  (:documentation "A group of musical objects, one after another"))

(defclass parallel (compound-musical-object) ()
  (:documentation "A group of musical objects, starting at the same time"))

These definitions show that the duplicated slot descriptors in the definition of the sequential and parallel class are indeed no longer necessary. The duplicated transpose methods of these two classes have become redundant as well. A graphical representation of the inheritance in the form of a class hierarchy is quite useful, especially when the situations becomes more complex. For the moment we have just 2 concrete classes that inherit from the same abstract one. In Figure 6 these classes are shown with the arrows representing their directly-inherits-from relation. This is the inverse of the is-direct-superclass-of relation.  The concrete classes (that can be instantiated directly) are shown in a gray shade. 

Figure 6. Inheriting transposition behavior for compound musical objects.

The classes in the inheritance diagram above is annotated with the transpose method defined for these different classes. We see that, next to the new general transpose method that was defined for all compound musical objects, the old transpose definitions for parallel and sequential object are still around. In the selection of an applicable method the so called most specific method will be chosen to run. This is, roughly spoken, the one encountered first when one starts at the class of the object in the inheritance hierarchy and moves upward. Thus any parallel or sequential object will still be transposed with its old definition that was defined for that specific class, not with the method specialized for the less specific compound musical objects. The old methods are said to shadow the new one. In general the use of shadowing can be correct and intended by the programmer, but often it signals a flaw in the design of the classes, an inelegant partitioning of knowledge, and it is hard to keep track of the complexities of inheritance when shadowing is used a lot. In our case, however, the unintended shadowing resulted because old method definitions were still around. Removing these old method definitions in de editor textually does not remove them from the system. The difference between the textual state in the editor and the internal state of the system, which can be a source of great confusion, is managed best  by keeping all method definitions together and starting afresh after these kinds of large changes. This is done by removing all old method definitions using fmakunbound, before evaluating the new ones. 

(fmakunbound 'transpose)

(defmethod transpose ((object compound-musical-object) interval)
  "Transpose a compound musical structure, by transposing its components"
  (loop for element in (elements object) 
        do (transpose element interval))
  object)

(defmethod transpose ((object pause) interval)
  "Transpose a rest, do nothing to it"
  (declare (ignore interval))
  object)

(defmethod transpose ((object note) interval)
  "Raise the pitch of a note by interval"
  (incf (pitch object) interval)
  object)

Now the definition of the transpose method for sequential or parallel objects is disposed of because it is handled by the transpose method specialized for compound‑ musical‑object, and the system will find this method to apply to an object of type sequential or parallel because no other, more specific, methods are available. We can similarly cleanup the definition of note and pause which can inherit their shared duration slot from a new class for basic (non-compound) musical objects. And it even makes sense to provide an abstract class musical‑object for (yet unforeseen) behavior that will be shared by all musical objects. This leads to the following class definitions. 

(defclass musical-object () ()
  (:documentation "The abstract root class of all musical objects"))

(defclass compound-musical-object (musical-object)
  ((elements :accessor elements 
             :initarg :elements 
             :documentation "The components"))
  (:documentation "A group of musical objects ordered in time"))

(defclass sequential (compound-musical-object) ()
  (:documentation "A group of musical objects, one after another"))

(defclass parallel (compound-musical-object) ()
  (:documentation "A group of musical objects, starting at the same time"))

(defclass basic-musical-object (musical-object) 
  ((duration :accessor duration 
             :initarg :duration 
             :initform 1 
             :documentation "Duration in time units"))
  (:documentation "A primitive musical object with a duration attribute"))

(defclass note (basic-musical-object)
  ((pitch :accessor pitch 
          :initarg :pitch 
          :initform 60 
          :documentation "Pitch in MIDI numbers"))
  (:documentation "A pitched note"))

(defclass pause (basic-musical-object) ()
  (:documentation "A rest"))

Now we have created a more complex class hierarchy that reflects our view of musical objects and their shared characteristics. It can be depicted as a directed a-cyclic IS-A network of directly-inherits-from relations, in Figure 7. In this example hierarchy, each class inherits only from one other class. More elaborate examples of inheritance will be given later. Note that this figure depicts the general IS-A relation between classes, in contrast to Figure 3 which shows the HAS-PART pointers between real instantiated objects. 

Figure 7. The hierarchy of classes for musical objects.

Again we can show some method definitions in this tree to see where the knowledge  needed to show and transpose different types of musical objects resides. 

Figure 8. The method definitions of transpose and show.

Initial slot values 
In the slot descriptors given in the class definitions above you may have noticed a new slot option :initform. This indicates that when an instance is made (with make‑instance) without supplying an initialization argument for a certain slot this value can be used instead. It gives a default value for the slot, e.g. the duration slots default to 1 second. Because our note and pause constructor functions pass their whole argument list to make‑instance, they do not interfere with this mechanism. And any incomplete call to create a musical object will result in an object with all of its slots bound, some by specification, some by default. 

(show (pause)) -> (pause :duration 1)
(show (note :duration 4)) -> (note :duration 4 :pitch "C3")

This is the first place where we encounter the possibility of declaring default values. In CLOS there are many mechanisms like these available, and the understanding of their proper use, and of their interaction can be quite difficult. We save this topic for the next chapter. 

Customizing build-in behavior of the Lisp system 
It would be nice if our external representation could be used for all printing (instead of the unreadable #<...> format). The Common Lisp system already incorporates a print‑object method for the predefined data types (array’s, records etc.) that is called in all printing. We can link our way of showing the musical objects directly into the system by defining our own print‑object method for musical‑object, which takes care of pretty-printing as well. 

(defmethod print-object ((object musical-object) stream)
  "Prints a description of a musical object as a list to a stream"
  (pprint (show object) stream))

After this, all printing is done via our print‑object method, be it by calls to print or format, in traces and error messages, or in the top-level read-eval-print loop in which the user types and expression to the interpreter and the evaluated result is printed by the system:

(example) 
->
(parallel (sequential (note :duration 1 :pitch "C3")
                      (note :duration 2 :pitch "D#3")
                      (note :duration 1 :pitch "C#3"))
          (note :duration 4 :pitch "G2"))

This illustrates an important advantage of object-oriented style: the ease of extending code of which the source is not available: even a system-defined primitive is elaborated here. It also shows a constraint on this approach: the argument list of the definition of a method must be known, because they must be similar for all methods. That is, they have to be congruent lambda lists in which only the types of the argument (and the names) may vary. Further treatment of this topic is given in Steele (1990, page 791). 

The intuition, that a class which can handle things common to all kinds of musical objects might come in handy one time, thus proved to be right: the print‑object method is specialized to this new class. The skill to design class hierarchies for a domain such that later extensions fit in easily can only be developed during the construction and re-construction of many object oriented programs. Another way of saying the same is that, though object oriented programming languages are very flexible and dynamic, and code can often be re-used and extended instead of being re-written, the modification of class hierarchies often still entails many complex changes throughout the program. And thus a proper design of these definitions from the start, with enough hooks for adding unforeseen behavior later is an essential habit. 

Copying objects 
Because the transformations on musical objects are done destructively, we need a facility to copy a musical object, in case the original is needed as well. It is neither difficult to extract the attributes of a note and create a new note with the same attributes, nor is it complex to create a new compound musical object with as elements copies of the elements of the original. But because we already defined a show method, which delivers a description that resembles the expression that created the musical object directly, copying can be implemented as evaluating the result of the show method. 

(defmethod copy-object ((object musical-object))
  (eval (show object)))

Now e.g. a function can be written that applies an arbitrary transformation to a musical object, and returns the result preceded by the original and separator (e.g. a rest). 

(defmethod original-and-transform ((object musical-object) 
                                   separator transform &rest args)
  "Return the original, a separator and a transformed musical object in sequence"
  (let ((result (apply transform (copy-object object) args)))
    (if separator
      (sequential object separator result)
      (sequential object result))))

(original-and-transform (note :duration 1 :pitch "C3") 
                        (pause :duration 1) 
                        #'transpose 2) 
->
(sequential (note :duration 1 :pitch "C3") 
            (pause :duration 1)
            (note :duration 1 :pitch "D3"))

Other combinators of musical objects can be written as well, e.g. functions to create a repetition and a canon. The results of these functions can be safely subjected to destructive transformations because duplicated objects are copied: there is no shared structure. 

(defmethod repeat ((object musical-object) 
                   &optional (n 2) (separator (pause :duration 1)))
  "Return a number of repetions of a musical object"
  (apply #'sequential
         object
         (loop repeat (1- n)
               when separator collect (copy-object separator)
               collect (copy-object object))))

(defmethod canon ((object musical-object) n onset-delay)
  "Return an n-voiced canon of a musical object"
  (parallel object
            (apply #'sequential 
                   (loop for voice from 1 below n
                         collect (pause :duration (* voice onset-delay))
                         collect (copy-object object)))))

A metric grammar 
To give some hints how generative aspects can be implemented elegantly within our framework, we will write some functions that produce a rhythm that is strictly metric. This is, every note fits one of the levels of the metric hierarchy. Or to say it in other words, every level is either completely divided or presented as one whole. We first write a program that recursively divides a time duration, using a different divisor at each level, and deciding at random whether subdivision has to continue. This function can be say to embody some kind of generative grammar, deciding at random which of the alternative generative rules to use. 

(defun strictly-metric-division (duration divisors)
  "return a list of strictly metric durations"
  (if (and divisors (toss .75))
    (loop repeat (first divisors) 
          append (strictly-metric-division (/ duration (first divisors)) 
                                           (rest divisors)))
    (list duration)))

(defun toss (chance)
  "There is a chance that this will return t"
  (< (/ (random 10000) 10000) chance))

(strictly-metric-division 12 '(2 3 2))
->
(1 1 2 1 1 6)

The results of the strictly‑metric‑division have to be mapped to a sequence of notes. Generating a bar of notes in a certain some time-signature now entails nothing more than passing the duration of the bar and the subdivision at each level to the strictly metric generator. 

(defun make-rhythm (durations)
  "Return a sequence of notes with certain durations"
  (apply #'sequential (loop for duration in durations 
                            collect (note :duration duration))))

(defun bar-strict-3/4 ()
  "Return a bar of notes in strict 3/4"
  (make-rhythm (strictly-metric-division 3/4 '(3 2 2))))

(defun bar-strict-6/8 ()
  "Return a bar of notes in strict 6/8"
  (make-rhythm (strictly-metric-division 6/8 '(2 3 2))))

(bar-strict-6/8)
->
(sequential (note :duration 3/8 :pitch "C3")
            (note :duration 1/16 :pitch "C3")
            (note :duration 1/16 :pitch "C3")
            (note :duration 1/8 :pitch "C3")
            (note :duration 1/16 :pitch "C3")
            (note :duration 1/16 :pitch "C3"))

Reflecting pitches 
To study another transformation, and see how it differs from transposition, consider a transformation that inverts a piece: reflecting the pitches of all the notes around a pitch center. 

(defmethod mirror ((object compound-musical-object) center)
  "Reflect all pitches occurring in a compound musical object around a center"
  (loop for element in (elements object) 
        do (mirror element center))
  object)

(defmethod mirror ((object note) center)
  "Reflect the pitch of a note around a center"
  (setf (pitch object) 
        (- center 
           (- (pitch object) center)))
  object)

(defmethod mirror ((object pause) center)
  "Reflect a rest: do nothing to it"
  (declare (ignore center))
  object)

In Figure 9 the result of this transformation is shown, applied to the example and preceded by the original. 

Figure 9. The example and an inversion around middle-c.

The body of the mirror method for notes still is a bit too complex to our taste. An auxiliary function can easily do the arithmetic on pitches. A natural name for this function would be mirror of course. But then we need to make it into a real method and specialize it for a certain kind of data. Because CLOS is well-integrated in the Lisp type system, we can use ordinary types as specializers for methods. This means that on any Lisp data type (e.g. number, string or array) specialized methods can be defined, just as easy as for real classes. We will use this facility to write a mirror method specialized for numbers, and call that method inside the method specialized for notes. 

(defmethod mirror ((pitch number) center)
  "Reflect a pitch around a center"
  (- center (- pitch center)))

(defmethod mirror ((object note) center)
  "Reflect the pitch of a note around a center"
  (setf (pitch object) (mirror (pitch object) center))
  object)

To allow naming of center pitches we need to check for strings and translate them into numbers before we apply the arithmetic. 

(defmethod mirror ((pitch number) center-pitch)
  "Reflect a pitch around a center"
  (let ((center (if (stringp center-pitch)
                  (pitch-name-to-midi-number center-pitch)
                  center-pitch)))
    (- center (- pitch center))))

(defmethod mirror ((object note) center)
  "Reflect the pitch of a note around a center"
  (setf (pitch object) 
        (mirror (pitch object) center))
  object)

There is one last sideline. We noticed that in the definition of transpose, incf was used to update the pitch slot of an object with the sum of its old value and a specified interval. And instead of (setf (pitch object)(+ (pitch object) interval) we wrote (incf (pitch object) interval). Of course this mechanism is what we need her as well: a mirrorf macro to update a generalized variable, like a slot, directly with our user-defined mirror method for numbers. Writing the macro to achieve this is not trivial, even though it may look otherwise. However, Common Lisp provides a facility that can define the macro for us. The define‑modify‑macro macro needs a name, a list of extra arguments, a function that calculates a new value from an old one and some extra arguments, and possibly some documentation. 

(define-modify-macro mirrorf (center) 
  mirror 
  "Change a variable by reflecting it around a certain center")

Now we may use this new macro in the body of the mirror method for notes.

(defmethod mirror ((object note) center)
  "Reflect the pitch of a note around a center"
  (mirrorf (pitch object) center)
  object)

(play (original-and-transform
       (example) 
       (pause :duration 1)
       #'mirror "C3")) => 

Figure 10. The example and its inversion.

Iterating over part-of structures 
When working with structures of objects that are linked to one another (like via the elements slot of the compound musical objects) one tends to find that many programs exhibit more or less the same way of visiting the different objects of such a structure. Compare e.g the mirror program and the definition of transpose. 

(defmethod mirror ((object compound-musical-object) center)
  "Reflect all pitches occurring in a compound musical object around a center"
  (loop for element in (elements object) 
        do (mirror element center))
  object)

(defmethod transpose ((object compound-musical-object) interval)
  "Transpose a compound musical structure, by transposing its components"
  (loop for element in (elements object) 
        do (transpose element interval))
  object)

You can see that both have the same control structure. To extract that functionality we could write something that is like mapc is for lists, a general mapping function that applies the operation to each element of a compound musical object. To add some more generality we will have the new function pass any extra arguments that it receives in its call to the operation. 

(defmethod mapc-elements ((object compound-musical-object) operation &rest args)
  "Apply the operation to each element in a musical object"
  (mapc #'(lambda(element)(apply operation element args))
        (elements object))
  object)

Thus we can define transpose and mirror (and a whole bunch of similar transformations) much more concise. 

(defmethod transpose ((object compound-musical-object) interval)
  "Transpose all pitches occurring in a musical object over an interval"
  (mapc-elements object #'transpose interval))

(defmethod mirror ((object compound-musical-object) center)
  "Mirror all pitches occurring in a musical object around a center"
  (mapc-elements object #'mirror center))

Slot access vs. methods 
In designing inheritance networks it is always good to check if behavior defined for a specific class can be generalized to others. An example is the duration slot of basic musical objects that can be accessed by the duration method generated by the system. This accessor was generated because duration was defined as an accessor in the slot descriptor in the definition of the basic‑musical‑object class. Because the duration accessor is itself a method we can extend its applicability by defining methods to calculate the duration of other musical objects. Calling duration on a note or a pause will return the value of the slot and calling it on a sequential (or parallel) object will calculate the sum (or maximum) of the duration of its elements. 

(defmethod duration ((object sequential))
  "Return the duration of a sequential musical object"
  (loop for element in (elements object) sum (duration element)))

(defmethod duration ((object parallel))
  "Return the duration of a parallel musical object"
  (loop for element in (elements object) maximize (duration element)))

(duration (example)) -> 4

This illustrates the advantages of the uniform way of slot access and method calls. The duration method can be applied to any musical object now, without having to remember whether a slot access or a calculation has to be done to retrieve it. This enables easy changes of implementation of the duration concept in the program as well (from stored to calculated, from procedural to declarative), without having to rewrite the parts that use it. 

The same mechanism can be used to get an objects attribute value in a different format. E.g. the frequency of a note (in Hz.) might be of interest. Writing a method to retrieve it looks the same as a slot accessor from the outside. Thus we need not worry whether pitches or frequencies are stored in a note slot, they both can be retrieved in the same way. The auxiliary pitch‑to‑frequency arithmetic is given in the full code listing that appears at the end of this chapter. 

(defmethod frequency ((object note))
  "Return the frequency of a note (in Hz.)"
  (pitch-to-frequency (pitch object)))

(frequency (note :pitch "C#3")) -> 277.18

Mapping over the parts of an object 
Some calculations on compound musical objects cannot be programmed with the mapc‑elements iterator, even though the way in which they visit the parts of a musical object is identical. This is e.g. the case when side-effects are not appropriate and return values have to be constructed. We will write a simple map‑elements which analogous to the mapc‑elements function, applies an operation to each element of a compound musical object, but this time it collects the results and uses an extra argument that specifies how they are to be combined. 

(defmethod map-elements ((object compound-musical-object) 
                         combination operation &rest args)
  "Combine the results of the operation applied to each element"
  (apply combination
         (mapcar #'(lambda (element) (apply operation element args))
                 (elements object))))

We will use this mapper to program the duration method anew. 

(defmethod duration ((object sequential))
  "Return the duration of a sequential musical object"
  (map-elements object #'+ #'duration))

(defmethod duration ((object parallel))
  "Return the duration of a parallel musical object"
  (map-elements object #'max #'duration))

Which concisely simple reads that the duration of a sequential structure can be obtained by adding the duration of its elements, and that the duration of a of a parallel structure is the maximum of the duration of its elements. To further illustrate the mapper's use we will write a simple program that counts the number of notes in a musical object. 

(defmethod note-count ((object compound-musical-object))
  "Return the number of notes in this musical object"
  (map-elements object #'+ #'note-count))

(defmethod note-count ((object note)) 
  "Return 1, there is one note in this musical object"
  1)

(defmethod note-count ((object pause)) 
  "Return 0, there are no notes in this musical object"
  0)

(note-count (example)) -> 4

A slight variation can count the number of musical objects, including compound ones. 

(defmethod object-count ((object compound-musical-object))
  "Return the number of objects in this musical object"
  (map-elements object 
                #'(lambda(&rest counts)
                    (1+ (apply #'+ counts)))
                #'object-count))

(defmethod object-count ((object basic-musical-object))
  "Return one: the multitude of this one objects"
  1)

(object-count (example)) -> 6

Another variation counts the number of parallel voices that happen inside a musical object, a function that may come of use when we handle voice allocation for synthesizers with a limited number of oscillators that can sound simultaneously.  

(defmethod voice-count ((object sequential))
  "Return the number of parallel voices in this musical object"
  (map-elements object #'max #'voice-count))

(defmethod voice-count ((object parallel))
  "Return the number of parallel voices in this musical object"
  (map-elements object #'+ #'voice-count))

(defmethod voice-count ((object note)) 
  "Return 1, there is one note needed for this musical object"
  1)

(defmethod voice-count ((object pause)) 
  "Return 0, there are no voices needed for this musical object"
  0)

(voice-count (example)) -> 2

Iteration and onset times 
Sometimes transformations need the start time of a note, which is not explicitly stored in the object itself. It is not difficult to define an auxiliary function that calculates the onset times of elements of a compound object, given the onset time of the object itself. 

(defmethod onsets ((object sequential) &optional (onset 0))
  "Return the onsets of the components of object, given its own onset"
  (loop as start = onset then (+ start (duration element))
        for element in (elements object)
        collect start))

(defmethod onsets ((object parallel) &optional (onset 0))
  "Return the onsets of the components of object, given its own onset"
  (make-list (length (elements object)) :initial-element onset))

Calling this method on an sequential object and its onset time will yield the subsequent onset time of its components, and for a parallel structure will return the list of identical onset times of its components. 

(onsets (sequential (note :duration 1)
                    (pause :duration 2)
                    (note :duration 4))
        3) 
-> 
(3 4 6)

(onsets (parallel (note :duration 1)
                  (pause :duration 2)
                  (note :duration 4))
        3) 
-> 
(3 3 3)

Extending the iterator slightly, it can keep track of these onset times and supply them as extra argument to an operation. 

(defmethod mapc-elements-onsets ((object compound-musical-object) operation 
                                 onset
                                 &rest args)
  "Apply the operation to each element in a musical object"
  (mapc #'(lambda(element element-onset)
            (apply operation element element-onset args))
        (elements object)
        (onsets object onset))
  object)

A simple test of this mapper can be undertaken by having it print the elements and their onset times. 

(mapc-elements-onsets (sequential (note :duration 1)
                                  (pause :duration 2)
                                  (note :duration 4))
                      #'(lambda (element onset)
                          (format t "~%~A ; at ~A" (show element) onset))
                      0)
=> 
(note :duration 1 :pitch "C3") ; at 0
(pause :duration 2) ; at 1
(note :duration 4 :pitch "C3") ; at 3

Defining a rather dumb onsets method for parallel structures allows one definition for the mapc‑elements‑onsets method for all compound objects. We choose here to sacrifice efficiency a bit in favor of the simplicity of the program. A version of mapc‑elements‑onsets for parallel objects that is more efficient, can always be added. Thus even optimization becomes an activity of adding efficient code for special cases, there is no need to modify existing programs. As such a program will be separated into a part that is essential, elegant, and easy to maintain, and a part that addresses the optimization issues. For now we extend the elegant part with a drawing program based on this new mapper. 

Draw 
As an example of the use of mapc‑elements‑onsets, and to have an easy way of inspecting the results of our transformations, we will write a graphical program to draw a piano-roll notation of a musical structure using the new iterator. We assume that a graphical primitive draw‑rectangle exists that draws a rectangle, given a list of the left, top, right and bottom sides. 

(defmethod draw ((object musical-object))
  "Draw a graphical piano-roll representation of a compound musical object"
  (draw-object object 0))

(defmethod draw-object ((object compound-musical-object) onset)
  "Draw a graphical piano-roll representation of a compound musical object"
  (mapc-elements-onsets object #'draw-object onset))

(defmethod draw-object ((object note) onset)
  "Draw a graphical representation of a note (a rectangle)"
  (draw-rectangle (boundary object onset)))

(defmethod draw-object ((object pause) onset)
  "Draw nothing"
  (declare (ignore onset)))

(defmethod boundary ((object note) onset)
  "Return a list of the sides of a graphical representation of a note"
  (list onset                            ; left
        (+ (pitch object) .5)            ; top
        (+ onset (duration object))      ; right, the offset
        (- (pitch object) .5)))          ; bottom

This was in fact the program used to draw Figure 2, Figure 5 and Figure 9, as the reader who is using the software that comes with this book can check, only the grids and axes were added. Isn't it surprising how such a simple program can achieve this quite complex task? 

(draw (example)) => Figure 2

(draw (original-and-transform
       (example) 
       (pause :duration 1)
       #'transpose 2)) => Figure 5

(draw (original-and-transform
       (example) 
       (pause :duration 1)
       #'mirror "C3")) => Figure 9

In Figure 11 the call diagram is given for this drawing program. It illustrates which functions (methods) call which others. The mapper itself is left out. The direction of the arrows indicate the "calls" or "invokes" relation, gray arrows mean that the call is made indirectly, through a functional argument that was passed to e.g. a mapper. 

Figure 11. Flow of control in the draw program.

Time dependent transformations 
Time dependent transformations can also be defined with the new iterator. As example let's program a gradual tempo change, using a general time-time map.

(defmethod map-time ((object compound-musical-object) onset time-map)
  "Apply a time mapping to the notes of the object"
  (mapc-elements-onsets object #'map-time onset time-map))

(defmethod map-time ((object basic-musical-object) onset time-map)
  "Adjust the duration of a basic musical object according to the time map"
  (let* ((offset (+ onset (duration object)))
         (new-onset (funcall time-map onset))
         (new-offset (funcall time-map offset)))
    (setf (duration object)(- new-offset new-onset))))

(defun make-tempo-change (duration begin end)
  "Return a time-warp function based on begin and end tempo factors" 
  #'(lambda (time)
       (+ (* begin time)
          (* (/ (- end begin)
                (* 2 duration))
             (square time)))))

(defun square (x)
  "Return the square of a number"
  (* x x))

(draw (map-time (repeat (example) 3)
                0
                (make-tempo-change 14 1 .33))) => 

Figure 12. A gradual tempo change.

Before and after methods 
To illustrate how behavior of objects defined in methods can be extended further, let us modify the piano roll drawing by filling each note rectangle with a dark shade. There again we would like to add a piece of code instead of rewriting the draw‑object method. In CLOS methods come in different qualities (we have only used the primary methods unto now). And when more methods are applicable, they can be combined by CLOS using the so called method combination procedures. In our case, the added behavior can operate by side effect, and the extra work to be done can be put in a so called before method or after method. When a draw‑object call is to be executed, the system finds all applicable methods depending on the class of object that the method is applied to and combines them into a whole, the so called effective method. In the standard method combination all before and after methods and the primary method are assembled in their proper order. Any behavior that can be conceptualized as a side effect to take place before or after some main behavior that was already defined can be added in this way. The use of declarative method combination frees the programmer from writing code that searches for methods and calls them in the correct way. We will assume that a primitive paint‑rectangle exists to fill a rectangle with a shade and write a before method for draw‑object. The quality of this methods is indicated with the method qualifier :beforethat appears between the name and the argument list. 

(defmethod draw-object :before ((object note) onset)
  "Fill a graphical representation of a note with a gray color"
  (paint-rectangle (boundary object onset) :light-gray))

 (draw (example)) => 

Figure 13. Shaded piano-roll notation of example.

Note that the source of the main primary method is not changed - it needs not even be available. Thus also predefined behavior e.g. of the Lisp system itself, can be modified. the primary and the before method cannot communicate information, the boundary method is wastefully called twice for each note. By defining an auxiliary function (draw‑boundary), and moving the code that needs the boundary from draw‑object to this new function, this duplicated calculation can be prevented. 

(defmethod draw-object ((object note) onset)
  "Draw a graphical representation of a note (a rectangle)"
  (draw-boundary object (boundary object onset)))

(defmethod draw-boundary ((object note) boundary)
  "Draw a box with a specified boundary (left top right bottom)"
  (draw-rectangle boundary))

(defmethod draw-boundary :before ((object note) boundary)
  "Fill a graphical representation of a note with a gray color"
  (paint-rectangle boundary :light-gray))

Mapping and onset times 
Of course our map‑elements should be extended to be able to maintain the onset times of the objects to which the operation is applied as well, just like mapc‑elements was. 

(defmethod map-elements-onsets ((object compound-musical-object) 
                                combination operation onset &rest args)
  "Combine the results of the operation applied to each element and its onset"
  (apply combination
         (mapcar #'(lambda (element element-onset)
                     (apply operation element element-onset args))
                 (elements object)
                 (onsets object onset))))

Our example that uses the recursor will calculate a flat, non-hierarchical, representation of musical objects, a kind of note list (as is often used in composition systems). 

Event lists 
To facilitate translating our musical objects to other representations that use flat agenda-like structures (like standard MIDI files of score files for Csound or Cmusic), we will write a function that can distill an event list, a list that contains pairs of start times and notes in some format. The as‑event‑list is given a function to calculate the event-list of a basic musical object. Compound musical objects only have to append the note list of their components to yield the event list for that object itself. 

(defmethod as-event-list ((object compound-musical-object) onset basic)
  "Return a list of all the notes of a musical object in some format"
  (map-elements-onsets object #'append #'as-event-list onset basic))

(defmethod as-event-list ((object basic-musical-object) onset basic)
  "Return an event list of this basic musical object in some format"
  (funcall basic object onset))

Let us try a simple version first, which returns an event list in which each event is in the format of a list of an onset and a note. 

(defmethod as-note-list ((object musical-object))
  "Return a note list: a list of (onset note) pairs"
  (as-event-list object 0 #'basic-note-list))

(defmethod basic-note-list ((object note) onset)
  "Return a note list containing this one note"
  (list (list onset object)))

(defmethod basic-note-list ((object pause) onset)
  "Return a empty note list"
  (declare (ignore onset))
  nil)

(pprint (as-note-list (example))) =>
((0 (note :duration 1 :pitch "C3"))
 (1 (note :duration 2 :pitch "D#3"))
 (3 (note :duration 1 :pitch "C#3"))
 (0 (note :duration 4 :pitch "G2")))

Though the function succeeds in extracting al notes and their onsets from a structured musical object, the resulting note list does not necessarily appear in time order, a requirement that is often encountered. One solution is to sort the list after it has been made. Note how the sort function is used with a proper key to retrieve the onset time as the first of each pair, this will act as the index upon which the list is sorted. 

(defmethod as-sorted-note-list ((object musical-object))
  "Return a note list: (onset note) pairs, with onsets in ascending order"
  (sort-event-list  (as-event-list object 0 #'basic-note-list)))

(defun sort-event-list (event-list &key (key #'first))
  "Sort a list of onset-note pairs in order of increasing onsets"
  (sort event-list #'< :key key))

(pprint (as-sorted-note-list (example))) =>
((0 (note :duration 1 :pitch "C3"))
 (0 (note :duration 4 :pitch "G2"))
 (1 (note :duration 2 :pitch "D#3"))
 (3 (note :duration 1 :pitch "C#3")))

However, sorting is an expensive operation, and the event-lists for the components of a sequential musical object will all follow each other in time. Concatenating them together directly is possible, without further sorting. And for parallel objects, assuming that the recursive calls of as-event-list derive event-lists for the components that are sorted themselves, they only have to be merged together, which is a much more efficient processes than sorting. 

(defmethod as-event-list ((object sequential) onset basic)
  "Return a time-sorted list of the notes of a musical object in some format"
  (map-elements-onsets object #'concatenate-events #'as-event-list onset basic))

(defmethod as-event-list ((object parallel) onset basic)
  "Return a time-sorted list of the notes of a musical object in some format"
  (map-elements-onsets object #'merge-events #'as-event-list onset basic))

We need to write the auxiliary functions that combine event lists. Because the predefined merge function only can deal with two lists at a time we will need to extend its use to be able to merge a set of sorted lists. The reduce function is a handy tool in these situations. It can generalize binary operations to the n-ary case. 

(defun merge-events (&rest events)
  "Merge a list of sorted agenda's into one sorted agenda"
  (reduce #'(lambda(list1 list2)
              (merge 'list list1 list2 #'< :key  #'first))
          events))

(defun concatenate-events (&rest events)
  "Concatenate a list of sorted agenda's into one sorted agenda"
  (apply #'concatenate 'list events))

Now event lists will be guaranteed to be sorted at every level directly, and the top function needs not sort the result anymore - a much more efficient solution. 

Promoting event lists to musical objects 
From here we can go to ways: further upward, trying to incorporate some sort of event list notion into our musical objects (and allow transformations, drawing etc.), and further downward towards simple data structures and score file writers, and We will indeed travel both ways, starting with the former. is claimed often that the object oriented style enables programmers to quickly add small pieces of code that takes care of new situations. We will try to do so now by introducing a new musical object and integrating it into the code we have build up. This will also test the flexibility of the classes and methods we defined and form a kind of summary in which the different constructs are used once again. We want to introduce an object with a more absolute way of specifying the onsets of its elements, similar to an event list. Or alternatively, we want to extend our code, which is completely score-based, a bit more towards performance data. Then we need to define an object that can arbitrarily specify the timing of objects as well - not just a strict parallel or sequential order. It will be a compound musical object, but next to a list of elements, it will hold a list of onset times, one for each element. These onset times will be relative to the start time of the object itself, a nice middle ground between absolutely absolute time specifications of flat event lists and completely relative time specifications like in the compound musical objects. We will call his agenda-like structure a collection. It can inherit the elements slot from the abstract compound‑musical‑object class. It can be used in a hierarchical way, nested with another collection or compound musical object and containing other collections or compound musical objects. It resembles the event-lists of e.g. SmOKe (Pope, 1992) closely. 

(defclass collection (compound-musical-object)
  ((onsets :initarg :onsets 
           :documentation "The list of start times of its components"))
  (:documentation "A group of musical objects, starting at specified times"))

Now we can define the constructor function for a collection that makes the call easy to read, by alternating onsets and elements. 

(defun collection (&rest agenda)
  "Return a collection of musical objects starting at specified onsets"
  (let ((onsets (loop for onset in agenda by #'cddr collect onset))
        (elements (loop for element in (rest agenda) by #'cddr collect element)))
  (make-instance 'collection :onsets onsets :elements elements)))

Because the onsets method already exists, and its two-fold arguments list is incompatible with a simple slot accessor, we cannot use this name for the accessor function. That is why the :accessor slot option was missing in the class definition of collection. But we want to write the next method that indirectly makes use of the slot accessor onsets. 

(defmethod show ((object collection))
  "Return a list describing a collection of musical objects"
  (cons 'collection
        (map-elements-onsets object 
                             #'append 
                             #'(lambda(element onset)
                                 (list onset (show element)))
                             0)))

For consistency with the onsets method for the other compound musical objects we want to define the slot reader with an optional argument that specifies the onset time of the object itself. But how can we define a reader for a slot if we cannot access the slot? The slot‑value function is a slot accessor that can always access all slots directly. It should not be used in ordinary programs, because it would disclose that certain data is stored in a slot and disallow easy redesign of the data representation. However, it is allowed to use the slot‑value function when we write a slot accessor function ourselves. It takes the object and the name of a slot (the first item in the slot descriptor list) as its arguments. 

(defmethod onsets ((object collection) &optional (onset 0))
  (loop for element-onset in (slot-value object 'onsets)
        collect (+ element-onset onset)))

The accessor function now take care of the translation between two formats: the internal onset times relative to the start of the object (this is what is stored in the slot) and the external absolute onset times, which are returned as the results of the reader. We can check the constructor function and the show method.

(defun example-collection ()
  "Return a simple collection of 5 notes"
  (collection 0.0 (note :duration 4 :pitch "G2")
              0.3 (note :duration 1 :pitch "C3")
              0.7 (note :duration 2 :pitch "D#3")
              1.7 (note :duration 2 :pitch "B2")
              3.1 (note :duration 1 :pitch "C#3")))

(example-collection)
->
(collection 0.0 (note :duration 4 :pitch "G2")
            0.3 (note :duration 1 :pitch "C3")
            0.7 (note :duration 2 :pitch "D#3")
            1.7 (note :duration 2 :pitch "B2")
            3.1 (note :duration 1 :pitch "C#3"))

Figure 14. A collection.

This collection is shown in Figure 14, a figure whose drawing has to be postponed till we have defined some more methods to work for collections as well, e.g. its duration. The duration of a collection is calculated by returning the offset time of the component that ends last. 

(defmethod duration ((object collection))
  "Return the duration of a collection"
  (map-elements-onsets object #'max #'offset 0))

(defmethod offset ((object musical-object) onset)
  "Return the end time of a musical object"
  (+ onset (duration object)))

(duration (example-collection)) -> 4.1

Because show works well, so will the copy‑object and thus the repeat, canon, and original‑and‑transform functions. Most transformations rely on one of the mappers, and the definition of the mappers for compound musical objects rely only on the availability of an onsets method to calculate proper onset times for the components. This method, which was written by us for sequential and parallel objects, was later defined as a custom-made slot accessor for collections. Thus the old definitions of the mappers will turn out to work just fine for collections.  And transpose and mirror etc. will work well for collections automatically, because they only need a well defined iterator, as will draw. Thus figures can be drawn immediately to check the programs. 

All our compound musical objects can be converted to a collection. In this sense a collection is a more general data structure. However, a collection tells less about the time order of the components, it is less specific. The program to translate compound musical objects into a collection only has to call itself recursively on the components and calculates the onset times with the onsets method. Note that the resulting structure can hierarchically contain collections of collections. 

(defmethod as-collection ((object basic-musical-object))
  "Return the basic musical object"
  object)

(defmethod as-collection ((object compound-musical-object))
  "Translate any compound musical object into a collection"
  (apply #'collection 
         (map-elements-onsets object 
                              #'append 
                              #'(lambda (object onset)
                                  (list onset (as-collection object)))
                              0)))

(as-collection (example))
->
(collection 0 (collection 0 (note :duration 1 :pitch "C3")
                          1 (note :duration 2 :pitch "D#3")
                          3 (note :duration 1 :pitch "C#3"))
            0 (note :duration 4 :pitch "G2"))

Now we come back to our event list functions: the translation of a musical object into a flat collection of notes is best done using these utilities. The argument list needed for the collection constructor (a list of alternating onsets and objects) can be constructed from a note list (a list of lists of onset and object) by applying the append function to it. 

(defmethod as-note-collection ((object musical-object))
  "Translate any musical object into a flat collection of notes"
  (apply #'collection 
         (apply #'append (as-note-list object))))

(as-note-collection (example))
->
(collection 0 (note :duration 1 :pitch "C3")
            0 (note :duration 4 :pitch "G2")
            1 (note :duration 2 :pitch "D#3")
            3 (note :duration 1 :pitch "C#3"))

Event arrays 
Now we will develop the notion of event lists further downward. In general it is not a good idea to use lists for large flat data structures, as our note lists are. In those cases an array is much better for space and time efficiency. A variant of the event list builder that delivers arrays is easy to write. When we make merge‑events and concatenate‑events behave a bit smarter, they cab infer the type of result needed from the type of their arguments, are they arrays, then they deliver arrays, are they lists, then they yield lists. In that way the basic event list is responsible for the type of result returned by as-event-list.

(defun merge-events (&rest events)
  "Merge a sequence of events into one"
  (let ((type (if (listp (first events)) 'list 'vector)))
    (reduce #'(lambda(seq1 seq2)
                (merge type seq1 seq2 #'< :key  #'first))
            events)))

(defun concatenate-events (&rest events)
  "Concatenate a sequence of events into one"
  (let ((type (if (listp (first events)) 'list 'vector)))
    (apply #'concatenate type events)))

Below it is shown how indeed the return a basic note array triggers the result and all intermediate sequences to be of the type array. 

(defmethod as-note-array ((object musical-object))
  "Return a note list: a list of (onset note) pairs"
  (as-event-list object 0 #'basic-note-array))

(defmethod basic-note-array ((object note) onset)
  "Return a note list containing this one note"
  (vector (list onset object)))

(defmethod basic-note-array ((object pause) onset)
  "Return a empty note list"
  (declare (ignore onset))
  (vector))

(pprint (as-note-array (example))) =>
#((0 (note :duration 1 :pitch "C3"))
  (0 (note :duration 4 :pitch "G2"))
  (1 (note :duration 2 :pitch "D#3"))
  (3 (note :duration 1 :pitch "C#3")))

Writing score files 
We can use the function to output our musical objects in formats for other systems, e.g. Standard MIDI files, the Cmusic score file format and other music representation systems that use some form of non-hierarchical note lists. Assuming that these formats all need a specific file header and trailer and a specific way to write musical data and times, we can write a general function that writes a musical object as a score file, abstracting from the details. It is given some extra arguments that set the file's type and creator (such that applications that e.g deal with MIDI files will recognize one written by us and double clicking the file will launch the appropriate appreciation). 

(defmethod write-score-stream ((object musical-object)
                               &key basic
                               header-writer 
                               trailer-writer
                               event-writer
                               (stream t)
                               &allow-other-keys)
  "Output a musical object to a stream, in a score file format"
  (loop with array = (as-event-list object 0  basic)
        initially (funcall header-writer stream)
        for (time event) across array 
        do (funcall event-writer event time stream)
        finally (funcall trailer-writer stream)))

(defmethod write-score-file ((object musical-object)
                             &rest args
                             &key
                             (file (choose-new-file-dialog))
                             (file-type :TEXT)
                             (element-type 'character)
                             (file-creator :CCL2)
                             &allow-other-keys)
  "Save a musical object in a score file"
  (with-open-file (stream file 
                          :direction :output
                          :if-exists :supersede
                          :element-type element-type)
    (apply #'write-score-stream object :stream stream args))
  (set-mac-file-type file file-type)
  (set-mac-file-creator file file-creator))

A test of the score writer program can be made by supplying some trivial writers. 

(write-score-file (example)
                  :basic #'basic-note-array
                  :header-writer #'(lambda(stream)
                                     (format stream "~%; score"))
                  :trailer-writer #'(lambda(stream)
                                      (format stream "~%; end"))
                  :event-writer #'(lambda(event time stream)
                                    (format stream "~%~A: ~S" 
                                            time (show event))))
=>
; score
0: (note :duration 1 :pitch "C3")
0: (note :duration 4 :pitch "G2")
1: (note :duration 2 :pitch "D#3")
3: (note :duration 1 :pitch "C#3")
; end

writing Cmusic score files 
The score file writer can be used to produce the Cmusic score file format. Cmusic is a flexible language for software sound synthesis. It belongs to the family of Music-V and Csound languages, has a similar representation of instruments and score files, see (Pope, 1993). The parameters that need to be listed in the score file depend on the instrument definition in use. We will assume a that a function that delivers the parameter values for a note is passed as argument to the score writer. In that way the program will be useful for different instruments. To print a proper heading we assume that the names of the parameters are supplied as an argument as well. 

(defmethod write-Cmusic-score-file ((object musical-object) 
                                    parameter-names
                                    parameter-values
                                    &optional (file (choose-new-file-dialog)))
  "Store a musical object in a Cmusic score file"
  (write-score-file object
                    :basic #'basic-note-array
                    :header-writer (make-Cmusic-header-writer parameter-names)
                    :trailer-writer #'Cmusic-trailer-writer
                    :event-writer (make-Cmusic-note-writer parameter-values)
                    :file file))

(defmethod make-Cmusic-header-writer (names)
  "Return a function that can write the initial part of a Cmusic score file"
  #'(lambda (stream)
      (format stream "~%{ Cmusic note list }~%{")
      (loop for i from 1 to (length names) do (format stream "~Ap~A" #\tab i))
      (print-tab-list "}~%{" names "}" stream)))

(defun print-tab-list (before items after stream)
  "Print a list of items separated by tabs, with something to open and close it"
  (loop initially (format stream before)
        for item in items 
        do (format stream "~A~A" #\tab item)
        finally (format stream after)))

(defmethod Cmusic-trailer-writer (stream)
  "Write the final part of a Cmusic score file"
  (format stream "~%ter;"))

(defmethod make-Cmusic-note-writer (note-values)
  "Return a function that can write a note to a Cmusic score file"
  #'(lambda (object time stream)
      (print-tab-list "~%note" (funcall note-values object time) ";" stream)))

To test the code we need to commit ourselves to a particular instrument definition. We choose a simple fm instrument (Chowning, 1973) that is given in (Pope, 1993 page 31, figure 7) The parameters for this instrument are its onset, duration, amplitude and frequency plus some parameters that determine its timbre: the carrier to modulation frequency ratio, the modulation index, the attack time, the decay time, the modulation index attack time and the modulation index decay time. The latter parameters are all given constant values in our program, as is the amplitude parameter. However, it will be easy to expand the note definition to be able to store more attributes per note, as we will do later. 

(defmethod fm-instrument-parameters ()
  "Return the names of the parameters of a simple FM instrument (Chowning, 1973)"
  '("O" "fm1" "dur" "amp" "freq" "c/m" "ind" "att" "dec" "iAtt" "iDec"))

(defmethod fm-instrument-values ((object note) time)
  "Return the values of the parameters for a simple FM instrument"
  (list time "fm1" (duration object) "-6dB"
        (format nil "~AHz" (round (frequency object)))
        1 1 .2 .2 .05 .4))

(write-Cmusic-score-file (example) 
                         (fm-instrument-parameters)
                         #'fm-instrument-values) 
=>
{ Cmusic note list }
{      p1    p2    p3   p4      p5      p6   p7   p8    p9    p10    p11}
{      O     fm1   dur  amp     freq    c/m  ind  att   dec   iAtt   iDec}
note   0     fm1   1    -6dB    440Hz   1    1    0.2   0.2   0.05   0.4;
note   0     fm1   4    -6dB    330Hz   1    1    0.2   0.2   0.05   0.4;
note   1     fm1   2    -6dB    523Hz   1    1    0.2   0.2   0.05   0.4;
note   3     fm1   1    -6dB    466Hz   1    1    0.2   0.2   0.05   0.4;
ter;

This program can easily be adapted for other types of instrument definitions in Cmusic. An adapting to other formats like Csound, Music-V, Cmix is often just an issue of changing textual details. Thus we have opened up the use of programs that allow control of the synthesis of the sound itself. Later we will treat the translation of our musical objects into MIDI using the same note list facilities. 

Changing structure 
In our transformations the main operation was applied to notes only, the structure of the musical object remained invariant. This will no longer be the case in the next example: a retrograde transformation for musical objects that reverses each sequential (sub-)structure. The result of applying this transform is illustrated in Figure 15. This transformation destructively modifies only the sequential objects encountered, the others are visited but left untouched. 

(defmethod retrograde ((object parallel))
  "Reverse all components of a musical object in time, recursively"
  (mapc-elements object #'retrograde))

(defmethod retrograde ((object basic-musical-object))
  "Do nothing"
  object)

(defmethod retrograde ((object sequential))
  "Reverse all components of the object in time"
  (setf (elements object)
        (reverse (elements object)))
  object)

(draw (original-and-transform
       (example) 
       (pause :duration 1)
       #'retrograde)) => 

Figure 15. The example and a reversal.

Applying transformations on parts of musical objects 
We have only applied our transformations at the top level of our musical objects, from the outside so to say. However, there is no reason why they shouldn't be used on objects nested (deeply) inside other objects. In Figure 16 we select a part of the example, and apply a mirror operation to it. A let is used to bind the musical object and its part for reference by the operation and as value to return. 

(draw (let* ((object (example))
             (part (first (elements object))))
        (mirror part "C3")
        object)) => 

Figure 16. A reflection of a part of an object.

But accessing sub-objects by the position in which they happen to appear in the elements slot of compound object, is not such a good idea. It would be much nicer if we could access them by some property. A general function that searches through an object to find a part that satisfies a certain predicate, is easy to write. To make the predicates simple to write we will add a type argument to the function: only objects that satisfy the type will be considered. As types we can use the familiar names of classes. 

(defmethod find-musical-object ((object compound-musical-object) 
                                type predicate &optional (onset 0))
  "Return the object or the part that satisfies the predicate at its onset time"
  (if (and (typep object type) 
           (funcall predicate object onset))
    object
    (loop for element in (elements object)
          for time in (onsets object onset)
          thereis (find-musical-object element type predicate time))))

(defmethod find-musical-object ((object basic-musical-object) 
                                type predicate &optional (onset 0))
  "Return the object when it satisfies the predicate at its onset time"
  (when (and (typep object type)
             (funcall predicate object onset))
    object))

(draw 
 (let* ((object (example))
        (part (find-musical-object object 
                                   'note
                                   #'(lambda (object time)
                                       (= (pitch object) 
                                          (pitch-name-to-midi-number "C#3"))))))
   (when part (transpose part 3))
   object)) => 

Figure 17. A transposition of a part of an object.

In the example above the first object encountered that satisfies the predicate will be returned by find‑musical‑object. In contrast we could collect all objects that satisfy a constraint and apply a transformation to all of them. 

(defmethod find-musical-objects ((object compound-musical-object) 
                                 onset type predicate)
  "Return all parts that satisfy the predicate at their onset time"
  (let ((found (map-elements-onsets object 
                                    #'append 
                                    #'find-musical-objects 
                                    onset 
                                    type predicate)))
    (if (and (typep object type)
             (funcall predicate object onset))
      (cons object found)
      found)))

(defmethod find-musical-objects ((object basic-musical-object) 
                                 onset type predicate)
  "Return the object in a list when it satisfies the predicate at its onset time"
  (when (and (typep object type)
             (funcall predicate object onset))
    (list object)))

More drawing 
Structure is paramount in our examples, but it is not even visible in the piano-roll graphics. With our new iterator, which can support actions for any type of musical object, it is easy to make up for that omission. We can visualize the structure of a musical object in the form of a box extending for the duration of the whole object and covering its pitch range. A graphical representation cannot easily be drawn for rests nor for compound objects containing only rests. So we will not draw those objects in the hope that later an idea for drawing rests too will arise. For simplicity we also ignore for the moment that one might want to properly nest the boxes graphically - enclosing boxes will be drawn here over each other. The main addition that has to be made to the drawing program is the calculation of boundaries for compound musical objects. It has somehow to combine the boundaries of its components into a larger box that covers them all. This entails minimization and maximization of times and pitches, which can be done easily using the loop macro facilities. Since the boundary method may return nil for certain kinds of objects (like rests), we have to carefully remove these before the list of boundaries is combined into one.  Furthermore, we have to protect the draw‑boundary method to prevent drawing when it is called with a nil boundary. The draw‑object method may now draw the boundary of any musical object, for compound musical elements, an after method takes care of drawing the objects that it encloses. 

(fmakunbound 'draw-object)
(fmakunbound 'boundary)

(defmethod draw ((object musical-object))
  "Draw a piano roll for a musical object"
  (draw-object object 0))

(defmethod draw-object ((object musical-object) onset)
  "Draw a box around a musical object"
  (draw-boundary object (boundary object onset)))

(defmethod draw-object :after ((object compound-musical-object) onset)
  "Draw the elements of a compound musical object"
  (mapc-elements-onsets object #'draw-object onset))

(defmethod boundary ((object note) onset)
  "Return a list of the sides of a graphical representation of a note"
  (list onset                            ; left
        (+ (pitch object) .5)            ; top
        (offset object onset)            ; right
        (- (pitch object) .5)))          ; bottom

(defmethod boundary ((object pause) onset)
  "Return nil, a rest cannot be drawn"
  nil)

(defmethod boundary ((object compound-musical-object) onset)
  "Return a list of the sides of a box enclosing a compound musical object"
  (map-elements-onsets object #'combine-boundaries #'boundary onset))

(defmethod combine-boundaries (&rest boundaries)
  "Combine a list of boundary boxes into one box that encloses them all"
  (let ((true-boundaries (remove nil boundaries)))
    (when true-boundaries
      (loop for (x1 y1 x2 y2) in true-boundaries
            minimize x1 into left
            maximize y1 into top
            maximize x2 into right
            minimize y2 into bottom
            finally (return (list left top right bottom))))))

(defmethod draw-boundary ((object musical-object) boundary)
  "Draw a box with a specified boundary (left top right bottom)"
  (when boundary (draw-rectangle boundary)))

(defmethod draw-boundary :before ((object note) boundary)
  "Fill a graphical representation of a note with a gray color"
  (paint-rectangle boundary :light-gray))

(draw (repeat (example))) => 

Figure 18. A musical object with its structure visualized.

The call hierarchy is given in Figure 19. We will show later how this structure can be simplified a bit. 

Figure 19. The complex flow of control while drawing.

Setf methods 
Let us recall how slots of an object can be updated by setf, changing the duration of a note. 

(let ((object (note :duration 1 :pitch "C3")))
  (setf (duration object) 2)
  object)
->
(note :duration 2 :pitch "C3")

It may be necessary to change the duration of compound musical objects as well, compressing or expanding all their elements. It is not so difficult to write a procedure that establishes that, called say set‑duration. But now the programmer has to remember that durations of basic musical objects are changed using setf on the duration slot, and that durations of compound musical objects are changed with a call to the set‑duration procedure. It is clear that this situation is not very elegant. The more so because it may have been the result of a low level implementation decision and, worse, it may be one that has to change in future modifications of the underlying data representation. The uniform way of slot access and method calls in reading slots provided a solution that can be generalized to the writing of slots as well. In the definition of the basic‑musical‑object class an accessor was asked for the duration slot. This made the system generate a duration method to read the slot plus a method to set the duration of that slot. The last method is named (setf duration). This is an extension of the common notion of function names and method names that only allow symbols as names. Upon encountering a (setf (duration Y) Y) the method (setf duration) is applied to the arguments Y and X. The system supplied the (setf duration) method specialized for basic musical objects, because it was asked to do so in the slot descriptor or duration. The principle of a programmable standard environment applies here as well. And we can define a so called setf method ourselves, specialized for compound musical objects. It will be called if in the code a (setf (duration X) ..) expression, which looks just like a slot update, is evaluated where X is a compound musical object. This method needs to calculate a stretch factor (a ration between desired and actual duration) and elongates (or shrinks) the duration of all its elements with that factor. The use of mutual recursion between the (setf duration) method and the stretch transformation comes quite natural here. 

(defmethod (setf duration) (value (object compound-musical-object))
  "Set the duration of a compound musical object by stretching its components"
  (let ((factor (/ value (duration object))))
    (mapc-elements object #'(lambda(object)(stretch object factor)))
    value))

(defmethod stretch ((object musical-object) factor)
  "Multiply the duration of a musical object by a factor"
  (setf (duration object) (* (duration object) factor))
  object)

Note the confusing fact that the order in the argument list of the setf method is first the value and then the object: the reverse from the order in which they appear in the call to setf. It is important to let a user-written setf method return its value, such that we may rely on it to behave the same as ordinary assignments. The stretch transformation that changes the duration of a musical fragment is not just an auxiliary function for the (setf duration) method, it is useful in itself. In Figure 20 it is applied to the example to yield a fast version. 

(draw (original-and-transform
       (example) 
       (pause :duration 1)
       #'stretch .5)) => 

Figure 20. The example and a shortened version.

The definition of a modification macro for multiplications makes the code look more like the other transformations (e.g. mirror) 

(define-modify-macro multf (args) * "Multiply a generalized variable")

(defmethod stretch ((object musical-object) factor)
  "Multiply the duration of a musical object by a factor"
  (multf (duration object) factor)
  object)

Setting onsets and durations of collections 
Setf method can be made to have more arguments, e.g. it would be natural to create a writer for the onsets slot of a collection as having an optional argument that represents the onset time of the object itself, similar to the reader. 

(defmethod (setf onsets) (onsets (object collection) &optional (onset 0))
  "Set the onset times of a collection, relative to the onset of the object"
  (setf (slot-value object 'onsets)
        (loop for element-onset in onsets collect (- element-onset onset))))

The setf method for duration defined for compound musical objects works for collections: it shrinks or stretches the duration of all its components. But after that, the onsets of the components, which are stored as data in a collection, need to adapted as well. An after method is the proper solution in such a case. Note that this illustrates again the usefulness of uniform slot assignment and method calls: the after method mechanism is just as easily used for extra side effects that have to take place after an assignment. 

(defmethod (setf duration) :after (duration (object collection))
  "Update the onsets of the components after their duration has been changed"
  (setf (onsets object)
        (loop with factor = (/ duration (duration object))
              for onset in (onsets object) 
              collect (* onset factor))))

Figure 21. The collection and a shortened version.

To make retrograde work for collections as well, a method definition needs to be added. It calculates the time left from the end of each component till the end of the whole collection. These times form the new onsets of the components of a retrograde collection. The result of this transformation applied to a collection is shown in Figure 22. 

(defmethod retrograde ((object collection))
  "Reverse all components of the object in time"
  (setf (onsets object)
        (map-elements-onsets object 
                             #'(lambda(&rest elements) elements) 
                             #'(lambda (element element-onset)
                                 (- (duration object)
                                    (offset element element-onset)))
                             0))
  object)

Figure 22. The collection and its retrograde.

Setting the frequency of notes 
Setf methods give us the mechanism to solve that problem that we can ask for both the pitch and the frequency of a note but only set the former. The auxiliary function frequency‑to‑pitch is given in the full code listing at the end of this chapter. 

(defmethod (setf frequency) (value (object note))
  "Set the frequency of a note"
  (setf (pitch object)
        (frequency-to-pitch value))
  value)

In an update of the frequency of a note, the corresponding pitch is stored in the slot. 

(let ((object (note :pitch "C3")))
  (setf (frequency object) 440)
  object)
 ->
(note :duration 1 :pitch "A3")

A rather beautiful consequence of these simple definition of the frequency and (setf frequency) method is the fact that all modification macro's work (like incf and mirrorf) consistently when applied to the frequency of a note, translating the pitch of a note to a frequency, modifying that number in the frequency domain (which is an essentially different operation) and translating the result back to proper pitches. 

(let ((object (note :pitch "A1")))
  (format t "~%~S ; ~A Hz" (show object) (round (frequency object)))
  (mirrorf (frequency object) 440) ; A3
  (format t "~%~S ; ~A HZ" (show object) (round (frequency object))))
=>
(note :duration 1 :pitch "A1") ; 110 Hz
(note :duration 1 :pitch "G4") ; 770 Hz

(let ((object (note :pitch "A1")))
  (format t "~%~S ; ~A Hz" (show object) (round (frequency object)))
  (mirrorf (pitch object) "A3") ; 440 Hz
  (format t "~%~S ; ~A Hz" (show object) (round (frequency object))))
=>
(note :duration 1 :pitch "A1") ; 110 Hz
(note :duration 1 :pitch "A5") ; 1760 Hz

We will show later how we can modify CLOS' initialization mechanism, to be able to create a note from scratch with a specification of its frequency using an initialization keyword. 

An around method can modify the result of its primary method 
To add some more examples we will rewrite the show method for compound musical objects, because the showing of either sequential and parallel musical objects involves showing the elements of the object, and thus duplicates code, but now only partially. The old definitions were: 

(defmethod show ((object sequential))
  "Return a list describing a sequential musical object with its components"
  (cons 'sequential  
        (loop for element in (elements object) 
              collect (show element))))

(defmethod show ((object parallel))
  "Return a list describing a parallel musical object with its components"
  (cons 'parallel  
        (loop for element in (elements object) 
              collect (show element))))

However, consing either the atom sequential or parallel in the result of this calculation cannot be separated from the other code in a before method because it cannot be done by side effect. However, there is another way to use method combination in this case, by defining a so called around method. Let us first assume that the primary show method for compound musical object constructs only a partial result: the list of the shown elements. 

(fmakunbound 'show)

(defmethod show ((object compound-musical-object))
 "Return a list describing each component of a compound musical object"
  (loop for element in (elements object) 
        collect (show element)))

If we define another method for show, specialized for compound musical objects as well, but with an added qualifier :around, that method will be called as the applicable method, instead of the primary one. However the around method can actively ask for the primary method to run and obtain its result by evaluating the form (call‑next‑method). In this sense the method combination is not declarative anymore: calling the primary method is under direct control of the program. When the (call‑next‑method) form is evaluated, the primary method is called with the same arguments as the around method itself. In the show around method this result used and completed by consing one extra atom in it. Thus all that the showing of a sequential or a parallel object entails specifically is consing another atom in the result of the primary show method for any compound musical object. 

(defmethod show :around ((object sequential))
  "Add the atom sequential to the descriptions of the object's components"
  (cons 'sequential (call-next-method)))

(defmethod show :around ((object parallel))
  "Add the atom parallel to the descriptions of the object's components"
  (cons 'parallel (call-next-method)))

Now the two show around methods capture the behavior that is different for sequential and parallel objects, the shared primary method captures the behavior that is identical, a neat separation of concerns. We could leave the show method specialized for collections, as it is, shadowing the less specific method for compound musical objects. However, with a bit of extra work, it can make use of the result of the primary method, and insert the onset times of its elements at the proper places. 

(defmethod show :around ((object collection))
  "Return a list describing a collection of musical objects"
  (cons 'collection
        (loop for result in (call-next-method)
              for onset in (onsets object)
              collect onset
              collect result)))

In the same way around methods specialized for notes and rests can modify the result of a simple primary method for basic musical objects to only returns a description of the duration attribute. 

(defmethod show ((object basic-musical-object))
  "Return a list showing the duration attribute"
  (list :duration (duration object)))

(defmethod show :around ((object pause))
  "Add the name 'pause' to the show"
  (cons 'pause (call-next-method)))

(defmethod show :around ((object note))
  "Add the name 'note' and the pitch attribute to the show"
  (cons 'note (append (call-next-method) 
                      (list :pitch (midi-number-to-pitch-name (pitch object))))))

In Figure 23 the class hierarchy for musical objects is shown annotated with the show methods specialized for them. In general there may be more around methods applicable when a method is applied to an object. They can all be found in the path from the node that represents the class of the object, searching upwards. And they may call each other in turn (starting from the most specific, the lowest one in the graph) before finally the least specific around method calls the next method. In that case the primary one will be run (the most specific primary one, in case their are more candidates). The situation is even more complicated because before and after the primary method is invoked, all before and after methods are run. Luckily in our show method the situation is quite clear with for each type of object only one around method can call only one primary method. 

Figure 23. The show methods for different kinds of musical objects.

Before committing to this specific program for showing notes it is time to question (and correct) the limited concept of a note that was used unto now. 

More note attributes 
Let us invent some new attributes for notes, just to see the consequences for the programs that we have written. We choose to add an instrument slot (a string naming the instrument) and a loudness slot (intended to hold a value between 0 and 1). 

(defclass note (basic-musical-object)
  ((pitch :accessor pitch 
          :initarg :pitch 
          :initform 60 
          :documentation "Pitch in MIDI numbers")
   (instrument :accessor instrument 
          :initarg :instrument 
          :initform nil 
          :documentation "Instrument name")
   (loudness :accessor loudness 
          :initarg :loudness 
          :initform 1 
          :documentation "Loudness in [0,1] range"))
  (:documentation "A note with pitch, loudness and instrument attributes"))

The external representation of a note has to be updated to show the new attributes too. We will not show them when they have their default value.

(defmethod show :around ((object note))
  "Add the name 'note' and the attributes to the show"
  (cons 'note (append (call-next-method) 
                      (list :pitch (midi-number-to-pitch-name (pitch object)))
                      (unless (equal (loudness object) 1.0) 
                        (list :loudness (loudness object)))
                      (unless (null (instrument object))
                        (list :instrument (instrument object))))))

We need a new example, a small musical object that contains notes that specify these new attributes 

(defun note-attributes-example ()
  (parallel 
   (sequential 
    (note :duration 1 :pitch 60 :instrument "Piano")
    (note :duration 2 :pitch 63 :loudness .4 :instrument "Piano")
    (note :duration 1 :pitch 61 :loudness .2 :instrument "Piano"))
   (note :duration 4 :pitch 55 :loudness .7 :instrument "Cello")))

We may want to show the attributes in the pianoroll notation, for that we change the before boundary method, to have the size of the note reflect the loudness, and we change the before draw‑boundary method, to select a shade of gray to reflect the instrument. 

(defmethod boundary ((object note) onset)
  "Return a list of the sides of a graphical representation of a note"
  (let ((height (/ (loudness object) 2.0)))
    (list onset                            ; left
          (+ (pitch object) height)        ; top
          (offset object onset)            ; right
          (- (pitch object) height))))     ; bottom

(defmethod draw-boundary :before ((object note) boundary)
  "Paint a note with a shade reflecting its instrument"
  (paint-rectangle boundary (instrument-shade (instrument object))))

(defmethod instrument-shade (instrument)
  (or (second (assoc instrument
                     '(("Piano" :light-gray)
                       ("Cello" :black)
                       ("Bell" :white)
                       ("Voice" :dark-gray))
                     :test #'string-equal))
      :light-gray))

(draw (note-attributes-example)) => 

Figure 24. An example with more note attributes.

In a primary method the next method may be called as well, but please don't 
The call‑next‑method form can be used in any primary method too (not only in around methods), calling the most specific method shadowed by this one. This fact is considered an invitation to programmers to write ugly code for which actual method combination may become a hard puzzle of control-flow, especially since the decision whether a call to call‑next‑method is done depends on the actual computation, not just on the program text. Therefore it is better to avoid this use of call‑next‑method. 

An around method can prevent the execution of its primary method 
The fact that an around method can decide whether a primary method need to be called is a mechanism that can e.g. be used for wrapping checks on arguments around methods. Let's take for instance the stretch transformation that may not be called with a negative or zero stretch factor and needs not be called with a unity factor. These protections can be implemented in a simple around method. 

(defmethod stretch :around ((object musical-object) factor)
  "Protect the stretch transformation from illegal or trivial arguments"
  (cond ((<= factor 0) (warn "Cannot stretch with factor ~A" factor) object)
        ((= factor 1) object)
        (t (call-next-method))))

A similar kind of protection is needed for the draw‑rectangle method, which may not be called with a nil boundary. This was implemented in the draw‑boundary method as follows: 

(defmethod draw-boundary ((object musical-object) boundary)
  "Draw a graphical representation of a musical object (an enclosing rectangle)"
  (when boundary (draw-rectangle boundary)))

But an around method for draw‑boundary suffices, it would make this method more robust. 

(defmethod draw-boundary :around ((object musical-object) boundary)
  "Draw a graphical representation of a musical object (an enclosing rectangle)"
    (when boundary (call-next-method)))

And the primary draw‑boundary method may now call draw-rectangle without checking the argument that it will pass to it, it will never be called itself with a nil boundary. 

(defmethod draw-boundary ((object musical-object) boundary)
  "Draw a graphical representation of a musical object (an enclosing rectangle)"
  (draw-rectangle boundary))

An around method can change the arguments of its primary method 
Consider a protection that would be desirable for modifications of the pitches of musical objects. By mistake a transposition or mirror operation can easily create pitches outside a permitted range. E.g. for MIDI the pitches are numbered from 0 to 127. When we can protect the (setf pitch) method somehow to never update a pitch to a value outside the range, all the programs that change pitches, and use this accessor method will be protected automatically. A first trial might look like: 

(defmethod (setf pitch) :around (value (object note))
  "Call the primary method with a pitch that is kept within [0,127]"
  (cond ((< value 0) (setf (pitch object) 0))
        ((> value 127) (setf (pitch object) 127))
        (t (call-next-method))))

But this is ineffective, and quite dangerous, because in the first two clauses a new assignment is done from scratch, which will call the around method (redundantly) a second time. And a programming error made here might easily lead to an infinite loop. There is a better solution. A call‑next‑method form may include new arguments to which the primary method has to be applied. In this way an around method can modify arguments, before they are being passed on. This is a fine way to wrap a protection around an assignment. 

(defmethod (setf pitch) :around (value (object note))
  "Call the primary method with a pitch that is kept within [0,127]"
  (call-next-method (clip 0 value 127) object))

(defun clip (min value max)
  "Return the value, clipped within [min,max]"
  (cond ((< value min) min)
        ((> value max) max)
        (t value)))

An around method can ensure proper return values of the primary method 
A next application of around methods will be given in which only the side effects of the primary method is needed. In those cases an around method can calculate an appropriate value to return. This is a proper way to enforce correct return values whenever the set of primary methods is intended to be extendible. In our case we had to remember that the transformations had to always return the object that it was applied to, even though it achieved its working by side-effect. Now this decision can be represented directly in the program code, and the primary methods don't have to bother. 

(defmethod transpose :around ((object musical-object) interval)
  "Always return the object that was subject to the transposition"
  (declare (ignore operation onset))
  (call-next-method)
  object)

An around method can merge programs 
Looking at the control flow of our drawing program, it can be seen that, though very elegant, it is unacceptably inefficient: drawing a musical object means calculating a boundary, descending recursively through its whole structure, calculating boundaries of every sub-object encountered and combining them at every level. Then the largest enclosing box is drawn, and the process continues drawing the first sub-object, for which again all structure beneath it is visited, boundaries are calculated etc. This amounts to an explosive number of calls to the boundary method as can be seen when the boundary function is traced. A simple count of the number of invocations of the boundary method can be obtained with some program scaffolding.

(defvar *boundary-call-count* 0)

(defmethod boundary :after ((object musical-object) onset)
  (declare (ignore onset))
  (incf *boundary-call-count*))

(defmethod draw :around ((object musical-object))
  (let ((*boundary-call-count* 0))
    (call-next-method)
    (format t "~%~A calls to calculate boundaries" 
            *boundary-call-count*)))

(object-count (example)) -> 6

(draw (example))
=> 14 calls to calculate boundaries

(object-count (sequential (example) (example))) -> 13

(draw (sequential (example) (example)))
=> 41 calls to calculate boundaries

For a simple structure like our example, which contains only 6 object, already 14 calls to calculate their boundary were made. The structure consisting of twice the example in succession (13 objects) calculated note boundaries 41 times. One might get away with this inefficient implementation in a rapid prototype for small toy examples, but it is not acceptable in serious applications. The natural way to proceed would be to merge the draw‑boundary and the boundary program into one, because after a boundary has been calculated, it might as well be drawn immediately, before passing it upward for calculation and drawing of enclosing boundaries. Program merging always is an error-prone activity, and often we are stuck between the choice of a neatly decomposed, but inefficient implementation and a fast, but difficult to maintain, one in which the control structure for two different calculations are entangled. However, CLOS can sometimes do the merging for us if we add an around method. In our case an around method for boundary can, after the invocation of the main boundary method, call draw‑boundary. 

(defmethod boundary :around ((object musical-object) onset)
  "Draw the boundary as side effect of its calculation"
  (declare (ignore onset))
  (let ((boundary (call-next-method)))
    (draw-boundary object boundary)
    boundary))

The program structure becomes very simple: drawing an object is as easy as asking for its boundary, and draw‑object has become superfluous 

(fmakunbound 'draw-object)

(defmethod draw ((object musical-object))
  (boundary object 0))

One boundary around method effectively ties together the draw‑boundary and the boundary calculation at every level: and the link between these programs is defined (and maintained at this one central place, without sacrificing modularity, a true feat). We can check now that indeed the explosive number of call to the boundary method is no longer present. 

(draw (example))
=> 6 calls to calculate boundaries

(draw (sequential (example) (example)))
=> 13 calls to calculate boundaries

The program's complexity is now O(n) (read: in the order of n) it's execution time has a linear relation with the number of objects (n) in the input. To say it in other words, a musical object of double size will take twice as long to draw, and we cannot expect to get any better than that, can we? In Figure 25 it is apparent as well that the control structure has improved when compared to Figure 19. 

Figure 25. The improved flow of control while drawing.

To get rid of our scaffolding without upsetting the rest of the program we show a quick trick, useful in programming: we re-define the after method to do nothing, and the around method such that it makes itself transparent. 

(defmethod boundary :after ((object musical-object) onset))
(defmethod draw :around ((object musical-object)) (call-next-method))

An around method can modify the results returned by the primary method 
Now we can try a rough solution to the problem that the boundary boxes in our piano-roll drawings do not nest properly. It is obtained by enlarging the boundary enclosing a compound musical object a bit before it is drawn and returned one level up. This is again an example of the use of an around method that amends the return value of the primary method. The result of this kind of drawing is shown in Figure 26 that can be compared to the original Figure 20 which had no nesting. Of course this solution is not yet a final one because boundary boxes can easily overlap when musical objects are close in time or pitch. A better solution will be given later. 

(defmethod boundary :around ((object musical-object) onset)
  "Draw the boundary as side effect of its calculation"
  (declare (ignore onset))
  (let ((boundary (enlarge-boundary object (call-next-method))))
    (draw-boundary object boundary)
    boundary))

(defmethod enlarge-boundary ((object compound-musical-object) boundary 
                             &optional (horizontal .15)(vertical .2))
  "Add margins to a compound boundary box"
  (mapcar #'+ 
          boundary
          (list (- horizontal) vertical horizontal (- vertical))))

(defmethod enlarge-boundary ((object basic-musical-object) boundary 
                             &optional (horizontal .15)(vertical .2))
  "Add no margins"
  (declare (ignore horizontal vertical))
  boundary)

(draw (original-and-transform (example) 
                              (pause :duration 1)
                              #'stretch .5)) => 

Figure 26. New drawing of the example and a shortened version.

Naming musical objects 
In searching through musical objects, the compound objects can only be distinguished by onset time and duration. In modeling musical knowledge using our classes, it would be nice to be able to distinguish, say a sequential structure representing a bar, from one representing the parts of a piece. Instead of incorporating much more musical knowledge into our class hierarchy we will introduce names for musical objects. These names can be the link to knowledge that will be kept outside the system itself. 

Instead of defining a new class for named object we add the name slot to the existing class definition for compound musical objects. 

(defclass compound-musical-object (musical-object)
  ((elements :accessor elements 
             :initarg :elements 
             :documentation "The components")
   (name :accessor name :initarg :name :initform nil))
  (:documentation "A named group of musical objects ordered in time"))

To allow the name slot to be filled when we create a compound object, we define some new creators that are similar to the old, but have a name as first argument and pass it to make-instance. 

(defun S (name &rest elements)
  "Return a named sequential musical object"
  (make-instance 'sequential :name name :elements elements))

(defun P (name &rest elements)
  "Return a named parallel musical object"
  (make-instance 'parallel :name name :elements elements))

(defun C (name &rest agenda)
  "Return a named collection" 
  (let ((onsets (loop for onset in agenda by #'cddr collect onset)) 
        (elements (loop for element in (rest agenda) by #'cddr collect element))) 
  (make-instance 'collection :name name :onsets onsets :elements elements)))

This allows an example to be defined with some names. 

(defun named-example ()
  "Returns a simple musical object with named structure"
  (P "Example" (S "Melody"
                  (note :duration 1 :pitch "C3")
                  (note :duration 2 :pitch "D#3")
                  (note :duration 1 :pitch "C#3"))
     (note :duration 4 :pitch "G2")))

The show method can see if a name is stored, if so it builds a list in the format according to the new creators, otherwise it uses the old ones. 

(defmethod show :around ((object sequential))
  "Add the constructor and possibly the name to the showing of the elements"
  (if (name object)
    (list* 'S (name object) (call-next-method))
    (cons 'sequential (call-next-method))))

(defmethod show :around ((object parallel))
  "Add the constructor and possibly the name to the showing of the elements"
  (if (name object)
    (list* 'P (name object) (call-next-method))
    (cons 'parallel (call-next-method))))

(defmethod show :around ((object collection))
  "Return a list describing a collection of musical objects"
  (let ((sub-result (loop for result in (call-next-method)
                          for onset in (onsets object)
                          collect onset
                          collect result)))
    (if (name object)
      (list* 'C (name object) sub-result)
      (cons 'collection sub-result))))

On top of the search function we can now define the search for an object with a specific name, e.g. to subject it to a transformation. 

(defmethod find-named-musical-object ((object musical-object) name)
  (find-musical-object (example) 
                       'compound-musical-object 
                       #'(lambda(object onset)
                           (and (name object)
                                (string-equal (name object) name)))))

(find-named-musical-object (named-example) "Melody")
->
(S "Melody"
   (note :duration 1 :pitch "C3")
   (note :duration 2 :pitch "D#3")
   (note :duration 1 :pitch "C#3"))

For printing the names in the piano roll notation we need to reserve some space, extending the boundary a bit upward, whenever a compound musical objec has a name. An extra before method for draw‑boundary can print the name at the appropriate place. 

(defmethod enlarge-boundary :around ((object compound-musical-object) boundary 
                                     &optional (horizontal .15)(vertical .2))
  "Leave space in a boundary box for the name"
  (declare (ignore horizontal vertical))
  (if (name object)
    (destructuring-bind (left top right bottom)
                        (call-next-method)
      (list left (+ top .4) right bottom))
    (call-next-method)))

(defmethod draw-boundary :before ((object compound-musical-object) boundary)
  "Draw a label in the box when a compound musical object is named"
    (when (name object)
      (draw-label boundary (name object))))

(draw (named-example)) => 

Figure 27. Named musical objects.

This finalizes the work on the drawing of musical objects. An overview of the methods involved and how they are specialized to the different classes can be shown in the inheritance graph. 

Figure 28. Overview of the drawing methods.

Implementing the MIDI protocol 
To allow us to write programs that play synthesizers directly via MIDI, a communication protocol for musical data (IMA, 1983) we need to build some auxiliary functions that assemble messages in the proper format. The messages consist of a number of bytes (numbers between 0 and 255 inclusive). The first byte represents the command. There are commands for start and ends of notes, changing the overall volume, changing the synthesizer's preset sound etc. We only need one, the note-on command. The command to turn notes off is the note-on command with a velocity of 0. The note-on command comes in 16 varieties, one for each channel. After that the MIDI key number (the pitch) is given plus a velocity with which that key was hit. 

(defun midi-note-on-command (channel)
  "Return the MIDI byte initiating note-on command"
  (+ #x90 channel))

(defun midi-note-on  (channel key velocity)
  "Return a list of MIDI bytes for a note-on command"
  (list (midi-note-on-command channel)
        key
        velocity))

(defun midi-note-off  (channel key)
  "Return a list of MIDI bytes for a note-off command"
  (list (midi-note-on-command channel)
        key
        0))

(midi-note-on 0 60 64) -> (144 60 64)

Assume we have a function, called play to write a MIDI message directly to the MIDI port of the computer. Than it be used to turn notes on and off by hand and test the setup. 

(play (midi-note-on 0 60 64))
.
.
(play (midi-note-off 0 60))

Now we need to translate our note objects to MIDI messages. First we translate the instrument names to channel numbers. Because we cannot question synthesizers in a device independent way about the sounds they support, we assume you program a function yourself that reflects a standard setup or does some brand specific processing to get hold of a list of instrument names, one for each channel. For the moment we just give you a dummy one, that assumes a piano sound is available on channel 0, a cello on channel 1, and a violin on channel 2. 

(defun midi-instruments ()
  "Return a list of instrument names, in order of channel"
  '("Piano" "Cello" "Violin"))

To get the MIDI channel of a note we search for the position of its instrument in the instrument list, protecting for unknown instruments, and out of range channels. 

(defmethod midi-channel ((object note))
  "Return the MIDI channel to use for a note"
  (min (or (position (instrument object)
                     (midi-instruments) 
                     :test #'string-equal)
           0)
       15))

(midi-channel (note :instrument "Cello")) -> 1

The translation from a note's pitch to a MIDI key is almost trivial in our representation. We protect for fractional and out of range pitches. 

(defmethod midi-key ((object note))
  "Return the midi key number of a note"
  (clip 0 (round (pitch object)) 127))

The translating from a note's loudness to a velocity, is a simple linear scaling. Note that it depends on the synthesizers setting how a key-velocity is handled, it may control something different than amplitude. This still makes sense because in real instruments the subjective impression of loudness is often not directly related to physical amplitude but e.g. more to spectral brightness. 

(defmethod midi-velocity ((object note))
  "Return the midi key velocity of a note"
  (clip 0 (round (* (loudness object) 127)) 127))

Now we can translate note into an array of two MIDI events, each event being a list of an onset and a MIDI message. 

(defmethod basic-midi-array ((object note) onset)
  "Return an event array of MIDI messages for a note"
  (let ((channel (midi-channel object))
        (key (midi-key object))
        (velocity (midi-velocity object))
        (offset (+ onset (duration object))))
    (vector  
     (list onset (midi-note-on channel key velocity))
     (list offset (midi-note-off channel key)))))

(defmethod basic-midi-array ((object pause) onset)
  "Return an empty MIDI event array"
  (declare (ignore onset))
  (vector))

(basic-midi-array (note :duration 2 
                        :pitch "C#3" 
                        :instrument "Cello" 
                        :loudness .5) 
                  0) 
->
#((0 (145 61 64)) 
  (2 (145 61 0)))

Playing over MIDI 
A simple method can now play notes directly over MIDI. The timing accuracy will still be unacceptable, because we cannot trust the system not to interrupt lisp in the middle of a note for some other processing, but it is a first step towards playing our musical objects. 

(defmethod play ((object note) &key (time-unit 1/4) (tempo 60))
  "Play a note via the MIDI port"
  (let ((array (basic-midi-array object 0)))
    (destructuring-bind (onset note-on)
                        (aref array 0)
      (destructuring-bind (offset note-off)
                          (aref array 1)    
        (play note-on)
        (sleep (* time-unit (- offset onset) (/ tempo 60)))
        (play note-off)))))

(play (note :duration 1 :pitch 62 :instrument "Cello"))

Musical objects can now be fully translated into MIDI using the event list facilities defined before before. 

(defmethod as-midi-array ((object musical-object))
  "Return an event list of MIDI messages for a musical object"
  (as-event-list object 0 #'basic-midi-array))

(as-midi-array (example))
->
#((0 (144 60 127)) 
  (0 (144 55 127)) 
  (1 (144 60 0)) 
  (1 (144 63 127)) 
  (3 (144 63 0)) 
  (3 (144 61 127)) 
  (4 (144 61 0)) 
  (4 (144 55 0)))

And complete musical objects can be played, in the same rough manner.

(defmethod play ((object musical-object) &key (time-unit 1/4) (tempo 60))
  "Play a musical object via the MIDI port"
  (play (as-midi-array object) :time-unit time-unit :tempo tempo))

(defmethod play ((object vector) &key (time-unit 1/4) (tempo 60))
  "Play a midi event-list via the MIDI port"
  (loop for time = 0 then onset
        for (onset data) across object
        do (sleep (- onset time))
        do (play data)))

(play (example))
=> Figure 1.

For proper timing the sending of MIDI messages must be handled by a scheduler (like the Macintosh MIDI-manager), a low level process that runs in parallel with the main program. The scheduler is well integrated into the system and maintains a ordered buffer of data that is still to be send. This buffer is often of limited size, and the simple solution, to fill it completely with all our data before starting the scheduler, cannot work. We will implement a simple way of sending our MIDI event lists to such a scheduler, aiming at reliable, but non-real time, playing of music, because all our composition work will have to be done first before it is translated into a MIDI event list and sent to the scheduler. See (Dannenberg, 1991) for more details on scheduling algorithms and real-time use of schedulers. We will assume that a simple scheduler interface exists, consisting of the functions schedule, start‑scheduler, stop‑scheduler, and scheduler‑time. The schedule function tries to put one message in the buffer, it returns nil if it fails to do so because the buffer is full. The start‑scheduler function start the scheduler, its clock will be started and whenever data in the buffer is time stamped with the current clock time (or earlier) it is sent out via the MIDI port. The scheduler can be stopped by stop‑scheduler that prevents further action. the scheduler‑time function returns the clock time, so we know which data will have been outputted by now. Our program works ahead as far as possible, first filling the buffer completely, only then it starts the clock. It tries to keep up with the scheduler emptying the buffer, busy‑waiting when the buffer is full. After sending the last message it waits for the scheduler clock to reach the time of the last message, and then it stops. 

(defmethod play ((array vector) &key (time-unit 1/4) (tempo 60))
  "Play a musical object by means of a scheduler"
  (let* ((factor (* time-unit (/ 60 tempo)))
         (index (fill-scheduler array factor nil)))
    (start-scheduler)
    (fill-scheduler array index factor t)
    (wait-for-scheduler array factor)))

(defun fill-scheduler (array factor busy-wait)
  "Fill the scheduler with data"
  (loop for index from 0 below (length array)
        as (onset data) = (aref array index)
        while (try-schedule (* onset factor) data busy-wait)
        finally (return index)))

(defun try-schedule (time data busy-wait)
  "Transmit one message to the scheduler, possibly busy waiting"
  (if busy-wait
    (loop while (not (schedule time data))
          finally (return t))
    (schedule time data)))

(defun wait-for-scheduler (array factor)
  "Wait until the scheduler has sent all its data"
  (loop with end = (* factor (first (aref array (1- (length array)))))
        while (< (scheduler-time) end))
  (stop-scheduler))

The full interface to the Macintosh MIDI-Manager is given in the software that comes with this text. The next call will play the example over MIDI with a much improved timing accuracy . 

(play (example))
=> Figure 1.

Writing Standard MIDI Files 
Another way to play music is to write a standard MIDI file, a portable file format (IMA, 1988), and to import this in one of the commercial sequencer packages. This is does not depend on the hardware configuration, as our scheduler example did. Furthermore, other software can be used to do useful things to our data, like creating a neat score in common music notation. Again we use the scorefile writer written above. We need to put a specific header in the file that describes the format, the tempo, and other data. Then we can write times (relative time is used here, the time between two subsequent messages) in a specific variable length format, and the messages. Finally we need to add a trailer to the file. Note that for the retrieval of the time of the previous event, which is needed to calculate the delta times, the event writer is constructed as a closure that retrieves this time from a variable and updates it. 

(defmethod write-midi-file ((object musical-object) 
                            &key 
                            (file (choose-new-file-dialog))
                            (time-unit 1/4)
                            (tempo 60)
                            (ticks-per-1/4 96))
  "Store a musical object in a simple MIDI file"
  (let* ((old-time 0)
         (event-writer #'(lambda (event time stream)
                           (write-midi-file-time (* (- time old-time) 
                                                    time-unit ticks-per-1/4) 
                                                 stream)
                           (setf old-time time)
                           (write-bytes event stream))))
    (write-score-file 
           object
           :file file
           :basic #'basic-midi-array
           :header-writer (make-midi-file-header-writer tempo ticks-per-1/4)
           :trailer-writer #'write-midi-file-trailer
           :event-writer event-writer
           :file-type :|Midi|
           :file-creator :MIDI
           :element-type '(unsigned-byte 8))))

All auxiliary functions that calculate the binary format and write the appropriate (but rudimentary) header and trailer are given in the full code listing, at the end of this chapter. Don't feel obliged to look at it, it is just plain low-level manipulation of binary data. After these functions have been evaluated, the following call will produce a standard MIDI file (in format 0) that can be loaded in most sequencers and notation software. 

(write-midi-file (example) :tempo 60 :time-unit 1/4)

Full code listing 
It is difficult to find a good order to present object oriented code, and it is often argued that this is no longer needed, given the existence of code browsers and other tools in the programming environment, and although these tools are indispensable, it is worth while to find a nice order, and add some comments at the level of groups of related programs etc. Other programmers that have to read your code, and even yourself (some time after you wrote it), will be thankful for a good listing. That is why we present in the appendix all code developed in this chapter. It is re-ordered and only the last versions of functions are kept. The listing contains some details that were omitted in the text (on handling pitch names and low-level byte manipulations for writing MIDI files). In the real world the code would have been split in a couple of files - but we present them here as one. Separation lines and comments at different levels indicate the structural units within the program. 

Conclusion 
What have we got after all this writing and rewriting of code? We have designed a data representation for musical objects that proved quite flexible. We wrote tools for printing, drawing, playing and outputting them in different formats. We have defined a couple of transformations on them. The modularity is of a very fine grain - most programs are a couple of lines long, all take less then 10 lines of code. Though the code achieves quite some complex behavior, e.g. the nested drawing of boxes around compound musical objects or the writing of standard MIDI files, it still is easy to oversee and maintain because of that modularity. However, it has to be stressed that most programs are still bare skeletons of what a complete computer music system needs. But they are very good skeletons, and easy to fill-in. The constructions used and explained are the basic, object oriented constructs of CLOS. However, even these constructs (take e.g. the method combination for around methods) surpass with ease the power found in other object oriented languages. The design of programming language environments for the time based arts is so complicated in itself that programming language constructs that help in managing the complexity become a crucial issue Dannenberg, Desain and Honing (in Press). We hope to have shown how the constructs of CLOS can help achieving this. 

References 
Balaban, M. 1992 Music Structures: Interleaving the Temporal and Hierarchical Aspects in Music. in Balaban, M., K. Ebcioglu & O. Laske (Eds.) Understanding Music with AI. Cambridge, Mass.: MIT Press. 111-138 
Desain, P. 1990 Lisp as a second language, functional aspects. Perspectives of new music. (28)1 192-222 
Steele, G.R. 1990 Common Lisp the language, second edition. Bedford MA: Digital Press. 
Tatar,D.G. 1987 A Programmer's Guide to Common Lisp. Bedford, MA: Digital Press. 
Desain, P. and H. Honing (In Preparation) CLOSe to the edge, Advanced Object-Oriented Techniques in the Representation of Musical Knowledge. 
Dannenberg, R.B. 1991 Real-time Scheduling and Computer Accompaniment. in Mathews, M.V and J.R. Pierce (Eds.) Current Directions in Computer Music Research. Cambridge, MA: MIT Press. 
Dannenberg, R.B. 1993 The Implementation of Nyquist, a Sound Synthesis Language. in Proceedings of the 1993 International Computer Music Conference. San Francisco: ICMA. 
Dannenberg, R.B., P. Desain and H. Honing (in Press) Programming Language Design for Music. in G. De Poli, A. Picialli, S.T. Pope & C. Roads (Eds.) Musical Signal Processing. Cambridge, Mass.: MIT Press. 
Pope, S.T. 1992 The Interim DynaPiano: An Integrated Computer Tool and Instrument for Composers. Computer Music Journal. 16(3) 73-91 
Pope, S.T. 1993 Machine Tongues XV: Three Packages for Software Sound Synthesis. Computer Music Journal. 17(2) 23-54 
International MIDI Association 1983 MIDI Musical Instrument Digital Interface Specification 1.0. North Holywood: IMA. 
International MIDI Association 1988 Standard MIDI Files 1.0. North Holywood: IMA. 
Moore, F.R. 1990 Elements of Computer Music. Englewood Cliffs, New Jersey: Prentice Hall. 
Chowning, 1973 The Synthesis of Complex Audio Spectra by Means of Frequency Modulation Journal of the Audio Engineering Society, 21(7):526-534. Reprinted in C. Roads and J. Strawn, (Eds.) 1985 Foundations of Computer Music. Cambride, MA: MIT Press. pp. 6-29. 

Appendix: Full code listing 

Literature references made in text
Dannenberg, Desain and Honing (in Press)
(IMA, 1988)
(Dannenberg, 1991)
(IMA, 1983)
(Pope, 1993 page 31, figure 7)
(Chowning, 1973)
(Pope, 1993)
(Pope, 1992)
Steele (1990, page 791)
(Balaban, 1992)
Dannenberg (1993)
Tatar (1987)
Desain (1990)
Steele (1990)

Glossary references made in text
abstract class
accessor function
after method
applicable method
applicable methods
assignment
before method
class
class hierarchy
class options
congruent lambda lists
constructor functions
data abstraction
declarative
declarative method combination
direct superclasses
effective method
free variables
generalized variable
generic
imperative style
inheritance
initialization argument list
initialization keyword
initialization keyword
initialization protocol
instantiation
lambda list keyword
list
message passing
meta object protocol
method combination
method qualifier
methods
modification macro's
most specific method
multi-methods
multiple inheritance
name clash
name clashes
object-oriented style
part-of hierarchy
polymorphic
pretty printing
primary method
procedural
read-eval-print
selector functions
setf method
shadow
side effect
slot descriptors
standard method combination