1 /*
2  * Copyright (C) 2020-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 <stdlib.h>
21 
22 #include "audio/channel.h"
23 #include "audio/group_target_track.h"
24 #include "audio/router.h"
25 #include "audio/track.h"
26 #include "utils/arrays.h"
27 #include "gui/backend/event.h"
28 #include "gui/backend/event_manager.h"
29 #include "project.h"
30 #include "utils/flags.h"
31 #include "utils/mem.h"
32 #include "utils/objects.h"
33 #include "zrythm.h"
34 #include "zrythm_app.h"
35 
36 void
group_target_track_init_loaded(Track * self)37 group_target_track_init_loaded (
38   Track * self)
39 {
40   self->children_size = (size_t) self->num_children;
41   if (self->num_children == 0)
42     {
43       self->children_size = 1;
44       self->children =
45         calloc (
46           (size_t) self->children_size, sizeof (int));
47     }
48 }
49 
50 void
group_target_track_init(Track * self)51 group_target_track_init (
52   Track * self)
53 {
54   self->children_size = 1;
55   self->children =
56     calloc (
57       (size_t) self->children_size, sizeof (int));
58 }
59 
60 /**
61  * Updates the output of the child channel (where the
62  * Channel routes to).
63  */
64 static void
update_child_output(Channel * ch,Track * output,bool recalc_graph,bool pub_events)65 update_child_output (
66   Channel * ch,
67   Track *   output,
68   bool      recalc_graph,
69   bool      pub_events)
70 {
71   g_return_if_fail (ch);
72 
73   if (ch->has_output)
74     {
75       Track * track =
76         channel_get_output_track (ch);
77       /* disconnect Channel's output from the
78        * current
79        * output channel */
80       switch (track->in_signal_type)
81         {
82         case TYPE_AUDIO:
83           port_disconnect (
84             ch->stereo_out->l,
85             track->processor->stereo_in->l);
86           port_disconnect (
87             ch->stereo_out->r,
88             track->processor->stereo_in->r);
89           break;
90         case TYPE_EVENT:
91           port_disconnect (
92             ch->midi_out,
93             track->processor->midi_in);
94           break;
95         default:
96           break;
97         }
98     }
99 
100   if (output)
101     {
102       /* connect Channel's output to the given
103        * output */
104       switch (output->in_signal_type)
105         {
106         case TYPE_AUDIO:
107           port_connect (
108             ch->stereo_out->l,
109             output->processor->stereo_in->l,
110             F_LOCKED);
111           port_connect (
112             ch->stereo_out->r,
113             output->processor->stereo_in->r,
114             F_LOCKED);
115           break;
116         case TYPE_EVENT:
117           port_connect (
118             ch->midi_out,
119             output->processor->midi_in,
120             F_LOCKED);
121           break;
122         default:
123           break;
124         }
125       ch->has_output = true;
126       ch->output_name_hash =
127         track_get_name_hash (output);
128     }
129   else
130     {
131       ch->has_output = 0;
132       ch->output_name_hash = 0;
133     }
134 
135   if (recalc_graph)
136     {
137       router_recalc_graph (ROUTER, F_NOT_SOFT);
138     }
139 
140   if (pub_events)
141     {
142       EVENTS_PUSH (ET_CHANNEL_OUTPUT_CHANGED, ch);
143     }
144 }
145 
146 static bool
contains_child(Track * self,unsigned int child_name_hash)147 contains_child (
148   Track *      self,
149   unsigned int child_name_hash)
150 {
151   for (int i = 0; i < self->num_children; i++)
152     {
153       if (self->children[i] == child_name_hash)
154         {
155           return true;
156         }
157     }
158 
159   return false;
160 }
161 
162 /**
163  * Removes a child track from the list of children.
164  */
165 void
group_target_track_remove_child(Track * self,unsigned int child_name_hash,bool disconnect,bool recalc_graph,bool pub_events)166 group_target_track_remove_child (
167   Track *      self,
168   unsigned int child_name_hash,
169   bool         disconnect,
170   bool         recalc_graph,
171   bool         pub_events)
172 {
173   g_return_if_fail (
174     child_name_hash !=
175       track_get_name_hash (self));
176   g_return_if_fail (
177     contains_child (self, child_name_hash));
178 
179   Tracklist * tracklist =
180     track_get_tracklist (self);
181 
182   Track * child =
183     tracklist_find_track_by_name_hash (
184       tracklist, child_name_hash);
185   g_return_if_fail (IS_TRACK_AND_NONNULL (child));
186   g_message (
187     "removing '%s' from '%s' - disconnect? %d",
188     child->name, self->name, disconnect);
189 
190   if (disconnect)
191     {
192       update_child_output (
193         child->channel, NULL, recalc_graph,
194         pub_events);
195     }
196   array_delete_primitive (
197     self->children, self->num_children,
198     child_name_hash);
199 
200   g_message (
201     "removed '%s' from direct out '%s' - "
202     "num children: %d",
203     child->name, self->name, self->num_children);
204 }
205 
206 /**
207  * Remove all known children.
208  *
209  * @param disconnect Also route the children to "None".
210  */
211 void
group_target_track_remove_all_children(Track * self,bool disconnect,bool recalc_graph,bool pub_events)212 group_target_track_remove_all_children (
213   Track * self,
214   bool    disconnect,
215   bool    recalc_graph,
216   bool    pub_events)
217 {
218   for (int i = self->num_children - 1; i >= 0; i--)
219     {
220       group_target_track_remove_child (
221         self, self->children[i], disconnect,
222         recalc_graph, pub_events);
223     }
224 }
225 
226 bool
group_target_track_validate(Track * self)227 group_target_track_validate (
228   Track * self)
229 {
230   for (int i = 0; i < self->num_children; i++)
231     {
232       Track * track =
233         tracklist_find_track_by_name_hash (
234           TRACKLIST, self->children[i]);
235       g_return_val_if_fail (
236         IS_TRACK_AND_NONNULL (track), false);
237       Track * out_track =
238         channel_get_output_track (track->channel);
239       g_return_val_if_fail (
240         self == out_track, false);
241     }
242 
243   return true;
244 }
245 
246 /**
247  * Adds a child track to the list of children.
248  *
249  * @param connect Connect the child to the group track.
250  */
251 void
group_target_track_add_child(Track * self,unsigned int child_name_hash,bool connect,bool recalc_graph,bool pub_events)252 group_target_track_add_child (
253   Track *      self,
254   unsigned int child_name_hash,
255   bool         connect,
256   bool         recalc_graph,
257   bool         pub_events)
258 {
259   g_return_if_fail (
260     IS_TRACK (self) && self->children);
261 
262   g_debug (
263     "adding child track with name hash %u to "
264     "group %s",
265     child_name_hash, self->name);
266 
267   if (connect)
268     {
269       Tracklist * tracklist =
270         track_get_tracklist (self);
271       Track * out_track =
272         tracklist_find_track_by_name_hash (
273           tracklist, child_name_hash);
274       g_return_if_fail (
275         IS_TRACK_AND_NONNULL (out_track)
276         && out_track->channel);
277       update_child_output (
278         out_track->channel, self,
279         recalc_graph, pub_events);
280     }
281 
282   array_double_size_if_full (
283     self->children, self->num_children,
284     self->children_size, unsigned int);
285   array_append (
286     self->children, self->num_children,
287     child_name_hash);
288 }
289 
290 void
group_target_track_add_children(Track * self,unsigned int * children,int num_children,bool connect,bool recalc_graph,bool pub_events)291 group_target_track_add_children (
292   Track *        self,
293   unsigned int * children,
294   int            num_children,
295   bool           connect,
296   bool           recalc_graph,
297   bool           pub_events)
298 {
299   for (int i = 0; i < num_children; i++)
300     {
301       group_target_track_add_child (
302         self, children[i], connect, recalc_graph,
303         pub_events);
304     }
305 }
306 
307 /**
308  * Returns the index of the child matching the
309  * given hash.
310  */
311 int
group_target_track_find_child(Track * self,unsigned int track_name_hash)312 group_target_track_find_child (
313   Track *      self,
314   unsigned int track_name_hash)
315 {
316   for (int i = 0; i < self->num_children; i++)
317     {
318       if (track_name_hash == self->children[i])
319         {
320           return i;
321         }
322     }
323 
324   return -1;
325 }
326