TETHERPLEX.ck

From CSWiki
Jump to: navigation, search

/* Thomas Abend Plork cos 314 Final Project

Welcome to...THE TETHERPLEX!!! Behold its options: lisa recording and overdubbing, 3 fx pedals, spinning mode and... tether control!

Use the tethers to control the various parameters of the lisa loops you record using the bottom 2 tethers. Change which parameters those are using the top 3 footime buttons. Use the tether button to switch between spin and normal mode. enjoy the helpful MAUI interface that lets you know what is on.


Class Distortion is a distortion class taken from http://electro-music.com/forum/topic-19287-25.html credit due to kijjaz

Thanks to Dan for his lisa examples which are heavily borrowed from and Ceramics from which i took some of my tether code

Thanks to Michael and Rebecca for their help


Outline of the functions: munger and getgrain are functions that manage the munging portion of the instrument. They are sporked when the mungilator pedal is on

Empty essentially sets up shreds to be killed early on and replaced by spinner and player. this is because the machine.remove is called before the function is initally sporked at the programs beginning

tether gets Hid information from the tether system and sets them to global vars

FOOTIME gets Hid information from the footime pedal and sets them to global vars. it also sporks the connector and contains the recording portions of the record and overdub functions the connector sets things to and from the dac in order to

loopsetter sets the initial variables for the playing portions of the record and overdub functions

player is sporked by loopsetter and controls the loop using the global variables set by tether

realtimevars uses the tether variables to set certain loop parameters in "real time" (once every 10::ms) as opposed to once every loop revolution

spinner turns on and off spin mode



if you don't have a footime pedal try TETHERPLEX_KB.ck

Also note that Munge mode doesn't spin.

  • /


class Distortion {

   // this class calculate overdrive transfer function from this function:
   // f(x) = (ax + bx^2 + cx^3) / (1 + bx^2 + |ax + cx^3|)
   
   // to set a, b, c
   // use aInit.next, bInit.next, cInit.next
   
   // or if you want to adjust a, b, c by multiplying with other signal,
   // chuck the signals to ax, bxx, cxxx (their .op's are all MULTIPLY in here)
   
   Gain input, output; // chuck or unchuck this to connect

Step aInit; aInit.next(1); // initialize a Step bInit; bInit.next(1); // initialize b Step cInit; cInit.next(1); // initialize c

aInit => Gain ax; input => ax; ax.op(3); // calculate ax bInit => Gain bxx; input => bxx; input => Gain dummyB1 => bxx; bxx.op(3); // calculate bx^2 cInit => Gain cxxx; input => cxxx; input => Gain dummyC1 => cxxx; input => Gain dummyC2 => cxxx; cxxx.op(3); // calculate cx^3

Gain UPPER; // prepare the upper part of the division ax => UPPER; bxx => UPPER; cxxx => UPPER; // calculate ax + bx^2 + cx^3

Step one; // prepare 1 ax => Gain axANDcxxx; cxxx => axANDcxxx; // calculate ax + cx^3 axANDcxxx => FullRect ABSaxANDcxxx; // calculate |ax + cx^3|

Gain LOWER; // prepare the lower part of the division one => LOWER; bxx => LOWER; ABSaxANDcxxx => LOWER; // calculate 1 + bx^2 + |ax + cx^3|

UPPER => output; LOWER => output; output.op(4); // calculate f(x) by dividing UPPER with LOWER



}


//lisa setup adc => LiSa looper; //this baby will do a lot of work today 10::second => looper.duration; //allocate memory time startTime; //for remembering when recording started

looper => HPF filt => dac; //the HPF which is present on all play modes

2 => filt.Q; //set Q


dac.channels() => int voices;//for spinning later

0 => looper.loop; //the effect of looping in this program is done by while loops which allows for change


//This Section Initiates the 2 hid devices: Hid hi;HidMsg msg;0 => int deviceNum; HidIn hii; HidMsg msgi; if( !hii.openMouse( deviceNum ) ) me.exit(); <<< "mouse '", hii.name(), "' ready" >>>;

   0 => int device;   
   if( !hi.openJoystick( device ) ) me.exit();
   <<< "joystick '" + hi.name() + "' ready", "" >>>;

//This Section Contains the Global Vars we will need //these are for the fx buttons int Akey; int Bkey; int Ckey; 2 => int Dkey; 2 => int Ekey; //the tether vars float xl; float yl; float zl;float xr; float yr; float zr; //spinning info 0 => int UOD; int Spinner; //switches between play modes int Pselect; //helps with the length of each loop revolution. works like magic dur Magic;


//Echoator setups Gain g; Gain f; Gain feedback; DelayL delay; //Disorter setups Distortion OD; JCRev verb;

5::second => delay.max;

//helps with theconnector and spinner Gain daccer;

//munging variables //gain float MG; //length dur ML; //rate float MR;

//shred setups for spin and player fun void empty(){

   while(1)
   {1::hour => now;}

}

spork ~empty() @=> Shred s; spork ~empty() @=> Shred spin;


//THE BEAUTIFUL MAUI!

MAUI_LED a, b, c, d, e, sp; MAUI_Button Echoator, Distorticon, Mungilator, Record, Overdub, Spin; MAUI_View v;

v.size( 400, 200 );

a.color( a.blue ); a.size( 100, 100 ); a.position( 0, 0 ); a.unlight();

Echoator.size(100,50); Echoator.position(0, 0); Echoator.name("Echoator");

b.color( b.red ); b.size( 100, 100 ); b.position( 75, 0 ); b.unlight();

Distorticon.size(110, 50); Distorticon.position(68, 0); Distorticon.name("Distorticon");

c.color( c.green ); c.size( 100, 100 ); c.position( 150, 0 ); c.unlight();

Mungilator.size(120, 50); Mungilator.position(145, 0); Mungilator.name("Mungilator");

d.color( d.red ); d.size( 100, 100 ); d.position( 75, 100 ); d.unlight();

Record.size(100, 50); Record.position(75, 100); Record.name("Record");

e.color( e.green ); e.size( 100, 100 ); e.position( 150, 100 ); e.unlight();

Overdub.size(100 ,50); Overdub.position(150,100); Overdub.name("Overdub");

sp.color( sp.blue ); sp.size( 100, 100 ); sp.position( 300, 50 ); sp.unlight();

Spin.size(100, 50); Spin.position(300, 50); Spin.name("Spin");

v.addElement( a ); v.addElement( b ); v.addElement( c ); v.addElement( d ); v.addElement( e ); v.addElement( sp ); v.addElement(Echoator); v.addElement(Distorticon); v.addElement(Mungilator); v.addElement(Record); v.addElement(Overdub); v.addElement(Spin);

v.display();


//This section sets up many of the munging elements used later

//transposition table [0, 4, 7, -2, 12, 15] @=> int pitchtable[]; //use three buffers to avoid clicks LiSa l[3]; 1::second => dur bufferlen; //allocated buffer size -- remains static .5::second => dur reclooplen;//portion of the buffer size to use -- can vary 0 => int recbuf; 2 => int playbuf; int MUNGEIT; //LiSa params, set for(0=>int i; i<3; i++) {

   l[i].duration(bufferlen);
   l[i].loopEndRec(reclooplen);
   l[i].maxVoices(30);
   l[i].clear();
   l[i].gain(0.2);
   //if you want to retain earlier passes through the recording buff when loop recording:
   //l[i].feedback(0.5); 
   l[i].recRamp(20::ms); //ramp at extremes of record buffer while recording
   l[i].record(0);
  

} //start recording in buffer 0 l[recbuf].record(1);

//munging player function. interacts with the tethers through the player and realtimevars functions fun void munger(){

   1::second => now;
   while(MUNGEIT==1) {
     MR $ int => int newpitch; //choose a transposition from the table
     Std.mtof(pitchtable[newpitch] + 60)/Std.mtof(60) => float newrate;
       //spork off the grain!
       spork ~ getgrain(playbuf, ML, 20::ms, 20::ms, newrate, MG);
       //wait a bit.... then do it again, until we reach reclooplen
       5::ms => now;
   //rotate the record and playbufs
   l[recbuf++].record(0);
   if(recbuf == 3) 0 => recbuf;
   l[recbuf].record(1);
   playbuf++;
   if(playbuf == 3) 0 => playbuf;

} //to help kill this shred if(MUNGEIT==0){} }

//what the munger shred calls fun void getgrain(int which, dur grainlen, dur rampup, dur rampdown, float rate, float gain) {

   l[which].getVoice() => int newvoice;
   if(newvoice > -1) {
       l[which].gain(gain);
       l[which].rate(newvoice, rate);
       l[which].playPos(newvoice, Std.rand2f(0., 1.) * reclooplen);
       l[which].rampUp(newvoice, rampup);
       (grainlen - (rampup + rampdown)) => now;
       l[which].rampDown(newvoice, rampdown);
       rampdown => now;
   }
   

}


//tether listener. sets vars and triggers spin mode fun void tether(){ while( true ) {

   hi => now;
   
   while( hi.recv( msg ) )
   {
       if( msg.isButtonDown() )
       {
         //this code makes it a toggle button
         if(UOD==0){1 => UOD;}
         else if(UOD==1){0 => UOD;}
           
           //spork spin if pushed down
      Machine.remove(spin.id());

spork ~spinner(UOD) @=> spin;

           }
       
       if(msg.isButtonUp() )
       {
                  
       }
       if( msg.isAxisMotion() )
       {

//the variables

           if	   ( msg.which == 0 ) msg.axisPosition => xl;
           else if( msg.which == 1 ) msg.axisPosition => yl;
           else if( msg.which == 2 ) msg.axisPosition => zl;
           
           else if( msg.which == 3 ) msg.axisPosition => xr;
           else if( msg.which == 4 ) msg.axisPosition => yr;
           else if( msg.which == 5 ) msg.axisPosition => zr;
   }

} } }

//The Illustrius Spinner fun void spinner(int which){

   if(which==0){
       sp.light();
       //take everything off the dac and put it back on
       //this is normal
       looper =< dac;
       daccer =< dac;
       daccer => dac;      
       looper => dac;
       <<<"Normal Output">>>;        
   }
   if(which==1){
       //set envelope
  looper => Envelope blip;
  120::ms => blip.duration; 
     <<<"Spinnnnniiiiing">>>;
     looper =< dac;
     daccer =< dac;
     //launch the spin. keeps going till you kill it
     while(1){
         sp.unlight();
   for(0 => int i; i < voices; i++) {
       daccer => dac.chan(i);
       looper => dac.chan(i);                                               
       blip.keyOn();             
       .15::second => now;
       blip.keyOff();
       looper =< dac.chan(i);
       daccer =< dac.chan(i);
        sp.light();
   }

} } }

//This is the Pedal Board Listener. Includes FX and Recording buttons fun void FOOTIME() {

while( true )
   {
       hii => now;
       while( hii.recv( msgi ) )
       {
           //the FX are te first 3 buttons
           if( msgi.isButtonDown() )
           {
               
          if( msgi.which == 94 ){ 
                   //toggle style
                   if(Akey==0){ 1 => Akey;}
                   else if(Akey==1){0=> Akey;}

if( Akey ==1){

                       <<<"Echo Mode">>>;
                       a.light();
                       //plug in the settings with connector and Pselect

spork ~theconnector(3);

     3 => Pselect;
 }
 if( Akey ==0){
     //Back to empty with connector and pselect
     <<<"Exiting">>>;
     spork ~theconnector(4);
     0 => Pselect;
     a.unlight();
 }

} else if( msgi.which == 93 ){

   if(Bkey==0){ 1 => Bkey;}
   else if(Bkey==1){0=> Bkey;}
   if( Bkey ==1){
          <<<"Distortion Mode">>>;
               b.light();
               //same as before. plug em in
               spork ~theconnector(2);
               2 => Pselect;                
           }
           if( Bkey ==0){
               <<<"Exiting">>>;
               spork ~theconnector(4);
               0 => Pselect;
               b.unlight();
           }
       }      
               else if( msgi.which == 92 ){
                   if(Ckey==0){ 1 => Ckey;}
                   else if(Ckey==1){0=> Ckey;}
                   if( Ckey ==1){
                       <<<"Munge Mode">>>;
                       c.light();
                       //plug em in. the 1ms is just in case munging is weird
                       spork ~theconnector(0); 
                       1::ms => now;
                       //launch the munger with the on variable (1=mungeit)
                       spork ~munger();  
                      1 => MUNGEIT;                   
                      1 => Pselect;
                  }
                  if(Ckey ==0){
               <<<"Exiting">>>;
               //kill munger using mungeit=0
              0 => MUNGEIT;
              //get rid of the munging residue
               spork ~theconnector(1);
                spork ~theconnector(4);
               0 => Pselect;
               c.unlight();
           }
       }
   //RECORDING FUNCTIONS
               else if( msgi.which == 91 ){ 1 => Dkey; 
             d.light();                    
     <<< "Recording">>>;
     //basically from Dan's looper code
       looper.clear();
     0::ms => looper.recPos;
     1 => looper.record;
     now => startTime; //remember when recording started
 }
 else if( msgi.which == 77 ){ 1 => Ekey; 
           <<<"Overdubbing">>>;
           //same as before
           1. => looper.feedback;
           looper.playPos() - 1::samp => looper.recPos;
           1 => looper.record;
           e.light();

}

           }
           
           // mouse button up
           else if( msgi.isButtonUp() )
           {
      //D and E key tell loopsetter when to spork player
               if( msgi.which == 91 ){d.unlight(); 0 => Dkey;}
               if( msgi.which == 77 ){e.unlight(); 0 => Ekey;}
                 
           }
           
           
       }
   }
   

}

//plug in and out the pedals. also hook em all up to the spinner properly. fun void theconnector(int which){

   if(which==4){

looper =< g; f =< dac; looper =< verb; looper =< OD.input; OD.output =< dac; 1. => looper.rate; 0=> feedback.gain => delay.gain;

}

    if(which==0){
        for(0=>int i; i<3; i++){   looper => l[i] => dac;}
      Machine.remove(spin.id());

spork ~spinner(UOD);

   } 
    if(which==1){
  for(0=>int i; i<3; i++) {l[i] =< dac;}
        
      Machine.remove(spin.id());

spork ~spinner(UOD); }

   if(which==2){

looper => verb => OD.input; 0=>verb.mix; OD.output => daccer;

      Machine.remove(spin.id());

spork ~spinner(UOD); } if(which==3){

   // feedforward

looper => g => f => daccer; // feedback g => feedback => delay => g;

      Machine.remove(spin.id());

spork ~spinner(UOD); } }

//these are the vars set in "real time" every 10ms fun void realtimevars(){

   while(1){
       //10::ms is almost realtime
       10::ms => now;
       //volume

1. - ( (zl + 1.) / 2.) => float newgain;

     2 * newgain * newgain => looper.gain;

//HPF setting 1600 + (1595*(-1*yl)) => filt.freq;

if(Pselect==1){

   //gain
    6* (1 - ( (zr + 1.) / 2.)) -.2 => MG;
      //length
       (75 + 25 * xr)::ms  => ML;
       //rate
       2.9 + 2.9*yr => MR;
      
   }
   
   if(Pselect==2){
       //distortion 
            1. - ( (zr + 1.) / 2.) => float g2;
     700*g2*g2=> float dist;     
     OD.aInit.next(dist+1.);
     OD.bInit.next(dist+1.);
     OD.cInit.next(dist+1.);
     //reverb
       .6 +  xr/2 => verb.mix;
   }
   
   if(Pselect==3){
       

// set feedback

.75 + ( xr ) => feedback.gain;

// set effects mix

.875 + ( yr ) => delay.gain; 

}


   }

}


//these are the vars set in

   fun void player(){
       while(1){     
      if(Pselect==0){     

//Use Magic (the length of the recording) to help find playPositions within the sample

      (.45*Magic + ( xl * .45*Magic )) => looper.playPos;
    //play
      1 => looper.play; 
      //play the sample length - the time you skipped at the beginning
      (Magic - looper.playPos()) => now;                           
    }  
       if(Pselect==1){
      (.45*Magic + ( xl * .45*Magic )) => looper.playPos;
       1 => looper.play;
     (Magic - looper.playPos()) => now;
     //the munging variables are done in real time rather than per revolution
 }
 if(Pselect==2){  
     (.45*Magic + ( xl * .45*Magic )) => looper.playPos;
     
     //ensure that rates don't get too low
   //  if(yr< -.25  && yr > -.75) { -.25 => yr;}
//rate
     .7 + 2*yr => looper.rate;
     //not too slow now
     if(looper.rate()<.5  && looper.rate() > 0) { .5 => looper.rate;}
     if(looper.rate()<0  && looper.rate() > -.5) { -.5 => looper.rate;}        
   
     1 => looper.play;
     //rate changes length of sample and thus amount of time we want to wait
     (Std.fabs(looper.rate())) => float divider;
     (Magic - looper.playPos())/divider => now;
 }
 if(Pselect==3){          
      (.45*Magic + ( xl * .45*Magic )) => looper.playPos;
      //delay could be a realtime vars but i prefer the sound you get when you change every loop
         (100 + 1500*(1. - ( (zr + 1.) / 2.)))::ms=> delay.delay;
       1 => looper.play;
     (Magic - looper.playPos()) => now; 
        
    }   
    }  
   }    

//this sporks player and plays overdubs fun void loopsetter(){

   while(true){
       1::ms => now; 
       if (Dkey==0){
         Machine.remove(s.id());
         0 => looper.record;
         now - startTime => Magic => looper.loopEnd => looper.loopEndRec; 
         spork ~player() @=> s; 
         2 => Dkey; 
          }
       if (Ekey ==0) {
           <<<"stop overdub">>>;            
           0 => looper.record;
           0. => looper.feedback;
           2 => Ekey;
       }
   }

} //spork the shreds we need to start with spork ~spinner(UOD) @=> spin; spork ~FOOTIME(); spork ~loopsetter(); spork ~tether(); spork ~realtimevars();

//wait forever while(true) {

   1::hour => now;

}