/**********************************************************************
 * WRITTEN BY:           TOM SCARFF
 * DATE:                 11/10/2008
 * FILE SAVED AS:        midi_16drums_2xprog.pde
 * FOR:                  Miduino Atmega168
 * CLOCK:                16.00 MHz CRYSTAL                                        
 * PROGRAMME FUNCTION:   To read 8  currentSwitch and 6 piezos to produce midi drum outputs
 * and 2 Program Changes +1 and -1.
 * 
 * 
 * IMPORTANT:
 * your Miduino might not start if it receives data directly after a reset,
 * because the bootloader thinks you want to upload a new progam.You might 
 * need to unplug the midi-hardware until the board is running your program. 
 * Also when programming the Miduino disconnect the MIDI IN cable.
 * 
 * HARDWARE NOTE:
 * The Midi IN Socket is connected to the Miduino RX through an 6N139 opto-isolator
 *
 * To send MIDI, attach a MIDI out Female 180 Degree 5-Pin DIN socket to Arduino.
 * Socket is seen from solder tags at rear.
 * DIN-5 pinout is:                                         _______ 
 *    pin 2 - Gnd                                          /       \
 *    pin 4 - 220 ohm resistor to +5V                     | 1     3 |  MIDI jack
 *    pin 5 - Arduino Pin 1 (TX) via a 220 ohm resistor   |  4   5  |
 *    all other pins - unconnected                         \___2___/
 *
 *************************************************************************
 * 
 * // General MIDI (GM) Drum Selections
 * 35 Bass Kick; 36 Rock Kick; 37 Side Kick; 38 Acoustic Snare;
 * 39 Handclap; 40 Electric Snare; 41 Low Floor Tom; 42 Closed Hi-Hat;
 * 43 High Floor Tom; 44 Pedal Hi-Hat; 45 Low Tom; 46 Open Hi-Hat;
 * 47 Low Mid-Tom; 48 High Mid-Tom; 49 Crash Cymbal 1; 50 High Tom
 * 51 Ride Cymbal 1; 52 Chinese Cymbal; 53 Ride Bell; 54 Tambourine
 * 55 Splash Cymbal; 56 Cowbell; 57 Crash Cymbal 2; 58 Vibraslap
 * 59 Ride Cymbal 2; 60 High Bongo; 61 Low Bongo; 62 Mute High Conga
 * 63 Open High Conga; 64 Low Conga; 65 High Timbale; 66 Low Timbale
 * 67 High Agogo; 68 Low Agogo; 69 Cabasa; 70 Maracas; 71 Short Whistle
 * 72 Long Whistle; 73 Short Guiro; 74 Long Guiro; 75 Claves; 76 High Woodblock
 * 77 Low Woodblock; 78 Mute Cuica; 79 Open Cuico
 * 80 Mute Triangle; 81 Open Triangle
 * 
 * 
 **************************************************************************/

// define the pins we use

#define LedPin 13  // for midi out status

#define drumchannel  9

// analog thresholds for piezo sensing
#define PIEZOTHRESHOLD_ON 10
#define PIEZOTHRESHOLD_OFF 5
#define BOUNCE_PERIOD 10

long last_read_time[6]={
  0,0,0,0,0,0};
;

byte switchState[10]={
  0,0,0,0,0,0,0,0,0,0};
byte piezoState[6]={
  0,0,0,0,0,0};
byte peakVal[6]={
  0,0,0,0,0,0};
byte currentSwitch = 0;
byte drum[10]={
  47,48,49,50,51,53,54,55,56,57};
byte piezoDrum[6]={
  35,38,41,42,43,45};

byte midiByte;
byte MIDIchannel;
byte channel;
byte status=0;
byte statusType;
byte statusTest;
byte x;
byte startTest=0;
byte runningStatus;
byte Flag;
byte runStatusFlag=0;
byte realTimeTest;
byte count;
byte test;

int Program=59;

byte i,n,val,t;
byte last_val=0;
byte flag_peak=1;

void setup() {

  for (i = 2; i <=12; i++){
    pinMode(i, INPUT);
  }
  for (i = 2; i <=12; i++){  // turn on internal pullups
    digitalWrite(i, HIGH);
  }


  pinMode(LedPin, OUTPUT);
   for (x=1; x<=4; x++){
    digitalWrite( LedPin, HIGH );
    delay(300);
    digitalWrite( LedPin, LOW );
    delay(300);
  }
  Serial.begin(31250);   // set MIDI baud rate
  Serial.flush();

  midiSend( 0xC0 | drumchannel, Program );
}

void loop() {

  midiThru(); 

  ProgramSwitch();



  // scan 8  currentSwitch on inputs 4 to 12
  for(n=2; n<10; n++){
    currentSwitch = digitalRead(n+2);
    if( currentSwitch == LOW && switchState[n] == HIGH ) // push
      noteOn(drumchannel,  drum[n], 96);    // 3/4 volume
    if( currentSwitch == HIGH && switchState[n] == LOW ) // release
      noteOff(drumchannel, drum[n], 0);
    switchState[n] = currentSwitch;

  }

  // scan 6 piezo sensors  NOTE: Connect any unused analog sensor inputs to ground.
  for(n=0; n<6; n++){
    val = analogRead(n)/8;
    if( val >= PIEZOTHRESHOLD_ON  && piezoState[n]==0) {


      while(flag_peak){
        val = analogRead(n)/8;
        if(last_val > val){
          flag_peak=0;
        }
        last_val=val;
      }

      last_val=0;
      flag_peak=1;

      noteOn(drumchannel,  piezoDrum[n], val);
      piezoState[n]=0xff;
      last_read_time[n] = millis();
    }
  }

  for(n=0; n<6; n++){
    val = analogRead(n)/8;
    if( val <= PIEZOTHRESHOLD_OFF  && piezoState[n]>0 && (millis() - last_read_time[n]) >BOUNCE_PERIOD) {
      noteOff(drumchannel,  piezoDrum[n], val);
      piezoState[n]=0;
    }
  }

}

//++++++++++++++++++++++Functions++++++++++++++++++++++++++++++++++++++++

void ProgramSwitch(){

  test=digitalRead(2);
  if((test==00)&&(switchState[0]==00)){  // program Switch on, flag off
    Program = Program--;
    if( Program <= 0 ){
      Program = 0; // Don't go lower than 0
    }
    midiSend( 0xC0 | drumchannel, Program );
    switchState[0]=0x01;
  }

  if((test!=00)&&(switchState[0]!=00)){  // program Switch off, flag on
    switchState[0]=0x00;
  }

  test=digitalRead(3);
  if((test==00)&&(switchState[1]==00)){  // program Switch on, flag off
    Program = Program++;
    if( Program >= 127 ){
      Program = 127; // Don't go higher than 127
    }
    midiSend( 0xC0 | drumchannel, Program );
    switchState[1]=0x01;
  }

  if((test!=00)&&(switchState[1]!=00)){  // program Switch off, flag on
    switchState[1]=0x00;
  }
}

//=====================================================================  

//  Send a two byte midi message  
void midiSend(byte status, byte data ) {
  Serial.print(status, BYTE);
  Serial.print(data, BYTE);
}

//---------------------------------------------------------------------

// Send a MIDI note-on message.  
void noteOn(byte channel, byte note, byte velocity) {
  midiMsg( (0x90 | channel), note, velocity);
}

// Send a MIDI note-off message. 
void noteOff(byte channel, byte note, byte velocity) {
  midiMsg( (0x80 | channel), note, velocity);
}

// Send a general MIDI message
void midiMsg(byte cmd, byte data1, byte data2) {
  digitalWrite(LedPin,HIGH);  // indicate we're sending MIDI data
  Serial.print(cmd, BYTE);
  Serial.print(data1, BYTE);
  Serial.print(data2, BYTE);
  digitalWrite(LedPin,LOW);
}


//++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 


void midiThru(){

  if (Serial.available() > 0){      //If MIDI available read the incoming MIDI byte and transmit it thru
    midiByte = Serial.read();


    statusType= midiByte&0xF0;
    statusTest=midiByte&0x80;

    if(statusTest==0x80){        // if midi status and NOT data

      if(midiByte>=0x80 && midiByte<=0xEF){ //if midi channel command
        runningStatus=midiByte;
      }
      status=midiByte;
      Serial.print(status,BYTE); // transmit midi status byte
      txMidi();                   // transmit following data bytes
    }


    if(statusTest!=0x80&&status!=0){      // if running status and NOT starting data byte

      status=runningStatus;    // insert status byte
      statusType= status&0xF0;
      Serial.print(status,BYTE); // transmit midi running status byte
      txMidi2(status);                   // transmit following data bytes
    }

  }
} 



//++++++++++++++++++++++++++++++++++++++++++++++++++++++++++


void readMidi(){

  while (Flag==0){
    if (Serial.available() > 0) {  
      midiByte = Serial.read();         
      Flag=1;
    }
  }
  Flag=0;

}

////++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

void txMidi(){



  if(statusType==0xF0){

    getStatus1(status);  // complete status byte

  }

  else if(statusType!=0xF0){

    getStatus2(status); // status with channel removed
  }

}
//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

void getStatus1(byte val){


  switch (val) {
  case 0xF0:                //Begin System Exclusive 11110000 (F0)
    while (midiByte!=0XF7){      // until End of Sysex
      if (Serial.available() > 0) {  
        midiByte = Serial.read(); 
        Serial.print(midiByte,BYTE);      
      }
    }
    break;

  case 0xF1:                //MIDI  Time Code 11110001 (F1)

    mididata1Tx();
    break;

  case 0xF2:             //Song Position Pointer  11110010 (F2) 

    mididata2Tx();
    break;

  case 0xF3:            //Song Select  11110011 (F3)

    mididata1Tx();
    break;


  case 0xF7:           //End System Exclusive 11110111 (F7)    
    break;                // single byte data

  case 0xF8:           //Timing Clock 11111000 (F8) 
    // single byte data
    break;

  case 0xFA:           //Start 11111010  (FA)
    // single byte data
    break;

  case 0xFB:          //Continue 11111011 (FB)
    // single byte data
    break;

  case 0xFC:          //Stop 11111100 (FC)
    // single byte data
    break;

  case 0xFE:          //Active Sensing  11111110 (FE)
    // single byte data
    break;

  case 0xFF:         //System Reset 11111111 (FF) 
    // single byte data  
    break;




  default:
    break;
  }
}

//___________________________________________________

void getStatus2(byte val){

  val = val & 0xF0;          //remove channel info

  switch (val) {
  case 0x90:                // Note-On Event            1001bbbb (9X) 

    mididata2Tx();
    break;

  case 0x80:                //Note-Off  Event          1000bbbb (8X)

    mididata2Tx();
    break;

  case 0xA0:                //Control Polyphonic Key Pressure  1010bbbb (AX)      0kkkkkkk  0fffffff

    mididata2Tx();  
    break;

  case 0xB0:                //Control Change           1011bbbb (BX)

    mididata2Tx();   
    break;

  case 0xC0:                //Program Change           1100bbbb (CX)

    mididata1Tx(); 
    break;

  case 0xD0:                //Channel Pressure         1101bbbb (DX)

    mididata1Tx();  
    break;

  case 0xE0:                //Pitch Bend               1110bbbb (EX)

    mididata2Tx();   
    break;


  default:
    break;
  }
}


////++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

void txMidi2(byte val){      // running status mode data 1 available already in midiByte


  val = val & 0xF0;          //remove channel info

  switch (val) {
  case 0x90:                // Note-On Event            1001bbbb (9X) 

    Serial.print(midiByte,BYTE); // transmit midi data
    mididata1Tx(); 
    break;

  case 0x80:                //Note-Off  Event          1000bbbb (8X)

    Serial.print(midiByte,BYTE); // transmit midi note data
    mididata1Tx();
    break;

  case 0xA0:                //Control Polyphonic Key Pressure  1010bbbb (AX)      0kkkkkkk  0fffffff

    Serial.print(midiByte,BYTE); // transmit midi Polyphonic Key Pressure  data 1
    mididata1Tx();   
    break;

  case 0xB0:                //Control Change           1011bbbb (BX)

    Serial.print(midiByte,BYTE); // transmit midi control data 1
    mididata1Tx();   
    break;

  case 0xC0:                //Program Change           1100bbbb (CX)

    Serial.print(midiByte,BYTE); // transmit midi program change data  
    break;

  case 0xD0:                //Channel Pressure         1101bbbb (DX)

    Serial.print(midiByte,BYTE); // transmit midi channel pressure data     
    break;

  case 0xE0:                //Pitch Bend               1110bbbb (EX)

    Serial.print(midiByte,BYTE); // transmit midi Pitch Bend  data 1
    mididata1Tx();    
    break;


  default:
    break;
  }
}

//---------------------------------------------------------------------------------

void mididata2Tx()

{
  count=2;
  while (count!=0){
    readMidi();             // get midi 
    realTimeTest=midiByte&0x80;   // if Real Time dont decrement count
    if (realTimeTest==0){
      count=count-1;
    }            // data byte
    Serial.print(midiByte,BYTE); // transmit midi 
  }

}    

//------------------------------------------------------------------------------

void mididata1Tx()

{
  count=1;
  while (count!=0){
    readMidi();             // get midi 
    realTimeTest=midiByte&0x80;   // if Real Time dont decrement count
    if (realTimeTest==0){
      count=count-1;
    }            // data byte
    Serial.print(midiByte,BYTE); // transmit midi 
  }

}