### Week 4: Cellular Automata, Fractal Techniques

Here are some interesting links -- I demonstrated most of these in class:

#### Developing a Simple Cellular Automata Music Program

Even though there are a whole bunch of snazzy cellular automata programs out there (check out the links above...), we decided to go ahead and create a simple CA/music program in class. One of the first decisions we had to make concerned the mapping of CA parameters onto salient musical paramaters. Because of the elegance of the patterns that unfold in a simple 1-dimensional CA, it seemed like a potentially interesting idea to map these to a constantly-evolving rhythm.

So we created a simple program that would generate 16 "hits", and used a "1" to represent a "hit" and a "0" to represent a rest.

To accomplish this, we used a 2-dimensional array, where one dimension of 16 elements stored the "1"s or "0"s, and the other dimension held the immediately-past and currently-calculated rows (why?). The code for this CA is as follows:

```#include <stdio.h>
#include <unistd.h>

#include "/musr/cmix/H/randfuncs.h"

#define NGENS 40
#define NCELLS 16

main()
{
int cells[2][NCELLS];
int left, right, combine;
int i, j, cur, next;
float fval;

/* initialize the first row */
cur = 0;
next = 1;
for (i = 0; i < NCELLS; i++) {
fval = brrand();
if (fval > 0.5) cells[cur][i] = 1;
else cells[cur][i] = 0;
}

/* go for it! */
for (i = 0; i < NGENS; i++) {
/* print out the current row */
for (j = 0; j < NCELLS; j++) printf("%d  ",cells[cur][j]);
printf("\n");

/* now calculate the next row */
for (j = 0; j < NCELLS; j++) {
/* find the neighbors */
if (j == 0) left = 0;
else left = cells[cur][j-1];
if (j == NCELLS-1) right = 1;
else right = cells[cur][j+1];

/* calculate the next generation state of
this cell, and store it in the next row */
combine = left + right;
if (combine == 0) {
cells[next][j] = 0;
}
if (combine == 1) {
cells[next][j] = 1;
}
if (combine == 2) {
cells[next][j] = 0;
}
}

/* now set for the next round */
cur = next;
if (++next > 1) next = 0;
}
}
```
[NOTE: this program makes use of the RTcmix randfuncs library, compile it by saying:
cc -o cellauto cellauto.c /musr/cmix/lib/randfuncs.o -lm
or use the 'cells'
Makefile to compile it.]

Notice the flipping from 1 row of the 2-dimensional array to the other accomplished towards the end of the program by:
```		/* now set for the next round */
cur = next;
if (++next > 1) next = 0;
```
cur is always 1 'behind' next, which keeps going from 0 to 1 and then back to 0.

Also notice the method used for the calculation of the next row of "1"s and "0"s. The algorithm sets the variables left and right to be equal to the values of the cells to the immediate left and right of the cell in the row currently under consideration, using a value of "0" is the cell is at the far left of the row and a value of "1" if it is at the far right:

```			/* find the neighbors */
if (j == 0) left = 0;
else left = cells[cur][j-1];
if (j == NCELLS-1) right = 1;
else right = cells[cur][j+1];
```
The rule it applies to calculate the next row is very simple. If the two next-door neighbors of the current cell are 0 or 2 (calculated by adding the values of the neighbor cells), the current cell will be set to "0" in the next generation. If the summed values of the neighbors equals "1" (i.e. either one or the other of the neighboring cells is a 1, but not both), then the current cell will be set to a "1" in the next generation -- regardless of its present value. If you want to think of this anthropomorphically, imagine that if a cell's neighborhood is too sparse (both neighbors == 0) or too populated (both neighbors == 1), then the cell will "die" and be "0" in the next generation. If the crowding is just right (only one neighbor == 1), then the cell will survive -- or be born anew -- and take the value "1" for the next generation.

The code that accomplishes this is quite simple:

```			/* calculate the next generation state of
this cell, and store it in the next row */
combine = left + right;
if (combine == 0) {
cells[next][j] = 0;
}
if (combine == 1) {
cells[next][j] = 1;
}
if (combine == 2) {
cells[next][j] = 0;
}
```
Even with this very basic rule, the output of this little CA can be remarkably complex:
```1  0  0  1  1  0  1  0  0  0  0  0  0  0  1  1
0  1  1  1  1  0  0  1  0  0  0  0  0  1  1  0
1  1  0  0  1  1  1  0  1  0  0  0  1  1  1  0
1  1  1  1  1  0  1  0  0  1  0  1  1  0  1  0
1  0  0  0  1  0  0  1  1  0  0  1  1  0  0  0
0  1  0  1  0  1  1  1  1  1  1  1  1  1  0  1
1  0  0  0  0  1  0  0  0  0  0  0  0  1  0  1
0  1  0  0  1  0  1  0  0  0  0  0  1  0  0  1
1  0  1  1  0  0  0  1  0  0  0  1  0  1  1  1
0  0  1  1  1  0  1  0  1  0  1  0  0  1  0  0
0  1  1  0  1  0  0  0  0  0  0  1  1  0  1  1
1  1  1  0  0  1  0  0  0  0  1  1  1  0  1  0
1  0  1  1  1  0  1  0  0  1  1  0  1  0  0  0
0  0  1  0  1  0  0  1  1  1  1  0  0  1  0  1
0  1  0  0  0  1  1  1  0  0  1  1  1  0  0  1
1  0  1  0  1  1  0  1  1  1  1  0  1  1  1  1
0  0  0  0  1  1  0  1  0  0  1  0  1  0  0  0
0  0  0  1  1  1  0  0  1  1  0  0  0  1  0  1
0  0  1  1  0  1  1  1  1  1  1  0  1  0  0  1
0  1  1  1  0  1  0  0  0  0  1  0  0  1  1  1
1  1  0  1  0  0  1  0  0  1  0  1  1  1  0  0
1  1  0  0  1  1  0  1  1  0  0  1  0  1  1  1
1  1  1  1  1  1  0  1  1  1  1  0  0  1  0  0
1  0  0  0  0  1  0  1  0  0  1  1  1  0  1  1
0  1  0  0  1  0  0  0  1  1  1  0  1  0  1  0
1  0  1  1  0  1  0  1  1  0  1  0  0  0  0  0
0  0  1  1  0  0  0  1  1  0  0  1  0  0  0  1
0  1  1  1  1  0  1  1  1  1  1  0  1  0  1  1
1  1  0  0  1  0  1  0  0  0  1  0  0  0  1  0
1  1  1  1  0  0  0  1  0  1  0  1  0  1  0  0
1  0  0  1  1  0  1  0  0  0  0  0  0  0  1  1
0  1  1  1  1  0  0  1  0  0  0  0  0  1  1  0
1  1  0  0  1  1  1  0  1  0  0  0  1  1  1  0
1  1  1  1  1  0  1  0  0  1  0  1  1  0  1  0
1  0  0  0  1  0  0  1  1  0  0  1  1  0  0  0
0  1  0  1  0  1  1  1  1  1  1  1  1  1  0  1
1  0  0  0  0  1  0  0  0  0  0  0  0  1  0  1
0  1  0  0  1  0  1  0  0  0  0  0  1  0  0  1
1  0  1  1  0  0  0  1  0  0  0  1  0  1  1  1
0  0  1  1  1  0  1  0  1  0  1  0  0  1  0  0
```
Next we simply replaced the printf() statement with an appropriately formatted START command for the STRUM instrument:
```		/* print out the current row */
for (j = 0; j < NCELLS; j++) {
if (cells[cur][j] == 0)
printf("START(%f, 0.5, 7.00, 0.5, 0.3, 5000, 3)\n",start);
start = start + beat;
}
```
The variables start and beat are used to track and increment time as necessary (both are declared as type float). By routing the output into a file and adding the rtsetparams() line at the front, we can run this score to hear the output realized as sound.

Finally, we translated the core of this program into a simple X/motif application, cellautoG, that generated a display of the emerging 1-dimensional CA as well as sending out each row of note-data in real time (this makes use of the RTtimeit() function described in the Week 4 class. We won't be wading through the X-windows code for this application. You may download it and peruse at will with the link below.

Here is a picture of a sample run of cellautoG: