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