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.
Files changed (83) hide show
  1. checksums.yaml +4 -4
  2. data/.doc/ext/reflex/application.cpp +16 -0
  3. data/.doc/ext/reflex/device.cpp +46 -3
  4. data/.doc/ext/reflex/device_event.cpp +62 -0
  5. data/.doc/ext/reflex/key_event.cpp +49 -19
  6. data/.doc/ext/reflex/midi.cpp +82 -0
  7. data/.doc/ext/reflex/native.cpp +10 -4
  8. data/.doc/ext/reflex/note_event.cpp +121 -0
  9. data/.doc/ext/reflex/reflex.cpp +30 -8
  10. data/.doc/ext/reflex/view.cpp +11 -16
  11. data/.doc/ext/reflex/window.cpp +24 -0
  12. data/ChangeLog.md +16 -0
  13. data/Rakefile +7 -0
  14. data/VERSION +1 -1
  15. data/ext/reflex/application.cpp +18 -0
  16. data/ext/reflex/device.cpp +48 -3
  17. data/ext/reflex/device_event.cpp +65 -0
  18. data/ext/reflex/key_event.cpp +49 -19
  19. data/ext/reflex/midi.cpp +87 -0
  20. data/ext/reflex/native.cpp +10 -4
  21. data/ext/reflex/note_event.cpp +130 -0
  22. data/ext/reflex/reflex.cpp +31 -8
  23. data/ext/reflex/view.cpp +11 -16
  24. data/ext/reflex/window.cpp +27 -0
  25. data/include/reflex/application.h +4 -0
  26. data/include/reflex/defs.h +58 -21
  27. data/include/reflex/device.h +22 -0
  28. data/include/reflex/event.h +64 -2
  29. data/include/reflex/gamepad.h +175 -0
  30. data/include/reflex/midi.h +53 -0
  31. data/include/reflex/reflex.h +2 -0
  32. data/include/reflex/ruby/application.h +18 -0
  33. data/include/reflex/ruby/device.h +40 -0
  34. data/include/reflex/ruby/event.h +22 -0
  35. data/include/reflex/ruby/midi.h +84 -0
  36. data/include/reflex/ruby/window.h +27 -0
  37. data/include/reflex/view.h +9 -1
  38. data/include/reflex/window.h +6 -0
  39. data/lib/reflex/note_event.rb +34 -0
  40. data/lib/reflex/view.rb +2 -1
  41. data/lib/reflex.rb +9 -8
  42. data/reflex.gemspec +3 -3
  43. data/src/application.cpp +70 -0
  44. data/src/application.h +9 -0
  45. data/src/device.cpp +24 -0
  46. data/src/event.cpp +133 -7
  47. data/src/event.h +5 -0
  48. data/src/gamepad.cpp +176 -0
  49. data/src/gamepad.h +74 -0
  50. data/src/ios/app_delegate.mm +2 -2
  51. data/src/ios/application.mm +0 -25
  52. data/src/ios/event.h +0 -5
  53. data/src/ios/event.mm +11 -87
  54. data/src/ios/gamepad.mm +314 -0
  55. data/src/ios/reflex.mm +0 -5
  56. data/src/midi.cpp +379 -0
  57. data/src/midi.h +32 -0
  58. data/src/osx/app_delegate.mm +2 -2
  59. data/src/osx/application.mm +0 -25
  60. data/src/osx/event.h +0 -5
  61. data/src/osx/event.mm +9 -86
  62. data/src/osx/gamepad.mm +40 -0
  63. data/src/osx/gamepad_gc.mm +299 -0
  64. data/src/osx/gamepad_hid.mm +567 -0
  65. data/src/osx/reflex.mm +0 -5
  66. data/src/queue.h +71 -0
  67. data/src/reflex.cpp +18 -0
  68. data/src/timer.cpp +3 -10
  69. data/src/view.cpp +39 -0
  70. data/src/view.h +2 -0
  71. data/src/win32/application.cpp +5 -26
  72. data/src/win32/event.cpp +6 -89
  73. data/src/win32/event.h +1 -1
  74. data/src/win32/gamepad.cpp +110 -0
  75. data/src/win32/gamepad.h +20 -0
  76. data/src/win32/window.cpp +2 -1
  77. data/src/window.cpp +61 -10
  78. data/src/window.h +2 -0
  79. data/test/test_capture_event.rb +20 -16
  80. data/test/test_key_event.rb +8 -1
  81. data/test/test_note_event.rb +43 -0
  82. data/test/test_view.rb +24 -12
  83. metadata +43 -14
@@ -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