]> git.sesse.net Git - nageru/blob - futatabi/midi_mapper.cpp
Reintroduce faster DeckLink shutdown; now with a fix for the UI switcher.
[nageru] / futatabi / midi_mapper.cpp
1 #include "midi_mapper.h"
2
3 #include <alsa/asoundlib.h>
4 #include <assert.h>
5 #include <errno.h>
6 #include <fcntl.h>
7 #include <google/protobuf/descriptor.h>
8 #include <google/protobuf/io/zero_copy_stream_impl.h>
9 #include <google/protobuf/message.h>
10 #include <google/protobuf/text_format.h>
11 #include <math.h>
12 #include <pthread.h>
13 #include <poll.h>
14 #include <stdint.h>
15 #include <stdio.h>
16 #include <sys/eventfd.h>
17 #include <unistd.h>
18 #include <algorithm>
19 #include <functional>
20 #include <map>
21 #include <thread>
22
23 #include "defs.h"
24 #include "futatabi_midi_mapping.pb.h"
25 #include "shared/midi_mapper_util.h"
26 #include "shared/text_proto.h"
27
28 using namespace google::protobuf;
29 using namespace std;
30 using namespace std::placeholders;
31
32 MIDIMapper::MIDIMapper(ControllerReceiver *receiver)
33         : receiver(receiver), mapping_proto(new MIDIMappingProto), midi_device(this)
34 {
35 }
36
37 MIDIMapper::~MIDIMapper() {}
38
39 bool load_midi_mapping_from_file(const string &filename, MIDIMappingProto *new_mapping)
40 {
41         return load_proto_from_file(filename, new_mapping);
42 }
43
44 bool save_midi_mapping_to_file(const MIDIMappingProto &mapping_proto, const string &filename)
45 {
46         return save_proto_to_file(mapping_proto, filename);
47 }
48
49 void MIDIMapper::set_midi_mapping(const MIDIMappingProto &new_mapping)
50 {
51         lock_guard<mutex> lock(mu);
52         if (mapping_proto) {
53                 mapping_proto->CopyFrom(new_mapping);
54         } else {
55                 mapping_proto.reset(new MIDIMappingProto(new_mapping));
56         }
57
58         num_controller_banks = min(max(mapping_proto->num_controller_banks(), 1), 5);
59         current_controller_bank = 0;
60 }
61
62 void MIDIMapper::start_thread()
63 {
64         midi_device.start_thread();
65 }
66
67 const MIDIMappingProto &MIDIMapper::get_current_mapping() const
68 {
69         lock_guard<mutex> lock(mu);
70         return *mapping_proto;
71 }
72
73 ControllerReceiver *MIDIMapper::set_receiver(ControllerReceiver *new_receiver)
74 {
75         lock_guard<mutex> lock(mu);
76         swap(receiver, new_receiver);
77         return new_receiver;  // Now old receiver.
78 }
79
80 void MIDIMapper::controller_received(int controller, int value_int)
81 {
82         int delta_value = value_int - 64;  // For infinite controllers such as jog.
83         float value = map_controller_to_float(controller, value_int);
84
85         receiver->controller_changed(controller);
86
87         match_controller(controller, MIDIMappingProto::kJogFieldNumber, MIDIMappingProto::kJogBankFieldNumber,
88                 delta_value, bind(&ControllerReceiver::jog, receiver, _1));
89
90         // Speed goes from 0.0 to 2.0 (the receiver will clamp).
91         match_controller(controller, MIDIMappingProto::kMasterSpeedFieldNumber, MIDIMappingProto::kMasterSpeedBankFieldNumber,
92                 value * 2.0, bind(&ControllerReceiver::set_master_speed, receiver, _1));
93 }
94
95 void MIDIMapper::note_on_received(int note)
96 {
97         lock_guard<mutex> lock(mu);
98         receiver->note_on(note);
99
100         if (mapping_proto->has_prev_bank() &&
101             mapping_proto->prev_bank().note_number() == note) {
102                 current_controller_bank = (current_controller_bank + num_controller_banks - 1) % num_controller_banks;
103                 update_lights_lock_held();
104         }
105         if (mapping_proto->has_next_bank() &&
106             mapping_proto->next_bank().note_number() == note) {
107                 current_controller_bank = (current_controller_bank + 1) % num_controller_banks;
108                 update_lights_lock_held();
109         }
110         if (mapping_proto->has_select_bank_1() &&
111             mapping_proto->select_bank_1().note_number() == note) {
112                 current_controller_bank = 0;
113                 update_lights_lock_held();
114         }
115         if (mapping_proto->has_select_bank_2() &&
116             mapping_proto->select_bank_2().note_number() == note &&
117             num_controller_banks >= 2) {
118                 current_controller_bank = 1;
119                 update_lights_lock_held();
120         }
121         if (mapping_proto->has_select_bank_3() &&
122             mapping_proto->select_bank_3().note_number() == note &&
123             num_controller_banks >= 3) {
124                 current_controller_bank = 2;
125                 update_lights_lock_held();
126         }
127         if (mapping_proto->has_select_bank_4() &&
128             mapping_proto->select_bank_4().note_number() == note &&
129             num_controller_banks >= 4) {
130                 current_controller_bank = 3;
131                 update_lights_lock_held();
132         }
133         if (mapping_proto->has_select_bank_5() &&
134             mapping_proto->select_bank_5().note_number() == note &&
135             num_controller_banks >= 5) {
136                 current_controller_bank = 4;
137                 update_lights_lock_held();
138         }
139
140         match_button(note, MIDIMappingProto::kPreviewFieldNumber, MIDIMappingProto::kPreviewBankFieldNumber,
141                 bind(&ControllerReceiver::preview, receiver));
142         match_button(note, MIDIMappingProto::kQueueFieldNumber, MIDIMappingProto::kQueueBankFieldNumber,
143                 bind(&ControllerReceiver::queue, receiver));
144         match_button(note, MIDIMappingProto::kPlayFieldNumber, MIDIMappingProto::kPlayBankFieldNumber,
145                 bind(&ControllerReceiver::play, receiver));
146         match_button(note, MIDIMappingProto::kNextFieldNumber, MIDIMappingProto::kNextButtonBankFieldNumber,
147                 bind(&ControllerReceiver::next, receiver));
148         match_button(note, MIDIMappingProto::kToggleLockFieldNumber, MIDIMappingProto::kToggleLockBankFieldNumber,
149                 bind(&ControllerReceiver::toggle_lock, receiver));
150
151         unsigned num_cameras = std::min(MAX_STREAMS, mapping_proto->camera_size());
152         for (unsigned camera_idx = 0; camera_idx < num_cameras; ++camera_idx) {
153                 const CameraMIDIMappingProto &camera = mapping_proto->camera(camera_idx);
154                 if (match_bank_helper(camera, CameraMIDIMappingProto::kBankFieldNumber, current_controller_bank) &&
155                     match_button_helper(camera, CameraMIDIMappingProto::kButtonFieldNumber, note)) {
156                         receiver->switch_camera(camera_idx);
157                 }
158         }
159
160         match_button(note, MIDIMappingProto::kCueInFieldNumber, MIDIMappingProto::kCueInBankFieldNumber,
161                 bind(&ControllerReceiver::cue_in, receiver));
162         match_button(note, MIDIMappingProto::kCueOutFieldNumber, MIDIMappingProto::kCueOutBankFieldNumber,
163                 bind(&ControllerReceiver::cue_out, receiver));
164 }
165
166 void MIDIMapper::match_controller(int controller, int field_number, int bank_field_number, float value, function<void(float)> func)
167 {
168         if (bank_mismatch(bank_field_number)) {
169                 return;
170         }
171
172         if (match_controller_helper(*mapping_proto, field_number, controller)) {
173                 func(value);
174         }
175 }
176
177 void MIDIMapper::match_button(int note, int field_number, int bank_field_number, function<void()> func)
178 {
179         if (bank_mismatch(bank_field_number)) {
180                 return;
181         }
182
183         if (match_button_helper(*mapping_proto, field_number, note)) {
184                 func();
185         }
186 }
187
188 bool MIDIMapper::has_active_controller(int field_number, int bank_field_number)
189 {
190         if (bank_mismatch(bank_field_number)) {
191                 return false;
192         }
193
194         const FieldDescriptor *descriptor = mapping_proto->GetDescriptor()->FindFieldByNumber(field_number);
195         const Reflection *reflection = mapping_proto->GetReflection();
196         return reflection->HasField(*mapping_proto, descriptor);
197 }
198
199 bool MIDIMapper::bank_mismatch(int bank_field_number)
200 {
201         return !match_bank_helper(*mapping_proto, bank_field_number, current_controller_bank);
202 }
203
204 void MIDIMapper::refresh_lights()
205 {
206         lock_guard<mutex> lock(mu);
207         update_lights_lock_held();
208 }
209
210 void MIDIMapper::update_lights_lock_held()
211 {
212         map<MIDIDevice::LightKey, uint8_t> active_lights;  // Desired state.
213         if (current_controller_bank == 0) {
214                 activate_mapped_light(*mapping_proto, MIDIMappingProto::kBank1IsSelectedFieldNumber, &active_lights);
215         }
216         if (current_controller_bank == 1) {
217                 activate_mapped_light(*mapping_proto, MIDIMappingProto::kBank2IsSelectedFieldNumber, &active_lights);
218         }
219         if (current_controller_bank == 2) {
220                 activate_mapped_light(*mapping_proto, MIDIMappingProto::kBank3IsSelectedFieldNumber, &active_lights);
221         }
222         if (current_controller_bank == 3) {
223                 activate_mapped_light(*mapping_proto, MIDIMappingProto::kBank4IsSelectedFieldNumber, &active_lights);
224         }
225         if (current_controller_bank == 4) {
226                 activate_mapped_light(*mapping_proto, MIDIMappingProto::kBank5IsSelectedFieldNumber, &active_lights);
227         }
228         if (preview_enabled_light == On) {  // Playing.
229                 activate_mapped_light(*mapping_proto, MIDIMappingProto::kPreviewPlayingFieldNumber, &active_lights);
230         } else if (preview_enabled_light == Blinking) {  // Preview ready.
231                 activate_mapped_light(*mapping_proto, MIDIMappingProto::kPreviewReadyFieldNumber, &active_lights);
232         }
233         if (queue_enabled_light) {
234                 activate_mapped_light(*mapping_proto, MIDIMappingProto::kQueueEnabledFieldNumber, &active_lights);
235         }
236         if (play_enabled_light == On) {  // Playing.
237                 activate_mapped_light(*mapping_proto, MIDIMappingProto::kPlayingFieldNumber, &active_lights);
238         } else if (play_enabled_light == Blinking) {  // Play ready.
239                 activate_mapped_light(*mapping_proto, MIDIMappingProto::kPlayReadyFieldNumber, &active_lights);
240         }
241         if (next_ready_light == On) {
242                 activate_mapped_light(*mapping_proto, MIDIMappingProto::kNextReadyFieldNumber, &active_lights);
243         }
244         if (locked_light == On) {
245                 activate_mapped_light(*mapping_proto, MIDIMappingProto::kLockedFieldNumber, &active_lights);
246         } else if (locked_light == Blinking) {
247                 activate_mapped_light(*mapping_proto, MIDIMappingProto::kLockedBlinkingFieldNumber, &active_lights);
248         }
249         if (current_highlighted_camera >= 0 && current_highlighted_camera < mapping_proto->camera_size()) {
250                 const CameraMIDIMappingProto &camera = mapping_proto->camera(current_highlighted_camera);
251                 activate_mapped_light(camera, CameraMIDIMappingProto::kIsCurrentFieldNumber, &active_lights);
252         }
253
254         // Master speed light.
255         if (mapping_proto->has_master_speed_light()) {
256                 unsigned controller = mapping_proto->master_speed_light().controller_number();
257                 unsigned min = mapping_proto->master_speed_light_min();
258                 unsigned max = mapping_proto->master_speed_light_max();
259                 int speed_light_value = lrintf((max - min) * current_speed / 2.0f) + min;
260                 active_lights[MIDIDevice::LightKey{MIDIDevice::LightKey::CONTROLLER, controller}] = speed_light_value;
261         }
262
263         // These are always enabled right now.
264         activate_mapped_light(*mapping_proto, MIDIMappingProto::kCueInEnabledFieldNumber, &active_lights);
265         activate_mapped_light(*mapping_proto, MIDIMappingProto::kCueOutEnabledFieldNumber, &active_lights);
266
267         midi_device.update_lights(active_lights);
268 }