/* 
NAME:                 MIDI Panic 
WRITTEN BY:           TOM SCARFF
DATE:                 9/9/2008
FILE SAVED AS:        midi_panic.pde
FOR:                  Miduino ATmega168
CLOCK:                16.00 MHz CRYSTAL                                        
PROGRAMME FUNCTION:   Switch OFF ALL MIDI notes.
 
 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___/
 *
 */


#define Panic 8

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

byte channel;
byte note;
byte velocity=0;
byte testPanic;
byte LedPin=13;




void setup() {

  pinMode(Panic,INPUT);
  digitalWrite(Panic,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();

}



void loop() {

  
    midiThru();
  
  testPanic=digitalRead(Panic);
  
  if (testPanic==0){
  
   for(channel=0;channel<16;channel++){    // switch off ALL notes on channel 1 to 16
       
        midiSend( (0xB0 | (channel)), 123, 0); // All Notes Off command
        
   }
   
   for(channel=0;channel<16;channel++){  
         
           for(note=0;note<=127;note++){
              midiSend( (0x80 | (channel)), note, velocity); // Note off each note
           }
      }
  }
      
  }
  
  //____________________________________________________________________________________
  
  
  //  Send a three byte midi message  
void midiSend(byte status, byte data1, byte data2) {
  digitalWrite(LedPin,HIGH);  // indicate we're sending MIDI data
  Serial.print(status, BYTE);
  Serial.print(data1, BYTE);
  Serial.print(data2, BYTE);
  digitalWrite(LedPin,LOW);  // indicate we're sending MIDI data
}

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


void midiThru(){
  
   if (Serial.available() > 0){      //If MIDI available read the incoming MIDI byte and transmit it thru
    midiByte = Serial.read();
     digitalWrite(LedPin,HIGH);  // indicate we're sending MIDI data
   
    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
    }
    
  }
  
   digitalWrite(LedPin,LOW);
} 



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


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 
    }
    
}