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 this program.  If not, see <https://www.gnu.org/licenses/>.
18  */
19 
20 #include "gui/backend/event.h"
21 #include "gui/backend/event_manager.h"
22 #include "gui/backend/mixer_selections.h"
23 #include "gui/widgets/center_dock.h"
24 #include "gui/widgets/left_dock_edge.h"
25 #include "gui/widgets/main_window.h"
26 #include "project.h"
27 #include "utils/arrays.h"
28 #include "utils/error.h"
29 #include "utils/flags.h"
30 #include "utils/objects.h"
31 #include "utils/yaml.h"
32 #include "zrythm_app.h"
33 
34 #include <gtk/gtk.h>
35 #include <glib/gi18n.h>
36 
37 void
mixer_selections_init_loaded(MixerSelections * self,bool is_project)38 mixer_selections_init_loaded (
39   MixerSelections * self,
40   bool              is_project)
41 {
42   if (!is_project)
43     {
44       for (int i = 0; i < self->num_slots; i++)
45         {
46           plugin_init_loaded (
47             self->plugins[i], NULL, self);
48         }
49     }
50 
51   /* sort the selections */
52   mixer_selections_sort (self, F_ASCENDING);
53 }
54 
55 void
mixer_selections_init(MixerSelections * self)56 mixer_selections_init (
57   MixerSelections * self)
58 {
59   self->schema_version =
60     MIXER_SELECTIONS_SCHEMA_VERSION;
61 }
62 
63 MixerSelections *
mixer_selections_new(void)64 mixer_selections_new (void)
65 {
66   MixerSelections * self =
67     object_new (MixerSelections);
68 
69   mixer_selections_init (self);
70 
71   return self;
72 }
73 
74 /**
75  * Returns if there are any selections.
76  */
77 int
mixer_selections_has_any(MixerSelections * self)78 mixer_selections_has_any (
79   MixerSelections * self)
80 {
81   return self->has_any;
82 }
83 
84 static int
sort_plugins_asc_func(const void * _a,const void * _b)85 sort_plugins_asc_func (
86   const void * _a,
87   const void * _b)
88 {
89   Plugin * a =
90     *(Plugin * const *) _a;
91   Plugin * b =
92     *(Plugin * const *)_b;
93   return a->id.slot - b->id.slot;
94 }
95 
96 static int
sort_plugins_desc_func(const void * _a,const void * _b)97 sort_plugins_desc_func (
98   const void * _a,
99   const void * _b)
100 {
101   return - sort_plugins_asc_func (_a, _b);
102 }
103 
104 static int
sort_slots_asc_func(const void * _a,const void * _b)105 sort_slots_asc_func (
106   const void * _a,
107   const void * _b)
108 {
109   int a = *(int const *) _a;
110   int b = *(int const *) _b;
111   return a - b;
112 }
113 
114 static int
sort_slots_desc_func(const void * _a,const void * _b)115 sort_slots_desc_func (
116   const void * _a,
117   const void * _b)
118 {
119   return - sort_slots_asc_func (_a, _b);
120 }
121 
122 /**
123  * Sorts the selections by slot index.
124  *
125  * @param asc Ascending or not.
126  */
127 void
mixer_selections_sort(MixerSelections * self,bool asc)128 mixer_selections_sort (
129   MixerSelections * self,
130   bool              asc)
131 {
132   if (!self->has_any)
133     {
134       return;
135     }
136 
137   qsort (
138     self->slots, (size_t) self->num_slots,
139     sizeof (int),
140     asc ?
141       sort_slots_asc_func : sort_slots_desc_func);
142   qsort (
143     self->plugins, (size_t) self->num_slots,
144     sizeof (Plugin *),
145     asc ?
146       sort_plugins_asc_func :
147       sort_plugins_desc_func);
148 }
149 
150 /**
151  * Gets highest slot in the selections.
152  */
153 int
mixer_selections_get_highest_slot(MixerSelections * ms)154 mixer_selections_get_highest_slot (
155   MixerSelections * ms)
156 {
157   int min = STRIP_SIZE;
158 
159   for (int i = 0; i < ms->num_slots; i++)
160     {
161       if (ms->slots[i] < min)
162         min = ms->slots[i];
163     }
164 
165   g_warn_if_fail (min < STRIP_SIZE);
166 
167   return min;
168 }
169 
170 /**
171  * Gets lowest slot in the selections.
172  */
173 int
mixer_selections_get_lowest_slot(MixerSelections * ms)174 mixer_selections_get_lowest_slot (
175   MixerSelections * ms)
176 {
177   int max = -1;
178 
179   for (int i = 0; i < ms->num_slots; i++)
180     {
181       if (ms->slots[i] > max)
182         max = ms->slots[i];
183     }
184 
185   g_warn_if_fail (max > -1);
186 
187   return max;
188 }
189 
190 /**
191  * Get current Track.
192  */
193 Track *
mixer_selections_get_track(MixerSelections * self)194 mixer_selections_get_track (
195   MixerSelections * self)
196 {
197   if (!self->has_any)
198     return NULL;
199 
200   Track * track =
201     tracklist_find_track_by_name_hash (
202       TRACKLIST, self->track_name_hash);
203   g_return_val_if_fail (track, NULL);
204   return track;
205 }
206 
207 /**
208  * Adds a slot to the selections.
209  *
210  * The selections can only be from one channel.
211  *
212  * @param track The track.
213  * @param slot The slot to add to the selections.
214  * @param clone_pl Whether to clone the plugin
215  *   when storing it in \ref
216  *   MixerSelections.plugins. Used in some actions.
217  */
218 void
mixer_selections_add_slot(MixerSelections * ms,Track * track,PluginSlotType type,int slot,bool clone_pl)219 mixer_selections_add_slot (
220   MixerSelections * ms,
221   Track *           track,
222   PluginSlotType    type,
223   int               slot,
224   bool              clone_pl)
225 {
226   unsigned int name_hash =
227     track_get_name_hash (track);
228 
229   if (!ms->has_any
230       || name_hash != ms->track_name_hash
231       || type != ms->type)
232     {
233       mixer_selections_clear (
234         ms, F_NO_PUBLISH_EVENTS);
235     }
236   ms->track_name_hash = name_hash;
237   ms->type = type;
238   ms->has_any = true;
239 
240   g_debug ("adding slot %s:%s:%d",
241     track->name,
242     plugin_slot_type_strings[type].str, slot);
243 
244   Plugin * pl = NULL;
245   if (!array_contains_int (
246         ms->slots, ms->num_slots, slot))
247     {
248       switch (type)
249         {
250         case PLUGIN_SLOT_MIDI_FX:
251           pl = track->channel->midi_fx[slot];
252           break;
253         case PLUGIN_SLOT_INSERT:
254           pl = track->channel->inserts[slot];
255           break;
256         case PLUGIN_SLOT_MODULATOR:
257           pl = track->modulators[slot];
258           break;
259         case PLUGIN_SLOT_INSTRUMENT:
260           pl = track->channel->instrument;
261           break;
262         default:
263           break;
264         }
265       g_return_if_fail (pl);
266 
267       Plugin * pl_to_append = pl;
268       if (clone_pl)
269         {
270           GError * err = NULL;
271           pl_to_append =
272             plugin_clone (pl, &err);
273           if (!pl_to_append)
274             {
275               /* FIXME handle properly */
276               g_critical (
277                 "failed to clone plugin: %s",
278                 err->message);
279               return;
280             }
281         }
282 
283       array_double_append (
284         ms->slots, ms->plugins, ms->num_slots,
285         slot, pl_to_append);
286     }
287 
288   if (pl && plugin_is_in_active_project (pl))
289     {
290       EVENTS_PUSH (
291         ET_MIXER_SELECTIONS_CHANGED, pl);
292     }
293 }
294 
295 /**
296  * Removes a slot from the selections.
297  *
298  * Assumes that the channel is the one already
299  * selected.
300  */
301 void
mixer_selections_remove_slot(MixerSelections * ms,int slot,PluginSlotType type,bool publish_events)302 mixer_selections_remove_slot (
303   MixerSelections * ms,
304   int               slot,
305   PluginSlotType    type,
306   bool              publish_events)
307 {
308   g_message ("removing slot %d", slot);
309   array_delete_primitive (
310     ms->slots, ms->num_slots, slot);
311 
312   if (ms->num_slots == 0 ||
313       ms->type == PLUGIN_SLOT_INSTRUMENT)
314     {
315       ms->has_any = 0;
316       ms->track_name_hash = 0;
317     }
318 
319   if (ZRYTHM_HAVE_UI && publish_events)
320     {
321       EVENTS_PUSH (ET_MIXER_SELECTIONS_CHANGED,
322                    NULL);
323     }
324 }
325 
326 
327 /**
328  * Returns if the slot is selected or not.
329  */
330 bool
mixer_selections_contains_slot(MixerSelections * self,PluginSlotType type,int slot)331 mixer_selections_contains_slot (
332   MixerSelections * self,
333   PluginSlotType    type,
334   int               slot)
335 {
336   if (type != self->type)
337     return false;
338 
339   if (type == PLUGIN_SLOT_INSTRUMENT)
340     {
341       return self->has_any;
342     }
343   else
344     {
345       for (int i = 0; i < self->num_slots; i++)
346         {
347           if (self->slots[i] == slot)
348             {
349               return true;
350             }
351         }
352     }
353 
354   return false;
355 }
356 
357 /**
358  * Returns if the plugin is selected or not.
359  */
360 bool
mixer_selections_contains_plugin(MixerSelections * ms,Plugin * pl)361 mixer_selections_contains_plugin (
362   MixerSelections * ms,
363   Plugin *          pl)
364 {
365   g_return_val_if_fail (ms && IS_PLUGIN (pl), false);
366 
367   if (ms->track_name_hash != pl->id.track_name_hash)
368     return false;
369 
370   if (ms->type == PLUGIN_SLOT_INSTRUMENT)
371     {
372       if (pl->id.slot_type ==
373             PLUGIN_SLOT_INSTRUMENT &&
374           pl->id.track_name_hash ==
375             ms->track_name_hash)
376         {
377           return true;
378         }
379     }
380   else
381     {
382       for (int i = 0; i < ms->num_slots; i++)
383         {
384           if (ms->slots[i] == pl->id.slot &&
385               ms->type == pl->id.slot_type)
386             return true;
387         }
388     }
389 
390   return false;
391 }
392 
393 /**
394  * Returns the first selected plugin if any is
395  * selected, otherwise NULL.
396  */
397 Plugin *
mixer_selections_get_first_plugin(MixerSelections * self)398 mixer_selections_get_first_plugin (
399   MixerSelections * self)
400 {
401   if (self->has_any)
402     {
403       Track * track =
404         mixer_selections_get_track (self);
405       g_return_val_if_fail (track, NULL);
406       switch (self->type)
407         {
408         case PLUGIN_SLOT_INSTRUMENT:
409           return track->channel->instrument;
410         case PLUGIN_SLOT_INSERT:
411           return
412             track->channel->inserts[
413               self->slots[0]];
414         case PLUGIN_SLOT_MIDI_FX:
415           return
416             track->channel->midi_fx[
417               self->slots[0]];
418         case PLUGIN_SLOT_MODULATOR:
419           return
420             track->modulators[self->slots[0]];
421         default:
422           g_return_val_if_reached (NULL);
423           break;
424         }
425     }
426 
427   return NULL;
428 }
429 
430 bool
mixer_selections_validate(MixerSelections * self)431 mixer_selections_validate (
432   MixerSelections * self)
433 {
434   if (!self->has_any)
435     return true;
436 
437   Track * track =
438     mixer_selections_get_track (self);
439   g_return_val_if_fail (
440     IS_TRACK_AND_NONNULL (track), false);
441 
442   for (int i = 0; i < self->num_slots; i++)
443     {
444       Plugin * pl = NULL;
445       switch (self->type)
446         {
447         case PLUGIN_SLOT_INSTRUMENT:
448           pl = track->channel->instrument;
449           break;
450         case PLUGIN_SLOT_INSERT:
451           pl =
452             track->channel->inserts[
453               self->slots[i]];
454           break;
455         case PLUGIN_SLOT_MIDI_FX:
456           pl =
457             track->channel->midi_fx[
458               self->slots[i]];
459           break;
460         case PLUGIN_SLOT_MODULATOR:
461           pl = track->modulators[self->slots[i]];
462           break;
463         default:
464           g_return_val_if_reached (false);
465           break;
466         }
467 
468       g_return_val_if_fail (
469         IS_PLUGIN_AND_NONNULL (pl), false);
470     }
471 
472   return true;
473 }
474 
475 /**
476  * Clears selections.
477  */
478 void
mixer_selections_clear(MixerSelections * self,const int pub_events)479 mixer_selections_clear (
480   MixerSelections * self,
481   const int         pub_events)
482 {
483   int i;
484 
485   for (i = self->num_slots - 1; i >= 0; i--)
486     {
487       mixer_selections_remove_slot (
488         self, self->slots[i], self->type, 0);
489     }
490 
491   self->has_any = false;
492   self->track_name_hash = 0;
493 
494   if (pub_events)
495     {
496       EVENTS_PUSH (ET_MIXER_SELECTIONS_CHANGED,
497                    NULL);
498     }
499 
500   g_message ("cleared mixer selections");
501 }
502 
503 /**
504  * Clone the struct for copying, undoing, etc.
505  *
506  * @bool src_is_project Whether \ref src are the
507  *   project selections.
508  */
509 MixerSelections *
mixer_selections_clone(MixerSelections * src,bool src_is_project)510 mixer_selections_clone (
511   MixerSelections * src,
512   bool              src_is_project)
513 {
514   MixerSelections * ms =
515     mixer_selections_new ();
516 
517   int i;
518 
519   for (i = 0; i < src->num_slots; i++)
520     {
521       Plugin * pl = NULL;
522       if (src_is_project)
523         {
524           Track * track =
525             tracklist_find_track_by_name_hash (
526               TRACKLIST, src->track_name_hash);
527           pl =
528             track_get_plugin_at_slot (
529               track,
530               src->type, src->slots[i]);
531           g_return_val_if_fail (
532             IS_PLUGIN (pl), NULL);
533         }
534       else
535         {
536           pl = src->plugins[i];
537         }
538 
539       GError * err = NULL;
540       ms->plugins[i] =
541         plugin_clone (pl, &err);
542       if (!ms->plugins[i])
543         {
544           g_warning (
545             "failed to clone plugin %s: %s",
546             pl->setting->descr->name, err->message);
547           g_error_free_and_null (err);
548           return NULL;
549         }
550       ms->slots[i] = src->slots[i];
551     }
552 
553   ms->type = src->type;
554   ms->num_slots = src->num_slots;
555   ms->has_any = src->has_any;
556   ms->track_name_hash = src->track_name_hash;
557 
558   return ms;
559 }
560 
561 void
mixer_selections_post_deserialize(MixerSelections * self)562 mixer_selections_post_deserialize (
563   MixerSelections * self)
564 {
565   for (int i = 0; i < self->num_slots; i++)
566     {
567       plugin_init_loaded (
568         self->plugins[i], NULL, self);
569     }
570 
571   /* sort the selections */
572   mixer_selections_sort (self, F_ASCENDING);
573 }
574 
575 /**
576  * Returns whether the selections can be pasted to
577  * MixerWidget.paste_slot.
578  */
579 bool
mixer_selections_can_be_pasted(MixerSelections * self,Channel * ch,PluginSlotType type,int slot)580 mixer_selections_can_be_pasted (
581   MixerSelections * self,
582   Channel *         ch,
583   PluginSlotType    type,
584   int               slot)
585 {
586   int lowest =
587     mixer_selections_get_lowest_slot (self);
588   int highest =
589     mixer_selections_get_highest_slot (self);
590   int delta = lowest - highest;
591 
592   return slot + delta < STRIP_SIZE;
593 }
594 
595 /**
596  * Paste the selections starting at the slot in the
597  * given channel.
598  */
599 void
mixer_selections_paste_to_slot(MixerSelections * ms,Channel * ch,PluginSlotType type,int slot)600 mixer_selections_paste_to_slot (
601   MixerSelections * ms,
602   Channel *         ch,
603   PluginSlotType    type,
604   int               slot)
605 {
606   GError * err = NULL;
607   bool ret =
608     mixer_selections_action_perform_paste (
609       ms, PORT_CONNECTIONS_MGR,
610       type,
611       track_get_name_hash (ch->track),
612       slot, &err);
613   if (!ret)
614     {
615       HANDLE_ERROR (
616         err, "%s",
617         _("Failed to paste plugins"));
618     }
619 }
620 
621 void
mixer_selections_free(MixerSelections * self)622 mixer_selections_free (MixerSelections * self)
623 {
624   free (self);
625 }
626