try to name/number new routes-from-templates to avoid colliding names if adding more...
[ardour2.git] / gtk2_ardour / export_dialog.cc
blobe7ad83b836eb1993e0d5b29cbd9de4bf492a0fa9
1 /*
2 Copyright (C) 1999-2005 Paul Davis
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
15 along with this program; if not, write to the Free Software
16 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
21 #include <unistd.h>
22 #include <utility>
23 #include <sys/stat.h>
24 #include <fstream>
26 #include <samplerate.h>
27 #include <pbd/convert.h>
28 #include <pbd/xml++.h>
30 #include <gtkmm2ext/utils.h>
31 #include <gtkmm2ext/window_title.h>
33 #include <ardour/export.h>
34 #include <ardour/sndfile_helpers.h>
35 #include <ardour/audio_track.h>
36 #include <ardour/audioregion.h>
37 #include <ardour/audioengine.h>
38 #include <ardour/gdither.h>
39 #include <ardour/utils.h>
40 #include <ardour/profile.h>
42 #include "export_dialog.h"
43 #include "ardour_ui.h"
44 #include "public_editor.h"
45 #include "keyboard.h"
46 #include "nag.h"
48 #include "i18n.h"
50 #define FRAME_NAME "BaseFrame"
52 using namespace std;
53 using namespace ARDOUR;
54 using namespace PBD;
55 using namespace sigc;
56 using namespace Gtk;
57 using namespace Gtkmm2ext;
59 static const gchar *sample_rates[] = {
60 N_("22.05kHz"),
61 N_("44.1kHz"),
62 N_("48kHz"),
63 N_("88.2kHz"),
64 N_("96kHz"),
65 N_("192kHz"),
69 static const gchar *src_quality[] = {
70 N_("best"),
71 N_("fastest"),
72 N_("linear"),
73 N_("better"),
74 N_("intermediate"),
78 static const gchar *dither_types[] = {
79 N_("None"),
80 N_("Rectangular"),
81 N_("Shaped Noise"),
82 N_("Triangular"),
86 static const gchar* channel_strings[] = {
87 N_("stereo"),
88 N_("mono"),
92 static const gchar* cue_file_types[] = {
93 N_("None"),
94 N_("CUE"),
95 N_("TOC"),
99 ExportDialog::ExportDialog(PublicEditor& e)
100 : ArdourDialog ("export dialog"),
101 editor (e),
102 format_table (9, 2),
103 format_frame (_("Format")),
104 cue_file_label (_("CD Marker File Type"), 1.0, 0.5),
105 channel_count_label (_("Channels"), 1.0, 0.5),
106 header_format_label (_("File Type"), 1.0, 0.5),
107 bitdepth_format_label (_("Sample Format"), 1.0, 0.5),
108 endian_format_label (_("Sample Endianness"), 1.0, 0.5),
109 sample_rate_label (_("Sample Rate"), 1.0, 0.5),
110 src_quality_label (_("Conversion Quality"), 1.0, 0.5),
111 dither_type_label (_("Dither Type"), 1.0, 0.5),
112 cuefile_only_checkbox (_("Export CD Marker File Only")),
113 file_browse_button (_("Browse")),
114 track_selector_button (_("Specific tracks ..."))
116 guint32 n;
117 guint32 len;
118 guint32 maxlen;
120 session = 0;
121 track_and_master_selection_allowed = true;
122 channel_count_selection_allowed = true;
123 export_cd_markers_allowed = true;
125 WindowTitle title(Glib::get_application_name());
126 title += _("Export");
128 set_title (title.get_string());
129 set_wmclass (X_("ardour_export"), "Ardour");
130 set_name ("ExportWindow");
131 add_events (Gdk::KEY_PRESS_MASK|Gdk::KEY_RELEASE_MASK);
133 spec.running = false;
135 file_entry.set_name ("ExportFileNameEntry");
137 master_list = ListStore::create (exp_cols);
138 master_selector.set_model (master_list);
140 master_selector.set_name ("ExportTrackSelector");
141 master_selector.set_size_request (-1, 100);
142 master_selector.append_column(_("Output"), exp_cols.output);
143 master_selector.append_column_editable(_("Left"), exp_cols.left);
144 master_selector.append_column_editable(_("Right"), exp_cols.right);
145 master_selector.get_column(0)->set_min_width(100);
147 master_selector.get_column(1)->set_min_width(40);
148 master_selector.get_column(1)->set_sizing(Gtk::TREE_VIEW_COLUMN_AUTOSIZE);
149 master_selector.get_column(2)->set_min_width(40);
150 master_selector.get_column(2)->set_sizing(Gtk::TREE_VIEW_COLUMN_AUTOSIZE);
151 master_selector.get_selection()->set_mode (Gtk::SELECTION_NONE);
153 track_list = ListStore::create (exp_cols);
154 track_selector.set_model (track_list);
156 track_selector.set_name ("ExportTrackSelector");
157 track_selector.set_size_request (-1, 130);
158 track_selector.append_column(_("Output"), exp_cols.output);
159 track_selector.append_column_editable(_("Left"), exp_cols.left);
160 track_selector.append_column_editable(_("Right"), exp_cols.right);
162 track_selector.get_column(0)->set_min_width(100);
163 track_selector.get_column(1)->set_min_width(40);
164 track_selector.get_column(1)->set_sizing(Gtk::TREE_VIEW_COLUMN_AUTOSIZE);
165 track_selector.get_column(2)->set_min_width(40);
166 track_selector.get_column(2)->set_sizing(Gtk::TREE_VIEW_COLUMN_AUTOSIZE);
167 track_selector.get_selection()->set_mode (Gtk::SELECTION_NONE);
169 progress_bar.set_name ("ExportProgress");
171 format_frame.add (format_table);
172 format_frame.set_name (FRAME_NAME);
174 track_scroll.set_policy(Gtk::POLICY_AUTOMATIC, Gtk::POLICY_AUTOMATIC);
175 master_scroll.set_policy(Gtk::POLICY_AUTOMATIC, Gtk::POLICY_AUTOMATIC);
177 get_vbox()->pack_start (file_frame, false, false);
179 hpacker.set_spacing (5);
180 hpacker.set_border_width (5);
181 hpacker.pack_start (format_frame, false, false);
183 master_scroll.add (master_selector);
184 track_scroll.add (track_selector);
186 master_scroll.set_size_request (220, 100);
187 track_scroll.set_size_request (220, 100);
189 /* we may hide some of these later */
190 track_vpacker.pack_start (master_scroll);
191 track_vpacker.pack_start (track_scroll);
192 track_vpacker.pack_start (track_selector_button, Gtk::PACK_EXPAND_PADDING);
194 hpacker.pack_start (track_vpacker);
196 get_vbox()->pack_start (hpacker);
198 track_selector_button.set_name ("EditorGTKButton");
199 track_selector_button.signal_clicked().connect (mem_fun(*this, &ExportDialog::track_selector_button_click));
201 get_vbox()->pack_start (progress_bar, false, false);
203 Gtkmm2ext::set_size_request_to_display_given_text (file_entry, X_("Kg/quite/a/reasonable/size/for/files/i/think"), 5, 8);
205 file_hbox.set_spacing (5);
206 file_hbox.set_border_width (5);
207 file_hbox.pack_start (file_entry, true, true);
208 file_hbox.pack_start (file_browse_button, false, false);
210 file_frame.add (file_hbox);
211 file_frame.set_border_width (5);
212 file_frame.set_name (FRAME_NAME);
214 /* pop_strings needs to be created on the stack because set_popdown_strings()
215 takes a reference.
218 vector<string> pop_strings = I18N (sample_rates);
219 Gtkmm2ext::set_popdown_strings (sample_rate_combo, pop_strings);
220 sample_rate_combo.set_active_text (pop_strings.front());
221 pop_strings = I18N (src_quality);
222 Gtkmm2ext::set_popdown_strings (src_quality_combo, pop_strings);
223 src_quality_combo.set_active_text (pop_strings.front());
224 pop_strings = I18N (dither_types);
225 Gtkmm2ext::set_popdown_strings (dither_type_combo, pop_strings);
226 dither_type_combo.set_active_text (pop_strings.front());
227 pop_strings = I18N (channel_strings);
228 Gtkmm2ext::set_popdown_strings (channel_count_combo, pop_strings);
229 channel_count_combo.set_active_text (pop_strings.front());
230 pop_strings = I18N ((const char **) sndfile_header_formats_strings);
231 Gtkmm2ext::set_popdown_strings (header_format_combo, pop_strings);
232 header_format_combo.set_active_text (pop_strings.front());
233 pop_strings = I18N ((const char **) sndfile_bitdepth_formats_strings);
234 Gtkmm2ext::set_popdown_strings (bitdepth_format_combo, pop_strings);
235 bitdepth_format_combo.set_active_text (pop_strings.front());
236 pop_strings = I18N ((const char **) sndfile_endian_formats_strings);
237 Gtkmm2ext::set_popdown_strings (endian_format_combo, pop_strings);
238 endian_format_combo.set_active_text (pop_strings.front());
239 pop_strings = I18N (cue_file_types);
240 Gtkmm2ext::set_popdown_strings (cue_file_combo, pop_strings);
241 cue_file_combo.set_active_text (pop_strings.front());
243 /* this will re-sensitized as soon as a non RIFF/WAV
244 header format is chosen.
247 endian_format_combo.set_sensitive (false);
249 /* determine longest strings at runtime */
251 maxlen = 0;
252 const char *longest = X_("gl"); /* translators: one ascender, one descender */
253 string longest_str;
255 for (n = 0; n < SNDFILE_HEADER_FORMATS; ++n) {
256 if ((len = strlen (sndfile_header_formats_strings[n])) > maxlen) {
257 maxlen = len;
258 longest = sndfile_header_formats_strings[n];
262 for (n = 0; n < SNDFILE_BITDEPTH_FORMATS; ++n) {
263 if ((len = strlen (sndfile_bitdepth_formats_strings[n])) > maxlen) {
264 maxlen = len;
265 longest = sndfile_bitdepth_formats_strings[n];
269 for (n = 0; n < SNDFILE_ENDIAN_FORMATS; ++n) {
270 if ((len = strlen (sndfile_endian_formats_strings[n])) > maxlen) {
271 maxlen = len;
272 longest = sndfile_endian_formats_strings[n];
276 longest_str = longest;
278 /* force ascender + descender */
280 longest_str[0] = 'g';
281 longest_str[1] = 'l';
283 //Gtkmm2ext::set_size_request_to_display_given_text (header_format_combo, longest_str.c_str(), 5+FUDGE, 5);
285 // TRANSLATORS: "slereg" is "stereo" with ascender and descender substituted
286 //Gtkmm2ext::set_size_request_to_display_given_text (channel_count_combo, _("slereg"), 5+FUDGE, 5);
288 /* header_format_combo.set_focus_on_click (true);
289 bitdepth_format_combo.set_focus_on_click (true);
290 endian_format_combo.set_focus_on_click (true);
291 channel_count_combo.set_focus_on_click (true);
292 src_quality_combo.set_focus_on_click (true);
293 dither_type_combo.set_focus_on_click (true);
294 sample_rate_combo.set_focus_on_click (true);
295 cue_file_combo.set_focus_on_click (true);
297 dither_type_label.set_name ("ExportFormatLabel");
298 sample_rate_label.set_name ("ExportFormatLabel");
299 src_quality_label.set_name ("ExportFormatLabel");
300 channel_count_label.set_name ("ExportFormatLabel");
301 header_format_label.set_name ("ExportFormatLabel");
302 bitdepth_format_label.set_name ("ExportFormatLabel");
303 endian_format_label.set_name ("ExportFormatLabel");
304 cue_file_label.set_name ("ExportFormatLabel");
306 header_format_combo.set_name ("ExportFormatDisplay");
307 bitdepth_format_combo.set_name ("ExportFormatDisplay");
308 endian_format_combo.set_name ("ExportFormatDisplay");
309 channel_count_combo.set_name ("ExportFormatDisplay");
310 dither_type_combo.set_name ("ExportFormatDisplay");
311 src_quality_combo.set_name ("ExportFormatDisplay");
312 sample_rate_combo.set_name ("ExportFormatDisplay");
313 cue_file_combo.set_name ("ExportFormatDisplay");
315 cuefile_only_checkbox.set_name ("ExportCheckbox");
317 format_table.set_homogeneous (false);
318 format_table.set_border_width (5);
319 format_table.set_col_spacings (5);
320 format_table.set_row_spacings (5);
322 int row = 0;
324 format_table.attach (channel_count_label, 0, 1, row, row+1);
325 format_table.attach (channel_count_combo, 1, 2, row, row+1);
327 row++;
329 format_table.attach (header_format_label, 0, 1, row, row+1);
330 format_table.attach (header_format_combo, 1, 2, row, row+1);
332 row++;
334 format_table.attach (bitdepth_format_label, 0, 1, row, row+1);
335 format_table.attach (bitdepth_format_combo, 1, 2, row, row+1);
337 row++;
339 if (!Profile->get_sae()) {
340 format_table.attach (endian_format_label, 0, 1, row, row+1);
341 format_table.attach (endian_format_combo, 1, 2, row, row+1);
342 row++;
345 format_table.attach (sample_rate_label, 0, 1, row, row+1);
346 format_table.attach (sample_rate_combo, 1, 2, row, row+1);
348 row++;
350 if (!Profile->get_sae()) {
351 format_table.attach (src_quality_label, 0, 1, row, row+1);
352 format_table.attach (src_quality_combo, 1, 2, row, row+1);
353 row++;
356 format_table.attach (dither_type_label, 0, 1, row, row+1);
357 format_table.attach (dither_type_combo, 1, 2, row, row+1);
359 row++;
361 if (!Profile->get_sae()) {
362 format_table.attach (cue_file_label, 0, 1, row, row+1);
363 format_table.attach (cue_file_combo, 1, 2, row, row+1);
364 row++;
366 format_table.attach (cuefile_only_checkbox, 0, 2, row, row+1);
369 file_entry.set_name ("ExportFileDisplay");
371 signal_delete_event().connect (mem_fun(*this, &ExportDialog::window_closed));
373 cancel_button = add_button (Stock::CANCEL, RESPONSE_CANCEL);
374 cancel_button->signal_clicked().connect (mem_fun(*this, &ExportDialog::end_dialog));
375 ok_button = add_button (_("Export"), RESPONSE_ACCEPT);
376 ok_button->signal_clicked().connect (mem_fun(*this, &ExportDialog::do_export));
378 file_browse_button.set_name ("EditorGTKButton");
379 file_browse_button.signal_clicked().connect (mem_fun(*this, &ExportDialog::browse));
381 channel_count_combo.signal_changed().connect (mem_fun(*this, &ExportDialog::channels_chosen));
382 bitdepth_format_combo.signal_changed().connect (mem_fun(*this, &ExportDialog::bitdepth_chosen));
383 header_format_combo.signal_changed().connect (mem_fun(*this, &ExportDialog::header_chosen));
384 sample_rate_combo.signal_changed().connect (mem_fun(*this, &ExportDialog::sample_rate_chosen));
385 cue_file_combo.signal_changed().connect (mem_fun(*this, &ExportDialog::cue_file_type_chosen));
388 ExportDialog::~ExportDialog()
392 void
393 ExportDialog::do_not_allow_track_and_master_selection()
395 track_and_master_selection_allowed = false;
396 track_vpacker.set_no_show_all();
399 void
400 ExportDialog::do_not_allow_channel_count_selection()
402 channel_count_selection_allowed = false;
403 channel_count_combo.set_no_show_all();
404 channel_count_label.set_no_show_all();
407 void
408 ExportDialog::do_not_allow_export_cd_markers()
410 export_cd_markers_allowed = false;
411 cue_file_label.set_no_show_all();
412 cue_file_combo.set_no_show_all();
413 cuefile_only_checkbox.set_no_show_all();
416 void
417 ExportDialog::connect_to_session (Session *s)
419 session = s;
420 session->GoingAway.connect (mem_fun(*this, &Window::hide_all));
422 switch (session->frame_rate()) {
423 case 22050:
424 sample_rate_combo.set_active_text (_("22.05kHz"));
425 break;
426 case 44100:
427 sample_rate_combo.set_active_text (_("44.1kHz"));
428 break;
429 case 48000:
430 sample_rate_combo.set_active_text (_("48kHz"));
431 break;
432 case 88200:
433 sample_rate_combo.set_active_text (_("88.2kHz"));
434 break;
435 case 96000:
436 sample_rate_combo.set_active_text (_("96kHz"));
437 break;
438 case 192000:
439 sample_rate_combo.set_active_text (_("192kHz"));
440 break;
441 default:
442 sample_rate_combo.set_active_text (_("44.1kHz"));
443 break;
446 src_quality_combo.set_sensitive (false);
448 set_state();
451 void
452 ExportDialog::set_state()
454 XMLNode* node = session->instant_xml(X_("ExportDialog"), session->path());
455 XMLProperty* prop;
457 if (node) {
459 if ((prop = node->property (X_("sample_rate"))) != 0) {
460 sample_rate_combo.set_active_text(prop->value());
462 if ((prop = node->property (X_("src_quality"))) != 0) {
463 src_quality_combo.set_active_text(prop->value());
465 if ((prop = node->property (X_("dither_type"))) != 0) {
466 dither_type_combo.set_active_text(prop->value());
468 if ((prop = node->property (X_("channel_count"))) != 0) {
469 channel_count_combo.set_active_text(prop->value());
471 if ((prop = node->property (X_("header_format"))) != 0) {
472 header_format_combo.set_active_text(prop->value());
474 if ((prop = node->property (X_("bitdepth_format"))) != 0) {
475 bitdepth_format_combo.set_active_text(prop->value());
477 if ((prop = node->property (X_("endian_format"))) != 0) {
478 endian_format_combo.set_active_text(prop->value());
480 if ((prop = node->property (X_("filename"))) != 0) {
481 file_entry.set_text(prop->value());
483 if ((prop = node->property (X_("cue_file_type"))) != 0) {
484 cue_file_combo.set_active_text(prop->value());
488 header_chosen ();
489 bitdepth_chosen();
490 channels_chosen();
491 sample_rate_chosen();
493 if (session->master_out()) {
494 track_scroll.hide ();
495 } else {
496 master_scroll.hide ();
497 track_selector_button.hide ();
500 if (!node) {
501 return;
504 if (session->master_out()) {
505 XMLNode* master = find_named_node(*node, (X_("Master")));
506 int nchns;
508 if (!master) {
510 /* default is to use all */
511 if (channel_count_combo.get_active_text() == _("mono")) {
512 nchns = 1;
513 } else {
514 nchns = 2;
517 TreeModel::Children rows = master_selector.get_model()->children();
518 for (uint32_t r = 0; r < session->master_out()->n_outputs(); ++r) {
519 if (nchns == 2) {
520 if (r % 2) {
521 rows[r][exp_cols.right] = true;
522 } else {
523 rows[r][exp_cols.left] = true;
525 } else {
526 rows[r][exp_cols.left] = true;
530 } else {
531 /* XXX use XML state */
535 XMLNode* tracks = find_named_node(*node, (X_("Tracks")));
536 if (!tracks) {
537 return;
540 XMLNodeList track_list = tracks->children(X_("Track"));
541 TreeModel::Children rows = track_selector.get_model()->children();
542 TreeModel::Children::iterator ri = rows.begin();
543 TreeModel::Row row;
545 for (XMLNodeIterator it = track_list.begin(); it != track_list.end(); ++it, ++ri) {
546 if (ri == rows.end()){
547 break;
550 XMLNode* track = *it;
551 row = *ri;
553 if ((prop = track->property(X_("channel1"))) != 0) {
554 if (prop->value() == X_("on")) {
555 row[exp_cols.left] = true;
556 } else {
557 row[exp_cols.left] = false;
561 if ((prop = track->property(X_("channel2"))) != 0) {
562 if (prop->value() == X_("on")) {
563 row[exp_cols.right] = true;
564 } else {
565 row[exp_cols.right] = false;
571 void
572 ExportDialog::save_state()
574 if (!session) {
575 return;
578 XMLNode* node = new XMLNode(X_("ExportDialog"));
580 node->add_property(X_("sample_rate"), sample_rate_combo.get_active_text());
581 node->add_property(X_("src_quality"), src_quality_combo.get_active_text());
582 node->add_property(X_("dither_type"), dither_type_combo.get_active_text());
583 node->add_property(X_("channel_count"), channel_count_combo.get_active_text());
584 node->add_property(X_("header_format"), header_format_combo.get_active_text());
585 node->add_property(X_("bitdepth_format"), bitdepth_format_combo.get_active_text());
586 node->add_property(X_("endian_format"), endian_format_combo.get_active_text());
587 node->add_property(X_("filename"), file_entry.get_text());
588 node->add_property(X_("cue_file_type"), cue_file_combo.get_active_text());
590 XMLNode* tracks = new XMLNode(X_("Tracks"));
592 TreeModel::Children rows = track_selector.get_model()->children();
593 TreeModel::Row row;
594 for (TreeModel::Children::iterator ri = rows.begin(); ri != rows.end(); ++ri) {
595 XMLNode* track = new XMLNode(X_("Track"));
597 row = *ri;
598 track->add_property(X_("channel1"), row[exp_cols.left] ? X_("on") : X_("off"));
599 track->add_property(X_("channel2"), row[exp_cols.right] ? X_("on") : X_("off"));
601 tracks->add_child_nocopy(*track);
603 node->add_child_nocopy(*tracks);
605 session->add_instant_xml(*node, session->path());
608 void
609 ExportDialog::set_range (nframes_t start, nframes_t end)
611 spec.start_frame = start;
612 spec.end_frame = end;
615 gint
616 ExportDialog::progress_timeout ()
618 progress_bar.set_fraction (spec.progress);
619 return TRUE;
622 void
623 frames_to_cd_frames_string (char* buf, nframes_t when, nframes_t fr)
626 long unsigned int remainder;
627 int mins, secs, frames;
629 mins = when / (60 * fr);
630 remainder = when - (mins * 60 * fr);
631 secs = remainder / fr;
632 remainder -= secs * fr;
633 frames = remainder / (fr / 75);
634 sprintf (buf, " %02d:%02d:%02d", mins, secs, frames);
638 struct LocationSortByStart {
639 bool operator() (Location *a, Location *b) {
640 return a->start() < b->start();
644 void
645 ExportDialog::export_toc_file (Locations::LocationList& locations, const string& path)
647 if(!export_cd_markers_allowed){
648 return;
651 long unsigned int last_end_time = spec.start_frame, last_start_time = spec.start_frame;
652 gchar buf[18];
654 /* Build the toc's file name from the specified audio file name. */
655 string basename = Glib::path_get_basename(path);
656 size_t ext_pos = basename.rfind('.');
657 if (ext_pos != string::npos) {
658 basename = basename.substr(0, ext_pos); /* strip file extension, if there is one */
660 string filepath = Glib::build_filename(Glib::path_get_dirname(path), basename + ".toc");
662 ofstream out (filepath.c_str());
663 if (!out) {
664 error << string_compose(_("Editor: cannot open \"%1\" as export file for CD toc file"), filepath) << endmsg;
665 return;
667 out << "CD_DA" << endl;
668 out << "CD_TEXT {" << endl << " LANGUAGE_MAP {" << endl << " 0 : EN" << endl << " }" << endl;
669 out << " LANGUAGE 0 {" << endl << " TITLE \"" << session->name() << "\"" << endl << " }" << endl << "}" << endl;
671 Locations::LocationList::iterator i;
672 Locations::LocationList temp;
674 for (i = locations.begin(); i != locations.end(); ++i) {
675 if ((*i)->start() >= spec.start_frame && (*i)->end() <= spec.end_frame && (*i)->is_cd_marker() && !(*i)->is_end()) {
676 temp.push_back (*i);
680 if (temp.size() > 0) {
681 LocationSortByStart cmp;
682 temp.sort (cmp);
683 Location * curr_range = 0;
684 Locations::LocationList::iterator nexti;
686 for (i = temp.begin(); i != temp.end(); ++i) {
688 if ((*i)->start() >= last_end_time)
690 /* this is a track, defined by a cd range marker or a cd location marker outside of a cd range */
691 out << endl << "TRACK AUDIO" << endl;
693 if ((*i)->cd_info.find("scms") != (*i)->cd_info.end()) {
694 out << "NO ";
696 out << "COPY" << endl;
698 if ((*i)->cd_info.find("preemph") != (*i)->cd_info.end()) {
699 out << "PRE_EMPHASIS" << endl;
700 } else {
701 out << "NO PRE_EMPHASIS" << endl;
704 if ((*i)->cd_info.find("isrc") != (*i)->cd_info.end()) {
705 out << "ISRC \"" << (*i)->cd_info["isrc"] << "\"" << endl;
708 out << "CD_TEXT {" << endl << " LANGUAGE 0 {" << endl << " TITLE \"" << (*i)->name() << "\"" << endl;
709 if ((*i)->cd_info.find("performer") != (*i)->cd_info.end()) {
710 out << " PERFORMER \"" << (*i)->cd_info["performer"] << "\"" << endl;
712 if ((*i)->cd_info.find("string_composer") != (*i)->cd_info.end()) {
713 out << " COMPOSER \"" << (*i)->cd_info["string_composer"] << "\"" << endl;
716 if ((*i)->cd_info.find("isrc") != (*i)->cd_info.end()) {
717 out << " ISRC \"";
718 out << (*i)->cd_info["isrc"].substr(0,2) << "-";
719 out << (*i)->cd_info["isrc"].substr(2,3) << "-";
720 out << (*i)->cd_info["isrc"].substr(5,2) << "-";
721 out << (*i)->cd_info["isrc"].substr(7,5) << "\"" << endl;
724 out << " }" << endl << "}" << endl;
726 frames_to_cd_frames_string (buf, last_end_time - spec.start_frame, session->frame_rate());
727 out << "FILE \"" << Glib::path_get_basename(path) << "\"" << buf;
729 if ((*i)->is_mark()) {
730 // a mark track location needs to look ahead to the next marker's start to determine length
731 nexti = i;
732 ++nexti;
733 if (nexti != temp.end()) {
734 frames_to_cd_frames_string (buf, (*nexti)->start() - last_end_time, session->frame_rate());
735 out << buf << endl;
737 frames_to_cd_frames_string (buf, (*i)->start() - last_end_time, session->frame_rate());
738 out << "START" << buf << endl;
740 last_start_time = (*i)->start();
741 last_end_time = (*nexti)->start();
743 else {
744 // this was the last marker, use session end
745 frames_to_cd_frames_string (buf, spec.end_frame - last_end_time, session->frame_rate());
746 out << buf << endl;
748 frames_to_cd_frames_string (buf, (*i)->start() - last_end_time, session->frame_rate());
749 out << "START" << buf << endl;
751 last_start_time = (*i)->start();
752 last_end_time = spec.end_frame;
755 curr_range = 0;
757 else {
758 // range
759 frames_to_cd_frames_string (buf, (*i)->end() - last_end_time, session->frame_rate());
760 out << buf << endl;
762 frames_to_cd_frames_string (buf, (*i)->start() - last_end_time, session->frame_rate());
763 out << "START" << buf << endl;
765 last_start_time = (*i)->start();
766 last_end_time = (*i)->end();
768 curr_range = (*i);
772 else if ((*i)->is_mark())
774 /* this is an index within a track */
776 frames_to_cd_frames_string (buf, (*i)->start() - last_start_time, session->frame_rate());
777 out << "INDEX" << buf << endl;
784 void
785 ExportDialog::export_cue_file (Locations::LocationList& locations, const string& path)
787 if(!export_cd_markers_allowed){
788 return;
791 gchar buf[18];
792 long unsigned int last_track_end = spec.start_frame;
793 int numtracks = 0, tracknum = 0, indexnum = 0;
795 /* Build the cue sheet's file name from the specified audio file name. */
796 string basename = Glib::path_get_basename(path);
797 size_t ext_pos = basename.rfind('.');
798 if (ext_pos != string::npos) {
799 basename = basename.substr(0, ext_pos); /* strip file extension, if there is one */
801 string filepath = Glib::build_filename(Glib::path_get_dirname(path), basename + ".cue");
803 ofstream out (filepath.c_str());
804 if (!out) {
805 error << string_compose(_("Editor: cannot open \"%1\" as export file for CD cue file"), filepath) << endmsg;
806 return;
809 Locations::LocationList::iterator i;
810 Locations::LocationList temp;
812 for (i = locations.begin(); i != locations.end(); ++i) {
813 if ((*i)->start() >= spec.start_frame && (*i)->end() <= spec.end_frame && (*i)->is_cd_marker() && !(*i)->is_end()) {
814 temp.push_back (*i);
815 if (!(*i)->is_mark()) {
816 numtracks++;
821 out << "REM Cue file generated by Ardour" << endl;
822 out << "TITLE \"" << session->name() << "\"" << endl;
824 out << "FILE \"" << Glib::path_get_basename(path) << "\" ";
826 /* The cue sheet syntax has originally five file types:
827 WAVE : 44.1 kHz, 16 Bit (little endian)
828 AIFF : 44.1 kHz, 16 Bit (big endian)
829 BINARY : 44.1 kHz, 16 Bit (little endian)
830 MOTOROLA : 44.1 kHz, 16 Bit (big endian)
833 We want to use cue sheets not only as CD images but also as general playlyist
834 format, thus for WAVE and AIFF we don't care if it's really 44.1 kHz/16 Bit, the
835 soundfile's header shows it anyway. But for the raw formats, i.e. BINARY
836 and MOTOROLA we do care, because no header would tell us about a different format.
838 For all other formats we just make up our own file type. MP3 is not supported
839 at the moment.
841 int file_format = sndfile_header_format_from_string (header_format_combo.get_active_text ());
842 if ((file_format & SF_FORMAT_TYPEMASK) == SF_FORMAT_WAV) {
843 out << "WAVE";
844 } else if ((file_format & SF_FORMAT_TYPEMASK) == SF_FORMAT_AIFF) {
845 out << "AIFF";
846 } else if ( ((file_format & SF_FORMAT_TYPEMASK) == SF_FORMAT_RAW)
847 && (sndfile_bitdepth_format_from_string(bitdepth_format_combo.get_active_text()) == SF_FORMAT_PCM_16)
848 && (sample_rate_combo.get_active_text() == _("44.1kHz")) ) {
849 /* raw audio, 16 Bit, 44.1 kHz */
850 if (sndfile_endian_format_from_string(endian_format_combo.get_active_text()) == SF_ENDIAN_LITTLE) {
851 out << "BINARY";
852 } else {
853 out << "MOTOROLA";
855 } else {
856 out << (header_format_combo.get_active_text());
858 out << endl;
860 if (false && numtracks == 0) {
861 /* the user has supplied no track markers.
862 the entire export is treated as one track.
865 numtracks++;
866 tracknum++;
867 indexnum = 0;
869 snprintf (buf, sizeof(buf), " TRACK %02d AUDIO", tracknum);
870 out << buf << endl;
871 out << " FLAGS DCP" << endl;
873 /* use the session name*/
875 out << " TITLE \"" << session->name() << "\"" << endl;
877 /* No pregap is specified in this case, adding the default pregap
878 is left to the burning application. */
880 out << " INDEX 01 00:00:00" << endl;
881 indexnum = 2;
882 last_track_end = spec.end_frame;
885 if (temp.size()) {
886 LocationSortByStart cmp;
887 temp.sort (cmp);
888 Location * curr_range = 0;
889 Locations::LocationList::iterator nexti;
891 for ( i = temp.begin(); i != temp.end(); ++i) {
893 if ((*i)->start() >= last_track_end)
895 /* this is a track and it doesn't start inside another one*/
897 tracknum++;
898 indexnum = 0;
900 snprintf (buf, sizeof(buf), " TRACK %02d AUDIO", tracknum);
901 out << buf << endl;
903 out << " FLAGS" ;
904 if ((*i)->cd_info.find("scms") != (*i)->cd_info.end()) {
905 out << " SCMS";
906 } else {
907 out << " DCP";
909 if ((*i)->cd_info.find("preemph") != (*i)->cd_info.end()) {
910 out << " PRE";
912 out << endl;
914 if ((*i)->cd_info.find("isrc") != (*i)->cd_info.end()) {
915 out << " ISRC " << (*i)->cd_info["isrc"] << endl;
918 if ((*i)->name() != "") {
919 out << " TITLE \"" << (*i)->name() << "\"" << endl;
922 if ((*i)->cd_info.find("performer") != (*i)->cd_info.end()) {
923 out << " PERFORMER \"" << (*i)->cd_info["performer"] << "\"" << endl;
926 if ((*i)->cd_info.find("string_composer") != (*i)->cd_info.end()) {
927 out << " SONGWRITER \"" << (*i)->cd_info["string_composer"] << "\"" << endl;
930 /* only print "Index 00" if not at the same position as "Index 01" */
931 if (last_track_end != (*i)->start()) {
932 frames_to_cd_frames_string (buf, last_track_end - spec.start_frame, session->frame_rate());
933 out << " INDEX 00" << buf << endl;
936 indexnum++;
938 if ((*i)->is_mark()) {
939 // need to find the next start to define the end
940 nexti = i;
941 ++nexti;
942 if (nexti != temp.end()) {
943 last_track_end = (*nexti)->start();
945 else {
946 last_track_end = spec.end_frame;
948 curr_range = 0;
950 else {
951 last_track_end = (*i)->end();
952 curr_range = (*i);
956 if ((tracknum > 0) && ((*i)->start() < last_track_end)) {
957 /*this is an index and it lies within a track*/
958 snprintf (buf, sizeof(buf), " INDEX %02d", indexnum);
959 out << buf;
960 frames_to_cd_frames_string (buf,(*i)->start() - spec.start_frame, session->frame_rate());
961 out << buf << endl;
962 indexnum++;
969 void
970 ExportDialog::do_export_cd_markers (const string& path,const string& cuefile_type)
972 if (cuefile_type == _("TOC")) {
973 session->locations()->apply (*this, &ExportDialog::export_toc_file, path);
974 } else {
975 session->locations()->apply (*this, &ExportDialog::export_cue_file, path);
980 void
981 ExportDialog::do_export ()
983 string filepath = file_entry.get_text();
985 if (!ARDOUR_UI::instance()->the_engine().connected()) {
986 MessageDialog msg (*this,
987 _("Not connected to audioengine"),
988 true,
989 MESSAGE_ERROR,
990 BUTTONS_OK);
991 msg.set_secondary_text (_("Ardour cannot export audio when disconnected"));
992 msg.present ();
993 msg.run ();
994 return;
997 if (!wants_dir()) {
999 /* maybe add suffix */
1001 int file_format = sndfile_header_format_from_string (header_format_combo.get_active_text ());
1003 if ((file_format & SF_FORMAT_TYPEMASK) == SF_FORMAT_WAV) {
1004 if (filepath.find (".wav") != filepath.length() - 4) {
1005 filepath += ".wav";
1007 } else if ((file_format & SF_FORMAT_TYPEMASK) == SF_FORMAT_AIFF) {
1008 if (filepath.find (".aiff") != filepath.length() - 5) {
1009 filepath += ".aiff";
1011 } else if ((file_format & SF_FORMAT_TYPEMASK) == SF_FORMAT_W64) {
1012 if (filepath.find (".w64") != filepath.length() - 4) {
1013 filepath += ".w64";
1015 } else if ((file_format & SF_FORMAT_TYPEMASK) == SF_FORMAT_FLAC) {
1016 if (filepath.find (".flac") != filepath.length() - 5) {
1017 filepath += ".flac";
1019 } else if ((file_format & SF_FORMAT_TYPEMASK) == SF_FORMAT_CAF) {
1020 if (filepath.find (".caf") != filepath.length() - 4) {
1021 filepath += ".caf";
1025 /* others ? */
1028 if(!is_filepath_valid(filepath)){
1029 return;
1032 if (!Profile->get_sae() && export_cd_markers_allowed) {
1033 if (cue_file_combo.get_active_text () != _("None")) {
1034 do_export_cd_markers (filepath, cue_file_combo.get_active_text ());
1037 if (cuefile_only_checkbox.get_active()) {
1038 end_dialog ();
1039 return;
1043 ok_button->set_sensitive(false);
1044 save_state();
1046 set_modal (true);
1048 // read user input into spec
1049 initSpec(filepath);
1051 progress_connection = Glib::signal_timeout().connect (mem_fun(*this, &ExportDialog::progress_timeout), 100);
1052 cancel_label.set_text (_("Stop Export"));
1054 session->pre_export ();
1056 export_audio_data();
1058 progress_connection.disconnect ();
1059 end_dialog ();
1061 /* if not stopped early and not SAE, ask for money, maybe */
1063 if (!spec.stop && !Profile->get_sae()) {
1065 NagScreen* ns = NagScreen::maybe_nag (_("export"));
1067 if (ns) {
1068 ns->nag ();
1069 delete ns;
1074 void
1075 ExportDialog::end_dialog ()
1077 if (spec.running) {
1078 spec.stop = true;
1080 while (spec.running) {
1081 if (gtk_events_pending()) {
1082 gtk_main_iteration ();
1083 } else {
1084 usleep (10000);
1089 session->finalize_audio_export ();
1091 hide_all ();
1093 set_modal (false);
1094 ok_button->set_sensitive(true);
1097 void
1098 ExportDialog::start_export ()
1100 if (session == 0) {
1101 return;
1104 /* If the filename hasn't been set before, use the
1105 current session's export directory as a default
1106 location for the export.
1109 if (file_entry.get_text().length() == 0) {
1110 Glib::ustring export_path = session->export_dir();
1112 if (!wants_dir()) {
1113 export_path = Glib::build_filename (export_path, "export.wav");
1116 file_entry.set_text (export_path);
1119 progress_bar.set_fraction (0);
1120 cancel_label.set_text (_("Cancel"));
1122 show_all ();
1124 if (session->master_out()) {
1125 track_scroll.hide ();
1126 } else {
1127 master_scroll.hide ();
1128 track_selector_button.hide ();
1132 void
1133 ExportDialog::header_chosen ()
1135 if (sndfile_header_format_from_string (header_format_combo.get_active_text ()) == SF_FORMAT_WAV) {
1136 endian_format_combo.set_sensitive (false);
1137 } else {
1138 endian_format_combo.set_sensitive (true);
1142 void
1143 ExportDialog::bitdepth_chosen ()
1145 int format = sndfile_bitdepth_format_from_string (bitdepth_format_combo.get_active_text ());
1146 switch (format) {
1147 case SF_FORMAT_PCM_24:
1148 case SF_FORMAT_PCM_32:
1149 case SF_FORMAT_FLOAT:
1150 dither_type_combo.set_sensitive (false);
1151 break;
1153 default:
1154 dither_type_combo.set_sensitive (true);
1155 break;
1159 void
1160 ExportDialog::cue_file_type_chosen ()
1162 if (cue_file_combo.get_active_text () != "None") {
1163 cuefile_only_checkbox.set_sensitive (true);
1164 } else {
1165 cuefile_only_checkbox.set_active (false);
1166 cuefile_only_checkbox.set_sensitive (false);
1170 void
1171 ExportDialog::sample_rate_chosen ()
1173 string sr_str = sample_rate_combo.get_active_text();
1174 nframes_t rate;
1176 if (sr_str == N_("22.05kHz")) {
1177 rate = 22050;
1178 } else if (sr_str == _("44.1kHz")) {
1179 rate = 44100;
1180 } else if (sr_str == _("48kHz")) {
1181 rate = 48000;
1182 } else if (sr_str == _("88.2kHz")) {
1183 rate = 88200;
1184 } else if (sr_str == _("96kHz")) {
1185 rate = 96000;
1186 } else if (sr_str == _("192kHz")) {
1187 rate = 192000;
1188 } else {
1189 rate = session->frame_rate();
1192 if (rate != session->frame_rate()) {
1193 src_quality_combo.set_sensitive (true);
1194 } else {
1195 src_quality_combo.set_sensitive (false);
1199 void
1200 ExportDialog::channels_chosen ()
1202 bool mono;
1204 mono = (channel_count_combo.get_active_text() == _("mono"));
1206 if (mono) {
1207 track_selector.get_column(2)->set_visible(false);
1208 track_selector.get_column(1)->set_title(_("Export"));
1210 if (session->master_out()) {
1211 master_selector.get_column(2)->set_visible(false);
1212 master_selector.get_column(1)->set_title(_("Export"));
1215 } else {
1216 track_selector.get_column(2)->set_visible(true);
1217 track_selector.get_column(1)->set_title(_("Left"));
1219 if (session->master_out()) {
1220 master_selector.get_column(2)->set_visible(true);
1221 master_selector.get_column(1)->set_title(_("Left"));
1225 fill_lists();
1228 void
1229 ExportDialog::fill_lists ()
1231 track_list->clear();
1232 master_list->clear();
1234 boost::shared_ptr<Session::RouteList> routes = session->get_routes ();
1236 for (Session::RouteList::iterator ri = routes->begin(); ri != routes->end(); ++ri) {
1238 boost::shared_ptr<Route> route = (*ri);
1240 if (route->hidden()) {
1241 continue;
1244 for (uint32_t i=0; i < route->n_outputs(); ++i) {
1245 string name;
1246 if (route->n_outputs() == 1) {
1247 name = route->name();
1248 } else {
1249 name = string_compose("%1: out-%2", route->name(), i+1);
1252 if (route == session->master_out()) {
1253 TreeModel::iterator iter = master_list->append();
1254 TreeModel::Row row = *iter;
1255 row[exp_cols.output] = name;
1256 row[exp_cols.left] = false;
1257 row[exp_cols.right] = false;
1258 row[exp_cols.port] = route->output (i);
1259 } else {
1260 TreeModel::iterator iter = track_list->append();
1261 TreeModel::Row row = *iter;
1262 row[exp_cols.output] = name;
1263 row[exp_cols.left] = false;
1264 row[exp_cols.right] = false;
1265 row[exp_cols.port] = route->output (i);
1272 bool
1273 ExportDialog::is_filepath_valid(string &filepath)
1275 // sanity check file name first
1277 struct stat statbuf;
1279 if (filepath.empty()) {
1280 string txt = _("Please enter a valid filename.");
1281 MessageDialog msg (*this, txt, false, MESSAGE_ERROR, BUTTONS_OK, true);
1282 msg.run();
1283 return false;
1286 // check if file exists already and warn
1288 if (stat (filepath.c_str(), &statbuf) == 0) {
1289 if (S_ISDIR (statbuf.st_mode)) {
1290 string txt = _("Please specify a complete filename for the audio file.");
1291 MessageDialog msg (*this, txt, false, MESSAGE_ERROR, BUTTONS_OK, true);
1292 msg.run();
1293 return false;
1295 else {
1296 string txt = _("File already exists, do you want to overwrite it?");
1297 MessageDialog msg (*this, txt, false, MESSAGE_QUESTION, BUTTONS_YES_NO, true);
1298 if ((ResponseType) msg.run() == Gtk::RESPONSE_NO) {
1299 return false;
1304 // directory needs to exist and be writable
1306 string dirpath = Glib::path_get_dirname (filepath);
1307 if (::access (dirpath.c_str(), W_OK) != 0) {
1308 string txt = _("Cannot write file in: ") + dirpath;
1309 MessageDialog msg (*this, txt, false, MESSAGE_ERROR, BUTTONS_OK, true);
1310 msg.run();
1311 return false;
1314 return true;
1317 void
1318 ExportDialog::initSpec(string &filepath)
1320 spec.path = filepath;
1321 spec.progress = 0;
1322 spec.running = false;
1323 spec.stop = false;
1324 spec.port_map.clear();
1326 if (channel_count_combo.get_active_text() == _("mono")) {
1327 spec.channels = 1;
1328 } else {
1329 spec.channels = 2;
1332 spec.format = 0;
1334 spec.format |= sndfile_header_format_from_string (header_format_combo.get_active_text ());
1336 if (!Profile->get_sae()) {
1337 if (((spec.format & SF_FORMAT_TYPEMASK) != SF_FORMAT_WAV)) {
1338 /* RIFF/WAV specifies endianess */
1339 spec.format |= sndfile_endian_format_from_string (endian_format_combo.get_active_text ());
1343 spec.format |= sndfile_bitdepth_format_from_string (bitdepth_format_combo.get_active_text ());
1345 string sr_str = sample_rate_combo.get_active_text();
1346 if (sr_str == N_("22.05kHz")) {
1347 spec.sample_rate = 22050;
1348 } else if (sr_str == _("44.1kHz")) {
1349 spec.sample_rate = 44100;
1350 } else if (sr_str == _("48kHz")) {
1351 spec.sample_rate = 48000;
1352 } else if (sr_str == _("88.2kHz")) {
1353 spec.sample_rate = 88200;
1354 } else if (sr_str == _("96kHz")) {
1355 spec.sample_rate = 96000;
1356 } else if (sr_str == _("192kHz")) {
1357 spec.sample_rate = 192000;
1358 } else {
1359 spec.sample_rate = session->frame_rate();
1362 if (Profile->get_sae()) {
1363 spec.src_quality = SRC_SINC_BEST_QUALITY;
1364 } else {
1365 string src_str = src_quality_combo.get_active_text();
1366 if (src_str == _("fastest")) {
1367 spec.src_quality = SRC_ZERO_ORDER_HOLD;
1368 } else if (src_str == _("linear")) {
1369 spec.src_quality = SRC_LINEAR;
1370 } else if (src_str == _("better")) {
1371 spec.src_quality = SRC_SINC_FASTEST;
1372 } else if (src_str == _("intermediate")) {
1373 spec.src_quality = SRC_SINC_MEDIUM_QUALITY;
1374 } else {
1375 spec.src_quality = SRC_SINC_BEST_QUALITY;
1379 string dither_str = dither_type_combo.get_active_text();
1380 if (dither_str == _("None")) {
1381 spec.dither_type = GDitherNone;
1382 } else if (dither_str == _("Rectangular")) {
1383 spec.dither_type = GDitherRect;
1384 } else if (dither_str == _("Triangular")) {
1385 spec.dither_type = GDitherTri;
1386 } else {
1387 spec.dither_type = GDitherShaped;
1390 write_track_and_master_selection_to_spec();
1394 void
1395 ExportDialog::write_track_and_master_selection_to_spec()
1397 if(!track_and_master_selection_allowed){
1398 return;
1401 uint32_t chan=0;
1402 Port *last_port = 0;
1404 TreeModel::Children rows = master_selector.get_model()->children();
1405 TreeModel::Children::iterator ri;
1406 TreeModel::Row row;
1407 for (ri = rows.begin(); ri != rows.end(); ++ri) {
1408 row = *ri;
1409 Port* port = row[exp_cols.port];
1411 if (last_port != port) {
1412 chan = 0;
1415 if (row[exp_cols.left]) {
1416 spec.port_map[0].push_back (std::pair<Port*,uint32_t>(port, chan));
1419 if (spec.channels == 2) {
1420 if (row[exp_cols.right]) {
1421 spec.port_map[1].push_back (std::pair<Port*,uint32_t>(port, chan));
1426 chan = 0;
1427 rows = track_selector.get_model()->children();
1429 for (ri = rows.begin(); ri != rows.end(); ++ri) {
1430 row = *ri;
1432 Port* port = row[exp_cols.port];
1434 if (last_port != port) {
1435 chan = 0;
1438 if (row[exp_cols.left]) {
1439 spec.port_map[0].push_back (std::pair<Port*,uint32_t>(port, chan));
1442 if (spec.channels == 2) {
1443 if (row[exp_cols.right]) {
1444 spec.port_map[1].push_back (std::pair<Port*,uint32_t>(port, chan));
1449 last_port = port;
1450 ++chan;
1455 gint
1456 ExportDialog::window_closed (GdkEventAny *ignored)
1458 end_dialog ();
1459 return TRUE;
1462 void
1463 ExportDialog::browse ()
1465 FileChooserDialog dialog("Export to file", browse_action());
1466 dialog.set_transient_for(*this);
1467 dialog.set_filename (file_entry.get_text());
1469 dialog.add_button(Gtk::Stock::CANCEL, Gtk::RESPONSE_CANCEL);
1470 dialog.add_button(Gtk::Stock::OK, Gtk::RESPONSE_OK);
1472 int result = dialog.run();
1474 if (result == Gtk::RESPONSE_OK) {
1475 string filename = dialog.get_filename();
1477 if (filename.length()) {
1478 file_entry.set_text (filename);
1483 void
1484 ExportDialog::track_selector_button_click ()
1486 if (track_scroll.is_visible ()) {
1487 track_scroll.hide ();
1488 } else {
1489 track_scroll.show_all ();