1 /*
2 * Copyright (C) 2019-2021 Alexandros Theodotou <alex at zrythm dot org>
3 *
4 * This file is part of Zrythm
5 *
6 * Zrythm is free software: you can redistribute it and/or modify
7 * it under the terms of the GNU Affero General Public License as published by
8 * the Free Software Foundation, either version 3 of the License, or
9 * (at your option) any later version.
10 *
11 * Zrythm is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU Affero General Public License for more details.
15 *
16 * You should have received a copy of the GNU Affero General Public License
17 * along with Zrythm. If not, see <https://www.gnu.org/licenses/>.
18 */
19
20 #include "audio/control_port.h"
21 #include "audio/midi_event.h"
22 #include "audio/midi_mapping.h"
23 #include "gui/backend/event.h"
24 #include "gui/backend/event_manager.h"
25 #include "project.h"
26 #include "utils/arrays.h"
27 #include "utils/flags.h"
28 #include "utils/objects.h"
29 #include "zrythm_app.h"
30
31 /**
32 * Initializes the MidiMappings after a Project
33 * is loaded.
34 */
35 void
midi_mappings_init_loaded(MidiMappings * self)36 midi_mappings_init_loaded (
37 MidiMappings * self)
38 {
39 self->mappings_size = (size_t) self->num_mappings;
40
41 for (int i = 0; i < self->num_mappings; i++)
42 {
43 MidiMapping * mapping = self->mappings[i];
44 mapping->dest =
45 port_find_from_identifier (
46 &mapping->dest_id);
47 }
48 }
49
50 /**
51 * Binds the CC represented by the given raw buffer
52 * (must be size 3) to the given Port.
53 *
54 * @param idx Index to insert at.
55 * @param buf The buffer used for matching at [0] and
56 * [1].
57 * @param device_port Device port, if custom mapping.
58 */
59 void
midi_mappings_bind_at(MidiMappings * self,midi_byte_t * buf,ExtPort * device_port,Port * dest_port,int idx,bool fire_events)60 midi_mappings_bind_at (
61 MidiMappings * self,
62 midi_byte_t * buf,
63 ExtPort * device_port,
64 Port * dest_port,
65 int idx,
66 bool fire_events)
67 {
68 g_return_if_fail (
69 self && buf && dest_port);
70
71 array_double_size_if_full (
72 self->mappings, self->num_mappings,
73 self->mappings_size, MidiMapping);
74
75 /* if inserting, move later mappings further */
76 for (int i = self->num_mappings; i > idx; i--)
77 {
78 self->mappings[i] = self->mappings[i - 1];
79 }
80 self->num_mappings++;
81
82 MidiMapping * mapping = midi_mapping_new ();
83 self->mappings[idx] = mapping;
84
85 memcpy (
86 mapping->key, buf, sizeof (midi_byte_t) * 3);
87 if (device_port)
88 {
89 mapping->device_port =
90 ext_port_clone (device_port);
91 }
92 mapping->dest_id = dest_port->id;
93 mapping->dest = dest_port;
94 g_atomic_int_set (
95 &mapping->enabled, (guint) true);
96
97 char str[100];
98 midi_ctrl_change_get_ch_and_description (
99 buf, str);
100
101 if (!(dest_port->id.flags &
102 PORT_FLAG_MIDI_AUTOMATABLE))
103 {
104 g_message (
105 "bounded MIDI mapping from %s to %s",
106 str, dest_port->id.label);
107 }
108
109 if (fire_events && ZRYTHM_HAVE_UI)
110 {
111 EVENTS_PUSH (ET_MIDI_BINDINGS_CHANGED, NULL);
112 }
113 }
114
115 /**
116 * Unbinds the given binding.
117 *
118 * @note This must be called inside a port operation
119 * lock, such as inside an undoable action.
120 */
121 void
midi_mappings_unbind(MidiMappings * self,int idx,bool fire_events)122 midi_mappings_unbind (
123 MidiMappings * self,
124 int idx,
125 bool fire_events)
126 {
127 g_return_if_fail (self && idx >= 0);
128
129 MidiMapping * mapping_before =
130 self->mappings[idx];
131
132 for (int i = self->num_mappings - 2;
133 i >= idx; i--)
134 {
135 self->mappings[i] = self->mappings[i + 1];
136 }
137 self->num_mappings--;
138
139 object_free_w_func_and_null (
140 midi_mapping_free, mapping_before);
141
142 if (fire_events && ZRYTHM_HAVE_UI)
143 {
144 EVENTS_PUSH (ET_MIDI_BINDINGS_CHANGED, NULL);
145 }
146 }
147
148 void
midi_mapping_set_enabled(MidiMapping * self,bool enabled)149 midi_mapping_set_enabled (
150 MidiMapping * self,
151 bool enabled)
152 {
153 g_atomic_int_set (
154 &self->enabled, (guint) enabled);
155 }
156
157 int
midi_mapping_get_index(MidiMappings * self,MidiMapping * mapping)158 midi_mapping_get_index (
159 MidiMappings * self,
160 MidiMapping * mapping)
161 {
162 for (int i = 0; i < self->num_mappings; i++)
163 {
164 if (self->mappings[i] == mapping)
165 return i;
166 }
167 g_return_val_if_reached (-1);
168 }
169
170 MidiMapping *
midi_mapping_new(void)171 midi_mapping_new (void)
172 {
173 MidiMapping * self = object_new (MidiMapping);
174 self->schema_version = MIDI_MAPPING_SCHEMA_VERSION;
175
176 return self;
177 }
178
179 MidiMapping *
midi_mapping_clone(const MidiMapping * src)180 midi_mapping_clone (
181 const MidiMapping * src)
182 {
183 MidiMapping * self = object_new (MidiMapping);
184 self->schema_version = MIDI_MAPPING_SCHEMA_VERSION;
185
186 memcpy (
187 &self->key[0], &src->key[0],
188 3 * sizeof (midi_byte_t));
189 if (src->device_port)
190 self->device_port =
191 ext_port_clone (src->device_port);
192 port_identifier_copy (
193 &self->dest_id, &src->dest_id);
194 g_atomic_int_set (
195 &self->enabled,
196 (guint) g_atomic_int_get (&src->enabled));
197
198 return self;
199 }
200
201 void
midi_mapping_free(MidiMapping * self)202 midi_mapping_free (
203 MidiMapping * self)
204 {
205 object_free_w_func_and_null (
206 ext_port_free, self->device_port);
207
208 object_zero_and_free (self);
209 }
210
211 static void
apply_mapping(MidiMapping * mapping,midi_byte_t * buf)212 apply_mapping (
213 MidiMapping * mapping,
214 midi_byte_t * buf)
215 {
216 g_return_if_fail (mapping->dest);
217
218 Port * dest = mapping->dest;
219 if (dest->id.type == TYPE_CONTROL)
220 {
221 /* if toggle, reverse value */
222 if (dest->id.flags & PORT_FLAG_TOGGLE)
223 {
224 control_port_set_toggled (
225 dest,
226 !control_port_is_toggled (
227 dest),
228 F_PUBLISH_EVENTS);
229 }
230 /* else if not toggle set the control
231 * value received */
232 else
233 {
234 float normalized_val =
235 (float) buf[2] / 127.f;
236 port_set_control_value (
237 dest, normalized_val,
238 F_NORMALIZED, F_PUBLISH_EVENTS);
239 }
240 }
241 else if (dest->id.type == TYPE_EVENT)
242 {
243 if (dest->id.flags2 &
244 PORT_FLAG2_TRANSPORT_ROLL)
245 {
246 transport_request_roll (TRANSPORT);
247 }
248 else if (dest->id.flags2 &
249 PORT_FLAG2_TRANSPORT_STOP)
250 {
251 transport_request_pause (
252 TRANSPORT);
253 }
254 else if (dest->id.flags2 &
255 PORT_FLAG2_TRANSPORT_BACKWARD)
256 {
257 transport_move_backward (
258 TRANSPORT);
259 }
260 else if (dest->id.flags2 &
261 PORT_FLAG2_TRANSPORT_FORWARD)
262 {
263 transport_move_forward (TRANSPORT);
264 }
265 else if (dest->id.flags2 &
266 PORT_FLAG2_TRANSPORT_LOOP_TOGGLE)
267 {
268 transport_set_loop (
269 TRANSPORT, !TRANSPORT->loop);
270 }
271 else if (dest->id.flags2 &
272 PORT_FLAG2_TRANSPORT_REC_TOGGLE)
273 {
274 transport_set_recording (
275 TRANSPORT,
276 !TRANSPORT->recording,
277 F_PUBLISH_EVENTS);
278 }
279 }
280 }
281
282 /**
283 * Applies the events to the appropriate mapping.
284 *
285 * This is used only for TrackProcessor.cc_mappings.
286 *
287 * @note Must only be called while transport is
288 * recording.
289 */
290 void
midi_mappings_apply_from_cc_events(MidiMappings * self,MidiEvents * events,bool queued)291 midi_mappings_apply_from_cc_events (
292 MidiMappings * self,
293 MidiEvents * events,
294 bool queued)
295 {
296 /* queued not implemented yet */
297 g_return_if_fail (!queued);
298
299 for (int i = 0; i < events->num_events; i++)
300 {
301 MidiEvent * ev = &events->events[i];
302 if (ev->raw_buffer[0] >=
303 (midi_byte_t) MIDI_CH1_CTRL_CHANGE &&
304 ev->raw_buffer[0] <=
305 (midi_byte_t)
306 (MIDI_CH1_CTRL_CHANGE | 15))
307 {
308 midi_byte_t channel =
309 (midi_byte_t)
310 ((ev->raw_buffer[0] & 0xf) + 1);
311 midi_byte_t controller = ev->raw_buffer[1];
312 MidiMapping * mapping =
313 self->mappings[
314 (channel - 1) * 128 + controller];
315 apply_mapping (
316 mapping, ev->raw_buffer);
317 }
318 }
319 }
320
321 /**
322 * Applies the given buffer to the matching ports.
323 */
324 void
midi_mappings_apply(MidiMappings * self,midi_byte_t * buf)325 midi_mappings_apply (
326 MidiMappings * self,
327 midi_byte_t * buf)
328 {
329 for (int i = 0; i < self->num_mappings; i++)
330 {
331 MidiMapping * mapping = self->mappings[i];
332
333 if (g_atomic_int_get (&mapping->enabled) &&
334 mapping->key[0] == buf[0] &&
335 mapping->key[1] == buf[1])
336 {
337 apply_mapping (mapping, buf);
338 }
339 }
340 }
341
342 /**
343 * Returns a newly allocated MidiMappings.
344 */
345 MidiMappings *
midi_mappings_new()346 midi_mappings_new ()
347 {
348 MidiMappings * self = object_new (MidiMappings);
349 self->schema_version =
350 MIDI_MAPPINGS_SCHEMA_VERSION;
351
352 self->mappings_size = 4;
353 self->mappings =
354 object_new_n (self->mappings_size, MidiMapping);
355
356 return self;
357 }
358
359 /**
360 * Get MIDI mappings for the given port.
361 *
362 * @param size Size to set.
363 *
364 * @return a newly allocated array that must be
365 * free'd with free().
366 */
367 MidiMapping **
midi_mappings_get_for_port(MidiMappings * self,Port * dest_port,int * count)368 midi_mappings_get_for_port (
369 MidiMappings * self,
370 Port * dest_port,
371 int * count)
372 {
373 g_return_val_if_fail (self && dest_port, NULL);
374
375 MidiMapping ** arr = NULL;
376 *count = 0;
377 for (int i = 0; i < self->num_mappings; i++)
378 {
379 MidiMapping * mapping =
380 self->mappings[i];
381 if (mapping->dest == dest_port)
382 {
383 arr =
384 g_realloc (
385 arr,
386 (size_t) (*count + 1) *
387 sizeof (MidiMapping *));
388 array_append (
389 arr, *count, mapping);
390 }
391 }
392 return arr;
393 }
394
395 MidiMappings *
midi_mappings_clone(const MidiMappings * src)396 midi_mappings_clone (
397 const MidiMappings * src)
398 {
399 MidiMappings * self = object_new (MidiMappings);
400 self->schema_version =
401 MIDI_MAPPINGS_SCHEMA_VERSION;
402
403 self->mappings =
404 object_new_n (
405 (size_t) src->num_mappings, MidiMapping);
406 for (int i = 0; i < src->num_mappings; i++)
407 {
408 self->mappings[i] =
409 midi_mapping_clone (src->mappings[i]);
410 }
411 self->num_mappings = src->num_mappings;
412
413 return self;
414 }
415
416 void
midi_mappings_free(MidiMappings * self)417 midi_mappings_free (
418 MidiMappings * self)
419 {
420 for (int i = 0; i < self->num_mappings; i++)
421 {
422 object_free_w_func_and_null (
423 midi_mapping_free, self->mappings[i]);
424 }
425 object_zero_and_free (self->mappings);
426
427 object_zero_and_free (self);
428 }
429