copy mixer utility evolution
[ardour.git] / session_utils / copy-mixer.cc
1 #include <iostream>
2 #include <cstdlib>
3 #include <getopt.h>
4
5 #include "pbd/stateful.h"
6 #include "ardour/send.h"
7 #include "ardour/track.h"
8
9 #include "common.h"
10
11 #define X_(Text) Text
12
13 using namespace std;
14 using namespace ARDOUR;
15 using namespace SessionUtils;
16
17 /* this is copied from  Session::new_route_from_template */
18 static void
19 trim_state_for_mixer_copy (Session*s, XMLNode& node)
20 {
21         /* trim bitslots from listen sends so that new ones are used */
22         XMLNodeList children = node.children ();
23         for (XMLNodeList::iterator i = children.begin (); i != children.end (); ++i) {
24                 if ((*i)->name() == X_("Processor")) {
25                         /* ForceIDRegeneration does not catch the following */
26                         XMLProperty const * role = (*i)->property (X_("role"));
27                         XMLProperty const * type = (*i)->property (X_("type"));
28                         if (role && role->value () == X_("Aux")) {
29                                 /* check if the target bus exists,
30                                  * HERE: we use the bus-name (not target-id)
31                                  */
32                                 XMLProperty const * target = (*i)->property (X_("name"));
33                                 if (!target) {
34                                         (*i)->add_property ("type", "dangling-aux-send");
35                                         continue;
36                                 }
37                                 boost::shared_ptr<Route> r = s->route_by_name (target->value ());
38                                 if (!r || boost::dynamic_pointer_cast<Track> (r)) {
39                                         (*i)->add_property ("type", "dangling-aux-send");
40                                         continue;
41                                 }
42                                 (*i)->add_property ("target", r->id ().to_s ());
43                         }
44                         if (role && role->value () == X_("Listen")) {
45                                 (*i)->remove_property (X_("bitslot"));
46                         }
47                         else if (role && (role->value () == X_("Send") || role->value () == X_("Aux"))) {
48                                 char buf[32];
49                                 Delivery::Role xrole;
50                                 uint32_t bitslot = 0;
51                                 xrole = Delivery::Role (string_2_enum (role->value (), xrole));
52                                 std::string name = Send::name_and_id_new_send (*s, xrole, bitslot, false);
53                                 snprintf (buf, sizeof (buf), "%" PRIu32, bitslot);
54                                 (*i)->remove_property (X_("bitslot"));
55                                 (*i)->remove_property (X_("name"));
56                                 (*i)->add_property ("bitslot", buf);
57                                 (*i)->add_property ("name", name);
58                         }
59                         else if (type && type->value () == X_("intreturn")) {
60                                 // ignore, in case bus existed in old session,
61                                 // tracks in old session may be connected to it.
62                                 // if the bus is new, new_route_from_template()
63                                 // will have re-created an ID.
64                                 (*i)->add_property ("type", "ignore-aux-return");
65                         }
66                         else if (type && type->value () == X_("return")) {
67                                 // Return::set_state() generates a new one
68                                 (*i)->remove_property (X_("bitslot"));
69                         }
70                         else if (type && type->value () == X_("port")) {
71                                 // PortInsert::set_state() handles the bitslot
72                                 (*i)->remove_property (X_("bitslot"));
73                                 (*i)->add_property ("ignore-name", "1");
74                         }
75                 }
76         }
77 }
78
79 static void
80 copy_mixer_settings (Session*s, boost::shared_ptr<Route> dst, XMLNode& state)
81 {
82         PBD::Stateful::ForceIDRegeneration force_ids;
83
84         trim_state_for_mixer_copy (s, state);
85         state.remove_nodes_and_delete ("Diskstream");
86         state.remove_nodes_and_delete ("Automation");
87         state.dump (cerr);
88
89         dst->set_state (state, PBD::Stateful::loading_state_version);
90 }
91
92 static int
93 copy_session_routes (
94                 const std::string& src_path, const std::string& src_name,
95                 const std::string& dst_path, const std::string& dst_load, const std::string& dst_save)
96 {
97         SessionUtils::init (false);
98         Session* s = 0;
99
100         typedef std::map<std::string,XMLNode*> StateMap;
101         StateMap routestate;
102         StateMap buslist;
103
104         s = SessionUtils::load_session (src_path, src_name);
105
106         if (!s) {
107                 printf ("Cannot load source session %s/%s.\n", src_path.c_str (), src_name.c_str ());
108                 SessionUtils::cleanup ();
109                 return -1;
110         }
111
112         /* get route state from first session */
113         boost::shared_ptr<RouteList> rl = s->get_routes ();
114         for (RouteList::iterator i = rl->begin (); i != rl->end (); ++i) {
115                 boost::shared_ptr<Route> r = *i;
116                 if (r->is_master () || r->is_monitor () || r->is_auditioner ()) {
117                         continue;
118                 }
119                 XMLNode& state (r->get_state ());
120                 routestate[r->name ()] = &state;
121                 if (boost::dynamic_pointer_cast<Track> (r)) {
122                         continue;
123                 }
124                 buslist[r->name ()] = &state;
125         }
126         rl.reset ();
127         SessionUtils::unload_session (s);
128
129
130         /* open target session */
131         s = SessionUtils::load_session (dst_path, dst_load);
132         if (!s) {
133                 printf ("Cannot load target session %s/%s.\n", dst_path.c_str (), dst_load.c_str ());
134                 SessionUtils::cleanup ();
135                 return -1;
136         }
137
138         /* iterate over all busses in the src session, add missing ones to target */
139         // TODO: make optional
140         rl = s->get_routes ();
141         for (StateMap::const_iterator i = buslist.begin (); i != buslist.end (); ++i) {
142                 if (s->route_by_name (i->first)) {
143                         continue;
144                 }
145                 XMLNode& rs (*(i->second));
146                 s->new_route_from_template (1, rs, rs.property (X_("name"))->value (), NewPlaylist);
147         }
148
149         /* iterate over all *busses* in the target session.
150          * setup internal return targets.
151          */
152         rl = s->get_routes ();
153         for (RouteList::iterator i = rl->begin (); i != rl->end (); ++i) {
154                 boost::shared_ptr<Route> r = *i;
155                 /* skip special busses */
156                 if (r->is_master () || r->is_monitor () || r->is_auditioner ()) {
157                         continue;
158                 }
159                 if (boost::dynamic_pointer_cast<Track> (r)) {
160                         continue;
161                 }
162                 /* find matching route by name */
163                 std::map<std::string,XMLNode*>::iterator it = routestate.find (r->name ());
164                 if (it == routestate.end ()) {
165                         printf (" -- no match for '%s'\n", (*i)->name ().c_str ());
166                         continue;
167                 }
168                 printf ("-- found match '%s'\n", (*i)->name ().c_str ());
169                 XMLNode *state = it->second;
170                 // copy state
171                 copy_mixer_settings (s, r, *state);
172         }
173
174         /* iterate over all tracks in the target session.. */
175         rl = s->get_routes ();
176         for (RouteList::iterator i = rl->begin (); i != rl->end (); ++i) {
177                 boost::shared_ptr<Route> r = *i;
178                 /* skip special busses */
179                 if (r->is_master () || r->is_monitor () || r->is_auditioner ()) {
180                         continue;
181                 }
182                 if (!boost::dynamic_pointer_cast<Track> (r)) {
183                         continue;
184                 }
185
186                 /* find matching route by name */
187                 std::map<std::string,XMLNode*>::iterator it = routestate.find (r->name ());
188                 if (it == routestate.end ()) {
189                         printf (" -- no match for '%s'\n", (*i)->name ().c_str ());
190                         continue;
191                 }
192                 printf ("-- found match '%s'\n", (*i)->name ().c_str ());
193                 XMLNode *state = it->second;
194                 /* copy state */
195                 copy_mixer_settings (s, r, *state);
196         }
197
198         s->save_state (dst_save);
199
200         rl.reset ();
201         SessionUtils::unload_session (s);
202
203         // clean up.
204         for (StateMap::iterator i = routestate.begin (); i != routestate.end (); ++i) {
205                 XMLNode *state = i->second;
206                 delete state;
207         }
208
209         SessionUtils::cleanup ();
210         return 0;
211 }
212
213
214 static void usage (int status) {
215         // help2man compatible format (standard GNU help-text)
216         printf ("copy-mixer - copy mixer settings from one session to another.\n\n");
217         printf ("Usage: copy-mixer [ OPTIONS ] <src-path> <name> <dst-path> <name> [name]\n\n");
218         printf ("Options:\n\
219   -h, --help                 display this help and exit\n\
220   -V, --version              print version information and exit\n\
221 \n");
222         printf ("\n\
223 .. not yet documented..\n\
224 \n");
225
226         printf ("Report bugs to <http://tracker.ardour.org/>\n"
227                 "Website: <http://ardour.org/>\n");
228         ::exit (status);
229 }
230
231
232 int main (int argc, char* argv[])
233 {
234         const char *optstring = "hV";
235
236         // TODO add some arguments. (save snapshot, copy busses...)
237
238         const struct option longopts[] = {
239                 { "help",       0, 0, 'h' },
240                 { "version",    0, 0, 'V' },
241         };
242
243         int c = 0;
244
245         while (EOF != (c = getopt_long (argc, argv,
246                                         optstring, longopts, (int *) 0))) {
247                 switch (c) {
248
249                         case 'V':
250                                 printf ("ardour-utils version %s\n\n", VERSIONSTRING);
251                                 printf ("Copyright (C) GPL 2015 Robin Gareus <robin@gareus.org>\n");
252                                 exit (0);
253                                 break;
254
255                         case 'h':
256                                 usage (0);
257                                 break;
258
259                         default:
260                                         usage (EXIT_FAILURE);
261                                         break;
262                 }
263         }
264
265         // TODO parse path/name  from a single argument.
266
267         if (optind + 4 > argc) {
268                 usage (EXIT_FAILURE);
269         }
270
271         return copy_session_routes (
272                         argv[optind], argv[optind + 1],
273                         argv[optind + 2], argv[optind + 3],
274                         (optind + 4 < argc) ? argv[optind + 4] : "");
275 }