Add support for MiDi output
Work in Progress
Implements https://github.com/godotengine/godot-proposals/issues/2321
Currently the API is very simple, a new method is created OS.send_midi that accepts a InputEventMIDI and sends it to the midi device specified in the event. Output device IDs can be found in OS.get_connected_midi_outputs. To use this API you first need to open MIDI with OS.open_midi_inputs which now opens both inputs and outputs.
I've tested the code on OSX and Windows, but haven't been able to find a linux machine with ALSA set up in a way Godot seems to be able to see the midi devices for, so the ALSA code is just my best guess currently.
TODO
- [ ] Test on Linux
- [ ] Discuss API, especially wrt (https://github.com/godotengine/godot-proposals/issues/8761)
- [ ] Should
open_midi_inputsonly open inputs, andopen_midi_outputsbe added. If so, figure out the best way to refactor the MIDI driveropen(). (Maybe add open_inputs and open_outputs, and open() stays as a deprecated alias of open_inputs() - [ ] Additional testing on OSX and Windows
What is a way of testing this on windows? Do you have a test project?
Here's a small test project. If you push the button, on the default configuration of windows, you should hear a piano sound.
Tested on Linux (NixOS w/ steam-run). The demo project opened fine, but pressing the button crashes the project:
godot.linuxbsd.editor.dev.x86_64: rawmidi.c:1083: snd_rawmidi_write: Assertion rawmidi->stream == SND_RAWMIDI_STREAM_OUTPUT' failed.
It seems the RawMidi struct that you pass to snd_rawmidi_write() needs to have its stream info field set to SND_RAWMIDI_STREAMOUTPUT.
@basicer asked for a more complete stack trace, but if there isn’t one we can try other ways
Sorry I didn't get to this sooner. There wasn't a more complete stack trace, just the one error. It's from an assert statement in the alsalib source code, so nothing actually broke. It's just an implementation issue getting caught by the library.
I'm 99% sure the issue is just that the snd_rawmidi_t that's used for output doesn't have the stream field on its snd_rawmidi_info_t set to output. The snd_rawmidi_write() function in the library source code checks for that on the line the error message refers to, and asserts an error if it's not set to output.
This hasn't been an issue before because the default state when creating a snd_rawmidi_t is input.
Sorry I didn't get to this sooner. There wasn't a more complete stack trace, just the one error. It's from an
assertstatement in the alsalib source code, so nothing actually broke. It's just an implementation issue getting caught by the library.I'm 99% sure the issue is just that the
snd_rawmidi_tthat's used for output doesn't have thestreamfield on itssnd_rawmidi_info_tset to output. Thesnd_rawmidi_write()function in the library source code checks for that on the line the error message refers to, and asserts an error if it's not set to output.This hasn't been an issue before because the default state when creating a
snd_rawmidi_tis input.
I re-read the documentation and fixed a few things. Can you try now?
No more crashes, and the synth I've got hooked up is playing a tone. Looks good!
I am getting some errors when I unplug a device that's in use, but they're not crashing anything. Hot plugging is probably a future PR type of issue though, especially with how niche MIDI functionality is for Godot's userbase.
Texte original en Français en dessous
French to English Google translate: Hello. I don't know yet when or how to test but I'm already equipped. I can also use the MIDI-OX application ( http://www.midiox.com ) if needed. I would like to be able to read MIDI files in games, create MIDI input/output plugins and why not one day create an app to make it easier for me to compose and create MIDI files to then convert to FLAC or MP3 with the Computer Aided Music software that I use for my creations ( https://soundcloud.com/musclor13 ). I don't know Godot very well but the code close to Python and the simplicity for graphical interfaces please me. Have a nice day.
Texte original en Français: Bonjour. Je ne sais pas encore quand ni comment tester mais je suis déjà équipé. Je peux aussi utiliser l'application MIDI-OX ( http://www.midiox.com ) si besoin. J'aimerais pouvoir lire des fichiers MIDI dans des jeux, creer des plugins d'entrée/sortie midi et pourquoi pas un jour creer une app pour me faciliter la composition et la création de fichiers MIDI pour convertir ensuite en flac ou MP3 avec le logiciel de Musique Assisté par Ordinateur que j'utilise pour mes créations ( https://soundcloud.com/musclor13 ). Je ne connais pas super bien Godot mais le code proche de Python et la simplicité pour les interfaces graphiques me plais bien. Bonne journée.
Tested locally (rebased on top of
master6dc78c8), it works as expected on Windows 11 24H2. Code looks good to me.Using FluidSynth and loopMIDI (so that FluidSynth will see an input and create an output accordingly), you can use it to play notes from a soundfont (I've used
gzdoom.sf2that comes with GZDoom for this video):midi.mp4 Note that even after you stop the project, FluidSynth will continue playing sound. Interestingly, this does not happen with Microsoft GS Wavetable Synth though (the first example in the video, which is much quieter). When using the wavetable synth, sound will stop if you stop the project.
(google translate) Hello. I know that Jack and Qsynth have been ported to Windows, just like VMPK or Carla were. I don't know if Godot allows you to create Jack audio and midi inputs and outputs, but even without that, midi out is really something I've been waiting for since I discovered Godot a year ago.
(French original text) Bonjour. Je sais que Jack et Qsynth ont été portés sous windows tout comme l'était déja VMPK ou Carla. Je sais pas si Godot permet de creer des entrés et sorties Jack audio et midi mais même sans ça le midi out est vraiment un truc que j'attendais depuis que j'ai découvert Godot y a un an.
- Discuss API, especially wrt (https://github.com/godotengine/godot-proposals/issues/8761)
In a recent Core meeting, we discussed the proposal and both PRs. We concluded that moving the MidiMessage enum and OS's methods is desirable, but using the new enum would break compatibility just for the sake of tidiness. You probably shouldn't worry about it anytime soon.
I've also tested this PR on macOS 15.4.1 (with a FluidSynth virtual device) and it works as expected too. To test this on your end, install Homebrew, use brew install fluidsynth, grab a soundfont (e.g. from GZDoom's Windows distribution) and run fluidsynth /path/to/soundfont.sf2. You don't need to set up a loopback device on macOS, unlike on Windows.
The GitHub interface shows a merge conflict, but I was able to run git rebase master without seeing any conflicts.
As far as a I can tell the work needed to complete is to update the documentation, and to rebase on godot engine git master.