In order to finish a synth project that I'm working on I built a MIDI interface. To test it, I drew up some code which (is supposed to) light up an LED when any key is pressed. The light turns off again when the key is released. This will later be merged with another branch of code here to tell me which note is playing. This sounds simple enough but the devil's in the details. Due to my project's size constraints I am using ATMEL's ATtiny85 chip instead of my Arduino Uno. The ATtiny doesn't support the Arduino Serial library and by extension the standard MIDI library. As I can't make use of simple functions like MIDI.read() I've opted to work with an alternative Software Serial technique I adapted from an Instructable. Unfortunately, the results have been less than exemplary. Simply put, the LED almost never lights up. I have conducted two test only 1 of which somewhat succeeded:
1)
if(velocityByte > 0 ){digitalWrite(LED, HIGH);}
if(velocityByte == 0 ){digitalWrite(LED, LOW);}
Initially, I had figured that would be the most successful of my options. My keyboard* is binary about its ouput velocity. There are no pressure sensors so "on" is on and "off" is off. Unfortunately, however, the code seems to think otherwise.
2)
if(commandByte == noteOn ){digitalWrite(LED, HIGH);}
if(commandByte != noteOff ){digitalWrite(LED, LOW);}
Though originally less sure of this one, I've found it to be better than the other at catching notes. noteOn is the integer 144. While it does in fact trigger it does so only very rarely - once every 10 presses or so.
I believe I have however, whittled down the possible number of problems to 2:
1) Something is up with one or more of my calls to Serial. I've never really felt super confident with the author's method of obtaining data from Serial. For one, I have no confidence that I can insure I have collected the correct byte. If the code were to somehow "miss" counting a byte would it not assign, say, a commandByte to a noteByte? Is a better method available?
2) My keyboard's MIDI protocol is incompatible with my if-statement cases. At first I was under the assumption that my keyboard never sends a true "note off" message of any kind but instead a velocity = 0 message. But my test casts much doubt on this being the case and because of this, I don't really know how the keyboard handles note off messages. Does anyone else know?
*A Yamaha YPT-230
//MIDI INPUT LED TEST
//Adapted from code written
//by Instructables user Amanda Ghassaei
#include <SoftwareSerial.h>
SoftwareSerial mySerial(0, 1); //RX, TX
#define LED 2
byte commandByte;
byte noteByte;
byte velocityByte;
byte noteOn = 144;
byte noteOff = 128;
void setup(){
mySerial.begin(31250);
digitalWrite(LED, LOW);
delay(1000);
}
void checkMIDI(){
do{
commandByte = mySerial.read();//read first byte
noteByte = mySerial.read();//read next byte
velocityByte = mySerial.read();//read final byte
if (commandByte == noteOn){
digitalWrite(LED, HIGH);
}
if(commandByte != noteOn){
digitalWrite(LED, LOW);
}
}
while (mySerial.available() > 2);//when at least three bytes available
}
void loop(){
checkMIDI();
}
/*
void gotNoteOn(commandByte, noteByte, velocityByte){
digitalWrite(LED,HIGH);
}
void gotNoteOff(commandByte, noteByte, velocityByte){
digitalWrite(LED,LOW);
}
*/
UPDATE Following a suggesting, I have replaced the do .. while loop with an ordinary while loop. Relevant changes below:
void checkMIDI(){
while(mySerial.available() > 2){
commandByte = mySerial.read();
if (velocityByte > 0x00){//if note on message
digitalWrite(LED, HIGH);
}
if(velocityByte == 0x00){
digitalWrite(LED, LOW);
}
}
}
I'd like to think it's an improvement but if so only a slight one. Curiously, it almost works backwards - The default state of the LED is on while pressing a key produces no change pressing and holding causes the light to briefly shut off. But even this is sporadic. It may also be worth noting that I elected to use hex '0x00' as opposed to decimal '0'.
UPDATE II / NOTE Having slept on the problem I think I now understand why I am encountering this issue in the first place. The author of the Instructables code I adapted used an Arduino for both MIDI In as well as MIDI Out. Had they simply hooked one Arduino Uno to the other in an in/out configuration they could have perfectly ensured there were no running status issues by just never sending any running status messages. This would explain the lack of conformity to the actual realities of MIDI on the input side. If you can control exactly what data you are sending on one end (MIDI Out) it becomes fairly trivial to receive that data on the other end (MIDI In). Generalizing, the author may have assumed that a true keyboard behave the same thing and failed to actually verify this. Thoughts?
UPDATE III Following the lead of CL but deviating somewhat because I didn't fully understand what CL was doing, I have written up a highly derivative, new code. It is based around what I now know to be the highly fluid nature of MIDI messages and seeks to intelligently discriminate important messages from non-important messages based on content. It does not work (except for LED test at the beginning) but seems like a promising step nonetheless.
//MIDI Input LED Test
#include <SoftwareSerial.h>
SoftwareSerial mySerial(0, 1); //RX, TX
#define LED 2 //Test LED
void setup(){
mySerial.begin(31250);
digitalWrite(LED, HIGH); //Test
delay(1000);
digitalWrite(LED, LOW);
delay(1000);
}
void loop(){
checkMIDI();
}
void checkMIDI(){
/*Two of the three independent types of bytes are always
distinct and can be identified simply by their values.
A Status byte (on channel 1 at least) is always either
0x80 (off) or 0x90 (on). No other type of message
(Note, Velocity) can ever take this value and so if
this is the value of the byte received I can be
assured it is a Status byte.
The Note and Velocity bytes are a bit (haha) dicier.
They have overlapping ranges with both taking values of
anywhere between 0x00 to 0x7F. *Ordinarily*. A caveat
is that my Yamaha YPT-230 is binary as far as velocity
goes. A YPT-230 data is either 0x00 (full off) or 0x7F
(full on). As long as a Note byte is never 0 or 127 I can
use this to discriminate between these two types
in this manner.
*/
if(mySerial.available() > 0){
byte b = mySerial.read();
if(b == 0x80 || 0x90){
byte commandByte = b;
}else if(b == 0x00 || 0x7F){
if(b == 0x00){
digitalWrite(LED, LOW); //If vel = 0 turn LED off
}else if(b == 0x7F){
digitalWrite(LED, HIGH); //If vel = 127 turn LED on
}
//velocityByte = b;
}else{
byte noteByte = b; //Do nothing with this for right now.
}
}
}