/*

 Nebulophone v01 by Dr. Bleep for Handmade Music Austin #4
 bleeplabs.com for schematic and more info
 handmademusic.noisepages.com for handmae music events
 
 Sources for setting up timers.
 http://www.cs.mun.ca/~rod/Winter2007/4723/notes/timer0/timer0.html
 http://arcfn.com/2009/07/secrets-of-arduino-pwm.html
 http://blog.wingedvictorydesign.com/2009/05/29/generate-real-time-audio-on-the-arduino-using-pulse-code-modulation/2/
 

The Nebulophone is a simple synthesizer designed for the Andromeda Space Rockers series.

Nebulophone01 :
- Five waveforms with adustable decay
- Analog Lowpass filter contolled by photocell
- LFO modulation of filter through LED
- Arpeggiator that can be linked to IR clock rate
- 10 keys played by alligator clip stylus. Can be set to 3 ranges in major or chromatic temperment.

Future additions? :
- Multiplexer chip to add more contols and inputs.
- Isolate LEDs to reduce output noise.
- Fix IR clocking bugs
- Pitch modulation
- Attack controls
- Audio input for modulation

 */

// Here's the wavetable. It contains four differnt waveforms(ramp, tri, squ, and pluse) that are selected by adding
// an offest (waveselect). You could have differnt tables but this allows you to move around between different
// waveforms with the "waveselect" variable, though that is not implemented in this code. More waveforms could be added but
// the pot used to select them is already divided into some tiny slices.

int wavetable[]= {
  0,  8, 16, 24, 32, 40, 48, 56, 64, 72, 80, 88, 96,104,112,120
    ,128,136,144,152,160,168,176,184,192,200,208,216,224,232,240,248
    ,  0, 16, 32, 48, 64, 80, 96,112,128,144,160,176,192,218,234,250
    ,255,250,234,218,192,176,160,144,128,112, 96, 80, 64, 48, 32, 16
    ,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0
    ,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,  0
    ,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0
    ,  0,  0,  0,  0,  0,  0,  0,  0,  0,255,255,255,255,255,255,  0
};
//Values for a chromatic scale. These we determined by hooking the output to a tuner
int chromatic[] = {
  1012, 954, 899, 850, 802, 759, 716, 674, 636, 600, 566, 536, 506
    , 478, 450, 426, 399, 377, 356, 336, 316, 300, 283, 268, 253, 239
    , 225, 213, 199, 188, 178, 168, 158, 150, 141, 134, 126, 120, 113
};
//Values for C major
int major[] = {
  1012, 954, 850, 759, 716, 636, 566, 506, 478, 426, 377, 356, 316
    , 283, 253, 239, 213, 188, 178, 158, 141, 126, 119, 106,  94,  89
};

//The keyboard pads. Since they wen't all in order or on just one port, this allows you to scan them in order easily. 
int pin[]={
  9,10,12,0,2,4,5,6,7,8
};

// And the rest. More on individual variables as they come up.
int irc=0;
int osc=1;
int wave;
int wavec=255;
int waveindex = 2;
int n =1;
int frequency = 850;
int keypress;
int prevn;
int prevkeypress;
int attack;
int waveselect;
int r;
int i=1;
long prev;
long prev2;
long prev3;
long prev4;
long prev5;
long prev6;
long prev7;
long prev8;
int release=256;
int releaselength =20;
int wavepot; 
int envpot;
int lfopot;
int irin;
int irout;
long irtempo;
int ir=0;
long previrtempo=1;
long previr=0;
int arppot;
int arpselect;
int count;
int a;
int key =1;
int d;
int s;
int lforate;
int lforateC;
int lfo =0;
int l=0;
int j=10;
int r2;
int r3;
int irtempomod;
int irswitch;
int arprate =10; 
int arprateF; 
int arprateL;
int keyoffset;
int attacklength = 30;
int scan=1;
int shiftbutton;
int prevshiftbutton;
int shift=0;
int octaveselect = 1;
int octaveselectpot = 1;
int scaleselectpot = 1;
int maxrelease =64;
int maxbrightness =200;
int beat;
int keym;
int prevarprateF;

void setup() {
  randomSeed(analogRead(2));
  // Leave serial off if you want to be in tune or get fast arpeggios and lfo 
  //Serial.begin(9600); 

  // The key pins
  pinMode(0, INPUT);  
  pinMode(2, INPUT);  
  pinMode(4, INPUT); 
  pinMode(5, INPUT);        
  pinMode(6, INPUT);        
  pinMode(7, INPUT); 
  pinMode(8, INPUT);
  pinMode(9, INPUT);
  pinMode(10, INPUT);        
  pinMode(12, INPUT);
  pinMode(14, INPUT); 
  pinMode(15, INPUT); 

  pinMode(3,OUTPUT); // PWM audio out
  pinMode(13, OUTPUT); // IR out
  pinMode(11, OUTPUT); // LFO LED out


  // Turn on pullups for all keys
  digitalWrite(0, HIGH);
  digitalWrite(2, HIGH);
  digitalWrite(4, HIGH);
  digitalWrite(5, HIGH);
  digitalWrite(6, HIGH);
  digitalWrite(7, HIGH);
  digitalWrite(8, HIGH);
  digitalWrite(9, HIGH);
  digitalWrite(10, HIGH);
  digitalWrite(12, HIGH);
  digitalWrite(14, HIGH);
  digitalWrite(15, HIGH);


  cli(); // Turn off interrupts. I did seem to have problems if this wasn't done.

  // Basically what is happening is that timer1 is being used to oscillate at the scale values while stepping through
  // the wavetable. The PWM wants to output at only 500Hz, which is pretty useless for audio. Here we can have it go
  // all across the audio range! More info at the site listed at the top

  TCCR2A = B00000011;
  TCCR2B = B00000001;
  TCCR1A = B00000000;
  TCCR1B = B00001010;
  TIMSK1 = B00000010;

  sei(); // Turn on interrupts. 
}

// This happens every time timer 1 overflows 8 times, as setup by the prescaler. 

ISR(TIMER1_COMPA_vect) {

  //Step through 32 places of the wavetable and go back to 1.
  if (waveindex > 32) {
    waveindex = 1;
  }
  waveindex ++;




  // For the first for waveforms. The wave is equal to the index that is counting up each cycle of this interupt plus the offset.
  // Release is handeled by simply subtracing an increading variable. This isn't a great way to decrease the volume of waveforms
  // but it does get the job doe without needing alot and does produce and interesting sound
  // Wave is then constrained to 8 bits so any variables outside the wavetable or release range dont crash the interupt.

  if (s==1){

    wave = ((wavetable[waveindex+waveselect])-release);
    wavec = constrain( wave, 0, 255);
    OCR1A = frequency;

  }

  // The noise waveform. You could call random at a differnt rate but this way makes it sound just like Atari tanks!
  // if you don't add to fequency, you can crash the interupt at lower frequencies. 224 is arbitrary. 

  else if (s==0){

    r = random(250);
    wave = ((r)-release);   
    wavec = constrain( wave, 0, 255);
    OCR1A = frequency+224;
  }

  analogWrite(3, wavec);


}

void loop(){





  ////////////////////////////////////////////////////////////////// read controls
  // To keep the processor free, controls are only read every 10 milliseconds.

  if (millis() - prev5 > 10 ) {       
    prev5 = millis();   

    shiftbutton = digitalRead(14);
    irswitch = digitalRead(15);
    r2 = random(220)*10;

    wavepot = (analogRead(3));
    arppot = (analogRead(5));
    lfopot= (analogRead(4));

  }

  if (scan ==1){
    n++;  
    if(n>9){
      n=0;
    }
  }
  else if (scan==0){

  }


  ////////////////////////////////////////////////////////////////// Keys
  // Keys are read by quickly stepping through the pins and seeing if any are low. If so,
  // that pin is active and the key is applied to one of the scales of notes which is in 
  // turn applied to the interupt oscillator.
  // If that some key the changes to high, the the release starts, decreasing the volume
  // of the output. Keep in mind that the ocsillator is always going, even when no key is
  // pressed, the output is simply reduced by 255, makking it silent. stoping the interupt
  // would crash it.
  keypress = digitalRead (pin[n]);  
  prevkeypress = digitalRead (pin[key]);  

  if (keypress == LOW){
    scan=0;
    key = n;
    release=1;
    osc=1;
  }
  if (prevkeypress == LOW){
    release=1;
    osc=1;
  }


  if (prevkeypress == HIGH){
    scan=1;

    if (millis() - prev > releaselength ) {       
      prev = millis();   
      if (release<=254){
        release+=10;

      }

      if (release>=255){
        release=255;
        osc=0;
      }
    }

  }
  ////////////////////////////////////////////////////////////////// shift button
  // Here the shift button is read and stps through the octave the keyboard will play and
  // the scale. "key" is the pin we jsut got srom scalling the keyboard, "a" is the
  // arpeggiator value, and "keyoffset" is the octave.

  if (shiftbutton != prevshiftbutton){
    if (shiftbutton == HIGH) {
      shift++;
    }     

    else{

    }
    prevshiftbutton = shiftbutton;
  }

  frequency = constrain(frequency,100,1024);



  keym = (key + a + keyoffset);



  if (shift ==0){
    keyoffset=1;
    frequency=major[keym];
  }
  else if (shift ==1){
    keyoffset=6;
    frequency=major[keym];
  }
  else if (shift ==2){
    keyoffset=11;
    frequency=major[keym];
  }
  else if( shift == 3)
  {
    keyoffset=1;
    frequency=chromatic[keym];
  }
  else if( shift == 4)
  {
    keyoffset = 10;
    frequency=chromatic[keym];
  }
  else if( shift == 5)
  {
    keyoffset = 20;
    frequency=chromatic[keym];
  }
  else if( shift >= 5){
    shift=0;
  }

  ////////////////////////////////////////////////////////////////// LFO
  // The LFO outputs analog levels through the PWM in a more traditional way but since it's using
  // pin 11 with is also on Timer 1, it will sound much better since the LED won't actually be 
  // blinking at 500 Hz.
  // Only one pot is used to control mode as well as rate. Inside each mode a differnt output
  // is performed based on lforateC.

  if (lfopot <256){
    lforate = map(lfopot, 0, 256, 255 , 1);
    lforateC = constrain(lforate, 0,maxbrightness);
    analogWrite(11, lforateC);
  }

  else if (lfopot >=256 && lfopot <512){

    lforate = map(lfopot, 256, 512, 96 , 1);
    lforateC = constrain(lforate, 1 , 96);

    if (millis() - prev4 > lforateC ) {
      prev4 = millis();    

      lfo+=8;
      analogWrite(11, lfo);
      if (lfo>=maxbrightness){
        lfo=0;  
      }
    }

  }

  else if (lfopot >=512 && lfopot <768){
    lforate = map(lfopot, 512, 768, 255 , 1);
    lforateC = constrain(lforate, 1 , 255);

    if (millis() - prev4 > lforateC ) {
      prev4 = millis();    

      if (lfo==0){
        analogWrite(11, 0);
        lfo=1;
      }

      else if (lfo>=1){
        analogWrite(11, maxbrightness);
        lfo=0;
      }
    }
  }

  else if (lfopot >=768&& lfopot<960){

    lforate = map(lfopot, 768, 960, 255 , 1);
    lforateC = constrain(lforate, 1 , 255);

    if (millis() - prev4 > lforateC ) {
      prev4 = millis();    

      analogWrite(11, r2);

    }

  }
  else if (lfopot>=960 && lfopot<992){

    if (millis() - prev4 > arprate/3 ) {
      prev4 = millis();    

      if (lfo==0){
        analogWrite(11, 0);
        lfo=1;
      }

      else if (lfo>=1){
        analogWrite(11, maxbrightness);
        lfo=0;
      }
    }

  }
  else if (lfopot>=992){

    if (millis() - prev4 > arprate/2 ) {
      prev4 = millis();    

      if (lfo==0){
        analogWrite(11, 0);
        lfo=1;
      }

      else if (lfo>=1){
        analogWrite(11, maxbrightness);
        lfo=0;
      }
    }

  }

  ////////////////////////////////////////////////////////////////// Wave select
//Just like the LFO pot except the variable inside each mode controls release.

  if (wavepot <204){
    releaselength = map(wavepot, 0,256,1,maxrelease);
    releaselength = constrain(releaselength, 1,maxrelease);
    waveselect=0;
    s=1;

  }

  else if (wavepot >=204 && wavepot<408){
    releaselength = map(wavepot, 204,408,1,maxrelease);
    releaselength = constrain(releaselength, 1,maxrelease);  
    waveselect=32;
    s=1;

  }

  else if (wavepot >=408 && wavepot<612){  
    releaselength = map(wavepot, 408,612,1,maxrelease);
    releaselength = constrain(releaselength, 1,maxrelease);  
    waveselect=64;
    s=1;

  }

  else if (wavepot >=612 && wavepot<816){  
    releaselength = map(wavepot, 612,816,1,maxrelease);
    releaselength = constrain(releaselength, 1,maxrelease);  
    waveselect=95;
    s=1;

  }
  else if (wavepot >=816){  
    releaselength = map(wavepot, 816,1024,1,maxrelease);
    releaselength = constrain(releaselength, 1,maxrelease);  
    waveselect=0;
    s=0;

  }


  ////////////////////////////////////////////////////////////////// ARP
  //Variable effects speed but is changed based on the IR switch
  
  if (arppot <5){
    count=0;  
    a=0;

  }
  if (arppot >=5 && arppot<256)  {

    arprateL = map(arppot, 5, 256, 319, 1);
    arprateL = constrain(arprateL, 1 , 319);  
    arprateF = map(arppot, 5, 256, 4, 1);
    arprateF = constrain(arprateL, 1 , 4);  

    if (count==0){
      a=0;
    }
    else if (count==1){
      a=2;
    }
    else if (count>=2){ 
      count=0;
    }

  }


  if (arppot >=256 && arppot<512)  {

    arprateL = map(arppot, 256, 512, 319 , 1);
    arprateL = constrain(arprateL, 1 , 319);  
    arprateF = map(arppot, 256, 512, 1, 4);
    arprateF = constrain(arprateF, 1 , 4);  


    if (count==0)      {
      a=0;
    }
    else if (count==1)      {
      a=1;
    }
    else if (count==2)      {        
      a=2;   
    }
    else if (count>=3)      {        
      count=0;
      a=1;   
    }
  }

  if (arppot >=512 && arppot<768)  {

    arprateL = map(arppot, 512, 768,  255 , 1);
    arprateL = constrain(arprateL, 1 , 255);  
    arprateF = map(arppot, 512, 768,  1, 4);
    arprateF = constrain(arprateF, 1 , 4);  


    if (count==0)
    { 

      a=r3;
    }
    else if (count==1)
    {
      a=0;
    }
    else if (count>=2)
    {
      count=0;
    }

  }

  if (arppot >=768)  {

    arprateL = map(arppot, 768, 1024, 319 , 1);
    arprateL = constrain(arprateL, 1 , 319);  
    arprateF = map(arppot, 768, 1024,  1, 4);
    arprateF = constrain(arprateF, 1 , 4);  

    if (count==0)
    {         
      a=0;
    }
    if (count >=1 && count<8)
    {
      a=count;
    }
    else if (count>=8)
    {
      a=0; 
      count=0;
    }
  }


  if (prevarprateF != arprateF){
    previrtempo=0;
  }

  prevarprateF = arprateF;
  ////////////////////////////////////////////////////////////////// IR
// All of the Andromeda Space Rocker Kits can communicate via IR. The Nebulophone can sync it's arpeggiator to a multiple
// of the IR clock rate. This is done but simply reading the analog input conected to the detector and measuring the time
// between peaks. There are some sync issuse to work, though. I believe the rate is read correctly but sometimes the arp
// speed can lag.

  irin = (analogRead(2));
  if (irin < 600 ){
    ir=1;
  }
  else if (irin >= 600 ){
    ir=0;   
  }


  if (irswitch == 0){
    arprate = irtempo / arprateF;

    if (ir != previr ){ 
      irsub();
    }
  }



  else if (irswitch == 1){
    arprate = arprateL;

  }



  if (millis() - prev8 >= arprate ) {
    r3 = random(-8,8);
    prev8 = millis();
    count++;   
    digitalWrite(13, HIGH);  
  }  

  else {
    digitalWrite(13, LOW);
  }

}

void irsub(){
  if (ir == 1){
    irtempo = millis() - previrtempo; 
    previrtempo = millis();
  }

  else{
  }   

  previr = ir;



}