better fix for send/redirect renaming, plus (sorry) small comment change in public_ed...
[ardour2.git] / gtk2_ardour / export_dialog.cc
blob6e9fb683ee9a3cf03acbebba13617b69c3352404
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);
979 string
980 ExportDialog::get_suffixed_filepath ()
982 string filepath = file_entry.get_text();
984 if (wants_dir()) {
985 return filepath;
988 string::size_type dotpos;
990 /* maybe add suffix */
992 int file_format = sndfile_header_format_from_string (header_format_combo.get_active_text ());
994 if ((file_format & SF_FORMAT_TYPEMASK) == SF_FORMAT_WAV) {
995 if (filepath.find (".wav") != filepath.length() - 4) {
996 if ((dotpos = filepath.rfind ('.')) != string::npos) {
997 filepath = filepath.substr (0, dotpos);
999 filepath += ".wav";
1001 } else if ((file_format & SF_FORMAT_TYPEMASK) == SF_FORMAT_AIFF) {
1002 if (filepath.find (".aiff") != filepath.length() - 5) {
1003 if ((dotpos = filepath.rfind ('.')) != string::npos) {
1004 filepath = filepath.substr (0, dotpos);
1006 filepath += ".aiff";
1008 } else if ((file_format & SF_FORMAT_TYPEMASK) == SF_FORMAT_CAF) {
1009 if (filepath.find (".caf") != filepath.length() - 4) {
1010 if ((dotpos = filepath.rfind ('.')) != string::npos) {
1011 filepath = filepath.substr (0, dotpos);
1013 filepath += ".caf";
1015 } else if ((file_format & SF_FORMAT_TYPEMASK) == SF_FORMAT_W64) {
1016 if (filepath.find (".w64") != filepath.length() - 4) {
1017 if ((dotpos = filepath.rfind ('.')) != string::npos) {
1018 filepath = filepath.substr (0, dotpos);
1020 filepath += ".w64";
1022 } else if ((file_format & SF_FORMAT_TYPEMASK) == SF_FORMAT_FLAC) {
1023 if (filepath.find (".flac") != filepath.length() - 5) {
1024 if ((dotpos = filepath.rfind ('.')) != string::npos) {
1025 filepath = filepath.substr (0, dotpos);
1027 filepath += ".flac";
1029 } else if ((file_format & SF_FORMAT_TYPEMASK) == SF_FORMAT_OGG) {
1030 if (filepath.find (".ogg") != filepath.length() - 4) {
1031 if ((dotpos = filepath.rfind ('.')) != string::npos) {
1032 filepath = filepath.substr (0, dotpos);
1034 filepath += ".ogg";
1036 } else if ((file_format & SF_FORMAT_TYPEMASK) == SF_FORMAT_RAW) {
1037 if (filepath.find (".raw") != filepath.length() - 4) {
1038 if ((dotpos = filepath.rfind ('.')) != string::npos) {
1039 filepath = filepath.substr (0, dotpos);
1041 filepath += ".raw";
1044 return filepath;
1047 void
1048 ExportDialog::do_export ()
1050 if (!ARDOUR_UI::instance()->the_engine().connected()) {
1051 MessageDialog msg (*this,
1052 _("Not connected to audioengine"),
1053 true,
1054 MESSAGE_ERROR,
1055 BUTTONS_OK);
1056 msg.set_secondary_text (_("Ardour cannot export audio when disconnected"));
1057 msg.present ();
1058 msg.run ();
1059 return;
1062 string filepath;
1064 filepath = get_suffixed_filepath ();
1066 if(!is_filepath_valid(filepath)){
1067 return;
1070 if (!Profile->get_sae() && export_cd_markers_allowed) {
1071 if (cue_file_combo.get_active_text () != _("None")) {
1072 do_export_cd_markers (filepath, cue_file_combo.get_active_text ());
1075 if (cuefile_only_checkbox.get_active()) {
1076 end_dialog ();
1077 return;
1081 ok_button->set_sensitive(false);
1082 save_state();
1084 set_modal (true);
1086 // read user input into spec
1087 initSpec(filepath);
1089 progress_connection = Glib::signal_timeout().connect (mem_fun(*this, &ExportDialog::progress_timeout), 100);
1090 cancel_label.set_text (_("Stop Export"));
1092 session->pre_export ();
1094 export_audio_data();
1096 progress_connection.disconnect ();
1097 end_dialog ();
1099 /* if not stopped early and not SAE, ask for money, maybe */
1101 if (!spec.stop && !Profile->get_sae()) {
1103 NagScreen* ns = NagScreen::maybe_nag (_("export"));
1105 if (ns) {
1106 ns->nag ();
1107 delete ns;
1112 void
1113 ExportDialog::end_dialog ()
1115 if (spec.running) {
1116 spec.stop = true;
1118 while (spec.running) {
1119 if (gtk_events_pending()) {
1120 gtk_main_iteration ();
1121 } else {
1122 usleep (10000);
1127 session->finalize_audio_export ();
1129 hide_all ();
1131 set_modal (false);
1132 ok_button->set_sensitive(true);
1135 void
1136 ExportDialog::start_export ()
1138 if (session == 0) {
1139 return;
1142 /* If the filename hasn't been set before, use the
1143 current session's export directory as a default
1144 location for the export.
1147 if (file_entry.get_text().length() == 0) {
1148 Glib::ustring export_path = session->export_dir();
1150 if (!wants_dir()) {
1151 export_path = Glib::build_filename (export_path, "export.wav");
1154 file_entry.set_text (export_path);
1157 progress_bar.set_fraction (0);
1158 cancel_label.set_text (_("Cancel"));
1160 show_all ();
1162 if (session->master_out()) {
1163 track_scroll.hide ();
1164 } else {
1165 master_scroll.hide ();
1166 track_selector_button.hide ();
1170 void
1171 ExportDialog::header_chosen ()
1173 int fmt = sndfile_header_format_from_string (header_format_combo.get_active_text ());
1175 if ((fmt & SF_FORMAT_TYPEMASK) == SF_FORMAT_OGG) {
1176 endian_format_combo.set_sensitive (false);
1177 bitdepth_format_combo.set_sensitive (false);
1178 } else {
1179 if ((fmt & SF_FORMAT_TYPEMASK) == SF_FORMAT_WAV) {
1180 endian_format_combo.set_sensitive (false);
1181 } else {
1182 endian_format_combo.set_sensitive (true);
1184 bitdepth_format_combo.set_sensitive (true);
1187 file_entry.set_text (get_suffixed_filepath());
1190 void
1191 ExportDialog::bitdepth_chosen ()
1193 int format = sndfile_bitdepth_format_from_string (bitdepth_format_combo.get_active_text ());
1194 switch (format) {
1195 case SF_FORMAT_PCM_24:
1196 case SF_FORMAT_PCM_32:
1197 case SF_FORMAT_FLOAT:
1198 dither_type_combo.set_sensitive (false);
1199 break;
1201 default:
1202 dither_type_combo.set_sensitive (true);
1203 break;
1207 void
1208 ExportDialog::cue_file_type_chosen ()
1210 if (cue_file_combo.get_active_text () != "None") {
1211 cuefile_only_checkbox.set_sensitive (true);
1212 } else {
1213 cuefile_only_checkbox.set_active (false);
1214 cuefile_only_checkbox.set_sensitive (false);
1218 void
1219 ExportDialog::sample_rate_chosen ()
1221 string sr_str = sample_rate_combo.get_active_text();
1222 nframes_t rate;
1224 if (sr_str == N_("22.05kHz")) {
1225 rate = 22050;
1226 } else if (sr_str == _("44.1kHz")) {
1227 rate = 44100;
1228 } else if (sr_str == _("48kHz")) {
1229 rate = 48000;
1230 } else if (sr_str == _("88.2kHz")) {
1231 rate = 88200;
1232 } else if (sr_str == _("96kHz")) {
1233 rate = 96000;
1234 } else if (sr_str == _("192kHz")) {
1235 rate = 192000;
1236 } else {
1237 rate = session->frame_rate();
1240 if (rate != session->frame_rate()) {
1241 src_quality_combo.set_sensitive (true);
1242 } else {
1243 src_quality_combo.set_sensitive (false);
1247 void
1248 ExportDialog::channels_chosen ()
1250 bool mono;
1252 mono = (channel_count_combo.get_active_text() == _("mono"));
1254 if (mono) {
1255 track_selector.get_column(2)->set_visible(false);
1256 track_selector.get_column(1)->set_title(_("Export"));
1258 if (session->master_out()) {
1259 master_selector.get_column(2)->set_visible(false);
1260 master_selector.get_column(1)->set_title(_("Export"));
1263 } else {
1264 track_selector.get_column(2)->set_visible(true);
1265 track_selector.get_column(1)->set_title(_("Left"));
1267 if (session->master_out()) {
1268 master_selector.get_column(2)->set_visible(true);
1269 master_selector.get_column(1)->set_title(_("Left"));
1273 fill_lists();
1276 void
1277 ExportDialog::fill_lists ()
1279 track_list->clear();
1280 master_list->clear();
1282 boost::shared_ptr<Session::RouteList> routes = session->get_routes ();
1284 for (Session::RouteList::iterator ri = routes->begin(); ri != routes->end(); ++ri) {
1286 boost::shared_ptr<Route> route = (*ri);
1288 if (route->hidden()) {
1289 continue;
1292 for (uint32_t i=0; i < route->n_outputs(); ++i) {
1293 string name;
1294 if (route->n_outputs() == 1) {
1295 name = route->name();
1296 } else {
1297 name = string_compose("%1: out-%2", route->name(), i+1);
1300 if (route == session->master_out()) {
1301 TreeModel::iterator iter = master_list->append();
1302 TreeModel::Row row = *iter;
1303 row[exp_cols.output] = name;
1304 row[exp_cols.left] = false;
1305 row[exp_cols.right] = false;
1306 row[exp_cols.port] = route->output (i);
1307 } else {
1308 TreeModel::iterator iter = track_list->append();
1309 TreeModel::Row row = *iter;
1310 row[exp_cols.output] = name;
1311 row[exp_cols.left] = false;
1312 row[exp_cols.right] = false;
1313 row[exp_cols.port] = route->output (i);
1320 bool
1321 ExportDialog::is_filepath_valid(string &filepath)
1323 // sanity check file name first
1325 struct stat statbuf;
1327 if (filepath.empty()) {
1328 string txt = _("Please enter a valid filename.");
1329 MessageDialog msg (*this, txt, false, MESSAGE_ERROR, BUTTONS_OK, true);
1330 msg.run();
1331 return false;
1334 // check if file exists already and warn
1336 if (stat (filepath.c_str(), &statbuf) == 0) {
1337 if (S_ISDIR (statbuf.st_mode)) {
1338 string txt = _("Please specify a complete filename for the audio file.");
1339 MessageDialog msg (*this, txt, false, MESSAGE_ERROR, BUTTONS_OK, true);
1340 msg.run();
1341 return false;
1343 else {
1344 string txt = _("File already exists, do you want to overwrite it?");
1345 MessageDialog msg (*this, txt, false, MESSAGE_QUESTION, BUTTONS_YES_NO, true);
1346 if ((ResponseType) msg.run() == Gtk::RESPONSE_NO) {
1347 return false;
1352 // directory needs to exist and be writable
1354 string dirpath = Glib::path_get_dirname (filepath);
1355 if (::access (dirpath.c_str(), W_OK) != 0) {
1356 string txt = _("Cannot write file in: ") + dirpath;
1357 MessageDialog msg (*this, txt, false, MESSAGE_ERROR, BUTTONS_OK, true);
1358 msg.run();
1359 return false;
1362 return true;
1365 void
1366 ExportDialog::initSpec(string &filepath)
1368 spec.path = filepath;
1369 spec.progress = 0;
1370 spec.running = false;
1371 spec.stop = false;
1372 spec.port_map.clear();
1374 if (channel_count_combo.get_active_text() == _("mono")) {
1375 spec.channels = 1;
1376 } else {
1377 spec.channels = 2;
1380 spec.format = 0;
1382 spec.format |= sndfile_header_format_from_string (header_format_combo.get_active_text ());
1384 /* if they picked Ogg, give them Ogg/Vorbis */
1386 if ((spec.format & SF_FORMAT_TYPEMASK) == SF_FORMAT_OGG) {
1387 spec.format |= SF_FORMAT_VORBIS;
1390 if (!Profile->get_sae()) {
1391 if (((spec.format & SF_FORMAT_TYPEMASK) != SF_FORMAT_WAV) && ((spec.format & SF_FORMAT_TYPEMASK) != SF_FORMAT_OGG)) {
1392 /* RIFF/WAV specifies endianess and O/V has no such concept */
1393 spec.format |= sndfile_endian_format_from_string (endian_format_combo.get_active_text ());
1397 if ((spec.format & SF_FORMAT_TYPEMASK) != SF_FORMAT_OGG) {
1398 spec.format |= sndfile_bitdepth_format_from_string (bitdepth_format_combo.get_active_text ());
1401 string sr_str = sample_rate_combo.get_active_text();
1402 if (sr_str == N_("22.05kHz")) {
1403 spec.sample_rate = 22050;
1404 } else if (sr_str == _("44.1kHz")) {
1405 spec.sample_rate = 44100;
1406 } else if (sr_str == _("48kHz")) {
1407 spec.sample_rate = 48000;
1408 } else if (sr_str == _("88.2kHz")) {
1409 spec.sample_rate = 88200;
1410 } else if (sr_str == _("96kHz")) {
1411 spec.sample_rate = 96000;
1412 } else if (sr_str == _("192kHz")) {
1413 spec.sample_rate = 192000;
1414 } else {
1415 spec.sample_rate = session->frame_rate();
1418 if (Profile->get_sae()) {
1419 spec.src_quality = SRC_SINC_BEST_QUALITY;
1420 } else {
1421 string src_str = src_quality_combo.get_active_text();
1422 if (src_str == _("fastest")) {
1423 spec.src_quality = SRC_ZERO_ORDER_HOLD;
1424 } else if (src_str == _("linear")) {
1425 spec.src_quality = SRC_LINEAR;
1426 } else if (src_str == _("better")) {
1427 spec.src_quality = SRC_SINC_FASTEST;
1428 } else if (src_str == _("intermediate")) {
1429 spec.src_quality = SRC_SINC_MEDIUM_QUALITY;
1430 } else {
1431 spec.src_quality = SRC_SINC_BEST_QUALITY;
1435 string dither_str = dither_type_combo.get_active_text();
1436 if (dither_str == _("None")) {
1437 spec.dither_type = GDitherNone;
1438 } else if (dither_str == _("Rectangular")) {
1439 spec.dither_type = GDitherRect;
1440 } else if (dither_str == _("Triangular")) {
1441 spec.dither_type = GDitherTri;
1442 } else {
1443 spec.dither_type = GDitherShaped;
1446 write_track_and_master_selection_to_spec();
1450 void
1451 ExportDialog::write_track_and_master_selection_to_spec()
1453 if(!track_and_master_selection_allowed){
1454 return;
1457 uint32_t chan=0;
1458 Port *last_port = 0;
1460 TreeModel::Children rows = master_selector.get_model()->children();
1461 TreeModel::Children::iterator ri;
1462 TreeModel::Row row;
1463 for (ri = rows.begin(); ri != rows.end(); ++ri) {
1464 row = *ri;
1465 Port* port = row[exp_cols.port];
1467 if (last_port != port) {
1468 chan = 0;
1471 if (row[exp_cols.left]) {
1472 spec.port_map[0].push_back (std::pair<Port*,uint32_t>(port, chan));
1475 if (spec.channels == 2) {
1476 if (row[exp_cols.right]) {
1477 spec.port_map[1].push_back (std::pair<Port*,uint32_t>(port, chan));
1482 chan = 0;
1483 rows = track_selector.get_model()->children();
1485 for (ri = rows.begin(); ri != rows.end(); ++ri) {
1486 row = *ri;
1488 Port* port = row[exp_cols.port];
1490 if (last_port != port) {
1491 chan = 0;
1494 if (row[exp_cols.left]) {
1495 spec.port_map[0].push_back (std::pair<Port*,uint32_t>(port, chan));
1498 if (spec.channels == 2) {
1499 if (row[exp_cols.right]) {
1500 spec.port_map[1].push_back (std::pair<Port*,uint32_t>(port, chan));
1505 last_port = port;
1506 ++chan;
1511 gint
1512 ExportDialog::window_closed (GdkEventAny *ignored)
1514 end_dialog ();
1515 return TRUE;
1518 void
1519 ExportDialog::browse ()
1521 FileChooserDialog dialog("Export to file", browse_action());
1522 dialog.set_transient_for(*this);
1523 dialog.set_filename (file_entry.get_text());
1525 dialog.add_button(Gtk::Stock::CANCEL, Gtk::RESPONSE_CANCEL);
1526 dialog.add_button(Gtk::Stock::OK, Gtk::RESPONSE_OK);
1528 int result = dialog.run();
1530 if (result == Gtk::RESPONSE_OK) {
1531 string filename = dialog.get_filename();
1533 if (filename.length()) {
1534 file_entry.set_text (filename);
1539 void
1540 ExportDialog::track_selector_button_click ()
1542 if (track_scroll.is_visible ()) {
1543 track_scroll.hide ();
1544 } else {
1545 track_scroll.show_all ();