// ==== Directions ==== // run the shred, control parameters MPD26 // pad corresponds to selecting sound file // first two sliders (F1, F2) for gain // first 2 knobs for r and x adjustments // change the sound file "data/dustpond.wav" to what you wang // select keys // 36 -> whatever // data/dropitonme.wav:36:0.8 // data/pianonew.wav:37:0.999 // data/icingnicely.wav:38:0.999 // data/oceanscut.wav:39:0.1 // data/khoas.wav:40:0.000001 me.arg(0) => string thefile; Std.atoi(me.arg(1)) => int selectKey; Std.atof(me.arg(2)) => float thegain; 0 => int selected; // ===== PREPARE MIDI ===== // device to open 0 => int device; MidiIn min; MidiMsg msg; // try to open MIDI port, exit if we cannot if( !min.open( device ) ) me.exit( ); // print out the device that we opened <<< "MIDI device:", min.num( ), " -> ", min.name( ) >>>; // ==== SINE MAP THINGS ==== // usual values: 0.0 < r < 4.0 (noise) 1.0 => float r; // usual values: -pi/2 < x < pi/2 pi/4.0 => float x; // r parameters for interpolating float r_target, r_inc; // feedback fm and chaos // ==== SOUND MAKING THINGS ==== SndBuf buf; // set buffer parameters thefile => buf.read; buf.gain(thegain); Envelope e; buf => e => X.in; e.time(2); Gain g; // use three buffers (removes clipping sounds) LiSa l[3]; // buffer length in seconds length buffers buf.length( ) => dur bufferlen; // record from 0th buffer and play from 2nd buffer 0 => int recbuf; 2 => int playbuf; // LiSa parameters for(0 => int i; i < 3; i++) { l[i].duration(bufferlen); l[i].maxVoices(30); l[i].clear(); l[i].gain(0.99); l[i].feedback(0.9); // ramp recording so there's no clipping l[i].recRamp(20::ms); // turn recording off l[i].record(0); buf => l[i] => g => X.in; } // 0.999 => g.gain; // gain parameters float g_target, g_inc; 0.0 => g.gain; // write another LiSa buffer that holds what actually comes out // this may be like microphone output later on. // we'll use this as kind of a feedback mechanism LiSa output => blackhole; 5::second => output.duration; l[playbuf] => output; output.feedback(0.9); output.record(1); // start reading controls from the keyboard spork ~ getMIDI( ); spork ~ fade( ); spork ~ interp_r( ); spork ~ playBuf( ); // start recording from record buf l[recbuf].record(1); [-0.05, -0.1, -0.2, -0.4, 0.4, 0.2, 0.1, 0.05] @=> float rates[ ]; 0 => int counter; // create grains, rotate record and play bufs as needed // shouldn't click as long as the grainlen < bufferlen while( true ) { // calculate the time to keep creating grains for // this is the time when one buffer is getting recorded into now + bufferlen => time later; // toss some grains while ( now < later) { // get a new semi-random rate of playback // Std.rand2f(0.5, 2.5) => float newrate; x*rates[Std.rand2(0, 7)] => float newrate; // get a new semi-random duration // Std.rand2f(250, 600) * 1::ms => dur newdur; Std.rand2f(250, 350) * 1::ms => dur newdur; // get new position for buffer based on sin map Math.sin(r*x) => x; // set buffer sample position Math.fabs( x ) * bufferlen => dur pos; // get new position for buffer // Std.rand2f(0., 1.) * bufferlen => dur pos; // get a grain with the duration and rate we just got // put the grain in our play buffer spork ~ getgrain(playbuf, newdur, 20::ms, 20::ms, newrate, pos); // advance time 10::ms => now; // 500 Hz } // rotate the record and playbufs l[recbuf++].record(0); if(recbuf == 3) 0 => recbuf; l[recbuf].record(1); playbuf++; if(playbuf == 3) 0 => playbuf; // <<< "poop" >>>; // stop buffer recording after we've recorded into each of // the lisa buffers so that we can play with just feedback if( counter == 3 ) { buf => blackhole; buf =< l[0]; buf =< l[1]; buf =< l[2]; <<< "stopped buffer recording" >>>; } else if( counter < 3 ) { 0 => buf.pos; } // now record what's being played into the output buffer l[playbuf] => output; dac => output; // put this output into the recbuf output => Gain g2 => l[recbuf]; l[recbuf].record(1); 0.9 => g2.gain; counter++; } // function to get next grain with paramaters as arguments fun void getgrain(int which, dur grainlen, dur rampup, dur rampdown, float rate, dur pos) { // get the next available voice l[which].getVoice() => int newvoice; // <<>>; if(newvoice > -1) { l[which].rate(newvoice, rate); l[which].playPos(newvoice, pos); l[which].rampUp(newvoice, rampup); (grainlen - (rampup + rampdown)) => now; l[which].rampDown(newvoice, rampdown); rampdown => now; } } // respond to keyboard events fun void getMIDI( ) { while( true ) { // wait on event min => now; // get one or more messages while( min.recv( msg ) ) { // pad + midi note to choose file (maybe slider later) if( msg.data1 == 144 ) { if( msg.data2 == selectKey ) { if( selected == 0 ) { 1 => selected; <<< thefile, " is selected" >>>; } else { 0 => selected; <<< thefile, " is unselected" >>>; } } } // if it's selected if( selected == 1 ) { // use slider to control gain if( msg.data1 == 176 && msg.data2 == 1 ) { <<< "fade in" >>>; msg.data3 / 127.0 => g_target; } if( msg.data1 == 176 && msg.data2 == 2 ) { // uhh why do i even need this?? <<< "fade out">>>; msg.data3 / 127.0 => g_target; } // use knobs to control x and r if( msg.data1 == 176 && msg.data2 == 11 ) { // adjust x (pi/2 -(-pi/2))*( msg.data3 / 127.0 ) + -pi/2 => x; <<< "x: ", x >>>; } if( msg.data1 == 176 && msg.data2 == 13 ) { // adjust r // allow for r to go beyond stated boundaries (5.0 - (-1.0))*( msg.data3 / 127.0 ) + (-1.0) => r_target; <<< "r: ", r >>>; } } } } } // interpolate gain fun void fade( ) { .02 => float slew; while (1) { (g_target - g_inc) * slew + g_inc => g_inc => g.gain; 0.1 :: second => now; } } // interpolate r fun void interp_r( ) { 0.05 => float slew; while (1) { (r_target - r_inc) * slew + r_inc => r_inc => r; 10 :: ms => now; } } // play the buffer fun void playBuf( ) { e.keyOn( ); (buf.samples( )/2)::samp => now; e.keyOff( ); 2::second => now; 0 => buf.pos; }