//launchpad or lisa loop pedal //work on articulating what exactly it is that i want to do. what would be the most fun? sketch it out! //ideas: potentially press a button to record & repeat a LiSa sequence? or press buttons to save particular grain positions. //potentially: throw in buttons for multiple granulated voices? //-------------------------------------------------------------------- // based off of twilight-redux-kb.ck and ge's mouse input file // use UP/DOWN for coarse grain position control, use mouse X for fine grain position control // use mouse Y for control over grain size, and LPF filter frequency. // use 1 to launch the drums // use 2 to effectively turn on/off the LPF (opens sound up) // use 3 to effectively turn on/off reverb (makes sound quieter and less rich, more pure) // // // keyboard control (also see kb() below): // UP/DOWN -- grain position // LEFT/DOWN -- grain rate / tuning // ','/'.' -- grain size // '-'/'=' -- grain position random amount //-------------------------------------------------------------------- // default filename (can be overwritten via input argument) "6f7f_acapella.wav" => string FILENAME; "6f7f_instrumental.wav" => string DRUM_FILENAME; // get file name, if one specified as input x0argument //if( me.args() > 0 ) me.arg(0) => FILENAME; //TODO redefine these. wtf are these. 120 => float bpm; (60.0/bpm)::second => dur beat => dur eighth; 2*beat => dur quarter; 2*quarter => dur half; 2*half=> dur whole; beat/2 => dur sixteenth; // overall volume .5 => float MAIN_VOLUME; // grain duration base 50::ms => dur GRAIN_LENGTH; // factor relating grain duration to ramp up/down time .5 => float GRAIN_RAMP_FACTOR; // playback rate; have to double for some reason to get the right playback speed :/ 2 => float GRAIN_PLAY_RATE; // grain position (0 start; 1 end) 0 => float GRAIN_POSITION; // grain position randomization .001 => float GRAIN_POSITION_RANDOM; // grain jitter (0 == periodic fire rate) 1 => float GRAIN_FIRE_RANDOM; // max lisa voices 2 => int LISA_MAX_VOICES; // load file into a LiSa (use one LiSa per sound) load( me.dir() + FILENAME ) @=> LiSa @ lisa; // connect lisa.chan(0) => dac; // HID objects Hid hi; HidMsg msg; Hid hi2; HidMsg msg2; // which joystick 0 => int device; // get from command line if( me.args() ) me.arg(0) => Std.atoi => device; // open joystick 0, exit on fail if( !hi2.openKeyboard( device ) ) me.exit(); // log <<< "keyboard '" + hi2.name() + "' ready", "" >>>; // open mouse 2, exit on fail if( !hi.openMouse( device ) ) me.exit(); <<< "mouse '" + hi.name() + "' ready", "" >>>; // keys 12 => int KEY_DASH; 13 => int KEY_EQUAL; 51 => int KEY_COMMA; 52 => int KEY_PERIOD; 205 => int KEY_RIGHT; 203 => int KEY_LEFT; 208 => int KEY_DOWN; 200 => int KEY_UP; 0.1 => float SAVE_1; 0.2 => float SAVE_2; 0.3 => float SAVE_3; 0.4 => float SAVE_4; 0.5 => float SAVE_5; 0.6 => float SAVE_6; 0.7 => float SAVE_7; 0.8 => float SAVE_8; 0.9 => float SAVE_9; //press 2 to turn off/on the LPF 1 => int FREQMAX; //press 3 to turn off/on the reverb 1 => int REVOFF; // spork it spork ~ print(); spork ~ kb(); spork ~ mouse(); // main loop while( true ) { // fire a grain fireGrain(); // amount here naturally controls amount of overlap between grains GRAIN_LENGTH / 8 + Math.random2f(0,GRAIN_FIRE_RANDOM)::ms => now; } fun void drums() { SndBuf drummerboy => LPF filter => dac; //Delay delay => LPF lowpass => JCRev r => 3000 => filter.freq; me.dir() + DRUM_FILENAME => drummerboy.read; // <--- maybe take this out of the for-loop, could make a few global (set gain to 0 at start) 0=>drummerboy.pos; MAIN_VOLUME => drummerboy.gain; repeat(60) { //strange fix that I have to do because i have to double GRAIN_PLAY_RATE not sure why GRAIN_PLAY_RATE/2 => drummerboy.rate; 1::second=>now; } } // fire! fun void fireGrain() { // grain length GRAIN_LENGTH => dur grainLen; // ramp time GRAIN_LENGTH * GRAIN_RAMP_FACTOR => dur rampTime; // play pos GRAIN_POSITION + Math.random2f(0,GRAIN_POSITION_RANDOM) => float pos; // a grain if( lisa != null && pos >= 0 ) spork ~ grain( lisa, pos * lisa.duration(), grainLen, rampTime, rampTime, GRAIN_PLAY_RATE ); } // grain sporkee fun void grain( LiSa @ lisa, dur pos, dur grainLen, dur rampUp, dur rampDown, float rate ) { // get a voice to use lisa.getVoice() => int voice; // if available if( voice > -1 ) { //set volume lisa.voiceGain(voice, MAIN_VOLUME); // set rate lisa.rate( voice, rate ); // set playhead lisa.playPos( voice, pos ); // ramp up lisa.rampUp( voice, rampUp ); // wait (grainLen - rampUp) => now; // ramp down lisa.rampDown( voice, rampDown ); // wait rampDown => now; } } // print fun void print() { // time loop while( true ) { // values <<< "pos:", GRAIN_POSITION, "random:", GRAIN_POSITION_RANDOM, "rate:", GRAIN_PLAY_RATE, "size:", GRAIN_LENGTH/second >>>; // advance time 100::ms => now; } } // keyboard fun void mouse() { // infinite event loop while( true ) { hi => now; while( hi.recv( msg ) ) { if( msg.isMouseMotion() ) { //mouseX controls the grain position of the first track (this is the key element of the performance) if( msg.deltaX ) { //set to .00005 for full sound mobility msg.deltaX*.00005 +=> GRAIN_POSITION; if( GRAIN_POSITION < 0 ) 0 => GRAIN_POSITION; if( GRAIN_POSITION > 1 ) 1 => GRAIN_POSITION; } //mouseY controls the speed of grains firing (and the speed of the backing track as of 5/17) if( msg.deltaY ) { //<<< "mouse motion:", msg.deltaY, "on y-axis" >>>; msg.deltaY*.00025 +=> GRAIN_PLAY_RATE; if( GRAIN_PLAY_RATE < .01 ) .01 => GRAIN_PLAY_RATE; if( GRAIN_PLAY_RATE > 10 ) 10 => GRAIN_PLAY_RATE; } } } } } fun void kb() { // infinite event loop while( true ) { // wait on HidIn as event hi2 => now; // messages received while( hi2.recv( msg2 ) ) { // button donw if( msg2.isButtonDown() ) { <<<"Button: ",msg2.which>>>; if( msg2.which == KEY_LEFT ) { .005 -=> GRAIN_PLAY_RATE; if( GRAIN_PLAY_RATE < 0 ) 0.005 => GRAIN_PLAY_RATE; } else if( msg2.which == KEY_RIGHT ) { .005 +=> GRAIN_PLAY_RATE; if( GRAIN_PLAY_RATE > 2 ) 2 => GRAIN_PLAY_RATE; } else if( msg2.which == KEY_DOWN ) { .1 -=> GRAIN_POSITION; //was .005 if( GRAIN_POSITION < 0 ) 0 => GRAIN_POSITION; } else if( msg2.which == KEY_UP ) { .1 +=> GRAIN_POSITION; //was .005 if( GRAIN_POSITION > 1 ) 1 => GRAIN_POSITION; } else if( msg2.which == KEY_COMMA ) { .85 *=> GRAIN_LENGTH; if( GRAIN_LENGTH < 1::ms ) 1::ms => GRAIN_LENGTH; } else if( msg2.which == KEY_PERIOD ) { 1.15 *=> GRAIN_LENGTH; if( GRAIN_LENGTH > 1::second ) 1::second => GRAIN_LENGTH; } else if( msg2.which == KEY_DASH ) { .9 *=> GRAIN_POSITION_RANDOM; if( GRAIN_POSITION_RANDOM < .000001 ) .000001 => GRAIN_POSITION_RANDOM; } else if( msg2.which == KEY_EQUAL ) { 1.1 *=> GRAIN_POSITION_RANDOM; if( GRAIN_POSITION_RANDOM > 1 ) 1 => GRAIN_POSITION_RANDOM; } else if(msg2.which == 2) //press 1 for drums { spork ~ drums(); } else if(msg2.which == 3) //press 2; { SAVE_1 => GRAIN_POSITION; } else if(msg2.which == 4) //press 3; { SAVE_2 => GRAIN_POSITION; } else if(msg2.which == 5) //press 4; { SAVE_3 => GRAIN_POSITION; } else if(msg2.which == 6) //press 5; { SAVE_4 => GRAIN_POSITION; } else if(msg2.which == 7) //press 6; { SAVE_5 => GRAIN_POSITION; } else if(msg2.which == 8) //press 7; { SAVE_6 => GRAIN_POSITION; } else if(msg2.which == 9) //press 8; { SAVE_7 => GRAIN_POSITION; } else if(msg2.which == 10) //press 9; { SAVE_8 => GRAIN_POSITION; } else if(msg2.which == 11) //press 0; { SAVE_9 => GRAIN_POSITION; } } } } } // load file into a LiSa fun LiSa load( string filename ) { // sound buffer SndBuf buffy; // load it filename => buffy.read; // new LiSa LiSa lisa; // set duration buffy.samples()::samp => lisa.duration; // transfer values from SndBuf to LiSa for( 0 => int i; i < buffy.samples(); i++ ) { // args are sample value and sample index // (dur must be integral in samples) lisa.valueAt( buffy.valueAt(i), i::samp ); } // set LiSa parameters lisa.play( false ); lisa.loop( false ); lisa.maxVoices( LISA_MAX_VOICES ); return lisa; }