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