]> git.sesse.net Git - nageru/blob - nageru/midi_mapper.cpp
Split out the ALSA-specific parts from MIDIMapper into a new class MIDIDevice.
[nageru] / nageru / 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 <pthread.h>
12 #include <poll.h>
13 #include <stdint.h>
14 #include <stdio.h>
15 #include <sys/eventfd.h>
16 #include <unistd.h>
17 #include <algorithm>
18 #include <functional>
19 #include <thread>
20
21 #include "audio_mixer.h"
22 #include "midi_mapping.pb.h"
23
24 using namespace google::protobuf;
25 using namespace std;
26 using namespace std::placeholders;
27
28 namespace {
29
30 double map_controller_to_float(int val)
31 {
32         // Slightly hackish mapping so that we can represent exactly 0.0, 0.5 and 1.0.
33         if (val <= 0) {
34                 return 0.0;
35         } else if (val >= 127) {
36                 return 1.0;
37         } else {
38                 return (val + 0.5) / 127.0;
39         }
40 }
41
42 }  // namespace
43
44 MIDIMapper::MIDIMapper(ControllerReceiver *receiver)
45         : receiver(receiver), mapping_proto(new MIDIMappingProto), midi_device(this)
46 {
47 }
48
49 MIDIMapper::~MIDIMapper() {}
50
51 bool load_midi_mapping_from_file(const string &filename, MIDIMappingProto *new_mapping)
52 {
53         // Read and parse the protobuf from disk.
54         int fd = open(filename.c_str(), O_RDONLY);
55         if (fd == -1) {
56                 perror(filename.c_str());
57                 return false;
58         }
59         io::FileInputStream input(fd);  // Takes ownership of fd.
60         if (!TextFormat::Parse(&input, new_mapping)) {
61                 input.Close();
62                 return false;
63         }
64         input.Close();
65         return true;
66 }
67
68 bool save_midi_mapping_to_file(const MIDIMappingProto &mapping_proto, const string &filename)
69 {
70         // Save to disk. We use the text format because it's friendlier
71         // for a user to look at and edit.
72         int fd = open(filename.c_str(), O_WRONLY | O_TRUNC | O_CREAT, 0666);
73         if (fd == -1) {
74                 perror(filename.c_str());
75                 return false;
76         }
77         io::FileOutputStream output(fd);  // Takes ownership of fd.
78         if (!TextFormat::Print(mapping_proto, &output)) {
79                 // TODO: Don't overwrite the old file (if any) on error.
80                 output.Close();
81                 return false;
82         }
83
84         output.Close();
85         return true;
86 }
87
88 void MIDIMapper::set_midi_mapping(const MIDIMappingProto &new_mapping)
89 {
90         lock_guard<mutex> lock(mu);
91         if (mapping_proto) {
92                 mapping_proto->CopyFrom(new_mapping);
93         } else {
94                 mapping_proto.reset(new MIDIMappingProto(new_mapping));
95         }
96
97         num_controller_banks = min(max(mapping_proto->num_controller_banks(), 1), 5);
98         current_controller_bank = 0;
99
100         receiver->clear_all_highlights();
101         update_highlights();
102 }
103
104 void MIDIMapper::start_thread()
105 {
106         midi_device.start_thread();
107 }
108
109 const MIDIMappingProto &MIDIMapper::get_current_mapping() const
110 {
111         lock_guard<mutex> lock(mu);
112         return *mapping_proto;
113 }
114
115 ControllerReceiver *MIDIMapper::set_receiver(ControllerReceiver *new_receiver)
116 {
117         lock_guard<mutex> lock(mu);
118         swap(receiver, new_receiver);
119         return new_receiver;  // Now old receiver.
120 }
121
122 void MIDIMapper::controller_received(int controller, int value_int)
123 {
124         const float value = map_controller_to_float(value_int);
125
126         receiver->controller_changed(controller);
127
128         // Global controllers.
129         match_controller(controller, MIDIMappingBusProto::kLocutFieldNumber, MIDIMappingProto::kLocutBankFieldNumber,
130                 value, bind(&ControllerReceiver::set_locut, receiver, _2));
131         match_controller(controller, MIDIMappingBusProto::kLimiterThresholdFieldNumber, MIDIMappingProto::kLimiterThresholdBankFieldNumber,
132                 value, bind(&ControllerReceiver::set_limiter_threshold, receiver, _2));
133         match_controller(controller, MIDIMappingBusProto::kMakeupGainFieldNumber, MIDIMappingProto::kMakeupGainBankFieldNumber,
134                 value, bind(&ControllerReceiver::set_makeup_gain, receiver, _2));
135
136         // Bus controllers.
137         match_controller(controller, MIDIMappingBusProto::kStereoWidthFieldNumber, MIDIMappingProto::kStereoWidthBankFieldNumber,
138                 value, bind(&ControllerReceiver::set_stereo_width, receiver, _1, _2));
139         match_controller(controller, MIDIMappingBusProto::kTrebleFieldNumber, MIDIMappingProto::kTrebleBankFieldNumber,
140                 value, bind(&ControllerReceiver::set_treble, receiver, _1, _2));
141         match_controller(controller, MIDIMappingBusProto::kMidFieldNumber, MIDIMappingProto::kMidBankFieldNumber,
142                 value, bind(&ControllerReceiver::set_mid, receiver, _1, _2));
143         match_controller(controller, MIDIMappingBusProto::kBassFieldNumber, MIDIMappingProto::kBassBankFieldNumber,
144                 value, bind(&ControllerReceiver::set_bass, receiver, _1, _2));
145         match_controller(controller, MIDIMappingBusProto::kGainFieldNumber, MIDIMappingProto::kGainBankFieldNumber,
146                 value, bind(&ControllerReceiver::set_gain, receiver, _1, _2));
147         match_controller(controller, MIDIMappingBusProto::kCompressorThresholdFieldNumber, MIDIMappingProto::kCompressorThresholdBankFieldNumber,
148                 value, bind(&ControllerReceiver::set_compressor_threshold, receiver, _1, _2));
149         match_controller(controller, MIDIMappingBusProto::kFaderFieldNumber, MIDIMappingProto::kFaderBankFieldNumber,
150                 value, bind(&ControllerReceiver::set_fader, receiver, _1, _2));
151 }
152
153 void MIDIMapper::note_on_received(int note)
154 {
155         lock_guard<mutex> lock(mu);
156         receiver->note_on(note);
157
158         for (size_t bus_idx = 0; bus_idx < size_t(mapping_proto->bus_mapping_size()); ++bus_idx) {
159                 const MIDIMappingBusProto &bus_mapping = mapping_proto->bus_mapping(bus_idx);
160                 if (bus_mapping.has_prev_bank() &&
161                     bus_mapping.prev_bank().note_number() == note) {
162                         current_controller_bank = (current_controller_bank + num_controller_banks - 1) % num_controller_banks;
163                         update_highlights();
164                         update_lights_lock_held();
165                 }
166                 if (bus_mapping.has_next_bank() &&
167                     bus_mapping.next_bank().note_number() == note) {
168                         current_controller_bank = (current_controller_bank + 1) % num_controller_banks;
169                         update_highlights();
170                         update_lights_lock_held();
171                 }
172                 if (bus_mapping.has_select_bank_1() &&
173                     bus_mapping.select_bank_1().note_number() == note) {
174                         current_controller_bank = 0;
175                         update_highlights();
176                         update_lights_lock_held();
177                 }
178                 if (bus_mapping.has_select_bank_2() &&
179                     bus_mapping.select_bank_2().note_number() == note &&
180                     num_controller_banks >= 2) {
181                         current_controller_bank = 1;
182                         update_highlights();
183                         update_lights_lock_held();
184                 }
185                 if (bus_mapping.has_select_bank_3() &&
186                     bus_mapping.select_bank_3().note_number() == note &&
187                     num_controller_banks >= 3) {
188                         current_controller_bank = 2;
189                         update_highlights();
190                         update_lights_lock_held();
191                 }
192                 if (bus_mapping.has_select_bank_4() &&
193                     bus_mapping.select_bank_4().note_number() == note &&
194                     num_controller_banks >= 4) {
195                         current_controller_bank = 3;
196                         update_highlights();
197                         update_lights_lock_held();
198                 }
199                 if (bus_mapping.has_select_bank_5() &&
200                     bus_mapping.select_bank_5().note_number() == note &&
201                     num_controller_banks >= 5) {
202                         current_controller_bank = 4;
203                         update_highlights();
204                         update_lights_lock_held();
205                 }
206         }
207
208         match_button(note, MIDIMappingBusProto::kToggleLocutFieldNumber, MIDIMappingProto::kToggleLocutBankFieldNumber,
209                 bind(&ControllerReceiver::toggle_locut, receiver, _1));
210         match_button(note, MIDIMappingBusProto::kToggleAutoGainStagingFieldNumber, MIDIMappingProto::kToggleAutoGainStagingBankFieldNumber,
211                 bind(&ControllerReceiver::toggle_auto_gain_staging, receiver, _1));
212         match_button(note, MIDIMappingBusProto::kToggleCompressorFieldNumber, MIDIMappingProto::kToggleCompressorBankFieldNumber,
213                 bind(&ControllerReceiver::toggle_compressor, receiver, _1));
214         match_button(note, MIDIMappingBusProto::kClearPeakFieldNumber, MIDIMappingProto::kClearPeakBankFieldNumber,
215                 bind(&ControllerReceiver::clear_peak, receiver, _1));
216         match_button(note, MIDIMappingBusProto::kToggleMuteFieldNumber, MIDIMappingProto::kClearPeakBankFieldNumber,
217                 bind(&ControllerReceiver::toggle_mute, receiver, _1));
218         match_button(note, MIDIMappingBusProto::kToggleLimiterFieldNumber, MIDIMappingProto::kToggleLimiterBankFieldNumber,
219                 bind(&ControllerReceiver::toggle_limiter, receiver));
220         match_button(note, MIDIMappingBusProto::kToggleAutoMakeupGainFieldNumber, MIDIMappingProto::kToggleAutoMakeupGainBankFieldNumber,
221                 bind(&ControllerReceiver::toggle_auto_makeup_gain, receiver));
222 }
223
224 void MIDIMapper::update_num_subscribers(unsigned num_subscribers)
225 {
226         num_subscribed_ports = num_subscribers;
227         update_highlights();
228 }
229
230 void MIDIMapper::match_controller(int controller, int field_number, int bank_field_number, float value, function<void(unsigned, float)> func)
231 {
232         if (bank_mismatch(bank_field_number)) {
233                 return;
234         }
235
236         for (size_t bus_idx = 0; bus_idx < size_t(mapping_proto->bus_mapping_size()); ++bus_idx) {
237                 const MIDIMappingBusProto &bus_mapping = mapping_proto->bus_mapping(bus_idx);
238
239                 const FieldDescriptor *descriptor = bus_mapping.GetDescriptor()->FindFieldByNumber(field_number);
240                 const Reflection *bus_reflection = bus_mapping.GetReflection();
241                 if (!bus_reflection->HasField(bus_mapping, descriptor)) {
242                         continue;
243                 }
244                 const MIDIControllerProto &controller_proto =
245                         static_cast<const MIDIControllerProto &>(bus_reflection->GetMessage(bus_mapping, descriptor));
246                 if (controller_proto.controller_number() == controller) {
247                         func(bus_idx, value);
248                 }
249         }
250 }
251
252 void MIDIMapper::match_button(int note, int field_number, int bank_field_number, function<void(unsigned)> func)
253 {
254         if (bank_mismatch(bank_field_number)) {
255                 return;
256         }
257
258         for (size_t bus_idx = 0; bus_idx < size_t(mapping_proto->bus_mapping_size()); ++bus_idx) {
259                 const MIDIMappingBusProto &bus_mapping = mapping_proto->bus_mapping(bus_idx);
260
261                 const FieldDescriptor *descriptor = bus_mapping.GetDescriptor()->FindFieldByNumber(field_number);
262                 const Reflection *bus_reflection = bus_mapping.GetReflection();
263                 if (!bus_reflection->HasField(bus_mapping, descriptor)) {
264                         continue;
265                 }
266                 const MIDIButtonProto &button_proto =
267                         static_cast<const MIDIButtonProto &>(bus_reflection->GetMessage(bus_mapping, descriptor));
268                 if (button_proto.note_number() == note) {
269                         func(bus_idx);
270                 }
271         }
272 }
273
274 bool MIDIMapper::has_active_controller(unsigned bus_idx, int field_number, int bank_field_number)
275 {
276         if (bank_mismatch(bank_field_number)) {
277                 return false;
278         }
279
280         const MIDIMappingBusProto &bus_mapping = mapping_proto->bus_mapping(bus_idx);
281         const FieldDescriptor *descriptor = bus_mapping.GetDescriptor()->FindFieldByNumber(field_number);
282         const Reflection *bus_reflection = bus_mapping.GetReflection();
283         return bus_reflection->HasField(bus_mapping, descriptor);
284 }
285
286 bool MIDIMapper::bank_mismatch(int bank_field_number)
287 {
288         const FieldDescriptor *bank_descriptor = mapping_proto->GetDescriptor()->FindFieldByNumber(bank_field_number);
289         const Reflection *reflection = mapping_proto->GetReflection();
290         return (reflection->HasField(*mapping_proto, bank_descriptor) &&
291                 reflection->GetInt32(*mapping_proto, bank_descriptor) != current_controller_bank);
292 }
293
294 void MIDIMapper::refresh_highlights()
295 {
296         receiver->clear_all_highlights();
297         update_highlights();
298 }
299
300 void MIDIMapper::refresh_lights()
301 {
302         lock_guard<mutex> lock(mu);
303         update_lights_lock_held();
304 }
305
306 void MIDIMapper::update_highlights()
307 {
308         if (num_subscribed_ports.load() == 0) {
309                 receiver->clear_all_highlights();
310                 return;
311         }
312
313         // Global controllers.
314         bool highlight_locut = false;
315         bool highlight_limiter_threshold = false;
316         bool highlight_makeup_gain = false;
317         bool highlight_toggle_limiter = false;
318         bool highlight_toggle_auto_makeup_gain = false;
319         for (size_t bus_idx = 0; bus_idx < size_t(mapping_proto->bus_mapping_size()); ++bus_idx) {
320                 if (has_active_controller(
321                         bus_idx, MIDIMappingBusProto::kLocutFieldNumber, MIDIMappingProto::kLocutBankFieldNumber)) {
322                         highlight_locut = true;
323                 }
324                 if (has_active_controller(
325                         bus_idx, MIDIMappingBusProto::kLimiterThresholdFieldNumber, MIDIMappingProto::kLimiterThresholdBankFieldNumber)) {
326                         highlight_limiter_threshold = true;
327                 }
328                 if (has_active_controller(
329                         bus_idx, MIDIMappingBusProto::kMakeupGainFieldNumber, MIDIMappingProto::kMakeupGainBankFieldNumber)) {
330                         highlight_makeup_gain = true;
331                 }
332                 if (has_active_controller(
333                         bus_idx, MIDIMappingBusProto::kToggleLimiterFieldNumber, MIDIMappingProto::kToggleLimiterBankFieldNumber)) {
334                         highlight_toggle_limiter = true;
335                 }
336                 if (has_active_controller(
337                         bus_idx, MIDIMappingBusProto::kToggleAutoMakeupGainFieldNumber, MIDIMappingProto::kToggleAutoMakeupGainBankFieldNumber)) {
338                         highlight_toggle_auto_makeup_gain = true;
339                 }
340         }
341         receiver->highlight_locut(highlight_locut);
342         receiver->highlight_limiter_threshold(highlight_limiter_threshold);
343         receiver->highlight_makeup_gain(highlight_makeup_gain);
344         receiver->highlight_toggle_limiter(highlight_toggle_limiter);
345         receiver->highlight_toggle_auto_makeup_gain(highlight_toggle_auto_makeup_gain);
346
347         // Per-bus controllers.
348         for (size_t bus_idx = 0; bus_idx < size_t(mapping_proto->bus_mapping_size()); ++bus_idx) {
349                 receiver->highlight_stereo_width(bus_idx, has_active_controller(
350                         bus_idx, MIDIMappingBusProto::kStereoWidthFieldNumber, MIDIMappingProto::kStereoWidthBankFieldNumber));
351                 receiver->highlight_treble(bus_idx, has_active_controller(
352                         bus_idx, MIDIMappingBusProto::kTrebleFieldNumber, MIDIMappingProto::kTrebleBankFieldNumber));
353                 receiver->highlight_mid(bus_idx, has_active_controller(
354                         bus_idx, MIDIMappingBusProto::kMidFieldNumber, MIDIMappingProto::kMidBankFieldNumber));
355                 receiver->highlight_bass(bus_idx, has_active_controller(
356                         bus_idx, MIDIMappingBusProto::kBassFieldNumber, MIDIMappingProto::kBassBankFieldNumber));
357                 receiver->highlight_gain(bus_idx, has_active_controller(
358                         bus_idx, MIDIMappingBusProto::kGainFieldNumber, MIDIMappingProto::kGainBankFieldNumber));
359                 receiver->highlight_compressor_threshold(bus_idx, has_active_controller(
360                         bus_idx, MIDIMappingBusProto::kCompressorThresholdFieldNumber, MIDIMappingProto::kCompressorThresholdBankFieldNumber));
361                 receiver->highlight_fader(bus_idx, has_active_controller(
362                         bus_idx, MIDIMappingBusProto::kFaderFieldNumber, MIDIMappingProto::kFaderBankFieldNumber));
363                 receiver->highlight_mute(bus_idx, has_active_controller(
364                         bus_idx, MIDIMappingBusProto::kToggleMuteFieldNumber, MIDIMappingProto::kToggleMuteBankFieldNumber));
365                 receiver->highlight_toggle_locut(bus_idx, has_active_controller(
366                         bus_idx, MIDIMappingBusProto::kToggleLocutFieldNumber, MIDIMappingProto::kToggleLocutBankFieldNumber));
367                 receiver->highlight_toggle_auto_gain_staging(bus_idx, has_active_controller(
368                         bus_idx, MIDIMappingBusProto::kToggleAutoGainStagingFieldNumber, MIDIMappingProto::kToggleAutoGainStagingBankFieldNumber));
369                 receiver->highlight_toggle_compressor(bus_idx, has_active_controller(
370                         bus_idx, MIDIMappingBusProto::kToggleCompressorFieldNumber, MIDIMappingProto::kToggleCompressorBankFieldNumber));
371         }
372 }
373
374 void MIDIMapper::update_lights_lock_held()
375 {
376         if (global_audio_mixer == nullptr) {
377                 return;
378         }
379
380         set<unsigned> active_lights;  // Desired state.
381         if (current_controller_bank == 0) {
382                 activate_lights_all_buses(MIDIMappingBusProto::kBank1IsSelectedFieldNumber, &active_lights);
383         }
384         if (current_controller_bank == 1) {
385                 activate_lights_all_buses(MIDIMappingBusProto::kBank2IsSelectedFieldNumber, &active_lights);
386         }
387         if (current_controller_bank == 2) {
388                 activate_lights_all_buses(MIDIMappingBusProto::kBank3IsSelectedFieldNumber, &active_lights);
389         }
390         if (current_controller_bank == 3) {
391                 activate_lights_all_buses(MIDIMappingBusProto::kBank4IsSelectedFieldNumber, &active_lights);
392         }
393         if (current_controller_bank == 4) {
394                 activate_lights_all_buses(MIDIMappingBusProto::kBank5IsSelectedFieldNumber, &active_lights);
395         }
396         if (global_audio_mixer->get_limiter_enabled()) {
397                 activate_lights_all_buses(MIDIMappingBusProto::kLimiterIsOnFieldNumber, &active_lights);
398         }
399         if (global_audio_mixer->get_final_makeup_gain_auto()) {
400                 activate_lights_all_buses(MIDIMappingBusProto::kAutoMakeupGainIsOnFieldNumber, &active_lights);
401         }
402         unsigned num_buses = min<unsigned>(global_audio_mixer->num_buses(), mapping_proto->bus_mapping_size());
403         for (unsigned bus_idx = 0; bus_idx < num_buses; ++bus_idx) {
404                 if (global_audio_mixer->get_mute(bus_idx)) {
405                         activate_lights(bus_idx, MIDIMappingBusProto::kIsMutedFieldNumber, &active_lights);
406                 }
407                 if (global_audio_mixer->get_locut_enabled(bus_idx)) {
408                         activate_lights(bus_idx, MIDIMappingBusProto::kLocutIsOnFieldNumber, &active_lights);
409                 }
410                 if (global_audio_mixer->get_gain_staging_auto(bus_idx)) {
411                         activate_lights(bus_idx, MIDIMappingBusProto::kAutoGainStagingIsOnFieldNumber, &active_lights);
412                 }
413                 if (global_audio_mixer->get_compressor_enabled(bus_idx)) {
414                         activate_lights(bus_idx, MIDIMappingBusProto::kCompressorIsOnFieldNumber, &active_lights);
415                 }
416                 if (has_peaked[bus_idx]) {
417                         activate_lights(bus_idx, MIDIMappingBusProto::kHasPeakedFieldNumber, &active_lights);
418                 }
419         }
420
421         midi_device.update_lights(active_lights);
422 }
423
424 void MIDIMapper::activate_lights(unsigned bus_idx, int field_number, set<unsigned> *active_lights)
425 {
426         const MIDIMappingBusProto &bus_mapping = mapping_proto->bus_mapping(bus_idx);
427
428         const FieldDescriptor *descriptor = bus_mapping.GetDescriptor()->FindFieldByNumber(field_number);
429         const Reflection *bus_reflection = bus_mapping.GetReflection();
430         if (!bus_reflection->HasField(bus_mapping, descriptor)) {
431                 return;
432         }
433         const MIDILightProto &light_proto =
434                 static_cast<const MIDILightProto &>(bus_reflection->GetMessage(bus_mapping, descriptor));
435         active_lights->insert(light_proto.note_number());
436 }
437
438 void MIDIMapper::activate_lights_all_buses(int field_number, set<unsigned> *active_lights)
439 {
440         for (size_t bus_idx = 0; bus_idx < size_t(mapping_proto->bus_mapping_size()); ++bus_idx) {
441                 const MIDIMappingBusProto &bus_mapping = mapping_proto->bus_mapping(bus_idx);
442
443                 const FieldDescriptor *descriptor = bus_mapping.GetDescriptor()->FindFieldByNumber(field_number);
444                 const Reflection *bus_reflection = bus_mapping.GetReflection();
445                 if (!bus_reflection->HasField(bus_mapping, descriptor)) {
446                         continue;
447                 }
448                 const MIDILightProto &light_proto =
449                         static_cast<const MIDILightProto &>(bus_reflection->GetMessage(bus_mapping, descriptor));
450                 active_lights->insert(light_proto.note_number());
451         }
452 }