Add missing i18n include (after 7f3f2018334)
[ardour.git] / session_utils / copy-mixer.cc
1 /*
2  * Copyright (C) 2016-2019 Robin Gareus <robin@gareus.org>
3  *
4  * This program is free software; you can redistribute it and/or modify
5  * it under the terms of the GNU General Public License as published by
6  * the Free Software Foundation; either version 2 of the License, or
7  * (at your option) any later version.
8  *
9  * This program is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12  * GNU General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public License along
15  * with this program; if not, write to the Free Software Foundation, Inc.,
16  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
17  */
18
19 #include <iostream>
20 #include <cstdlib>
21 #include <getopt.h>
22 #include <glibmm.h>
23
24 #include "pbd/basename.h"
25 #include "pbd/stateful.h"
26 #include "ardour/filename_extensions.h"
27 #include "ardour/send.h"
28 #include "ardour/track.h"
29
30 #include "common.h"
31
32 #define X_(Text) Text
33
34 using namespace std;
35 using namespace ARDOUR;
36 using namespace SessionUtils;
37
38 static bool opt_debug_dump = false;
39 static bool opt_copy_busses = false;
40 static bool opt_verbose = false;
41 static bool opt_log = false;
42
43 /* this is copied from  Session::new_route_from_template */
44 static void
45 trim_state_for_mixer_copy (Session*s, XMLNode& node)
46 {
47         /* trim bitslots from listen sends so that new ones are used */
48         XMLNodeList children = node.children ();
49         for (XMLNodeList::iterator i = children.begin (); i != children.end (); ++i) {
50                 if ((*i)->name() == X_("Processor")) {
51                         /* ForceIDRegeneration does not catch the following */
52                         XMLProperty const * role = (*i)->property (X_("role"));
53                         XMLProperty const * type = (*i)->property (X_("type"));
54                         if (role && role->value () == X_("Aux")) {
55                                 /* check if the target bus exists,
56                                  * HERE: we use the bus-name (not target-id)
57                                  */
58                                 XMLProperty const * target = (*i)->property (X_("name"));
59                                 if (!target) {
60                                         (*i)->set_property ("type", "dangling-aux-send");
61                                         continue;
62                                 }
63                                 boost::shared_ptr<Route> r = s->route_by_name (target->value ());
64                                 if (!r || boost::dynamic_pointer_cast<Track> (r)) {
65                                         (*i)->set_property ("type", "dangling-aux-send");
66                                         continue;
67                                 }
68                                 (*i)->set_property ("target", r->id ().to_s ());
69                         }
70                         if (role && role->value () == X_("Listen")) {
71                                 (*i)->remove_property (X_("bitslot"));
72                         }
73                         else if (role && (role->value () == X_("Send") || role->value () == X_("Aux"))) {
74                                 Delivery::Role xrole;
75                                 uint32_t bitslot = 0;
76                                 xrole = Delivery::Role (string_2_enum (role->value (), xrole));
77                                 std::string name = Send::name_and_id_new_send (*s, xrole, bitslot, false);
78                                 (*i)->remove_property (X_("bitslot"));
79                                 (*i)->remove_property (X_("name"));
80                                 (*i)->set_property ("bitslot", bitslot);
81                                 (*i)->set_property ("name", name);
82                         }
83                         else if (type && type->value () == X_("intreturn")) {
84                                 // ignore, in case bus existed in old session,
85                                 // tracks in old session may be connected to it.
86                                 // if the bus is new, new_route_from_template()
87                                 // will have re-created an ID.
88                                 (*i)->set_property ("type", "ignore-aux-return");
89                         }
90                         else if (type && type->value () == X_("return")) {
91                                 // Return::set_state() generates a new one
92                                 (*i)->remove_property (X_("bitslot"));
93                         }
94                         else if (type && type->value () == X_("port")) {
95                                 // PortInsert::set_state() handles the bitslot
96                                 (*i)->remove_property (X_("bitslot"));
97                                 (*i)->set_property ("ignore-name", "1");
98                         }
99                 }
100         }
101 }
102
103 static void
104 copy_mixer_settings (Session*s, boost::shared_ptr<Route> dst, XMLNode& state)
105 {
106         PBD::Stateful::ForceIDRegeneration force_ids;
107
108         trim_state_for_mixer_copy (s, state);
109         state.remove_nodes_and_delete ("Diskstream");
110         state.remove_nodes_and_delete ("Automation");
111         if (opt_debug_dump) {
112                 state.dump (cout);
113         }
114
115         dst->set_state (state, PBD::Stateful::loading_state_version);
116 }
117
118 static int
119 copy_session_routes (
120                 const std::string& src_path, const std::string& src_name,
121                 const std::string& dst_path, const std::string& dst_load, const std::string& dst_save)
122 {
123         SessionUtils::init (opt_log);
124         Session* s = 0;
125
126         typedef std::map<std::string,XMLNode*> StateMap;
127         StateMap routestate;
128         StateMap buslist;
129
130         s = SessionUtils::load_session (src_path, src_name, false);
131
132         if (!s) {
133                 fprintf (stderr, "Cannot load source session %s/%s.\n", src_path.c_str (), src_name.c_str ());
134                 SessionUtils::cleanup ();
135                 return -1;
136         }
137
138         /* get route state from first session */
139         boost::shared_ptr<RouteList> rl = s->get_routes ();
140         for (RouteList::iterator i = rl->begin (); i != rl->end (); ++i) {
141                 boost::shared_ptr<Route> r = *i;
142                 if (r->is_master () || r->is_monitor () || r->is_auditioner ()) {
143                         continue;
144                 }
145                 XMLNode& state (r->get_state ());
146                 routestate[r->name ()] = &state;
147                 if (boost::dynamic_pointer_cast<Track> (r)) {
148                         continue;
149                 }
150                 buslist[r->name ()] = &state;
151         }
152         rl.reset ();
153         SessionUtils::unload_session (s);
154
155
156         /* open target session */
157         s = SessionUtils::load_session (dst_path, dst_load);
158         if (!s) {
159                 fprintf (stderr, "Cannot load target session %s/%s.\n", dst_path.c_str (), dst_load.c_str ());
160                 SessionUtils::cleanup ();
161                 return -1;
162         }
163
164         /* iterate over all busses in the src session, add missing ones to target */
165         if (opt_copy_busses) {
166                 rl = s->get_routes ();
167                 for (StateMap::const_iterator i = buslist.begin (); i != buslist.end (); ++i) {
168                         if (s->route_by_name (i->first)) {
169                                 continue;
170                         }
171                         XMLNode& rs (*(i->second));
172                         s->new_route_from_template (1, PresentationInfo::max_order, rs, rs.property (X_("name"))->value (), NewPlaylist);
173                 }
174         }
175
176         /* iterate over all *busses* in the target session.
177          * setup internal return targets.
178          */
179         rl = s->get_routes ();
180         for (RouteList::iterator i = rl->begin (); i != rl->end (); ++i) {
181                 boost::shared_ptr<Route> r = *i;
182                 /* skip special busses */
183                 if (r->is_master () || r->is_monitor () || r->is_auditioner ()) {
184                         continue;
185                 }
186                 if (boost::dynamic_pointer_cast<Track> (r)) {
187                         continue;
188                 }
189                 /* find matching route by name */
190                 std::map<std::string,XMLNode*>::iterator it = routestate.find (r->name ());
191                 if (it == routestate.end ()) {
192                         if (opt_verbose) {
193                                 printf (" -- no match for '%s'\n", (*i)->name ().c_str ());
194                         }
195                         continue;
196                 }
197                 if (opt_verbose) {
198                         printf ("-- found match '%s'\n", (*i)->name ().c_str ());
199                 }
200                 XMLNode *state = it->second;
201                 // copy state
202                 copy_mixer_settings (s, r, *state);
203         }
204
205         /* iterate over all tracks in the target session.. */
206         rl = s->get_routes ();
207         for (RouteList::iterator i = rl->begin (); i != rl->end (); ++i) {
208                 boost::shared_ptr<Route> r = *i;
209                 /* skip special busses */
210                 if (r->is_master () || r->is_monitor () || r->is_auditioner ()) {
211                         continue;
212                 }
213                 if (!boost::dynamic_pointer_cast<Track> (r)) {
214                         continue;
215                 }
216
217                 /* find matching route by name */
218                 std::map<std::string,XMLNode*>::iterator it = routestate.find (r->name ());
219                 if (it == routestate.end ()) {
220                         if (opt_verbose) {
221                                 printf (" -- no match for '%s'\n", (*i)->name ().c_str ());
222                         }
223                         continue;
224                 }
225                 if (opt_verbose) {
226                         printf ("-- found match '%s'\n", (*i)->name ().c_str ());
227                 }
228                 XMLNode *state = it->second;
229                 /* copy state */
230                 copy_mixer_settings (s, r, *state);
231         }
232
233         s->save_state (dst_save);
234
235         rl.reset ();
236         SessionUtils::unload_session (s);
237
238         // clean up.
239         for (StateMap::iterator i = routestate.begin (); i != routestate.end (); ++i) {
240                 XMLNode *state = i->second;
241                 delete state;
242         }
243
244         SessionUtils::cleanup ();
245         return 0;
246 }
247
248
249 static void usage () {
250         // help2man compatible format (standard GNU help-text)
251         printf (UTILNAME " - copy mixer settings from one session to another.\n\n");
252         printf ("Usage: " UTILNAME " [ OPTIONS ] <src> <dst>\n\n");
253
254         printf ("Options:\n\
255   -h, --help                 display this help and exit\n\
256   -b, --bus-copy             add busses present in src to dst\n\
257   -d, --debug                print pre-processed XML for each route\n\
258   -l, --log-messages         display libardour log messages\n\
259   -s, --snapshot <name>      create a new snapshot in dst\n\
260   -v, --verbose              show performed copy operations\n\
261   -V, --version              print version information and exit\n\
262 \n");
263
264         printf ("\n\
265 This utility copies mixer-settings from the src-session to the dst-session.\n\
266 Both <src> and <dst> are paths to .ardour session files.\n\
267 If --snapshot is not given, the <dst> session file is overwritten.\n\
268 When --snapshot is set, a new snaphot in the <dst> session is created.\n\
269 \n");
270
271         printf ("Report bugs to <http://tracker.ardour.org/>\n"
272                 "Website: <http://ardour.org/>\n");
273         ::exit (EXIT_SUCCESS);
274 }
275
276 static bool ends_with (std::string const& value, std::string const& ending)
277 {
278         if (ending.size() > value.size()) return false;
279         return std::equal(ending.rbegin(), ending.rend(), value.rbegin());
280 }
281
282 int main (int argc, char* argv[])
283 {
284         const char *optstring = "bhls:Vv";
285
286         const struct option longopts[] = {
287                 { "bus-copy",     required_argument, 0, 'b' },
288                 { "debug",        no_argument,       0, 'd' },
289                 { "help",         no_argument,       0, 'h' },
290                 { "log-messages", no_argument,       0, 'l' },
291                 { "snapshot",     no_argument,       0, 's' },
292                 { "version",      no_argument,       0, 'V' },
293                 { "vebose",       no_argument,       0, 'v' },
294         };
295
296         int c = 0;
297         std::string dst_snapshot_name = "";
298
299         while (EOF != (c = getopt_long (argc, argv,
300                                         optstring, longopts, (int *) 0))) {
301                 switch (c) {
302                         case 'b':
303                                 opt_copy_busses = true;
304                                 break;
305
306                         case 'd':
307                                 opt_debug_dump = true;
308                                 break;
309
310                         case 'h':
311                                 usage ();
312                                 break;
313
314                         case 'l':
315                                 opt_log = true;
316                                 break;
317
318                         case 's':
319                                 dst_snapshot_name = optarg;
320                                 break;
321
322                         case 'V':
323                                 printf ("ardour-utils version %s\n\n", VERSIONSTRING);
324                                 printf ("Copyright (C) GPL 2016 Robin Gareus <robin@gareus.org>\n");
325                                 exit (EXIT_SUCCESS);
326                                 break;
327
328                         case 'v':
329                                 opt_verbose = true;
330                                 break;
331
332                         default:
333                                 cerr << "Error: unrecognized option. See --help for usage information.\n";
334                                 ::exit (EXIT_FAILURE);
335                                 break;
336                 }
337         }
338
339         // TODO parse path/name  from a single argument.
340
341         if (optind + 2 > argc) {
342                 cerr << "Error: Missing parameter. See --help for usage information.\n";
343                 ::exit (EXIT_FAILURE);
344         }
345
346         std::string src = argv[optind];
347         std::string dst = argv[optind + 1];
348
349         // statefile_suffix
350
351         if (!ends_with (src, statefile_suffix)) {
352                 fprintf (stderr, "source is not a .ardour session file.\n");
353                 exit (EXIT_FAILURE);
354         }
355         if (!ends_with (dst, statefile_suffix)) {
356                 fprintf (stderr, "target is not a .ardour session file.\n");
357                 exit (EXIT_FAILURE);
358         }
359         if (!Glib::file_test (src, Glib::FILE_TEST_IS_REGULAR)) {
360                 fprintf (stderr, "source is not a regular file.\n");
361                 exit (EXIT_FAILURE);
362         }
363         if (!Glib::file_test (dst, Glib::FILE_TEST_IS_REGULAR)) {
364                 fprintf (stderr, "target is not a regular file.\n");
365                 exit (EXIT_FAILURE);
366         }
367
368         std::string src_path = Glib::path_get_dirname (src);
369         std::string src_name = PBD::basename_nosuffix (src);
370         std::string dst_path = Glib::path_get_dirname (dst);
371         std::string dst_name = PBD::basename_nosuffix (dst);
372
373         // TODO check if src != dst ..
374         return copy_session_routes (
375                         src_path, src_name,
376                         dst_path, dst_name,
377                         dst_snapshot_name);
378 }