lathoub / Arduino-USBMIDI

Allows a microcontroller, with native USB capabilities, to appear as a MIDI device over USB to a connected computer
MIT License
161 stars 11 forks source link

No midi send or received with board profile arduino zero #5

Closed jspx-projects closed 3 years ago

jspx-projects commented 3 years ago

Reproduce like this: Install MIDI and USB-MIDI, the latter pulls in also MIDIUSB. Load the example NoteOnOffEverySecond. The computer connected to the MCU via USB detects MIDI device Arduino zero, but no MIDI is received, and no midi notes are received by the MCU. This example works perfectly: MIDIUSBwrite, it uses the MIDIUSB library. So I will use that library now.

Device used: SAMD21 MINI, using profile "Arduino-zero (native USB).

lathoub commented 3 years ago

So I will use that library now.

OK

jspx-projects commented 3 years ago

So are you interested in making it work? Do you need any support or more info? I think it is not customary to close a bug report like that...

lathoub commented 3 years ago

You made up your mind to use the underlying library ('So I will use that library now') - fine with me.

I have no Arduino Zero here to test with, but this library does not call any other methods than the one in the MIDIUSBwriteexample. How about you add some Serial.printlns in the code and see what you get.

Happy to re-open the issue

jspx-projects commented 3 years ago

I investigated a little the example NoteOnOffEverySecond. I had to change Serial to SerialUSB, and then I got the message Arduino Ready at the USB serial console. Using amidi -p hw:4,0,0 --dump I see that the note on and note off commands are sent over USB, so that works. However, I see no more output on the serial monitor. This means that the call back functions are not triggered. I changed the MIDI.read() call in loop() to this:

byte input;
 if( (input=MIDI.read()) )
  {
      Serial.print("MIDI.read returned ");Serial.println(input, HEX);
  }

And this does not print anything. So if I understand the use of MIDI.read correctly this means that no MIDI is being read. I did not investigate this any further, but I hope this information is useful. Can you confirm that this example runs for you, and on what hardware?

lathoub commented 3 years ago

I have a hardtime understanding your setup, so let me show you my development setup:

IMG_7252

A Leonardo with a DIN MIDI shield, the Leonardo is connected to the computer via the USB cable. A Roland UM-One connecting the DIN connectors to the DIN MIDI shield, the USB to the computer.

The notes send from te Leonardo, come in MIDI-OX via the UM-One and the notes send from the UM-One and received in the Leonardo debug window.

jspx-projects commented 3 years ago

My setup is the same as yours, see photos, I also use the Roland UM-One. To the left you see my prototype, which can scan 4 complete organ keyboards 3 times per ms, and also read some analog inputs for volume and expression pedals. It outputs to MIDI DIN5 and USB and forwards everything from DIN5 MIDI in to DIN5 MIDI out and MIDI USB. The MCU board plugged into it is the SAMD21 MINI board I am using with board profile Arduino Zero (Native USB) in Arduino environment.

See the second photo with the results I mentioned in my previous post. I use the test program NoteOnOffEverySecond modified to work on my hardware (copied at the end of this post). Terminal on bottom-left shows MIDI USB output to be OK (command: amidi -p hw:3,0,0 --dump, I use Linux). Arduino serial console only shows "Arduino ready" so no messages received. In this test, the UM-Ono is not used since we are testing only USB MIDI. Just to be completely clear: I expect the test program to work only on USB MIDI and also to react to the messages it sends itself.

I am beginning to suspect that for this board MIDI.read() is looking at the wrong port, if such a thing is possible using USB MIDI.

I hope this helps, let me know if you have any other questions.

test setup

test results

#include <USB-MIDI.h>

USBMIDI_CREATE_DEFAULT_INSTANCE();
#define Serial SerialUSB

unsigned long t1 = millis();

void handleNoteOn(byte inChannel, byte inNumber, byte inVelocity)
{
  Serial.print("NoteOn  ");
  Serial.print(inNumber);
  Serial.print("\tvelocity: ");
  Serial.println(inVelocity);
}

void handleNoteOff(byte inChannel, byte inNumber, byte inVelocity)
{
  Serial.print("NoteOff ");
  Serial.print(inNumber);
  Serial.print("\tvelocity: ");
  Serial.println(inVelocity);
}

void setup()
{
  Serial.begin(115200);
  while (!Serial);

  MIDI.begin();
  MIDI.setHandleNoteOn(handleNoteOn);
  MIDI.setHandleNoteOff(handleNoteOff);
  Serial.println("Arduino ready.");
}

// -----------------------------------------------------------------------------
//
// -----------------------------------------------------------------------------
void loop()
{
  byte input;
  if( (input=MIDI.read()) )
  {
      Serial.print("MIDI.read returned ");Serial.println(input, HEX);
  }
  if ((millis() - t1) > 500)
  {
    t1 = millis();

    MIDI.sendNoteOn(27, 55, 1);
    MIDI.sendNoteOff(27, 55, 1);
  }
}
jspx-projects commented 3 years ago

I did an experiment. The test program is running and I use various sources of MIDI signals.

  1. The MIDI input of the test program does not react to its own messages. That seems an error.
  2. If I play a music midi file directly to the UM-one port I get three times the message that MIDI.read returned 1. Apparently these are not noteon or noteoff messages, since I get no other output.
  3. If I play a music file directly to the "Arduino Zero MIDI" port I also get three times the message MIDI.read returned 1. Then I have to wait about 1 minute before the noteon-off messages start streaming over the Serial Console. There are large pauses (tens of seconds) before the messages start streaming again.

Conclusion: MIDI.read is not reliable. This experiment gets me to the point where the program reacts to messages directly sent to the "Arduino Zero MIDI" port, but sporadically.

jspx-projects commented 3 years ago

Using my own keyboard reading program, and the MIDIUSB library, I can send the MIDI file to the port of the ARDUINO ZERO MIDI and everything is echoed without delay to the serial console. I think this really shows that something is going on in the layer on top of the MIDIUSB. For now this is the end of my input, I hope it is useful.

lathoub commented 3 years ago

It outputs to MIDI DIN5 and USB and forwards everything from DIN5 MIDI in to DIN5 MIDI out and MIDI USB. The MCU board plugged into it is the SAMD21 MINI board I am using with board profile Arduino Zero (Native USB) in Arduino environment.

I still struggle to understand your topology and the photos don't help. Your program (with the correction i mention below) works like a charm in my setup (and removing the #define Serial SerialUSB).

I'm pretty sure the issue has to do with your setup (MIDI DIN5, USB, MCU, SAMD21), try to simplify to the bare minimum. Remove the callback and just send 1 note every 500ms and see where the note ends up.

Note:

 byte input;
 if( (input=MIDI.read()) )
  {
      Serial.print("MIDI.read returned ");Serial.println(input, HEX);
  }

MIDI.read() returns a boolean, data come in or not. So printing input will only give you 1 or 0. The callbacks are the way to get informed about incoming MIDI commands. So:

 if( (MIDI.read()) )
  {
      Serial.println("MIDI activity");
  }
jspx-projects commented 3 years ago

Hello. I already know that sending notes work. They are received correctly on the MIDI port of the ARDUINO ZERO MIDI device, as shown by the screenshot. So I do not need to test that any further. Wrt the boolean: thanks for the info. Your change will not influence the rest of the program. but indeed you prevent needlessly printing out 1.

Can you indicate what the behavior of the test program is in your case? Are the callbacks triggered by the MIDI noteon/noteoff that is sent by the program itself? Since there is no documentation in the examples it is not at all clear what the expected behavior is...

Topology: MCU board connected via USB to computer. Roland UM-One connected to different USB port of same computer. Also Roland UM-One connected to DIN5 MIDI in and out of my prototype. Exactly your setup, no?

I also unplugged the two DIN5 connectors and the UM-One, so the setup is just my prototype over USB to the computer, and forget all the rest. MIDI sent correctly, but no MIDI received, so still does not work. So forget about the UM-One and the physical MIDI connectors, they are not related to the problem.

IMHU My earlier post [(https://github.com/lathoub/Arduino-USBMIDI/issues/5#issuecomment-752960723)] describes the problem clearly. That should be the base for further analysis. I think we need to think about differences in the Arduino core of my and your boards. What could be different and cause this problem?

EDIT: Unrelated information / perhaps useful in some way FYI: I used the setup to demonstrate that my prototype can forward everything it receives on DIN5 in to both DIN5 out and USB out. Further I demonstrated that the prototype generates MIDI correctly (key presses and volume changes) and sends it both to DIN5 out and USB correctly. The only thing I still need to implement is also to forward MIDI received over USB to DIN5 out. All of this works perfectly, as long as I use the MIDIUSB library.

EDIT:deleted incorrect part

lathoub commented 3 years ago

OK - understanding that sending works.

Here is how i get receiving MIDI messages to work:

Hardware: Leonardo USB connected to computer (no MIDI shields or anything else). Arduino: open the AllEvents example, compile/upload and open the serial monitor On a Windows box: open MIDI-OX, select "Arduino Leonardo" [1] from the output devices list, and output MIDI notes in MIDI-OX - they show in the Arduino Serial monitor.

screenshot

I don't use linux, don't what the equivalent is for MIDI-OX

Since there is no documentation in the examples it is not at all clear what the expected behavior is...

I look forward to your documentation suggestions, after we get this to work.

[1] the underlying USBMIDI library takes all care of this

jspx-projects commented 3 years ago

I did as you asked. I do not have an Arduino Leonardo, so I have to use board profile "Arduino zero (Native USB)". As expected, the result is the same as before.

  1. Sending is OK. I can monitor the MIDI that is sent: they are noteon and noteoff directly after each other, with a bunch of 00 00 messages in between, presumably the clock signals sent by the example.

  2. Receiving is not OK. 2a. I do not receive any MIDI generated by the example itself - my question to you still is: should I receive them? 2b. When I play a MIDI file the program receives something, but the results are erratic. The Arduino serial monitor shows this:

    ProgramChange from channel: 1, number: 1
    ControlChange from channel: 1, number: 7, value: 127
    ControlChange from channel: 1, number: 10, value: 64

(these are the first three MIDI messages I referred to in my earlier posts). Then after 36 seconds of waiting the Serial monitor shows more notes and program changes for about 24 seconds. Then a pause, and again some notes after the pause.

If I start the song again - same pattern. The three program changes, wait for 36 seconds, only then see some more notes and program changes etc.

So my conclusion is the same as before, MIDI.read works erratically.

ps these are the linux commands I use to play a song: list all MIDI ports:

 aplaymidi -l
 Port    Client name                      Port name
 14:0    Midi Through                     Midi Through Port-0
 20:0    UM-ONE                           UM-ONE MIDI 1
 28:0    Arduino Zero                     Arduino Zero MIDI 1

Send a MIDI file to the Arduino MIDI port: aplaymidi -p 28:0 ./Disney_Themes_-_Fantasmic.mid Just a random MIDI file I found on the internet.

If I play the MIDI file like this and use my own software (using USBMIDI directly) everything works perfectly. I see the same song "playing" on the Arduino serial monitor really fast, no delays.

And these are the commands I use to monitor the raw data on the MIDI ports (in HEX): list MIDI devices:

amidi -l
Dir Device    Name
IO  hw:1,0,0  UM-ONE MIDI 1
IO  hw:3,0,0  Arduino Zero MIDI 1

monitor MIDI port

amidi -p hw:3,0,0 --dump
lathoub commented 3 years ago

put in some 'poor-man debugging' in the source code:

https://github.com/lathoub/Arduino-USBMIDI/blob/da85b845f594c660998bb1169a9817860f17914f/src/USB-MIDI.h#L137

add Serial.println(mPacket);

This library is a thin rapper over USBMIDI on top of the MIDI-library, with only 1 MidiUSB.read(); See what the Serial.println does

lathoub commented 3 years ago

(just checking: you do not have old versions of the MIDI-library, USBMIDI or Arduino-USBMIDI in your path)

jspx-projects commented 3 years ago

I had to do it like this (in my file, the line was 139, not 137):

        mPacket = MidiUSB.read();
        SerialUSB.print(mPacket.header, HEX); 
        SerialUSB.print(mPacket.byte1, HEX); 
        SerialUSB.print(mPacket.byte2, HEX); 
        SerialUSB.println(mPacket.byte3, HEX); 

And the result is this (all zeros):

...
0000
0000
...

I installed USB-MIDI and MIDIUSB together last week. Just upgraded MIDIUSB to 1.05. Not sure when I installed MIDI_library. Arduino output says this:


Linking everything together...
/home/john/.arduino15/packages/arduino/tools/arm-none-eabi-gcc/7-2017q4/bin/arm-none-eabi-g++ -L/tmp/arduino_build_640052 -O2 -Wl,--gc-sections -save-temps -T/home/john/.arduino15/packages/arduino/hardware/samd/1.8.9/variants/arduino_zero/linker_scripts/gcc/flash_with_bootloader.ld -Wl,-Map,/tmp/arduino_build_640052/AllEvents.ino.map --specs=nano.specs --specs=nosys.specs -mcpu=cortex-m0plus -mthumb -Wl,--cref -Wl,--check-sections -Wl,--gc-sections -Wl,--unresolved-symbols=report-all -Wl,--warn-common -Wl,--warn-section-align -o /tmp/arduino_build_640052/AllEvents.ino.elf /tmp/arduino_build_640052/sketch/AllEvents.ino.cpp.o /tmp/arduino_build_640052/libraries/MIDI_Library/MIDI.cpp.o /tmp/arduino_build_640052/libraries/MIDIUSB/MIDIUSB.cpp.o /tmp/arduino_build_640052/core/variant.cpp.o -Wl,--start-group -L/home/john/.arduino15/packages/arduino/tools/CMSIS/4.5.0/CMSIS/Lib/GCC/ -larm_cortexM0l_math -lm /tmp/arduino_build_640052/../arduino_cache_616852/core/core_arduino_samd_arduino_zero_native_1f31e3723d891ad684fba1f25f33f1e5.a -Wl,--end-group
/home/john/.arduino15/packages/arduino/tools/arm-none-eabi-gcc/7-2017q4/bin/arm-none-eabi-objcopy -O binary /tmp/arduino_build_640052/AllEvents.ino.elf /tmp/arduino_build_640052/AllEvents.ino.bin
/home/john/.arduino15/packages/arduino/tools/arm-none-eabi-gcc/7-2017q4/bin/arm-none-eabi-objcopy -O ihex -R .eeprom /tmp/arduino_build_640052/AllEvents.ino.elf /tmp/arduino_build_640052/AllEvents.ino.hex
Using library USB-MIDI at version 1.1.2 in folder: /home/john/Arduino/libraries/USB-MIDI 
Using library MIDI_Library at version 5.0.2 in folder: /home/john/Arduino/libraries/MIDI_Library 
Using library MIDIUSB at version 1.0.5 in folder: /home/john/Arduino/libraries/MIDIUSB 
jspx-projects commented 3 years ago

I will reply again tomorrow, I already wish you a happy new year :-) :+1:

ps I get this warning (not important, but still)

/tmp/arduino_modified_sketch_881307/AllEvents.ino: In function 'void OnSystemExclusive(byte*, unsigned int)':
/tmp/arduino_modified_sketch_881307/AllEvents.ino:133:38: warning: unused parameter 'array' [-Wunused-parameter]
 static void OnSystemExclusive(byte * array, unsigned size) {
                                      ^~~~~
/tmp/arduino_modified_sketch_881307/AllEvents.ino:133:54: warning: unused parameter 'size' [-Wunused-parameter]
 static void OnSystemExclusive(byte * array, unsigned size) {
jspx-projects commented 3 years ago

[EDIT: included spaces in the print statements so you can distinguish the nibbles in each byte]

I now made some progress. I changed the location of the print statements to this:

        mPacket = MidiUSB.read();

        if (mPacket.header != 0) {
//
        SerialUSB.print(mPacket.header, HEX);SerialUSB.print(" "); 
        SerialUSB.print(mPacket.byte1, HEX);SerialUSB.print(" "); 
        SerialUSB.print(mPacket.byte2, HEX);SerialUSB.print(" "); 
        SerialUSB.println(mPacket.byte3, HEX); 

If I now play a MIDI file to my boards USB MIDI port, I can see a continuous stream of output, so I think that the complete MIDI file is received: the MidiUSB.read() works. But not all received notes are "received" by the test program

Here is the first part of the serial monitor output, it shows that after the first three MIDI notes, a lot are received but do not result in statement printed to the serial monitor, so not actually received by the test program.

C C0 1 0
ProgramChange from channel: 1, number: 1
B B0 7 7F
ControlChange from channel: 1, number: 7, value: 127
B B0 A 40
ControlChange from channel: 1, number: 10, value: 64
B B1 0 0
B B1 20 0
C C1 3C 0
B B1 7 7F
B B1 A 40
B B2 0 0
B B2 20 0
C C2 3D 0
B B2 7 7F
B B2 A 40
B B3 0 0
B B3 20 0
C C3 3E 0
B B3 7 7F
B B3 A 40
B B4 0 0
B B4 20 0
C C4 21 0
B B4 7 7F
B B4 A 40
B B5 0 0

Please find attached the complete serial monitor output. It confirms my earlier experiments: there are only a few sections (with large pauses in between) where the mPackets that were received result in MIDI "received" by the test program. I hope this allows you to form a hypothesis of any problems.

Let me know if I can do anything more :-)

John output of serial monitor.txt

lathoub commented 3 years ago

Lets see what is fed to the underlying MIDI parser, add a SerialUSB.print(byte, HEX); just before return byte;

https://github.com/lathoub/Arduino-USBMIDI/blob/da85b845f594c660998bb1169a9817860f17914f/src/USB-MIDI.h#L123-L128

Then also add an errorHandler

void handleError(int8_t err)
{
   SerialUSB.print(err, HEX);
}
...

void setup()
{
...
  MIDI.setHandleError(handleError);

What does the input stream look like?

jspx-projects commented 3 years ago

Here is the requested output of again the whole song. It seems again the same pattern and I do not see any err from the error handler. I made it like this

  void handleError(int8_t err)
{
    SerialUSB.print("Error ");
    SerialUSB.println(err, HEX); 
}

20210101 Bytes read from USBM MIDI.txt

lathoub commented 3 years ago

What does the input stream look like? (what do you send to the USB device)

lathoub commented 3 years ago

I was able to find a hypothesis for the problem. The Disney_Themes_-_Fantasmic.mid on the internet plays a song on all midi channels . Your application only listens to channel 1 (not passing any argument to MIDI.begin() will set the channel to 1)

Solution: MIDI.begin(MIDI_CHANNEL_OMNI );

jspx-projects commented 3 years ago

Yes, this modification allows "all_events.ino" to receive the complete MIDI from the file now, including the channel number! So this solves the problem for that test program. I still suggest adding some documentation to the various test programs in the comments at the start and modify the test program so it listens on all channels.

I will now create a new version of my own program using the USB-MIDI library instead of MIDIUSB, and report back on that.

lathoub commented 3 years ago

I still suggest adding some documentation to the various test programs in the comments at the start and modify the test program so it listens on all channels.

I look forward to your contributions to this open source project

jspx-projects commented 3 years ago

IThe library itself is ready, but I will try to document the examples a bit. The source of my own confusion is the example NoteOnOffEverySec. I expected the noteOn and NoteOff sent by the program to be also received by the program, which is not the case.

jspx-projects commented 3 years ago

Ok, it was an error on my side, caused by misinterpretation of the example file.