]> git.sesse.net Git - nageru/blob - futatabi/midi_mapper.cpp
Support changing overall playing speed at runtime, using a slider or a MIDI controller.
[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 <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 "defs.h"
22 #include "futatabi_midi_mapping.pb.h"
23 #include "shared/midi_mapper_util.h"
24 #include "shared/text_proto.h"
25
26 using namespace google::protobuf;
27 using namespace std;
28 using namespace std::placeholders;
29
30 MIDIMapper::MIDIMapper(ControllerReceiver *receiver)
31         : receiver(receiver), mapping_proto(new MIDIMappingProto), midi_device(this)
32 {
33 }
34
35 MIDIMapper::~MIDIMapper() {}
36
37 bool load_midi_mapping_from_file(const string &filename, MIDIMappingProto *new_mapping)
38 {
39         return load_proto_from_file(filename, new_mapping);
40 }
41
42 bool save_midi_mapping_to_file(const MIDIMappingProto &mapping_proto, const string &filename)
43 {
44         return save_proto_to_file(mapping_proto, filename);
45 }
46
47 void MIDIMapper::set_midi_mapping(const MIDIMappingProto &new_mapping)
48 {
49         lock_guard<mutex> lock(mu);
50         if (mapping_proto) {
51                 mapping_proto->CopyFrom(new_mapping);
52         } else {
53                 mapping_proto.reset(new MIDIMappingProto(new_mapping));
54         }
55
56         num_controller_banks = min(max(mapping_proto->num_controller_banks(), 1), 5);
57         current_controller_bank = 0;
58 }
59
60 void MIDIMapper::start_thread()
61 {
62         midi_device.start_thread();
63 }
64
65 const MIDIMappingProto &MIDIMapper::get_current_mapping() const
66 {
67         lock_guard<mutex> lock(mu);
68         return *mapping_proto;
69 }
70
71 ControllerReceiver *MIDIMapper::set_receiver(ControllerReceiver *new_receiver)
72 {
73         lock_guard<mutex> lock(mu);
74         swap(receiver, new_receiver);
75         return new_receiver;  // Now old receiver.
76 }
77
78 void MIDIMapper::controller_received(int controller, int value_int)
79 {
80         int delta_value = value_int - 64;  // For infinite controllers such as jog.
81         float value = map_controller_to_float(controller, value_int);
82
83         receiver->controller_changed(controller);
84
85         match_controller(controller, MIDIMappingProto::kJogFieldNumber, MIDIMappingProto::kJogBankFieldNumber,
86                 delta_value, bind(&ControllerReceiver::jog, receiver, _1));
87
88         // Speed goes from 0.0 to 2.0 (the receiver will clamp).
89         match_controller(controller, MIDIMappingProto::kMasterSpeedFieldNumber, MIDIMappingProto::kMasterSpeedBankFieldNumber,
90                 value * 2.0, bind(&ControllerReceiver::set_master_speed, receiver, _1));
91 }
92
93 void MIDIMapper::note_on_received(int note)
94 {
95         lock_guard<mutex> lock(mu);
96         receiver->note_on(note);
97
98         if (mapping_proto->has_prev_bank() &&
99             mapping_proto->prev_bank().note_number() == note) {
100                 current_controller_bank = (current_controller_bank + num_controller_banks - 1) % num_controller_banks;
101                 update_lights_lock_held();
102         }
103         if (mapping_proto->has_next_bank() &&
104             mapping_proto->next_bank().note_number() == note) {
105                 current_controller_bank = (current_controller_bank + 1) % num_controller_banks;
106                 update_lights_lock_held();
107         }
108         if (mapping_proto->has_select_bank_1() &&
109             mapping_proto->select_bank_1().note_number() == note) {
110                 current_controller_bank = 0;
111                 update_lights_lock_held();
112         }
113         if (mapping_proto->has_select_bank_2() &&
114             mapping_proto->select_bank_2().note_number() == note &&
115             num_controller_banks >= 2) {
116                 current_controller_bank = 1;
117                 update_lights_lock_held();
118         }
119         if (mapping_proto->has_select_bank_3() &&
120             mapping_proto->select_bank_3().note_number() == note &&
121             num_controller_banks >= 3) {
122                 current_controller_bank = 2;
123                 update_lights_lock_held();
124         }
125         if (mapping_proto->has_select_bank_4() &&
126             mapping_proto->select_bank_4().note_number() == note &&
127             num_controller_banks >= 4) {
128                 current_controller_bank = 3;
129                 update_lights_lock_held();
130         }
131         if (mapping_proto->has_select_bank_5() &&
132             mapping_proto->select_bank_5().note_number() == note &&
133             num_controller_banks >= 5) {
134                 current_controller_bank = 4;
135                 update_lights_lock_held();
136         }
137
138         match_button(note, MIDIMappingProto::kPreviewFieldNumber, MIDIMappingProto::kPreviewBankFieldNumber,
139                 bind(&ControllerReceiver::preview, receiver));
140         match_button(note, MIDIMappingProto::kQueueFieldNumber, MIDIMappingProto::kQueueBankFieldNumber,
141                 bind(&ControllerReceiver::queue, receiver));
142         match_button(note, MIDIMappingProto::kPlayFieldNumber, MIDIMappingProto::kPlayBankFieldNumber,
143                 bind(&ControllerReceiver::play, receiver));
144
145         unsigned num_cameras = std::min(MAX_STREAMS, mapping_proto->camera_size());
146         for (unsigned camera_idx = 0; camera_idx < num_cameras; ++camera_idx) {
147                 const CameraMIDIMappingProto &camera = mapping_proto->camera(camera_idx);
148                 if (match_bank_helper(camera, CameraMIDIMappingProto::kBankFieldNumber, current_controller_bank) &&
149                     match_button_helper(camera, CameraMIDIMappingProto::kButtonFieldNumber, note)) {
150                         receiver->switch_camera(camera_idx);
151                 }
152         }
153
154         match_button(note, MIDIMappingProto::kCueInFieldNumber, MIDIMappingProto::kCueInBankFieldNumber,
155                 bind(&ControllerReceiver::cue_in, receiver));
156         match_button(note, MIDIMappingProto::kCueOutFieldNumber, MIDIMappingProto::kCueOutBankFieldNumber,
157                 bind(&ControllerReceiver::cue_out, receiver));
158 }
159
160 void MIDIMapper::match_controller(int controller, int field_number, int bank_field_number, float value, function<void(float)> func)
161 {
162         if (bank_mismatch(bank_field_number)) {
163                 return;
164         }
165
166         if (match_controller_helper(*mapping_proto, field_number, controller)) {
167                 func(value);
168         }
169 }
170
171 void MIDIMapper::match_button(int note, int field_number, int bank_field_number, function<void()> func)
172 {
173         if (bank_mismatch(bank_field_number)) {
174                 return;
175         }
176
177         if (match_button_helper(*mapping_proto, field_number, note)) {
178                 func();
179         }
180 }
181
182 bool MIDIMapper::has_active_controller(int field_number, int bank_field_number)
183 {
184         if (bank_mismatch(bank_field_number)) {
185                 return false;
186         }
187
188         const FieldDescriptor *descriptor = mapping_proto->GetDescriptor()->FindFieldByNumber(field_number);
189         const Reflection *reflection = mapping_proto->GetReflection();
190         return reflection->HasField(*mapping_proto, descriptor);
191 }
192
193 bool MIDIMapper::bank_mismatch(int bank_field_number)
194 {
195         return !match_bank_helper(*mapping_proto, bank_field_number, current_controller_bank);
196 }
197
198 void MIDIMapper::refresh_lights()
199 {
200         lock_guard<mutex> lock(mu);
201         update_lights_lock_held();
202 }
203
204 void MIDIMapper::update_lights_lock_held()
205 {
206         set<unsigned> active_lights;  // Desired state.
207         if (current_controller_bank == 0) {
208                 activate_mapped_light(*mapping_proto, MIDIMappingProto::kBank1IsSelectedFieldNumber, &active_lights);
209         }
210         if (current_controller_bank == 1) {
211                 activate_mapped_light(*mapping_proto, MIDIMappingProto::kBank2IsSelectedFieldNumber, &active_lights);
212         }
213         if (current_controller_bank == 2) {
214                 activate_mapped_light(*mapping_proto, MIDIMappingProto::kBank3IsSelectedFieldNumber, &active_lights);
215         }
216         if (current_controller_bank == 3) {
217                 activate_mapped_light(*mapping_proto, MIDIMappingProto::kBank4IsSelectedFieldNumber, &active_lights);
218         }
219         if (current_controller_bank == 4) {
220                 activate_mapped_light(*mapping_proto, MIDIMappingProto::kBank5IsSelectedFieldNumber, &active_lights);
221         }
222         if (preview_enabled_light) {
223                 activate_mapped_light(*mapping_proto, MIDIMappingProto::kPreviewEnabledFieldNumber, &active_lights);
224         }
225         if (queue_enabled_light) {
226                 activate_mapped_light(*mapping_proto, MIDIMappingProto::kQueueEnabledFieldNumber, &active_lights);
227         }
228         if (play_enabled_light) {
229                 activate_mapped_light(*mapping_proto, MIDIMappingProto::kPlayEnabledFieldNumber, &active_lights);
230         }
231         if (current_highlighted_camera >= 0 && current_highlighted_camera < mapping_proto->camera_size()) {
232                 const CameraMIDIMappingProto &camera = mapping_proto->camera(current_highlighted_camera);
233                 activate_mapped_light(camera, CameraMIDIMappingProto::kIsCurrentFieldNumber, &active_lights);
234         }
235
236         // These are always enabled right now.
237         activate_mapped_light(*mapping_proto, MIDIMappingProto::kCueInFieldNumber, &active_lights);
238         activate_mapped_light(*mapping_proto, MIDIMappingProto::kCueOutFieldNumber, &active_lights);
239
240         midi_device.update_lights(active_lights);
241 }