Read/Receive Midi - Midi NoteOff not returned on playing chords

Hi Arduino Audio developers.

I am developing a polyphonic synthesiser controller as part of a larger project. Whilst the code works there seem to be some serious performance (or data stream corruption) issues when receiving multiple notes or chords via Midi In.

Currently I have an Arduino Uno connected to a Midi IN circuit based on a 6n137 opto-isolator. I am using the standard Midi library (version 4.2). The Midi Rx from pin 6 of the 6n137 opto-isolator is connected to Digital Pin 2 of the Arduino, using a dedicated SoftwareSerial port for Midi. This is to allow use of the hardware Serial USB port for debugging using Serial.println as the SerialMonitor cannot be set at the correct speed for Midi (31250 baud). The Lcd display uses digital pins 4-7 and 8,9. The Midi IN socket is connected to a polyphonic keyboard.

The code is as follows:

#include <LiquidCrystal.h>
#include <MIDI.h>
#include <string.h>
#include <stdio.h>

#define DEBUG
#define NUM_VOICES 6

#if defined(ARDUINO_SAM_DUE) || defined(USBCON)
    // Print through USB and bench with Hardware serial
    MIDI_CREATE_INSTANCE(HardwareSerial, Serial1, MIDI);
#else
    #include <SoftwareSerial.h>
    SoftwareSerial midiSerial(2, 3); // midi Serial Port
    MIDI_CREATE_INSTANCE(SoftwareSerial, midiSerial, MIDI);
#endif

LiquidCrystal lcd(8, 9, 4, 5, 6, 7);

typedef struct Voice
{
  byte channel;
  byte note;
  byte velocity;
  boolean assigned;
};

static struct Voice v[NUM_VOICES];
static short voiceloop = 0;


void setup()
{
  MIDI.setHandleNoteOn(handleNoteOn);
  MIDI.setHandleNoteOff(handleNoteOff);
  MIDI.begin(MIDI_CHANNEL_OMNI); 
 
  lcd.begin(16,2);
  lcd.noCursor();
  lcd.clear();
  lcd.print("Synthtest");
  
  memset(v,  0, sizeof(struct Voice) * NUM_VOICES);
  for (int i = 0; i < NUM_VOICES; i++)
  {
    v[i].assigned = false;
  }
 
#ifdef DEBUG
    Serial.begin(115200); // This is the Debug Serial Port (using Serial.println to view data)
    Serial.println("Synthesiser Debug Test");
#endif
} /* function setup */

void loop()
{
  MIDI.read();
} /* function loop */

void handleNoteOn(byte inChannel, byte inNote, byte inVelocity)
{
  if (inVelocity > 0) 
  {
    assignNoteToVoice(inChannel, inNote, inVelocity);
#ifdef DEBUG  
    debugMidi(9, inChannel, inNote, inVelocity);
#endif
  } 
  else 
  {
    unassignNoteFromVoice(inChannel, inNote);
#ifdef DEBUG  
    debugMidi(8, inChannel, inNote, inVelocity);
#endif
  }

#ifdef DEBUG  
  debugVoice();
#endif
}

void handleNoteOff(byte inChannel, byte inNote, byte inVelocity)
{
  unassignNoteFromVoice(inChannel, inNote);
#ifdef DEBUG  
    debugMidi(8, inChannel, inNote, inVelocity);
#endif

#ifdef DEBUG  
  debugVoice();
#endif
}

#ifdef DEBUG
void debugMidi(byte inCmd, byte inChannel, byte inNote, byte inVelocity)
{
// dumps midi data to Serial.println
}
#endif

#ifdef DEBUG
void debugVoice()
{
 // dump voice data to Serial.println
}
#endif

void assignNoteToVoice(byte inChannel, byte inNote, byte inVelocity)
{
  int index;
  boolean steal = true;
  
  for (int i=0; i < NUM_VOICES; i++)
  {
    index = (voiceloop + i) % NUM_VOICES;  
    if (! v[index].assigned)
    {
      steal = false;
      v[index].assigned = true;
      v[index].channel = inChannel; 
      v[index].note = inNote;
      v[index].velocity = inVelocity;
      break;      
    }
  }

  if (steal)
  {
    index = (voiceloop) % NUM_VOICES;  
    v[index].assigned = true;
    v[index].channel = inChannel; 
    v[index].note = inNote;
    v[index].velocity = inVelocity;
  }
  voiceloop = (voiceloop + 1) % NUM_VOICES;  
}

void unassignNoteFromVoice(byte inChannel, byte inNote)
{
  for (int i=0; i < NUM_VOICES; i++)
  {
    if ((v[i].assigned) && (v[i].channel == inChannel) && (v[i].note == inNote))
    {
         v[i].assigned = false;
    }
  }
}

The code works correctly for single notes played slowly. For example, playing a single middle C, the NoteOn callback captures this note (ID 60, Midi C4) and this is placed in the first voice slot. On receiving the NoteOff message the voice is unassigned from the note. Subsequent notes are placed in sucessive voices.

Synthesiser Debug Test
note ON:  chn:01 nte:60 vel:84
 [60] [  ] [  ] [  ] [  ] [  ]
note OFF: chn:01 nte:60 vel:00
 [  ] [  ] [  ] [  ] [  ] [  ]

When playing chords the underlying logic works most of the time. For example, playing a C-major triad from middle C is shown. The debug dump below shows all 3 Midi NoteOn commands being assigned to a voice, and the corresponding NoteOff calls result in the voice being de-assigned. This is correct, and the Note off messages from the callback routine correspond to a note which has already had a prior NoteOn message.

note ON:  chn:01 nte:67 vel:69
 [  ] [67] [  ] [  ] [  ] [  ]
note ON:  chn:01 nte:64 vel:69
 [  ] [67] [64] [  ] [  ] [  ]
note ON:  chn:01 nte:60 vel:81
 [  ] [67] [64] [60] [  ] [  ]
note OFF: chn:01 nte:64 vel:00
 [  ] [67] [  ] [60] [  ] [  ]
note OFF: chn:01 nte:67 vel:00
 [  ] [  ] [  ] [60] [  ] [  ]
note OFF: chn:01 nte:60 vel:00
 [  ] [  ] [  ] [  ] [  ] [  ]

However, in many cases the NoteOff either fails to be recognised (i.e. data not sent to the callback routine) , or I receive a NoteOff command for a Note that has not been played. In both cases, this leaves a stuck note assigned to the voice. The below example is a 5-note E-minor chord, but the Midi callback has not received the NoteOff message for one of the notes (in this case the B of the E-minor chord):

note ON:  chn:01 nte:52 vel:28
 [52] [  ] [  ] [  ] [  ] [  ]
note ON:  chn:01 nte:55 vel:44
 [52] [55] [  ] [  ] [  ] [  ]
note ON:  chn:01 nte:59 vel:44
 [52] [55] [59] [  ] [  ] [  ]
note ON:  chn:01 nte:67 vel:84
 [52] [55] [59] [67] [  ] [  ]
note ON:  chn:01 nte:64 vel:55
 [52] [55] [59] [67] [64] [  ]
note OFF: chn:01 nte:55 vel:00
 [52] [  ] [59] [67] [64] [  ]
note OFF: chn:01 nte:67 vel:00
 [52] [  ] [59] [  ] [64] [  ]
note OFF: chn:01 nte:52 vel:00
 [  ] [  ] [59] [  ] [64] [  ]
note OFF: chn:01 nte:64 vel:00
 [  ] [  ] [59] [  ] [  ] [  ]

Here is a dump of a C-major triad - where one Note off is missing and a noteOn has been received for note 25 (which is outside the bounds of the keyboard being used to transmit the midi data.

note ON:  chn:01 nte:55 vel:67
 [  ] [55] [  ] [  ] [  ] [  ]
note ON:  chn:01 nte:52 vel:69
 [  ] [55] [52] [  ] [  ] [  ]
note ON:  chn:01 nte:48 vel:49
 [  ] [55] [52] [48] [  ] [  ]
note OFF: chn:01 nte:52 vel:00
 [  ] [55] [  ] [48] [  ] [  ]
note OFF: chn:01 nte:55 vel:00
 [  ] [  ] [  ] [48] [  ] [  ]
note ON:  chn:01 nte:25 vel:38
 [  ] [  ] [  ] [48] [25] [  ]

Another incorrect outcome is that a NoteOff is received for a note that has not been played.

Possibilities:

1 - Does Midi Receive work correctly on a SoftwareSerial port (in this case using pin 2 for Midi Rx)? Do you require any extra commands to flush the Serial port stream? As mentioned, receiving Midi via Pin 0 corrupts the data as the Serial Print also uses the same port, for example Pin 0 is specified when using the “default” Midi class initialisation macro: MIDI_CREATE_DEFAULT INSTANCE, so this cannot be used.

2 - could this be a problem with the opto-isolator? I believe that the 6N137 is faster than the 6N138.

3 - is this a problem with the speed of the Arduino Uno itself, i.e. it cannot keep up with the midi In stream when chords are played. I would have thought that a Arduino Uno would be many times faster than the chips used to control polyphonic synthesisers back in the 1980s. There is no other code within the loop function other than the MIDI.Read() method.

Please note that I have removed the Lcd library and Lcd display code from the project in an effort to isolate this - this does not cure the issue.

The most puzzling issue is that the NoteOn messages appear to work correctly, it is the NoteOff data that is missing or corrupted. However this may be an illusion, as the callback would not be called if the valid Note On data had not been read from the Midi data stream.

Any help would be much appreciated.

Thanks in advance

SpaceAvenger.

I would not use software serial for MIDI, it just uses up too much of the software resources and I am not surprised at what you see.
As to the MIDI input, I hope there is more than just the opto isolator you describe, for a proper MIDI in circuit see:-
http://www.thebox.myzen.co.uk/Hardware/MIDI_Shield.html

Hi Mike.

Many thanks for the info.

My Midi In circuit/schematic is the standard Midi In circuit containing a 220R resistor from pin 4 of Midi IN to pin 2 of the 6N137 with a 1N4148 diode between pins 2 and 3 on the Midi socket side. A 270R resistor is connected from +5v to the 6N137 pin 6 and Arduino Rx . This value 270R, AFAIK, is the correct value for the 6N137 chip. There seems to be a bit of discussion whether you need another resistor of around 1-10k connected from pin 7 to GND. Interestingly, your circuit is the first that I have seen that uses a PNP transistor for Midi OUT. There are several examples with no buffering at all (not recommended), and a couple which incorrectly invert the Tx stream (incorrect for Arduino). On the Midi out side my circuit uses a pair of 74HC14 inverters back to back - some examples use a combination of one inverter and one NPN transistor. A test Midi OUT project works fine from the Arduino, but as part of the investigation I removed the inverter chip and disconnected the Tx cable and this did not help with the Midi In problems as described. I also have wired in a Midi Thru socket but this is not currently connected to the Rx.

More interesting is the fact that it could be that SoftwareSerial is not up to the job for Midi. I will try changing over to using the Hardware Serial, although then I will not be able to see what it is doing as will not have Serial.println for debugging. However I could try to get some debug info on the Lcd. It is also possible that things may be better on the Due if the Serial interfaces are better. However, on a performance basis, I really would expect the Uno to be fast enough to read and decode Midi IN data at 31250bps

Warm regards

SpaceAvenger

I really would expect the Uno to be fast enough to read and decode Midi IN data at 31250bps

But bit banging the RX is not the most efficient, it just sucks CUP power from the system.
A better platform is the Mega, while the processor is of the same power it has four hardware serial ports so you can have your debug and your MIDI I/O.

The HC series of logic is not the best for driving power, better is the LS.

Sorry I got a bit lost in your description of the input side, I only talk schematic. :slight_smile:

However I am sure it is the software, either the software serial or the MIDI library. I have not been too impressed with it myself.

you might contact the rugged circuits guys and see if they have some MDI shields left. They're designed to work around the Serial issue via jumpers & the use of D2 through D11.

http://shieldlist.org/ruggedcircuits/midi

Did you ever figure this out? I'm thinking you might have missed the Running Status encoded events. It could be more likely that note-offs are batched with Running Status than Note-On's for the keyboard. I'm not sure what the default is for the MIDI lib.

I hope someone is still watching this ancient post . . . I have had similar problems, using Casio digital piano AP-21 and a "normal" MIDI hardware connection to the RX pin in the Arduino.

I want to use the MIDI input so that I can play a monophonic synthesiser on the Arduino. The synthesiser bit works a treat, it just needs to be told what notes to play. (It creates a "traditional" 888000000 Hammond sound, for those of you who like such things).

The only sketch that I've found that worked at all (trying for a cheap & fast solution) is the one by Amamda Ghassaei in

http://www.instructables.com/id/Send-and-Receive-MIDI-with-Arduino Step 10

(polling using Serial.read)

  • her interrupt-driven one looks better but I can't figure out of it is working, because of course I can't send serial data to my PC at 31250 bps.

I haven't found any other sketches that work at all (AFAIK) despite weeks of trawling the web. (At 1Mbps broadband, this is tedious!).

So I wondered if anyone has got a working solution. With over 1000 MIDI-Receive related threads in this Forum, there seems to be an awful lot of people having MIDI woes.

Maybe I can change my PC serial port to dream about receiving at 31250 bps . . . well within the normal maximum range.

  • Jim McLay (grumpy Old Man)

Hello grumpy old pirate

So you’re going from the keyboard to the arduino and the sound should come from the Arduino?

Are you taking the output of the keyboard to the standard opto isolator then to the rx input? Use a 6N138 or 6N139 opto circuit if you can.

If so, connect an LED and resistor to the opto output and see if it flickers when you press a key and then release it. It should flicker both times.
If not, then make that work.

Can you post your code please?

Let us know the results.

S

Hi Sparx,

I eventually figured out how to get the MIDI working, and have posted the working code in the Forum yesterday. However, I can no longer find the post, as it seems to have been moved by a Moderator. Maybe it’s being vetted. I’ll attach it here if I can.

It turned out (surprise surprise!) that I didn’t read the Casio MIDI Users Guide with 3-D glasses on. They don’t mention MIDI Note On commands as such although they do talk about 9n H which turns out to be just that, i.e. 1001 nnnn, although I only ever get 144 decimal from it when I press a new note. The trouble was, all the Serial.write lines produced rubbish on screen because I set the sketch baud rate to 31250, even though I tried Serial.end and Serial.begin(9600) with the Serial.write statements in between.

I almost gave up at this stage, but the breakthrough came when I spotted someone else trying to get Serial.write to work when the baud rate is set to suit MIDI, i.e. 31250. My PC doesn’t recognise this speed, and I couldn’t persuade it otherwise, but this other person recommended using “PuTTY” and sure enough, I could now see exactly what the Casio was sending out.

A typical sequence would be, say, press Bottom “A” and then A#, B & C (all next to each other) without letting the first note go, and then letting the notes back up one by one. The casio puts out

144 21 (vv) 22 (nn) 23 (aa) 24 (jj) 21 00 22 00 23 00 24 00

where the (pp) numbers are velocity bytes. These I didn’t need, because being an organ, velocity is irrelevant.

so because it’s monophonic, my code had to be written (I think) to remember how many keys are pressed down at every instant, so that it didn’t keep stopping the note all the time.

(A single note press would create 144 21 (xx) 21 00).

Anyway, once I figured out what the Arduino needed to look for, it all fell into place. I could see from ny scope that the MIDI signals were coming in Ok and fed straight into the RX pin.

I hope the attachment works Ok for you; I found so many sketches that didn’t do what they were supposed to.

I tried attaching the actual sound but the Forum doesn’t allow the MP3 as is. Will try and find a way around it.

Hammond.ino (5.38 KB)

Sorry, yes, MIDI in to the Arduino, and audio out from an external DAC MCP4921 mounted on a "shield".

Forum doesn't allow the MP3 as is. Will try and find a way around it.

Zip up the file first then it will allow you to post it.

What you're describing reminds me of this project: Vox/Farfissa organ
Have a look there to see if that helps.
Now you know that the hardware is working as it should, you could use a MIDI library, the one I use is here, but as you're only interested in Note on and off messages, it may be a bit over the top.

Slightly off topic, but there is a truly awesome DIY Hammond B3 project called PropB3 which you might be interested in.
It works very well.

I will, hopefully this week, give your code a go.

Regards

S

"Forum doesn’t allow the MP3 as is. Will try and find a way around it "/ “Zip up the file first then it will allow you to post it.”

Thanks G. Mike, that’s handy to know.

However, having listened to the PropB3 organ mentioned above, I’m not sure I’ll bother! Mine is embarrassingly poor in comparison!

Sparx, you may need to build the hardware I used if you’re going to try out the code. I have attached the circuit diagram (I think!).

I built a working prototype and now laying it out for a shield PCB; if you want one, I can add an extra one in to the order when I get the bugs out.

The Vox/Farfisa link was interesting because they used a Midivox PCB. I tried very hard to get Narbotic to tell me a bit more; the guy did actually email the Eagle files for the board, but it had an Arduino pin-out that wasn’t compatible with the Arduino SPI library (?). Took me ages to figure out why the SPI didn’t work. I can’t honestly see how to use it as-is, unless one was writing one’s own SPI code.

The PropB3 demo is just staggering!

May need to buy the next level of C++ programming text books.

I notice in the Combo organ webpage:

http://sandsoftwaresound.net/source/arduino-project-source/combo-organ/midivox-organ-comboorgan-ino/

that he uses pin 9 for SS, which is where my hardware fell down. The Arduino UNO schematic clearly shows SS on pin 10 so I’m simply mystified, sigh , and not for the first time!

Arduino audio shield.pdf (54.4 KB)

that he uses pin 9 for SS,

You can use any pin for the SS, your code has to set it specifically.

The Arduino UNO schematic clearly shows SS on pin 10

Yes but it is not fixed.

I must have cocked up my coding but feel righteous indignation, in that SPI didn’t work (for me) until I changed only the hardware, nothing else as far as I can remember. I physically disconnected pin 9 and shifted the SS signal to pin 10.

I wonder why Arduino specifically describe those pins as SS, MOSI, MISO and SCK if one can use any pin.

The bit of SPI code I used was:

" SPDR = highByte(sample) | 0x70;
while (!(SPSR & (1 << SPIF)));

SPDR = lowByte(sample);
while (!(SPSR & (1 << SPIF)));"

but I didn’t see any hardware advice in relation to SPI, and the circuit I was using was copied from a shield PCB called “Midivox”, which was purported to be a working project.

Look, GM, I’ll be interested to know, so’s I don’t fall into the same trap again.

(I hasten to add that I picked up this code without understanding it in depth, and was testing it in a very newbie fashion).

Any pin can be used for SS, but D10 must be set to OUTPUT for the Arduino to be SPI master. If it is set to INPUT or INPUT_PULLUP and the line is taken low, it will react as an SPI slave and may appear to hang the processor if there are no clocks to go with SS on D10 being low.

SCK, MISO, MOSI are connected to specific internal hardware and 13,12,11 Must be used on a '328P.
50-51-52 (check the pin assignments) and SS on 53 for a Mega2560.

Just to expand slightly on what grumpy said, any free pin can be used for SS (SS= Slave Select which is a Chip Select). Each slave (device) needs its own SS but all SPI devices will share the other three.
CrossRoads info is useful too.

Yes, I will have to build the DAC circuit to try the code. From the Narbotic website, the actual DAC part consists of an MCP4921 plus a few resistors and capacitors. Hidden in the blog, somewhere is that the 890R’s should actually be 909R. Don’t really think it will make much difference but please take a look before soldering. Notice you’re not using a preset at the output for level control?
It’s a simple circuit, so mine will be done on stripboard. Many thanks for the offer of a board.

This part is an afterthought to my own PropB3 set up, USB MIDI via a host shield to DIN MIDI. This is also sent to an additional DIN MIDI Out. There is also a DIN MIDI In.
This will enable the PropB3 to be played via a USB or DIN MIDI as well as act as a converter for a USB keyboard to DIN MIDI equipment.

There are also some buttons with associated LED’s to control Leslie on/off, fast and slow.
Other button/LED combinations control vibrato and percussion.

Somewhere on the internet I found a site with loads of Hammond registrations, these are programmed into external EEPROM and selected with up/down buttons.

Of course there is the 16 x 2 LCD with custom drawbars graphics, updated live.

All this fits in a laser cut Perspex case.

The above may sound a bit complicated to do, but taken a step at a time is achievable and is run on one Arduino Uno. Before anyone asks me to post the code, it is Work in Progress, delayed due to a leg injury.

Where the Vox/Farfissa bit comes in, if possible, is to have them as two extra voices in the set up, it may need an extra jack output, but that’s fine.

The PropB3 is an awesome project and I would urge you to do it. The author provides all the files on the site. I had to get a “propclip” to program one of the chips but it was all straightforward.

If only someone really good at this kind of thing would do a Vox version……

I wonder why Arduino specifically describe those pins as SS, MOSI, MISO and SCK if one can use any pin.

It is only the SS you can change, the others are fixed by the hardware.

Going back to the original question about not getting Note Off messages decoded properly, I don't know if it got sorted, but for what it is worth, the UNO I am using is easily able to keep up with the MIDI messages from a physical digital (piano) keyboard.

The 6N139 has very short (us) rise & fall times, well able for the task.

I didn't use any libraries because I don't know how. The Serial.read code SEEMS to take microseconds to read data, based on my uneducated way of using port commands to create a timing signal, before and after a Serial.read instruction.

It might help to prevent code from going around the void loop() - I imagine there could be a time delay there. If you use a WHILE command, at least the time taken is predictable.

And somewhere he refers to needing to read the Midi messages which I did with PuTTY running on my (Windows 7) PC.

Hope this helps.

I've just seen the latest posts about SPI pins on the Uno, thanks guys, I'm guilty of accessing the Forum via a small smartphone. Arduino is obsessive.

I discovered the levels I set in the code actually suited the mixer I am using with the pot set to max. I think I should actually put a 10k fixed resistor to keep the circuit impedance as it was with the pot.

I guess I am straying away from the original problem - oops.