Quellcode durchsuchen

Added a new MIDIDriver class

Marcelo Fernandez vor 7 Jahren
Ursprung
Commit
7a5f9fc08e

+ 5 - 0
core/bind/core_bind.cpp

@@ -221,6 +221,10 @@ String _OS::get_audio_driver_name(int p_driver) const {
 	return OS::get_singleton()->get_audio_driver_name(p_driver);
 }
 
+PoolStringArray _OS::get_connected_midi_inputs() {
+	return OS::get_singleton()->get_connected_midi_inputs();
+}
+
 void _OS::set_video_mode(const Size2 &p_size, bool p_fullscreen, bool p_resizeable, int p_screen) {
 
 	OS::VideoMode vm;
@@ -1058,6 +1062,7 @@ void _OS::_bind_methods() {
 	ClassDB::bind_method(D_METHOD("get_video_driver_name", "driver"), &_OS::get_video_driver_name);
 	ClassDB::bind_method(D_METHOD("get_audio_driver_count"), &_OS::get_audio_driver_count);
 	ClassDB::bind_method(D_METHOD("get_audio_driver_name", "driver"), &_OS::get_audio_driver_name);
+	ClassDB::bind_method(D_METHOD("get_connected_midi_inputs"), &_OS::get_connected_midi_inputs);
 
 	ClassDB::bind_method(D_METHOD("get_screen_count"), &_OS::get_screen_count);
 	ClassDB::bind_method(D_METHOD("get_current_screen"), &_OS::get_current_screen);

+ 2 - 0
core/bind/core_bind.h

@@ -152,6 +152,8 @@ public:
 	virtual int get_audio_driver_count() const;
 	virtual String get_audio_driver_name(int p_driver) const;
 
+	virtual PoolStringArray get_connected_midi_inputs();
+
 	virtual int get_screen_count() const;
 	virtual int get_current_screen() const;
 	virtual void set_current_screen(int p_screen);

+ 10 - 0
core/global_constants.cpp

@@ -89,6 +89,7 @@ VARIANT_ENUM_CAST(KeyList);
 VARIANT_ENUM_CAST(KeyModifierMask);
 VARIANT_ENUM_CAST(ButtonList);
 VARIANT_ENUM_CAST(JoystickList);
+VARIANT_ENUM_CAST(MidiMessageList);
 
 void register_global_constants() {
 
@@ -458,6 +459,15 @@ void register_global_constants() {
 	BIND_GLOBAL_ENUM_CONSTANT(JOY_ANALOG_L2);
 	BIND_GLOBAL_ENUM_CONSTANT(JOY_ANALOG_R2);
 
+	// midi
+	BIND_GLOBAL_ENUM_CONSTANT(MIDI_MESSAGE_NOTE_OFF);
+	BIND_GLOBAL_ENUM_CONSTANT(MIDI_MESSAGE_NOTE_ON);
+	BIND_GLOBAL_ENUM_CONSTANT(MIDI_MESSAGE_AFTERTOUCH);
+	BIND_GLOBAL_ENUM_CONSTANT(MIDI_MESSAGE_CONTROL_CHANGE);
+	BIND_GLOBAL_ENUM_CONSTANT(MIDI_MESSAGE_PROGRAM_CHANGE);
+	BIND_GLOBAL_ENUM_CONSTANT(MIDI_MESSAGE_CHANNEL_PRESSURE);
+	BIND_GLOBAL_ENUM_CONSTANT(MIDI_MESSAGE_PITCH_BEND);
+
 	// error list
 
 	BIND_GLOBAL_ENUM_CONSTANT(OK);

+ 119 - 0
core/os/input_event.cpp

@@ -1080,3 +1080,122 @@ InputEventPanGesture::InputEventPanGesture() {
 
 	delta = Vector2(0, 0);
 }
+/////////////////////////////
+
+void InputEventMIDI::set_channel(const int p_channel) {
+
+	channel = p_channel;
+}
+
+int InputEventMIDI::get_channel() const {
+	return channel;
+}
+
+void InputEventMIDI::set_message(const int p_message) {
+
+	message = p_message;
+}
+
+int InputEventMIDI::get_message() const {
+	return message;
+}
+
+void InputEventMIDI::set_pitch(const int p_pitch) {
+
+	pitch = p_pitch;
+}
+
+int InputEventMIDI::get_pitch() const {
+	return pitch;
+}
+
+void InputEventMIDI::set_velocity(const int p_velocity) {
+
+	velocity = p_velocity;
+}
+
+int InputEventMIDI::get_velocity() const {
+	return velocity;
+}
+
+void InputEventMIDI::set_instrument(const int p_instrument) {
+
+	instrument = p_instrument;
+}
+
+int InputEventMIDI::get_instrument() const {
+	return instrument;
+}
+
+void InputEventMIDI::set_pressure(const int p_pressure) {
+
+	pressure = p_pressure;
+}
+
+int InputEventMIDI::get_pressure() const {
+	return pressure;
+}
+
+void InputEventMIDI::set_controller_number(const int p_controller_number) {
+
+	controller_number = p_controller_number;
+}
+
+int InputEventMIDI::get_controller_number() const {
+	return controller_number;
+}
+
+void InputEventMIDI::set_controller_value(const int p_controller_value) {
+
+	controller_value = p_controller_value;
+}
+
+int InputEventMIDI::get_controller_value() const {
+	return controller_value;
+}
+
+String InputEventMIDI::as_text() const {
+
+	return "InputEventMIDI : channel=(" + itos(get_channel()) + "), message=(" + itos(get_message()) + ")";
+}
+
+void InputEventMIDI::_bind_methods() {
+
+	ClassDB::bind_method(D_METHOD("set_channel", "channel"), &InputEventMIDI::set_channel);
+	ClassDB::bind_method(D_METHOD("get_channel"), &InputEventMIDI::get_channel);
+	ClassDB::bind_method(D_METHOD("set_message", "message"), &InputEventMIDI::set_message);
+	ClassDB::bind_method(D_METHOD("get_message"), &InputEventMIDI::get_message);
+	ClassDB::bind_method(D_METHOD("set_pitch", "pitch"), &InputEventMIDI::set_pitch);
+	ClassDB::bind_method(D_METHOD("get_pitch"), &InputEventMIDI::get_pitch);
+	ClassDB::bind_method(D_METHOD("set_velocity", "velocity"), &InputEventMIDI::set_velocity);
+	ClassDB::bind_method(D_METHOD("get_velocity"), &InputEventMIDI::get_velocity);
+	ClassDB::bind_method(D_METHOD("set_instrument", "instrument"), &InputEventMIDI::set_instrument);
+	ClassDB::bind_method(D_METHOD("get_instrument"), &InputEventMIDI::get_instrument);
+	ClassDB::bind_method(D_METHOD("set_pressure", "pressure"), &InputEventMIDI::set_pressure);
+	ClassDB::bind_method(D_METHOD("get_pressure"), &InputEventMIDI::get_pressure);
+	ClassDB::bind_method(D_METHOD("set_controller_number", "controller_number"), &InputEventMIDI::set_controller_number);
+	ClassDB::bind_method(D_METHOD("get_controller_number"), &InputEventMIDI::get_controller_number);
+	ClassDB::bind_method(D_METHOD("set_controller_value", "controller_value"), &InputEventMIDI::set_controller_value);
+	ClassDB::bind_method(D_METHOD("get_controller_value"), &InputEventMIDI::get_controller_value);
+
+	ADD_PROPERTY(PropertyInfo(Variant::INT, "channel"), "set_channel", "get_channel");
+	ADD_PROPERTY(PropertyInfo(Variant::INT, "message"), "set_message", "get_message");
+	ADD_PROPERTY(PropertyInfo(Variant::INT, "pitch"), "set_pitch", "get_pitch");
+	ADD_PROPERTY(PropertyInfo(Variant::INT, "velocity"), "set_velocity", "get_velocity");
+	ADD_PROPERTY(PropertyInfo(Variant::INT, "instrument"), "set_instrument", "get_instrument");
+	ADD_PROPERTY(PropertyInfo(Variant::INT, "pressure"), "set_pressure", "get_pressure");
+	ADD_PROPERTY(PropertyInfo(Variant::INT, "controller_number"), "set_controller_number", "get_controller_number");
+	ADD_PROPERTY(PropertyInfo(Variant::INT, "controller_value"), "set_controller_value", "get_controller_value");
+}
+
+InputEventMIDI::InputEventMIDI() {
+
+	channel = 0;
+	message = 0;
+	pitch = 0;
+	velocity = 0;
+	instrument = 0;
+	pressure = 0;
+	controller_number = 0;
+	controller_value = 0;
+}

+ 56 - 0
core/os/input_event.h

@@ -140,6 +140,16 @@ enum JoystickList {
 	JOY_ANALOG_R2 = JOY_AXIS_7,
 };
 
+enum MidiMessageList {
+	MIDI_MESSAGE_NOTE_OFF = 0x8,
+	MIDI_MESSAGE_NOTE_ON = 0x9,
+	MIDI_MESSAGE_AFTERTOUCH = 0xA,
+	MIDI_MESSAGE_CONTROL_CHANGE = 0xB,
+	MIDI_MESSAGE_PROGRAM_CHANGE = 0xC,
+	MIDI_MESSAGE_CHANNEL_PRESSURE = 0xD,
+	MIDI_MESSAGE_PITCH_BEND = 0xE,
+};
+
 /**
  * Input Modifier Status
  * for keyboard/mouse events.
@@ -525,4 +535,50 @@ public:
 
 	InputEventPanGesture();
 };
+
+class InputEventMIDI : public InputEvent {
+	GDCLASS(InputEventMIDI, InputEvent)
+
+	int channel;
+	int message;
+	int pitch;
+	int velocity;
+	int instrument;
+	int pressure;
+	int controller_number;
+	int controller_value;
+
+protected:
+	static void _bind_methods();
+
+public:
+	void set_channel(const int p_channel);
+	int get_channel() const;
+
+	void set_message(const int p_message);
+	int get_message() const;
+
+	void set_pitch(const int p_pitch);
+	int get_pitch() const;
+
+	void set_velocity(const int p_velocity);
+	int get_velocity() const;
+
+	void set_instrument(const int p_instrument);
+	int get_instrument() const;
+
+	void set_pressure(const int p_pressure);
+	int get_pressure() const;
+
+	void set_controller_number(const int p_controller_number);
+	int get_controller_number() const;
+
+	void set_controller_value(const int p_controller_value);
+	int get_controller_value() const;
+
+	virtual String as_text() const;
+
+	InputEventMIDI();
+};
+
 #endif

+ 107 - 0
core/os/midi_driver.cpp

@@ -0,0 +1,107 @@
+/*************************************************************************/
+/*  midi_driver.cpp                                                      */
+/*************************************************************************/
+/*                       This file is part of:                           */
+/*                           GODOT ENGINE                                */
+/*                      https://godotengine.org                          */
+/*************************************************************************/
+/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur.                 */
+/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md)    */
+/*                                                                       */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the       */
+/* "Software"), to deal in the Software without restriction, including   */
+/* without limitation the rights to use, copy, modify, merge, publish,   */
+/* distribute, sublicense, and/or sell copies of the Software, and to    */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions:                                             */
+/*                                                                       */
+/* The above copyright notice and this permission notice shall be        */
+/* included in all copies or substantial portions of the Software.       */
+/*                                                                       */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,       */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF    */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY  */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,  */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE     */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.                */
+/*************************************************************************/
+
+#include "midi_driver.h"
+
+#include "main/input_default.h"
+#include "os/os.h"
+
+MIDIDriver *MIDIDriver::singleton = NULL;
+MIDIDriver *MIDIDriver::get_singleton() {
+
+	return singleton;
+}
+
+void MIDIDriver::set_singleton() {
+
+	singleton = this;
+}
+
+void MIDIDriver::receive_input_packet(uint64_t timestamp, uint8_t *data, uint32_t length) {
+
+	Ref<InputEventMIDI> event;
+	event.instance();
+
+	if (length >= 1) {
+		event->set_channel(data[0] & 0xF);
+		event->set_message(data[0] >> 4);
+	}
+
+	switch (event->get_message()) {
+		case MIDI_MESSAGE_AFTERTOUCH:
+			if (length >= 3) {
+				event->set_pitch(data[1]);
+				event->set_pressure(data[2]);
+			}
+			break;
+
+		case MIDI_MESSAGE_CONTROL_CHANGE:
+			if (length >= 3) {
+				event->set_controller_number(data[1]);
+				event->set_controller_value(data[2]);
+			}
+			break;
+
+		case MIDI_MESSAGE_NOTE_ON:
+		case MIDI_MESSAGE_NOTE_OFF:
+		case MIDI_MESSAGE_PITCH_BEND:
+			if (length >= 3) {
+				event->set_pitch(data[1]);
+				event->set_velocity(data[2]);
+			}
+			break;
+
+		case MIDI_MESSAGE_PROGRAM_CHANGE:
+			if (length >= 2) {
+				event->set_instrument(data[1]);
+			}
+			break;
+
+		case MIDI_MESSAGE_CHANNEL_PRESSURE:
+			if (length >= 2) {
+				event->set_pressure(data[1]);
+			}
+			break;
+	}
+
+	InputDefault *id = Object::cast_to<InputDefault>(Input::get_singleton());
+	id->parse_input_event(event);
+}
+
+PoolStringArray MIDIDriver::get_connected_inputs() {
+
+	PoolStringArray list;
+	return list;
+}
+
+MIDIDriver::MIDIDriver() {
+
+	set_singleton();
+}

+ 59 - 0
core/os/midi_driver.h

@@ -0,0 +1,59 @@
+/*************************************************************************/
+/*  midi_driver.h                                                        */
+/*************************************************************************/
+/*                       This file is part of:                           */
+/*                           GODOT ENGINE                                */
+/*                      https://godotengine.org                          */
+/*************************************************************************/
+/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur.                 */
+/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md)    */
+/*                                                                       */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the       */
+/* "Software"), to deal in the Software without restriction, including   */
+/* without limitation the rights to use, copy, modify, merge, publish,   */
+/* distribute, sublicense, and/or sell copies of the Software, and to    */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions:                                             */
+/*                                                                       */
+/* The above copyright notice and this permission notice shall be        */
+/* included in all copies or substantial portions of the Software.       */
+/*                                                                       */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,       */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF    */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY  */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,  */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE     */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.                */
+/*************************************************************************/
+
+#ifndef MIDI_DRIVER_H
+#define MIDI_DRIVER_H
+
+#include "core/variant.h"
+#include "typedefs.h"
+/**
+ * Multi-Platform abstraction for accessing to MIDI.
+ */
+
+class MIDIDriver {
+
+	static MIDIDriver *singleton;
+
+public:
+	static MIDIDriver *get_singleton();
+	void set_singleton();
+
+	virtual Error open() = 0;
+	virtual void close() = 0;
+
+	virtual PoolStringArray get_connected_inputs();
+
+	static void receive_input_packet(uint64_t timestamp, uint8_t *data, uint32_t length);
+
+	MIDIDriver();
+	virtual ~MIDIDriver() {}
+};
+
+#endif

+ 10 - 0
core/os/os.cpp

@@ -33,6 +33,7 @@
 #include "dir_access.h"
 #include "input.h"
 #include "os/file_access.h"
+#include "os/midi_driver.h"
 #include "project_settings.h"
 #include "servers/audio_server.h"
 #include "version_generated.gen.h"
@@ -672,6 +673,15 @@ List<String> OS::get_restart_on_exit_arguments() const {
 	return restart_commandline;
 }
 
+PoolStringArray OS::get_connected_midi_inputs() {
+
+	if (MIDIDriver::get_singleton())
+		return MIDIDriver::get_singleton()->get_connected_inputs();
+
+	PoolStringArray list;
+	return list;
+}
+
 OS::OS() {
 	void *volatile stack_bottom;
 

+ 2 - 0
core/os/os.h

@@ -189,6 +189,8 @@ public:
 	virtual int get_audio_driver_count() const;
 	virtual const char *get_audio_driver_name(int p_driver) const;
 
+	virtual PoolStringArray get_connected_midi_inputs();
+
 	virtual int get_screen_count() const { return 1; }
 	virtual int get_current_screen() const { return 0; }
 	virtual void set_current_screen(int p_screen) {}

+ 5 - 0
drivers/SCsub

@@ -21,6 +21,11 @@ if (env["platform"] == "windows"):
 if env['xaudio2']:
     SConscript("xaudio2/SCsub")
 
+# Midi drivers
+SConscript('alsamidi/SCsub')
+SConscript('coremidi/SCsub')
+SConscript('winmidi/SCsub')
+
 # Graphics drivers
 if (env["platform"] != "server"):
     SConscript('gles3/SCsub')

+ 8 - 0
drivers/alsamidi/SCsub

@@ -0,0 +1,8 @@
+#!/usr/bin/env python
+
+Import('env')
+
+# Driver source files
+env.add_source_files(env.drivers_sources, "*.cpp")
+
+Export('env')

+ 201 - 0
drivers/alsamidi/alsa_midi.cpp

@@ -0,0 +1,201 @@
+/*************************************************************************/
+/*  alsa_midi.cpp                                                        */
+/*************************************************************************/
+/*                       This file is part of:                           */
+/*                           GODOT ENGINE                                */
+/*                      https://godotengine.org                          */
+/*************************************************************************/
+/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur.                 */
+/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md)    */
+/*                                                                       */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the       */
+/* "Software"), to deal in the Software without restriction, including   */
+/* without limitation the rights to use, copy, modify, merge, publish,   */
+/* distribute, sublicense, and/or sell copies of the Software, and to    */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions:                                             */
+/*                                                                       */
+/* The above copyright notice and this permission notice shall be        */
+/* included in all copies or substantial portions of the Software.       */
+/*                                                                       */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,       */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF    */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY  */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,  */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE     */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.                */
+/*************************************************************************/
+
+#ifdef ALSAMIDI_ENABLED
+
+#include <errno.h>
+
+#include "alsa_midi.h"
+#include "os/os.h"
+#include "print_string.h"
+
+static int get_message_size(uint8_t message) {
+	switch (message & 0xF0) {
+		case 0x80: // note off
+		case 0x90: // note on
+		case 0xA0: // aftertouch
+		case 0xB0: // continuous controller
+			return 3;
+
+		case 0xC0: // patch change
+		case 0xD0: // channel pressure
+		case 0xE0: // pitch bend
+			return 2;
+	}
+
+	return 256;
+}
+
+void MIDIDriverALSAMidi::thread_func(void *p_udata) {
+	MIDIDriverALSAMidi *md = (MIDIDriverALSAMidi *)p_udata;
+	uint64_t timestamp = 0;
+	uint8_t buffer[256];
+	int expected_size = 255;
+	int bytes = 0;
+
+	while (!md->exit_thread) {
+		int ret;
+
+		md->lock();
+
+		for (int i = 0; i < md->connected_inputs.size(); i++) {
+			snd_rawmidi_t *midi_in = md->connected_inputs[i];
+			do {
+				uint8_t byte = 0;
+				ret = snd_rawmidi_read(midi_in, &byte, 1);
+				if (ret < 0) {
+					if (ret != -EAGAIN) {
+						ERR_PRINTS("snd_rawmidi_read error: " + String(snd_strerror(ret)));
+					}
+				} else {
+					if (byte & 0x80) {
+						// Flush previous packet if there is any
+						if (bytes) {
+							md->receive_input_packet(timestamp, buffer, bytes);
+							bytes = 0;
+						}
+						expected_size = get_message_size(byte);
+					}
+
+					if (bytes < 256) {
+						buffer[bytes++] = byte;
+						// If we know the size of the current packet receive it if it reached the expected size
+						if (bytes >= expected_size) {
+							md->receive_input_packet(timestamp, buffer, bytes);
+							bytes = 0;
+						}
+					}
+				}
+			} while (ret > 0);
+		}
+
+		md->unlock();
+
+		OS::get_singleton()->delay_usec(1000);
+	}
+}
+
+Error MIDIDriverALSAMidi::open() {
+
+	void **hints;
+
+	if (snd_device_name_hint(-1, "rawmidi", &hints) < 0)
+		return ERR_CANT_OPEN;
+
+	int i = 0;
+	for (void **n = hints; *n != NULL; n++) {
+		char *name = snd_device_name_get_hint(*n, "NAME");
+
+		if (name != NULL) {
+			snd_rawmidi_t *midi_in;
+			int ret = snd_rawmidi_open(&midi_in, NULL, name, SND_RAWMIDI_NONBLOCK);
+			if (ret >= 0) {
+				connected_inputs.insert(i++, midi_in);
+			}
+		}
+
+		if (name != NULL)
+			free(name);
+	}
+	snd_device_name_free_hint(hints);
+
+	mutex = Mutex::create();
+	thread = Thread::create(MIDIDriverALSAMidi::thread_func, this);
+
+	return OK;
+}
+
+void MIDIDriverALSAMidi::close() {
+
+	if (thread) {
+		exit_thread = true;
+		Thread::wait_to_finish(thread);
+
+		memdelete(thread);
+		thread = NULL;
+	}
+
+	if (mutex) {
+		memdelete(mutex);
+		mutex = NULL;
+	}
+
+	for (int i = 0; i < connected_inputs.size(); i++) {
+		snd_rawmidi_t *midi_in = connected_inputs[i];
+		snd_rawmidi_close(midi_in);
+	}
+	connected_inputs.clear();
+}
+
+void MIDIDriverALSAMidi::lock() const {
+
+	if (mutex)
+		mutex->lock();
+}
+
+void MIDIDriverALSAMidi::unlock() const {
+
+	if (mutex)
+		mutex->unlock();
+}
+
+PoolStringArray MIDIDriverALSAMidi::get_connected_inputs() {
+
+	PoolStringArray list;
+
+	lock();
+	for (int i = 0; i < connected_inputs.size(); i++) {
+		snd_rawmidi_t *midi_in = connected_inputs[i];
+		snd_rawmidi_info_t *info;
+
+		snd_rawmidi_info_malloc(&info);
+		snd_rawmidi_info(midi_in, info);
+		list.push_back(snd_rawmidi_info_get_name(info));
+		snd_rawmidi_info_free(info);
+	}
+	unlock();
+
+	return list;
+}
+
+MIDIDriverALSAMidi::MIDIDriverALSAMidi() {
+
+	mutex = NULL;
+	thread = NULL;
+
+	exit_thread = false;
+}
+
+MIDIDriverALSAMidi::~MIDIDriverALSAMidi() {
+
+	close();
+}
+
+#endif

+ 69 - 0
drivers/alsamidi/alsa_midi.h

@@ -0,0 +1,69 @@
+/*************************************************************************/
+/*  alsa_midi.h                                                          */
+/*************************************************************************/
+/*                       This file is part of:                           */
+/*                           GODOT ENGINE                                */
+/*                      https://godotengine.org                          */
+/*************************************************************************/
+/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur.                 */
+/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md)    */
+/*                                                                       */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the       */
+/* "Software"), to deal in the Software without restriction, including   */
+/* without limitation the rights to use, copy, modify, merge, publish,   */
+/* distribute, sublicense, and/or sell copies of the Software, and to    */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions:                                             */
+/*                                                                       */
+/* The above copyright notice and this permission notice shall be        */
+/* included in all copies or substantial portions of the Software.       */
+/*                                                                       */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,       */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF    */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY  */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,  */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE     */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.                */
+/*************************************************************************/
+
+#ifdef ALSAMIDI_ENABLED
+
+#ifndef ALSA_MIDI_H
+#define ALSA_MIDI_H
+
+#include <alsa/asoundlib.h>
+#include <stdio.h>
+
+#include "core/os/mutex.h"
+#include "core/os/thread.h"
+#include "core/vector.h"
+#include "os/midi_driver.h"
+
+class MIDIDriverALSAMidi : public MIDIDriver {
+
+	Thread *thread;
+	Mutex *mutex;
+
+	Vector<snd_rawmidi_t *> connected_inputs;
+
+	bool exit_thread;
+
+	static void thread_func(void *p_udata);
+
+	void lock() const;
+	void unlock() const;
+
+public:
+	virtual Error open();
+	virtual void close();
+
+	virtual PoolStringArray get_connected_inputs();
+
+	MIDIDriverALSAMidi();
+	virtual ~MIDIDriverALSAMidi();
+};
+
+#endif
+#endif

+ 8 - 0
drivers/coremidi/SCsub

@@ -0,0 +1,8 @@
+#!/usr/bin/env python
+
+Import('env')
+
+# Driver source files
+env.add_source_files(env.drivers_sources, "*.cpp")
+
+Export('env')

+ 105 - 0
drivers/coremidi/core_midi.cpp

@@ -0,0 +1,105 @@
+/*************************************************************************/
+/*  core_midi.cpp                                                        */
+/*************************************************************************/
+/*                       This file is part of:                           */
+/*                           GODOT ENGINE                                */
+/*                      https://godotengine.org                          */
+/*************************************************************************/
+/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur.                 */
+/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md)    */
+/*                                                                       */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the       */
+/* "Software"), to deal in the Software without restriction, including   */
+/* without limitation the rights to use, copy, modify, merge, publish,   */
+/* distribute, sublicense, and/or sell copies of the Software, and to    */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions:                                             */
+/*                                                                       */
+/* The above copyright notice and this permission notice shall be        */
+/* included in all copies or substantial portions of the Software.       */
+/*                                                                       */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,       */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF    */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY  */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,  */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE     */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.                */
+/*************************************************************************/
+
+#ifdef COREMIDI_ENABLED
+
+#include "core_midi.h"
+#include "print_string.h"
+
+#include <CoreAudio/HostTime.h>
+#include <CoreServices/CoreServices.h>
+
+void MIDIDriverCoreMidi::read(const MIDIPacketList *packet_list, void *read_proc_ref_con, void *src_conn_ref_con) {
+	MIDIPacket *packet = const_cast<MIDIPacket *>(packet_list->packet);
+	for (int i = 0; i < packet_list->numPackets; i++) {
+		receive_input_packet(packet->timeStamp, packet->data, packet->length);
+		packet = MIDIPacketNext(packet);
+	}
+}
+
+Error MIDIDriverCoreMidi::open() {
+
+	CFStringRef name = CFStringCreateWithCString(NULL, "Godot", kCFStringEncodingASCII);
+	OSStatus result = MIDIClientCreate(name, NULL, NULL, &client);
+	CFRelease(name);
+	if (result != noErr) {
+		ERR_PRINTS("MIDIClientCreate failed: " + String(GetMacOSStatusErrorString(result)));
+		return ERR_CANT_OPEN;
+	}
+
+	result = MIDIInputPortCreate(client, CFSTR("Godot Input"), MIDIDriverCoreMidi::read, (void *)this, &port_in);
+	if (result != noErr) {
+		ERR_PRINTS("MIDIInputPortCreate failed: " + String(GetMacOSStatusErrorString(result)));
+		return ERR_CANT_OPEN;
+	}
+
+	int sources = MIDIGetNumberOfSources();
+	for (int i = 0; i < sources; i++) {
+
+		MIDIEndpointRef source = MIDIGetSource(i);
+		if (source != NULL) {
+			MIDIPortConnectSource(port_in, source, (void *)this);
+			connected_sources.insert(i, source);
+		}
+	}
+
+	return OK;
+}
+
+void MIDIDriverCoreMidi::close() {
+
+	for (int i = 0; i < connected_sources.size(); i++) {
+		MIDIEndpointRef source = connected_sources[i];
+		MIDIPortDisconnectSource(port_in, source);
+	}
+	connected_sources.clear();
+
+	if (port_in != 0) {
+		MIDIPortDispose(port_in);
+		port_in = 0;
+	}
+
+	if (client != 0) {
+		MIDIClientDispose(client);
+		client = 0;
+	}
+}
+
+MIDIDriverCoreMidi::MIDIDriverCoreMidi() {
+
+	client = 0;
+}
+
+MIDIDriverCoreMidi::~MIDIDriverCoreMidi() {
+
+	close();
+}
+
+#endif

+ 61 - 0
drivers/coremidi/core_midi.h

@@ -0,0 +1,61 @@
+/*************************************************************************/
+/*  core_midi.h                                                          */
+/*************************************************************************/
+/*                       This file is part of:                           */
+/*                           GODOT ENGINE                                */
+/*                      https://godotengine.org                          */
+/*************************************************************************/
+/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur.                 */
+/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md)    */
+/*                                                                       */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the       */
+/* "Software"), to deal in the Software without restriction, including   */
+/* without limitation the rights to use, copy, modify, merge, publish,   */
+/* distribute, sublicense, and/or sell copies of the Software, and to    */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions:                                             */
+/*                                                                       */
+/* The above copyright notice and this permission notice shall be        */
+/* included in all copies or substantial portions of the Software.       */
+/*                                                                       */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,       */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF    */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY  */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,  */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE     */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.                */
+/*************************************************************************/
+
+#ifdef COREMIDI_ENABLED
+
+#ifndef CORE_MIDI_H
+#define CORE_MIDI_H
+
+#include <stdio.h>
+
+#include <CoreMIDI/CoreMIDI.h>
+
+#include "core/vector.h"
+#include "os/midi_driver.h"
+
+class MIDIDriverCoreMidi : public MIDIDriver {
+
+	MIDIClientRef client;
+	MIDIPortRef port_in;
+
+	Vector<MIDIEndpointRef> connected_sources;
+
+	static void read(const MIDIPacketList *packet_list, void *read_proc_ref_con, void *src_conn_ref_con);
+
+public:
+	virtual Error open();
+	virtual void close();
+
+	MIDIDriverCoreMidi();
+	virtual ~MIDIDriverCoreMidi();
+};
+
+#endif
+#endif

+ 8 - 0
drivers/winmidi/SCsub

@@ -0,0 +1,8 @@
+#!/usr/bin/env python
+
+Import('env')
+
+# Driver source files
+env.add_source_files(env.drivers_sources, "*.cpp")
+
+Export('env')

+ 100 - 0
drivers/winmidi/win_midi.cpp

@@ -0,0 +1,100 @@
+/*************************************************************************/
+/*  win_midi.cpp                                                         */
+/*************************************************************************/
+/*                       This file is part of:                           */
+/*                           GODOT ENGINE                                */
+/*                      https://godotengine.org                          */
+/*************************************************************************/
+/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur.                 */
+/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md)    */
+/*                                                                       */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the       */
+/* "Software"), to deal in the Software without restriction, including   */
+/* without limitation the rights to use, copy, modify, merge, publish,   */
+/* distribute, sublicense, and/or sell copies of the Software, and to    */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions:                                             */
+/*                                                                       */
+/* The above copyright notice and this permission notice shall be        */
+/* included in all copies or substantial portions of the Software.       */
+/*                                                                       */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,       */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF    */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY  */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,  */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE     */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.                */
+/*************************************************************************/
+
+#ifdef WINMIDI_ENABLED
+
+#include "win_midi.h"
+#include "print_string.h"
+
+void MIDIDriverWinMidi::read(HMIDIIN hMidiIn, UINT wMsg, DWORD_PTR dwInstance, DWORD_PTR dwParam1, DWORD_PTR dwParam2) {
+
+	if (wMsg == MIM_DATA) {
+		receive_input_packet((uint64_t)dwParam2, (uint8_t *)&dwParam1, 3);
+	}
+}
+
+Error MIDIDriverWinMidi::open() {
+
+	for (UINT i = 0; i < midiInGetNumDevs(); i++) {
+		HMIDIIN midi_in;
+
+		MMRESULT res = midiInOpen(&midi_in, i, (DWORD_PTR)read, (DWORD_PTR)this, CALLBACK_FUNCTION);
+		if (res == MMSYSERR_NOERROR) {
+			midiInStart(midi_in);
+			connected_sources.insert(i, midi_in);
+		} else {
+			char err[256];
+			midiInGetErrorText(res, err, 256);
+			ERR_PRINTS("midiInOpen error: " + String(err));
+		}
+	}
+
+	return OK;
+}
+
+PoolStringArray MIDIDriverWinMidi::get_connected_inputs() {
+
+	PoolStringArray list;
+
+	for (int i = 0; i < connected_sources.size(); i++) {
+		HMIDIIN midi_in = connected_sources[i];
+		UINT id = 0;
+		MMRESULT res = midiInGetID(midi_in, &id);
+		if (res == MMSYSERR_NOERROR) {
+			MIDIINCAPS caps;
+			res = midiInGetDevCaps(i, &caps, sizeof(MIDIINCAPS));
+			if (res == MMSYSERR_NOERROR) {
+				list.push_back(caps.szPname);
+			}
+		}
+	}
+
+	return list;
+}
+
+void MIDIDriverWinMidi::close() {
+
+	for (int i = 0; i < connected_sources.size(); i++) {
+		HMIDIIN midi_in = connected_sources[i];
+		midiInStop(midi_in);
+		midiInClose(midi_in);
+	}
+	connected_sources.clear();
+}
+
+MIDIDriverWinMidi::MIDIDriverWinMidi() {
+}
+
+MIDIDriverWinMidi::~MIDIDriverWinMidi() {
+
+	close();
+}
+
+#endif

+ 61 - 0
drivers/winmidi/win_midi.h

@@ -0,0 +1,61 @@
+/*************************************************************************/
+/*  win_midi.h                                                           */
+/*************************************************************************/
+/*                       This file is part of:                           */
+/*                           GODOT ENGINE                                */
+/*                      https://godotengine.org                          */
+/*************************************************************************/
+/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur.                 */
+/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md)    */
+/*                                                                       */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the       */
+/* "Software"), to deal in the Software without restriction, including   */
+/* without limitation the rights to use, copy, modify, merge, publish,   */
+/* distribute, sublicense, and/or sell copies of the Software, and to    */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions:                                             */
+/*                                                                       */
+/* The above copyright notice and this permission notice shall be        */
+/* included in all copies or substantial portions of the Software.       */
+/*                                                                       */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,       */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF    */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY  */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,  */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE     */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.                */
+/*************************************************************************/
+
+#ifdef WINMIDI_ENABLED
+
+#ifndef WIN_MIDI_H
+#define WIN_MIDI_H
+
+#include <stdio.h>
+#include <windows.h>
+
+#include <mmsystem.h>
+
+#include "core/vector.h"
+#include "os/midi_driver.h"
+
+class MIDIDriverWinMidi : public MIDIDriver {
+
+	Vector<HMIDIIN> connected_sources;
+
+	static void CALLBACK read(HMIDIIN hMidiIn, UINT wMsg, DWORD_PTR dwInstance, DWORD_PTR dwParam1, DWORD_PTR dwParam2);
+
+public:
+	virtual Error open();
+	virtual void close();
+
+	virtual PoolStringArray get_connected_inputs();
+
+	MIDIDriverWinMidi();
+	virtual ~MIDIDriverWinMidi();
+};
+
+#endif
+#endif

+ 2 - 2
platform/osx/detect.py

@@ -108,8 +108,8 @@ def configure(env):
     ## Flags
 
     env.Append(CPPPATH=['#platform/osx'])
-    env.Append(CPPFLAGS=['-DOSX_ENABLED', '-DUNIX_ENABLED', '-DGLES_ENABLED', '-DAPPLE_STYLE_KEYS', '-DCOREAUDIO_ENABLED'])
-    env.Append(LINKFLAGS=['-framework', 'Cocoa', '-framework', 'Carbon', '-framework', 'OpenGL', '-framework', 'AGL', '-framework', 'AudioUnit', '-framework', 'CoreAudio', '-lz', '-framework', 'IOKit', '-framework', 'ForceFeedback'])
+    env.Append(CPPFLAGS=['-DOSX_ENABLED', '-DUNIX_ENABLED', '-DGLES_ENABLED', '-DAPPLE_STYLE_KEYS', '-DCOREAUDIO_ENABLED', '-DCOREMIDI_ENABLED'])
+    env.Append(LINKFLAGS=['-framework', 'Cocoa', '-framework', 'Carbon', '-framework', 'OpenGL', '-framework', 'AGL', '-framework', 'AudioUnit', '-framework', 'CoreAudio', '-framework', 'CoreMidi', '-lz', '-framework', 'IOKit', '-framework', 'ForceFeedback'])
     env.Append(LIBS=['pthread'])
 
     env.Append(CPPFLAGS=['-mmacosx-version-min=10.9'])

+ 2 - 0
platform/osx/os_osx.h

@@ -33,6 +33,7 @@
 
 #include "crash_handler_osx.h"
 #include "drivers/coreaudio/audio_driver_coreaudio.h"
+#include "drivers/coremidi/core_midi.h"
 #include "drivers/unix/os_unix.h"
 #include "joypad_osx.h"
 #include "main/input_default.h"
@@ -74,6 +75,7 @@ public:
 	IP_Unix *ip_unix;
 
 	AudioDriverCoreAudio audio_driver;
+	MIDIDriverCoreMidi midi_driver;
 
 	InputDefault *input;
 	JoypadOSX *joypad_osx;

+ 2 - 0
platform/osx/os_osx.mm

@@ -1351,6 +1351,8 @@ Error OS_OSX::initialize(const VideoMode &p_desired, int p_video_driver, int p_a
 
 	AudioDriverManager::initialize(p_audio_driver);
 
+	midi_driver.open();
+
 	input = memnew(InputDefault);
 	joypad_osx = memnew(JoypadOSX);
 

+ 2 - 1
platform/windows/detect.py

@@ -200,7 +200,8 @@ def configure_msvc(env, manual_msvc_config):
 
     env.AppendUnique(CPPDEFINES = ['WINDOWS_ENABLED', 'OPENGL_ENABLED',
                                    'RTAUDIO_ENABLED', 'WASAPI_ENABLED',
-                                   'TYPED_METHOD_BIND', 'WIN32', 'MSVC',
+                                   'WINMIDI_ENABLED', 'TYPED_METHOD_BIND',
+                                   'WIN32', 'MSVC',
                                    {'WINVER' : '$target_win_version',
                                     '_WIN32_WINNT': '$target_win_version'}])
     if env["bits"] == "64":

+ 8 - 0
platform/windows/os_windows.cpp

@@ -1219,6 +1219,10 @@ Error OS_Windows::initialize(const VideoMode &p_desired, int p_video_driver, int
 
 	AudioDriverManager::initialize(p_audio_driver);
 
+#ifdef WINMIDI_ENABLED
+	driver_midi.open();
+#endif
+
 	TRACKMOUSEEVENT tme;
 	tme.cbSize = sizeof(TRACKMOUSEEVENT);
 	tme.dwFlags = TME_LEAVE;
@@ -1347,6 +1351,10 @@ void OS_Windows::set_main_loop(MainLoop *p_main_loop) {
 
 void OS_Windows::finalize() {
 
+#ifdef WINMIDI_ENABLED
+	driver_midi.close();
+#endif
+
 	if (main_loop)
 		memdelete(main_loop);
 

+ 4 - 0
platform/windows/os_windows.h

@@ -35,6 +35,7 @@
 #include "crash_handler_win.h"
 #include "drivers/rtaudio/audio_driver_rtaudio.h"
 #include "drivers/wasapi/audio_driver_wasapi.h"
+#include "drivers/winmidi/win_midi.h"
 #include "os/input.h"
 #include "os/os.h"
 #include "power_windows.h"
@@ -144,6 +145,9 @@ class OS_Windows : public OS {
 #ifdef XAUDIO2_ENABLED
 	AudioDriverXAudio2 driver_xaudio2;
 #endif
+#ifdef WINMIDI_ENABLED
+	MIDIDriverWinMidi driver_midi;
+#endif
 
 	CrashHandler crash_handler;
 

+ 1 - 1
platform/x11/detect.py

@@ -240,7 +240,7 @@ def configure(env):
 
     if (os.system("pkg-config --exists alsa") == 0): # 0 means found
         print("Enabling ALSA")
-        env.Append(CPPFLAGS=["-DALSA_ENABLED"])
+        env.Append(CPPFLAGS=["-DALSA_ENABLED", "-DALSAMIDI_ENABLED"])
         env.ParseConfig('pkg-config alsa --cflags --libs')
     else:
         print("ALSA libraries not found, disabling driver")

+ 7 - 0
platform/x11/os_x11.cpp

@@ -342,6 +342,10 @@ Error OS_X11::initialize(const VideoMode &p_desired, int p_video_driver, int p_a
 
 	AudioDriverManager::initialize(p_audio_driver);
 
+#ifdef ALSAMIDI_ENABLED
+	driver_alsamidi.open();
+#endif
+
 	ERR_FAIL_COND_V(!visual_server, ERR_UNAVAILABLE);
 	ERR_FAIL_COND_V(x11_window == 0, ERR_UNAVAILABLE);
 
@@ -606,6 +610,9 @@ void OS_X11::finalize() {
 		memdelete(debugger_connection_console);
 	}
 	*/
+#ifdef ALSAMIDI_ENABLED
+	driver_alsamidi.close();
+#endif
 
 #ifdef JOYDEV_ENABLED
 	memdelete(joypad);

+ 5 - 0
platform/x11/os_x11.h

@@ -38,6 +38,7 @@
 #include "servers/visual_server.h"
 //#include "servers/visual/visual_server_wrap_mt.h"
 #include "drivers/alsa/audio_driver_alsa.h"
+#include "drivers/alsamidi/alsa_midi.h"
 #include "drivers/pulseaudio/audio_driver_pulseaudio.h"
 #include "joypad_linux.h"
 #include "main/input_default.h"
@@ -168,6 +169,10 @@ class OS_X11 : public OS_Unix {
 	AudioDriverALSA driver_alsa;
 #endif
 
+#ifdef ALSAMIDI_ENABLED
+	MIDIDriverALSAMidi driver_alsamidi;
+#endif
+
 #ifdef PULSEAUDIO_ENABLED
 	AudioDriverPulseAudio driver_pulseaudio;
 #endif