/*
 NAME:                 Infra-Red sensors to Control MIDI Volume and Pitch Outputs 
 WRITTEN BY:           TOM SCARFF
 DATE:                 1/1/2009
 FILE SAVED AS:        TheraMidi.pde
 FOR:                  Miduino ATmega168
 CLOCK:                16.00 MHz CRYSTAL                                        
 PROGRAMME FUNCTION:   Infra-Red (IR) sensors  to MIDI Pitch Bend and Volume Control change.
 Uses Infrared Proximity Sensor - Sharp GP2Y0A21YK which has an analog output that varies from 3.1V at 10cm to 0.4V at 80cm.

 
 Realtime Parameter Number (RPN) Settings:
 These messages specify which parameter is to be set by the following controller 6 (MSB) and 38 (LSB) data messages.   
 For that reason, it is important to send a RPN reset message after use, so that any 'stray' data messages 
 later in the song do not have undesirable effects.
 Bn means a channel controller message on channel n, where n is 0 to F for channels 1 to 16. 
 
 Pitch Bend Range: 
 
 RPN LSB = 0: Bn 64 00 
 RPN MSB = 0: Bn 65 00 
 Data MSB: Bn 06 mm (mm sets bend range in semitones.   mm can be from 00 to 18 for 0 to 24  (+/- 12) semitones both up and down) 
 Data LSB=0 (usually not required): Bn 26 00 
 
 RPN & NRPN Reset:
 
 RPN LSB = 7F: Bn 64 7F 
 RPN MSB = 7F: Bn 65 7F 
 Send this alone (no data messages) immediately after all other RPN and NRPN messages have been sent. 
 
 
 IMPORTANT:
 The 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 IN cable 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___/
 *
 */




//variables setup
byte n,k;
byte midiByte;
byte MIDIchannel;
byte pitchBendMSB_now;
byte pitchBendMSB_past;
byte pitchBendLSB_now=0x7f;
byte pitchBendLSB_past;
byte note_now=0;
byte note_past=0;
byte noteStart=48;


int pitch;
int volume;

byte noteFlag=0;
byte noteOnFlag;
byte noteOffFlag;

byte channel;
byte status=0;
byte statusType;
byte statusTest;
byte volume_now=0;
byte volume_past=0;
byte x;
byte startTest=0;
byte runningStatus;
byte Flag;
byte LedPin = 13;   // select the pin for the LED
byte runStatusFlag=0;
byte realTimeTest;
byte count;



void setup() {
  pinMode(4, INPUT); // Set Inputs for 4 way DIP Switch
  pinMode(5, INPUT); 
  pinMode(6, INPUT); 
  pinMode(7, INPUT); 
  digitalWrite(4, HIGH); // Set inputs Pull-up resistors High
  digitalWrite(5, HIGH);
  digitalWrite(6, HIGH);
  digitalWrite(7, HIGH);

  pinMode(LedPin,OUTPUT);   // declare the LED's pin as output

  for (x=1; x<=4; x++){
    digitalWrite( LedPin, HIGH );
    delay(300);
    digitalWrite( LedPin, LOW );
    delay(300);
  }


  Serial.begin(31250);  //start serial with midi baudrate 31250
  Serial.flush();

  // Read 4-way DIP switch
  MIDIchannel=digitalRead(4) + (digitalRead(5)<<1) + (digitalRead(6)<<2) + (digitalRead(7)<<3);


  delay(20);
  midi_control(MIDIchannel, 0x65, 0x00);  // set pitch bend range to +/- 12 semitones
  midi_control(MIDIchannel, 0x64, 0x00);
  midi_control(MIDIchannel, 0x06, 0x18);
  midi_control(MIDIchannel, 0x26, 0x00);
  delay(2);
  midi_control(MIDIchannel, 0x64, 0x7F); // Reset RPN and NRPN
  midi_control(MIDIchannel, 0x65, 0x7F);

  midiSend(0xC0 | MIDIchannel, 91);
  noteOn(MIDIchannel, noteStart, 0x7F);



}


void loop() {


  midiThru();

  pitch = analogRead(0);    // read pitch
  pitch = pitch/6;     // gives range 6 to 106
  //Serial.println(pitch,DEC);

  if(pitch>127){
    pitch=127;
  }

  pitchBendMSB_now = pitch & 0x007F;

  if(pitchBendMSB_now <10){
    pitchBendMSB_now = 0;
  }

  if(pitchBendMSB_now != pitchBendMSB_past){
    midi_pitchBend(MIDIchannel, pitchBendLSB_now, pitchBendMSB_now );
  }
  pitchBendMSB_past=pitchBendMSB_now;


  volume = analogRead(1); // read Volume 
  volume = volume/6;

  if(volume>127){
    volume=127;
  }

  volume_now = volume & 0x007F;

  if(volume_now <10){
    volume_now = 0;
  }

  if(volume_now != volume_past){
    midi_control(MIDIchannel, 0x07, volume_now);
  }
  volume_past=volume_now;


  delay(10);


}
//_______________________________________________________________________________________________


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

// 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 midi_control(byte channel, byte control, byte data) {
  digitalWrite(LedPin, HIGH);   // sets the LED on
  Serial.print((0xB0 | channel ), BYTE);  //  control change command
  Serial.print(control & 0x7F, BYTE);  //  control value 20 to 25
  Serial.print(data & 0x7F, BYTE);  //  volume 0-127
  digitalWrite(LedPin, LOW);    // sets the LED off
}


void midi_pitchBend(byte channel, byte dataLSB, byte dataMSB) {
  digitalWrite(LedPin, HIGH);   // sets the LED on
  Serial.print((0xE0 | channel ), BYTE);  //  control change command
  Serial.print(dataLSB & 0x7F, BYTE);  //  control value 20 to 25
  Serial.print(dataMSB & 0x7F, BYTE);  //  volume 0-127
  digitalWrite(LedPin, LOW);    // sets the LED off
}

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


void midiThru(){

  if (Serial.available() > 0){      //If MIDI available read the incoming MIDI byte and transmit it thru
    midiByte = Serial.read();
    digitalWrite(LedPin, HIGH);   // sets the LED on

    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);    // sets the LED off//++++++++++++++++++++++++++++++++++++++++++++++++++++++++++


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 
  }

}