///////////////////// Narrative Section ///////////////////// BPF filter; // set filter coefficients 1000 => filter.freq; 1 => filter.Q; SinOsc osc1; SinOsc osc2; SinOsc osc3; // osc1 => filter => JCRev r => dac; // osc2 => filter => r => dac; osc1 => Envelope e => JCRev r => Gain input; osc2 => e => r => input; osc3 => e => r => input; ModalBar bar => Gain g => r => input; input => dac; // set gain 0.02 => osc1.gain; 0.005 => osc2.gain; 0.005 => osc3.gain; // set reverb mix 0.05 => r.mix; // set a scale in semitones [ 0, 2, 4, 6, 7, 9, 11 ] @=> int scale[]; // Bach Cello Suite No.1 Prelude [43, 50, 59, 57, 59, 50, 59, 50, 43, 50, 59, 57, 59, 50, 59, 50, 43, 52, 60, 59, 60, 52, 60, 52, 43, 52, 60, 59, 60, 52, 60, 52, 43, 54, 60, 59, 60, 54, 60, 54, 43, 54, 60, 59, 60, 54, 60, 54, 43, 55, 59, 57, 59, 55, 59, 55, 43, 55, 59, 57, 59, 55, 59, 54, 43, 52, 59, 57, 59, 55, 54, 55, 52, 55, 54, 55, 47, 50, 49, 47, 49, 55, 57, 55, 57, 55, 57, 55, 49, 55, 57, 55, 57, 55, 57, 55, 54, 57, 62, 61, 62, 57, 55, 57, 54, 57, 55, 57, 50, 54, 52, 50, 40, 47, 55, 54, 55, 47, 55, 47, 40, 47, 55, 54, 55, 47, 55, 47, 40, 49, 50, 52, 50, 49, 47, 45, 55, 54, 52, 62, 61, 59, 57, 55, 54, 52, 50, 62, 57, 62, 54, 57, 50, 52, 54, 57, 55, 54, 52, 50, 56, 50, 53, 52, 53, 50, 56, 50, 59, 50, 53, 52, 53, 50, 56, 50, 48, 52, 57, 59, 60, 57, 52, 50, 48, 52, 57, 59, 60, 57, 54, 52, 51, 54, 51, 54, 57, 54, 57, 54, 51, 54, 51, 54, 57, 54, 57, 54, 55, 54, 52, 55, 54, 55, 57, 54, 55, 54, 52, 50, 48, 47, 45, 43, 42, 48, 50, 48, 50, 48, 50, 48, 42, 48, 50, 48, 50, 48, 50, 48, 43, 47, 53, 52, 53, 47, 53, 47, 43, 47, 53, 52, 53, 47, 53, 47, 43, 48, 52, 50, 52, 48, 52, 48, 43, 48, 52, 50, 52, 48, 52, 48, 43, 54, 60, 59, 60, 54, 60, 54, 43, 54, 60, 59, 60, 54, 60, 54, 43, 50, 59, 57, 59, 55, 54, 52, 50, 48, 47, 45, 43, 42, 40, 38, 37, 45, 52, 54, 55, 52, 54, 55, 37, 45, 52, 54, 55, 52, 54, 55, 36, 45, 50, 52, 54, 50, 52, 54, 36, 45, 50, 52, 54, 50, 52, 54, 36, 45, 50, 54, 57, 61, 62] @=> int melody[]; fun void controlNarrative() { // wait the show to start 10::second => now; 55 => float purefreq; 0 => osc1.freq; 0 => osc2.freq; 0 => osc3.freq; 0.02 => osc1.gain; for(int i; i < 7; i++ ) { purefreq => osc1.freq; purefreq * 2 => purefreq; e.keyOn(); 1::second => now; e.keyOff(); 500::ms => now; } 2::second => now; Math.random2( 0, 8 ) => bar.preset; Math.random2f( 0, 1 ) => bar.stickHardness; Math.random2f( 0, 1 ) => bar.strikePosition; Math.random2f( 0, 1 ) => bar.vibratoGain; Math.random2f( 0, 60 ) => bar.vibratoFreq; Math.random2f( 0, 1 ) => bar.volume; Math.random2f( .5, 1 ) => bar.directGain; Math.random2f( .5, .8 ) => bar.masterGain; Noise n => input; 0.1 => n.gain; 2::second => now; 0.0 => n.gain; 2::second => now; .5 => bar.noteOn; 1::second => now; .1 => bar.noteOn; 4::second => now; 0.01 => osc1.gain; for(int i; i < melody.size(); i++) { melody[i] => float note; Std.mtof( note + 12 ) => float f; f => osc1.freq; f * 1.5 => osc2.freq; f * 2 => osc3.freq; e.keyOn(); (melody.size() - i)$float / melody.size() => float progress; // progress * 150::ms => now; 150::ms => now; e.keyOff(); 50::ms => now; } } ///////////////////// Visualizer Section ///////////////////// // Audio Visualizer Test by Senyuan // Add points on top of the lines to jump according to waveform // Thickness/Color mapping? 1024 => int WINDOW_SIZE; 20 => int NUM_LINES; // number of spectral lines being rendered 30 => int NUM_STACK; // numbers of stacks on each cube 32 => int SPEC_RESOLUTION; // number of points per line, aka resolution, should be equal to WINDOW_SIZE to avoid aliazing 50 => int NUM_HISTORY; 2 => int HIST_RESOLUTION; 40 => float POINT_SIZE_1; 40 => float POINT_SIZE_2; 20 => float POINT_SIZE_3; 5 => float WIDTH; 5 => float DEPTH; 3 => float HEIGHT; 5 => float WAVE_HEIGHT; 40 => int FADE_THRESH; 2.5 => float MAG_BOOST; 2 => float AMP_BOOST; float amps[SPEC_RESOLUTION]; float mags[WINDOW_SIZE]; float phas[WINDOW_SIZE]; float amps_prev[SPEC_RESOLUTION]; float mags_prev[WINDOW_SIZE]; float phas_prev[WINDOW_SIZE]; vec3 colors[NUM_LINES]; vec3 colorlog[NUM_HISTORY][NUM_LINES]; vec3 colorsumlog[NUM_HISTORY]; float amplog[NUM_HISTORY][SPEC_RESOLUTION]; float maglog[NUM_HISTORY][NUM_LINES]; 0 => int loghead; 0 => int amploghead; // create a total of 24 critical band center frequencies // [30.0, 40, 50, 100, 150, 220, 440, 660, 880, 1200, 1440, 1800, 2400, 3600, 4800, 7200, 9600, 12000, 14400, 18000] @=> float cbfc[]; [50.0, 150, 250, 350, 450, 570, 700, 840, 1000, 1170, 1370, 1600, 1850, 2150, 2500, 2900, 3400, 4000, 4800, 5800, 7000, 8500, 10500, 13500] @=> float cbfc[]; int idx[NUM_LINES]; second / samp => float srate; for(int i; i < NUM_LINES; i++) { (cbfc[i] / srate * WINDOW_SIZE)$int => idx[i]; } for(int i; i < WINDOW_SIZE; i++) { 1.0 / WINDOW_SIZE => mags_prev[i]; } // fullscreen // GG.fullscreen(); // put camera on a dolly GG.camera() --> GGen dolly --> GG.scene(); // Close Up GG.camera().posX( 5 ); GG.camera().posY( 3 ); GG.camera().posZ( 2.5 ); GG.camera().lookAt( @(0, -2, -DEPTH/2) ); // GG.camera().fov( 70 ); // Far Away // GG.camera().posX( 10 ); // GG.camera().posY( 6 ); // GG.camera().posZ( 5 ); // GG.camera().lookAt( @(0, 0, -DEPTH/2) ); // // GG.camera().fov( 70 ); // // Front // GG.camera().posX( 0 ); // GG.camera().posY( 1 ); // GG.camera().posZ( 5 ); // GG.camera().lookAt( @(0, 0, 0) ); // GG.camera().fov( 70 ); // set clipping plane GG.camera().clip( .05, 128 * 10 ); // set bg color GG.scene().backgroundColor( @(0.0 , 0.0 , 0.1) ); // 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(); spec.posY(0); History hist; hist --> GG.scene(); Waveform waveleft; Waveform waveright; waveleft.init(-1); waveright.init(1); waveleft --> GG.scene(); waveright --> GG.scene(); // WaveformFlip waveflip; // waveflip --> GG.scene(); // 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 vec3 mag2color( float m ) { vec3 color; if(m < 0.25) { 0 => color.x; 4 * m => color.y; 1 => color.z; } else if(0.25 <= m && m < 0.5) { 0 => color.x; 1 => color.y; 1 - 4 * (m - 0.25) => color.z; } else if(0.5 <= m && m < 0.75) { 4 * (m - 0.5) => color.x; 1 => color.y; 0 => color.z; } else if(0.75 <= m) { 1 => color.x; 1 - 4 * (m - 0.75) => color.y; 0 => color.z; } return color; } class Spectrum extends GGen { // initial lines in the stream GLines lines[NUM_LINES]; // GPoints lines[NUM_LINES]; // internal vertice location per line, reused over the update of all lines vec3 lineVertices[SPEC_RESOLUTION]; for( GLines line : lines ) // for( GPoints line : lines ) { // aww yea, connect as a child of this GGen line --> this; // 3 => line.mat().lineWidth; // line.mat().attenuatePoints(1); // line.mat().pointSize(POINT_SIZE_1); } float tempval; float tempsum; fun void update( float dt ) { for( int i; i < NUM_LINES; i++ ) { for( int j; j < SPEC_RESOLUTION; j++ ) { -WIDTH/2 + (i$float) / NUM_LINES * WIDTH => lineVertices[j].x; mags_prev[idx[i]] * Math.sin( ( (j$float)/SPEC_RESOLUTION + phas_prev[idx[i]] ) * 2 * Math.pi * idx[i] ) * window[ j * (WINDOW_SIZE / SPEC_RESOLUTION) ] => tempval; tempval => lineVertices[j].y; - (j$float) / SPEC_RESOLUTION * DEPTH => lineVertices[j].z; } lineVertices => lines[i].geo().positions; colors[i] => lines[i].mat().color; } } } // Defines one history frame. Need to an array of history elements class History extends GGen { // initial points in the stream GPoints points --> this; points.mat().attenuatePoints(1); points.mat().pointSize(POINT_SIZE_2); // internal vertice location per line, reused over the update of all lines NUM_HISTORY * NUM_LINES => int numPoints; float pointPos[numPoints * 3]; vec3 pointColor[numPoints]; fun void update( float dt ) { for( int i; i < NUM_LINES; i++) { for( int j; j < NUM_HISTORY; j++ ) { // get index of this point i * NUM_HISTORY + j => int index; (loghead - j) => int logread; if( logread < 0 ) { loghead + NUM_HISTORY - j => logread; } -WIDTH / 2 + (i$float) / NUM_LINES * WIDTH => pointPos[3 * index + 0]; - HEIGHT / NUM_HISTORY * j => pointPos[3 * index + 1]; maglog[logread][i] / 2 => pointPos[3 * index + 2]; 5 * colorlog[logread][i] => vec3 tempColor; if ( j > FADE_THRESH ) { tempColor * ( (NUM_HISTORY - j)$float ) / (NUM_HISTORY - FADE_THRESH) => pointColor[index]; } else { tempColor => pointColor[index]; } } } points.geo().positions(pointPos); // set vertex position data points.geo().colors(pointColor); // set vertex color data } } class Waveform extends GGen{ GPoints points --> this; points.mat().attenuatePoints(1); points.mat().pointSize(POINT_SIZE_1); // points.mat().alpha(1); NUM_HISTORY * SPEC_RESOLUTION => int numPoints; float pointPos[numPoints * 3]; vec3 pointColor[numPoints]; int direction; fun void init( int d ) { d => direction; } fun void update( float dt ) { for( int i; i < SPEC_RESOLUTION; i++) { for( int j; j < NUM_HISTORY; j++ ) { // get index of this point i * NUM_HISTORY + j => int index; (loghead - j) => int logread; if( logread < 0 ) { loghead + NUM_HISTORY - j => logread; } colorsumlog[j] / 20 => vec3 tempColor; // <<<"logread: ", logread>>>; direction * (amplog[logread][i] * 2 + WIDTH / 2) => pointPos[3 * index + 0]; - HEIGHT / NUM_HISTORY * j => pointPos[3 * index + 1]; - (i$float) / SPEC_RESOLUTION * DEPTH => pointPos[3 * index + 2]; if ( j > FADE_THRESH ) { tempColor * ( (NUM_HISTORY - j)$float ) / (NUM_HISTORY - FADE_THRESH) => pointColor[index]; } else { tempColor => pointColor[index]; } } points.geo().positions(pointPos); // set vertex position data points.geo().colors(pointColor); // set vertex color data } } } class WaveformFlip extends GGen{ GPoints points --> this; points.mat().attenuatePoints(1); points.mat().pointSize(POINT_SIZE_1); // points.mat().alpha(1); NUM_HISTORY * SPEC_RESOLUTION => int numPoints; float pointPos[numPoints * 3]; vec3 pointColor[numPoints]; fun void update( float dt ) { for( int i; i < SPEC_RESOLUTION; i++) { for( int j; j < NUM_HISTORY; j++ ) { // get index of this point i * NUM_HISTORY + j => int index; (loghead - j) => int logread; if( logread < 0 ) { loghead + NUM_HISTORY - j => logread; } colorsumlog[j] / 20 => vec3 tempColor; // <<<"logread: ", logread>>>; - (amplog[logread][i] * 2 + WIDTH / 2) => pointPos[3 * index + 0]; - HEIGHT / NUM_HISTORY * j => pointPos[3 * index + 1]; - (i$float) / SPEC_RESOLUTION * DEPTH => pointPos[3 * index + 2]; if ( j > FADE_THRESH ) { tempColor * ( (NUM_HISTORY - j)$float ) / (NUM_HISTORY - FADE_THRESH) => pointColor[index]; } else { tempColor => pointColor[index]; } } points.geo().positions(pointPos); // set vertex position data points.geo().colors(pointColor); // set vertex color data } } } 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]; (s$polar).phase => phas[i]; i++; // <<<"Value test: ", (s$polar).mag>>>; } // <<<"Mag: ", mags_prev[0]>>>; vec3 colorsum; for(int i; i < NUM_LINES; i++) { mag2color(mags_prev[idx[i]]) => colors[i]; mags_prev[idx[i]] => maglog[loghead][i]; // phas_prev[idx[i]] => maglog[loghead][i]; mag2color(mags_prev[idx[i]]) +=> colorsum; } colors @=> colorlog[loghead]; colorsum => colorsumlog[loghead]; // loghead advancement 1 +=> loghead; loghead % NUM_HISTORY => loghead; smoothing(mags_prev, mags, 0.9); smoothing(phas_prev, phas, 0.99); } (WINDOW_SIZE / SPEC_RESOLUTION)$int => int sampleInterp; fun void map2waveform( float in[] ) { for(int i; i < SPEC_RESOLUTION; i++){ Math.fabs(in[i + sampleInterp]) * AMP_BOOST => amps[i]; } smoothing(amps_prev, amps, 0.9); amps_prev @=> amplog[amploghead]; // loghead advancement 1 +=> amploghead; amploghead % NUM_HISTORY => amploghead; } // 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; spork ~ controlNarrative(); while( true ) { // map to spectrum display map2spectrum( response ); map2waveform( samples ); // next graphics frame GG.nextFrame() => now; if( KB.isKeyDown(KB.KEY_1) ) { // Close Up GG.camera().posX( 5 ); GG.camera().posY( 3 ); GG.camera().posZ( 2.5 ); GG.camera().lookAt( @(0, -2, -DEPTH/2) ); // GG.camera().fov( 70 ); } if( KB.isKeyDown(KB.KEY_2) ) { // Further Up GG.camera().posX( 10 ); GG.camera().posY( 6 ); GG.camera().posZ( 5 ); GG.camera().lookAt( @(0, -2, -DEPTH/2) ); // GG.camera().fov( 40 ); } if( KB.isKeyDown(KB.KEY_3) ) { // Further Up GG.camera().posX( -5 ); GG.camera().posY( 3 ); GG.camera().posZ( 2.5 ); GG.camera().lookAt( @(0, -2, -DEPTH/2) ); // GG.camera().fov( 40 ); } if( KB.isKeyDown(KB.KEY_4) ) { // Front GG.camera().posX( 0 ); GG.camera().posY( -3 ); GG.camera().posZ( 3 ); GG.camera().lookAt( @(0, 0, -3) ); // GG.camera().fov( 90 ); } if( KB.isKeyDown(KB.KEY_5) ) { // Front GG.camera().posX( 0 ); GG.camera().posY( 2 ); GG.camera().posZ( 2 ); GG.camera().lookAt( @(0, 0, -(DEPTH-2) ) ); // GG.camera().fov( 70 ); } if( KB.isKeyDown(KB.KEY_6) ) { // Front GG.camera().posX( 5 ); GG.camera().posY( 3.5 ); GG.camera().posZ( -DEPTH / 2 ); GG.camera().lookAt( @(0, -1, -DEPTH / 2) ); // GG.camera().fov( 70 ); } }