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