/* 'The Green Child' is a weird little book about an English man who becomes dictator of a South American country who then fakes his own death to return to the UK. He saves a green girl from being murdered and she takes him back to her homeland, which is in some magical caves underground. He lives with these people in an anarchist paradise. They goof around in pools until they're old enough to be over that. Then they serve the community for a while taking food to the kids in the pools and the wise men who live in alcoves. Then they retire to a life of conetmplation as wise men in the little alcoves. The highest art form is making these crystals that they hit with hammers. A wise man is supposed to forge his crystals then play them in every possible combination, then... die? I can't remember. Anyway, I changed that around a little to have a few voices cycle through all tuples. Each voice confined to a particular octave but all playing the same scale. I wanted a piece that would go indefinitely, so once a set of tuples is exhausted the key modulates. */ //Scales [ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11 ] @=> int chrom[];//Chromatic [ 0, 2, 4, 5, 7, 9, 11 ] @=> int diat[];//Diatonic [ 0, 2, 4, 6, 8, 10 ] @=> int whole[];//Whole tone [ 0, 2, 4, 7, 9 ] @=> int pent[];//Pentatonic ///End scales //You can change the scale //chrom @=> int thisScale[]; //diat @=> int thisScale[]; //whole @=> int thisScale[]; pent @=> int thisScale[]; //Midi value of initial tonic. //Cycle sets modulation after all tuples are played. 7=5ths, 5=4ths etc. //midiMin sets the lowest note that will be used as a tonic. 45 => int tonicMidi; 7 => int cycle; 45 => int midiMin; 0.2 => float maxGain; //Don't blow your speakers. //A few utilitarian things... fun void copyArray( int source[], int dest[] ){ for( 0 => int i; i < source.cap(); ++i ) source[i]=>dest[i]; }; fun void shuffle( int source[] ) { //shuffles the array 'source' int sourceStrike; int rand; for( source.cap()-1 => int strike; strike > 0; --strike ) { Math.random2( 0, strike ) => rand; source[ strike ] => sourceStrike; source[ rand ] => source[ strike ]; sourceStrike => source[ rand ]; } }; fun void ramp( UGen osc, float gainTarget, float rampDur ) { osc.gain() => float start; gainTarget - start => float spread; rampDur/100.0 => float timeStep; spread/100.0 => float gainStep; for( 0 => int r; r < 100; ++r) { gainStep +=> start; osc.gain( start ); timeStep * 1::ms => now; } }; //Set up the voices. SinOsc v0 => JCRev r0 => dac; SinOsc v1 => JCRev r1 => dac; SinOsc v2 => JCRev r2 => dac; //Octave relative to base note for each voice. 0 => int vOct0; 0 => int vOct1; 1 => int vOct2; r0.mix( 0.1 ); r1.mix( 0.2 ); r2.mix( 0.2 ); v0.gain(0.0); v1.gain(0.0); v2.gain(0.0); //Initialize arrays that will hold the notes //for each voice. int shuffle0[ thisScale.cap() ]; int shuffle1[ thisScale.cap() ]; int shuffle2[ thisScale.cap() ]; //Copy scale into each array. copyArray( thisScale, shuffle0 ); copyArray( thisScale, shuffle1 ); copyArray( thisScale, shuffle2 ); int m0; int m1; int m2; //these will be the midi numbers for each voice. while(true) { //First voice will sustain while all permuations of remaining //two voices are played. Then the first note advances and //All remaining permutations play. shuffle(shuffle0);//Shuffle before entering loop. for( 0 => int i; i < thisScale.cap(); ++i ) { //Get Midi number for scale tone tonicMidi + shuffle0[i] + 12 * vOct0 => m0; Std.mtof( m0 ) => v0.freq; ramp(v0, maxGain * tonicMidi/m0, 100);//ramp gain shuffle(shuffle1);//Shuffle before entering loop. for( 0 => int j; j < thisScale.cap(); ++j ) { tonicMidi + shuffle1[j] + 12 * vOct1 => m1; Std.mtof( m1 ) => v1.freq; ramp(v1, maxGain * tonicMidi/m1, 100); // shuffle(shuffle2); //Shuffle before entering loop. for( 0 => int k; k < thisScale.cap(); ++k ) { tonicMidi + shuffle2[k] + 12 * vOct2 => m2; Std.mtof( m2 ) => v2.freq; ramp(v2, maxGain * tonicMidi/m2, 100); Math.random2( 1, 8 ) * 250::ms => now; //Advance clock randomly. ramp(v2, 0.0, 100); //Ramp down third voice } ramp(v1, 0.0, 100); //Ramp down first voice } ramp(v0, 0.0, 100); //Ramp down second voice } //Modulate but stay within an octave of midiMin cycle +=> tonicMidi; if(tonicMidi >= midiMin + 12){ tonicMidi - 12 => tonicMidi; } }