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"
43 using namespace ARDOUR;
44 using namespace SessionUtils;
50 , _sample_format (ExportFormatBase::SF_16)
55 std::string samplerate () const
62 std::string sample_format () const
64 return enum_2_string (_sample_format);
67 std::string normalize () const
69 return _normalize ? "true" : "false";
72 std::string bwf () const
74 return _bwf ? "true" : "false";
78 ExportFormatBase::SampleFormat _sample_format;
83 static int export_session (Session *session,
85 ExportSettings const& settings)
87 ExportTimespanPtr tsp = session->get_export_handler()->add_timespan();
88 boost::shared_ptr<ExportChannelConfiguration> ccp = session->get_export_handler()->add_channel_config();
89 boost::shared_ptr<ARDOUR::ExportFilename> fnp = session->get_export_handler()->add_filename();
90 boost::shared_ptr<ARDOUR::BroadcastInfo> b;
94 tree.read_buffer(std::string(
95 "<?xml version=\"1.0\" encoding=\"UTF-8\"?>"
96 "<ExportFormatSpecification name=\"UTIL-WAV-EXPORT\" id=\"b1280899-0459-4aef-9dc9-7e2277fa6d24\">"
97 " <Encoding id=\"F_WAV\" type=\"T_Sndfile\" extension=\"wav\" name=\"WAV\" has-sample-format=\"true\" channel-limit=\"256\"/>"
98 " <SampleRate rate=\""+ settings.samplerate () +"\"/>"
99 " <SRCQuality quality=\"SRC_SincBest\"/>"
101 " <Option name=\"sample-format\" value=\"" + settings.sample_format () + "\"/>"
102 " <Option name=\"dithering\" value=\"D_None\"/>"
103 " <Option name=\"tag-metadata\" value=\"true\"/>"
104 " <Option name=\"tag-support\" value=\"false\"/>"
105 " <Option name=\"broadcast-info\" value=\"" + settings.bwf () +"\"/>"
106 " </EncodingOptions>"
108 " <Normalize enabled=\""+ settings.normalize () +"\" target=\"0\"/>"
111 " <Trim enabled=\"false\"/>"
112 " <Add enabled=\"false\">"
113 " <Duration format=\"Timecode\" hours=\"0\" minutes=\"0\" seconds=\"0\" frames=\"0\"/>"
117 " <Trim enabled=\"false\"/>"
118 " <Add enabled=\"false\">"
119 " <Duration format=\"Timecode\" hours=\"0\" minutes=\"0\" seconds=\"0\" frames=\"0\"/>"
124 "</ExportFormatSpecification>"
127 boost::shared_ptr<ExportFormatSpecification> fmp = session->get_export_handler()->add_format(*tree.root());
130 samplepos_t start, end;
131 start = session->current_start_sample();
132 end = session->current_end_sample();
133 tsp->set_range (start, end);
134 tsp->set_range_id ("session");
136 /* add master outs as default */
137 IO* master_out = session->master_out()->output().get();
139 PBD::warning << _("Export Util: No Master Out Ports to Connect for Audio Export") << endmsg;
143 for (uint32_t n = 0; n < master_out->n_ports().n_audio(); ++n) {
144 PortExportChannel * channel = new PortExportChannel ();
145 channel->add_port (master_out->audio (n));
146 ExportChannelPtr chan_ptr (channel);
147 ccp->register_channel (chan_ptr);
150 /* output filename */
151 if (outfile.empty ()) {
152 tsp->set_name ("session");
154 std::string dirname = Glib::path_get_dirname (outfile);
155 std::string basename = Glib::path_get_basename (outfile);
157 if (basename.size() > 4 && !basename.compare (basename.size() - 4, 4, ".wav")) {
158 basename = PBD::basename_nosuffix (basename);
161 fnp->set_folder(dirname);
162 tsp->set_name (basename);
165 /* set broadcast info */
167 b.reset (new BroadcastInfo);
168 b->set_from_session (*session, tsp->get_start ());
171 cout << "* Writing " << Glib::build_filename (fnp->get_folder(), tsp->name() + ".wav") << endl;
175 fnp->set_timespan(tsp);
176 fnp->include_label = false;
178 /* do audio export */
179 fmp->set_soundcloud_upload(false);
180 session->get_export_handler()->add_export_config (tsp, ccp, fmp, fnp, b);
181 session->get_export_handler()->do_export();
183 boost::shared_ptr<ARDOUR::ExportStatus> status = session->get_export_status ();
185 // TODO trap SIGINT -> status->abort();
187 while (status->running ()) {
188 double progress = 0.0;
189 switch (status->active_job) {
190 case ExportStatus::Normalizing:
191 progress = ((float) status->current_postprocessing_cycle) / status->total_postprocessing_cycles;
192 printf ("* Normalizing %.1f%% \r", 100. * progress); fflush (stdout);
194 case ExportStatus::Exporting:
195 progress = ((float) status->processed_samples_current_timespan) / status->total_samples_current_timespan;
196 printf ("* Exporting Audio %.1f%% \r", 100. * progress); fflush (stdout);
199 printf ("* Exporting... \r");
202 Glib::usleep (1000000);
206 status->finish (TRS_UI);
208 printf ("* Done.\n");
212 static void usage () {
213 // help2man compatible format (standard GNU help-text)
214 printf (UTILNAME " - export an ardour session from the commandline.\n\n");
215 printf ("Usage: " UTILNAME " [ OPTIONS ] <session-dir> <session/snapshot-name>\n\n");
217 -b, --bitdepth <depth> set export-format (16, 24, 32, float)\n\
218 -B, --broadcast include broadcast wave header\n\
219 -h, --help display this help and exit\n\
220 -n, --normalize normalize signal level (to 0dBFS)\n\
221 -o, --output <file> export output file name\n\
222 -s, --samplerate <rate> samplerate to use\n\
223 -V, --version print version information and exit\n\
226 This tool exports the session-range of a given ardour-session to a wave file,\n\
227 using the master-bus outputs.\n\
228 By default a 16bit signed .wav file at session-rate is exported.\n\
229 If the no output-file is given, the session's export dir is used.\n\
231 Note: the tool expects a session-name without .ardour file-name extension.\n\
234 printf ("Report bugs to <http://tracker.ardour.org/>\n"
235 "Website: <http://ardour.org/>\n");
236 ::exit (EXIT_SUCCESS);
239 int main (int argc, char* argv[])
241 ExportSettings settings;
244 const char *optstring = "b:Bhno:s:V";
246 const struct option longopts[] = {
247 { "bitdepth", 1, 0, 'b' },
248 { "broadcast", 0, 0, 'B' },
249 { "help", 0, 0, 'h' },
250 { "normalize", 0, 0, 'n' },
251 { "output", 1, 0, 'o' },
252 { "samplerate", 1, 0, 's' },
253 { "version", 0, 0, 'V' },
257 while (EOF != (c = getopt_long (argc, argv,
258 optstring, longopts, (int *) 0))) {
262 switch (atoi (optarg)) {
264 settings._sample_format = ExportFormatBase::SF_16;
267 settings._sample_format = ExportFormatBase::SF_24;
270 settings._sample_format = ExportFormatBase::SF_32;
273 if (0 == strcmp (optarg, "float")) {
274 settings._sample_format = ExportFormatBase::SF_Float;
279 fprintf(stderr, "Invalid Bit Depth\n");
285 settings._bwf = true;
289 settings._normalize = true;
298 const int sr = atoi (optarg);
299 if (sr >= 8000 && sr <= 192000) {
300 settings._samplerate = sr;
302 fprintf(stderr, "Invalid Samplerate\n");
308 printf ("ardour-utils version %s\n\n", VERSIONSTRING);
309 printf ("Copyright (C) GPL 2015,2017 Robin Gareus <robin@gareus.org>\n");
318 cerr << "Error: unrecognized option. See --help for usage information.\n";
319 ::exit (EXIT_FAILURE);
324 if (optind + 2 > argc) {
325 cerr << "Error: Missing parameter. See --help for usage information.\n";
326 ::exit (EXIT_FAILURE);
329 SessionUtils::init(false);
332 s = SessionUtils::load_session (argv[optind], argv[optind+1]);
334 if (settings._samplerate == 0) {
335 settings._samplerate = s->nominal_sample_rate ();
338 export_session (s, outfile, settings);
340 SessionUtils::unload_session(s);
341 SessionUtils::cleanup();