/* Framework for break-slicing by Kassen, 2008 Permision granted to copy and extend, kindly credit me if you use this. No waranties, no refunds; please mind your speakers and neighbours. Remixing and extending strongly encouraged; that's what it's here for. Questions may go to the ChucK-users mailing list. We start with a analysis of the break instead of cutting a half/quart/etc length inspired by Nick Collins's papers. I warmly reccomend that anybody seriously interested in extending this reads those. */ //duh "amn.wav" => string my_sample; //max amount of hits to detect 16 => int index_length; //minimal duration //below this slices will be discarded 50::ms => dur min_dur; //fraction of last peak the amplitude needs to decay to //before starting new slice .7 => float amp_reset; // our patch SndBuf buf => FFT fft =^ RMS rms => blackhole; buf => LiSa l => dac; UAnaBlob blob; buf.read(my_sample); buf.samples()::samp => l.duration; // set parameters 1024 => fft.size; // set hann window Windowing.hann(fft.size()) => fft.window; float peak_values[index_length]; dur peak_index[index_length]; dur dur_index[index_length]; now + buf.samples()::samp => time later; 1 => l.record; int index; float last_value; int direction; dur start_env; float env_max; // control loop while( now < later ) { // upchuck: take fft then rms rms.upchuck() @=> blob; //if amplitude is ramping up if (blob.fval(0) > last_value) { //detect start of ramping up if (!direction) { if( blob.fval(0) < (env_max * amp_reset)) { buf.pos()::samp => start_env; //reset 0 => env_max; } } 1 => direction; } else { //must be a peak if(direction) { //if peak is higher then the lowest peak yet detected if(env_max > peak_values[peak_values.cap()-1] ) { //find proper spot in sorted list to insert peak_values.cap()-1 => index; while ( index && (env_max > peak_values[index-1]) ) { index--; } insert(index, env_max, peak_values); insert(index, start_env, peak_index); } } 0 => direction; } if ( direction && (blob.fval(0) > env_max) ) { blob.fval(0) => env_max; } blob.fval(0) => last_value; // advance time fft.size()::samp => now; } 0 => l.record; sort(peak_index); //remove very small slices for (0 => index; index < peak_index.cap(); index++) { if ( (index+1< peak_index.cap() ) && ( (peak_index[index+1] - peak_index[index]) < min_dur) ) { <<<"removing", index>>>; remove(index+1, peak_index) @=> peak_index; -1 => index; } } dur_index.cap( peak_index.cap() ); for (0=> index; index < peak_index.cap(); index++) { if (index == peak_index.cap()-1) { (buf.samples()::samp - peak_index[index]) => dur_index[index]; } else { peak_index[index+1] - peak_index[index] => dur_index[index]; } } //report on slices left. <<>>; int score[dur_index.cap()]; for (0=> int i; i< score.cap(); i++) { i => score[i]; } 1 => l.play; 0 => index; 0 => int counter; int i; int temp, A, B; //actual breakage starts here. //purely random re-ordering while preserving slice-length //all slices used once and only once; //loop length should be preserved. //proof of concept only, //needs cleverness/ stupidity; please add while(1) { //once every 4 repeats.... if(!(counter%4)&& !index) { //rest to straight for (0=> i; i< score.cap(); i++) { i => score[i]; } } //every repeat if(!index) { //re-order order for(0 => i; i< 3; i++) { Std.rand2(1, dur_index.cap()-1)=> A; Std.rand2(1, dur_index.cap()-1)=> B; score[A] => temp; score[B] => score[A]; temp => score[B]; //take larger blocks where possible... maybe if( (A+1 temp; score[B+1] => score[A+1]; temp => score[B+1]; if( (A+2 temp; score[B+2] => score[A+2]; temp => score[B+2]; if( (A+3 temp; score[B+3] => score[A+3]; temp => score[B+3]; } } } } } peak_index[ score[index]] => l.playPos; dur_index[ score[index]] => now; ++index%peak_index.cap() => index; if (!index) counter++; } ///////////////////////////////////////////////////////// //////////////////////functions/////////////// //////////////////////////////////////////////////// fun void insert(int start, float value, float array[]) { if (start == array.cap()-1 ) { value => array[start]; return; } else if (start >= array.cap() ) { <<<"insert; indext too large for array">>>; return; } else { for (array.cap()-2 => int i; i >= start; i--) { array[i] => array[i+1]; } value => array[start]; return; } } fun void insert(int start, dur value, dur array[]) { if (start == (array.cap()-1) ) { value => array[start]; return; } else if (start >= array.cap() ) { <<<"insert; indext too large for array">>>; return; } else { for (array.cap()-2 => int i; i >= start; i--) { array[i] => array[i+1]; } value => array[start]; return; } } fun dur[] remove( int target, dur input[] ) { int i; for(target => i; i < input.cap()-1; i++) input[i+1] => input[i]; dur temp[input.cap()-1]; for(0 => i; i temp[i]; return temp; } //don't uncomment or risk seg-fault. //not used anyway, Ge's on the case. /* fun float[] remove( int target, float input[] ) { int i; for(target => i; i < input.cap()-1; i++) input[i+1] => input[i]; float temp[input.cap()-1]; for(0 => i; i temp[i]; return temp; } */ //bubble-sort-ish //stupid but probably fine for low array lengths. //made quickly, never returned to fun void sort( dur input[]) { dur temp; for (0 => int index; index< input.cap()-1; index++) { if(input[index]> input[index+1]) { input[index] => temp; input[index+1] => input[index]; temp => input[index+1]; -1 => index; } } }