//---------------------------- // Homework 2 - Milestone 1 // Chakravyuh // Author: Neha Rajagopalan //---------------------------- // fullscreen GG.fullscreen(); // position GG.camera().posZ( 3 ); // set bg color GG.scene().backgroundColor( @(0,0,0) ); // setting camera on dolly GG.camera() --> GGen dolly --> GG.scene(); //-------------------- // Drawing circular boundaries //---------------------- // how many circles? 15 => int NUM_CIRCLES; // how many vertices per circle? (more == smoother) 720 => int N; // normalized radius 1 => float RADIUS; // creating a custom GGen class for a 2D circle class Circle extends GGen { // for drawing our circle GLines circle --> this; // randomize rate Math.random2f(2,3) => float rate; // default color color( @(.5, 1, .5) ); // initialize a circle fun void init( int resolution, float radius ) { // incremental angle from 0 to 2pi in N steps 2*pi / resolution => float theta; // positions of our circle vec3 pos[resolution]; // previous, init to 1 zero @(radius,0) => vec3 prev; // loop over vertices for( int i; i < pos.size(); i++ ) { // rotate our vector to plot a circle // https://en.wikipedia.org/wiki/Rotation_matrix Math.cos(theta)*prev.x - Math.sin(theta)*prev.y => pos[i].x; Math.sin(theta)*prev.x + Math.cos(theta)*prev.y => pos[i].y; // just XY here, 0 for Z 0 => pos[i].z; // remember v as the new previous pos[i] => prev; } // set positions circle.geo().positions( pos ); } fun void color( vec3 c ) { circle.mat().color( c ); } fun void update( float dt ) { Math.random2f(0.75, 1) => float s; circle.sca( @(s,s,s ) ); // uncomment for xtra weirdness // circle.rotY(dt*rate/3); } } // an array of our custom circles Circle circles[NUM_CIRCLES]; // iterate over circles array for( auto circ : circles ) { // initialize each circ.init( N, RADIUS ); // connect it circ --> GG.scene(); // randomize location in XY @(0,0,0) => circ.pos; } // function to cycle colors fun void cycleColors( float dt ) { // iterate over circles array for( auto circ : circles ) { // cycle RGB @((0.75+Math.sin(now/second)/2), (0.5+Math.sin(now/second)/2), (Math.sin(now/second)/2) ) => circ.color; } } //------------------------ //Drawing the waveform //------------------------- // window size 1024 => int WINDOW_SIZE; // y position of waveform 2 => float WAVEFORM_Y; // width of waveform and spectrum display 10 => float DISPLAY_WIDTH; // waveform renderer GLines waveform --> GG.scene(); waveform.mat().lineWidth(1.0); // translate up waveform.posZ( WAVEFORM_Y ); // color0 waveform.mat().color( @(0.75+Math.sin(now/second)/2, 0.5+Math.sin(now/second)/2, Math.sin(now/second)/2)*1.5 ); // which input? adc => Gain input; // SinOsc sine => Gain input => dac; .15 => sine.gain; // accumulate samples from mic input => Flip accum => blackhole; // take the FFT input => PoleZero dcbloke => FFT fft => blackhole; // set DC blocker .95 => dcbloke.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[0]; // FFT response complex response[0]; // a vector to hold positions vec3 positions[WINDOW_SIZE]; // map audio buffer to 3D positions fun void map2waveform( float in[], vec3 out[] ) { if( in.size() != out.size() ) { <<< "size mismatch in map2waveform()", "" >>>; return; } // mapping to xyz coordinate int i; DISPLAY_WIDTH => float width; for( auto s : in ) { // space evenly in X -width/2 + width/WINDOW_SIZE*i => out[i].x; // map y, using window function to taper the ends s*6 * window[i] => out[i].y; // a constant Z of 0 0 => out[i].z; // increment i++; } } // 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(.25::second); fun void controlSine( Osc s ) { while( true ) { 100 + (Math.sin(now/second*5)+1)/2*20000 => s.freq; 10::ms => now; } } // spork ~ controlSine( sine ); //------------------------ // Spectral waveform //------------------------- -2.5 => float SPECTRUM_Y; 10 => int WATERFALL_DEPTH; Waterfall waterfall --> GG.scene(); waterfall.posY( SPECTRUM_Y ); // custom GGen to render waterfall class Waterfall extends GGen { // waterfall playhead 0 => int playhead; // lines GLines wfl[WATERFALL_DEPTH]; // color @(0.75+Math.sin(now/second)/2, 0.5+Math.sin(now/second)/2, Math.sin(now/second)/2) => vec3 color; // iterate over line GGens for( GLines w : wfl ) { // aww yea, connect as a child of this GGen w --> this; // color w.mat().color( @(0.75+Math.sin(now/second)/2, 0.5+Math.sin(now/second)/2, Math.sin(now/second)/2)); } // copy fun void latest( vec3 positions[] ) { // set into positions => wfl[playhead].geo().positions; // advance playhead playhead++; // wrap it WATERFALL_DEPTH %=> playhead; } // update fun void update( float dt ) { // position playhead => int pos; // for color WATERFALL_DEPTH/2.5 => float thresh; // depth WATERFALL_DEPTH - thresh => float fadeChunk; // so good for( int i; i < wfl.size(); i++ ) { // start with playhead-1 and go backwards pos--; if( pos < 0 ) WATERFALL_DEPTH-1 => pos; // offset Z wfl[pos].posZ( -i ); if( i > thresh ) { wfl[pos].mat().color( ((fadeChunk-(i-thresh))/fadeChunk) * color ); } else { wfl[pos].mat().color( color ); } } } } fun void map2spectrum( complex in[], vec3 out[] ) { if( in.size() != out.size() ) { <<< "size mismatch in map2spectrum()", "" >>>; return; } // mapping to xyz coordinate int i; DISPLAY_WIDTH => float width; for( auto s : in ) { // space evenly in X -width/2 + width/WINDOW_SIZE*i => out[i].x; // map frequency bin magnitide in Y 5 * Math.sqrt( (s$polar).mag * 25 ) => out[i].y; // constant 0 for Z 0 => out[i].z; // increment i++; } waterfall.latest( out ); } // time loop for everything while( true ) { // cycle colors cycleColors( GG.dt() ); // map to interleaved format map2waveform( samples, positions ); // set the mesh position waveform.geo().positions( positions ); // map to spectrum display map2spectrum( response, positions ); // uncomment to rotate camera around scene, for reasons unknown dolly.rotY(GG.dt()*.5); // next frame GG.nextFrame() => now; }