Logistic.ck

From CSWiki
Revision as of 17:45, 29 July 2006 by Gewang (talk | contribs)

(diff) ← Older revision | Latest revision (diff) | Newer revision → (diff)
Jump to: navigation, search
// 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;