# Logistic.ck

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;