2 * Copyright (C) 2015-2019 Robin Gareus <robin@gareus.org>
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.
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.
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.
26 #include "pbd/basename.h"
27 #include "pbd/enumwriter.h"
29 #include "ardour/broadcast_info.h"
30 #include "ardour/export_handler.h"
31 #include "ardour/export_status.h"
32 #include "ardour/export_timespan.h"
33 #include "ardour/export_channel_configuration.h"
34 #include "ardour/export_format_specification.h"
35 #include "ardour/export_filename.h"
36 #include "ardour/route.h"
37 #include "ardour/session_metadata.h"
38 #include "ardour/broadcast_info.h"
41 using namespace ARDOUR;
42 using namespace SessionUtils;
48 , _sample_format (ExportFormatBase::SF_16)
53 std::string samplerate () const
60 std::string sample_format () const
62 return enum_2_string (_sample_format);
65 std::string normalize () const
67 return _normalize ? "true" : "false";
70 std::string bwf () const
72 return _bwf ? "true" : "false";
76 ExportFormatBase::SampleFormat _sample_format;
81 static int export_session (Session *session,
83 ExportSettings const& settings)
85 ExportTimespanPtr tsp = session->get_export_handler()->add_timespan();
86 boost::shared_ptr<ExportChannelConfiguration> ccp = session->get_export_handler()->add_channel_config();
87 boost::shared_ptr<ARDOUR::ExportFilename> fnp = session->get_export_handler()->add_filename();
88 boost::shared_ptr<ARDOUR::BroadcastInfo> b;
92 tree.read_buffer(std::string(
93 "<?xml version=\"1.0\" encoding=\"UTF-8\"?>"
94 "<ExportFormatSpecification name=\"UTIL-WAV-EXPORT\" id=\"b1280899-0459-4aef-9dc9-7e2277fa6d24\">"
95 " <Encoding id=\"F_WAV\" type=\"T_Sndfile\" extension=\"wav\" name=\"WAV\" has-sample-format=\"true\" channel-limit=\"256\"/>"
96 " <SampleRate rate=\""+ settings.samplerate () +"\"/>"
97 " <SRCQuality quality=\"SRC_SincBest\"/>"
99 " <Option name=\"sample-format\" value=\"" + settings.sample_format () + "\"/>"
100 " <Option name=\"dithering\" value=\"D_None\"/>"
101 " <Option name=\"tag-metadata\" value=\"true\"/>"
102 " <Option name=\"tag-support\" value=\"false\"/>"
103 " <Option name=\"broadcast-info\" value=\"" + settings.bwf () +"\"/>"
104 " </EncodingOptions>"
106 " <Normalize enabled=\""+ settings.normalize () +"\" target=\"0\"/>"
109 " <Trim enabled=\"false\"/>"
110 " <Add enabled=\"false\">"
111 " <Duration format=\"Timecode\" hours=\"0\" minutes=\"0\" seconds=\"0\" frames=\"0\"/>"
115 " <Trim enabled=\"false\"/>"
116 " <Add enabled=\"false\">"
117 " <Duration format=\"Timecode\" hours=\"0\" minutes=\"0\" seconds=\"0\" frames=\"0\"/>"
122 "</ExportFormatSpecification>"
125 boost::shared_ptr<ExportFormatSpecification> fmp = session->get_export_handler()->add_format(*tree.root());
128 samplepos_t start, end;
129 start = session->current_start_sample();
130 end = session->current_end_sample();
131 tsp->set_range (start, end);
132 tsp->set_range_id ("session");
134 /* add master outs as default */
135 IO* master_out = session->master_out()->output().get();
137 PBD::warning << _("Export Util: No Master Out Ports to Connect for Audio Export") << endmsg;
141 for (uint32_t n = 0; n < master_out->n_ports().n_audio(); ++n) {
142 PortExportChannel * channel = new PortExportChannel ();
143 channel->add_port (master_out->audio (n));
144 ExportChannelPtr chan_ptr (channel);
145 ccp->register_channel (chan_ptr);
148 /* output filename */
149 if (outfile.empty ()) {
150 tsp->set_name ("session");
152 std::string dirname = Glib::path_get_dirname (outfile);
153 std::string basename = Glib::path_get_basename (outfile);
155 if (basename.size() > 4 && !basename.compare (basename.size() - 4, 4, ".wav")) {
156 basename = PBD::basename_nosuffix (basename);
159 fnp->set_folder(dirname);
160 tsp->set_name (basename);
163 /* set broadcast info */
165 b.reset (new BroadcastInfo);
166 b->set_from_session (*session, tsp->get_start ());
169 cout << "* Writing " << Glib::build_filename (fnp->get_folder(), tsp->name() + ".wav") << endl;
173 fnp->set_timespan(tsp);
174 fnp->include_label = false;
176 /* do audio export */
177 fmp->set_soundcloud_upload(false);
178 session->get_export_handler()->add_export_config (tsp, ccp, fmp, fnp, b);
179 session->get_export_handler()->do_export();
181 boost::shared_ptr<ARDOUR::ExportStatus> status = session->get_export_status ();
183 // TODO trap SIGINT -> status->abort();
185 while (status->running ()) {
186 double progress = 0.0;
187 switch (status->active_job) {
188 case ExportStatus::Normalizing:
189 progress = ((float) status->current_postprocessing_cycle) / status->total_postprocessing_cycles;
190 printf ("* Normalizing %.1f%% \r", 100. * progress); fflush (stdout);
192 case ExportStatus::Exporting:
193 progress = ((float) status->processed_samples_current_timespan) / status->total_samples_current_timespan;
194 printf ("* Exporting Audio %.1f%% \r", 100. * progress); fflush (stdout);
197 printf ("* Exporting... \r");
200 Glib::usleep (1000000);
206 printf ("* Done.\n");
210 static void usage () {
211 // help2man compatible format (standard GNU help-text)
212 printf (UTILNAME " - export an ardour session from the commandline.\n\n");
213 printf ("Usage: " UTILNAME " [ OPTIONS ] <session-dir> <session/snapshot-name>\n\n");
215 -b, --bitdepth <depth> set export-format (16, 24, 32, float)\n\
216 -B, --broadcast include broadcast wave header\n\
217 -h, --help display this help and exit\n\
218 -n, --normalize normalize signal level (to 0dBFS)\n\
219 -o, --output <file> export output file name\n\
220 -s, --samplerate <rate> samplerate to use\n\
221 -V, --version print version information and exit\n\
224 This tool exports the session-range of a given ardour-session to a wave file,\n\
225 using the master-bus outputs.\n\
226 By default a 16bit signed .wav file at session-rate is exported.\n\
227 If the no output-file is given, the session's export dir is used.\n\
229 Note: the tool expects a session-name without .ardour file-name extension.\n\
232 printf ("Report bugs to <http://tracker.ardour.org/>\n"
233 "Website: <http://ardour.org/>\n");
234 ::exit (EXIT_SUCCESS);
237 int main (int argc, char* argv[])
239 ExportSettings settings;
242 const char *optstring = "b:Bhno:s:V";
244 const struct option longopts[] = {
245 { "bitdepth", 1, 0, 'b' },
246 { "broadcast", 0, 0, 'B' },
247 { "help", 0, 0, 'h' },
248 { "normalize", 0, 0, 'n' },
249 { "output", 1, 0, 'o' },
250 { "samplerate", 1, 0, 's' },
251 { "version", 0, 0, 'V' },
255 while (EOF != (c = getopt_long (argc, argv,
256 optstring, longopts, (int *) 0))) {
260 switch (atoi (optarg)) {
262 settings._sample_format = ExportFormatBase::SF_16;
265 settings._sample_format = ExportFormatBase::SF_24;
268 settings._sample_format = ExportFormatBase::SF_32;
271 if (0 == strcmp (optarg, "float")) {
272 settings._sample_format = ExportFormatBase::SF_Float;
277 fprintf(stderr, "Invalid Bit Depth\n");
283 settings._bwf = true;
287 settings._normalize = true;
296 const int sr = atoi (optarg);
297 if (sr >= 8000 && sr <= 192000) {
298 settings._samplerate = sr;
300 fprintf(stderr, "Invalid Samplerate\n");
306 printf ("ardour-utils version %s\n\n", VERSIONSTRING);
307 printf ("Copyright (C) GPL 2015,2017 Robin Gareus <robin@gareus.org>\n");
316 cerr << "Error: unrecognized option. See --help for usage information.\n";
317 ::exit (EXIT_FAILURE);
322 if (optind + 2 > argc) {
323 cerr << "Error: Missing parameter. See --help for usage information.\n";
324 ::exit (EXIT_FAILURE);
327 SessionUtils::init(false);
330 s = SessionUtils::load_session (argv[optind], argv[optind+1]);
332 if (settings._samplerate == 0) {
333 settings._samplerate = s->nominal_sample_rate ();
336 export_session (s, outfile, settings);
338 SessionUtils::unload_session(s);
339 SessionUtils::cleanup();