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.
26 #include <samplerate.h>
27 #include <pbd/convert.h>
28 #include <pbd/xml++.h>
30 #include <gtkmm2ext/utils.h>
32 #include <ardour/export.h>
33 #include <ardour/sndfile_helpers.h>
34 #include <ardour/audio_track.h>
35 #include <ardour/audioregion.h>
36 #include <ardour/audioengine.h>
37 #include <ardour/gdither.h>
38 #include <ardour/utils.h>
39 #include <ardour/profile.h>
41 #include "export_dialog.h"
42 #include "ardour_ui.h"
43 #include "public_editor.h"
49 #define FRAME_NAME "BaseFrame"
52 using namespace ARDOUR;
56 using namespace Gtkmm2ext;
58 static const gchar *sample_rates[] = {
68 static const gchar *src_quality[] = {
77 static const gchar *dither_types[] = {
85 static const gchar* channel_strings[] = {
91 static const gchar* cue_file_types[] = {
98 ExportDialog::ExportDialog(PublicEditor& e)
99 : ArdourDialog ("export dialog"),
102 format_frame (_("Format")),
103 cue_file_label (_("CD Marker File Type"), 1.0, 0.5),
104 channel_count_label (_("Channels"), 1.0, 0.5),
105 header_format_label (_("File Type"), 1.0, 0.5),
106 bitdepth_format_label (_("Sample Format"), 1.0, 0.5),
107 endian_format_label (_("Sample Endianness"), 1.0, 0.5),
108 sample_rate_label (_("Sample Rate"), 1.0, 0.5),
109 src_quality_label (_("Conversion Quality"), 1.0, 0.5),
110 dither_type_label (_("Dither Type"), 1.0, 0.5),
111 cuefile_only_checkbox (_("Export CD Marker File Only")),
112 file_browse_button (_("Browse")),
113 track_selector_button (_("Specific tracks ..."))
120 track_and_master_selection_allowed = true;
121 channel_count_selection_allowed = true;
122 export_cd_markers_allowed = true;
124 set_title (_("Export"));
125 set_wmclass (X_("ardour_export"), "Ardour");
126 set_name ("ExportWindow");
127 add_events (Gdk::KEY_PRESS_MASK|Gdk::KEY_RELEASE_MASK);
129 spec.running = false;
131 file_entry.set_name ("ExportFileNameEntry");
133 master_list = ListStore::create (exp_cols);
134 master_selector.set_model (master_list);
136 master_selector.set_name ("ExportTrackSelector");
137 master_selector.set_size_request (-1, 100);
138 master_selector.append_column(_("Output"), exp_cols.output);
139 master_selector.append_column_editable(_("Left"), exp_cols.left);
140 master_selector.append_column_editable(_("Right"), exp_cols.right);
141 master_selector.get_column(0)->set_min_width(100);
143 master_selector.get_column(1)->set_min_width(40);
144 master_selector.get_column(1)->set_sizing(Gtk::TREE_VIEW_COLUMN_AUTOSIZE);
145 master_selector.get_column(2)->set_min_width(40);
146 master_selector.get_column(2)->set_sizing(Gtk::TREE_VIEW_COLUMN_AUTOSIZE);
147 master_selector.get_selection()->set_mode (Gtk::SELECTION_NONE);
149 track_list = ListStore::create (exp_cols);
150 track_selector.set_model (track_list);
152 track_selector.set_name ("ExportTrackSelector");
153 track_selector.set_size_request (-1, 130);
154 track_selector.append_column(_("Output"), exp_cols.output);
155 track_selector.append_column_editable(_("Left"), exp_cols.left);
156 track_selector.append_column_editable(_("Right"), exp_cols.right);
158 track_selector.get_column(0)->set_min_width(100);
159 track_selector.get_column(1)->set_min_width(40);
160 track_selector.get_column(1)->set_sizing(Gtk::TREE_VIEW_COLUMN_AUTOSIZE);
161 track_selector.get_column(2)->set_min_width(40);
162 track_selector.get_column(2)->set_sizing(Gtk::TREE_VIEW_COLUMN_AUTOSIZE);
163 track_selector.get_selection()->set_mode (Gtk::SELECTION_NONE);
165 progress_bar.set_name ("ExportProgress");
167 format_frame.add (format_table);
168 format_frame.set_name (FRAME_NAME);
170 track_scroll.set_policy(Gtk::POLICY_AUTOMATIC, Gtk::POLICY_AUTOMATIC);
171 master_scroll.set_policy(Gtk::POLICY_AUTOMATIC, Gtk::POLICY_AUTOMATIC);
173 get_vbox()->pack_start (file_frame, false, false);
175 hpacker.set_spacing (5);
176 hpacker.set_border_width (5);
177 hpacker.pack_start (format_frame, false, false);
179 master_scroll.add (master_selector);
180 track_scroll.add (track_selector);
182 master_scroll.set_size_request (220, 100);
183 track_scroll.set_size_request (220, 100);
185 /* we may hide some of these later */
186 track_vpacker.pack_start (master_scroll);
187 track_vpacker.pack_start (track_scroll);
188 track_vpacker.pack_start (track_selector_button, Gtk::PACK_EXPAND_PADDING);
190 hpacker.pack_start (track_vpacker);
192 get_vbox()->pack_start (hpacker);
194 track_selector_button.set_name ("EditorGTKButton");
195 track_selector_button.signal_clicked().connect (mem_fun(*this, &ExportDialog::track_selector_button_click));
197 get_vbox()->pack_start (progress_bar, false, false);
199 Gtkmm2ext::set_size_request_to_display_given_text (file_entry, X_("Kg/quite/a/reasonable/size/for/files/i/think"), 5, 8);
201 file_hbox.set_spacing (5);
202 file_hbox.set_border_width (5);
203 file_hbox.pack_start (file_entry, true, true);
204 file_hbox.pack_start (file_browse_button, false, false);
206 file_frame.add (file_hbox);
207 file_frame.set_border_width (5);
208 file_frame.set_name (FRAME_NAME);
210 /* pop_strings needs to be created on the stack because set_popdown_strings()
214 vector<string> pop_strings = I18N (sample_rates);
215 Gtkmm2ext::set_popdown_strings (sample_rate_combo, pop_strings);
216 sample_rate_combo.set_active_text (pop_strings.front());
217 pop_strings = I18N (src_quality);
218 Gtkmm2ext::set_popdown_strings (src_quality_combo, pop_strings);
219 src_quality_combo.set_active_text (pop_strings.front());
220 pop_strings = I18N (dither_types);
221 Gtkmm2ext::set_popdown_strings (dither_type_combo, pop_strings);
222 dither_type_combo.set_active_text (pop_strings.front());
223 pop_strings = I18N (channel_strings);
224 Gtkmm2ext::set_popdown_strings (channel_count_combo, pop_strings);
225 channel_count_combo.set_active_text (pop_strings.front());
226 pop_strings = I18N ((const char **) sndfile_header_formats_strings);
227 Gtkmm2ext::set_popdown_strings (header_format_combo, pop_strings);
228 header_format_combo.set_active_text (pop_strings.front());
229 pop_strings = I18N ((const char **) sndfile_bitdepth_formats_strings);
230 Gtkmm2ext::set_popdown_strings (bitdepth_format_combo, pop_strings);
231 bitdepth_format_combo.set_active_text (pop_strings.front());
232 pop_strings = I18N ((const char **) sndfile_endian_formats_strings);
233 Gtkmm2ext::set_popdown_strings (endian_format_combo, pop_strings);
234 endian_format_combo.set_active_text (pop_strings.front());
235 pop_strings = I18N (cue_file_types);
236 Gtkmm2ext::set_popdown_strings (cue_file_combo, pop_strings);
237 cue_file_combo.set_active_text (pop_strings.front());
239 /* this will re-sensitized as soon as a non RIFF/WAV
240 header format is chosen.
243 endian_format_combo.set_sensitive (false);
245 /* determine longest strings at runtime */
248 const char *longest = X_("gl"); /* translators: one ascender, one descender */
251 for (n = 0; n < SNDFILE_HEADER_FORMATS; ++n) {
252 if ((len = strlen (sndfile_header_formats_strings[n])) > maxlen) {
254 longest = sndfile_header_formats_strings[n];
258 for (n = 0; n < SNDFILE_BITDEPTH_FORMATS; ++n) {
259 if ((len = strlen (sndfile_bitdepth_formats_strings[n])) > maxlen) {
261 longest = sndfile_bitdepth_formats_strings[n];
265 for (n = 0; n < SNDFILE_ENDIAN_FORMATS; ++n) {
266 if ((len = strlen (sndfile_endian_formats_strings[n])) > maxlen) {
268 longest = sndfile_endian_formats_strings[n];
272 longest_str = longest;
274 /* force ascender + descender */
276 longest_str[0] = 'g';
277 longest_str[1] = 'l';
279 //Gtkmm2ext::set_size_request_to_display_given_text (header_format_combo, longest_str.c_str(), 5+FUDGE, 5);
281 // TRANSLATORS: "slereg" is "stereo" with ascender and descender substituted
282 //Gtkmm2ext::set_size_request_to_display_given_text (channel_count_combo, _("slereg"), 5+FUDGE, 5);
284 /* header_format_combo.set_focus_on_click (true);
285 bitdepth_format_combo.set_focus_on_click (true);
286 endian_format_combo.set_focus_on_click (true);
287 channel_count_combo.set_focus_on_click (true);
288 src_quality_combo.set_focus_on_click (true);
289 dither_type_combo.set_focus_on_click (true);
290 sample_rate_combo.set_focus_on_click (true);
291 cue_file_combo.set_focus_on_click (true);
293 dither_type_label.set_name ("ExportFormatLabel");
294 sample_rate_label.set_name ("ExportFormatLabel");
295 src_quality_label.set_name ("ExportFormatLabel");
296 channel_count_label.set_name ("ExportFormatLabel");
297 header_format_label.set_name ("ExportFormatLabel");
298 bitdepth_format_label.set_name ("ExportFormatLabel");
299 endian_format_label.set_name ("ExportFormatLabel");
300 cue_file_label.set_name ("ExportFormatLabel");
302 header_format_combo.set_name ("ExportFormatDisplay");
303 bitdepth_format_combo.set_name ("ExportFormatDisplay");
304 endian_format_combo.set_name ("ExportFormatDisplay");
305 channel_count_combo.set_name ("ExportFormatDisplay");
306 dither_type_combo.set_name ("ExportFormatDisplay");
307 src_quality_combo.set_name ("ExportFormatDisplay");
308 sample_rate_combo.set_name ("ExportFormatDisplay");
309 cue_file_combo.set_name ("ExportFormatDisplay");
311 cuefile_only_checkbox.set_name ("ExportCheckbox");
313 format_table.set_homogeneous (false);
314 format_table.set_border_width (5);
315 format_table.set_col_spacings (5);
316 format_table.set_row_spacings (5);
320 format_table.attach (channel_count_label, 0, 1, row, row+1);
321 format_table.attach (channel_count_combo, 1, 2, row, row+1);
325 format_table.attach (header_format_label, 0, 1, row, row+1);
326 format_table.attach (header_format_combo, 1, 2, row, row+1);
330 format_table.attach (bitdepth_format_label, 0, 1, row, row+1);
331 format_table.attach (bitdepth_format_combo, 1, 2, row, row+1);
335 if (!Profile->get_sae()) {
336 format_table.attach (endian_format_label, 0, 1, row, row+1);
337 format_table.attach (endian_format_combo, 1, 2, row, row+1);
341 format_table.attach (sample_rate_label, 0, 1, row, row+1);
342 format_table.attach (sample_rate_combo, 1, 2, row, row+1);
346 if (!Profile->get_sae()) {
347 format_table.attach (src_quality_label, 0, 1, row, row+1);
348 format_table.attach (src_quality_combo, 1, 2, row, row+1);
352 format_table.attach (dither_type_label, 0, 1, row, row+1);
353 format_table.attach (dither_type_combo, 1, 2, row, row+1);
357 if (!Profile->get_sae()) {
358 format_table.attach (cue_file_label, 0, 1, row, row+1);
359 format_table.attach (cue_file_combo, 1, 2, row, row+1);
362 format_table.attach (cuefile_only_checkbox, 0, 2, row, row+1);
365 file_entry.set_name ("ExportFileDisplay");
367 signal_delete_event().connect (mem_fun(*this, &ExportDialog::window_closed));
369 cancel_button = add_button (Stock::CANCEL, RESPONSE_CANCEL);
370 cancel_button->signal_clicked().connect (mem_fun(*this, &ExportDialog::end_dialog));
371 ok_button = add_button (_("Export"), RESPONSE_ACCEPT);
372 ok_button->signal_clicked().connect (mem_fun(*this, &ExportDialog::do_export));
374 file_browse_button.set_name ("EditorGTKButton");
375 file_browse_button.signal_clicked().connect (mem_fun(*this, &ExportDialog::browse));
377 channel_count_combo.signal_changed().connect (mem_fun(*this, &ExportDialog::channels_chosen));
378 bitdepth_format_combo.signal_changed().connect (mem_fun(*this, &ExportDialog::bitdepth_chosen));
379 header_format_combo.signal_changed().connect (mem_fun(*this, &ExportDialog::header_chosen));
380 sample_rate_combo.signal_changed().connect (mem_fun(*this, &ExportDialog::sample_rate_chosen));
381 cue_file_combo.signal_changed().connect (mem_fun(*this, &ExportDialog::cue_file_type_chosen));
384 ExportDialog::~ExportDialog()
389 ExportDialog::do_not_allow_track_and_master_selection()
391 track_and_master_selection_allowed = false;
392 track_vpacker.set_no_show_all();
396 ExportDialog::do_not_allow_channel_count_selection()
398 channel_count_selection_allowed = false;
399 channel_count_combo.set_no_show_all();
400 channel_count_label.set_no_show_all();
404 ExportDialog::do_not_allow_export_cd_markers()
406 export_cd_markers_allowed = false;
407 cue_file_label.set_no_show_all();
408 cue_file_combo.set_no_show_all();
409 cuefile_only_checkbox.set_no_show_all();
413 ExportDialog::connect_to_session (Session *s)
416 session->GoingAway.connect (mem_fun(*this, &Window::hide_all));
418 switch (session->frame_rate()) {
420 sample_rate_combo.set_active_text (_("22.05kHz"));
423 sample_rate_combo.set_active_text (_("44.1kHz"));
426 sample_rate_combo.set_active_text (_("48kHz"));
429 sample_rate_combo.set_active_text (_("88.2kHz"));
432 sample_rate_combo.set_active_text (_("96kHz"));
435 sample_rate_combo.set_active_text (_("192kHz"));
438 sample_rate_combo.set_active_text (_("44.1kHz"));
442 src_quality_combo.set_sensitive (false);
448 ExportDialog::set_state()
450 XMLNode* node = session->instant_xml(X_("ExportDialog"), session->path());
455 if ((prop = node->property (X_("sample_rate"))) != 0) {
456 sample_rate_combo.set_active_text(prop->value());
458 if ((prop = node->property (X_("src_quality"))) != 0) {
459 src_quality_combo.set_active_text(prop->value());
461 if ((prop = node->property (X_("dither_type"))) != 0) {
462 dither_type_combo.set_active_text(prop->value());
464 if ((prop = node->property (X_("channel_count"))) != 0) {
465 channel_count_combo.set_active_text(prop->value());
467 if ((prop = node->property (X_("header_format"))) != 0) {
468 header_format_combo.set_active_text(prop->value());
470 if ((prop = node->property (X_("bitdepth_format"))) != 0) {
471 bitdepth_format_combo.set_active_text(prop->value());
473 if ((prop = node->property (X_("endian_format"))) != 0) {
474 endian_format_combo.set_active_text(prop->value());
476 if ((prop = node->property (X_("filename"))) != 0) {
477 file_entry.set_text(prop->value());
479 if ((prop = node->property (X_("cue_file_type"))) != 0) {
480 cue_file_combo.set_active_text(prop->value());
487 sample_rate_chosen();
489 if (session->master_out()) {
490 track_scroll.hide ();
492 master_scroll.hide ();
493 track_selector_button.hide ();
500 if (session->master_out()) {
501 XMLNode* master = find_named_node(*node, (X_("Master")));
506 /* default is to use all */
507 if (channel_count_combo.get_active_text() == _("mono")) {
513 TreeModel::Children rows = master_selector.get_model()->children();
514 for (uint32_t r = 0; r < session->master_out()->n_outputs(); ++r) {
517 rows[r][exp_cols.right] = true;
519 rows[r][exp_cols.left] = true;
522 rows[r][exp_cols.left] = true;
527 /* XXX use XML state */
531 XMLNode* tracks = find_named_node(*node, (X_("Tracks")));
536 XMLNodeList track_list = tracks->children(X_("Track"));
537 TreeModel::Children rows = track_selector.get_model()->children();
538 TreeModel::Children::iterator ri = rows.begin();
541 for (XMLNodeIterator it = track_list.begin(); it != track_list.end(); ++it, ++ri) {
542 if (ri == rows.end()){
546 XMLNode* track = *it;
549 if ((prop = track->property(X_("channel1"))) != 0) {
550 if (prop->value() == X_("on")) {
551 row[exp_cols.left] = true;
553 row[exp_cols.left] = false;
557 if ((prop = track->property(X_("channel2"))) != 0) {
558 if (prop->value() == X_("on")) {
559 row[exp_cols.right] = true;
561 row[exp_cols.right] = false;
568 ExportDialog::save_state()
574 XMLNode* node = new XMLNode(X_("ExportDialog"));
576 node->add_property(X_("sample_rate"), sample_rate_combo.get_active_text());
577 node->add_property(X_("src_quality"), src_quality_combo.get_active_text());
578 node->add_property(X_("dither_type"), dither_type_combo.get_active_text());
579 node->add_property(X_("channel_count"), channel_count_combo.get_active_text());
580 node->add_property(X_("header_format"), header_format_combo.get_active_text());
581 node->add_property(X_("bitdepth_format"), bitdepth_format_combo.get_active_text());
582 node->add_property(X_("endian_format"), endian_format_combo.get_active_text());
583 node->add_property(X_("filename"), file_entry.get_text());
584 node->add_property(X_("cue_file_type"), cue_file_combo.get_active_text());
586 XMLNode* tracks = new XMLNode(X_("Tracks"));
588 TreeModel::Children rows = track_selector.get_model()->children();
590 for (TreeModel::Children::iterator ri = rows.begin(); ri != rows.end(); ++ri) {
591 XMLNode* track = new XMLNode(X_("Track"));
594 track->add_property(X_("channel1"), row[exp_cols.left] ? X_("on") : X_("off"));
595 track->add_property(X_("channel2"), row[exp_cols.right] ? X_("on") : X_("off"));
597 tracks->add_child_nocopy(*track);
599 node->add_child_nocopy(*tracks);
601 session->add_instant_xml(*node, session->path());
605 ExportDialog::set_range (nframes_t start, nframes_t end)
607 spec.start_frame = start;
608 spec.end_frame = end;
612 ExportDialog::progress_timeout ()
614 progress_bar.set_fraction (spec.progress);
619 frames_to_cd_frames_string (char* buf, nframes_t when, nframes_t fr)
622 long unsigned int remainder;
623 int mins, secs, frames;
625 mins = when / (60 * fr);
626 remainder = when - (mins * 60 * fr);
627 secs = remainder / fr;
628 remainder -= secs * fr;
629 frames = remainder / (fr / 75);
630 sprintf (buf, " %02d:%02d:%02d", mins, secs, frames);
634 struct LocationSortByStart {
635 bool operator() (Location *a, Location *b) {
636 return a->start() < b->start();
641 ExportDialog::export_toc_file (Locations::LocationList& locations, const string& path)
643 if(!export_cd_markers_allowed){
647 long unsigned int last_end_time = spec.start_frame, last_start_time = spec.start_frame;
650 /* Build the toc's file name from the specified audio file name. */
651 string basename = Glib::path_get_basename(path);
652 size_t ext_pos = basename.rfind('.');
653 if (ext_pos != string::npos) {
654 basename = basename.substr(0, ext_pos); /* strip file extension, if there is one */
656 string filepath = Glib::build_filename(Glib::path_get_dirname(path), basename + ".toc");
658 ofstream out (filepath.c_str());
660 error << string_compose(_("Editor: cannot open \"%1\" as export file for CD toc file"), filepath) << endmsg;
663 out << "CD_DA" << endl;
664 out << "CD_TEXT {" << endl << " LANGUAGE_MAP {" << endl << " 0 : EN" << endl << " }" << endl;
665 out << " LANGUAGE 0 {" << endl << " TITLE \"" << session->name() << "\"" << endl << " }" << endl << "}" << endl;
667 Locations::LocationList::iterator i;
668 Locations::LocationList temp;
670 for (i = locations.begin(); i != locations.end(); ++i) {
671 if ((*i)->start() >= spec.start_frame && (*i)->end() <= spec.end_frame && (*i)->is_cd_marker() && !(*i)->is_end()) {
676 if (temp.size() > 0) {
677 LocationSortByStart cmp;
679 Location * curr_range = 0;
680 Locations::LocationList::iterator nexti;
682 for (i = temp.begin(); i != temp.end(); ++i) {
684 if ((*i)->start() >= last_end_time)
686 /* this is a track, defined by a cd range marker or a cd location marker outside of a cd range */
687 out << endl << "TRACK AUDIO" << endl;
689 if ((*i)->cd_info.find("scms") != (*i)->cd_info.end()) {
692 out << "COPY" << endl;
694 if ((*i)->cd_info.find("preemph") != (*i)->cd_info.end()) {
695 out << "PRE_EMPHASIS" << endl;
697 out << "NO PRE_EMPHASIS" << endl;
700 if ((*i)->cd_info.find("isrc") != (*i)->cd_info.end()) {
701 out << "ISRC \"" << (*i)->cd_info["isrc"] << "\"" << endl;
704 out << "CD_TEXT {" << endl << " LANGUAGE 0 {" << endl << " TITLE \"" << (*i)->name() << "\"" << endl;
705 if ((*i)->cd_info.find("performer") != (*i)->cd_info.end()) {
706 out << " PERFORMER \"" << (*i)->cd_info["performer"] << "\"" << endl;
708 if ((*i)->cd_info.find("string_composer") != (*i)->cd_info.end()) {
709 out << " COMPOSER \"" << (*i)->cd_info["string_composer"] << "\"" << endl;
712 if ((*i)->cd_info.find("isrc") != (*i)->cd_info.end()) {
714 out << (*i)->cd_info["isrc"].substr(0,2) << "-";
715 out << (*i)->cd_info["isrc"].substr(2,3) << "-";
716 out << (*i)->cd_info["isrc"].substr(5,2) << "-";
717 out << (*i)->cd_info["isrc"].substr(7,5) << "\"" << endl;
720 out << " }" << endl << "}" << endl;
722 frames_to_cd_frames_string (buf, last_end_time - spec.start_frame, session->frame_rate());
723 out << "FILE \"" << Glib::path_get_basename(path) << "\"" << buf;
725 if ((*i)->is_mark()) {
726 // a mark track location needs to look ahead to the next marker's start to determine length
729 if (nexti != temp.end()) {
730 frames_to_cd_frames_string (buf, (*nexti)->start() - last_end_time, session->frame_rate());
733 frames_to_cd_frames_string (buf, (*i)->start() - last_end_time, session->frame_rate());
734 out << "START" << buf << endl;
736 last_start_time = (*i)->start();
737 last_end_time = (*nexti)->start();
740 // this was the last marker, use session end
741 frames_to_cd_frames_string (buf, spec.end_frame - last_end_time, session->frame_rate());
744 frames_to_cd_frames_string (buf, (*i)->start() - last_end_time, session->frame_rate());
745 out << "START" << buf << endl;
747 last_start_time = (*i)->start();
748 last_end_time = spec.end_frame;
755 frames_to_cd_frames_string (buf, (*i)->end() - last_end_time, session->frame_rate());
758 frames_to_cd_frames_string (buf, (*i)->start() - last_end_time, session->frame_rate());
759 out << "START" << buf << endl;
761 last_start_time = (*i)->start();
762 last_end_time = (*i)->end();
768 else if ((*i)->is_mark())
770 /* this is an index within a track */
772 frames_to_cd_frames_string (buf, (*i)->start() - last_start_time, session->frame_rate());
773 out << "INDEX" << buf << endl;
781 ExportDialog::export_cue_file (Locations::LocationList& locations, const string& path)
783 if(!export_cd_markers_allowed){
788 long unsigned int last_track_end = spec.start_frame;
789 int numtracks = 0, tracknum = 0, indexnum = 0;
791 /* Build the cue sheet's file name from the specified audio file name. */
792 string basename = Glib::path_get_basename(path);
793 size_t ext_pos = basename.rfind('.');
794 if (ext_pos != string::npos) {
795 basename = basename.substr(0, ext_pos); /* strip file extension, if there is one */
797 string filepath = Glib::build_filename(Glib::path_get_dirname(path), basename + ".cue");
799 ofstream out (filepath.c_str());
801 error << string_compose(_("Editor: cannot open \"%1\" as export file for CD cue file"), filepath) << endmsg;
805 Locations::LocationList::iterator i;
806 Locations::LocationList temp;
808 for (i = locations.begin(); i != locations.end(); ++i) {
809 if ((*i)->start() >= spec.start_frame && (*i)->end() <= spec.end_frame && (*i)->is_cd_marker() && !(*i)->is_end()) {
811 if (!(*i)->is_mark()) {
817 out << "REM Cue file generated by Ardour" << endl;
818 out << "TITLE \"" << session->name() << "\"" << endl;
820 out << "FILE \"" << Glib::path_get_basename(path) << "\" ";
822 /* The cue sheet syntax has originally five file types:
823 WAVE : 44.1 kHz, 16 Bit (little endian)
824 AIFF : 44.1 kHz, 16 Bit (big endian)
825 BINARY : 44.1 kHz, 16 Bit (little endian)
826 MOTOROLA : 44.1 kHz, 16 Bit (big endian)
829 We want to use cue sheets not only as CD images but also as general playlyist
830 format, thus for WAVE and AIFF we don't care if it's really 44.1 kHz/16 Bit, the
831 soundfile's header shows it anyway. But for the raw formats, i.e. BINARY
832 and MOTOROLA we do care, because no header would tell us about a different format.
834 For all other formats we just make up our own file type. MP3 is not supported
837 int file_format = sndfile_header_format_from_string (header_format_combo.get_active_text ());
838 if ((file_format & SF_FORMAT_TYPEMASK) == SF_FORMAT_WAV) {
840 } else if ((file_format & SF_FORMAT_TYPEMASK) == SF_FORMAT_AIFF) {
842 } else if ( ((file_format & SF_FORMAT_TYPEMASK) == SF_FORMAT_RAW)
843 && (sndfile_bitdepth_format_from_string(bitdepth_format_combo.get_active_text()) == SF_FORMAT_PCM_16)
844 && (sample_rate_combo.get_active_text() == _("44.1kHz")) ) {
845 /* raw audio, 16 Bit, 44.1 kHz */
846 if (sndfile_endian_format_from_string(endian_format_combo.get_active_text()) == SF_ENDIAN_LITTLE) {
852 out << (header_format_combo.get_active_text());
856 if (false && numtracks == 0) {
857 /* the user has supplied no track markers.
858 the entire export is treated as one track.
865 snprintf (buf, sizeof(buf), " TRACK %02d AUDIO", tracknum);
867 out << " FLAGS DCP" << endl;
869 /* use the session name*/
871 out << " TITLE \"" << session->name() << "\"" << endl;
873 /* No pregap is specified in this case, adding the default pregap
874 is left to the burning application. */
876 out << " INDEX 01 00:00:00" << endl;
878 last_track_end = spec.end_frame;
882 LocationSortByStart cmp;
884 Location * curr_range = 0;
885 Locations::LocationList::iterator nexti;
887 for ( i = temp.begin(); i != temp.end(); ++i) {
889 if ((*i)->start() >= last_track_end)
891 /* this is a track and it doesn't start inside another one*/
896 snprintf (buf, sizeof(buf), " TRACK %02d AUDIO", tracknum);
900 if ((*i)->cd_info.find("scms") != (*i)->cd_info.end()) {
905 if ((*i)->cd_info.find("preemph") != (*i)->cd_info.end()) {
910 if ((*i)->cd_info.find("isrc") != (*i)->cd_info.end()) {
911 out << " ISRC " << (*i)->cd_info["isrc"] << endl;
914 if ((*i)->name() != "") {
915 out << " TITLE \"" << (*i)->name() << "\"" << endl;
918 if ((*i)->cd_info.find("performer") != (*i)->cd_info.end()) {
919 out << " PERFORMER \"" << (*i)->cd_info["performer"] << "\"" << endl;
922 if ((*i)->cd_info.find("string_composer") != (*i)->cd_info.end()) {
923 out << " SONGWRITER \"" << (*i)->cd_info["string_composer"] << "\"" << endl;
926 /* only print "Index 00" if not at the same position as "Index 01" */
927 if (last_track_end != (*i)->start()) {
928 frames_to_cd_frames_string (buf, last_track_end - spec.start_frame, session->frame_rate());
929 out << " INDEX 00" << buf << endl;
934 if ((*i)->is_mark()) {
935 // need to find the next start to define the end
938 if (nexti != temp.end()) {
939 last_track_end = (*nexti)->start();
942 last_track_end = spec.end_frame;
947 last_track_end = (*i)->end();
952 if ((tracknum > 0) && ((*i)->start() < last_track_end)) {
953 /*this is an index and it lies within a track*/
954 snprintf (buf, sizeof(buf), " INDEX %02d", indexnum);
956 frames_to_cd_frames_string (buf,(*i)->start() - spec.start_frame, session->frame_rate());
966 ExportDialog::do_export_cd_markers (const string& path,const string& cuefile_type)
968 if (cuefile_type == _("TOC")) {
969 session->locations()->apply (*this, &ExportDialog::export_toc_file, path);
971 session->locations()->apply (*this, &ExportDialog::export_cue_file, path);
976 ExportDialog::get_suffixed_filepath ()
978 string filepath = file_entry.get_text();
984 string::size_type dotpos;
986 /* maybe add suffix */
988 int file_format = sndfile_header_format_from_string (header_format_combo.get_active_text ());
990 if ((file_format & SF_FORMAT_TYPEMASK) == SF_FORMAT_WAV) {
991 if (filepath.find (".wav") != filepath.length() - 4) {
992 if ((dotpos = filepath.rfind ('.')) != string::npos) {
993 filepath = filepath.substr (0, dotpos);
997 } else if ((file_format & SF_FORMAT_TYPEMASK) == SF_FORMAT_AIFF) {
998 if (filepath.find (".aiff") != filepath.length() - 5) {
999 if ((dotpos = filepath.rfind ('.')) != string::npos) {
1000 filepath = filepath.substr (0, dotpos);
1002 filepath += ".aiff";
1004 } else if ((file_format & SF_FORMAT_TYPEMASK) == SF_FORMAT_CAF) {
1005 if (filepath.find (".caf") != filepath.length() - 4) {
1006 if ((dotpos = filepath.rfind ('.')) != string::npos) {
1007 filepath = filepath.substr (0, dotpos);
1011 } else if ((file_format & SF_FORMAT_TYPEMASK) == SF_FORMAT_W64) {
1012 if (filepath.find (".w64") != filepath.length() - 4) {
1013 if ((dotpos = filepath.rfind ('.')) != string::npos) {
1014 filepath = filepath.substr (0, dotpos);
1018 } else if ((file_format & SF_FORMAT_TYPEMASK) == SF_FORMAT_FLAC) {
1019 if (filepath.find (".flac") != filepath.length() - 5) {
1020 if ((dotpos = filepath.rfind ('.')) != string::npos) {
1021 filepath = filepath.substr (0, dotpos);
1023 filepath += ".flac";
1025 } else if ((file_format & SF_FORMAT_TYPEMASK) == SF_FORMAT_OGG) {
1026 if (filepath.find (".ogg") != filepath.length() - 4) {
1027 if ((dotpos = filepath.rfind ('.')) != string::npos) {
1028 filepath = filepath.substr (0, dotpos);
1032 } else if ((file_format & SF_FORMAT_TYPEMASK) == SF_FORMAT_RAW) {
1033 if (filepath.find (".raw") != filepath.length() - 4) {
1034 if ((dotpos = filepath.rfind ('.')) != string::npos) {
1035 filepath = filepath.substr (0, dotpos);
1044 ExportDialog::do_export ()
1046 if (!ARDOUR_UI::instance()->the_engine().connected()) {
1047 MessageDialog msg (*this,
1048 _("Not connected to audioengine"),
1052 msg.set_secondary_text (_("Ardour cannot export audio when disconnected"));
1060 filepath = get_suffixed_filepath ();
1062 if(!is_filepath_valid(filepath)){
1066 if (!Profile->get_sae() && export_cd_markers_allowed) {
1067 if (cue_file_combo.get_active_text () != _("None")) {
1068 do_export_cd_markers (filepath, cue_file_combo.get_active_text ());
1071 if (cuefile_only_checkbox.get_active()) {
1077 ok_button->set_sensitive(false);
1082 // read user input into spec
1085 progress_connection = Glib::signal_timeout().connect (mem_fun(*this, &ExportDialog::progress_timeout), 100);
1086 cancel_label.set_text (_("Stop Export"));
1088 session->pre_export ();
1090 export_audio_data();
1092 progress_connection.disconnect ();
1095 /* if not stopped early and not SAE, ask for money, maybe */
1097 if (!spec.stop && !Profile->get_sae()) {
1099 NagScreen* ns = NagScreen::maybe_nag (_("export"));
1109 ExportDialog::end_dialog ()
1114 while (spec.running) {
1115 if (gtk_events_pending()) {
1116 gtk_main_iteration ();
1123 session->finalize_audio_export ();
1128 ok_button->set_sensitive(true);
1132 ExportDialog::start_export ()
1138 /* If the filename hasn't been set before, use the
1139 current session's export directory as a default
1140 location for the export.
1143 if (file_entry.get_text().length() == 0) {
1144 Glib::ustring export_path = session->export_dir();
1147 export_path = Glib::build_filename (export_path, "export.wav");
1150 file_entry.set_text (export_path);
1153 progress_bar.set_fraction (0);
1154 cancel_label.set_text (_("Cancel"));
1158 if (session->master_out()) {
1159 track_scroll.hide ();
1161 master_scroll.hide ();
1162 track_selector_button.hide ();
1167 ExportDialog::header_chosen ()
1169 int fmt = sndfile_header_format_from_string (header_format_combo.get_active_text ());
1171 if ((fmt & SF_FORMAT_TYPEMASK) == SF_FORMAT_OGG) {
1172 endian_format_combo.set_sensitive (false);
1173 bitdepth_format_combo.set_sensitive (false);
1175 if ((fmt & SF_FORMAT_TYPEMASK) == SF_FORMAT_WAV) {
1176 endian_format_combo.set_sensitive (false);
1178 endian_format_combo.set_sensitive (true);
1180 bitdepth_format_combo.set_sensitive (true);
1183 file_entry.set_text (get_suffixed_filepath());
1187 ExportDialog::bitdepth_chosen ()
1189 int format = sndfile_bitdepth_format_from_string (bitdepth_format_combo.get_active_text ());
1191 case SF_FORMAT_PCM_24:
1192 case SF_FORMAT_PCM_32:
1193 case SF_FORMAT_FLOAT:
1194 dither_type_combo.set_sensitive (false);
1198 dither_type_combo.set_sensitive (true);
1204 ExportDialog::cue_file_type_chosen ()
1206 if (cue_file_combo.get_active_text () != "None") {
1207 cuefile_only_checkbox.set_sensitive (true);
1209 cuefile_only_checkbox.set_active (false);
1210 cuefile_only_checkbox.set_sensitive (false);
1215 ExportDialog::sample_rate_chosen ()
1217 string sr_str = sample_rate_combo.get_active_text();
1220 if (sr_str == N_("22.05kHz")) {
1222 } else if (sr_str == _("44.1kHz")) {
1224 } else if (sr_str == _("48kHz")) {
1226 } else if (sr_str == _("88.2kHz")) {
1228 } else if (sr_str == _("96kHz")) {
1230 } else if (sr_str == _("192kHz")) {
1233 rate = session->frame_rate();
1236 if (rate != session->frame_rate()) {
1237 src_quality_combo.set_sensitive (true);
1239 src_quality_combo.set_sensitive (false);
1244 ExportDialog::channels_chosen ()
1248 mono = (channel_count_combo.get_active_text() == _("mono"));
1251 track_selector.get_column(2)->set_visible(false);
1252 track_selector.get_column(1)->set_title(_("Export"));
1254 if (session->master_out()) {
1255 master_selector.get_column(2)->set_visible(false);
1256 master_selector.get_column(1)->set_title(_("Export"));
1260 track_selector.get_column(2)->set_visible(true);
1261 track_selector.get_column(1)->set_title(_("Left"));
1263 if (session->master_out()) {
1264 master_selector.get_column(2)->set_visible(true);
1265 master_selector.get_column(1)->set_title(_("Left"));
1273 ExportDialog::fill_lists ()
1275 track_list->clear();
1276 master_list->clear();
1278 boost::shared_ptr<Session::RouteList> routes = session->get_routes ();
1280 for (Session::RouteList::iterator ri = routes->begin(); ri != routes->end(); ++ri) {
1282 boost::shared_ptr<Route> route = (*ri);
1284 if (route->hidden()) {
1288 for (uint32_t i=0; i < route->n_outputs(); ++i) {
1290 if (route->n_outputs() == 1) {
1291 name = route->name();
1293 name = string_compose("%1: out-%2", route->name(), i+1);
1296 if (route == session->master_out()) {
1297 TreeModel::iterator iter = master_list->append();
1298 TreeModel::Row row = *iter;
1299 row[exp_cols.output] = name;
1300 row[exp_cols.left] = false;
1301 row[exp_cols.right] = false;
1302 row[exp_cols.port] = route->output (i);
1304 TreeModel::iterator iter = track_list->append();
1305 TreeModel::Row row = *iter;
1306 row[exp_cols.output] = name;
1307 row[exp_cols.left] = false;
1308 row[exp_cols.right] = false;
1309 row[exp_cols.port] = route->output (i);
1317 ExportDialog::is_filepath_valid(string &filepath)
1319 // sanity check file name first
1321 struct stat statbuf;
1323 if (filepath.empty()) {
1324 string txt = _("Please enter a valid filename.");
1325 MessageDialog msg (*this, txt, false, MESSAGE_ERROR, BUTTONS_OK, true);
1330 // check if file exists already and warn
1332 if (stat (filepath.c_str(), &statbuf) == 0) {
1333 if (S_ISDIR (statbuf.st_mode)) {
1334 string txt = _("Please specify a complete filename for the audio file.");
1335 MessageDialog msg (*this, txt, false, MESSAGE_ERROR, BUTTONS_OK, true);
1340 string txt = _("File already exists, do you want to overwrite it?");
1341 MessageDialog msg (*this, txt, false, MESSAGE_QUESTION, BUTTONS_YES_NO, true);
1342 if ((ResponseType) msg.run() == Gtk::RESPONSE_NO) {
1348 // directory needs to exist and be writable
1350 string dirpath = Glib::path_get_dirname (filepath);
1351 if (::access (dirpath.c_str(), W_OK) != 0) {
1352 string txt = _("Cannot write file in: ") + dirpath;
1353 MessageDialog msg (*this, txt, false, MESSAGE_ERROR, BUTTONS_OK, true);
1362 ExportDialog::initSpec(string &filepath)
1364 spec.path = filepath;
1366 spec.running = false;
1368 spec.port_map.clear();
1370 if (channel_count_combo.get_active_text() == _("mono")) {
1378 spec.format |= sndfile_header_format_from_string (header_format_combo.get_active_text ());
1380 /* if they picked Ogg, give them Ogg/Vorbis */
1382 if ((spec.format & SF_FORMAT_TYPEMASK) == SF_FORMAT_OGG) {
1383 spec.format |= SF_FORMAT_VORBIS;
1386 if (!Profile->get_sae()) {
1387 if (((spec.format & SF_FORMAT_TYPEMASK) != SF_FORMAT_WAV) && ((spec.format & SF_FORMAT_TYPEMASK) != SF_FORMAT_OGG)) {
1388 /* RIFF/WAV specifies endianess and O/V has no such concept */
1389 spec.format |= sndfile_endian_format_from_string (endian_format_combo.get_active_text ());
1393 if ((spec.format & SF_FORMAT_TYPEMASK) != SF_FORMAT_OGG) {
1394 spec.format |= sndfile_bitdepth_format_from_string (bitdepth_format_combo.get_active_text ());
1397 string sr_str = sample_rate_combo.get_active_text();
1398 if (sr_str == N_("22.05kHz")) {
1399 spec.sample_rate = 22050;
1400 } else if (sr_str == _("44.1kHz")) {
1401 spec.sample_rate = 44100;
1402 } else if (sr_str == _("48kHz")) {
1403 spec.sample_rate = 48000;
1404 } else if (sr_str == _("88.2kHz")) {
1405 spec.sample_rate = 88200;
1406 } else if (sr_str == _("96kHz")) {
1407 spec.sample_rate = 96000;
1408 } else if (sr_str == _("192kHz")) {
1409 spec.sample_rate = 192000;
1411 spec.sample_rate = session->frame_rate();
1414 if (Profile->get_sae()) {
1415 spec.src_quality = SRC_SINC_BEST_QUALITY;
1417 string src_str = src_quality_combo.get_active_text();
1418 if (src_str == _("fastest")) {
1419 spec.src_quality = SRC_ZERO_ORDER_HOLD;
1420 } else if (src_str == _("linear")) {
1421 spec.src_quality = SRC_LINEAR;
1422 } else if (src_str == _("better")) {
1423 spec.src_quality = SRC_SINC_FASTEST;
1424 } else if (src_str == _("intermediate")) {
1425 spec.src_quality = SRC_SINC_MEDIUM_QUALITY;
1427 spec.src_quality = SRC_SINC_BEST_QUALITY;
1431 string dither_str = dither_type_combo.get_active_text();
1432 if (dither_str == _("None")) {
1433 spec.dither_type = GDitherNone;
1434 } else if (dither_str == _("Rectangular")) {
1435 spec.dither_type = GDitherRect;
1436 } else if (dither_str == _("Triangular")) {
1437 spec.dither_type = GDitherTri;
1439 spec.dither_type = GDitherShaped;
1442 write_track_and_master_selection_to_spec();
1447 ExportDialog::write_track_and_master_selection_to_spec()
1449 if(!track_and_master_selection_allowed){
1454 Port *last_port = 0;
1456 TreeModel::Children rows = master_selector.get_model()->children();
1457 TreeModel::Children::iterator ri;
1459 for (ri = rows.begin(); ri != rows.end(); ++ri) {
1461 Port* port = row[exp_cols.port];
1463 if (last_port != port) {
1467 if (row[exp_cols.left]) {
1468 spec.port_map[0].push_back (std::pair<Port*,uint32_t>(port, chan));
1471 if (spec.channels == 2) {
1472 if (row[exp_cols.right]) {
1473 spec.port_map[1].push_back (std::pair<Port*,uint32_t>(port, chan));
1479 rows = track_selector.get_model()->children();
1481 for (ri = rows.begin(); ri != rows.end(); ++ri) {
1484 Port* port = row[exp_cols.port];
1486 if (last_port != port) {
1490 if (row[exp_cols.left]) {
1491 spec.port_map[0].push_back (std::pair<Port*,uint32_t>(port, chan));
1494 if (spec.channels == 2) {
1495 if (row[exp_cols.right]) {
1496 spec.port_map[1].push_back (std::pair<Port*,uint32_t>(port, chan));
1508 ExportDialog::window_closed (GdkEventAny *ignored)
1515 ExportDialog::browse ()
1517 FileChooserDialog dialog("Export to file", browse_action());
1518 dialog.set_transient_for(*this);
1519 dialog.set_filename (file_entry.get_text());
1521 dialog.add_button(Gtk::Stock::CANCEL, Gtk::RESPONSE_CANCEL);
1522 dialog.add_button(Gtk::Stock::OK, Gtk::RESPONSE_OK);
1524 int result = dialog.run();
1526 if (result == Gtk::RESPONSE_OK) {
1527 string filename = dialog.get_filename();
1529 if (filename.length()) {
1530 file_entry.set_text (filename);
1536 ExportDialog::track_selector_button_click ()
1538 if (track_scroll.is_visible ()) {
1539 track_scroll.hide ();
1541 track_scroll.show_all ();