diff options
28 files changed, 1029 insertions, 4 deletions
diff --git a/core/bind/core_bind.cpp b/core/bind/core_bind.cpp index 7a14e85f20..af1d49ae8c 100644 --- a/core/bind/core_bind.cpp +++ b/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); diff --git a/core/bind/core_bind.h b/core/bind/core_bind.h index 48b7b74005..1729c23779 100644 --- a/core/bind/core_bind.h +++ b/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); diff --git a/core/global_constants.cpp b/core/global_constants.cpp index 5b4dd05dbf..187813f9d0 100644 --- a/core/global_constants.cpp +++ b/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); diff --git a/core/os/input_event.cpp b/core/os/input_event.cpp index ca6446d015..e94ccb4f48 100644 --- a/core/os/input_event.cpp +++ b/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; +} diff --git a/core/os/input_event.h b/core/os/input_event.h index bd1a85ce29..04126fee77 100644 --- a/core/os/input_event.h +++ b/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 diff --git a/core/os/midi_driver.cpp b/core/os/midi_driver.cpp new file mode 100644 index 0000000000..7b4f84473c --- /dev/null +++ b/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(); +} diff --git a/core/os/midi_driver.h b/core/os/midi_driver.h new file mode 100644 index 0000000000..1a3a67a411 --- /dev/null +++ b/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 diff --git a/core/os/os.cpp b/core/os/os.cpp index 89866e4044..8dcf0990fc 100644 --- a/core/os/os.cpp +++ b/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; diff --git a/core/os/os.h b/core/os/os.h index 4e0cb003fb..dd783408e8 100644 --- a/core/os/os.h +++ b/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) {} diff --git a/drivers/SCsub b/drivers/SCsub index 2c5e9434e8..f9cfa3fb05 100644 --- a/drivers/SCsub +++ b/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') diff --git a/drivers/alsamidi/SCsub b/drivers/alsamidi/SCsub new file mode 100644 index 0000000000..233593b0f9 --- /dev/null +++ b/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') diff --git a/drivers/alsamidi/alsa_midi.cpp b/drivers/alsamidi/alsa_midi.cpp new file mode 100644 index 0000000000..599470d7e0 --- /dev/null +++ b/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 diff --git a/drivers/alsamidi/alsa_midi.h b/drivers/alsamidi/alsa_midi.h new file mode 100644 index 0000000000..90e458a365 --- /dev/null +++ b/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 diff --git a/drivers/coremidi/SCsub b/drivers/coremidi/SCsub new file mode 100644 index 0000000000..233593b0f9 --- /dev/null +++ b/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') diff --git a/drivers/coremidi/core_midi.cpp b/drivers/coremidi/core_midi.cpp new file mode 100644 index 0000000000..3619be4a8e --- /dev/null +++ b/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 diff --git a/drivers/coremidi/core_midi.h b/drivers/coremidi/core_midi.h new file mode 100644 index 0000000000..fd35e12f4b --- /dev/null +++ b/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 diff --git a/drivers/winmidi/SCsub b/drivers/winmidi/SCsub new file mode 100644 index 0000000000..233593b0f9 --- /dev/null +++ b/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') diff --git a/drivers/winmidi/win_midi.cpp b/drivers/winmidi/win_midi.cpp new file mode 100644 index 0000000000..6da6e31b2b --- /dev/null +++ b/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 diff --git a/drivers/winmidi/win_midi.h b/drivers/winmidi/win_midi.h new file mode 100644 index 0000000000..1cf9b19b5d --- /dev/null +++ b/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 diff --git a/platform/osx/detect.py b/platform/osx/detect.py index 1cc5c619fe..af96659239 100644 --- a/platform/osx/detect.py +++ b/platform/osx/detect.py @@ -115,8 +115,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']) diff --git a/platform/osx/os_osx.h b/platform/osx/os_osx.h index 3d14a6d4fb..686e3f8c90 100644 --- a/platform/osx/os_osx.h +++ b/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; diff --git a/platform/osx/os_osx.mm b/platform/osx/os_osx.mm index 282f5e2d1b..e77f8b3173 100644 --- a/platform/osx/os_osx.mm +++ b/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); diff --git a/platform/windows/detect.py b/platform/windows/detect.py index cacb573236..34fc3e09b5 100644 --- a/platform/windows/detect.py +++ b/platform/windows/detect.py @@ -206,7 +206,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": diff --git a/platform/windows/os_windows.cpp b/platform/windows/os_windows.cpp index ac37e1246d..e083fd7323 100644 --- a/platform/windows/os_windows.cpp +++ b/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); diff --git a/platform/windows/os_windows.h b/platform/windows/os_windows.h index b99d3e3422..69c7d851b8 100644 --- a/platform/windows/os_windows.h +++ b/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; diff --git a/platform/x11/detect.py b/platform/x11/detect.py index feaa2e598f..6a7a426804 100644 --- a/platform/x11/detect.py +++ b/platform/x11/detect.py @@ -249,7 +249,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") diff --git a/platform/x11/os_x11.cpp b/platform/x11/os_x11.cpp index af0e02173f..260ce57732 100644 --- a/platform/x11/os_x11.cpp +++ b/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); diff --git a/platform/x11/os_x11.h b/platform/x11/os_x11.h index b67bc824be..44455a2d8d 100644 --- a/platform/x11/os_x11.h +++ b/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 |