From CSWiki
// Logistic Map Sonification
// -- written by Graham Percival, gperciva@uvic.ca, Feb 2005
// Placed in the public domain.
// X(n+1) = k*( X(n) )*( 1-X(n) )
// For more details about this wonderful equation, see
// http://en.wikipedia.org/wiki/Logistic_map
0=>int EXTERNALSOUNDS; // this program uses sitar sounds, but if
// desired we can send frequencies as OSC messages to another program
// or machine to synthesize (for reasons of performance)
16 => int MAXSOUND; // if we're making the sounds in chuck, how
// many sitar notes can your computer generate. On my
// G4 1ghz laptop, I can do 16 reliably.
100000.0=>float DECIMAL; // precision of our floating-point
// operations. We don't want to use real floats.
1=>int APPROX; // set to 0 if you want to use real floats.
// But trust me, you don't want to. :)
dur wait;
50::ms=>dur MINWAIT; // our sounds begin 1 second apart and
// then gradually speed up. This is the fastest the sounds
// get.
0=>int ALTERNATEPAN; // the plan was to make each pluck
// alternate between being panned at pp and 1-pp, but I don't
// think this is working right now. Consider it an
// exercise left to the reader! :)
// (it's not hard to do, but this code served
// its purpose for me, so I'm not going to bother)
float pf; // pluck frequency
float pp; // pluck pan
0=>int i; int j;
int done;
130=>int MAXSIZE; // size of our array for comparing previous
// values of X(n).
float x[MAXSIZE];
for (0=>i; i<MAXSIZE-1; i++) {
0=>x[i];
}
float xn; // current value of... you guessed it, X(n)
int cycle; // length of cycle
0=>int nextuse;
pan2 pans[MAXSOUND];
// init output formats. Unfortunately we need to init both
// types of output.
Sitar sound[MAXSOUND];
for (0=>i; i<MAXSOUND; i++) {
sound[i]=>pans[i]=>dac;
}
OscSend xmit;
xmit.setHost( "localhost", 7777 );
fun void pluck(int use, float f, float p) {
if (EXTERNALSOUNDS) {
xmit.startMsg( "a", "f");
xmit.addFloat(std.ftom(f));
} else {
f=>sound[use].freq;
1=>sound[use].pluck;
1=>sound[use].noteOn;
p=>pans[use].pan;
}
}
fun float docycle(float a, float init) {
init=>x[0];
0=>i;
1000::ms=>wait;
0=>done;
// keeps on calculating X(n+1) until we find a cycle
while (!done)
{
i++;
// if we can't find a cycle within MAXSIZE values, assume
// that we're in chaos. This is not necessarily true,
// but it's an acceptable approximation for this sonification
if (i<MAXSIZE) {
x[i-1] => xn;
} else {
<<< "found CHAOS for a =", a>>>;
x[i-1]=>x[0];
10=>cycle;
break;
}
// it takes too long to calculate an exact match,
// so I use some fake floating-point math with
// my own number of decimal places.
// NB: this approximation results in cycles being found
// earlier than they should be (for example,
if (APPROX) {
((a*xn*(1-xn)*DECIMAL) $int )/DECIMAL => x[i];
} else {
a*xn*(1-xn) => x[i];
}
// look for a match (signifying a cycle, since we only
// have X(n-1) in the equation (no n-2, n-3, etc))
for (0=>j; j<MAXSIZE-1; j++) {
if ( x[j]==x[i]) {
if (j!=i) {
1=>done;
std.abs(i-j)=>cycle;
<<< "found pattern of length", cycle, "for a =", a>>>;
x[i]=>x[0];
break;
}
}
}
x[i]*600+200=>pf;
// doesn't work; would be simple to fix; go ahead and have fun. :)
if (ALTERNATEPAN)
x[i]=>pp;
else
1-x[i]=>pp;
pluck(nextuse,pf,pp);
// alternate through sitars (ie if sitar 2 is in use, play sound
// with sitar 3)
nextuse++;
if (nextuse>=MAXSOUND) 0=>nextuse;
if (wait>MINWAIT) {
wait*0.9=>wait;
}
wait=>now;
}
// By now we've found our cycle (or given up and declared chaos).
// For aesthetic reasons, we play the cycle three more times before
// looking for a new cycle with new values.
// this program is badly designed; this code is the same
// as in the first half of this function. This part should be
// moved into a separate function. :(
for (1=>i; i<=3*cycle; i++) {
if (i<MAXSIZE) {
x[i-1] => xn;
} else {
x[i-1]=>x[0];
0=>i;
} if (APPROX) {
((a*xn*(1-xn)*DECIMAL) $int )/DECIMAL => x[i];
} else {
a*xn*(1-xn) => x[i];
}
x[i]*600+200=>pf;
if (ALTERNATEPAN)
x[i]=>pp;
else
1-x[i]=>pp;
pluck(nextuse,pf,pp);
nextuse++;
if (nextuse>=MAXSOUND) 0=>nextuse;
if (wait>MINWAIT) {
wait*0.9=>wait;
}
wait=>now;
}
if (x[i]!=0) return x[i];
else return std.randf();
}
0.1=>float y;
docycle(2.4,y) => y;
docycle(2.9,y) => y;
docycle(3.2,y) => y;
//docycle(3.3,y) => y;
docycle(3.4,y) => y;
docycle(3.5,y) => y;
docycle(3.53,y) => y;
docycle(3.55,y) => y;
docycle(3.7,y) => y;
docycle(3.832,y) => y;
docycle(3.845,y) => y;
docycle(3.848,y) => y;
2::second=>now;