Open kevinjwalters opened 5 years ago
I'm running into this issue with my M-Audio Axiom61 controller. Checking to see if M-Audio has an option to turn it off. Albetron controllers also use the running status method. Newer controllers like my Arturia appear to send fully-formed messages as a default. UPDATE: No option for selecting/deselecting running status on the Axiom61 controller.
I started looking at this because I thought I was seeing use of running status from playing MIDI files in Ableton. It turned out to be byte loss somewhere due to some big bursts of pitch bends.
I checked for that on the Axiom, but was able to create the issue manually by pressing a single key and releasing it before the ~500ms running status timeout (confirmed with an oscilloscope). Results in a stuck note. It's very noticeable when practicing scales. ;)
I'm running into a similar issue reading MIDI off of a Roland TD-17 vDrum module. I worked around the issue for the cymbals by enabling a "Cymbal Choke Shot" setting on the vDrum module, but unfortunately there is no such setting for the drum heads.
If I hit two drums nearly consecutively, I receive two 'note' and 'velocity' pairs, but only one 'NoteOn' event message. This has been causing me some heartache, as I kept trying to troubleshoot where I might be 'dropping' notes.
I'm super happy I now understand the issue... but not sure the best approach to resolve it. Is this something that's best implemented within the library, or can I modify my code to read two consecutive 'note' values from one NoteOn event message?
Any guidance would be greatly appreciated :)
This has to be done in the library. I'm just having a look now to see how easy it is to slot in. I almost left a gap for it when I implemented the receive side of the library...
Much appreciated!! -- Let me know if you could use any help testing the implementation.
@kevinjwalters, curious if you've been able to take a look at this or if there's anything I can do to help. I've started digging through the library a little bit to understand it, but am not very practiced.
@DyerMaker953 I have something that passes some unit tests but haven't tried it with any real MIDI. I'll upload it later today for you to have a play with.
@DyerMaker953 Have a play with https://github.com/kevinjwalters/Adafruit_CircuitPython_MIDI/tree/running-status and let me know what happens. It'll be the first test with an actual MIDI device!
@kevinjwalters Awesome job! - I fired it up and played around a little bit; everything seems to be working well on my Roland TD-17 Drum module.
I noticed the debug output now shows both the note value & velocity value of the running status as a single message -- previously this was outputted as two separate messages, one for note and one for velocity. I assume it's in the changes you made to how the buffer is being built -- Pretty cool stuff!
I'll continue messing around with it some more and let you know if I discover anything unusual.
Can you cut and paste the some of the relevant debug output? I didn't mean to change that. I suspect I haven't changed it, it's just the timing has changed a little from correct parsing of running status. I'd imagine the data is now coalesced because parsing take a little longer and by that time the next note may have arrived in the hardware serial buffer or over USB.
I believe you're right about it being a timing issue. When I recorded the midi messages before your changes it just happened to be more consistent in timing. Now that I've recorded the messages again, it appears to group values together as it's able to with the time it's given.
Without running status:
Snare Hit
Receiving: ['0x99'] NoteOn
Receiving: ['0x26', '0x34'] Note , Velocity
Receiving: ['0x89'] NoteOff
Receiving: ['0x26', '0x40'] Note, ???
Tom3 Hit
Receiving: ['0x99'] NoteOn
Receiving: ['0x30', '0x61'] Note, Velocity
Receiving: ['0x89'] NoteOff
Receiving: ['0x30', '0x40'] Note, ???
Snare Hit
Tom3 Hit (undetected)
Receiving: ['0x99'] NoteOn
Receiving: ['0x26', '0x4d'] Note, Velocity
Receiving: ['0x30'] Note
Receiving: ['0x4a'] ???
Receiving: ['0x89'] NoteOff
Receiving: ['0x26', '0x40'] Note, ???
Receiving: ['0x30'] Note
Receiving: ['0x40'] ???
Snare Hit
Receiving: ['0x99'] NoteOn
Receiving: ['0x26', '0x34'] Note, Velocity
Receiving: ['0x89'] NoteOff
Receiving: ['0x26', '0x40'] Note, ???
Tom3 Hit
Receiving: ['0x99'] NoteOn
Receiving: ['0x30', '0x61'] Note, Velocity
Receiving: ['0x89'] NoteOff
Receiving: ['0x30', '0x40'] Note, ???
Snare Hit
Tom3 Hit (undetected)
Receiving: ['0x99'] NoteOn
Receiving: ['0x26', '0x4d'] Note, ???
Receiving: ['0x30'] Note
Receiving: ['0x4a'] Velocity
Receiving: ['0x89'] NoteOff
Receiving: ['0x26', '0x40'] Note, ???
Receiving: ['0x30'] Note
Receiving: ['0x40'] ???
With running status:
8772.35 : Snare hit
Receiving: ['0x99', '0x26'] NoteOn, Note
Receiving: ['0x4b'] Velocity
Receiving: ['0x89'] NoteOff
Receiving: ['0x26', '0x40'] Note, ???
8773.1 : Tom2 hit
Receiving: ['0x99'] NoteOn
Receiving: ['0x2d', '0x64'] Note, Velocity
Receiving: ['0x89', '0x2d'] NoteOff, Note
Receiving: ['0x40'] ???
8774.2 : Snare hit
8774.26 : Tom2 hit
Receiving: ['0x99'] NoteOn
Receiving: ['0x26', '0x50'] Note, Velocity
Receiving: ['0x2f'] Note
Receiving: ['0x6f'] Velocity
Receiving: ['0x89'] NoteOff
Receiving: ['0x26', '0x40'] Note, ???
8775.32 : Snare hit
Receiving: ['0x99'] NoteOn
Receiving: ['0x26', '0x40'] Note, Velocity
Receiving: ['0x89', '0x26'] NoteOff, Note
Receiving: ['0x40'] ???
8775.84 : Tom2 hit
Receiving: ['0x99', '0x2d'] NoteOn, Note
Receiving: ['0x55'] Velocity
Receiving: ['0x89'] NoteOff
Receiving: ['0x2d', '0x40'] Note, ???
8776.59 : Snare hit
8776.62 : Tom2 hit
Receiving: ['0x99', '0x26'] NoteOn, Note
Receiving: ['0x55'] Velocity
Receiving: ['0x2d', '0x5f'] Note, Velocity
Receiving: ['0x89'] NoteOff
Receiving: ['0x26', '0x40'] Note, ???
Receiving: ['0x89'] NoteOff
Receiving: ['0x2d', '0x40'] Note, ???
Per channel messages can omit the status byte and just provide data bytes which are then consider to be a repeat of the previous MIDI event with the new data.
For the implementation this implies storing the last status byte for every relevant message in case it occurs.
Some non-official discussions on this:
Test for this is partially written, see
Test_MIDI.test_running_status_when_implemented()
in https://github.com/adafruit/Adafruit_CircuitPython_MIDI/blob/master/tests/MIDI_unittests.py