
The Chaos Controller:A Stand Alone Hardware MIDI InstrumentSchematic diagram

Since the early 90's I have been interested in making music with chaos theory, by generating organic sounding melodies with iterated mathematical equations. 1rx^2 has been my equation of choice, for it's recurring recognizable melody sets. I refer to the different phrases that result from these iterations as "melody sets" because of the fact that the varying melodies are basically recognizable as originating from a certain equation. After experimenting for many hours, I can recognize the difference, musically, between 1rx^2 and rx(1x), etc. Basically, what is happening here is that numbers (between 2 and 2, for example) are plugged into r and x, and the solution is fed back into x and then solved for x again, repeatedly. Eerie, futuristic, yet organic sounding melodies result from this musical mathematical feedback loop.
The variable x can be thought of as the "seed" value. When new values for x are initiated, the melodies begin in a different manner, but always "settle" down into the same pattern (or tend to the same note), depending on the r value. The r variable can be thought of as the "chaos" value. The chaos value may have a bumpy curve. In other words, raising and lowering the r value do change the amount of chaos, but not necessarilly in a linear fashion. There may be "pockets" where, in the midst of chaos on either side, at one delicate point a certain r value, will result in a very stable and "consonant" melody. I relate this loosely to the concept of "dissonance curves" in musical scales. Intervalic dissonance has a bumpy curve, where playing consistantly larger intervals will repeatedly pass through cycles of consonant and dissonant intervals, in a very non linear fashion. This I compare to the concept of continually raising the r value, going through areas of stability, and chaos in a nonlinear fashion.
Recently, I have tried expanding this whole idea into the complex domain, by the prompting of Dr. Edward Belbruno, chaos mathematician at Princeton, and had some extremely interesting reslults which I will return to at a later date as part of my thesis. This semester I took a break from MAX, and any actual theoretical research on how to make music with chaos theory, in order to concentrate on producing a stand alone MIDI instrument for performing simple chaos music. I stuck to real numbers this time around, avoiding the complex domain. Next semester I will return to looking at the concept of making music with complex variables, and the feasibility of controlling two feedback loops simultaneously, and attempt to come to some sort of conclusion. Either the results will be more musically satisfying, or perhaps I will decide that the melodies that result from the complex domain are just too chaotic (too complicated for rocknroll, so to speak).
On a more basic level, I will continue my research on how chaos theory may or may not be considered an alternate type of music theory. I have been imagining this concept for several years, and perhaps there is something to it. Perhaps not. It would involve too lengthy of an explanation to be justified in this short writeup, and will have to wait for my thesis, but it has something to do with consonance and dissonance of melody sets, rather than interavals and chords. It makes sense in my head visually, and has for some time. However, I will need to spend at least one more sememster attempting to make sense out of it on paper, and with further musical examples, before I think it will make sense to anyone else (and I fear this is being optomistic).
The idea for the Chaos Controller emerged during the summer in a Physical Computing class. While I learned to program the Basic Stamp, I realized that it would be possible to emmulate my MAX patch with an actual stand alone musical instrument to generate musical fractals, that would not need a separate computer. During the summer I built a crude version of the Chaos Controller, which had only one light sensor out of six actually working, and no control over tempo or dynamics. This Fall, I continued where I had left off, with my haphazard circuit layout, literally making the circuit up as I went along. As the semester progressed, my circuit got to a level of complexity (not very complex, but complex enough) to justify starting fresh, with a well thought out schematic beforehand.
This Fall, to get the Chaos Controller to it's current state, it has been a matter of learning some more BASIC programming, some C programming, becoming more electronics savvy, deciding upon the physical design and schematic layout for the actual instrument, and learning how in the world do to floating point math with only 16 bit positive integers. All of these aspects were a challenge, but by far the most time was spent on the floating point math problem. Looking back, it seems like the most simple of all of these, but it was a matter of grasping the concept, and then the syntax. I have included exerpts from the PBasic and C programs to show the different steps I took in the learning process. (PBasic is the language used to program the Basic Stamp microcontroller, which is very similar to BASIC.)
Click here to view the schematic diagram  Additions and improvements to the circuit design over the old design include buffers to keep the high and low signals as close to +5v and 0v as possible. The old circuit had the 3bit control signals from the two sliders going straight into the Stamp, and no buffers before the MIDI output. It worked absolutely fine until I added a controller pedal with the rctime circuit. I suspected that the Stamp had too much of a load on it at that point and was not outputting proper voltages. Even disconnecting extraneous LED's did not help at this point, and that is when I decided it was time to start fresh, designing the new circuit ahead of time, this time using buffers everywhere I could.
I did manage to reuse the same box, but it was such a tight fit with the extra chips and 25 pin wire connector that I had to modify it so that one of the poles is now coming out the opposite side. It actually makes more sense this way, since now the MIDI jacks are in the back, more hidden, and the audience can see the flashing 3bit lights better. The lights are not very intuitive for the performer anyway, but make for a nice light show as the sliders go up and down the poles. The lights are more for troubleshooting and for setting the threshold on the sliders which are now fitted with 10k variable resistors, accessible with a small flathead screwdriver.
When I get around to making new sliders, one vast improvement will be to run the +9v signal to the sliders instead of +5 volts, so that the voltage difference between high and low will be larger (closer to +5v and 0v). As it is now, if the photoresisters catch the full reflection of the LED's against the white paper on the pole, the output control signal is only a maximum of approximately 2 volts, and at minium, approximately .7 volts. These are dangerously close to the thresholds for both high and low of the buffers that the signals are now running through. Before I implemented the buffers, it wasn't as close since I was just relying on the Stamps threshold of 1.5 volts (higher than 1.5 is considered a high signal, and lower than 1.5 is a low signal).
The seven segment display and accompanying button (yes, one increment button) has not been impemented yet, but will eventually allow me to scroll through 10 preset programs. This could be used to store different presets, or several different sections for one piece, etc. Also not implemented yet is the sustain pedal, which will be very simple because it is just a normally open switch. MIDI in will also be implemented soon, although I'm not entirely sure what I will be using it for. I'm sure there must be some useful things I could do with a MIDI input signal, and think it is worth giving up one pin for. All of the pins except one were already used with the old design, including 6 of them being used only to light LED's. This was a waste of pins, so now the LED's are getting their signals elsewhere, and the 6 pins are being put to much better use (3 outs for a 7seg display, an increment button, sustain, and a MIDI input). The previously unused pin is now being used for the control pedal.
Another slight improvement to the design is the fact that the wires from the circuit board all go to one 25 pin jack, as do the wires on the lid of the Chaos Controller. This way, it is much easier to unplug the board and do any repairs or improvements. It was very hard with the old design to see the back of the board well enough to do any soldering. Quite a few of the ground connections and bridges are on the back of the board now which contribute to neatness and clarity. Also, this time I've used THICK wire for power and ground, and much smaller, less cumbersome wires for the control signals, bent at right angles around the chips and components.
The new control pedal is working fine as a tempo control. It could also be used to control velocity or volume, or really anything I choose. For my performance "Neural Net" for Tom Beyer's ensemble class, I used the pedal to control three preset tempos  slow, medium, and fast  a strange concept, but it worked out well. I can also control tempo somewhat by simply pressing the red button on the chaos slider for each note. At this point, the duration of the note is controlled by the tempo. The dynamic and tempo control is klunky, however, enough to allow a fairly expressive musical performance.
Following are examples of code that I wrote during
the learning process, with comments and explanations. PBasic code
is in yellow, and C code is in green. Additional explanations are
in white and red.
Below is the latest version of the software
that is loaded into the Basic Stamp for the Chaos Controller. Comments
are preceded by a asterisk ( ' ).
'Chaos Controller, by Elaine Walker 2000
'This program
is currently loaded into the Chaos Controller.
'====================================INITIALIZATION=======================================
'DATA
FROM SLIDERS
input 0
'START button (starts the music)
input 1
'SEED button (sets seed value)
input 8
'photoresistor LSB (chaos value)
input 9
'photoresistor vld
input 10
'photoresistor MSB
input 11
'photoresistors for SLIDER 2 (seed value)
input 12
input 13
'LIGHTS
ON BOX
output 7
'light indicator on box for SLIDER 1
output 6
'light indicator
output 5
'light indicator
output 4
'same for SLIDER 2 indicator lights
output 3
output 2
'MIDI
OUT PORT
output 14
'serial out for MIDI
'READ
GREY CODE
n var nib
'reads chaos slider 3bit grey code
code var nib
'puts code in the right order, 0 through 7
m var nib
'reads seed slider 3bit grey code
mcode var nib
'puts code in the right order, 0 through 7
hibits var word
medbits1 var
word
medbits2 var
word
product var
word
'MIDI
CHAOS
seed var word
chaos var word
noteout var
byte
seedsquared
var word
tempo var word
pedal var word
'duration var
byte
'velocity var
byte
seed = 45875
chaos =
55000
chaos = chaos
 32768
'=================================BLINK
LIGHTS AT STARTUP================================
startup:
pause
1000
out2=1
pause
200
out3=1
pause
200
out4=1
pause
200
out5=1
pause
200
out6=1
pause
200
out7=1
pause
200
outs=%00000000
pause
200
outs=%11111100
pause
500
outs=%00000000
'===================WAIT
FOR PLAYER TO PRESS BUTTON ON VERTICAL SLIDER===================
waitforstart:
high 15
pause
1
rctime
15, 1, pedal
pedal
= pedal/150
lookup
pedal, [1000, 250, 250, 75], tempo
IF in1=1
THEN main
IF in0=1
THEN resetseedpaused
goto waitforstart
'==============================READ
DATA FROM CHAOS SLIDER================================
main:
IF in1=0 THEN waitforstart
n = (in8)
+ (in9 * 2) + (in10 * 4)
'read inputs 8,9,10 for chaos
lookup
n, [50000, 62259, 60000, 56000, 62000, 59000, 55000, 58000], chaos
chaos
= chaos  32768 'ABOVE ARE PRESET CHAOS VALUES
IF in0=1 THEN resetseed
'CHECK IF SEED IS NEGATIVE, AND SQUARE THE SEED VALUE
continue:
if(seed<32768) then negativeseed 'if seed is negative... (below 32768)
positiveseed:
seed =
seed  32768
'make seed range 0 to 32768 (0 to 2, absolute value)
'however, the seed never exceedes the range of 0 to 1
'which is 1 to 16384 at this point
'the sign doesn't matter, since seed gets squared
goto keepgoing
negativeseed:
seed =
32768  seed
keepgoing:
hibits
= (seed/128) * (seed/128)
'high bits * high bits +
medbits1
= ((seed*128)/256) * (seed/128)
'low bits * high bits +
medbits2
= (seed/128) * ((seed*128)/256)
'high bits * low bits +
seedsquared
= hibits + (medbits1/256) + (medbits2/256)
'MULTIPLY CHAOS * SEED SQUARED
hibits
= (chaos/128) * (seedsquared/128)
'high bits * high bits +
medbits1
= ((chaos*128)/256) * (seedsquared/128)
'low bits * high bits +
medbits2
= (chaos/128) * ((seedsquared*128)/256)
'high bits * low bits +
product
= hibits + (medbits1/256) + (medbits2/256)
' 1  rx^2  (rx^2 is "product") 
seed =
49152  product
'1rx^2
'49152 is 1 now instead of 16384 to make 0 = 32000
'so that negative numbers can be represented
'MIDI OUTPUT
noteout = seed / 744 'put in range of 88 note keyboard
serout
14, 12, [144, noteout, 120]
'play MIDI notes
pause
tempo
serout
14, 12, [128, noteout, 0]
pause
1
'debug ?tempo
goto main
'==========SET NEW SEED VALUE WITH BUTTON ON HORIZONTAL SLIDER DURING ITERATION==========
resetseed:
m = (in11)
+ (in12 * 2) + (in13 * 4)
'read inputs 11,12,13 for seed value
lookdown
n, [0,1,3,2,6,4,5,7], mcode
'[000, 001, 011, 010, 110, 100, 101, 111]
seed = mcode * 4681 + 16384 'scale seed to be 1 to 1, which is 16384 to 49152
''''' seed = 45875 'TEMPORARY PRESET SEED VALUE
goto continue
'===========SET NEW SEED VALUE WITH BUTTON ON HORIZONTAL SLIDER WHILE PAUSED=============
resetseedpaused:
m = (in11)
+ (in12 * 2) + (in13 * 4)
'read inputs 11,12,13 for seed value
lookdown
n, [0,1,3,2,6,4,5,7], mcode
'[000, 001, 011, 010, 110, 100, 101, 111]
seed = mcode * 4681 + 16384 'scale seed to be 1 to 1, which is 16384 to 49152
''''' seed = 45875 'TEMPORARY PRESET SEED VALUE
goto waitforstart
Below is the original program I had typed in,
just to get some notes happening, although it was producing some crazy
values for obvious reasons.
Original Program
BASIC does not do floating point math, and only uses positive unsigned integers, so the following example does not work out at all. The biggest variable possible is a 16 bit word. The numbers get huge and then become modulo 16 bit (%65535). Some nice melodies result, but it does not resemble the patterns of 1rx^2.
'VARIABLES
seed var word
chaos var byte
noteout var
byte
'EXAMPLE PRESETS
seed = 1
chaos = 0
'EXERPT FROM
PROGRAM
seed = 1  chaos
* seed * seed
'1  rx^2
Next are some attempts to emmulate in C what my PBasic program was doing so that I could figure out what was really going on with the math, and find the best way to go about doing the floating point math in PBasic.
First, I emmulated the positive integer math
in C by specifying that the variables are positive 16 bit integers.
unsigned int
seed;
unsigned short
chaos, noteout;
unsigned short
n, code;
main()
{seed=1;
chaos=7;
for(n=1;n<=50;++n)
{
seed = 1  chaos
* (seed)^2;
noteout = seed/512;
printf("%d
",noteout);
}
printf("\n");
}
erika: {18} ./chaos
65535
0 65535 5 65496
274 63613 13454 36887
3932 3
8006
61636 27299 5510
26960 7884 10341
58680 47988 57
297
57669 55065 7754
11257 52266 27347
5177 29293 570
86
59148 44714 14677
28327 63852 11781
48601 53006 22
173
41394 37920 62234
23109 34840 18259
3252 42765 28
320
63898 11460 50845
Then, the same thing with signed integers,
just to compare the numbers.
int seed;
short chaos,
noteout;
short n, code;
main()
{seed=1;
chaos=7;
for(n=1;n<=50;++n)
{
seed = 1  chaos
* (seed)^2;
noteout = seed/512;
printf("%d
",noteout);
}
printf("\n");
}
erika: {19} ./signed_chaos
0
0 0 5 39
274 1922 13454 28648
3932 27530
3899
27299 5511 26960
7884 10342 6855
17547 8239
7867
10470 7754 11257
13270 27348 5178
29293 8450
6388
20821 14677 28327
1683 11781 16934
12530 2217
4
24142 27616 3302
23109 30695 18259
3253 22771
28321
1638 11460 14691
Now, in C, I have taken a stab at doing some
floating point math with integers. The idea is that the values need
to be scaled every time there is a proccess done, to insure the values
don't overflow the 16 bit variable size (65535).
Simple floating point math
In this example I have scaled the range so that 2 to 2 is spread accross the entire possible 16 bit range of 65535. So now, 0 to 65535 represents 2 to 2 and 32768 represents zero. The seed value never exceedes the range of 1 to 1.
The seed value gets squared independently. First, the absolute value is taken (the sign doesn't matter since the seed gets squared each time) and it is scaled to a 7 bit range so that the highest possible value that results will be 16384 (14 bits).
This assures that the largest possible value resulting
will not exceed the 16 bit range.
float printme;
unsigned int seed, chaos, noteout, n, temp0,
temp1;
/* Assume, like the BASIC Stamp, all integers are 16 bit unsigned */
main()
{
seed = 45875; /* .8 * 16384
+ 32768 */
chaos = 62259; /* 1.8 * 16384 + 32768
*/
for(n=1;n<=24;++n)
{
if(seed<32768)
temp0 = 32768  seed;
else
temp0 = seed  32768;
/* temp0 is now in the range of 0 to 32767; sign doesn't matter, since we're squaring */
seedsquared = (seed/256) * (seed/256);
seed = 49152  (chaos/256) * (seedsquared/256);
/* temp1 is now seed squared; note that this only works for seed <= 1.414 */
temp0 = chaos  32768;
temp0 = temp0 / 128;
temp1 = temp1 / 128;
if(temp2)
seed = 49152 + (temp0 * temp1);
else
seed = 49152  (temp0 * temp1);
printme = (seed  32768.0) / 16384;
printf("%d
%f\n",seed, printme);
}
printf("\n");
}
The following two programs, one in Cand one
in PBasic, both generate the same outputvalues in the 16 bit range (between
0 and 65536) as follows. At the time this seemed like a breakthrough!
Both programs produce the exact same values in the column on the left. The column on the right compare the scaled values with their unscaled counterpart (between 2 and 2)
This example is using presets: seed = .8 (45875)
and chaos = 1.8 (62259).
(The seed value generated is always between 1
and 1 (or 0 and 49152)
16bit value, where 0 through 65536 = 2 to 2 floating
point value
30522
0.137085
48692
0.971924
21552
0.684570
35582
0.171753
48462
0.957886
22472
0.628418
37652
0.298096
46622
0.845581
28222
0.277466
47082
0.873657
27072
0.347656
45702
0.789429
30982
0.109009
48922
0.985962
20632
0.740723
33282
0.031372
49152
1.000000
19712
0.796875
30522
0.137085
48692
0.971924
21552
0.684570
35582
0.171753
48462
0.957886
22472
0.628418
float printme;
unsigned int
seed, chaos, noteout, n, temp0, temp1, temp2;
/* Assume, like the BASIC Stamp, all integers are 16 bit unsigned */
main()
{
/* 16
bit range is 65536, seed range is .99 to .99, and chaos range is 0 to
1.99 */
/* so
we let 0=2, 16384=1, 32768=0, 49152=1, 65536=2 */
seed = 45875; /* .8 * 16384 + 32768 */
chaos = 62259; /* 1.8 * 16384 + 32768 */
for(n=1;n<=24;++n)
{
/* 
*/
if(seed<32768)
temp0 = 32768  seed;
else
temp0 = seed  32768;
/* temp0 is now in the range of 0 to 32768 (15bits); 0 to 2, absolute value
*/
/* the
sign doesn't matter, since seed gets squared */
temp1 = temp0 / 128;
temp1 = temp1 * temp1;
/* temp1 is now seed squared, in the 0  256 range (8bits) */
/* this
only works for seed <= 1.414, ok since seed range is .99 to .99 */
/* 
*/
temp2
= 0; /* temp2 keeps track of whether chaos is negative */
if(chaos<32768)
{
temp0 = 32768  chaos;
temp2
= 1;
}
else
temp0 = chaos  32768;
temp0 = temp0 / 128;
temp1 = temp1 / 128;
/* 
*/
if(temp2)
seed
= 49152 + (temp0 * temp1); /* 1rx^2 */
else
seed
= 49152  (temp0 * temp1); /* 1rx^2 */
printme = (seed  32768.0) / 16384;
printf("%d %f\n",seed, printme);
}
printf("\n");
}
'VARIABLES
temp0 var word
temp1 var word
seed var word
seedsquared
var word
chaos var word
'EXAMPLE PRESETS
seed = 45875
chaos = 62259
'EXERPT FROM
PROGRAM
if(seed<32768)
then negativeseed
'if seed is negative... (below 32768)
positiveseed:
'seed is put in range of 0 to 16386, representing 0 to 1
temp0
= seed  32768
goto keepgoing
negativeseed:
'absolute value is taken, seed is put in range of 0 to 16386
temp0
= 32768  seed
'representing 0 to 1
keepgoing:
temp1
= temp0 / 128
seedsquared
= temp1 * temp1
'temp1 is now seed squared, in the 0  16384 range (14bits)
'this only works for seed <= 1.414
'ok since seed range is 1 to 1
'SCALE THE CHAOS VALUE AND DO ANOTHER ITERATION
chaos =
chaos  32768
'put in range of 032768
chaos
= chaos / 128
'scale by square root of 65536
seedsquared
= seedsquared / 128
'temp1 is x^2, scale by square root of 65536
seed =
49152  (chaos * seedsquared) '1rx^2 where
r is positive;(temp1 is x squared)
What follows are several different methods
I tried in PBasic as experiments in an attempt to find the best method
to use to do floating point math with 16 bit unsigned integers. Some
work and some that don't.
1rx^2 or 65535  chaos * seed^2 it needs to be scaled by dividing by something like this:
65535  ((chaostop/4) * ((seed^2)/32768))
where r = chaostop/4 and the seed is divided by
2^15 to keep it from going out of the 16 bit range.
32768 * 4 is too big so is divided into 256 *
512 and can be rewritten as (rx^2)/(32768 * 4).
'VARIABLES
seed var word
chaos var word
noteout var
byte
chaostop var
byte
'EXAMPLE PRESETS
seed = 40000
chaostop = 4
'EXERPT FROM
PROGRAM
iterate:
if(seed<32767) then negativeseed 'if seed is negative... (below 32768)
positiveseed:
temp0
= seed  32767
goto keepgoing
negativeseed:
temp0
= 32767  seed
keepgoing:
seed = 65535
 (chaostop * (seed/256) * (seed/512))
chaos = chaostop/4.
The reason for this is that the vaues coming
in from the chaos slider range from 0 to 7. I am adding one and then
dividing by 4 to get a range of 1/4 to 2 for the chaos value.
In this version I tried not scaling the chaos range to be 1 to 65535 instead of 1 to 2. This was an experiment to see if the chaos value is really just acting as a proportion, and perchance this was really the most accurate way to do it. It gives the same graph, however, just on a different scale. The seed value is in the range of 0 to 65535 in place of 1 to 1 now which gives the math better resolution.
256 = 2^8
512 = 2^9, which comes from 2^7 + 2^2 (see above)
instead of dividing by 2^15 (which is 32768 or
2^8 + 2^7) the divisor is split up into two parts (2^8, and 2^7 + 2^2)
so that nothing gets out of range. What we are really doing here
is:
65536  (r * (((x32768)^2)/32768)). In
other words: 1rx^2.
VARIABLES
seed var word
chaostop var
byte
EXAMPLE PRESETS
seed = 45875
chaostop = 7
EXERPT FROM
PROGRAM
seed = 65535
 (chaostop * (seed/256) * (seed/512))
This is (as far as I can tell) the proper way to do the floating point math. I'm dealing with it in chunks, multiplying 7 bits at a time and then adding the pieces back together, making sure they never overflow the 16 bit variables. I suspect that there is a way to do this with more resolution, by multiplying smaller chunks, and I will try more variations in the future. However, with this method I finally heard the familiar fractal patterns emerging. I could hear the familiar patterns that result from iterating "1  rx^2", and for that reason, this was a good stopping place for the semester. The concept of doing floating point math with integers actually seemed very simple once I got a grasp of it. I think this concept can be taken as far as literally multiplying it out bit by bit, to get the highest possible resolution, instead of 7 bits at a time like I am doing (if I did 8 bits at a time the values would overflow. Obviously, multiplying 7 bits at a time was not my first guess).
'VARIABLES
hibits var word
medbits1 var
word
medbits2 var
word
product var
word
seed var word
chaos var word
seedsquared
var word
'EXAMPLE PRESETS
seed = 45875
chaos =
55000
chaos = chaos
 32768
'EXERPT FROM
PROGRAM
if(seed<32768)
then negativeseed
'if seed is negative... (below 32768)
positiveseed:
seed =
seed  32768
'make seed range 0 to 32768 (0 to 2, absolute value)
'however, the seed never exceedes the range of 0 to 1
'which is 1 to 16384 at this point
'the sign doesn't matter, since seed gets squared
goto keepgoing
negativeseed:
seed =
32768  seed
keepgoing:
hibits
= (seed/128) * (seed/128)
'high bits * high bits +
medbits1
= ((seed*128)/256) * (seed/128)
'low bits * high bits +
medbits2
= (seed/128) * ((seed*128)/256)
'high bits * low bits +
seedsquared
= hibits + (medbits1/256) + (medbits2/256)
'MULTIPLY CHAOS * SEED SQUARED
hibits
= (chaos/128) * (seedsquared/128)
'high bits * high bits +
medbits1
= ((chaos*128)/256) * (seedsquared/128)
'low bits * high bits +
medbits2
= (chaos/128) * ((seedsquared*128)/256)
'high bits * low bits +
product
= hibits + (medbits1/256) + (medbits2/256)
' 1  rx^2  (rx^2 is "product") 
seed =
49152  product
'1rx^2
'49152 is 1 now instead of 16384 to make 0 = 32000
'so that negative numbers can be represented