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 "actions/channel_send_action.h"
21 #include "audio/channel.h"
22 #include "audio/router.h"
23 #include "gui/backend/event.h"
24 #include "gui/backend/event_manager.h"
25 #include "gui/widgets/main_window.h"
26 #include "plugins/plugin.h"
27 #include "project.h"
28 #include "utils/error.h"
29 #include "utils/flags.h"
30 #include "utils/objects.h"
31 #include "zrythm_app.h"
32
33 #include <glib/gi18n.h>
34
35 void
channel_send_action_init_loaded(ChannelSendAction * self)36 channel_send_action_init_loaded (
37 ChannelSendAction * self)
38 {
39 }
40
41 /**
42 * Creates a new action.
43 *
44 * @param port MIDI port, if connecting MIDI.
45 * @param stereo Stereo ports, if connecting audio.
46 * @param port_connections_mgr Port connections
47 * manager at the start of the action, if needed.
48 */
49 WARN_UNUSED_RESULT
50 UndoableAction *
channel_send_action_new(ChannelSend * send,ChannelSendActionType type,Port * port,StereoPorts * stereo,float amount,const PortConnectionsManager * port_connections_mgr,GError ** error)51 channel_send_action_new (
52 ChannelSend * send,
53 ChannelSendActionType type,
54 Port * port,
55 StereoPorts * stereo,
56 float amount,
57 const PortConnectionsManager * port_connections_mgr,
58 GError ** error)
59 {
60 ChannelSendAction * self =
61 object_new (ChannelSendAction);
62 UndoableAction * ua = (UndoableAction *) self;
63 undoable_action_init (ua, UA_CHANNEL_SEND);
64
65 self->type = type;
66 self->send_before = channel_send_clone (send);
67 self->amount = amount;
68
69 if (port)
70 self->midi_id =
71 port_identifier_clone (&port->id);
72
73 if (stereo)
74 {
75 self->l_id =
76 port_identifier_clone (&stereo->l->id);
77 self->r_id =
78 port_identifier_clone (&stereo->r->id);
79 }
80
81 if (port_connections_mgr)
82 {
83 self->connections_mgr_before =
84 port_connections_manager_clone (
85 port_connections_mgr);
86 }
87
88 return ua;
89 }
90
91 ChannelSendAction *
channel_send_action_clone(const ChannelSendAction * src)92 channel_send_action_clone (
93 const ChannelSendAction * src)
94 {
95 ChannelSendAction * self =
96 object_new (ChannelSendAction);
97 self->parent_instance = src->parent_instance;
98
99 self->send_before =
100 channel_send_clone (src->send_before);
101 self->type = src->type;
102 self->amount = src->amount;
103
104 if (src->connections_mgr_before)
105 self->connections_mgr_before =
106 port_connections_manager_clone (
107 src->connections_mgr_before);
108 if (src->connections_mgr_after)
109 self->connections_mgr_after =
110 port_connections_manager_clone (
111 src->connections_mgr_after);
112
113 return self;
114 }
115
116 /**
117 * Wrapper to create action and perform it.
118 *
119 * @param port_connections_mgr Port connections
120 * manager at the start of the action, if needed.
121 */
122 bool
channel_send_action_perform(ChannelSend * send,ChannelSendActionType type,Port * port,StereoPorts * stereo,float amount,const PortConnectionsManager * port_connections_mgr,GError ** error)123 channel_send_action_perform (
124 ChannelSend * send,
125 ChannelSendActionType type,
126 Port * port,
127 StereoPorts * stereo,
128 float amount,
129 const PortConnectionsManager * port_connections_mgr,
130 GError ** error)
131 {
132 UNDO_MANAGER_PERFORM_AND_PROPAGATE_ERR (
133 channel_send_action_new,
134 error, send, type, port, stereo, amount,
135 port_connections_mgr, error);
136 }
137
138 static bool
connect_or_disconnect(ChannelSendAction * self,bool connect,bool _do,GError ** error)139 connect_or_disconnect (
140 ChannelSendAction * self,
141 bool connect,
142 bool _do,
143 GError ** error)
144 {
145 /* get the actual channel send from the project */
146 ChannelSend * send =
147 channel_send_find (self->send_before);
148
149 channel_send_disconnect (send, F_NO_RECALC_GRAPH);
150
151 if (_do)
152 {
153 if (connect)
154 {
155 Track * track =
156 channel_send_get_track (send);
157 switch (track->out_signal_type)
158 {
159 case TYPE_EVENT:
160 {
161 Port * port =
162 port_find_from_identifier (
163 self->midi_id);
164 GError * err = NULL;
165 bool connected =
166 channel_send_connect_midi (
167 send, port, F_NO_RECALC_GRAPH,
168 F_VALIDATE, &err);
169 if (!connected)
170 {
171 PROPAGATE_PREFIXED_ERROR (
172 error, err, "%s",
173 _("Failed to connect MIDI "
174 "send"));
175 return false;
176 }
177 }
178 break;
179 case TYPE_AUDIO:
180 {
181 Port * l =
182 port_find_from_identifier (
183 self->l_id);
184 Port * r =
185 port_find_from_identifier (
186 self->r_id);
187 GError * err = NULL;
188 bool connected =
189 channel_send_connect_stereo (
190 send, NULL, l, r,
191 self->type ==
192 CHANNEL_SEND_ACTION_CONNECT_SIDECHAIN,
193 F_NO_RECALC_GRAPH,
194 F_VALIDATE, &err);
195 if (!connected)
196 {
197 PROPAGATE_PREFIXED_ERROR (
198 error, err, "%s",
199 _("Failed to connect audio "
200 "send"));
201 return false;
202 }
203 }
204 break;
205 default:
206 break;
207 }
208 }
209 }
210 /* else if not doing */
211 else
212 {
213 /* copy the values - connections will be
214 * reverted later when resetting the
215 * connections manager */
216 channel_send_copy_values (
217 send, self->send_before);
218 }
219
220 return true;
221 }
222
223 int
channel_send_action_do(ChannelSendAction * self,GError ** error)224 channel_send_action_do (
225 ChannelSendAction * self,
226 GError ** error)
227 {
228 /* get the actual channel send from the project */
229 ChannelSend * send =
230 channel_send_find (self->send_before);
231
232 bool need_restore_and_recalc = false;
233
234 bool successful = false;
235 GError * err = NULL;
236 switch (self->type)
237 {
238 case CHANNEL_SEND_ACTION_CONNECT_MIDI:
239 case CHANNEL_SEND_ACTION_CONNECT_STEREO:
240 case CHANNEL_SEND_ACTION_CHANGE_PORTS:
241 case CHANNEL_SEND_ACTION_CONNECT_SIDECHAIN:
242 successful =
243 connect_or_disconnect (
244 self, true, true, &err);
245 need_restore_and_recalc = true;
246 break;
247 case CHANNEL_SEND_ACTION_DISCONNECT:
248 successful =
249 connect_or_disconnect (
250 self, false, true, &err);
251 need_restore_and_recalc = true;
252 break;
253 case CHANNEL_SEND_ACTION_CHANGE_AMOUNT:
254 successful = true;
255 channel_send_set_amount (
256 send, self->amount);
257 break;
258 default:
259 break;
260 }
261
262 if (!successful)
263 {
264 PROPAGATE_PREFIXED_ERROR (
265 error, err,
266 _("Failed to perform channel send action: %s"),
267 err->message);
268 return -1;
269 }
270
271 if (need_restore_and_recalc)
272 {
273 undoable_action_save_or_load_port_connections (
274 (UndoableAction *) self, true,
275 &self->connections_mgr_before,
276 &self->connections_mgr_after);
277
278 router_recalc_graph (ROUTER, F_NOT_SOFT);
279 }
280
281 EVENTS_PUSH (ET_CHANNEL_SEND_CHANGED, send);
282
283 return 0;
284 }
285
286 /**
287 * Edits the plugin.
288 */
289 int
channel_send_action_undo(ChannelSendAction * self,GError ** error)290 channel_send_action_undo (
291 ChannelSendAction * self,
292 GError ** error)
293 {
294 /* get the actual channel send from the project */
295 ChannelSend * send =
296 channel_send_find (self->send_before);
297
298 bool need_restore_and_recalc = false;
299
300 bool successful = false;
301 GError * err = NULL;
302 switch (self->type)
303 {
304 case CHANNEL_SEND_ACTION_CONNECT_MIDI:
305 case CHANNEL_SEND_ACTION_CONNECT_STEREO:
306 case CHANNEL_SEND_ACTION_CONNECT_SIDECHAIN:
307 successful =
308 connect_or_disconnect (
309 self, false, true, &err);
310 need_restore_and_recalc = true;
311 break;
312 case CHANNEL_SEND_ACTION_CHANGE_PORTS:
313 case CHANNEL_SEND_ACTION_DISCONNECT:
314 successful =
315 connect_or_disconnect (
316 self, true, false, &err);
317 need_restore_and_recalc = true;
318 break;
319 case CHANNEL_SEND_ACTION_CHANGE_AMOUNT:
320 channel_send_set_amount (
321 send, self->send_before->amount->control);
322 successful = true;
323 break;
324 default:
325 break;
326 }
327
328 if (!successful)
329 {
330 PROPAGATE_PREFIXED_ERROR (
331 error, err,
332 _("Failed to perform channel send action: %s"),
333 err->message);
334 return -1;
335 }
336
337 if (need_restore_and_recalc)
338 {
339 undoable_action_save_or_load_port_connections (
340 (UndoableAction *) self, false,
341 &self->connections_mgr_before,
342 &self->connections_mgr_after);
343
344 router_recalc_graph (ROUTER, F_NOT_SOFT);
345
346 tracklist_validate (TRACKLIST);
347 }
348
349 EVENTS_PUSH (ET_CHANNEL_SEND_CHANGED, send);
350
351 return 0;
352 }
353
354 char *
channel_send_action_stringize(ChannelSendAction * self)355 channel_send_action_stringize (
356 ChannelSendAction * self)
357 {
358 switch (self->type)
359 {
360 case CHANNEL_SEND_ACTION_CONNECT_SIDECHAIN:
361 return g_strdup (_("Connect sidechain"));
362 case CHANNEL_SEND_ACTION_CONNECT_STEREO:
363 return g_strdup (_("Connect stereo"));
364 case CHANNEL_SEND_ACTION_CONNECT_MIDI:
365 return g_strdup (_("Connect MIDI"));
366 case CHANNEL_SEND_ACTION_DISCONNECT:
367 return g_strdup (_("Disconnect"));
368 case CHANNEL_SEND_ACTION_CHANGE_AMOUNT:
369 return g_strdup (_("Change amount"));
370 case CHANNEL_SEND_ACTION_CHANGE_PORTS:
371 return g_strdup (_("Change ports"));
372 }
373 g_return_val_if_reached (
374 g_strdup (_("Channel send connection")));
375 }
376
377 void
channel_send_action_free(ChannelSendAction * self)378 channel_send_action_free (
379 ChannelSendAction * self)
380 {
381 object_free_w_func_and_null (
382 channel_send_free, self->send_before);
383 object_free_w_func_and_null (
384 port_identifier_free, self->l_id);
385 object_free_w_func_and_null (
386 port_identifier_free, self->r_id);
387 object_free_w_func_and_null (
388 port_identifier_free, self->midi_id);
389 object_free_w_func_and_null (
390 port_connections_manager_free,
391 self->connections_mgr_before);
392 object_free_w_func_and_null (
393 port_connections_manager_free,
394 self->connections_mgr_after);
395
396 object_zero_and_free (self);
397 }
398