Added a new MIDIDriver class

This commit is contained in:
Marcelo Fernandez 2018-07-14 09:11:28 -03:00
parent cfcb6e11f2
commit 7a5f9fc08e
28 changed files with 1029 additions and 4 deletions

View File

@ -221,6 +221,10 @@ String _OS::get_audio_driver_name(int p_driver) const {
return OS::get_singleton()->get_audio_driver_name(p_driver); 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) { void _OS::set_video_mode(const Size2 &p_size, bool p_fullscreen, bool p_resizeable, int p_screen) {
OS::VideoMode vm; 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_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_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_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_screen_count"), &_OS::get_screen_count);
ClassDB::bind_method(D_METHOD("get_current_screen"), &_OS::get_current_screen); ClassDB::bind_method(D_METHOD("get_current_screen"), &_OS::get_current_screen);

View File

@ -152,6 +152,8 @@ public:
virtual int get_audio_driver_count() const; virtual int get_audio_driver_count() const;
virtual String get_audio_driver_name(int p_driver) 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_screen_count() const;
virtual int get_current_screen() const; virtual int get_current_screen() const;
virtual void set_current_screen(int p_screen); virtual void set_current_screen(int p_screen);

View File

@ -89,6 +89,7 @@ VARIANT_ENUM_CAST(KeyList);
VARIANT_ENUM_CAST(KeyModifierMask); VARIANT_ENUM_CAST(KeyModifierMask);
VARIANT_ENUM_CAST(ButtonList); VARIANT_ENUM_CAST(ButtonList);
VARIANT_ENUM_CAST(JoystickList); VARIANT_ENUM_CAST(JoystickList);
VARIANT_ENUM_CAST(MidiMessageList);
void register_global_constants() { 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_L2);
BIND_GLOBAL_ENUM_CONSTANT(JOY_ANALOG_R2); 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 // error list
BIND_GLOBAL_ENUM_CONSTANT(OK); BIND_GLOBAL_ENUM_CONSTANT(OK);

View File

@ -1080,3 +1080,122 @@ InputEventPanGesture::InputEventPanGesture() {
delta = Vector2(0, 0); 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;
}

View File

@ -140,6 +140,16 @@ enum JoystickList {
JOY_ANALOG_R2 = JOY_AXIS_7, 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 * Input Modifier Status
* for keyboard/mouse events. * for keyboard/mouse events.
@ -525,4 +535,50 @@ public:
InputEventPanGesture(); 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 #endif

107
core/os/midi_driver.cpp Normal file
View File

@ -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
core/os/midi_driver.h Normal file
View File

@ -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

View File

@ -33,6 +33,7 @@
#include "dir_access.h" #include "dir_access.h"
#include "input.h" #include "input.h"
#include "os/file_access.h" #include "os/file_access.h"
#include "os/midi_driver.h"
#include "project_settings.h" #include "project_settings.h"
#include "servers/audio_server.h" #include "servers/audio_server.h"
#include "version_generated.gen.h" #include "version_generated.gen.h"
@ -672,6 +673,15 @@ List<String> OS::get_restart_on_exit_arguments() const {
return restart_commandline; 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() { OS::OS() {
void *volatile stack_bottom; void *volatile stack_bottom;

View File

@ -189,6 +189,8 @@ public:
virtual int get_audio_driver_count() const; virtual int get_audio_driver_count() const;
virtual const char *get_audio_driver_name(int p_driver) 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_screen_count() const { return 1; }
virtual int get_current_screen() const { return 0; } virtual int get_current_screen() const { return 0; }
virtual void set_current_screen(int p_screen) {} virtual void set_current_screen(int p_screen) {}

5
drivers/SCsub vendored
View File

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

8
drivers/alsamidi/SCsub Normal file
View File

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

View File

@ -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

View File

@ -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
drivers/coremidi/SCsub Normal file
View File

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

View File

@ -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

View File

@ -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
drivers/winmidi/SCsub Normal file
View File

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

View File

@ -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

View File

@ -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

View File

@ -108,8 +108,8 @@ def configure(env):
## Flags ## Flags
env.Append(CPPPATH=['#platform/osx']) env.Append(CPPPATH=['#platform/osx'])
env.Append(CPPFLAGS=['-DOSX_ENABLED', '-DUNIX_ENABLED', '-DGLES_ENABLED', '-DAPPLE_STYLE_KEYS', '-DCOREAUDIO_ENABLED']) 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', '-lz', '-framework', 'IOKit', '-framework', 'ForceFeedback']) 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(LIBS=['pthread'])
env.Append(CPPFLAGS=['-mmacosx-version-min=10.9']) env.Append(CPPFLAGS=['-mmacosx-version-min=10.9'])

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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