Classic Computer Magazine Archive ST-Log ISSUE 22 / AUGUST 1988 / PAGE 45

MICROCOMPOSITIONS

by Michael Gogins

Michael Gogins is 37 years old, has a B.A. in comparative religions and is supporting himself by word processing and dBASE programming while he tries to get a career going writing science fiction. He plays the flute, writes music and will further pursue compositional programming.

The term "computer music" evokes images of a PC running a sequencer program and controlling a bank of synthesizers—layering tracks, instantaneously transposing and correcting, and finally printing out a meticulous transcription of the human intuition. Yet the first computer musicians were interested in much more than simply automating the tedium of traditional arranging and copying. They wanted to hear sounds which had never been heard before, and to apply algorithmic and mathematical techniques to the actual process of composition. For these pioneers, a musical score did not have to be black squiggles on a page—it could as easily be a program for generating music.

Today, due perhaps to its academic heritage and association with the avant-garde, compositional programming is not as familiar to musicians and programmers as sequencers and transcription software. Yet on the Atari ST, with its built-in MIDI interface, some quite interesting compositions can be written even in that resource everyone has—ST BASIC!

To illustrate three simple but powerful techniques of compositional programming, I have written an ST BASIC program, MICROS (see Listing 1). MICROS contains three sub-programs, each of which consists of one or two pages of code and generates an entire piece of music from either a short sequence of notes or a few numerical constants:

OC1 Overlap canon Lines 2000-2490
KC1 Koch curve Lines 3000-3370
CD1 Complex dynamical system Lines 6000-6230

These programs do not store sequences. All they do is play a synthesizer in real-time by computing numbers and sending them to the MIDI-Out port. (If you want to record a sequence, you can, of course, plug the MIDI-Out port of your ST into the MIDI-In port of another sequencer.) You can load MICROS.BAS into your ST, hook up your Casio CZ, and immediately run these compositions to hear some very different music. The program menu is self-explanatory. You can stop the music at any time by pressing the S key.

Hierarchical structures

KC1 and OC1 use multiple real-time "processes" to build up complex compositions. This is made possible by using a master timing loop which constantly reads the ST system timer. Within the loop is an event timing list of conditional branches which can execute several independent processes at the same time, based on note-on and note-off times generated by the processes themselves. Each process, when called, computes and plays only its next note before returning to the timing loop. (This is the closest one can come in BASIC to multitasking!) Therefore, a second process can compute its notes based on the notes returned by the first process, a third process can compute its notes based on the notes returned by the second process, and so on. In this way hierarchical or recursive structures, such as musical fractals, can be constructed.

Hardware and software requirements

Despite its faults and the fact that it is an interpreted language, ST BASIC is fast enough for some simple real-time compositional programming. (You can always use a BASIC compiler such as GFA BASIC if you want to speed things up, attain more precision, or do modular programming.)

These programs are written to play the Casio CZ-101 synthesizer. The ability of this common, inexpensive machine to play up to four different timbres at the same time makes it suitable for realizing, all by itself, fairly sophisticated compositions. Therefore each program contains mode and program change messages for accessing favorite patches I have stored in the Casio's internal memory. Experiment with the program change messages to find the best sounds on your machine.

If you do not have a CZ, you will have to change the syntax to control your own synthesizer. Or, if you have no synthesizer at all, you can still get three notes at a time out of the sound chip in the ST. Replace the note-on and off statements in Lines 380-480 with SOUND statements, and replace the various program change statements with WAVE statements.

KC1—a musical fractal

This program (Lines 3000-3370 in Listing 1) was inspired by VARIATN.BAS, a program written by Curtis Bahn to generate musical fractals for the Yamaha CX5-M, an MSX computer with a built-in synthesizer. (See Charles Dodge and Curthis R. Bahn, "Musical Fractals," Byte, June 1986, p. 185.)

If you want to understand fractals in depth, read Benoit Mandelbrot's book, The Fractal Geometry of Nature (W.H. Freeman and Company, 1983). Basically, a fractal is a curve which has wiggles or spikes on top of wiggles or spikes, ad infinitum, so that the curve actually fills up a measurable fraction of its region of the plane (hence the term "fractional dimension"). (A circle, square, or other ordinary curve, being only one point wide, fills only an infinitesimally small part of the plane.) For example, to construct a Koch or snowflake curve, take an equilateral triangle, stick three smaller equilateral triangles on its sides, stick three yet smaller triangles on the sides of those triangles, and so on to infinity.

A musical Koch curve is constructed by taking a simple melody as a generator. On top of each note in the generator is then played a tiny copy of the generator, which makes a second and faster layer of music. On top of each note in the second layer is then played a yet tinier copy of the generator, which makes a third and yet faster layer of the "snowflake," and so on. KC1 has four such layers.

KC1 is called by the master timing loop in Lines 160-240 of MICROS. Lines 3110-3160 of KC1 are an event timing list for the four layers of the musical snowflake. Lines 3010-3100 of KC1 are an initialization section, which switches itself off after the first call from the timing loop. (For a detailed understanding of the MIDI statements which program the CZ, consult the MIDI 1.0 Specification of the International MIDI User's Group, or see John Jainschigg, "Sound Chip Part 3: A Nuts and Bolts Guide to MIDI," Atari Explorer, Summer 1987, p. 78.) KCNOTES is the number of notes in the "generator" melody, KCLEN is the total duration of the piece of music in 200ths of a second, and KCSTART is the MIDI note number upon which the snowflake curve is built. The data in Line 3080 specifies the generator in terms of alternating pitch movements and durations as fractions of the total duration (i.e., add three half steps to the starting point and play the resulting note for .2 of the total duration, add seven half steps to the starting point and play the result for .2 of the total, and so on).

On each call from the master timing loop, control passes to the event timing list in Lines 3110-3160. On the first pass, all note on times are 0, and each process is executed immediately in turn.

The first process, in Lines 3170-3210, increments a counter which steps through the generator one note at a time. The appropriate number of half-steps are added to the starting point to obtain NOTE1, which is turned on by calling a subroutine. NOTE1's duration is then computed by multiplying its fractional duration by the total duration, and the next note on time is computed by adding NOTE1's duration to the current note-on time. Control then returns to the event timing list.

The second process works exactly the same way as the first, except that NOTE1 is used for the starting point, and the duration of NOTE1 is used instead of the total duration of the piece, so that a copy of the generator is played (as NOTE2) on top of each note of the generator. The third process is similarly built up (NOTE3) on top of the second, and the fourth (NOTE4) on top of the third. Control then returns to the master timing loop. On each subsequent pass through the timing loop, each process will be called only at its appropriate time, and will compute only its next note.

Experiment with different durations, pitches, and numbers of notes in the generator in Lines 3050-3080 to create new compositions. Even a small change to the generator makes a big change in the final music! Just make sure all the fractional durations add up exactly to 1.

OC1—an overlap canon

A canon, or round, is simply a melody which is played against itself after a certain delay. Like the musical snowflake, a canon can have several voices. OC1, however, is an overlap or differential canon. (See Lines 2000-2490 in Listing 1.) Each voice of the canon plays one less note of the generator than the last voice. Therefore, as the layers repeat, the amounts of overlap or delay are constantly shifted, and the piece as a whole does not repeat for a very long time—more than a day, in fact, if you let it play that long!

The initialization section and event timing list are similar to KC1, except that OCLEN specifies the fraction of each note duration for which the note will actually be on. This is the degree of staccato or legato to be played. The processes, however, are simpler; each process simply steps through the generator melody one note at a time, plays its note, and computes its next note on time. The only difference between the processes is the number of notes to be read from the generator before repeating.

Experiment with different melodies and rhythms, and especially with different synthesizer patches. It's interesting to use the same timbre for at least two of the voices, so that the shifting overlaps will interlace to generate constantly changing melodies. You may want to choose a generator whose notes do not all clash with each other, because at one time or another each note will be played against every other note.

CD1—a complex dynamical system

CD1 (Lines 6000-6230 in Listing 1) takes a totally different approach to music. The entire composition is generated by a single, very simple equation in Lines 6160-6170, and the music is completely determined by a choice of four numbers.

The mathematics is based upon arithmetic in the complex plane, where each point is represented by two numbers, a real (or X) component and an imaginary (or Y) component. For musical purposes, each point in the plane can also be thought of as representing two notes, one on an X keyboard and one on a Y keyboard. Complex arithmetic specifies the rules for adding, subtracting, multipying, and dividing points.

The equation Z < = Z ∧ 2-M means: take the point Z, square it and subtract from it a constant point, M. Then make Z equal to the result, and repeat the procedure. As the equation is iterated, Z hops about on the plane—playing, according to our musical interpretation, two-voice counterpoint. This is a complex dynamical system. And the behavior of Z depends very sensitively on the value of M. For some values of M, Z hops very quickly off to infinity; for other values of M, Z spirals down into some stable point and stays there; for yet other values, Z whirls about until it settles into a hopping orbit with two, three or however many points.

If each point on the plane is taken as M and colored black if Z=0 never goes to infinity, or some other color according to how fast Z=0 does go to infinity, a map of the Mandelbrot set will be produced. The Mandelbrot set is one of the most complex and fascinating objects in all mathematics, because no matter how much it is enlarged, it reveals more and more detail, never exactly repeating itself as most fractals do. I used the set to choose an interesting value of M for CD1. Ms from the black region, the inside of the set, but close to the edge, produce relatively complicated orbits of Z. For more information about the Mandelbrot set and other fractals, with awesome color pictures, see H.O. Pietgen and P.H. Richter, The Beauty of Fractals: Images of Complex Dynamical Systems (Berlin, Springer-Verlag, 1987). You may be able to find public-domain programs for generating maps of the Mandelbrot set on the ST.

Experiment with CD1 by changing CDMR and CMI, the value of M, and/or CDZR and CDZI, the initial value of Z, which does not need to be 0. To guide your explorations, you may want to use one of the public-domain ST programs for mapping the Mandelbrot set.