Cellular Automata Music

Cellular Automata on computer is one of the earliest forms of Atrificial Intelligence, or Artifical Life research. The 'classic' form is Conway's Game Of Life which sets rules about cells in a grid being on or off. It is this closeness to the binary logic of 1s and 0s that most excited early researchers about the possabilities for computing. In the Game Of Life there is a matrix of cells, each cell can be alive (black) or dead (white) according to specific rules.

This screen shot shows a grid of cells (10 x 10)

This is a simple example which uses the Game of Life rules that if a cell has to many or to few neighours it dies (from over crowding or lack of support) if it has a moderate number it lives or comes to life. Each generation (iteration) of the game results in a new pattern of dead and alive cells. The pattern of cells can be mapped to various musical elements.

For this tutorial the number of live cells is counted each generation and this number is used to set the pitch. Each note is set to a semi quaver value.

This is what the result of this tutorial file sounds like,
first as a MIDI file, secondly as audio using triangle waveforms:

There are three source files which produce four classes.

AutomataMusic.java 

The Cellular Automata class is the engine room for this program and was written by Andrew Troedson, the graphics and music mapping were written by Andrew Brown and Andrew Troedson. This tutorial will talk about the music aspects, found in the AutomataMusic class. Yopu can make changes to this to explore different ways to map the CA data to music. You are left to your own devices to examine the CA and GUI classes if you are interested.

Let's have a closer look.

import jm.JMC;
import jm.music.data.*;
import jm.audio.*;
import jm.audio.synth.*;
import jm.util.*;

We import the usual suspects for the jMusic data, and the audio classes for the simple synthesis we have as output, and the util class for MIDI and AU file writing.

public class AutomataMusic implements JMC{
static boolean[][] cells = new boolean[10][10];
int maxNotes = 100;
Score score = new Score("The Cellular Drunk", 120);
Part piano = new Part("piano",0, 0);
Phrase phr1 = new Phrase();
Phrase phr2 = new Phrase();
//jMusic audio instrument specifications TriangleInst triInst = new TriangleInst(44100);

To specify the size of the cellular matrix (how many cells) a two dimensional array (an array of array - i.e, a matrix) called 'cells' is declared. The size of the matrix is 10 cells by 10 cells, 100 in all.

All the elements for a jMusic piece are declared, score, parts and phrases.
 
The last two lines declare an audio instrument that is a raw triangle waveform.

public static void main(String[] args){
new AutomataMusic();
}

This simple main() method should be familiar by now to you. It creates a new instance of this class.

public AutomataMusic() {
int liveCellCounter;
int noteNumber;
// Arguments are width, height, number of seed cells, // and wrap around?
CellularAutomata CA = new CellularAutomata(
cells.length, cells[0].length, 20, false);
cells = CA.getAllStates();

DrawCA DCA = new DrawCA(CA,"CA Music");
// Another window with cell size and // last 2 ints as x & y pos
DrawCA DCA2 = new DrawCA(CA,
"Big Cellular Automata Music", 30, 10, 100);

The constructor has most of the hard-work code in it. An instance of the CellularAutomata class called CA is decalared. This class is one of the three used in this program. having it separate keeps the details of the cellular automata code out of the way so we can focus on music, and also means that we can use it in a number of pieces without having to rewite it or cut and paste code each time.

Two instances of the DrawCA class are declared. We use two simply to show that the class is flexible enough to draw a cellular matrix display at any specified size. Notice that the second declaration passes additional arguments to sepcify a non-default size.

		for(int i=0;i<maxNotes;i++) {
CA.evolve();
cells = CA.getAllStates();
//do jMusic
//counts the number of live cells in the
// current iteration
liveCellCounter = 0;
for (int k=0; k<cells.length; k++) {
for (int j=0; j<cells[0].length; j++) {
if (CA.getState(k,j) == true) {
liveCellCounter++;
}
}
}
//pitch offset noteNumber = liveCellCounter + (127 - cells.length*cells[0].length);   Note n = new Note(noteNumber, SQ); phr1.addNote(n); //wait a bit to make it look more pretty try { Thread.sleep(100); System.out.println("Iteration "+i+ " of "+ maxNotes); } catch (Exception e) {} DCA.repaint(); DCA2.repaint(); }

In this code block (above) most of the important work is sepcified. A loop is set up to repeat for the maximum number of notes we want in our score.

The CA object has it's evolve() method called, which applies the rules to the matrix one time.

The next loop goes through each cell and checks it's state (on or off, true or false) and keeps a tally in the liveCellCounter variable.

The tally of live notes is mapped to pitch, via the variable called noteNumber. So that we don't get very low notes the pitch is offset. In this case with the maximum possible on-cells = 100 and knowing we can we can allow the top pitch to be 127, the pitch offset needs to be 27. The maths might look a bit complicated, but that is to allow for a wide range of possible cell matrix sizes. Of course the larger the matrix the wider the probable pitch range. (Can you explaine why?)

A pause is inserted into the programs thread (100 milliseconds) so that the graphics don't go too fast. This deliberatly slows down the running of the program - the things we do to look pretty!

Having used the cellular automata matrix we get it to redraw its appearance in the last couple of lines of the code segment. Each of the DrawCA objects has its repaint() method called.

		piano.addPhrase(phr1);
score.addPart(piano);

Write.midi(score,"AutomataMusic.mid");

ShowScore ss = new ShowScore(score, 330, 10);

Write.au(scoe, "AutomataMusic.au", triInst);
}
}

After looping for a number of notes we end up with a phrase. This phrase is added to the piano part, then to the score.

The score is written out as both a MIDI file and an audio file, using the triInst. The score is also displayed.

You can clearly see here that the jMusic score data structure is what we create and that is then translated into the MIDi format the AU audio format and a piano-roll visual display.

The Cellular Automata structure, when mapped to pitch in this way, creates a results that is not much different from randomness. This is to some degree a tendency of Cellular Automata but especially a limitation of our very simplistic mapping. More sophisticated work on music derived from Cellular Automat has been done by composers, in particular check out the work of Eduardo Miranda which is detailed in his book 'Composing Music with Computers' (Focus Press 2001).



jMusic Tutorial Index