I have made this Serial MIDI reader to catch song changes as MIDI ProgramChanges.
The problem is if I turn the song-knob fast on my external MIDI-device then it appears like it looses some of the ProgramChanges (192/C0h byte codes).
Any suggestions why?
I wanted to upload picture, but it seems like this froum doesn give that opportunity!?
But the trace output looks like this:
09:44:57.326 -> 192=PC:
09:44:57.372 -> 67
09:44:57.961 -> 192=PC:
09:44:58.148 -> 68
09:44:58.798 -> 192=PC:
09:44:58.920 -> 69
09:44:58.920 -> 75 ........here it didnt get the ProgramChange byte before the data byte
09:44:59.013 -> 192=PC:
09:44:59.169 -> 76
09:44:59.388 -> 192=PC:
09:44:59.435 -> 77
09:44:59.528 -> 192=PC:
09:44:59.668 -> 89
09:44:59.931 -> 192=PC:
09:44:59.931 -> 90
09:45:00.008 -> 192=PC:
09:45:00.134 -> 97
09:45:00.179 -> 98 ........here it didnt get the ProgramChange byte before the data byte
09:45:00.225 -> 192=PC:
09:45:00.309 -> 89
09:45:00.309 -> 240=SYSEX: -1
09:45:00.309 -> 240=SYSEX: -1 -1
09:45:00.309 -> 240=SYSEX: -1 -1 -1
09:45:00.309 -> 240=SYSEX: -1 -1 -1 -1
09:45:00.348 -> 240=SYSEX: -1 -1 -1 -1 -1
Also note that it appears to in the end that it goes into endless SysEx data if I turn the knob very fast. But doesnt catch the SystemExclusiveEnd.
CODE:
[code type=markup]
/////////////////////////////////////////////////////////////////////////////////////
// MIDI INPUT FOR PROGRAM CHANGE
/////////////////////////////////////////////////////////////////////////////////////
const byte SystemExclusiveStart = 0xF0; ///< System Exclusive
const byte SystemExclusiveEnd = 0xF7; ///< System Exclusive End
const int MIDI_BaudRate = 31250;
volatile int MIDI_Byte = 0;
const byte SERIAL_ProgramChange = 192; //~C0 i HEX
volatile bool lastByteWasProgramChange = false;
volatile byte lastSongNumber = 0;
String MIDI_data_str = "";
/////////////////////////////////////////////////////////////////////////////////////
// handleSerialMIDIinput() INTERRUPT ROUTINE
/////////////////////////////////////////////////////////////////////////////////////
void handleSerialMIDIinput() {
noInterrupts();
MIDI_data_str = "";
//WHile there is MIDI data available, stay in reading loop:
while (Serial1.available() > 0) {
MIDI_Byte = Serial1.read();
if (MIDI_Byte !=254) {
MIDI_data_str = String(MIDI_Byte);
}
switch (MIDI_Byte) {
case SystemExclusiveStart:
MIDI_data_str = MIDI_data_str + "=SYSEX: ";
while (MIDI_Byte != SystemExclusiveEnd) {
MIDI_Byte = Serial1.read();
MIDI_data_str = MIDI_data_str + String(MIDI_Byte) + " ";
Serial.println(MIDI_data_str);
}
MIDI_data_str = MIDI_data_str + "END.";
break;
case SERIAL_ProgramChange:
MIDI_data_str = MIDI_data_str + String("=PC: ");
break;
}
if (MIDI_data_str != "") {
Serial.println(MIDI_data_str);
}
// if (MIDI_Byte == SERIAL_ProgramChange) {
// MIDI_data_str = MIDI_data_str + " PC:";
// }
//MIDI_data_str = MIDI_data_str + String(MIDI_Byte) + " ";
//IF its a ProgramChange start the action by reading the next data byte
if (MIDI_Byte == SERIAL_ProgramChange) {
lastByteWasProgramChange = true;
} else { // if (MIDI_Byte = SERIAL_ProgramChange)
if (lastByteWasProgramChange) {
lastByteWasProgramChange = false;
}
}
} // while (Serial1.available() > 0)
interrupts();
}
/////////////////////////////////////////////////////////////////////////////////////
// SETUP()
/////////////////////////////////////////////////////////////////////////////////////
void setup(void) {
Serial1.begin(MIDI_BaudRate); //Start lytning på SERIEL-porten (som anvendes til MIDI in) via RX1=Seriel1.
Serial.begin(31250); // debug output to serial monitor. OBS: SKAL sættes til 9600 i baud-rate, ellers vises volapyk!
attachInterrupt(digitalPinToInterrupt(19), handleSerialMIDIinput, FALLING);
} //setup()
/////////////////////////////////////////////////////////////////////////////////////
// LOOP()
/////////////////////////////////////////////////////////////////////////////////////
void loop() {
//Serial.println(MIDI_data_str);
} //loop() end
[/code]
Hello,
What devices are you using?
More important, how are they connected?
Are you using a midi USB connector? There is one of these that seems to lose data as you describe. It's got a treble clef symbol on it. There's a pic of it linked on another recent post here.
Geoff
This post also appears on an Arduino forum: MIDI ProgramChange.
In that thread, user ardunew appears to be using a DIN MIDI cable to connect a Boss RC-505mkII Loop Station to a MIDI shield board on an Ardunio. Ardunew is programming the Arduino to do something with the incoming MIDI data.
Various replies there suggest trying to do everything in an interrupt handler could be causing problems.
It also appears user ardunew may be unaware of Running Status:
In the MIDI 1.0 specification, see the sections on Running Status on PDF pages 10, 64, and 65 (printed pages 5, A-1, and A-2).
[list]
I think Bavi_H nailed the reason!
It sounds very plausable that its because of the running status tactics! Thank you! I will investigate more on that! 🙂
This website forum doesnt work!
I have tried to post replyes several days now.. But it just keeps on turning around and the click on capcha keeps on turning red after some time, while the round symbol is still moving around for ever!
Aparently its if there is too much data, like 3 code blocks! It never saves and runs in endless loop!
LOL, its also not working with 1 codeblock!
So now I try post it without any block encodings!
Example 1:
Where it looses data:
248 1-1111000 COMMAND: Timing Clock
248 1-1111000 COMMAND: Timing Clock
248 1-1111000 COMMAND: Timing Clock
192 1-1000000 COMMAND: Program Change
76 0-1001100 DATA - SONG: 77 updateTFT()
tft.availableForWrite()==0
248 1-1111000 COMMAND: Timing Clock
240 1-1110000 COMMAND: SysEx begin
65 0-1000001 DATA
16 0-0010000 DATA
0 0-0000000 DATA
0 0-0000000 DATA
114 0-1110010 DATA
18 0-0010010 DATA
0 0-0000000 DATA
0 0-0000000 DATA
0 0-0000000 DATA
4 0-0000100 DATA
124 0-1111100 DATA
247 1-1110111 COMMAND: SysEx end
248 1-1111000 COMMAND: Timing Clock
248 1-1111000 COMMAND: Timing Clock
248 1-1111000 COMMAND: Timing Clock
248 1-1111000 COMMAND: Timing Clock
254 1-1111110 COMMAND: Active Sensing
248 1-1111000 COMMAND: Timing Clock
248 1-1111000 COMMAND: Timing Clock
248 1-1111000 COMMAND: Timing Clock
248 1-1111000 COMMAND: Timing Clock
248 1-1111000 COMMAND: Timing Clock
248 1-1111000 COMMAND: Timing Clock
248 1-1111000 COMMAND: Timing Clock
Problem: The last trace shown ProgramChange was 77, but on my source display the last was 85 (after the 77).
Here is example 2:
248 1-1111000 COMMAND: Timing Clock
248 1-1111000 COMMAND: Timing Clock
240 1-1110000 COMMAND: SysEx begin
65 0-1000001 DATA
16 0-0010000 DATA
0 0-0000000 DATA
0 0-0000000 DATA
114 0-1110010 DATA
248 1-1111000 COMMAND: Timing Clock
248 1-1111000 COMMAND: Timing Clock
248 1-1111000 COMMAND: Timing Clock
254 1-1111110 COMMAND: Active Sensing
248 1-1111000 COMMAND: Timing Clock
192 1-1000000 COMMAND: Program Change
17 0-0010001 DATA - SONG: 18 updateTFT()
tft.availableForWrite()==0
248 1-1111000 COMMAND: Timing Clock
248 1-1111000 COMMAND: Timing Clock
248 1-1111000 COMMAND: Timing Clock
248 1-1111000 COMMAND: Timing Clock
254 1-1111110 COMMAND: Active Sensing
192 1-1000000 COMMAND: Program Change
20 0-0010100 DATA - SONG: 21 updateTFT()
tft.availableForWrite()==0
248 1-1111000 COMMAND: Timing Clock
248 1-1111000 COMMAND: Timing Clock
248 1-1111000 COMMAND: Timing Clock
248 1-1111000 COMMAND: Timing Clock
248 1-1111000 COMMAND: Timing Clock
248 1-1111000 COMMAND: Timing Clock
Problems:
- It didnt get the last ProgramChange to 28, the last updated on TFT was 20 and
- It also lost a SysEx end code.
Any new suggestions?
How annoying it wont let me upload code at all.. not with or without code block tags.... So now I try as a zip file!
@Geoff, I answered you the other day, but apparently this forum is not working, because today my reply is gone and I have huge troubles of posting new replies at all. 🙁
Well.. My setup is as follows:
From my MIDI source device I send song changes via MIDI ProgramChange codes via 5pin MIDI cable to a MIDI shield boards MIDI IN and that board is connected to an Arduino Mega 2560 board via breadboard.
- I have tried to send MIDI data right trhu the MIDI THRU 5pin din on the MIDI shield board and further on to an identical MIDI-device b and it works fine without loosing the data.
- I have also tried the same setup with a simple Arduino code which only forwards the incoming MIDI data to the MIDI OUT on the MIDI shield board and further on to the identical MIDI device b and here it also works finde without loosing any data.
So my conclusion is that bothe my 2 midi devices work, and my midi cables work and the 3 5pin plugs on the MIDI shield also works and the MIDI shield board also works.
So that comes to the setup only looses data when the Arduino has several things to do and when it writes to the tft which slows the proces down. The writing to the serial trace output window doesnt seem to influence as it also looses data if I dont write to the trace window.
So
- Do I still interpret the MIDI data in a wrong way?
- Is the Arduino Mega 2560 not suitable for doing more than one litle test thing at a time? Like both midi AND tft writing?
- Do I have some bugs in my code?
- Is there something with the Arduino architecture I didnt catch abouty memories, flush, buffers, interrupts, timing or likewise?
Any suggestions would be appreciated.
Thanx.
Hello,
I'll try to look at your code, but a lot of what you're doing means nothing to me at all. Some of the devices you refer to also ???? I've heard of Arduino but I've never done anything with one.
Maybe you'll get a reply from someone here who does know something about these things.
As far as the forum is concerned, it works OK. We do get a lot of non-midi posts, often totally non-midi, and it's a constant battle to get rid of them. Your missing post might have been removed accidentally, or might have appeared to be non-midi. If it was in fact appropriate, but got deleted, we apologise. I note that your recent posts do appear. Regarding the code, I'm not sure about the rules, but I would always prefer that code be attached as a separate file so that it can be read/studies in comfort, and printed out, trying to make sense of large bodies of complex code on a small screen is not easy.
Given the seeming randomness of the lost code, the suggestions (from another, more specialised, forum) regarding interrupts sounds possible. I assume that you investigated this, and maybe tried to change your code to protect against this (more buffering?) What was the result of your checks regarding the earlier suggestions/
Geoff
Hello,
Just to confirm that I've got the code, which looks like C so I might be able to make sense of it.
I guess from the code that you are handling the data at a bit level - I assume that you are allowing for the fact that raw midi data contains extra bits, there is a 'stop' bit I think, and something else - this was discussed in a recent post regarding the quantity of midi data 'bits' that might be in a midi data stream, I had referred to there being 8 bits per byte, somone else corrected this as there was 10 bits in total for each data byte with the extra 'packing' bits. This could mean that there is more data than you expect, but the extra data would not be valid midi data and might generally confuse things so give the impression that data is 'lost'?
Just an initial thought.
Just found the prev thread. See
https://www.midi.org/forum/17325-midi-buffer-limits
reference to 10 bite, 1 start bit, 8 data bits, 1 stop bit
Geoff
I've never used Arduinos, but I've heard that software serial has problems like that, and you should use hardware serial.
Howver, the Mega 2560 appears to have four hardware ports, so I do not think that this is your problem.
Your parsing code still does not handle all MIDI correctly.
You should do it like this:
- Bytes 0xF8..0xFF are one-byte real-time messages that can be sent inside other commands. You can handle/print them, but otherwise ignore them; do not set MIDI_Command. (Your "Reset MIDI_Command back" code is wrong; the last command might have been anything else.)
- Bytes 0x80..0xF7 are normal command bytes. Set MIDI_Command only for those.
- Bytes 0x00..0x7F are data bytes. How to handle them depends on the last command byte. In you case, you want to do something only for program changes. Your existing code for MIDI_Data is correct.
(You do not need to read the uppermost bit; it would be easier to just compare the byte values.)
@Geoff,
Hmm Im not sure my posts got deleted, more that its impossible to post them!
I might have thought it got posted, but there are SO many obsrtugles to post, that it might didnt get posted at all.
E.g. Now its impossible to post my code, even I posted code at my first post!
It just goes into endless turn-around-looping the mouse icon. And after a while the Im not a robot-thing goes red again and needs to re-click, lol. And after have been doing that for 5 minutes I loose interest in continuing.
SO.. if u are the admin or know him, pls tell to:
1) Fix the bug that its not possible to add the code inside the zip-file.
2) Tell them to fix the bug that Im not a robot goes into re-asking for click, even it was clicked before clicking the post/Send form button.
But its probably because someone just used a free 3rdpart capcha instead of coding its own simple code.
Yes, I think I've had a problem with posting recently.
I suspect that this has happened: I've ticked the Captcha box, then gone to the 'Post' icon and clocked that. BUT, the Captcha arrow was still circling, I've not given it time to stop. Sometimes, it takes longer than other times, I'm not sure what it's doing. So, Captcha decides I'm some sort of robot, and no matter how careful I am with further tries I still have a problem.
If I try again later, log in again, allow the arrow to stop, it works fine.
I don't know why you couldn't put something into a zip file. This is nothing to do with this forum? But, this forum accepts certain file types ONLY. .zip is OK. .mid is NOT!! There may also be a limit on size. You said you tried to do a pic - I've seen pics posted here, but again, the specific file type might have been a problem, again, the file size?
Geoff
Martin, as you know there is an Arduino MIDI library, and as I understand it, you were having some problems with it originally and decided to write code to handle the incoming MIDI messages yourself. However, I suspect your original problems might have been due to other problems in your code. It looks like you might have learned some things and made some improvements to your code since then, so perhaps if you try using the Arduino MIDI library again it might work better for you this time? I think the Arduino MIDI library will probably buffer incoming MIDI bytes and handle Running Status, so you wouldn't have to worry about programming those things yourself.
[quotePost id=17732]Bytes 0x80..0xF7 are normal command bytes. Set MIDI_Command only for those.[/quotePost]
Clemens Ladisch: Remember that only Channel messages can use Running Status (see the references in my first post). As you said, System Realtime messages are always one-byte messages that can appear in-between the data of other messages and they don't change the remembered Running Status. But System Exclusive and System Common messages cause the Running Status to be forgotten. The next message after a System Exclusive or System Common message should always begin with a Status byte. (If it doesn't, then you just have to do nothing with the incoming bytes until you do see a Status byte.)
I'm finding it complicated to discuss without starting to write program snippets. When writing code to handle this, you might have to remember both what the current status byte is for the message you are currently building up, and what the "remembered Running Status byte" is for future data bytes that appear without an expected Status byte.
No, I didnt have problems posting a ZIP file. That was the ONLY solution way it succeeded me to upload the code file here.
Well enough about the forum here.. I will focus on solving my MIDI-problem now 🙂