8 #include "pbd/basename.h"
9 #include "pbd/enumwriter.h"
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"
23 using namespace ARDOUR;
24 using namespace SessionUtils;
30 , _sample_format (ExportFormatBase::SF_16)
35 std::string samplerate () const
42 std::string sample_format () const
44 return enum_2_string (_sample_format);
47 std::string normalize () const
49 return _normalize ? "true" : "false";
52 std::string bwf () const
54 return _bwf ? "true" : "false";
58 ExportFormatBase::SampleFormat _sample_format;
63 static int export_session (Session *session,
65 ExportSettings const& settings)
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;
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\"/>"
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 () +"\"/>"
88 " <Normalize enabled=\""+ settings.normalize () +"\" target=\"0\"/>"
91 " <Trim enabled=\"false\"/>"
92 " <Add enabled=\"false\">"
93 " <Duration format=\"Timecode\" hours=\"0\" minutes=\"0\" seconds=\"0\" samples=\"0\"/>"
97 " <Trim enabled=\"false\"/>"
98 " <Add enabled=\"false\">"
99 " <Duration format=\"Timecode\" hours=\"0\" minutes=\"0\" seconds=\"0\" samples=\"0\"/>"
104 "</ExportFormatSpecification>"
107 boost::shared_ptr<ExportFormatSpecification> fmp = session->get_export_handler()->add_format(*tree.root());
110 samplepos_t start, end;
111 start = session->current_start_sample();
112 end = session->current_end_sample();
113 tsp->set_range (start, end);
114 tsp->set_range_id ("session");
116 /* add master outs as default */
117 IO* master_out = session->master_out()->output().get();
119 PBD::warning << _("Export Util: No Master Out Ports to Connect for Audio Export") << endmsg;
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);
130 /* output filename */
131 if (outfile.empty ()) {
132 tsp->set_name ("session");
134 std::string dirname = Glib::path_get_dirname (outfile);
135 std::string basename = Glib::path_get_basename (outfile);
137 if (basename.size() > 4 && !basename.compare (basename.size() - 4, 4, ".wav")) {
138 basename = PBD::basename_nosuffix (basename);
141 fnp->set_folder(dirname);
142 tsp->set_name (basename);
145 /* set broadcast info */
147 b.reset (new BroadcastInfo);
148 b->set_from_session (*session, tsp->get_start ());
151 cout << "* Writing " << Glib::build_filename (fnp->get_folder(), tsp->name() + ".wav") << endl;
155 fnp->set_timespan(tsp);
156 fnp->include_label = false;
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();
163 boost::shared_ptr<ARDOUR::ExportStatus> status = session->get_export_status ();
165 // TODO trap SIGINT -> status->abort();
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);
174 case ExportStatus::Exporting:
175 progress = ((float) status->processed_samples_current_timespan) / status->total_samples_current_timespan;
176 printf ("* Exporting Audio %.1f%% \r", 100. * progress); fflush (stdout);
179 printf ("* Exporting... \r");
182 Glib::usleep (1000000);
188 printf ("* Done.\n");
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");
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\
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\
211 Note: the tool expects a session-name without .ardour file-name extension.\n\
214 printf ("Report bugs to <http://tracker.ardour.org/>\n"
215 "Website: <http://ardour.org/>\n");
219 int main (int argc, char* argv[])
221 ExportSettings settings;
224 const char *optstring = "b:Bhno:s:V";
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' },
237 while (EOF != (c = getopt_long (argc, argv,
238 optstring, longopts, (int *) 0))) {
242 switch (atoi (optarg)) {
244 settings._sample_format = ExportFormatBase::SF_16;
247 settings._sample_format = ExportFormatBase::SF_24;
250 settings._sample_format = ExportFormatBase::SF_32;
253 if (0 == strcmp (optarg, "float")) {
254 settings._sample_format = ExportFormatBase::SF_Float;
260 fprintf(stderr, "Invalid Bit Depth\n");
266 settings._bwf = true;
270 settings._normalize = true;
279 const int sr = atoi (optarg);
280 if (sr >= 8000 && sr <= 192000) {
281 settings._samplerate = sr;
283 fprintf(stderr, "Invalid Samplerate\n");
289 printf ("ardour-utils version %s\n\n", VERSIONSTRING);
290 printf ("Copyright (C) GPL 2015,2017 Robin Gareus <robin@gareus.org>\n");
299 usage (EXIT_FAILURE);
304 if (optind + 2 > argc) {
305 usage (EXIT_FAILURE);
308 SessionUtils::init(false);
311 s = SessionUtils::load_session (argv[optind], argv[optind+1]);
313 if (settings._samplerate == 0) {
314 settings._samplerate = s->nominal_sample_rate ();
317 export_session (s, outfile, settings);
319 SessionUtils::unload_session(s);
320 SessionUtils::cleanup();