# Logistic.ck

(diff) ← Older revision | Latest revision (diff) | Newer revision → (diff)
```// 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) )
// 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,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");
} 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=>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;
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;
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=>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;
```