-
Notifications
You must be signed in to change notification settings - Fork 39
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add Sketch for MIDI Using Interrupts #15
base: master
Are you sure you want to change the base?
Conversation
Status byte is available from interrupt and is passed into method.
This version can't use hub, but is more reliable.
Need to allocate EP 2 and up to Midi devices when present. Not sure how to do that yet.
Interrupts mean host needs a fixed EP address. MIDI device also needs fixed EP address. Those two EP addresses have to align. Hub also has EP address, and will conflict with device EP address. Polling the hub using the current approach causes the pipe to switch to the hub and breaks events on the MIDI device pipe, so that is not an option if the EP on the device is the same as the EP on the hub.
This reverts commit 20cb55b.
Ok. Thanks. I'll try that. |
Fixed bug in handleBank1 that always looked at bufBk1[0] instead of bufBk1[i].
On second thought, maybe neither is the problem.
I don't read the dataString inside the greater than zero check. At the end of the handleBank0(uint32_t epAddr) and handleBank1(uint32_t epAddr) i call a function that reads rcvd bytes from the bufBk0[i] or bufBk1[i] (depending who called it). Then i check these bytes by chunks of 4 (and this works right until the missing bytes). I receive them in this order: [ rcvd = uhd_byte_count0(epAddr) or uhd_byte_count1(epAddr) respectively ] 04 F0 52 00 -> 04=3 bytes following, F0=start of sysex message (1byte), 52 00 2 more bytes (rcvd=4) I don't know if this is easy to read. I'll try to provide the working part of the code.
if i read one byte by one, i'll end up again checking 4 bytes at a time as this is the way a sysex message works. Any other idea? I hope you have the patience to read it! Thanks. |
Each time I asked uhd_byte_count for the number of bytes it was 64,
with zeros if necessary to make up the 64. I thought this was a matter of the USB pipe, and would always be the case, but perhaps it is device specific.
It looks like you are sometimes getting a byte count of 4, which is great, but sometimes you get a byte count of 64 with extra zeros to make up the 64. Maybe that happens when the device has more than 4 bytes to send.
The zero padding on the end is why you get "56 bytes 00
which are not part of the expected sysex." You have to figure out how to strip those unwanted zeros off.
|
When i get (04 0C 00 00) (04 00 00 00) 00 00 00 00 ... 00 i know that i have 2 chunks of 4 bytes and i discard the rest zero bytes because they are not valid sysex headers. (i expect 04, 05, 06 or 07 as a valid sysex header of a chunk of 4 bytes, not zero). The second time it runs it also misses 42 bytes but in another location. |
I wonder if you are trying to transfer more than 64 bytes. |
You mean the device responds with a message larger than 64 bytes? The device (a multieffects pedal atually) has only USB. If i send a specific sysex command (by using amidi .... --send-hex="....") it responds with the expected sysex message which is 134 bytes. It is also listed as a MIDI device in amidi -l Also by using this code i have received a 15 bytes sysex message as a response to a sysex command that i had send to it |
Yes although I am just guessing. I would uncomment lines 159-183 (the last
one is an error and needs to say println instead of print). It will show
you the bank statuses and the byte counts. The handlers should scoop all of
the bytes in the banks on every interrupt. You might be able to see if both
banks are full and something is getting missed. I tested the case of both
banks full and it should be fine (it is specifically addressed on lines
186-195.
|
ok. I'll check. Thanks. |
Note that my modification here to the library is minimal. It just adds a way for the sketch to ask for the pipe endpoint so it can set up an interrupt on it. This example sketch leverages the library to poll the device for initialization (enumeration). The library therefore does the work of configuring the SAMD21 pipe. Once the pipe is configured, the sketch does some minimal touch up on the pipe config, but does not specify the pipe size. Once the interrupt is configured, the library is no longer used, and the sketch just depends on receiving interrupts from the SAMD21 and receiving the data directly from the pipe endpoint. The only part of this interrupt based approach that mentions pipe size is in the buffer declarations here, which are set to 64 in the sketch: I am not super clear on how the library handles Sysex, since I did not try it, but you can see in the library that usbh_midi.h has the following declarations. As near as I can tell, the SAMD21 does not impose a 64 byte limit. I am not sure if this library does, but that SYSEX define is encouraging. It looks like USB full speed has a max packet size on bulk endpoints of 64 though. You might try bumping up the buffers in the sketch to 256 and try it. The only other thing I can think of is that there is more than one IN endpoint set up when this device enumerates, and my function Midi.GetEpAddress() is not returning the one you want. But I don't think that's true because you are getting most of the data. We seem to have the correct endpoint. |
I tried it with 128, 256 byte buffers but no good. The receiving bytes are always the same. Basically whatever i tried leads to exactly the same result. It somehow loses, or better it doesn't get some pieces of the message. The weird thing is that the first time it misses 42 bytes here and for any other run it misses 42 bytes in a later part. Always the same. |
You can try the using one of the other examples in this library and see if it works. Maybe USBH_MIDI_dump.ino. In that case, the sketch calls Midi.RecvData to get the data from the device. That call will construct a pipe, and set it up to receive data. The pipe is constructed every time you make the call. This leads to dropped data, but it may be more robust if your device is requiring a new pipe in between transfers. Not sure if you got the print statements working. It's a really a must to troubleshoot. For example. your device might be disconnecting and reconnecting in the middle of the Sysex message. You would see that with print statements. The interrupt sketch might have trouble in that case, not sure. |
I had tried the print statements and was getting the same results. But i tested something else. I stopped calling a function (that is checking the received bytes) from withing handleBank0 and handleBank1 and commented most of the debugging prints i used. I left only your prints and the result is different. It now starts getting more bytes than the previous time and it now loses 21 bytes (half of the previously missed number, if that means something). This behavior applies the first time it runs. The second time (without reset) it misses a lot of bytes. And after that it keeps cycling these 2 cases. That means that the problem might be some kind of timing? The print statements i get:
|
So the last two lines there before the disconnect are where the data is dropped? What caused that disconnect? But yes if you are doing less work now within the handler, and as a result you are getting more data, it suggests that the device is trying to send more data than the SAMD21 can receive. Perhaps while you are processing the data, the device overflows or gives up. I just noticed as well that almost immediately you see that both banks always have data. I would consider this a bad sign. I think it means the device is filling up the banks faster than you can drain them. A healthy sign would be one of the banks empty every time you handle the interrupt. It would mean that the handling is not having trouble keeping up. Suddenly not so sure about this theory though because TRCPT flags are not both 1. It's been a while since I have looked at the debug output during my successful runs. I thought that the byte count on one of the banks was always zero. Notice this option in the sketch. This is the interval that the SAMD21 polls the device for data. Counter intuitive perhaps, but maybe an increase would improve reliability. Perhaps telling the device to slow down a little (by polling it less frequently) you will give your sketch more time to process the data from the banks. For bulk type, the datasheet pg 824 says this parameter means 1 ms to 255 ms. |
I got it working by adding
instead of in the By the way thanks for your interest, it kept me trying. |
The pipeConfig function was pieced together from the calls I found within the library that set up the pipe. At first I thought I needed to have it to set up the pipe because I was going to abandon the library, but later I realized that I could use the library to enumerate the USB device and set up the pipe. So I no longer needed pipeConfig, but I left it commented out, because it contained a lot of interesting methods that I thought someone might be able to use. So your case is exactly what I had in mind. The commented out code is a kind of reserve toolbox that you can use to try things. I am still a little confused about what is happening. I thought that the bulk endpoints had to be 64. Maybe I am confusing pipe size and endpoint size. Anyway, congrats. Thanks for picking up this code and taking it further. |
Thanks for trying to figure this one out! Did you ever try this out "for real"? I'm having problems with missed events if I press more than one key at the same time, seems like this code can reliably only handle one midi event at a time. I'm experimenting with an AKAI MPK Mini. |
USB is serial, and the events will be transferred in series, not parallel. It is not possible to have more than one event at a time communicated over USB. Of course it is possible to press more than one key at a time, but the keyboard must be capable of detecting that situation and submitting individual NOTE ON events sequentially to the USB interface. |
Thank you for the reply! Sorry, I didn't properly explain the problem. What I meant with the code only being able to handle one MIDI event at a time reliably was that as soon as I press or release two or more keys at the same time there is a big chance that some note on/off messages are missed and I can't really figure out why (same problem as with the non-interrupt based examples I have seen). The keyboard (AKAI MPK Mini) works perfectly when connected to a computer. |
I have used pipe interrupts for fast MIDI clock and polyphony without any message loss. My guess is that the problem is elsewhere. You might try the same test with a different host to check. |
Thanks, yes I will do some further investigation. I have this problem with the interrupt example in your fork (and this pull request) so I was wondering if you have experienced a similar issue. |
No, I have not seen any message loss problems using the interrupts approach. It's seems to be very reliable. |
Thanks for your replies and yes you were right, the problem was on the receiving end. Your interrupt based code seems to work great, thanks a bunch for sharing it! :) |
Here is an example sketch that uses interrupts instead of polling to read MIDI data from a single device.
The sketch requires only one small addition to usbh_midi.h to allow the sketch to fetch the device EP address.
Tested with Korg nanoKey2 and SQ-1.
The sketch illustrates how to use interrupts and might lead to further development.