// Audio Visualizer Test by Senyuan 1024 => int WINDOW_SIZE; 20 => int NUM_LINES; // number of spectral lines being rendered 256 => int NUM_POINTS; // number of points per line, aka resolution 5 => int NUM_HISTORY; 5 => float WIDTH; 10 => float DEPTH; 10 => float HEIGHT; 2 => int NUM_POINTS_HISTORY; 3 => float MAG_BOOST; float mags[WINDOW_SIZE]; float phas[WINDOW_SIZE]; float mags_prev[WINDOW_SIZE]; float mags_norm[WINDOW_SIZE]; float mags_norm_prev[WINDOW_SIZE]; float phas_prev[WINDOW_SIZE]; vec3 colors[WINDOW_SIZE]; vec3 colors_history[NUM_HISTORY][WINDOW_SIZE]; fun void colors_history_updator(){ for(int i; i < (NUM_HISTORY - 1); i++) { colors_history[i] @=> colors_history[i+1]; } } // fullscreen // GG.fullscreen(); // put camera on a dolly GG.camera() --> GGen dolly --> GG.scene(); // position GG.camera().posX( 0 ); GG.camera().posY( 1 ); GG.camera().posZ( 5 ); // set clipping plane GG.camera().clip( .05, 128 * 10 ); // set bg color GG.scene().backgroundColor( @(0,0,0) ); // 0. Input File SndBuf2 buf => dac; buf.read("./playinggod.wav"); // Read in our WAV file buf => Gain input; // 1. Microphone // adc => Gain input; // 2. Sine wave. Also uncomment the line spork ~ controlSine( sine ) // SinOsc sine => Gain input => dac; .15 => sine.gain; // accumulate samples from mic input => Flip accum => blackhole; // take the FFT input => PoleZero dcblocker => FFT fft => blackhole; // set DC blocker .95 => dcblocker.blockZero; // set size of flip WINDOW_SIZE => accum.size; // set window type and size Windowing.hann(WINDOW_SIZE) => fft.window; // set FFT size (will automatically zero pad) WINDOW_SIZE * 2 => fft.size; // get a reference for our window for visual tapering of the waveform Windowing.hann(WINDOW_SIZE) @=> float window[]; // sample array float samples[WINDOW_SIZE]; // FFT response complex response[WINDOW_SIZE]; Spectrum spec; // spec.init(); spec --> GG.scene(); History hist[NUM_HISTORY]; Waveform wave[NUM_HISTORY]; for( int i; i < NUM_HISTORY; i++ ){ hist[i].init(i); hist[i].posY(2 * i); hist[i] --> GG.scene(); } for( int i; i < NUM_HISTORY; i++ ){ wave[i].init(i); wave[i].posY(- 2 * i); wave[i] --> GG.scene(); } spec.posY(0); // wave.posY(2); int idx[NUM_LINES]; for(int i; i < NUM_LINES; i++) { Math.round((WINDOW_SIZE / 2 / NUM_LINES) * i)$int => idx[i]; } 1$int => idx[0]; 2$int => idx[1]; 4$int => idx[2]; 8$int => idx[3]; class Spectrum extends GGen { // initial lines in the stream GLines lines[NUM_LINES]; // internal vertice location per line, reused over the update of all lines vec3 lineVertices[NUM_POINTS]; for( GLines line : lines ) { // aww yea, connect as a child of this GGen line --> this; // color // line.mat().color( color; ); } fun void update( float dt ) { for( int i; i < NUM_LINES; i++ ) { for( int j; j < NUM_POINTS; j++ ) { -WIDTH/2 + (i$float) / NUM_LINES * WIDTH => lineVertices[j].x; mags_prev[idx[i]] * Math.sin( ( (j$float)/NUM_POINTS + phas_prev[idx[i]] ) * 2 * Math.pi * idx[i]) => lineVertices[j].y; - (j$float)/NUM_POINTS * DEPTH => lineVertices[j].z; } // <<<"Value test: ", mags[i]>>>; lineVertices => lines[i].geo().positions; colors[idx[i]] => lines[i].mat().color; } } } class History extends GGen { // initial lines in the stream GLines lines[NUM_LINES]; int location; fun void init(int l){ l => location; } // internal vertice location per line, reused over the update of all lines vec3 lineVertices[NUM_POINTS_HISTORY]; for( GLines line : lines ) { // aww yea, connect as a child of this GGen line --> this; // color // line.mat().color( color; ); } // iterate over line GGens // for( int i; i < NUM_HISTORY; i++ ) // { // for( int j; j < NUM_LINES; j++){ // lines[i][j] --> this; // } // } fun void update( float dt ) { for( int i; i < NUM_LINES; i++) { for( int j; j < NUM_POINTS_HISTORY; j++) { -WIDTH/2 + (i$float) / NUM_LINES * WIDTH => lineVertices[j].x; HEIGHT/NUM_HISTORY * j => lineVertices[j].y; - DEPTH => lineVertices[j].z; } // <<<"Value test: ", mags[i]>>>; lineVertices => lines[i].geo().positions; colors_history[location][idx[i]] => lines[i].mat().color; } } } class Waveform extends GGen { // initial lines in the stream GLines lines[NUM_LINES]; int location; fun void init(int l){ l => location; } // internal vertice location per line, reused over the update of all lines vec3 lineVertices[NUM_POINTS_HISTORY]; for( GLines line : lines ) { // aww yea, connect as a child of this GGen line --> this; // color // line.mat().color( color; ); } // iterate over line GGens // for( int i; i < NUM_HISTORY; i++ ) // { // for( int j; j < NUM_LINES; j++){ // lines[i][j] --> this; // } // } fun void update( float dt ) { for( int i; i < NUM_LINES; i++) { for( int j; j < NUM_POINTS_HISTORY; j++) { -WIDTH/2 + (i$float) / NUM_LINES * WIDTH => lineVertices[j].x; - HEIGHT/NUM_HISTORY * j => lineVertices[j].y; 0 => lineVertices[j].z; } // <<<"Value test: ", mags[i]>>>; lineVertices => lines[i].geo().positions; colors_history[location][idx[i]] => lines[i].mat().color; } } } // class History extends GGen // { // // initial lines in the stream // GLines lines[NUM_HISTORY][NUM_LINES]; // // internal vertice location per line, reused over the update of all lines // vec3 lineVertices[NUM_POINTS_HISTORY]; // // iterate over line GGens // for( int i; i < NUM_HISTORY; i++ ) // { // for( int j; j < NUM_LINES; j++){ // lines[i][j] --> this; // } // } // fun void update( float dt ) // { // for( int h; h < NUM_HISTORY; h++ ) // { // for( int i; i < NUM_LINES; i++) // { // for( int j; j < NUM_POINTS_HISTORY; j++) // { // -WIDTH/2 + (i$float) / NUM_LINES * WIDTH => lineVertices[j].x; // 0 - HEIGHT/NUM_HISTORY * h * j => lineVertices[j].y; // 0.0 => lineVertices[j].z; // } // // <<<"Value test: ", mags[i]>>>; // lineVertices => lines[i].geo().positions; // colors_history[h][idx[i]] => lines[i].mat().color; // } // } // } // } // smoothing fun void smoothing( float prev[], float curr[], float factor) { // factor * prev + (1 - factor) * curr => curr; // curr => prev; for (int i; i < prev.size(); i++) { factor * prev[i] + (1 - factor) * curr[i] => prev[i]; } } fun void map2spectrum( complex in[] ) { // mapping to xyz coordinate int i; for( auto s : in ) { MAG_BOOST * Math.sqrt( (s$polar).mag * 25 ) => mags[i] => mags_norm[i]; (s$polar).phase => phas[i]; // @(mags_norm_prev[i]*5, (i$float)/in.size(), (i$float)/in.size() ) * 1.5 => colors[i]; @(0.4, (i$float)/in.size(), (i$float)/in.size()*2 ) * 1.5 => colors[i]; i++; // <<<"Value test: ", (s$polar).mag>>>; } colors_history_updator(); colors @=> colors_history[0]; // smoothing( mags_norm_prev, mags_norm, 0.99999); smoothing( mags_prev, mags, 0.9); smoothing( phas_prev, phas, 0.99995); } // do audio stuff fun void doAudio() { while( true ) { // upchuck to process accum accum.upchuck(); // get the last window size samples (waveform) accum.output( samples ); // upchuck to take FFT, get magnitude reposne fft.upchuck(); // get spectrum (as complex values) fft.spectrum( response ); // jump by samples WINDOW_SIZE::samp/2 => now; } } spork ~ doAudio(); // fps printer fun void printFPS( dur howOften ) { while( true ) { <<< "fps:", GG.fps() >>>; howOften => now; } } spork ~ printFPS(1::second); fun void controlSine( Osc s ) { while( true ) { 100 + (Math.sin(now/second*5)+1)/2*20000 => s.freq; 10::ms => now; } } // uncomment if input is sine wave // spork ~ controlSine( sine ); // graphics render loop // 2::second => now; while( true ) { // map to spectrum display map2spectrum( response ); // next graphics frame GG.nextFrame() => now; }