reflexion 0.3.5 → 0.3.7
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/.doc/ext/reflex/application.cpp +16 -0
- data/.doc/ext/reflex/device.cpp +46 -3
- data/.doc/ext/reflex/device_event.cpp +62 -0
- data/.doc/ext/reflex/key_event.cpp +49 -19
- data/.doc/ext/reflex/midi.cpp +82 -0
- data/.doc/ext/reflex/native.cpp +10 -4
- data/.doc/ext/reflex/note_event.cpp +121 -0
- data/.doc/ext/reflex/reflex.cpp +30 -8
- data/.doc/ext/reflex/view.cpp +11 -16
- data/.doc/ext/reflex/window.cpp +24 -0
- data/ChangeLog.md +16 -0
- data/Rakefile +7 -0
- data/VERSION +1 -1
- data/ext/reflex/application.cpp +18 -0
- data/ext/reflex/device.cpp +48 -3
- data/ext/reflex/device_event.cpp +65 -0
- data/ext/reflex/key_event.cpp +49 -19
- data/ext/reflex/midi.cpp +87 -0
- data/ext/reflex/native.cpp +10 -4
- data/ext/reflex/note_event.cpp +130 -0
- data/ext/reflex/reflex.cpp +31 -8
- data/ext/reflex/view.cpp +11 -16
- data/ext/reflex/window.cpp +27 -0
- data/include/reflex/application.h +4 -0
- data/include/reflex/defs.h +58 -21
- data/include/reflex/device.h +22 -0
- data/include/reflex/event.h +64 -2
- data/include/reflex/gamepad.h +175 -0
- data/include/reflex/midi.h +53 -0
- data/include/reflex/reflex.h +2 -0
- data/include/reflex/ruby/application.h +18 -0
- data/include/reflex/ruby/device.h +40 -0
- data/include/reflex/ruby/event.h +22 -0
- data/include/reflex/ruby/midi.h +84 -0
- data/include/reflex/ruby/window.h +27 -0
- data/include/reflex/view.h +9 -1
- data/include/reflex/window.h +6 -0
- data/lib/reflex/note_event.rb +34 -0
- data/lib/reflex/view.rb +2 -1
- data/lib/reflex.rb +9 -8
- data/reflex.gemspec +3 -3
- data/src/application.cpp +70 -0
- data/src/application.h +9 -0
- data/src/device.cpp +24 -0
- data/src/event.cpp +133 -7
- data/src/event.h +5 -0
- data/src/gamepad.cpp +176 -0
- data/src/gamepad.h +74 -0
- data/src/ios/app_delegate.mm +2 -2
- data/src/ios/application.mm +0 -25
- data/src/ios/event.h +0 -5
- data/src/ios/event.mm +11 -87
- data/src/ios/gamepad.mm +314 -0
- data/src/ios/reflex.mm +0 -5
- data/src/midi.cpp +379 -0
- data/src/midi.h +32 -0
- data/src/osx/app_delegate.mm +2 -2
- data/src/osx/application.mm +0 -25
- data/src/osx/event.h +0 -5
- data/src/osx/event.mm +9 -86
- data/src/osx/gamepad.mm +40 -0
- data/src/osx/gamepad_gc.mm +299 -0
- data/src/osx/gamepad_hid.mm +567 -0
- data/src/osx/reflex.mm +0 -5
- data/src/queue.h +71 -0
- data/src/reflex.cpp +18 -0
- data/src/timer.cpp +3 -10
- data/src/view.cpp +39 -0
- data/src/view.h +2 -0
- data/src/win32/application.cpp +5 -26
- data/src/win32/event.cpp +6 -89
- data/src/win32/event.h +1 -1
- data/src/win32/gamepad.cpp +110 -0
- data/src/win32/gamepad.h +20 -0
- data/src/win32/window.cpp +2 -1
- data/src/window.cpp +61 -10
- data/src/window.h +2 -0
- data/test/test_capture_event.rb +20 -16
- data/test/test_key_event.rb +8 -1
- data/test/test_note_event.rb +43 -0
- data/test/test_view.rb +24 -12
- metadata +43 -14
data/src/ios/gamepad.mm
ADDED
@@ -0,0 +1,314 @@
|
|
1
|
+
// -*- c++ -*-
|
2
|
+
#include "../gamepad.h"
|
3
|
+
|
4
|
+
|
5
|
+
#import <GameController/GameController.h>
|
6
|
+
#include "reflex/exception.h"
|
7
|
+
#include "../event.h"
|
8
|
+
#include "window.h"
|
9
|
+
|
10
|
+
|
11
|
+
namespace Reflex
|
12
|
+
{
|
13
|
+
|
14
|
+
|
15
|
+
struct GameControllerGamepadData : Gamepad::Data
|
16
|
+
{
|
17
|
+
|
18
|
+
typedef Gamepad::Data Super;
|
19
|
+
|
20
|
+
GCController* controller = nil;
|
21
|
+
|
22
|
+
GameControllerGamepadData (GCController* controller)
|
23
|
+
: controller([controller retain])
|
24
|
+
{
|
25
|
+
prev.reset(new Gamepad());
|
26
|
+
}
|
27
|
+
|
28
|
+
~GameControllerGamepadData ()
|
29
|
+
{
|
30
|
+
//clear_event_handlers(controller);
|
31
|
+
[controller release];
|
32
|
+
}
|
33
|
+
|
34
|
+
const char* name () const override
|
35
|
+
{
|
36
|
+
return controller.vendorName.UTF8String;
|
37
|
+
}
|
38
|
+
|
39
|
+
bool is_valid () const override
|
40
|
+
{
|
41
|
+
return Super::is_valid() && controller;
|
42
|
+
}
|
43
|
+
|
44
|
+
bool has_handle (void* handle) const override
|
45
|
+
{
|
46
|
+
return handle == (__bridge void*) controller;
|
47
|
+
}
|
48
|
+
|
49
|
+
};// GameControllerGamepadData
|
50
|
+
|
51
|
+
|
52
|
+
static Gamepad*
|
53
|
+
Gamepad_create (GCController* controller)
|
54
|
+
{
|
55
|
+
Gamepad* g = Gamepad_create();
|
56
|
+
g->self.reset(new GameControllerGamepadData(controller));
|
57
|
+
return g;
|
58
|
+
}
|
59
|
+
|
60
|
+
static void
|
61
|
+
call_gamepad_event (int key_code, bool pressed)
|
62
|
+
{
|
63
|
+
Window* win = Window_get_active();
|
64
|
+
if (!win) return;
|
65
|
+
|
66
|
+
auto action = pressed ? KeyEvent::DOWN : KeyEvent::UP;
|
67
|
+
KeyEvent e(action, NULL, key_code, KeyEvent_get_modifiers(), 0);
|
68
|
+
Window_call_key_event(win, &e);
|
69
|
+
}
|
70
|
+
|
71
|
+
static void
|
72
|
+
call_button_event (
|
73
|
+
Gamepad* gamepad, ulonglong button, int key_code, float value)
|
74
|
+
{
|
75
|
+
Gamepad::Data* self = gamepad->self.get();
|
76
|
+
|
77
|
+
bool pressed = value > Gamepad_get_button_press_threshold();
|
78
|
+
bool current = self->state.buttons & button;
|
79
|
+
if (pressed == current) return;
|
80
|
+
|
81
|
+
self->update_prev();
|
82
|
+
if (pressed)
|
83
|
+
self->state.buttons |= button;
|
84
|
+
else
|
85
|
+
self->state.buttons &= ~button;
|
86
|
+
|
87
|
+
call_gamepad_event(key_code, pressed);
|
88
|
+
}
|
89
|
+
|
90
|
+
static void
|
91
|
+
handle_button_event (
|
92
|
+
Gamepad* gamepad, GCControllerButtonInput* input,
|
93
|
+
ulonglong button, int key_code)
|
94
|
+
{
|
95
|
+
[input setPressedChangedHandler:
|
96
|
+
^(GCControllerButtonInput*, float, BOOL pressed)
|
97
|
+
{
|
98
|
+
call_button_event(gamepad, button, key_code, pressed ? 1 : 0);
|
99
|
+
}];
|
100
|
+
}
|
101
|
+
|
102
|
+
static void
|
103
|
+
handle_stick_dpad_event (
|
104
|
+
Gamepad* gamepad, GCControllerButtonInput* input,
|
105
|
+
ulonglong button, int key_code)
|
106
|
+
{
|
107
|
+
[input setValueChangedHandler:
|
108
|
+
^(GCControllerButtonInput*, float value, BOOL)
|
109
|
+
{
|
110
|
+
call_button_event(gamepad, button, key_code, value);
|
111
|
+
}];
|
112
|
+
}
|
113
|
+
|
114
|
+
static void
|
115
|
+
handle_stick_event (
|
116
|
+
Gamepad* gamepad, GCControllerDirectionPad* input, Gamepad::Index index)
|
117
|
+
{
|
118
|
+
[input setValueChangedHandler:
|
119
|
+
^(GCControllerDirectionPad*, float x, float y)
|
120
|
+
{
|
121
|
+
gamepad->self->update_prev();
|
122
|
+
gamepad->self->state.sticks[index].reset(x, y);
|
123
|
+
}];
|
124
|
+
}
|
125
|
+
|
126
|
+
static void
|
127
|
+
handle_trigger_event (
|
128
|
+
Gamepad* gamepad, GCControllerButtonInput* input, Gamepad::Index index,
|
129
|
+
ulonglong button, int key_code)
|
130
|
+
{
|
131
|
+
[input setPressedChangedHandler:
|
132
|
+
^(GCControllerButtonInput*, float, BOOL pressed)
|
133
|
+
{
|
134
|
+
call_button_event(gamepad, button, key_code, pressed ? 1 : 0);
|
135
|
+
}];
|
136
|
+
|
137
|
+
[input setValueChangedHandler:
|
138
|
+
^(GCControllerButtonInput*, float value, BOOL)
|
139
|
+
{
|
140
|
+
Gamepad::Data* self = gamepad->self.get();
|
141
|
+
|
142
|
+
self->update_prev();
|
143
|
+
self->state.triggers[index] = value;
|
144
|
+
}];
|
145
|
+
}
|
146
|
+
|
147
|
+
static void
|
148
|
+
handle_gamepad_events (Gamepad* gamepad, GCController* controller)
|
149
|
+
{
|
150
|
+
GCExtendedGamepad* g = controller.extendedGamepad;
|
151
|
+
if (!gamepad) return;
|
152
|
+
|
153
|
+
static const Gamepad::Index L = Gamepad::INDEX_LEFT, R = Gamepad::INDEX_RIGHT;
|
154
|
+
|
155
|
+
auto* dpad = g.dpad;
|
156
|
+
handle_button_event(gamepad, dpad.left, Gamepad::LEFT, KEY_GAMEPAD_LEFT);
|
157
|
+
handle_button_event(gamepad, dpad.right, Gamepad::RIGHT, KEY_GAMEPAD_RIGHT);
|
158
|
+
handle_button_event(gamepad, dpad.up, Gamepad::UP, KEY_GAMEPAD_UP);
|
159
|
+
handle_button_event(gamepad, dpad.down, Gamepad::DOWN, KEY_GAMEPAD_DOWN);
|
160
|
+
|
161
|
+
auto* lstick = g.leftThumbstick;
|
162
|
+
handle_stick_event( gamepad, lstick, L);
|
163
|
+
handle_stick_dpad_event(gamepad, lstick.left, Gamepad::LSTICK_LEFT, KEY_GAMEPAD_LSTICK_LEFT);
|
164
|
+
handle_stick_dpad_event(gamepad, lstick.right, Gamepad::LSTICK_RIGHT, KEY_GAMEPAD_LSTICK_RIGHT);
|
165
|
+
handle_stick_dpad_event(gamepad, lstick.up, Gamepad::LSTICK_UP, KEY_GAMEPAD_LSTICK_UP);
|
166
|
+
handle_stick_dpad_event(gamepad, lstick.down, Gamepad::LSTICK_DOWN, KEY_GAMEPAD_LSTICK_DOWN);
|
167
|
+
|
168
|
+
auto* rstick = g.rightThumbstick;
|
169
|
+
handle_stick_event( gamepad, rstick, R);
|
170
|
+
handle_stick_dpad_event(gamepad, rstick.left, Gamepad::RSTICK_LEFT, KEY_GAMEPAD_RSTICK_LEFT);
|
171
|
+
handle_stick_dpad_event(gamepad, rstick.right, Gamepad::RSTICK_RIGHT, KEY_GAMEPAD_RSTICK_RIGHT);
|
172
|
+
handle_stick_dpad_event(gamepad, rstick.up, Gamepad::RSTICK_UP, KEY_GAMEPAD_RSTICK_UP);
|
173
|
+
handle_stick_dpad_event(gamepad, rstick.down, Gamepad::RSTICK_DOWN, KEY_GAMEPAD_RSTICK_DOWN);
|
174
|
+
|
175
|
+
handle_button_event(gamepad, g.buttonA, Gamepad::BUTTON_A, KEY_GAMEPAD_A);
|
176
|
+
handle_button_event(gamepad, g.buttonB, Gamepad::BUTTON_B, KEY_GAMEPAD_B);
|
177
|
+
handle_button_event(gamepad, g.buttonX, Gamepad::BUTTON_X, KEY_GAMEPAD_X);
|
178
|
+
handle_button_event(gamepad, g.buttonY, Gamepad::BUTTON_Y, KEY_GAMEPAD_Y);
|
179
|
+
|
180
|
+
handle_button_event( gamepad, g. leftShoulder, Gamepad::LSHOULDER, KEY_GAMEPAD_LSHOULDER);
|
181
|
+
handle_button_event( gamepad, g.rightShoulder, Gamepad::RSHOULDER, KEY_GAMEPAD_RSHOULDER);
|
182
|
+
handle_trigger_event(gamepad, g. leftTrigger, L, Gamepad::LTRIGGER, KEY_GAMEPAD_LTRIGGER);
|
183
|
+
handle_trigger_event(gamepad, g.rightTrigger, R, Gamepad::RTRIGGER, KEY_GAMEPAD_RTRIGGER);
|
184
|
+
|
185
|
+
if (@available(iOS 12.1, *))
|
186
|
+
{
|
187
|
+
handle_button_event(gamepad, g. leftThumbstickButton, Gamepad::LTHUMB, KEY_GAMEPAD_LTHUMB);
|
188
|
+
handle_button_event(gamepad, g.rightThumbstickButton, Gamepad::RTHUMB, KEY_GAMEPAD_RTHUMB);
|
189
|
+
}
|
190
|
+
|
191
|
+
if (@available(iOS 13.0, *))
|
192
|
+
{
|
193
|
+
handle_button_event(gamepad, g.buttonMenu, Gamepad::MENU, KEY_GAMEPAD_MENU);
|
194
|
+
handle_button_event(gamepad, g.buttonOptions, Gamepad::OPTION, KEY_GAMEPAD_OPTION);
|
195
|
+
}
|
196
|
+
|
197
|
+
if (@available(iOS 14.0, *))
|
198
|
+
handle_button_event(gamepad, g.buttonHome, Gamepad::HOME, KEY_GAMEPAD_HOME);
|
199
|
+
|
200
|
+
//if (@available(macOS 11.0, *))
|
201
|
+
{
|
202
|
+
if ([g isKindOfClass: GCDualShockGamepad.class])
|
203
|
+
{
|
204
|
+
GCDualShockGamepad* dualshock = (GCDualShockGamepad*) g;
|
205
|
+
handle_button_event(
|
206
|
+
gamepad, dualshock.touchpadButton,
|
207
|
+
Gamepad::BUTTON_TOUCH, KEY_GAMEPAD_BUTTON_TOUCH);
|
208
|
+
}
|
209
|
+
}
|
210
|
+
|
211
|
+
//if (@available(macOS 11.3, *))
|
212
|
+
{
|
213
|
+
if ([g isKindOfClass: GCDualSenseGamepad.class])
|
214
|
+
{
|
215
|
+
GCDualSenseGamepad* dualsense = (GCDualSenseGamepad*) g;
|
216
|
+
handle_button_event(
|
217
|
+
gamepad, dualsense.touchpadButton,
|
218
|
+
Gamepad::BUTTON_TOUCH, KEY_GAMEPAD_BUTTON_TOUCH);
|
219
|
+
}
|
220
|
+
}
|
221
|
+
|
222
|
+
//if (@available(macOS 11.0, *))
|
223
|
+
{
|
224
|
+
if ([g isKindOfClass: GCXboxGamepad.class])
|
225
|
+
{
|
226
|
+
GCXboxGamepad* xbox = (GCXboxGamepad*) g;
|
227
|
+
handle_button_event(
|
228
|
+
gamepad, xbox.paddleButton1, Gamepad::RPADDLE_0, KEY_GAMEPAD_RPADDLE_0);
|
229
|
+
handle_button_event(
|
230
|
+
gamepad, xbox.paddleButton2, Gamepad::LPADDLE_0, KEY_GAMEPAD_LPADDLE_0);
|
231
|
+
handle_button_event(
|
232
|
+
gamepad, xbox.paddleButton3, Gamepad::RPADDLE_1, KEY_GAMEPAD_RPADDLE_1);
|
233
|
+
handle_button_event(
|
234
|
+
gamepad, xbox.paddleButton4, Gamepad::LPADDLE_1, KEY_GAMEPAD_LPADDLE_1);
|
235
|
+
}
|
236
|
+
}
|
237
|
+
}
|
238
|
+
|
239
|
+
static void
|
240
|
+
add_gamepad (Application* app, GCController* controller)
|
241
|
+
{
|
242
|
+
Gamepad* gamepad = Gamepad_create(controller);
|
243
|
+
handle_gamepad_events(gamepad, controller);
|
244
|
+
|
245
|
+
Gamepad_add(app, gamepad);
|
246
|
+
}
|
247
|
+
|
248
|
+
static void
|
249
|
+
remove_gamepad (Application* app, GCController* controller)
|
250
|
+
{
|
251
|
+
Gamepad* gamepad = Gamepad_find((__bridge void*) controller);
|
252
|
+
if (!gamepad) return;
|
253
|
+
|
254
|
+
Gamepad_remove(app, gamepad);
|
255
|
+
}
|
256
|
+
|
257
|
+
static id connect_observer = nil;
|
258
|
+
|
259
|
+
static id disconnect_observer = nil;
|
260
|
+
|
261
|
+
void
|
262
|
+
init_gamepad (Application* app)
|
263
|
+
{
|
264
|
+
if (connect_observer || disconnect_observer)
|
265
|
+
invalid_state_error(__FILE__, __LINE__);
|
266
|
+
|
267
|
+
connect_observer = [NSNotificationCenter.defaultCenter
|
268
|
+
addObserverForName: GCControllerDidConnectNotification
|
269
|
+
object: nil
|
270
|
+
queue: NSOperationQueue.mainQueue
|
271
|
+
usingBlock: ^(NSNotification* n) {add_gamepad(app, n.object);}];
|
272
|
+
|
273
|
+
disconnect_observer = [NSNotificationCenter.defaultCenter
|
274
|
+
addObserverForName: GCControllerDidDisconnectNotification
|
275
|
+
object: nil
|
276
|
+
queue: NSOperationQueue.mainQueue
|
277
|
+
usingBlock: ^(NSNotification* n) {remove_gamepad(app, n.object);}];
|
278
|
+
|
279
|
+
for (GCController* c in GCController.controllers)
|
280
|
+
add_gamepad(app, c);
|
281
|
+
}
|
282
|
+
|
283
|
+
void
|
284
|
+
fin_gamepad (Application* app)
|
285
|
+
{
|
286
|
+
if (!connect_observer || !disconnect_observer)
|
287
|
+
invalid_state_error(__FILE__, __LINE__);
|
288
|
+
|
289
|
+
[NSNotificationCenter.defaultCenter
|
290
|
+
removeObserver: connect_observer];
|
291
|
+
[NSNotificationCenter.defaultCenter
|
292
|
+
removeObserver: disconnect_observer];
|
293
|
+
|
294
|
+
connect_observer = disconnect_observer = nil;
|
295
|
+
|
296
|
+
Gamepad_remove_all(app);
|
297
|
+
}
|
298
|
+
|
299
|
+
void
|
300
|
+
Gamepad_init (Application* app)
|
301
|
+
{
|
302
|
+
init_gamepad(app);
|
303
|
+
}
|
304
|
+
|
305
|
+
void
|
306
|
+
Gamepad_fin (Application* app)
|
307
|
+
{
|
308
|
+
fin_gamepad(app);
|
309
|
+
|
310
|
+
Gamepad_remove_all(app);
|
311
|
+
}
|
312
|
+
|
313
|
+
|
314
|
+
}// Reflex
|
data/src/ios/reflex.mm
CHANGED
@@ -3,7 +3,6 @@
|
|
3
3
|
|
4
4
|
|
5
5
|
#include "reflex/exception.h"
|
6
|
-
#include "event.h"
|
7
6
|
|
8
7
|
|
9
8
|
namespace Reflex
|
@@ -25,8 +24,6 @@ namespace Reflex
|
|
25
24
|
reflex_error(__FILE__, __LINE__, "already initialized.");
|
26
25
|
|
27
26
|
global::pool = [[NSAutoreleasePool alloc] init];
|
28
|
-
|
29
|
-
init_game_controllers();
|
30
27
|
}
|
31
28
|
|
32
29
|
void
|
@@ -35,8 +32,6 @@ namespace Reflex
|
|
35
32
|
if (!global::pool)
|
36
33
|
reflex_error(__FILE__, __LINE__, "not initialized.");
|
37
34
|
|
38
|
-
fin_game_controllers();
|
39
|
-
|
40
35
|
[global::pool release];
|
41
36
|
global::pool = nil;
|
42
37
|
}
|
data/src/midi.cpp
ADDED
@@ -0,0 +1,379 @@
|
|
1
|
+
#include "midi.h"
|
2
|
+
|
3
|
+
|
4
|
+
#include <algorithm>
|
5
|
+
#include <RtMidi.h>
|
6
|
+
#include <xot/time.h>
|
7
|
+
#include "reflex/exception.h"
|
8
|
+
#include "reflex/debug.h"
|
9
|
+
#include "queue.h"
|
10
|
+
#include "event.h"
|
11
|
+
#include "application.h"
|
12
|
+
#include "window.h"
|
13
|
+
|
14
|
+
|
15
|
+
namespace Reflex
|
16
|
+
{
|
17
|
+
|
18
|
+
|
19
|
+
struct MIDI::Data
|
20
|
+
{
|
21
|
+
|
22
|
+
RtMidiIn input;
|
23
|
+
|
24
|
+
String name;
|
25
|
+
|
26
|
+
double time = 0;
|
27
|
+
|
28
|
+
~Data ()
|
29
|
+
{
|
30
|
+
input.closePort();
|
31
|
+
}
|
32
|
+
|
33
|
+
};// MIDI::Data
|
34
|
+
|
35
|
+
|
36
|
+
static void
|
37
|
+
call_note_event (
|
38
|
+
MIDI* midi, bool on,
|
39
|
+
int channel, int note, float velocity, double time)
|
40
|
+
{
|
41
|
+
NoteEvent e(
|
42
|
+
on ? NoteEvent::ON : NoteEvent::OFF,
|
43
|
+
channel, note, velocity, time);
|
44
|
+
|
45
|
+
midi->on_note(&e);
|
46
|
+
if (e.is_blocked()) return;
|
47
|
+
|
48
|
+
switch ((int) e.action())
|
49
|
+
{
|
50
|
+
case NoteEvent::ON: midi->on_note_on(&e); break;
|
51
|
+
case NoteEvent::OFF: midi->on_note_off(&e); break;
|
52
|
+
}
|
53
|
+
if (e.is_blocked()) return;
|
54
|
+
|
55
|
+
Window* win = Window_get_active();
|
56
|
+
if (!win) return;
|
57
|
+
|
58
|
+
Window_call_note_event(win, &e);
|
59
|
+
}
|
60
|
+
|
61
|
+
|
62
|
+
struct MIDIEvent
|
63
|
+
{
|
64
|
+
|
65
|
+
typedef std::vector<unsigned char> Message;
|
66
|
+
|
67
|
+
enum Type {UNKNOWN, MESSAGE, ERROR};
|
68
|
+
|
69
|
+
Type type = UNKNOWN;
|
70
|
+
|
71
|
+
MIDI::Ref midi;
|
72
|
+
|
73
|
+
Message message;
|
74
|
+
|
75
|
+
RtMidiError error;
|
76
|
+
|
77
|
+
double time = 0;
|
78
|
+
|
79
|
+
MIDIEvent ()
|
80
|
+
: error("")
|
81
|
+
{
|
82
|
+
}
|
83
|
+
|
84
|
+
MIDIEvent (MIDI* midi, const Message& message, double time)
|
85
|
+
: type(MESSAGE), midi(midi), message(message), error(""), time(time)
|
86
|
+
{
|
87
|
+
}
|
88
|
+
|
89
|
+
MIDIEvent (MIDI* midi, const RtMidiError& error)
|
90
|
+
: type(ERROR), midi(midi), error(error)
|
91
|
+
{
|
92
|
+
}
|
93
|
+
|
94
|
+
};// MIDIEvent
|
95
|
+
|
96
|
+
|
97
|
+
static Queue<MIDIEvent> queue;
|
98
|
+
|
99
|
+
static void
|
100
|
+
dispatch_midi_event (MIDIEvent* event)
|
101
|
+
{
|
102
|
+
switch (event->type)
|
103
|
+
{
|
104
|
+
case MIDIEvent::MESSAGE:
|
105
|
+
{
|
106
|
+
auto& bytes = event->message;
|
107
|
+
switch (bytes[0] >> 4)
|
108
|
+
{
|
109
|
+
case 0x9:
|
110
|
+
call_note_event(
|
111
|
+
event->midi, true,
|
112
|
+
bytes[0] & 0xf, bytes[1], bytes[2] / 127.f, event->time);
|
113
|
+
break;
|
114
|
+
|
115
|
+
case 0x8:
|
116
|
+
call_note_event(
|
117
|
+
event->midi, false,
|
118
|
+
bytes[0] & 0xf, bytes[1], bytes[2] / 127.f, event->time);
|
119
|
+
break;
|
120
|
+
}
|
121
|
+
break;
|
122
|
+
}
|
123
|
+
|
124
|
+
case MIDIEvent::ERROR:
|
125
|
+
{
|
126
|
+
system_error(
|
127
|
+
__FILE__, __LINE__,
|
128
|
+
Xot::stringf("MIDI: %s", event->error.what()).c_str());
|
129
|
+
break;
|
130
|
+
}
|
131
|
+
|
132
|
+
case MIDIEvent::UNKNOWN:
|
133
|
+
invalid_state_error(__FILE__, __LINE__);
|
134
|
+
}
|
135
|
+
}
|
136
|
+
|
137
|
+
static void
|
138
|
+
process_midi_events ()
|
139
|
+
{
|
140
|
+
MIDIEvent event;
|
141
|
+
while (queue.try_pop(&event))
|
142
|
+
dispatch_midi_event(&event);
|
143
|
+
}
|
144
|
+
|
145
|
+
static void
|
146
|
+
midi_callback (double dt, MIDIEvent::Message* message, void* data)
|
147
|
+
{
|
148
|
+
MIDI* midi = (MIDI*) data;
|
149
|
+
MIDI::Data* self = midi->self.get();
|
150
|
+
|
151
|
+
if (self->time == 0)
|
152
|
+
self->time = Xot::time();
|
153
|
+
else
|
154
|
+
self->time += dt;
|
155
|
+
|
156
|
+
queue.push(MIDIEvent(midi, *message, self->time));
|
157
|
+
}
|
158
|
+
|
159
|
+
static void
|
160
|
+
error_callback (RtMidiError::Type type, const std::string& message, void* data)
|
161
|
+
{
|
162
|
+
MIDI* midi = (MIDI*) data;
|
163
|
+
|
164
|
+
queue.push(MIDIEvent(midi, RtMidiError(message, type)));
|
165
|
+
}
|
166
|
+
|
167
|
+
static MIDI_CreateFun midi_create_fun = NULL;
|
168
|
+
|
169
|
+
void
|
170
|
+
MIDI_set_create_fun (MIDI_CreateFun fun)
|
171
|
+
{
|
172
|
+
midi_create_fun = fun;
|
173
|
+
}
|
174
|
+
|
175
|
+
static MIDI*
|
176
|
+
create_midi ()
|
177
|
+
{
|
178
|
+
return midi_create_fun ? midi_create_fun() : new MIDI();
|
179
|
+
}
|
180
|
+
|
181
|
+
static void
|
182
|
+
open_midi (MIDI* midi, uint port)
|
183
|
+
{
|
184
|
+
MIDI::Data* self = midi->self.get();
|
185
|
+
|
186
|
+
if (port >= self->input.getPortCount())
|
187
|
+
argument_error(__FILE__, __LINE__);
|
188
|
+
|
189
|
+
self->name = self->input.getPortName(port);
|
190
|
+
self->input.setCallback(midi_callback, midi);
|
191
|
+
self->input.setErrorCallback(error_callback, midi);
|
192
|
+
self->input.openPort(port);
|
193
|
+
self->input.ignoreTypes(false, false, false);
|
194
|
+
}
|
195
|
+
|
196
|
+
static MIDI::List midis;
|
197
|
+
|
198
|
+
static void
|
199
|
+
add_midi (MIDI* midi)
|
200
|
+
{
|
201
|
+
midis.emplace_back(midi);
|
202
|
+
Application_call_device_connect(app(), midi);
|
203
|
+
}
|
204
|
+
|
205
|
+
static void
|
206
|
+
remove_midi (MIDI* midi)
|
207
|
+
{
|
208
|
+
MIDI::Ref ref = midi;
|
209
|
+
auto it = std::find(midis.begin(), midis.end(), ref);
|
210
|
+
if (it == midis.end()) return;
|
211
|
+
|
212
|
+
midis.erase(it);
|
213
|
+
Application_call_device_disconnect(app(), ref);
|
214
|
+
}
|
215
|
+
|
216
|
+
|
217
|
+
struct MIDIDeviceManager
|
218
|
+
{
|
219
|
+
|
220
|
+
RtMidiIn manager;
|
221
|
+
|
222
|
+
uint duplicated_port_name_count = 0;
|
223
|
+
|
224
|
+
MIDIDeviceManager ()
|
225
|
+
{
|
226
|
+
update();
|
227
|
+
}
|
228
|
+
|
229
|
+
~MIDIDeviceManager ()
|
230
|
+
{
|
231
|
+
for (auto& midi : midis)
|
232
|
+
Application_call_device_disconnect(app(), midi);
|
233
|
+
}
|
234
|
+
|
235
|
+
void update ()
|
236
|
+
{
|
237
|
+
uint count = manager.getPortCount() - duplicated_port_name_count;
|
238
|
+
if (count > midis.size())
|
239
|
+
on_connect();
|
240
|
+
else if (count < midis.size())
|
241
|
+
on_disconnect();
|
242
|
+
}
|
243
|
+
|
244
|
+
void on_connect ()
|
245
|
+
{
|
246
|
+
std::set<String> names;
|
247
|
+
for (auto& midi : midis)
|
248
|
+
names.emplace(midi->self->name);
|
249
|
+
|
250
|
+
each_port([&](int port, auto& name)
|
251
|
+
{
|
252
|
+
if (names.contains(name)) return;
|
253
|
+
|
254
|
+
MIDI* midi = create_midi();
|
255
|
+
open_midi(midi, port);
|
256
|
+
add_midi(midi);
|
257
|
+
});
|
258
|
+
|
259
|
+
update_duplicated_port_name_count();
|
260
|
+
}
|
261
|
+
|
262
|
+
void on_disconnect ()
|
263
|
+
{
|
264
|
+
std::set<String> port_names;
|
265
|
+
get_port_names(&port_names);
|
266
|
+
|
267
|
+
while (true)
|
268
|
+
{
|
269
|
+
auto it = std::find_if(
|
270
|
+
midis.begin(), midis.end(),
|
271
|
+
[&](auto& midi) {return !port_names.contains(midi->self->name);});
|
272
|
+
if (it == midis.end()) break;
|
273
|
+
|
274
|
+
remove_midi(*it);
|
275
|
+
}
|
276
|
+
|
277
|
+
update_duplicated_port_name_count();
|
278
|
+
}
|
279
|
+
|
280
|
+
void update_duplicated_port_name_count ()
|
281
|
+
{
|
282
|
+
std::set<String> port_names;
|
283
|
+
get_port_names(&port_names);
|
284
|
+
|
285
|
+
duplicated_port_name_count =
|
286
|
+
manager.getPortCount() - (uint) port_names.size();
|
287
|
+
}
|
288
|
+
|
289
|
+
void get_port_names (std::set<String>* names)
|
290
|
+
{
|
291
|
+
each_port([&](int, auto& name) {names->emplace(name);});
|
292
|
+
}
|
293
|
+
|
294
|
+
void each_port (std::function<void(int, const std::string&)> fun)
|
295
|
+
{
|
296
|
+
uint size = manager.getPortCount();
|
297
|
+
for (uint i = 0; i < size; ++i)
|
298
|
+
fun(i, manager.getPortName(i));
|
299
|
+
}
|
300
|
+
|
301
|
+
};// MIDIDeviceManager
|
302
|
+
|
303
|
+
|
304
|
+
static std::unique_ptr<MIDIDeviceManager> manager;
|
305
|
+
|
306
|
+
void
|
307
|
+
MIDI_init (Application* app)
|
308
|
+
{
|
309
|
+
if (manager)
|
310
|
+
invalid_state_error(__FILE__, __LINE__);
|
311
|
+
|
312
|
+
manager.reset(new MIDIDeviceManager());
|
313
|
+
}
|
314
|
+
|
315
|
+
void
|
316
|
+
MIDI_fin (Application* app)
|
317
|
+
{
|
318
|
+
if (!manager)
|
319
|
+
invalid_state_error(__FILE__, __LINE__);
|
320
|
+
|
321
|
+
manager.reset();
|
322
|
+
}
|
323
|
+
|
324
|
+
void
|
325
|
+
MIDI_process_events ()
|
326
|
+
{
|
327
|
+
process_midi_events();
|
328
|
+
|
329
|
+
static double prev_time = 0;
|
330
|
+
if (manager && Xot::time() - prev_time > 0.5)
|
331
|
+
{
|
332
|
+
manager->update();
|
333
|
+
prev_time = Xot::time();
|
334
|
+
}
|
335
|
+
}
|
336
|
+
|
337
|
+
|
338
|
+
MIDI::MIDI ()
|
339
|
+
{
|
340
|
+
}
|
341
|
+
|
342
|
+
MIDI::~MIDI ()
|
343
|
+
{
|
344
|
+
}
|
345
|
+
|
346
|
+
const char*
|
347
|
+
MIDI::name () const
|
348
|
+
{
|
349
|
+
return self->name;
|
350
|
+
}
|
351
|
+
|
352
|
+
void
|
353
|
+
MIDI::on_note (NoteEvent* e)
|
354
|
+
{
|
355
|
+
}
|
356
|
+
|
357
|
+
void
|
358
|
+
MIDI::on_note_on (NoteEvent* e)
|
359
|
+
{
|
360
|
+
}
|
361
|
+
|
362
|
+
void
|
363
|
+
MIDI::on_note_off (NoteEvent* e)
|
364
|
+
{
|
365
|
+
}
|
366
|
+
|
367
|
+
MIDI::operator bool () const
|
368
|
+
{
|
369
|
+
return self->input.isPortOpen();
|
370
|
+
}
|
371
|
+
|
372
|
+
const MIDI::List&
|
373
|
+
MIDI::all ()
|
374
|
+
{
|
375
|
+
return midis;
|
376
|
+
}
|
377
|
+
|
378
|
+
|
379
|
+
}// Reflex
|