Skip to content
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

Fix missing InputEventMIDI from MIDI with multiple messages eg chord #89871

Closed
wants to merge 1 commit into from
Closed

Conversation

markofjohnson
Copy link

Fix missing InputEventMidi from midi with multiple messages eg chord

MIDIDriver::receive_input_packet can be called to process an incoming midi packet that contains multiple messages. Eg playing a chord (several notes at the same time) may arrive using midi 'running status' and contain several NoteOn in one packet. This commit loops through all the messages in the midi packet creating an InputEventMidi for each one.

Fixes #77035

Simple fix but quite a serious MIDI bug that can be reproduced with eg Roland keyboards connected to macOS by bluetooth. Could effect Windows & Linux too, reproducibility will depend on hardware and OS midi configuration. Suggest consideration for Godot 4.2 and 3.x as their midi_driver.cpp is almost the same and has the same bug. Thanks.

@lawnjelly
Copy link
Member

You will need to squash commits:
https://docs.godotengine.org/en/stable/contributing/workflow/pr_workflow.html#the-interactive-rebase

There will probably be a few nitpicks but the principle looks sound, well done for spotting this.

core/os/midi_driver.cpp Outdated Show resolved Hide resolved
core/os/midi_driver.cpp Outdated Show resolved Hide resolved
core/os/midi_driver.cpp Outdated Show resolved Hide resolved
core/os/midi_driver.cpp Outdated Show resolved Hide resolved
core/os/midi_driver.cpp Outdated Show resolved Hide resolved
core/os/midi_driver.cpp Outdated Show resolved Hide resolved
core/os/midi_driver.cpp Outdated Show resolved Hide resolved
@AThousandShips
Copy link
Member

Also please make your one commit have a clear message, like that of the PR title

@AThousandShips AThousandShips changed the title Fix missing InputEventMidi from midi with multiple messages eg chord Fix missing InputEventMidi from MIDI with multiple messages eg chord Mar 25, 2024
@AThousandShips
Copy link
Member

You merged instead of pushing, you need to:

git reset --hard 8d4d345
git push -f

@AThousandShips AThousandShips added this to the 4.3 milestone Mar 25, 2024
@markofjohnson
Copy link
Author

Hey @AThousandShips I'm struggling with git. Is it in the correct state now?

Also, there is another change I will propose to this method to protect against a hang if a malformed midi packet comes in (which given it could come from any MIDI device is possible). How should I proceed? I am thinking I will make another commit and ask for feedback?

@AThousandShips
Copy link
Member

You can add it as a separate commit to make it easy to remove if it's not desired :) the commit history looks good now

@markofjohnson markofjohnson marked this pull request as draft March 25, 2024 17:34
@lawnjelly
Copy link
Member

It's actually decades since I did any MIDI but afaik some things to be sure here is:

  • There are no infinite loops
  • It handles gracefully any unknown MIDI messages
  • Ignores system exclusive messages

@markofjohnson
Copy link
Author

markofjohnson commented Mar 25, 2024 via email

@markofjohnson markofjohnson marked this pull request as ready for review March 25, 2024 19:27
@markofjohnson
Copy link
Author

@lawnjelly and @AThousandShips are we OK with commit d33ee9d re stopping the infinite loop. Adding this is necessary because I added the while loop in order to fix the bug #77035 . Its not perfect because I didn't extend the code to support every possible MIDI message, but I consider that outside the scope of fixing the original defect I ran into.

If you approve I suppose I have to squash commits again?

@AThousandShips
Copy link
Member

Will take a look later today

@markofjohnson
Copy link
Author

Hey I got really lost in git and decided to start my branch over. I think I now have clean 1 commit with the fixes we discussed. Thanks.

@markofjohnson markofjohnson marked this pull request as draft March 27, 2024 17:32
MIDIDriver::receive_input_packet can be called to process an incoming midi packet that contains multiple messages. Eg playing a chord (several notes at the same time) may arrive using midi 'running status' and contain several NoteOn in one packet. This commit loops through all the messages in the midi packet creating an InputEventMidi for each one.

Includes code review feedback by A Thousand Ships <[email protected]> and https://github.com/lawnjelly

Fixes #77035

Tested manually with various .mid and .sysex files to inject midi as if from midi hardware. Tested on macos with Roland keyboards. Fix isn't macos specific, but original defect 77035 found on mac, not found on windows because windows midi handling by OS 'unrolls' running status midi into individual messages before reaching Godot, which works around the defect.
@markofjohnson markofjohnson marked this pull request as ready for review March 28, 2024 20:31
Copy link
Member

@lawnjelly lawnjelly left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks ok to me and seems to have had some testing. I don't know if we have any specific maintainers for MIDI (reduz will probably be familiar, but may be busy) but anyone else welcome to take a look. 👍

I don't know how many users use MIDI input so might need some testing in the wild through betas.

@markofjohnson
Copy link
Author

@AThousandShips this MIDI PR ready for review if you want a look. Thanks.

Copy link
Member

@AThousandShips AThousandShips left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Style and general function looks good, can't verify the validity in regards to MIDI

@akien-mga akien-mga changed the title Fix missing InputEventMidi from MIDI with multiple messages eg chord Fix missing InputEventMIDI from MIDI with multiple messages eg chord Apr 4, 2024
@akien-mga
Copy link
Member

CC @fbcosentino @alcomposer @voidshine @ibrahn who contributed to MIDI support in the past year or so.

@fbcosentino
Copy link
Contributor

Will try to both reproduce the issue and test the commit on both windows and linux this weekend

@markofjohnson
Copy link
Author

markofjohnson commented Apr 4, 2024 via email

@ibrahn
Copy link
Contributor

ibrahn commented Apr 9, 2024

(Sorry I'm late)

On a read through, I see three possible issues:

  • Realtime messages (0xf8 to 0xff) will interrupt running status.
  • If a packet contains an "unsupported" message type, the remainder of the packet will be discarded.
  • Messages from one device can interrupt running status on another. The status bit cache last_received_message should be per connection, rather than global.

I think these problems will already have existed for MacOS builds, so this PR still improves things.

It looks like #77035 affects only Core MIDI (MacOS).
We currently only have two other MIDI drivers:

  • on Windows, the platform API handles running status for us and drops SysEx.
  • on Linux, our ALSA MIDI driver does this parsing, before passing single complete messages to receive_input_packet.

So the argument could be made to fix this at the Core MIDI driver.
However, looking forward it probably does makes sense to have a parser at the platform independent level, making it less work to add MIDI input drivers for iOS, Android etc.
Perhaps we should move the existing implementation up from the ALSA class to the platform independent. I can look at that this afternoon/tomorrow.

@markofjohnson
Copy link
Author

I think these problems will already have existed for MacOS builds, so this PR still improves things.

Yes, and cited are advanced cases of MIDI usage that would require more Godot development to support anyway. Without a fix, MIDI on Mac only works for single note at a time. BTW I tested MIDI in with 2 devices and indeed it's possible to corrupt running status data. Result was bad data through to the gdscript code but no crash.

So the argument could be made to fix this at the [Core MIDI driver]

Would require duplicate parsing code in midi_driver_coremidi.cpp, and it would have to allocate memory and copy data in order to turn running status into individual messages? Not ideal but it would isolate this fix from Windows.

Looking forward it probably does makes sense to have a parser at the platform independent level, making it less work to add MIDI input drivers for iOS, Android etc.

Good idea, especially working towards MIDI in for mobile, but out of scope for this bugfix PR.

thanks.

@ibrahn
Copy link
Contributor

ibrahn commented Apr 13, 2024

I've done the MIDI parser move, over at #90485. Could you run your chord test against it? I'm afraid I don't have access to a Mac at the moment

@markofjohnson
Copy link
Author

markofjohnson commented Apr 14, 2024

I've done the MIDI parser move, over at #90485. Could you run your chord test against it? I'm afraid I don't have access to a Mac at the moment

I tested chord playing on Mac. This fixes the bug :-) . I logged data going in and confirmed that packets with more than one MIDI message work correctly. I noticed that I was not seeing MIDI running status, but packets with multiple messages. (So the previous diagnosis of the problem may not have been due to running status, but due to more than 1 MIDI message in 1 packet.)

So should I withdraw this PR? How - just close it?

@ibrahn
Copy link
Contributor

ibrahn commented Apr 15, 2024

I'm not sure what the process is at the moment, but I'd guess leave it open for now, at least until we know if #90485 passes review. As a fallback to keep MacOS MIDI on the cards for 4.3

@akien-mga
Copy link
Member

I believe this is superseded by #90485, but I'm not too familiar with the details. Are there still changes from this PR which would be worth merge after rebasing / redoing the changes, or should this be closed now?

@markofjohnson
Copy link
Author

withdrawing pr because #90485

@markofjohnson
Copy link
Author

Superseded by #90485

@AThousandShips AThousandShips removed this from the 4.3 milestone Jun 26, 2024
@akien-mga
Copy link
Member

Thanks for the contribution anyway, which led to finding the better cross-platform solution.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

InputEvent System Fails to Capture Simultaneous MIDI Events
6 participants