Fix the Unicode workaround in wscript
[ardour.git] / session_utils / export.cc
1 #include <iostream>
2 #include <cstdlib>
3 #include <getopt.h>
4 #include <glibmm.h>
5
6 #include "common.h"
7
8 #include "pbd/basename.h"
9 #include "pbd/enumwriter.h"
10
11 #include "ardour/broadcast_info.h"
12 #include "ardour/export_handler.h"
13 #include "ardour/export_status.h"
14 #include "ardour/export_timespan.h"
15 #include "ardour/export_channel_configuration.h"
16 #include "ardour/export_format_specification.h"
17 #include "ardour/export_filename.h"
18 #include "ardour/route.h"
19 #include "ardour/session_metadata.h"
20 #include "ardour/broadcast_info.h"
21
22 using namespace std;
23 using namespace ARDOUR;
24 using namespace SessionUtils;
25
26 struct ExportSettings
27 {
28         ExportSettings ()
29                 : _samplerate (0)
30                 , _sample_format (ExportFormatBase::SF_16)
31                 , _normalize (false)
32                 , _bwf (false)
33         {}
34
35         std::string samplerate () const
36         {
37                 stringstream ss;
38                 ss << _samplerate;
39                 return ss.str();
40         }
41
42         std::string sample_format () const
43         {
44                 return enum_2_string (_sample_format);
45         }
46
47         std::string normalize () const
48         {
49                 return _normalize ? "true" : "false";
50         }
51
52         std::string bwf () const
53         {
54                 return _bwf ? "true" : "false";
55         }
56
57         int _samplerate;
58         ExportFormatBase::SampleFormat _sample_format;
59         bool _normalize;
60         bool _bwf;
61 };
62
63 static int export_session (Session *session,
64                 std::string outfile,
65                 ExportSettings const& settings)
66 {
67         ExportTimespanPtr tsp = session->get_export_handler()->add_timespan();
68         boost::shared_ptr<ExportChannelConfiguration> ccp = session->get_export_handler()->add_channel_config();
69         boost::shared_ptr<ARDOUR::ExportFilename> fnp = session->get_export_handler()->add_filename();
70         boost::shared_ptr<ARDOUR::BroadcastInfo> b;
71
72         XMLTree tree;
73
74         tree.read_buffer(std::string(
75 "<?xml version=\"1.0\" encoding=\"UTF-8\"?>"
76 "<ExportFormatSpecification name=\"UTIL-WAV-EXPORT\" id=\"b1280899-0459-4aef-9dc9-7e2277fa6d24\">"
77 "  <Encoding id=\"F_WAV\" type=\"T_Sndfile\" extension=\"wav\" name=\"WAV\" has-sample-format=\"true\" channel-limit=\"256\"/>"
78 "  <SampleRate rate=\""+ settings.samplerate () +"\"/>"
79 "  <SRCQuality quality=\"SRC_SincBest\"/>"
80 "  <EncodingOptions>"
81 "    <Option name=\"sample-format\" value=\"" + settings.sample_format () + "\"/>"
82 "    <Option name=\"dithering\" value=\"D_None\"/>"
83 "    <Option name=\"tag-metadata\" value=\"true\"/>"
84 "    <Option name=\"tag-support\" value=\"false\"/>"
85 "    <Option name=\"broadcast-info\" value=\"" + settings.bwf () +"\"/>"
86 "  </EncodingOptions>"
87 "  <Processing>"
88 "    <Normalize enabled=\""+ settings.normalize () +"\" target=\"0\"/>"
89 "    <Silence>"
90 "      <Start>"
91 "        <Trim enabled=\"false\"/>"
92 "        <Add enabled=\"false\">"
93 "          <Duration format=\"Timecode\" hours=\"0\" minutes=\"0\" seconds=\"0\" frames=\"0\"/>"
94 "        </Add>"
95 "      </Start>"
96 "      <End>"
97 "        <Trim enabled=\"false\"/>"
98 "        <Add enabled=\"false\">"
99 "          <Duration format=\"Timecode\" hours=\"0\" minutes=\"0\" seconds=\"0\" frames=\"0\"/>"
100 "        </Add>"
101 "      </End>"
102 "    </Silence>"
103 "  </Processing>"
104 "</ExportFormatSpecification>"
105 ));
106
107         boost::shared_ptr<ExportFormatSpecification> fmp = session->get_export_handler()->add_format(*tree.root());
108
109         /* set up range */
110         framepos_t start, end;
111         start = session->current_start_frame();
112         end   = session->current_end_frame();
113         tsp->set_range (start, end);
114         tsp->set_range_id ("session");
115
116         /* add master outs as default */
117         IO* master_out = session->master_out()->output().get();
118         if (!master_out) {
119                 PBD::warning << _("Export Util: No Master Out Ports to Connect for Audio Export") << endmsg;
120                 return -1;
121         }
122
123         for (uint32_t n = 0; n < master_out->n_ports().n_audio(); ++n) {
124                 PortExportChannel * channel = new PortExportChannel ();
125                 channel->add_port (master_out->audio (n));
126                 ExportChannelPtr chan_ptr (channel);
127                 ccp->register_channel (chan_ptr);
128         }
129
130         /* output filename */
131         if (outfile.empty ()) {
132                 tsp->set_name ("session");
133         } else {
134                 std::string dirname = Glib::path_get_dirname (outfile);
135                 std::string basename = Glib::path_get_basename (outfile);
136
137                 if (basename.size() > 4 && !basename.compare (basename.size() - 4, 4, ".wav")) {
138                         basename = PBD::basename_nosuffix (basename);
139                 }
140
141                 fnp->set_folder(dirname);
142                 tsp->set_name (basename);
143         }
144
145         /* set broadcast info */
146         if (settings._bwf) {
147                 b.reset (new BroadcastInfo);
148                 b->set_from_session (*session, tsp->get_start ());
149         }
150
151         cout << "* Writing " << Glib::build_filename (fnp->get_folder(), tsp->name() + ".wav") << endl;
152
153
154         /* output */
155         fnp->set_timespan(tsp);
156         fnp->include_label = false;
157
158         /* do audio export */
159         fmp->set_soundcloud_upload(false);
160         session->get_export_handler()->add_export_config (tsp, ccp, fmp, fnp, b);
161         session->get_export_handler()->do_export();
162
163         boost::shared_ptr<ARDOUR::ExportStatus> status = session->get_export_status ();
164
165         // TODO trap SIGINT -> status->abort();
166
167         while (status->running ()) {
168                 double progress = 0.0;
169                 switch (status->active_job) {
170                 case ExportStatus::Normalizing:
171                         progress = ((float) status->current_postprocessing_cycle) / status->total_postprocessing_cycles;
172                         printf ("* Normalizing %.1f%%      \r", 100. * progress); fflush (stdout);
173                         break;
174                 case ExportStatus::Exporting:
175                         progress = ((float) status->processed_frames_current_timespan) / status->total_frames_current_timespan;
176                         printf ("* Exporting Audio %.1f%%  \r", 100. * progress); fflush (stdout);
177                         break;
178                 default:
179                         printf ("* Exporting...            \r");
180                         break;
181                 }
182                 Glib::usleep (1000000);
183         }
184         printf("\n");
185
186         status->finish ();
187
188         printf ("* Done.\n");
189         return 0;
190 }
191
192 static void usage (int status) {
193         // help2man compatible format (standard GNU help-text)
194         printf (UTILNAME " - export an ardour session from the commandline.\n\n");
195         printf ("Usage: " UTILNAME " [ OPTIONS ] <session-dir> <session/snapshot-name>\n\n");
196         printf ("Options:\n\
197   -b, --bitdepth <depth>     set export-format (16, 24, 32, float)\n\
198   -B, --broadcast            include broadcast wave header\n\
199   -h, --help                 display this help and exit\n\
200   -n, --normalize            normalize signal level (to 0dBFS)\n\
201   -o, --output  <file>       export output file name\n\
202   -s, --samplerate <rate>    samplerate to use\n\
203   -V, --version              print version information and exit\n\
204 \n");
205         printf ("\n\
206 This tool exports the session-range of a given ardour-session to a wave file,\n\
207 using the master-bus outputs.\n\
208 By default a 16bit signed .wav file at session-rate is exported.\n\
209 If the no output-file is given, the session's export dir is used.\n\
210 \n\
211 Note: the tool expects a session-name without .ardour file-name extension.\n\
212 \n");
213
214         printf ("Report bugs to <http://tracker.ardour.org/>\n"
215                 "Website: <http://ardour.org/>\n");
216         ::exit (status);
217 }
218
219 int main (int argc, char* argv[])
220 {
221         ExportSettings settings;
222         std::string outfile;
223
224         const char *optstring = "b:Bhno:s:V";
225
226         const struct option longopts[] = {
227                 { "bitdepth",   1, 0, 'b' },
228                 { "broadcast",  0, 0, 'B' },
229                 { "help",       0, 0, 'h' },
230                 { "normalize",  0, 0, 'n' },
231                 { "output",     1, 0, 'o' },
232                 { "samplerate", 1, 0, 's' },
233                 { "version",    0, 0, 'V' },
234         };
235
236         int c = 0;
237         while (EOF != (c = getopt_long (argc, argv,
238                                         optstring, longopts, (int *) 0))) {
239                 switch (c) {
240
241                         case 'b':
242                                 switch (atoi (optarg)) {
243                                         case 16:
244                                                 settings._sample_format = ExportFormatBase::SF_16;
245                                                 break;
246                                         case 24:
247                                                 settings._sample_format = ExportFormatBase::SF_24;
248                                                 break;
249                                         case 32:
250                                                 settings._sample_format = ExportFormatBase::SF_32;
251                                                 break;
252                                         case 0:
253                                                 if (0 == strcmp (optarg, "float")) {
254                                                         settings._sample_format = ExportFormatBase::SF_Float;
255                                                         break;
256                                                 }
257                                                 // no break
258                                         default:
259                                                 fprintf(stderr, "Invalid Bit Depth\n");
260                                                 break;
261                                 }
262                                 break;
263
264                         case 'B':
265                                 settings._bwf = true;
266                                 break;
267
268                         case 'n':
269                                 settings._normalize = true;
270                                 break;
271
272                         case 'o':
273                                 outfile = optarg;
274                                 break;
275
276                         case 's':
277                                 {
278                                         const int sr = atoi (optarg);
279                                         if (sr >= 8000 && sr <= 192000) {
280                                                 settings._samplerate = sr;
281                                         } else {
282                                                 fprintf(stderr, "Invalid Samplerate\n");
283                                         }
284                                 }
285                                 break;
286
287                         case 'V':
288                                 printf ("ardour-utils version %s\n\n", VERSIONSTRING);
289                                 printf ("Copyright (C) GPL 2015,2017 Robin Gareus <robin@gareus.org>\n");
290                                 exit (0);
291                                 break;
292
293                         case 'h':
294                                 usage (0);
295                                 break;
296
297                         default:
298                                         usage (EXIT_FAILURE);
299                                         break;
300                 }
301         }
302
303         if (optind + 2 > argc) {
304                 usage (EXIT_FAILURE);
305         }
306
307         SessionUtils::init(false);
308         Session* s = 0;
309
310         s = SessionUtils::load_session (argv[optind], argv[optind+1]);
311
312         if (settings._samplerate == 0) {
313                 settings._samplerate = s->nominal_frame_rate ();
314         }
315
316         export_session (s, outfile, settings);
317
318         SessionUtils::unload_session(s);
319         SessionUtils::cleanup();
320
321         return 0;
322 }