LOCO is a system for (music) composition. It is built on the
general programming language Logo. It can be used as a workbench for
experimenting with musical structures, both for beginners and
professionals. It is a non-realtime system, which means that
composing and playing don't take place at the same time. LOCO
consists of three subsystems: the composition system, the score
system, and the instrument system (see figure 3). The composition
system consists of a set of program generators, programs that write
programs. They can be used to create the so-called choice systems
that will make a composition according to the rules provided by the
composer. The connection between the composition system and the
instrument is via the score system. The instrument system uses
standard MIDI codes to control any kind of MIDI synthesiser.
The scope of this manual is a step by step introduction to LOCO. It is not intended as a demonstration of the full power of LOCO, nor to explain theories about music composition. The underlying philosophy and design decisions, as well as some other examples of LOCO are given elsewhere (Desain/Honing 1986,1988).
After starting LOCO, a score writing and playing system is
available. This score system is to be considered as a tool - it is in
itself not so interesting. But with this tool you can make programs
that write music on the score in their own (slow) way. After that the
score can be played as fast as you like by your synthesiser.
On a score you can write simple and compound musical objects. An example of a simple musical object is a NOTE:
(note duration pitch loudness)
It has 3 inputs: a duration, a pitch and a loudness. The duration signifies how long the note is going to last. The pitch is a number counting the semi-tone steps of an equal-tempered scale, middle C has pitch number 60 (see chapter 6.7 for a table of MIDI pitches). The loudness is a number between 0 and 1. Later we will define translations from well-known names (like middle C and pianissimo) to these numbers. Let's first write one simple short loud middle C note to the score and play it.
(write-score (note 1 60 1)) (play-score)
(In all examples plain text is user input and italic is computer output.)
If we write more notes they will be added one after another to the score. Let's try some other inputs:
(write-score (note 2 60 1)) (write-score (note 1 58 1)) (play-score)
Of course we also need rests:
(write-score (pause 1)) (write-score (note 2 58 0.7)) (play-score)
Also other Lisp constructs like repetition (using the loop macro) are usable within LOCO:
(loop repeat 4 do (write-score (note 1 67 1))) (play-score)
If you are bored with such repeated notes or with typing all these notes yourself, and want to let the computer compose the piece for you, you should start reading chapter 5 and come back later to hear all about the facilities of the score system. On the other hand, if you like to explore one thing at a time just continue reading. Let's talk about polyphony now. We can start from the beginning of the score (location 0) and add a second layer of notes. Or we can see were we are in the score, go to a specific position, and continue from there.
(location 0) (write-score (note 3 51 1)) (write-score (note 1 52 1)) (play-score) (location?) 4 (location 2) (write-score (note 1 50 1)) (play-score)
The created score in a pianoroll notation looks like this:
Figure 1. Pianoroll notation of the created score.
The score can be played at different tempos. A tempo number gives the number of time-units per minute. So our little score of 11 time-units will last for 11 seconds when played at tempo 60:
(tempo) 60 (play-score) (tempo 120) (tempo?) 120 (play-score)
A score can be saved to disk, erased and retrieved at a later time. save-score will open a Save dialog box and asks for a filename.
(save-score) (erase-score) (play-score) (write-score (note 1 60 1)) (play-score) (load-score) (play-score)
load-score overwrites the current score. So if you want to keep it, save it before reading a new score from disk. load-score opens a Load dialog box and lets you select the file that has to be loaded.
Next to creating multiple layers of notes on the score by writing layer per layer and using location, we can make compound musical objects at once (note that later these objects will be calculated by some kind of program, but for the moment we will type them in).
Musical objects that will sound at the same time are constructed using P for Parallel. A parallel object will last as long as its longest component.
(erase-score) (write-score (p (note 4 60 1)(note 8 63 1))) (play-score) (write-score (p (note 4 57 1)(note 4 59 1)(note 4 63 1))) (play-score)
You can make a sequential ordering of musical objects into one compound object by using S for Sequence.
(erase-score) (write-score (s (note 1 60 1)(pause 1)(note 1 61 1))) (play-score)
P and S are functions that have a list of musical objects as argument.
Sometimes you want to use notes with smaller durations. Although it is permitted to use fractional durations, a change in the meaning of the time-unit is often more effective. The default value of time-unit is a sixteenth, each note of duration 1 will be a sixteenth note - lasting for 0.25 seconds. If you set the time-unit to a quarter note, and since at tempo 60 you will have 60 of them each minute, a note of duration 1 will last for one second. Changing the time-unit will only affect the writing of the score (way of notating) and not the actual playing. If this all seems too complicated to you, just remember the following. If your tempo has to be set at an unreasonable high value to get the result you want, then it is better to write the score with a smaller time-unit and vise-versa.time-unit is usually set before you write the score, tempo while playing it (see figure 3).
(erase-score) (time-unit?) 1 / 16 (time-unit 1/4) (write-score (note 1 60 1)) (write-score (note 1 61 1)) (play-score) (erase-score) (write-score (note 1 60 1)) (write-score (note 1 61 1)) (play-score)
Note that load-score and erase-score reset time-unit to its default value 1/16. That is why in the example after erase-score the written notes sounded shorter. time-unit was set to its default value.
Most MIDI synthesisers are equiped with different sounds called instruments, timbres or voices. The playing instrument can be changed using instrument. It has a number as input. This is the same number you would use when selecting the instrument manually on the synthesiser itself.
(erase-score) (write-score (note 4 54 1)) (instrument 3) (play-score) (instrument?) 3 (instrument 22) (play-score)
If you have a synthesiser
that can handle more instruments at once (this is called
multi-timbral), or if you have more synthesisers connected in a "dais
y chain", each receiving MIDI information on a different channel, you
can write music that has more then one part. If you do not have this,
skip the explanation of part.
PART changes the current part of the score you are writing (i.e. the MIDI channel the notes will go to when played). And instrument changes the instrument playing the current part.
(erase-score) (part?) 1 (write (note 1 60 1)) (write (note 2 63 1)) (instrument 3) (play-score) (part 2) (location 0) (write (note 2 55 1)) (write (note 1 56 1)) (instrument 11) (play-score) (part 1) (instrument 10) (play-score)
The resulting score can be visualised as follows:
Figure 2. Pianoroll notation of the given score. Note that load-score and erase-score reset part and time-unit to their default values, 1 and 1/16 respectively.
Advanced control of timing achievable with the LOCO system will be
When writing scores the position in the score can be manipulated by the musical objects themselves. (PRE object ) will be written to the score just before its current position without changing it. This is useful for objects like grace notes.
(erase-score) (loop repeat 4 (write-score (note 1 60 1))) (play-score) (location 3) (write-score (pre (note .2 50 1)) (play-score)
The pre object is prefixed to the current position in the score, i.e. it already happened when it's asked to start at position 3 (you can hear that the pre note is not on the same beat as the first layer). pre can also be combined in S.
(location 4) (loop repeat 4 (write-score (s (pre (note .15 70 1)) (note 1 62 1))) (play-score) (location?) 8
PRE behaves like a prefix attached to (note 1 62 1), but it does not change the duration, nor the timing of the S object.The post object is postfixed to the position in the score. This means that it can be written to the score at the current position without changing it. Mind that writing a pre object at position 0 of the score is an error (This is like writing a musical object on your desk instead of on the music paper). The next example will yield a chord:
(erase-score) (location?) 0 (write-score (post (note 1 70 1))) (location?) 0 (write-score (note 4 60 1)) (location?) 4 (play-score) (write (post (note 1 70 1)) (location?) 4 (play-score)
To make clear the information flow in the score system we summarize the commands in a picture:
Figure 3. Information flow in the score system.
Composition can be looked at as the making of choices. Choices can be made by the composer or by the computer. In the last case the composer (you!) will have to give the rules to the computer. One of the possible rules is a random choice from a fixed set of possibilities. We create such a program, a so-called "choice system", by means of the program generator def-aleatoric
(def-aleatoric color '(red green orange)) (color) RED (color) ORANGE (loop repeat 4 do (print (color))) GREEN GREEN ORANGE RED
Note that the program color is just a normal Lisp program, except that it is created by a program (by def-aleatoric). color can be used as any Lisp program, like in the example where its result is printed repeatedly. Note also that color does not know anything about colors, it just gives you a word as result.
In the window called "LOCO Work" an overview of all currently available choice-systems is displayed.
Now we are going to use def-aleatoric to construct a musical example, creating a melody with pitches chosen randomly from a fixed set of possibilities.
(erase-score) (def-aleatoric pitch '(60 65 67 69 70)) (loop repeat 12 do (write-score (note 1 (pitch) 1))) (play-score)
Until now, we have been supplying a number as the second argument
of note. Now the program pitch calculates such a
pitch number for us. Those of you who know about scales, and like to
experiment with them, can immediately start creating random melodies
in major, minor, or other scales, by selecting a suitable list of
possibilities for pitch (For MIDI pitch/key numbers see
Note that when you play the score a second time you will hear the same notes, while when you repeat the previous example (writing a new sequence of notes with a random pitch) you will hear a different score.
We also can make random rhythms using def-aleatoric:
(erase-score) (def-aleatoric dur '(1 2 4)) (loop repeat 12 do (write-score (note (dur) 60 1))) (play-score) (loop repeat 12 do (write-score (note (dur) (pitch) 1))) (play-score)
In the last example we combined the two mechanisms of choosing random pitches as well as durations. LOCO supports lots of ways of combining very simple mechanisms into complex ones. We encourage the free experimenting with these combinations. They are so manifold that it is impossible to describe them all, just like describing all possible Logo programs is an infinite task. When you want to start all over with a new composition you can use erase-work. It will erase all choice systems. erase-choice can be used for erasing just one choice system.
If you have a predefined order in mind, in which the choices have to appear, you can use the generator def-ordered instead of def-aleatoric. def-ordered would make the right traffic light, compared to the color example we did with ordered, but we will give a musical example here:
(erase-score) (def-ordered pitch '(60 61 64)) (loop repeat 9 do (write-score (note 1 (pitch) 1))) (play-score) (def-ordered dur '(1 2)) (loop repeat 12 do (write-score (note (dur) (pitch) 1))) (play-score)
In the second part of the example we put a duration order of two
elements against a melody line of three elements. Can you figure out
what goes on?
We can use loudness to accentuate one of the patterns:
(def-ordered loudness '(1 .7)) (loop repeat 12 do (write-score (note (dur) (pitch) (loudness)))) (play-score) (def-ordered loudness '(1 .7 .7)) (loop repeat 12 do (write-score (note (dur) (pitch) (loudness)))) (play-score)
Select an instrument on your synthesiser that is sensitive for loudness (for which 0.7 and 1 are distinguishable). Sometimes you need the possibility to reset a choice system in its initial state, as if you never used it. You can use reset-choice here (or reset-work that resets all choice systems). In the LOCO Work window you see how loudness was generated:
(def-ordered loudness '(1 .7 .7)) (loudness) 1 (loudness) .7 (reset-choice loudness) (loudness) 1
A third principle of choice used by many composers is serial choice. A serial choice of a given set of possibilities will first use all of them before one can be chosen again. Think of a serial choice as a bucket full of possibilities (written on little pieces of paper). Each time a new value is needed, one piece of paper is taken out of the bucket at random without putting it back. Only when the bucket is empty all of the possibilities (pieces of paper) are put back in the bucket and the process is continued.
(erase-score) (def-serial pitch '(60 63 65)) (loop repeat 12 do (write-score (note 1 (pitch) 1))) (play-score) (erase-score) (def-serial dur'(1 2 3)) (loop repeat 12 do (write-score (note (dur) (pitch) 1))) (play-score) (location 0) (loop repeat 24 do (write-score (note (dur) (- (pitch) 12) 1))) (play-score)
When we use a serial choice for making rhythms, as we did in the second part of the example above, a kind of feeling for measure is created. This happens because after each 1 + 2 + 3 = 6 time-units we can be sure a note will start. This even becomes more apparent when a second layer of the same kind of notes is added. This second layer is transposed down by an octave (12 semitones) by using just plain Logo arithmetic. Everywhere in LOCO the full power of Logo is available for these kinds of calculations.
Our next example will give a demonstration of the orthogonal nature of LOCO: everything can be coupled to everything. We will use a choice system to generate a list of possibilities that another choice system can choose from.
(def-aleatoric division '((3 3)(4 2)(5 1))) (division) (4 2) (division) (3 3) (def-serial dur (division)) (dur) 2 (dur) 4 (dur) 5 (reset-choice dur) (erase-score) (loop repeat 12 do (write-score (note (dur) 50 1))) (play-score) (location 0) (loop repeat 12 do (write-score (note (dur) 61 1))) (play-score)
In this example serial choice system duration only consults division when the previous list of possibilities is exhausted. Note that when you use these kind of stacked choices the name of the choice system must be quoted when it is used as an input to another choice system. Otherwise, Lisp considers it just a program, calculates its result and gives that as a constant value to the second program generator.
The next example shows another way of stacking choice systems. One choice system can choose the name of another one that has to be evaluated.
(erase-score) (erase-work) (def-aleatoric high '(60 61 62)) (def-aleatoric low '(50 51 52)) (def-ordered register '(high high low)) (def-evaluated pitch (register)) (register) HIGH (pitch) 61 (loop repeat 24 do (write-score (note 1 (pitch) 1))) (play-score)
register will return the name of a choice system.
pitch will use that name and run it once (in this example it
produces a pitch number).
To make your programs more readable you can use def-constant to give names to your musical structures, as in the following two examples:
(def-aleatoric pitch '(60 61 62)) (def-constant loud-note (note 1 (pitch) 1)) (def-constant soft-note (note 1 (pitch) .6)) (def-constant chord (p (note 4 60 1)(note 4 63 1)(note 4 64 1))) (loop repeat 8 do (write-score (s (chord)(loud-note)(soft-note)))) (play-score) (erase-score) (erase-work) (def-constant minor '(60 62 63 65 67 68 70)) (def-constant major '(60 62 64 65 67 68 70)) (def-aleatoric pitch (minor)) (loop repeat 24 do (write-score (note 1 (pitch) 1))) (play-score)
(def-aleatoric pitch (major))
(loop repeat 24 do (write-score (note 1 (pitch) 1)))
If you want to use an element by element translation of names you can use def-translated that has a list of pairs (a kind of dictionary) as its third input.
(erase-score) (erase-work) (def-aleatoric tone '(C D E F G A B)) (def-translated pitch (tone) '((C 60)(D 62)(E 64)(F 65)(G 67)(A 69)(B 71))) (tone) E (pitch) 69 (loop repeat 12 do (write-score (note 1 (pitch) 1))) (play-score)
When you need choices with different probabilities, you can use def-weighted. It works like def-aleatoric, but assigns to each possibility a different probability. The next example shows the use of weighted durations. Half of the choices will be of duration 1, the rest will be of duration 2 or, with a slight chance, of duration 8.
(erase-score) (def-weighted dur '((1 .5)(2 .4)(8 .1))) (loop repeat 12 do (write-score (note (dur) 55 1))) (play-score)
Sometimes you want to choose from a (very) large number of possibilities. It then can be much easier to give only the borders in between which can be chosen. With def-scaled you need a minimum, a maximum, and a resolution defining the size of the scaled steps in between (as if we used def-aleatoric with a list of equal spaced numbers between a minimum and a maximum). The larger the resolution, the less possibilities there are within the given range.
(erase-score) (def-scaled whole-tone-pitch 60 72 2) (whole-tone-pitch) 72 (whole-tone-pitch) 62 (whole-tone-pitch) 66 (loop repeat 12 do (write-score (note 1 (whole-tone-pitch) 1))) (play-score)
A whole tone scale has equal steps between every preceding note. You maybe want to compare the sound and making of this scale with the ones you made withdef-aleatoric. The next is an example using PRE for precise timing control of a grace note attached to a regular beat, while the duration fluctuates.
(erase-score) (def-scaled dur .1 .5 .01) (def-constant grace-note (pre (note (dur) 50 1))) (def-constant beat (note 2 52 1)) (write-score (pause 1)) (loop repeat 12 do (write-score (s (grace-note)(beat)))) (play-score)
The first pause is to prohibit the writing of a grace-notebefore the beginning of the score (see explanation of pre). Now we arrive at what we can call higher order choice systems. They are only useful when build on top of other choice systems. A very useful one is def-iterative which can repeat results a number of times. An example:
(erase-score) (def-ordered pattern '(64 62 64 67)) (loop repeat 16 do (write-score (note 2 (pattern) 1))) (play-score) (def-iterative double-pattern (pattern) 2) (double-pattern) 64 (double-pattern) 64 (double-pattern) 62 (double-pattern) 62 (reset-work) (location 0) (loop repeat 24 do (write-score (note 1 (+ (double-pattern) 12) 1))) (play-score)
Well, in this example a lot of things happened. Let's look at them
step by step. The first thing you played was a simple repeated pitch
pattern or melody of four notes. The second time the same pattern is
doubled, as well as in tempo as in notes. The double tempo is defined
by a smaller duration value, but, what is much more interesting, the
doubled notes were derived from pattern by taking its result
and repeating every new value two times. Because we checked out
double-pattern on the monitor half way, we used
reset-work to reset double-pattern to its initial
state. Otherwise both patterns wouldn't be that synchronous
(double-pattern , would start half-way).
patternand double-pattern could also be called Balungan and Pekingan, respectively, if it was a first step in making a simulation of a Javanese gamelan styled composition.
Construct for yourself an iterative choice system that lets another choice system take care of the repetition input (the third input of def-itereative ).
Another higher order program generator is def-cumulative . Next to the name of the program to generate it has an increment and a start value.
(erase-score) (def-cummulative chromatic-pitch 1 48) (chromatic-pitch) 48 (chromatic-pitch) 49 (chromatic-pitch) 50 (loop repeat 12 do (write-score (note 1 (chromatic-pitch) 1))) (play-score)
Although this is not a very shocking example, def-cumulativeis a strong principle. It, as its name suggests, cumulates all inputs, beginning with its starting value (48 in the example above). The next values are determined by the increment value. That can be a number (as in the previous example), but also, and more interesting, a choice system, as is thirdin the next example:
(erase-work) (erase-score) (def-aleatoric third-interval '(3 4 -3 -4)) (def-cummulative brown-third-interval (third-interval) 60) (brown-third-interval) 60 (brown-third-interval) 57 (loop repeat 24 do (write-score (note 1 (brown-third-interval) 1))) (play-score)
Brown melodies, as made here with def-cumulative , have a more flowing nature compared to aleatoric or white melodies.
The next program generator to be introduced is def-transitive . With this you can link choice systems in a network, each one choosing a next one to use. Like all generators it has the name of the program to be generated as its first argument. The second argument is a starting state.
(erase-work) (erase-score) (def-weighted I '((II .2)(IV .6)(V .2))) (def-weighted II '((I .7)(IV .3))) (def-weighted V '((I 1))) (def-weighted IV '((V 1))) (def-transitive progression 'I) (progression) I (progression) IV (progression) V (progression) I (def-translated chord-name (progression) '((I C)(II D)(IV F)(V G))) (def-constant C (P (note 2 48 1)(note 2 52 1)(note 2 55 1))) (def-constant D (P (note 1 50 1)(note 1 53 1)(note 1 57 1))) (def-constant F (P (note 2 48 1)(note 2 53 1)(note 2 57 1))) (def-constant G (P (note 3 50 1)(note 3 53 1)(note 3 55 1)(note 3 59 1))) (def-evaluated chord (chord-name)) (loop repeat 12 do (write-score (chord))) (play-score)
Figure 4. Graphical representation of progression.
After all this typing you can save the compositional work by using save-work. load-work reads it back in again.
(save-work) (erase-work) (load-work)
The last two program generators are used when
lists of values are needed instead of isolated values.
Sometimes it's useful to have available a scale of values (as used implicitly in def-scale). This can for example be used as argument for def-serial. The arguments of def-scale are a minimum, a maximum, and a resolution (like def-scaled).
(erase-score) (erase-work) (def-scale whole-tone-scale 60 72 2) (whole-tone-scale) (60 62 64 66 68 70 72) (def-aleatoric whole-tone-pitch (whole-tone-scale)) (whole-tone-pitch)) 70 (whole-tone-pitch) 64 (loop repeat 12 do (write-score (note 1 (whole-tone-pitch) 1))) (play-score)
Of course, the arguments for def-scale can also be calculated by another choice system.
(erase-score) (def-cummulative maximum 1 60) (def-scale pitches 60 (maximum) 1) (def-serial pitch (pitches)) (loop repeat 24 do (write-score (note 1 (pitch) 1))) (play-score)
This example produces a constantly widening serial melody.
If you want to collect a certain number of outcomes from a choice system into a list, it can be passed as an argument to another choice system, using def-collect. In the next example a number of trills are made.
(erase-score) (def-aleatoric pitch '(60 62 64 65 67 69 72)) (def-collect pitchset (pitch) 2) (pitchset) (69 62) (pitchset) (60 67) (def-iterative repeated-pitchset (pitchset) 4) (def-ordered trill (repeated-pitchset)) (loop repeat 36 do (write-score (note .5 (trill) 1))) (play-score)
Some more nice sounding examples:
(erase-score) (erase-work) (def-scale chromatic-scale 60 71 1) (def-ordered random-pitch (chromatic-scale)) (def-collect scale-of-three (random-pitch) 3) (def-serial serial-pitch (scale-of-three)) (def-ordered accent '(1 0.7 0.7)) (def-ordered note-duration '(1 0.5 0.5)) (loop repeat 36 do (write-score (note (note-duration) (serial-pitch) (accent)))) (play-score) If Philip Glass is what you want: (erase-score) (erase-work) (def-ordered mask '(1 0 0 1 0)) (def-ordered scale-pitch '(60 62 64 65 67 69 70)) (def-iterative pitch (scale-pitch) (mask)) (loop repeat 32 do (write-score (note 1 (pitch) 1))) (play-score)
Figure 5. MIDI pitch/key numbers
Desain, P., & Honing, H. (1986). LOCO, composition microworlds in Logo. In P. Berg (ed.), Proceedings of the 1986 International Computer Music Conference. 109-118. San Francisco: Computer Music Association.
Desain, P., & Honing, H. (1988). LOCO: a composition microworld in Logo. Computer Music Journal, 12(3), 30-42.