-
-
Notifications
You must be signed in to change notification settings - Fork 22k
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 Web MIDI support #95928
Add Web MIDI support #95928
Conversation
b80fb00
to
68d736b
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Since there is a permission request involved, this should be documented in doc/classes/InputEventMIDI.xml
's class description. Something like this:
[b]Note:[/b] On the Web platform, using MIDI input requires a browser permission to be granted first. This permission request is performed when calling [method OS.open_midi_inputs]. MIDI input will not work until the user accepts the permission request.
I would copy the same note at the end of OS' open_midi_inputs
description.
doc/classes/OS.xml
should have its open_midi_inputs
and close_midi_inputs
method descriptions modified to mention Web platform support.
PS: It seems OS.get_connected_midi_inputs()
will not work on the Web platform if I'm reading the code right. It might be worth documenting this explicitly in its description as well.
const input_names = inputs.map((input) => input.name); | ||
|
||
const c_ptr = GodotRuntime.allocStringArray(input_names); | ||
set_input_names_cb(input_names.length, c_ptr); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is where the connected_midi_inputs will be populated.
Kind of. |
68d736b
to
cb1a7c0
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The extra note is very good, particularly in line with what I would've written myself.
doc/classes/OS.xml
Outdated
@@ -242,7 +242,8 @@ | |||
<return type="PackedStringArray" /> | |||
<description> | |||
Returns an array of connected MIDI device names, if they exist. Returns an empty array if the system MIDI driver has not previously been initialized with [method open_midi_inputs]. See also [method close_midi_inputs]. | |||
[b]Note:[/b] This method is implemented on Linux, macOS and Windows. | |||
[b]Note:[/b] This method is implemented on Linux, macOS, Web, and Windows. | |||
[b]Note:[/b] On the Web platform, using MIDI input requires a browser permission to be granted first. This permission request is performed when calling [method open_midi_inputs]. MIDI input will not work until the user accepts the permission request. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't feel like it's necessary to note it here, because the prior description already says that you need to call open_midi_inputs()
(whose description contains the details) for this method to work.
If the driver has not been initialized on Web due to lack of permissions, it won't work. The logic feels straightforward.
What isn't straightforward is the asynchronous nature of it, I assume? On Web, are there any chances that the array may "falsely" return empty? If yes, that's worth noting down.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What isn't straightforward is the asynchronous nature of it, I assume?
Yes, exactly.
On Web, are there any chances that the array may "falsely" return empty? If yes, that's worth noting down.
Apart from the async nature, and the permissions request being denied, the other "false" cases would be:
- The midi device must be connected at the time of calling
open_midi_inputs
the first time - since the callbacks are cached after that. I could start listening to the statechange event, and then the array of connected inputs would change over time, but it doesn't look like any of the other MIDI drivers (alsamidi/coremidi/winmidi) do that? For example, every time you receive anInputEventMIDI
, thedevice
would need to be checked against the currentOS.get_connected_midi_inputs()
if you wanted to know which device it came from.- (re: "the first time") As a "workaround", you can "refresh" the inputs by calling
close_midi_inputs()
and thenopen_midi_inputs()
again.
- (re: "the first time") As a "workaround", you can "refresh" the inputs by calling
- Apart from the permissions request being denied, these are the other limitations called out on MDN:
- The method must be called in a secure context.
- But I don't think we need to call this out as being a special requirement for MIDI on web.
- Access may be gated by the midi HTTP Permission Policy.
- Maybe this would be too obscure to note here? The default permissions policy is that
midi
would be allowed, so whoever is hosting the godot web app would have specifically blocked it for you to run into it.
- Maybe this would be too obscure to note here? The default permissions policy is that
- The method must be called in a secure context.
- The user's browser not supporting WebMIDI.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Good work! I have a few comments, but it's on the right track.
doc/classes/OS.xml
Outdated
@@ -242,7 +242,8 @@ | |||
<return type="PackedStringArray" /> | |||
<description> | |||
Returns an array of connected MIDI device names, if they exist. Returns an empty array if the system MIDI driver has not previously been initialized with [method open_midi_inputs]. See also [method close_midi_inputs]. | |||
[b]Note:[/b] This method is implemented on Linux, macOS and Windows. | |||
[b]Note:[/b] This method is implemented on Linux, macOS, Web, and Windows. | |||
[b]Note:[/b] On the Web platform, using MIDI input requires a browser permission to be granted first. This permission request is performed when calling [method open_midi_inputs]. MIDI input will not work until the user accepts the permission request. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
[b]Note:[/b] On the Web platform, using MIDI input requires a browser permission to be granted first. This permission request is performed when calling [method open_midi_inputs]. MIDI input will not work until the user accepts the permission request. | |
[b]Note:[/b] On the Web platform, using MIDI input requires a browser permission to be granted first. This permission request is performed when calling [method open_midi_inputs]. The browser will restrain from processing MIDI input until the user accepts the permission request. |
That's closer to reality. Because if the user declines the permission or takes time to accept, Godot will show no connected devices, but will never know that the permission has been declined.
cb1a7c0
to
a5b7c7e
Compare
a5b7c7e
to
5f79ee9
Compare
Happy to rename any variables, or make any other changes. Just let me know! |
5f79ee9
to
d244d3f
Compare
It's been a while, but I am still interested in getting this merged! |
@ryanbraganza Sorry for the delay! I'm reviewing this PR right now! |
I'm currently investigating why it fails on the |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
So there's few issues, but overall, it's really nice. Once the changes are made, I'll approve and we'll put this in the merge queue.
platform/web/webmidi_driver.h
Outdated
WASM_EXPORT static void set_input_names_callback(int p_size, const char **p_input_names); | ||
static void _set_input_names_callback(const Vector<String> &p_input_names); | ||
|
||
WASM_EXPORT static void on_midi_message(int p_device_index, uint8_t p_status, const uint8_t *p_data, size_t p_data_len); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
As said before, we need to change the signature of on_midi_message()
to remove highly detailed types, and we need to add _on_midi_message()
.
WASM_EXPORT static void on_midi_message(int p_device_index, uint8_t p_status, const uint8_t *p_data, size_t p_data_len); | |
WASM_EXPORT static void on_midi_message(int p_device_index, int p_status, const uint8_t *p_data, int p_data_len); | |
static void _on_midi_message(int p_device_index, int p_status, const PackedByteArray &p_data, int p_data_len); |
|
d244d3f
to
1ee098d
Compare
applied the changes and rebased |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nice work! I have a few suggestions for the docs, but otherwise, all good!
cc. @AThousandShips and @Mickeon for my suggested doc changes. |
Co-authored-by: Adam Scott <ascott.ca@gmail.com> Co-authored-by: A Thousand Ships <96648715+AThousandShips@users.noreply.github.com>
39049f5
to
a7505ee
Compare
Applied all suggested changes, squashed, & rebased |
Thanks! |
This PR adds support for web exports by using webmidi.
Demo: (code)
Note: webmidi is not supported on all browsers