MIDI velocity, can it be optimized?

This will output 6 MIDI note on and off events depending on 6 switches.
the instrument i am building only needs 6 velocity switches which correspond to guitar strings (maybe more on that later, its basically a souped up sustainer guitar with hexaphonic control)

anyways i have breadboarded 2 switches just for testing, Ill add the rest of them tomorrow.
It outputs the number of milliseconds between break and make quite nicely, even if i press them both at once.

what i did notice though is that once i added the code for reading the additional pins, the measured time went up by a couple of milliseconds
(i could get 1 MS by "playing" really fast with only 1 switch but with code added to read all 12 pins it seems i can only get a minimum of about 3 milliseconds)

maybe it doesnt really matter, i will have to look up how fast real MIDI keyboards respond to velocity.

you must bear with me on this, It is my very first arduino project, normally i just code maxscript at work :slight_smile:

anyways here is what i have so far.

/*
 MIDI velocity switch detector
6 2 way switches are connected with the common pin to ground. the normally on position is the "a" button
and the normally open one is the "b"
after the switch is pressed the time is measured betwen the breaking of the a contact to the making of the b contact
the result will be output to a MIDI note on event.
when the b contact is broken a MIDI note off event will be sent.

Switches are Omron D3C-2220 with common grounded and arduinos internal pullup resistors used.

If it can be fast enough the analog inputs will be used to send MIDI CC changes.
 */

// this constant won't change:
const int buttonPin1b = 2;
const int  buttonPin1a = 3;    // the pin that the switches are attached 
const int buttonPin2b = 4;
const int  buttonPin2a = 5;    
const int buttonPin3b = 6;
const int  buttonPin3a = 7;    
const int buttonPin4b = 8;
const int  buttonPin4a = 9;    
const int buttonPin5b = 10;
const int  buttonPin5a = 11;    
const int buttonPin6b = 12;
const int  buttonPin6a = 13;    


// Variables will change:
//int buttonPushCounter1a = 0;   // counter for the number of button presses
//int buttonPushCounter1b = 0;   // counter for the number of button presses
int buttonState1a = 0, buttonState2a = 0,buttonState3a = 0,buttonState4a = 0,buttonState5a = 0,buttonState6a = 0;        // current state of the button
int lastButtonState1a = 0, lastButtonState2a = 0,lastButtonState3a = 0,lastButtonState4a = 0,lastButtonState5a = 0,lastButtonState6a = 0;    // previous state of the button
int buttonState1b = 0, buttonState2b = 0,buttonState3b = 0,buttonState4b = 0,buttonState5b = 0,buttonState6b = 0;        // current state of the button
int lastButtonState1b = 0, lastButtonState2b = 0, lastButtonState3b = 0, lastButtonState4b = 0, lastButtonState5b = 0, lastButtonState6b = 0;     // previous state of the button

int time1 = 0,time2 = 0,time3 = 0,time4 = 0,time5 = 0,time6 = 0;
int ready1 = 0, ready2 = 0, ready3 = 0, ready4 = 0, ready5 = 0, ready6 = 0;


void setup() {  
  for (int pin=2; pin <= 13; ++pin) {
          pinMode (pin, INPUT);
          digitalWrite(pin, HIGH);    // initialize switch pins and enable pull up resistor
      } 
       Serial.begin(9600);
}


void loop() {
  // read the switches input pins:
  buttonState1a = digitalRead(buttonPin1a);
  buttonState1b = digitalRead(buttonPin1b);
  buttonState2a = digitalRead(buttonPin2a);
  buttonState2b = digitalRead(buttonPin2b);
  buttonState3a = digitalRead(buttonPin3a);
  buttonState3b = digitalRead(buttonPin3b);
  buttonState4a = digitalRead(buttonPin4a);
  buttonState4b = digitalRead(buttonPin4b);
  buttonState5a = digitalRead(buttonPin5a);
  buttonState5b = digitalRead(buttonPin5b);
  buttonState6a = digitalRead(buttonPin6a);
  buttonState6b = digitalRead(buttonPin6b);

  //----------------------------------------SWITCH1------------------------------- 1 

  // compare the buttonState to its previous state
  if (buttonState1a != lastButtonState1a) {    
    if (buttonState1a == HIGH) {
      time1 = millis();
      ready1 = 1; //prevent double hits on second switch
      // if the current state is HIGH then the switch
      // went from on to off: (normally closed to ground, pullup resister)      
    }     

    // save the current state as the last state, 
    //for next time through the loop
    lastButtonState1a = buttonState1a;
  }
  
  if (buttonState1b != lastButtonState1b) {    
    if (buttonState1b == LOW) {
      if (ready1 == 1){
        time1 = (millis() - time1);
        Serial.println(time1); //this will be mapped to velocity 0-127 and send the MIDI output
        ready1 = 0;
      }     
   
    }  
   
   else {
     Serial.println("noteoff  SWITCH 1"); //MIDI NOTE OFF GOS HERE  
   }
      
    lastButtonState1b = buttonState1b;
  }
     
   //----------------------------------------SWITCH2------------------------------- 2  
   if (buttonState2a != lastButtonState2a) {    
    if (buttonState2a == HIGH) {
      time2 = millis();
      ready2 = 1;     
    }     
    lastButtonState2a = buttonState2a;
  }
  
  if (buttonState2b != lastButtonState2b) {
     if (buttonState2b == LOW) {
      if (ready2 == 1){
        time2 = (millis() - time2);
        Serial.println(time2); //this is to be mapped to velocity 0-127 and send the MIDI output
        ready2 = 0;
      }      
    } 
    
   else {
     Serial.println("noteoff  SWITCH 2"); //MIDI NOTE OFF GOS HERE  
   }
     
    lastButtonState2b = buttonState2b;
  }  
   //----------------------------------------SWITCH3------------------------------- 3  
   //----------------------------------------SWITCH4------------------------------- 4  
   //----------------------------------------SWITCH5------------------------------- 5  
   //----------------------------------------SWITCH6------------------------------- 6  
       
}

There are several things you can do:

call mills() only once per loop, just before reading the input ports. Then the time intervals, and therefore velocities are always calculated with respect to the same position in time relative to the code, and does not depend on the pin number. You also reduce the number of time consuming mills() calls.

void loop() {
  // read the switches input pins:
  [glow]int timenow = millis();[/glow]
  buttonState1a = digitalRead(buttonPin1a);
  buttonState1b = digitalRead(buttonPin1b);
  buttonState2a = digitalRead(buttonPin2a);
  buttonState2b = digitalRead(buttonPin2b);
  buttonState3a = digitalRead(buttonPin3a);
  buttonState3b = digitalRead(buttonPin3b);
  buttonState4a = digitalRead(buttonPin4a);
  buttonState4b = digitalRead(buttonPin4b);
  buttonState5a = digitalRead(buttonPin5a);
  buttonState5b = digitalRead(buttonPin5b);
  buttonState6a = digitalRead(buttonPin6a);
  buttonState6b = digitalRead(buttonPin6b);

  //----------------------------------------SWITCH1------------------------------- 1

  // compare the buttonState to its previous state
  if (buttonState1a != lastButtonState1a) {    
    if (buttonState1a == HIGH) {
      time1 = [glow]timenow[/glow];
      ready1 = 1; //prevent double hits on second switch
      // if the current state is HIGH then the switch
      // went from on to off: (normally closed to ground, pullup resister)      
    }    

    // save the current state as the last state,
    //for next time through the loop
    lastButtonState1a = buttonState1a;
  }
  
  if (buttonState1b != lastButtonState1b) {    
    if (buttonState1b == LOW) {
      if (ready1 == 1){
        time1 = ([glow]timenow[/glow] - time1);
        Serial.println(time1); //this will be mapped to velocity 0-127 and send the MIDI output
        ready1 = 0;
      }    
  
    }  
  
   else {
     Serial.println("noteoff  SWITCH 1"); //MIDI NOTE OFF GOS HERE  
   }
      
    lastButtonState1b = buttonState1b;
  }
    
   //----------------------------------------SWITCH2------------------------------- 2  
   if (buttonState2a != lastButtonState2a) {    
    if (buttonState2a == HIGH) {
      time2 = [glow]timenow[/glow];
      ready2 = 1;    
    }    
    lastButtonState2a = buttonState2a;
  }

You can also read the pin registers PINA, PINB and PINC directly so all the pins are sampled at almost the same time. But then you must handle the mapping from PINx register bits to pin numbers to buttonStatenn variables.

Thank you!
I will definitely use your first suggestion, it should also be more accurate because the time was taken close to the time of the pin polling rather than after some "if" tests.

I haven't quite found out how to convert port bits to variables yet, If the response is still a bit slow or inconsistent i will look in to it.

I am building a little circuit to fake switch flipping from an accurate timing source. so i can see how consistent i can get the output.

Thanks for your help!

edit: i have now connected all the switches and used the single read of millis per loop, it is working a lot better as far as i can tell, writing to the serial on each keyup is actually taking the most time, reducing the amount of text sent on keyoff makes quite a difference, and with MIDI it will be sending only 3 bytes and at a faster rate.

I have one small problem though, the switch connected to pin 13 does not register. whan i press it i can see the onboard pin13 LED light faintly, but the program does not see any keypress.

is there some limitation to using that pin as an input? (sorry getting out of programming area here, maybe i should go post in troubleshooting)

edit2:
yay, I figured out all by my self that ! cant use pin13 because the onboard LED overcomes the built in pull up resistor, so for now, i have just used analog in 0 as a digital in, (pin 14) and it works!

now i guess i should use arrays so i dont have to repeat the code 6 times!

/*
 MIDI velocity switch detector
6 2 way switches are connected with the common pin to ground. the normally on position is the "a" button
and the normally open one is the "b"
after the switch is pressed the time is measured betwen the breaking of the a contact to the making of the b contact
the result will be output to a MIDI note on event.
when the b contact is broken a MIDI note off event will be sent.

Switches are Omron D3C-2220 with common grounded and arduinos internal pullup resistors used.

If it can be fast enough the analog inputs will be used to send MIDI CC changes.

NOTE: ANALOG pin 0 is used as digital pin 14 because the built in arduino LED on pin 13 overcomes the arduino pull up resistor.
for final circuit, if using arduino with LED on pin 13, desolder it if you still need all the analog inputs open. and the marked line in the code to 13.
 */

// this constant won't change:
const int buttonPin1b = 2;
const int  buttonPin1a = 3;    // the pin that the switches are attached 
const int buttonPin2b = 4;
const int  buttonPin2a = 5;    
const int buttonPin3b = 6;
const int  buttonPin3a = 7;    
const int buttonPin4b = 8;
const int  buttonPin4a = 9;    
const int buttonPin5b = 10;
const int  buttonPin5a = 11;    
const int buttonPin6b = 12;
const int  buttonPin6a = 14;    //CHANGE TO 13 if no onboard LED on pin 13.


// Variables:
int buttonState1a = 0, buttonState2a = 0,buttonState3a = 0,buttonState4a = 0,buttonState5a = 0,buttonState6a = 0;        // current state of the button
int lastButtonState1a = 0, lastButtonState2a = 0,lastButtonState3a = 0,lastButtonState4a = 0,lastButtonState5a = 0,lastButtonState6a = 0;    // previous state of the button
int buttonState1b = 0, buttonState2b = 0,buttonState3b = 0,buttonState4b = 0,buttonState5b = 0,buttonState6b = 0;        // current state of the button
int lastButtonState1b = 0, lastButtonState2b = 0, lastButtonState3b = 0, lastButtonState4b = 0, lastButtonState5b = 0, lastButtonState6b = 0;     // previous state of the button

int time1 = 0,time2 = 0,time3 = 0,time4 = 0,time5 = 0,time6 = 0;
int ready1 = 0, ready2 = 0, ready3 = 0, ready4 = 0, ready5 = 0, ready6 = 0;
int timenow;


void setup() {  
  for (int pin=2; pin <= 14; ++pin) {
          pinMode (pin, INPUT);
          digitalWrite(pin, HIGH);    // initialize switch pins and enable pull up resistor
      } 
       Serial.begin(9600);    
}


void loop() {
  // read the switches input pins:
  timenow = millis();
  buttonState1a = digitalRead(buttonPin1a);
  buttonState1b = digitalRead(buttonPin1b);
  buttonState2a = digitalRead(buttonPin2a);
  buttonState2b = digitalRead(buttonPin2b);
  buttonState3a = digitalRead(buttonPin3a);
  buttonState3b = digitalRead(buttonPin3b);
  buttonState4a = digitalRead(buttonPin4a);
  buttonState4b = digitalRead(buttonPin4b);
  buttonState5a = digitalRead(buttonPin5a);
  buttonState5b = digitalRead(buttonPin5b);
  buttonState6a = digitalRead(buttonPin6a);
  buttonState6b = digitalRead(buttonPin6b);

  //----------------------------------------SWITCH1------------------------------- 1 

  // compare the buttonState to its previous state
  if (buttonState1a != lastButtonState1a) {    
    if (buttonState1a == HIGH) {
      time1 = timenow;
      ready1 = 1; //prevent double hits on second switch
      // if the current state is HIGH then the switch
      // went from on to off: (normally closed to ground, pullup resister)      
    }     

    // save the current state as the last state, 
    //for next time through the loop
    lastButtonState1a = buttonState1a;
  }
  
  if (buttonState1b != lastButtonState1b) {    
    if (buttonState1b == LOW) {
      if (ready1 == 1){
        time1 = (timenow - time1);
        Serial.println(time1); //this will be mapped to velocity 0-127 and send the MIDI output
        ready1 = 0;
      }     
   
    }  
   
   else {
     Serial.println("no1"); //MIDI NOTE OFF GOS HERE  
   }
      
    lastButtonState1b = buttonState1b;
  }
     
   //----------------------------------------SWITCH2------------------------------- 2  
   if (buttonState2a != lastButtonState2a) {    
    if (buttonState2a == HIGH) {
      time2 = timenow;
      ready2 = 1;     
    }     
    lastButtonState2a = buttonState2a;
  }
  
  if (buttonState2b != lastButtonState2b) {
     if (buttonState2b == LOW) {
      if (ready2 == 1){
        time2 = (timenow - time2);
        Serial.println(time2); //this is to be mapped to velocity 0-127 and send the MIDI output
        ready2 = 0;
      }      
    } 
    
   else {
     Serial.println("no2"); //MIDI NOTE OFF GOS HERE  
   }
     
    lastButtonState2b = buttonState2b;
  }  
   //----------------------------------------SWITCH3------------------------------- 3  
   if (buttonState3a != lastButtonState3a) {        
    if (buttonState3a == HIGH) {
      time3 = timenow;
      Serial.println(3);
      ready3 = 1;     
    }     
    lastButtonState3a = buttonState3a;
  }
  
  if (buttonState3b != lastButtonState3b) {
     if (buttonState3b == LOW) {
      if (ready3 == 1){
        time3 = (timenow - time3);
        Serial.println(time3); //this is to be mapped to velocity 0-127 and send the MIDI output
        ready3 = 0;
      }      
    } 
    
   else {
     Serial.println("no3"); //MIDI NOTE OFF GOS HERE  
   }
     
    lastButtonState3b = buttonState3b;
  }  
   //----------------------------------------SWITCH4------------------------------- 4  
   if (buttonState4a != lastButtonState4a) {    
    if (buttonState4a == HIGH) {
      time4 = timenow;
      ready4 = 1;     
    }     
    lastButtonState4a = buttonState4a;
  }
  
  if (buttonState4b != lastButtonState4b) {
     if (buttonState4b == LOW) {
      if (ready4 == 1){
        time4 = (timenow - time4);
        Serial.println(time4); //this is to be mapped to velocity 0-127 and send the MIDI output
        ready4 = 0;
      }      
    } 
    
   else {
     Serial.println("no4"); //MIDI NOTE OFF GOS HERE  
   }
     
    lastButtonState4b = buttonState4b;
  }  
  
   //----------------------------------------SWITCH5------------------------------- 5  
    if (buttonState5a != lastButtonState5a) {    
    if (buttonState5a == HIGH) {
      time5 = timenow;
      ready5 = 1;     
    }     
    lastButtonState5a = buttonState5a;
  }
  
  if (buttonState5b != lastButtonState5b) {
     if (buttonState5b == LOW) {
      if (ready5 == 1){
        time5 = (timenow - time5);
        Serial.println(time5); //this is to be mapped to velocity 0-127 and send the MIDI output
        ready5 = 0;
      }      
    } 
    
   else {
     Serial.println("no5"); //MIDI NOTE OFF GOS HERE  
   }
     
    lastButtonState5b = buttonState5b;
  }       
   
  
   //----------------------------------------SWITCH6------------------------------- 6  
    if (buttonState6a != lastButtonState6a) {  
    if (buttonState6a == HIGH) {
      time6 = timenow;
      ready6 = 1;     
    }     
    lastButtonState6a = buttonState6a;
  }
  
  if (buttonState6b != lastButtonState6b) {
     if (buttonState6b == LOW) {
      if (ready6 == 1){
        time6 = (timenow - time6);
        Serial.println(time6); //this is to be mapped to velocity 0-127 and send the MIDI output
        ready6 = 0;
      }      
    } 
    
   else {
     Serial.println("no6"); //MIDI NOTE OFF GOS HERE  
   }
     
    lastButtonState6b = buttonState6b;
  }  
  
}

changed to arrays, uses about 200 bytes less in arduino.
and it seems to work well. although the switches are much to crappy to use in a real instrument, i think i need to find something beefier. maybe even optical.

It responds very quickly I was actually playing a synth with it and could not notice the slightest delay. so i do not think i have to worry about the dreaded port reading!

/*
 MIDI velocity switch detector
 6 2 way switches are connected with the common pin to ground. the normally on position is the "a" button
 and the normally open one is the "b"
 after the switch is pressed the time is measured betwen the breaking of the a contact to the making of the b contact
 the result will be output to a MIDI note on event.
 when the b contact is broken a MIDI note off event will be sent.
 
 Switches are Omron D3C-2220 with common grounded and arduinos internal pullup resistors used.
 
 If it can be fast enough the analog inputs will be used to send MIDI CC changes.
 
 NOTE: ANALOG pin 0 is used as digital pin 14 because the built in arduino LED on pin 13 overcomes the arduino pull up resistor.
 for final circuit, if using arduino with LED on pin 13, desolder it if you still need all the analog inputs open. and the marked line in the code to 13.
 */

// this constant won't change:

const int  Pina[] = {
  3,5,7,9,11,14};  //CHANGE last value TO 13 if no onboard LED on pin 13.
const int Pinb[] = {
  2,4,6,8,10,12};
const char note[] = {
  60,62,64,65,67,69};  //white notes from middle C up.


// Variables:
int buttonStatea[] = {
  0,0,0,0,0,0};
int lastButtonStatea[] = {
  0,0,0,0,0,0};
int buttonStateb[] = {
  0,0,0,0,0,0};
int lastButtonStateb[] = {
  0,0,0,0,0,0};
int time[] = {
  0,0,0,0,0,0};
int ready[] = {
  0,0,0,0,0,0};
int timenow;
int i = 0;
int velocity;



void setup() {  
  for (int pin=2; pin <= 14; ++pin) {
    pinMode (pin, INPUT);
    digitalWrite(pin, HIGH);    // initialize switch pins and enable pull up resistor
  } 
  Serial.begin(31250);    
}


void loop() {
  // read the switches input pins:
  timenow = millis();
  for (i = 0; i < 6; i = i + 1) {
    buttonStatea[i] = digitalRead(Pina[i]);
    buttonStateb[i] = digitalRead(Pinb[i]);
  }


  //----------------------------------------SWITCH------------------------------- 1 

  for (i = 0; i < 6; i = i + 1) {  

    // compare the buttonState to its previous state
    if (buttonStatea[i] != lastButtonStatea[i]) {    
      if (buttonStatea[i] == HIGH) { // if the current state is HIGH then the switch went from on to off: (normally closed to ground, pullup resister)      
        time[i] = timenow;
        ready[i] = 1; //prevent double hits on second switch       
      }       
      lastButtonStatea[i] = buttonStatea[i];
    }

    if (buttonStateb[i] != lastButtonStateb[i]) {    
      if (buttonStateb[i] == LOW) {
        if (ready[i] == 1){
          time[i] = (timenow - time[i]);
          time[i] = constrain(time[i],1,200); //prevent sending nothing as velocity.
          velocity = map(time[i],1,200,127,1); //needs to be twaked with the final switches. velocity time could be controlled by a potentiometer.
          midisend (0x90, note[i], velocity);        
          ready[i] = 0;
        }     

      }  

      else {
        midisend (0x90, note[i], 0); //note velocity 0 = note off.
      }

      lastButtonStateb[i] = buttonStateb[i];
    }  
  }
}

void midisend(char cmd, char data1, char data2) {
  Serial.print(cmd, BYTE);
  Serial.print(data1, BYTE);
  Serial.print(data2, BYTE);

}