fix keyboard event handling for host-provided plugin GUIs
[ardour2.git] / gtk2_ardour / export_dialog.cc
blob00d2ebbe662e340c69a3879275c4e0fb7da60440
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>
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"
44 #include "keyboard.h"
45 #include "nag.h"
47 #include "i18n.h"
49 #define FRAME_NAME "BaseFrame"
51 using namespace std;
52 using namespace ARDOUR;
53 using namespace PBD;
54 using namespace sigc;
55 using namespace Gtk;
56 using namespace Gtkmm2ext;
58 static const gchar *sample_rates[] = {
59 N_("22.05kHz"),
60 N_("44.1kHz"),
61 N_("48kHz"),
62 N_("88.2kHz"),
63 N_("96kHz"),
64 N_("192kHz"),
68 static const gchar *src_quality[] = {
69 N_("best"),
70 N_("fastest"),
71 N_("linear"),
72 N_("better"),
73 N_("intermediate"),
77 static const gchar *dither_types[] = {
78 N_("None"),
79 N_("Rectangular"),
80 N_("Shaped Noise"),
81 N_("Triangular"),
85 static const gchar* channel_strings[] = {
86 N_("stereo"),
87 N_("mono"),
91 static const gchar* cue_file_types[] = {
92 N_("None"),
93 N_("CUE"),
94 N_("TOC"),
98 ExportDialog::ExportDialog(PublicEditor& e)
99 : ArdourDialog ("export dialog"),
100 editor (e),
101 format_table (9, 2),
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 ..."))
115 guint32 n;
116 guint32 len;
117 guint32 maxlen;
119 session = 0;
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"), PROGRAM_NAME);
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");
132 file_entry.set_activates_default (true);
134 master_list = ListStore::create (exp_cols);
135 master_selector.set_model (master_list);
137 master_selector.set_name ("ExportTrackSelector");
138 master_selector.set_size_request (-1, 100);
139 master_selector.append_column(_("Output"), exp_cols.output);
140 master_selector.append_column_editable(_("Left"), exp_cols.left);
141 master_selector.append_column_editable(_("Right"), exp_cols.right);
142 master_selector.get_column(0)->set_min_width(100);
144 master_selector.get_column(1)->set_min_width(40);
145 master_selector.get_column(1)->set_sizing(Gtk::TREE_VIEW_COLUMN_AUTOSIZE);
146 master_selector.get_column(2)->set_min_width(40);
147 master_selector.get_column(2)->set_sizing(Gtk::TREE_VIEW_COLUMN_AUTOSIZE);
148 master_selector.get_selection()->set_mode (Gtk::SELECTION_NONE);
150 track_list = ListStore::create (exp_cols);
151 track_selector.set_model (track_list);
153 track_selector.set_name ("ExportTrackSelector");
154 track_selector.set_size_request (-1, 130);
155 track_selector.append_column(_("Output"), exp_cols.output);
156 track_selector.append_column_editable(_("Left"), exp_cols.left);
157 track_selector.append_column_editable(_("Right"), exp_cols.right);
159 track_selector.get_column(0)->set_min_width(100);
160 track_selector.get_column(1)->set_min_width(40);
161 track_selector.get_column(1)->set_sizing(Gtk::TREE_VIEW_COLUMN_AUTOSIZE);
162 track_selector.get_column(2)->set_min_width(40);
163 track_selector.get_column(2)->set_sizing(Gtk::TREE_VIEW_COLUMN_AUTOSIZE);
164 track_selector.get_selection()->set_mode (Gtk::SELECTION_NONE);
166 progress_bar.set_name ("ExportProgress");
168 format_frame.add (format_table);
169 format_frame.set_name (FRAME_NAME);
171 track_scroll.set_policy(Gtk::POLICY_AUTOMATIC, Gtk::POLICY_AUTOMATIC);
172 master_scroll.set_policy(Gtk::POLICY_AUTOMATIC, Gtk::POLICY_AUTOMATIC);
174 get_vbox()->pack_start (file_frame, false, false);
176 hpacker.set_spacing (5);
177 hpacker.set_border_width (5);
178 hpacker.pack_start (format_frame, false, false);
180 master_scroll.add (master_selector);
181 track_scroll.add (track_selector);
183 master_scroll.set_size_request (220, 100);
184 track_scroll.set_size_request (220, 100);
186 /* we may hide some of these later */
187 track_vpacker.pack_start (master_scroll);
188 track_vpacker.pack_start (track_scroll);
189 track_vpacker.pack_start (track_selector_button, Gtk::PACK_EXPAND_PADDING);
191 hpacker.pack_start (track_vpacker);
193 get_vbox()->pack_start (hpacker);
195 track_selector_button.set_name ("EditorGTKButton");
196 track_selector_button.signal_clicked().connect (mem_fun(*this, &ExportDialog::track_selector_button_click));
198 get_vbox()->pack_start (progress_bar, false, false);
200 Gtkmm2ext::set_size_request_to_display_given_text (file_entry, X_("Kg/quite/a/reasonable/size/for/files/i/think"), 5, 8);
202 file_hbox.set_spacing (5);
203 file_hbox.set_border_width (5);
204 file_hbox.pack_start (file_entry, true, true);
205 file_hbox.pack_start (file_browse_button, false, false);
207 file_frame.add (file_hbox);
208 file_frame.set_border_width (5);
209 file_frame.set_name (FRAME_NAME);
211 /* pop_strings needs to be created on the stack because set_popdown_strings()
212 takes a reference.
215 vector<string> pop_strings = I18N (sample_rates);
216 Gtkmm2ext::set_popdown_strings (sample_rate_combo, pop_strings);
217 sample_rate_combo.set_active_text (pop_strings.front());
218 pop_strings = I18N (src_quality);
219 Gtkmm2ext::set_popdown_strings (src_quality_combo, pop_strings);
220 src_quality_combo.set_active_text (pop_strings.front());
221 pop_strings = I18N (dither_types);
222 Gtkmm2ext::set_popdown_strings (dither_type_combo, pop_strings);
223 dither_type_combo.set_active_text (pop_strings.front());
224 pop_strings = I18N (channel_strings);
225 Gtkmm2ext::set_popdown_strings (channel_count_combo, pop_strings);
226 channel_count_combo.set_active_text (pop_strings.front());
227 pop_strings = I18N ((const char **) sndfile_header_formats_strings);
228 Gtkmm2ext::set_popdown_strings (header_format_combo, pop_strings);
229 header_format_combo.set_active_text (pop_strings.front());
230 pop_strings = I18N ((const char **) sndfile_bitdepth_formats_strings);
231 Gtkmm2ext::set_popdown_strings (bitdepth_format_combo, pop_strings);
232 bitdepth_format_combo.set_active_text (pop_strings.front());
233 pop_strings = I18N ((const char **) sndfile_endian_formats_strings);
234 Gtkmm2ext::set_popdown_strings (endian_format_combo, pop_strings);
235 endian_format_combo.set_active_text (pop_strings.front());
236 pop_strings = I18N (cue_file_types);
237 Gtkmm2ext::set_popdown_strings (cue_file_combo, pop_strings);
238 cue_file_combo.set_active_text (pop_strings.front());
240 /* this will re-sensitized as soon as a something other than RIFF/WAV, AIFF or OGG
241 header format is chosen.
244 endian_format_combo.set_sensitive (false);
246 /* determine longest strings at runtime */
248 maxlen = 0;
249 const char *longest = X_("gl"); /* translators: one ascender, one descender */
250 string longest_str;
252 for (n = 0; n < SNDFILE_HEADER_FORMATS; ++n) {
253 if ((len = strlen (sndfile_header_formats_strings[n])) > maxlen) {
254 maxlen = len;
255 longest = sndfile_header_formats_strings[n];
259 for (n = 0; n < SNDFILE_BITDEPTH_FORMATS; ++n) {
260 if ((len = strlen (sndfile_bitdepth_formats_strings[n])) > maxlen) {
261 maxlen = len;
262 longest = sndfile_bitdepth_formats_strings[n];
266 for (n = 0; n < SNDFILE_ENDIAN_FORMATS; ++n) {
267 if ((len = strlen (sndfile_endian_formats_strings[n])) > maxlen) {
268 maxlen = len;
269 longest = sndfile_endian_formats_strings[n];
273 longest_str = longest;
275 /* force ascender + descender */
277 longest_str[0] = 'g';
278 longest_str[1] = 'l';
280 //Gtkmm2ext::set_size_request_to_display_given_text (header_format_combo, longest_str.c_str(), 5+FUDGE, 5);
282 // TRANSLATORS: "slereg" is "stereo" with ascender and descender substituted
283 //Gtkmm2ext::set_size_request_to_display_given_text (channel_count_combo, _("slereg"), 5+FUDGE, 5);
285 /* header_format_combo.set_focus_on_click (true);
286 bitdepth_format_combo.set_focus_on_click (true);
287 endian_format_combo.set_focus_on_click (true);
288 channel_count_combo.set_focus_on_click (true);
289 src_quality_combo.set_focus_on_click (true);
290 dither_type_combo.set_focus_on_click (true);
291 sample_rate_combo.set_focus_on_click (true);
292 cue_file_combo.set_focus_on_click (true);
294 dither_type_label.set_name ("ExportFormatLabel");
295 sample_rate_label.set_name ("ExportFormatLabel");
296 src_quality_label.set_name ("ExportFormatLabel");
297 channel_count_label.set_name ("ExportFormatLabel");
298 header_format_label.set_name ("ExportFormatLabel");
299 bitdepth_format_label.set_name ("ExportFormatLabel");
300 endian_format_label.set_name ("ExportFormatLabel");
301 cue_file_label.set_name ("ExportFormatLabel");
303 header_format_combo.set_name ("ExportFormatDisplay");
304 bitdepth_format_combo.set_name ("ExportFormatDisplay");
305 endian_format_combo.set_name ("ExportFormatDisplay");
306 channel_count_combo.set_name ("ExportFormatDisplay");
307 dither_type_combo.set_name ("ExportFormatDisplay");
308 src_quality_combo.set_name ("ExportFormatDisplay");
309 sample_rate_combo.set_name ("ExportFormatDisplay");
310 cue_file_combo.set_name ("ExportFormatDisplay");
312 cuefile_only_checkbox.set_name ("ExportCheckbox");
314 format_table.set_homogeneous (false);
315 format_table.set_border_width (5);
316 format_table.set_col_spacings (5);
317 format_table.set_row_spacings (5);
319 int row = 0;
321 format_table.attach (channel_count_label, 0, 1, row, row+1);
322 format_table.attach (channel_count_combo, 1, 2, row, row+1);
324 row++;
326 format_table.attach (header_format_label, 0, 1, row, row+1);
327 format_table.attach (header_format_combo, 1, 2, row, row+1);
329 row++;
331 format_table.attach (bitdepth_format_label, 0, 1, row, row+1);
332 format_table.attach (bitdepth_format_combo, 1, 2, row, row+1);
334 row++;
336 if (!Profile->get_sae()) {
337 format_table.attach (endian_format_label, 0, 1, row, row+1);
338 format_table.attach (endian_format_combo, 1, 2, row, row+1);
339 row++;
342 format_table.attach (sample_rate_label, 0, 1, row, row+1);
343 format_table.attach (sample_rate_combo, 1, 2, row, row+1);
345 row++;
347 if (!Profile->get_sae()) {
348 format_table.attach (src_quality_label, 0, 1, row, row+1);
349 format_table.attach (src_quality_combo, 1, 2, row, row+1);
350 row++;
353 format_table.attach (dither_type_label, 0, 1, row, row+1);
354 format_table.attach (dither_type_combo, 1, 2, row, row+1);
356 row++;
358 if (!Profile->get_sae()) {
359 format_table.attach (cue_file_label, 0, 1, row, row+1);
360 format_table.attach (cue_file_combo, 1, 2, row, row+1);
361 row++;
363 format_table.attach (cuefile_only_checkbox, 0, 2, row, row+1);
366 file_entry.set_name ("ExportFileDisplay");
368 signal_delete_event().connect (mem_fun(*this, &ExportDialog::window_closed));
370 cancel_button = add_button (Stock::CANCEL, RESPONSE_CANCEL);
371 cancel_button->signal_clicked().connect (mem_fun(*this, &ExportDialog::end_dialog));
372 ok_button = add_button (_("Export"), RESPONSE_ACCEPT);
373 set_default_response (RESPONSE_ACCEPT);
374 ok_button->signal_clicked().connect (mem_fun(*this, &ExportDialog::do_export));
376 file_browse_button.set_name ("EditorGTKButton");
377 file_browse_button.signal_clicked().connect (mem_fun(*this, &ExportDialog::browse));
379 channel_count_combo.signal_changed().connect (mem_fun(*this, &ExportDialog::channels_chosen));
380 bitdepth_format_combo.signal_changed().connect (mem_fun(*this, &ExportDialog::bitdepth_chosen));
381 header_format_combo.signal_changed().connect (mem_fun(*this, &ExportDialog::header_chosen));
382 sample_rate_combo.signal_changed().connect (mem_fun(*this, &ExportDialog::sample_rate_chosen));
383 cue_file_combo.signal_changed().connect (mem_fun(*this, &ExportDialog::cue_file_type_chosen));
386 ExportDialog::~ExportDialog()
390 void
391 ExportDialog::do_not_allow_track_and_master_selection()
393 track_and_master_selection_allowed = false;
394 track_vpacker.set_no_show_all();
397 void
398 ExportDialog::do_not_allow_channel_count_selection()
400 channel_count_selection_allowed = false;
401 channel_count_combo.set_no_show_all();
402 channel_count_label.set_no_show_all();
405 void
406 ExportDialog::do_not_allow_export_cd_markers()
408 export_cd_markers_allowed = false;
409 cue_file_label.set_no_show_all();
410 cue_file_combo.set_no_show_all();
411 cuefile_only_checkbox.set_no_show_all();
414 void
415 ExportDialog::connect_to_session (Session *s)
417 session = s;
418 session->GoingAway.connect (mem_fun(*this, &Window::hide_all));
420 switch (session->frame_rate()) {
421 case 22050:
422 sample_rate_combo.set_active_text (_("22.05kHz"));
423 break;
424 case 44100:
425 sample_rate_combo.set_active_text (_("44.1kHz"));
426 break;
427 case 48000:
428 sample_rate_combo.set_active_text (_("48kHz"));
429 break;
430 case 88200:
431 sample_rate_combo.set_active_text (_("88.2kHz"));
432 break;
433 case 96000:
434 sample_rate_combo.set_active_text (_("96kHz"));
435 break;
436 case 192000:
437 sample_rate_combo.set_active_text (_("192kHz"));
438 break;
439 default:
440 sample_rate_combo.set_active_text (_("44.1kHz"));
441 break;
444 src_quality_combo.set_sensitive (false);
446 set_state();
449 void
450 ExportDialog::set_state()
452 XMLNode* node = session->instant_xml(X_("ExportDialog"), session->path());
453 XMLProperty* prop;
455 if (node) {
457 if ((prop = node->property (X_("sample_rate"))) != 0) {
458 sample_rate_combo.set_active_text(prop->value());
460 if ((prop = node->property (X_("src_quality"))) != 0) {
461 src_quality_combo.set_active_text(prop->value());
463 if ((prop = node->property (X_("dither_type"))) != 0) {
464 dither_type_combo.set_active_text(prop->value());
466 if ((prop = node->property (X_("channel_count"))) != 0) {
467 channel_count_combo.set_active_text(prop->value());
469 if ((prop = node->property (X_("header_format"))) != 0) {
470 header_format_combo.set_active_text(prop->value());
472 if ((prop = node->property (X_("bitdepth_format"))) != 0) {
473 bitdepth_format_combo.set_active_text(prop->value());
475 if ((prop = node->property (X_("endian_format"))) != 0) {
476 endian_format_combo.set_active_text(prop->value());
478 if ((prop = node->property (X_("filename"))) != 0) {
479 file_entry.set_text(prop->value());
481 if ((prop = node->property (X_("cue_file_type"))) != 0) {
482 cue_file_combo.set_active_text(prop->value());
486 header_chosen ();
487 bitdepth_chosen();
488 channels_chosen();
489 sample_rate_chosen();
491 //header_chosen initializes the file_entry text. we need to clear it so it will be set to the default, and/or recover the val that was stored in instant.xml
492 file_entry.set_text("");
493 if (node) {
494 if ((prop = node->property (X_("filename"))) != 0) {
495 file_entry.set_text(prop->value());
500 if (session->master_out()) {
501 track_scroll.hide ();
502 } else {
503 master_scroll.hide ();
504 track_selector_button.hide ();
507 if (!node) {
508 return;
511 if (session->master_out()) {
512 XMLNode* master = find_named_node(*node, (X_("Master")));
513 int nchns;
515 if (!master) {
517 /* default is to use all */
518 if (channel_count_combo.get_active_text() == _("mono")) {
519 nchns = 1;
520 } else {
521 nchns = 2;
524 TreeModel::Children rows = master_selector.get_model()->children();
525 for (uint32_t r = 0; r < session->master_out()->n_outputs(); ++r) {
526 if (nchns == 2) {
527 if (r % 2) {
528 rows[r][exp_cols.right] = true;
529 } else {
530 rows[r][exp_cols.left] = true;
532 } else {
533 rows[r][exp_cols.left] = true;
537 } else {
538 /* XXX use XML state */
542 XMLNode* tracks = find_named_node(*node, (X_("Tracks")));
543 if (!tracks) {
544 return;
547 XMLNodeList track_list = tracks->children(X_("Track"));
548 TreeModel::Children rows = track_selector.get_model()->children();
549 TreeModel::Children::iterator ri = rows.begin();
550 TreeModel::Row row;
552 for (XMLNodeIterator it = track_list.begin(); it != track_list.end(); ++it, ++ri) {
553 if (ri == rows.end()){
554 break;
557 XMLNode* track = *it;
558 row = *ri;
560 if ((prop = track->property(X_("channel1"))) != 0) {
561 if (prop->value() == X_("on")) {
562 row[exp_cols.left] = true;
563 } else {
564 row[exp_cols.left] = false;
568 if ((prop = track->property(X_("channel2"))) != 0) {
569 if (prop->value() == X_("on")) {
570 row[exp_cols.right] = true;
571 } else {
572 row[exp_cols.right] = false;
578 void
579 ExportDialog::save_state()
581 if (!session) {
582 return;
585 XMLNode* node = new XMLNode(X_("ExportDialog"));
587 node->add_property(X_("sample_rate"), sample_rate_combo.get_active_text());
588 node->add_property(X_("src_quality"), src_quality_combo.get_active_text());
589 node->add_property(X_("dither_type"), dither_type_combo.get_active_text());
590 node->add_property(X_("channel_count"), channel_count_combo.get_active_text());
591 node->add_property(X_("header_format"), header_format_combo.get_active_text());
592 node->add_property(X_("bitdepth_format"), bitdepth_format_combo.get_active_text());
593 node->add_property(X_("endian_format"), endian_format_combo.get_active_text());
594 node->add_property(X_("filename"), file_entry.get_text());
595 node->add_property(X_("cue_file_type"), cue_file_combo.get_active_text());
597 XMLNode* tracks = new XMLNode(X_("Tracks"));
599 TreeModel::Children rows = track_selector.get_model()->children();
600 TreeModel::Row row;
601 for (TreeModel::Children::iterator ri = rows.begin(); ri != rows.end(); ++ri) {
602 XMLNode* track = new XMLNode(X_("Track"));
604 row = *ri;
605 track->add_property(X_("channel1"), row[exp_cols.left] ? X_("on") : X_("off"));
606 track->add_property(X_("channel2"), row[exp_cols.right] ? X_("on") : X_("off"));
608 tracks->add_child_nocopy(*track);
610 node->add_child_nocopy(*tracks);
612 session->add_instant_xml(*node, session->path());
615 void
616 ExportDialog::set_range (nframes_t start, nframes_t end)
618 spec.start_frame = start;
619 spec.end_frame = end;
622 gint
623 ExportDialog::progress_timeout ()
625 progress_bar.set_fraction (spec.progress);
626 return TRUE;
629 void
630 frames_to_cd_frames_string (char* buf, nframes_t when, nframes_t fr)
633 long unsigned int remainder;
634 int mins, secs, frames;
636 mins = when / (60 * fr);
637 remainder = when - (mins * 60 * fr);
638 secs = remainder / fr;
639 remainder -= secs * fr;
640 frames = remainder / (fr / 75);
641 sprintf (buf, " %02d:%02d:%02d", mins, secs, frames);
645 struct LocationSortByStart {
646 bool operator() (Location *a, Location *b) {
647 return a->start() < b->start();
651 void
652 ExportDialog::export_toc_file (Locations::LocationList& locations, const string& path)
654 if(!export_cd_markers_allowed){
655 return;
658 long unsigned int last_end_time = spec.start_frame, last_start_time = spec.start_frame;
659 gchar buf[18];
661 /* Build the toc's file name from the specified audio file name. */
662 string basename = Glib::path_get_basename(path);
663 size_t ext_pos = basename.rfind('.');
664 if (ext_pos != string::npos) {
665 basename = basename.substr(0, ext_pos); /* strip file extension, if there is one */
667 string filepath = Glib::build_filename(Glib::path_get_dirname(path), basename + ".toc");
669 ofstream out (filepath.c_str());
670 if (!out) {
671 error << string_compose(_("Editor: cannot open \"%1\" as export file for CD toc file"), filepath) << endmsg;
672 return;
674 out << "CD_DA" << endl;
675 out << "CD_TEXT {" << endl << " LANGUAGE_MAP {" << endl << " 0 : EN" << endl << " }" << endl;
676 out << " LANGUAGE 0 {" << endl << " TITLE \"" << session->name() << "\"" << endl << " }" << endl << "}" << endl;
678 Locations::LocationList::iterator i;
679 Locations::LocationList temp;
681 for (i = locations.begin(); i != locations.end(); ++i) {
682 if ((*i)->start() >= spec.start_frame && (*i)->end() <= spec.end_frame && (*i)->is_cd_marker() && !(*i)->is_end()) {
683 temp.push_back (*i);
687 if (temp.size() > 0) {
688 LocationSortByStart cmp;
689 temp.sort (cmp);
690 Location * curr_range = 0;
691 Locations::LocationList::iterator nexti;
693 for (i = temp.begin(); i != temp.end(); ++i) {
695 if ((*i)->start() >= last_end_time)
697 /* this is a track, defined by a cd range marker or a cd location marker outside of a cd range */
698 out << endl << "TRACK AUDIO" << endl;
700 if ((*i)->cd_info.find("scms") != (*i)->cd_info.end()) {
701 out << "NO ";
703 out << "COPY" << endl;
705 if ((*i)->cd_info.find("preemph") != (*i)->cd_info.end()) {
706 out << "PRE_EMPHASIS" << endl;
707 } else {
708 out << "NO PRE_EMPHASIS" << endl;
711 if ((*i)->cd_info.find("isrc") != (*i)->cd_info.end()) {
712 out << "ISRC \"" << (*i)->cd_info["isrc"] << "\"" << endl;
715 out << "CD_TEXT {" << endl << " LANGUAGE 0 {" << endl << " TITLE \"" << (*i)->name() << "\"" << endl;
716 if ((*i)->cd_info.find("performer") != (*i)->cd_info.end()) {
717 out << " PERFORMER \"" << (*i)->cd_info["performer"] << "\"" << endl;
719 if ((*i)->cd_info.find("string_composer") != (*i)->cd_info.end()) {
720 out << " COMPOSER \"" << (*i)->cd_info["string_composer"] << "\"" << endl;
723 if ((*i)->cd_info.find("isrc") != (*i)->cd_info.end()) {
724 out << " ISRC \"";
725 out << (*i)->cd_info["isrc"].substr(0,2) << "-";
726 out << (*i)->cd_info["isrc"].substr(2,3) << "-";
727 out << (*i)->cd_info["isrc"].substr(5,2) << "-";
728 out << (*i)->cd_info["isrc"].substr(7,5) << "\"" << endl;
731 out << " }" << endl << "}" << endl;
733 frames_to_cd_frames_string (buf, last_end_time - spec.start_frame, session->frame_rate());
734 out << "FILE \"" << Glib::path_get_basename(path) << "\"" << buf;
736 if ((*i)->is_mark()) {
737 // a mark track location needs to look ahead to the next marker's start to determine length
738 nexti = i;
739 ++nexti;
740 if (nexti != temp.end()) {
741 frames_to_cd_frames_string (buf, (*nexti)->start() - last_end_time, session->frame_rate());
742 out << buf << endl;
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 = (*nexti)->start();
750 else {
751 // this was the last marker, use session end
752 frames_to_cd_frames_string (buf, spec.end_frame - last_end_time, session->frame_rate());
753 out << buf << endl;
755 frames_to_cd_frames_string (buf, (*i)->start() - last_end_time, session->frame_rate());
756 out << "START" << buf << endl;
758 last_start_time = (*i)->start();
759 last_end_time = spec.end_frame;
762 curr_range = 0;
764 else {
765 // range
766 frames_to_cd_frames_string (buf, (*i)->end() - last_end_time, session->frame_rate());
767 out << buf << endl;
769 frames_to_cd_frames_string (buf, (*i)->start() - last_end_time, session->frame_rate());
770 out << "START" << buf << endl;
772 last_start_time = (*i)->start();
773 last_end_time = (*i)->end();
775 curr_range = (*i);
779 else if ((*i)->is_mark())
781 /* this is an index within a track */
783 frames_to_cd_frames_string (buf, (*i)->start() - last_start_time, session->frame_rate());
784 out << "INDEX" << buf << endl;
791 void
792 ExportDialog::export_cue_file (Locations::LocationList& locations, const string& path)
794 if(!export_cd_markers_allowed){
795 return;
798 gchar buf[18];
799 long unsigned int last_track_end = spec.start_frame;
800 int numtracks = 0, tracknum = 0, indexnum = 0;
802 /* Build the cue sheet's file name from the specified audio file name. */
803 string basename = Glib::path_get_basename(path);
804 size_t ext_pos = basename.rfind('.');
805 if (ext_pos != string::npos) {
806 basename = basename.substr(0, ext_pos); /* strip file extension, if there is one */
808 string filepath = Glib::build_filename(Glib::path_get_dirname(path), basename + ".cue");
810 ofstream out (filepath.c_str());
811 if (!out) {
812 error << string_compose(_("Editor: cannot open \"%1\" as export file for CD cue file"), filepath) << endmsg;
813 return;
816 Locations::LocationList::iterator i;
817 Locations::LocationList temp;
819 for (i = locations.begin(); i != locations.end(); ++i) {
820 if ((*i)->start() >= spec.start_frame && (*i)->end() <= spec.end_frame && (*i)->is_cd_marker() && !(*i)->is_end()) {
821 temp.push_back (*i);
822 if (!(*i)->is_mark()) {
823 numtracks++;
828 out << "REM Cue file generated by Ardour" << endl;
829 out << "TITLE \"" << session->name() << "\"" << endl;
831 out << "FILE \"" << Glib::path_get_basename(path) << "\" ";
833 /* The cue sheet syntax has originally five file types:
834 WAVE : 44.1 kHz, 16 Bit (little endian)
835 AIFF : 44.1 kHz, 16 Bit (big endian)
836 BINARY : 44.1 kHz, 16 Bit (little endian)
837 MOTOROLA : 44.1 kHz, 16 Bit (big endian)
840 We want to use cue sheets not only as CD images but also as general playlyist
841 format, thus for WAVE and AIFF we don't care if it's really 44.1 kHz/16 Bit, the
842 soundfile's header shows it anyway. But for the raw formats, i.e. BINARY
843 and MOTOROLA we do care, because no header would tell us about a different format.
845 For all other formats we just make up our own file type. MP3 is not supported
846 at the moment.
848 int file_format = sndfile_header_format_from_string (header_format_combo.get_active_text ());
849 if ((file_format & SF_FORMAT_TYPEMASK) == SF_FORMAT_WAV) {
850 out << "WAVE";
851 } else if ((file_format & SF_FORMAT_TYPEMASK) == SF_FORMAT_AIFF) {
852 out << "AIFF";
853 } else if ( ((file_format & SF_FORMAT_TYPEMASK) == SF_FORMAT_RAW)
854 && (sndfile_bitdepth_format_from_string(bitdepth_format_combo.get_active_text()) == SF_FORMAT_PCM_16)
855 && (sample_rate_combo.get_active_text() == _("44.1kHz")) ) {
856 /* raw audio, 16 Bit, 44.1 kHz */
857 if (sndfile_endian_format_from_string(endian_format_combo.get_active_text()) == SF_ENDIAN_LITTLE) {
858 out << "BINARY";
859 } else {
860 out << "MOTOROLA";
862 } else {
863 out << (header_format_combo.get_active_text());
865 out << endl;
867 if (false && numtracks == 0) {
868 /* the user has supplied no track markers.
869 the entire export is treated as one track.
872 numtracks++;
873 tracknum++;
874 indexnum = 0;
876 snprintf (buf, sizeof(buf), " TRACK %02d AUDIO", tracknum);
877 out << buf << endl;
878 out << " FLAGS DCP" << endl;
880 /* use the session name*/
882 out << " TITLE \"" << session->name() << "\"" << endl;
884 /* No pregap is specified in this case, adding the default pregap
885 is left to the burning application. */
887 out << " INDEX 01 00:00:00" << endl;
888 indexnum = 2;
889 last_track_end = spec.end_frame;
892 if (temp.size()) {
893 LocationSortByStart cmp;
894 temp.sort (cmp);
895 Location * curr_range = 0;
896 Locations::LocationList::iterator nexti;
898 for ( i = temp.begin(); i != temp.end(); ++i) {
900 if ((*i)->start() >= last_track_end)
902 /* this is a track and it doesn't start inside another one*/
904 tracknum++;
905 indexnum = 0;
907 snprintf (buf, sizeof(buf), " TRACK %02d AUDIO", tracknum);
908 out << buf << endl;
910 out << " FLAGS" ;
911 if ((*i)->cd_info.find("scms") != (*i)->cd_info.end()) {
912 out << " SCMS";
913 } else {
914 out << " DCP";
916 if ((*i)->cd_info.find("preemph") != (*i)->cd_info.end()) {
917 out << " PRE";
919 out << endl;
921 if ((*i)->cd_info.find("isrc") != (*i)->cd_info.end()) {
922 out << " ISRC " << (*i)->cd_info["isrc"] << endl;
925 if ((*i)->name() != "") {
926 out << " TITLE \"" << (*i)->name() << "\"" << endl;
929 if ((*i)->cd_info.find("performer") != (*i)->cd_info.end()) {
930 out << " PERFORMER \"" << (*i)->cd_info["performer"] << "\"" << endl;
933 if ((*i)->cd_info.find("string_composer") != (*i)->cd_info.end()) {
934 out << " SONGWRITER \"" << (*i)->cd_info["string_composer"] << "\"" << endl;
937 /* only print "Index 00" if not at the same position as "Index 01" */
938 if (last_track_end != (*i)->start()) {
939 frames_to_cd_frames_string (buf, last_track_end - spec.start_frame, session->frame_rate());
940 out << " INDEX 00" << buf << endl;
943 indexnum++;
945 if ((*i)->is_mark()) {
946 // need to find the next start to define the end
947 nexti = i;
948 ++nexti;
949 if (nexti != temp.end()) {
950 last_track_end = (*nexti)->start();
952 else {
953 last_track_end = spec.end_frame;
955 curr_range = 0;
957 else {
958 last_track_end = (*i)->end();
959 curr_range = (*i);
963 if ((tracknum > 0) && ((*i)->start() < last_track_end)) {
964 /*this is an index and it lies within a track*/
965 snprintf (buf, sizeof(buf), " INDEX %02d", indexnum);
966 out << buf;
967 frames_to_cd_frames_string (buf,(*i)->start() - spec.start_frame, session->frame_rate());
968 out << buf << endl;
969 indexnum++;
976 void
977 ExportDialog::do_export_cd_markers (const string& path,const string& cuefile_type)
979 if (cuefile_type == _("TOC")) {
980 session->locations()->apply (*this, &ExportDialog::export_toc_file, path);
981 } else {
982 session->locations()->apply (*this, &ExportDialog::export_cue_file, path);
986 string
987 ExportDialog::get_suffixed_filepath ()
989 string filepath = file_entry.get_text();
991 if (wants_dir()) {
992 return filepath;
995 string::size_type dotpos;
997 /* maybe add suffix */
999 int file_format = sndfile_header_format_from_string (header_format_combo.get_active_text ());
1001 if ((file_format & SF_FORMAT_TYPEMASK) == SF_FORMAT_WAV) {
1002 if (filepath.find (".wav") != filepath.length() - 4) {
1003 if ((dotpos = filepath.rfind ('.')) != string::npos) {
1004 filepath = filepath.substr (0, dotpos);
1006 filepath += ".wav";
1008 } else if ((file_format & SF_FORMAT_TYPEMASK) == SF_FORMAT_AIFF) {
1009 if (filepath.find (".aiff") != filepath.length() - 5) {
1010 if ((dotpos = filepath.rfind ('.')) != string::npos) {
1011 filepath = filepath.substr (0, dotpos);
1013 filepath += ".aiff";
1015 } else if ((file_format & SF_FORMAT_TYPEMASK) == SF_FORMAT_CAF) {
1016 if (filepath.find (".caf") != filepath.length() - 4) {
1017 if ((dotpos = filepath.rfind ('.')) != string::npos) {
1018 filepath = filepath.substr (0, dotpos);
1020 filepath += ".caf";
1022 } else if ((file_format & SF_FORMAT_TYPEMASK) == SF_FORMAT_W64) {
1023 if (filepath.find (".w64") != filepath.length() - 4) {
1024 if ((dotpos = filepath.rfind ('.')) != string::npos) {
1025 filepath = filepath.substr (0, dotpos);
1027 filepath += ".w64";
1029 } else if ((file_format & SF_FORMAT_TYPEMASK) == SF_FORMAT_FLAC) {
1030 if (filepath.find (".flac") != filepath.length() - 5) {
1031 if ((dotpos = filepath.rfind ('.')) != string::npos) {
1032 filepath = filepath.substr (0, dotpos);
1034 filepath += ".flac";
1036 } else if ((file_format & SF_FORMAT_TYPEMASK) == SF_FORMAT_OGG) {
1037 if (filepath.find (".ogg") != filepath.length() - 4) {
1038 if ((dotpos = filepath.rfind ('.')) != string::npos) {
1039 filepath = filepath.substr (0, dotpos);
1041 filepath += ".ogg";
1043 } else if ((file_format & SF_FORMAT_TYPEMASK) == SF_FORMAT_RAW) {
1044 if (filepath.find (".raw") != filepath.length() - 4) {
1045 if ((dotpos = filepath.rfind ('.')) != string::npos) {
1046 filepath = filepath.substr (0, dotpos);
1048 filepath += ".raw";
1051 return filepath;
1054 void
1055 ExportDialog::do_export ()
1057 if (!ARDOUR_UI::instance()->the_engine().connected()) {
1058 MessageDialog msg (*this,
1059 _("Not connected to audioengine"),
1060 true,
1061 MESSAGE_ERROR,
1062 BUTTONS_OK);
1063 msg.set_secondary_text (string_compose (_("%1 cannot export audio when disconnected"), PROGRAM_NAME));
1064 msg.present ();
1065 msg.run ();
1066 return;
1069 string filepath;
1071 filepath = get_suffixed_filepath ();
1073 if(!is_filepath_valid(filepath)){
1074 return;
1077 if (!Profile->get_sae() && export_cd_markers_allowed) {
1078 if (cue_file_combo.get_active_text () != _("None")) {
1079 do_export_cd_markers (filepath, cue_file_combo.get_active_text ());
1082 if (cuefile_only_checkbox.get_active()) {
1083 end_dialog ();
1084 return;
1088 ok_button->set_sensitive(false);
1089 save_state();
1091 set_modal (true);
1093 // read user input into spec
1094 initSpec(filepath);
1096 progress_connection = Glib::signal_timeout().connect (mem_fun(*this, &ExportDialog::progress_timeout), 100);
1097 cancel_label.set_text (_("Stop Export"));
1099 session->pre_export ();
1101 export_audio_data();
1103 progress_connection.disconnect ();
1104 end_dialog ();
1106 /* if not stopped early and not SAE, ask for money, maybe */
1108 if (!spec.stop && !Profile->get_sae()) {
1110 NagScreen* ns = NagScreen::maybe_nag (_("export"));
1112 if (ns) {
1113 ns->nag ();
1114 delete ns;
1119 void
1120 ExportDialog::end_dialog ()
1122 if (spec.running) {
1123 spec.stop = true;
1125 while (spec.running) {
1126 if (gtk_events_pending()) {
1127 gtk_main_iteration ();
1128 } else {
1129 usleep (10000);
1134 session->finalize_audio_export ();
1136 hide_all ();
1138 set_modal (false);
1139 ok_button->set_sensitive(true);
1142 void
1143 ExportDialog::start_export ()
1145 if (session == 0) {
1146 return;
1149 /* If the filename hasn't been set before, use the
1150 current session's export directory as a default
1151 location for the export.
1154 if (file_entry.get_text().length() == 0) {
1155 Glib::ustring export_path = session->export_dir();
1157 if (!wants_dir()) {
1158 export_path = Glib::build_filename (export_path, "export.wav");
1161 file_entry.set_text (export_path);
1164 progress_bar.set_fraction (0);
1165 cancel_label.set_text (_("Cancel"));
1167 show_all ();
1169 if (session->master_out()) {
1170 track_scroll.hide ();
1171 } else {
1172 master_scroll.hide ();
1173 track_selector_button.hide ();
1177 void
1178 ExportDialog::header_chosen ()
1180 int fmt = sndfile_header_format_from_string (header_format_combo.get_active_text ());
1182 if ((fmt & SF_FORMAT_TYPEMASK) == SF_FORMAT_OGG) {
1183 endian_format_combo.set_sensitive (false);
1184 bitdepth_format_combo.set_sensitive (false);
1185 } else {
1186 if ((fmt & SF_FORMAT_TYPEMASK) == SF_FORMAT_WAV) {
1187 endian_format_combo.set_active_text (sndfile_endian_formats_strings[0]);
1188 endian_format_combo.set_sensitive (false);
1189 } else if ((fmt & SF_FORMAT_TYPEMASK) == SF_FORMAT_AIFF) {
1190 endian_format_combo.set_active_text (sndfile_endian_formats_strings[1]);
1191 endian_format_combo.set_sensitive (false);
1192 } else {
1193 endian_format_combo.set_sensitive (true);
1196 bitdepth_format_combo.set_sensitive (true);
1199 file_entry.set_text (get_suffixed_filepath());
1202 void
1203 ExportDialog::bitdepth_chosen ()
1205 int format = sndfile_bitdepth_format_from_string (bitdepth_format_combo.get_active_text ());
1206 switch (format) {
1207 case SF_FORMAT_PCM_24:
1208 case SF_FORMAT_PCM_32:
1209 case SF_FORMAT_FLOAT:
1210 dither_type_combo.set_sensitive (false);
1211 break;
1213 default:
1214 dither_type_combo.set_sensitive (true);
1215 break;
1219 void
1220 ExportDialog::cue_file_type_chosen ()
1222 if (cue_file_combo.get_active_text () != "None") {
1223 cuefile_only_checkbox.set_sensitive (true);
1224 } else {
1225 cuefile_only_checkbox.set_active (false);
1226 cuefile_only_checkbox.set_sensitive (false);
1230 void
1231 ExportDialog::sample_rate_chosen ()
1233 string sr_str = sample_rate_combo.get_active_text();
1234 nframes_t rate;
1236 if (sr_str == N_("22.05kHz")) {
1237 rate = 22050;
1238 } else if (sr_str == _("44.1kHz")) {
1239 rate = 44100;
1240 } else if (sr_str == _("48kHz")) {
1241 rate = 48000;
1242 } else if (sr_str == _("88.2kHz")) {
1243 rate = 88200;
1244 } else if (sr_str == _("96kHz")) {
1245 rate = 96000;
1246 } else if (sr_str == _("192kHz")) {
1247 rate = 192000;
1248 } else {
1249 rate = session->frame_rate();
1252 if (rate != session->frame_rate()) {
1253 src_quality_combo.set_sensitive (true);
1254 } else {
1255 src_quality_combo.set_sensitive (false);
1259 void
1260 ExportDialog::channels_chosen ()
1262 bool mono;
1264 mono = (channel_count_combo.get_active_text() == _("mono"));
1266 if (mono) {
1267 track_selector.get_column(2)->set_visible(false);
1268 track_selector.get_column(1)->set_title(_("Export"));
1270 if (session->master_out()) {
1271 master_selector.get_column(2)->set_visible(false);
1272 master_selector.get_column(1)->set_title(_("Export"));
1275 } else {
1276 track_selector.get_column(2)->set_visible(true);
1277 track_selector.get_column(1)->set_title(_("Left"));
1279 if (session->master_out()) {
1280 master_selector.get_column(2)->set_visible(true);
1281 master_selector.get_column(1)->set_title(_("Left"));
1285 fill_lists();
1288 void
1289 ExportDialog::fill_lists ()
1291 track_list->clear();
1292 master_list->clear();
1294 boost::shared_ptr<Session::RouteList> routes = session->get_routes ();
1296 for (Session::RouteList::iterator ri = routes->begin(); ri != routes->end(); ++ri) {
1298 boost::shared_ptr<Route> route = (*ri);
1300 if (route->hidden()) {
1301 continue;
1304 for (uint32_t i=0; i < route->n_outputs(); ++i) {
1305 string name;
1306 if (route->n_outputs() == 1) {
1307 name = route->name();
1308 } else {
1309 name = string_compose("%1: out-%2", route->name(), i+1);
1312 if (route == session->master_out()) {
1313 TreeModel::iterator iter = master_list->append();
1314 TreeModel::Row row = *iter;
1315 row[exp_cols.output] = name;
1316 row[exp_cols.left] = false;
1317 row[exp_cols.right] = false;
1318 row[exp_cols.port] = route->output (i);
1319 } else {
1320 TreeModel::iterator iter = track_list->append();
1321 TreeModel::Row row = *iter;
1322 row[exp_cols.output] = name;
1323 row[exp_cols.left] = false;
1324 row[exp_cols.right] = false;
1325 row[exp_cols.port] = route->output (i);
1332 bool
1333 ExportDialog::is_filepath_valid(string &filepath)
1335 // sanity check file name first
1337 struct stat statbuf;
1339 if (filepath.empty()) {
1340 string txt = _("Please enter a valid filename.");
1341 MessageDialog msg (*this, txt, false, MESSAGE_ERROR, BUTTONS_OK, true);
1342 msg.run();
1343 return false;
1346 // check if file exists already and warn
1348 if (stat (filepath.c_str(), &statbuf) == 0) {
1349 if (S_ISDIR (statbuf.st_mode)) {
1350 string txt = _("Please specify a complete filename for the audio file.");
1351 MessageDialog msg (*this, txt, false, MESSAGE_ERROR, BUTTONS_OK, true);
1352 msg.run();
1353 return false;
1355 else {
1356 string txt = _("File already exists, do you want to overwrite it?");
1357 MessageDialog msg (*this, txt, false, MESSAGE_QUESTION, BUTTONS_YES_NO, true);
1358 if ((ResponseType) msg.run() == Gtk::RESPONSE_NO) {
1359 return false;
1364 // directory needs to exist and be writable
1366 string dirpath = Glib::path_get_dirname (filepath);
1367 if (::access (dirpath.c_str(), W_OK) != 0) {
1368 string txt = _("Cannot write file in: ") + dirpath;
1369 MessageDialog msg (*this, txt, false, MESSAGE_ERROR, BUTTONS_OK, true);
1370 msg.run();
1371 return false;
1374 return true;
1377 void
1378 ExportDialog::initSpec(string &filepath)
1380 spec.path = filepath;
1381 spec.progress = 0;
1382 spec.running = false;
1383 spec.stop = false;
1384 spec.port_map.clear();
1386 if (channel_count_combo.get_active_text() == _("mono")) {
1387 spec.channels = 1;
1388 } else {
1389 spec.channels = 2;
1392 spec.format = 0;
1394 spec.format |= sndfile_header_format_from_string (header_format_combo.get_active_text ());
1396 /* if they picked Ogg, give them Ogg/Vorbis */
1398 if ((spec.format & SF_FORMAT_TYPEMASK) == SF_FORMAT_OGG) {
1399 spec.format |= SF_FORMAT_VORBIS;
1402 if (!Profile->get_sae()) {
1403 if ((spec.format & SF_FORMAT_TYPEMASK) != SF_FORMAT_OGG) {
1404 /* O/V has no concept of endianness */
1405 spec.format |= sndfile_endian_format_from_string (endian_format_combo.get_active_text ());
1409 if ((spec.format & SF_FORMAT_TYPEMASK) != SF_FORMAT_OGG) {
1410 spec.format |= sndfile_bitdepth_format_from_string (bitdepth_format_combo.get_active_text ());
1413 string sr_str = sample_rate_combo.get_active_text();
1414 if (sr_str == N_("22.05kHz")) {
1415 spec.sample_rate = 22050;
1416 } else if (sr_str == _("44.1kHz")) {
1417 spec.sample_rate = 44100;
1418 } else if (sr_str == _("48kHz")) {
1419 spec.sample_rate = 48000;
1420 } else if (sr_str == _("88.2kHz")) {
1421 spec.sample_rate = 88200;
1422 } else if (sr_str == _("96kHz")) {
1423 spec.sample_rate = 96000;
1424 } else if (sr_str == _("192kHz")) {
1425 spec.sample_rate = 192000;
1426 } else {
1427 spec.sample_rate = session->frame_rate();
1430 if (Profile->get_sae()) {
1431 spec.src_quality = SRC_SINC_BEST_QUALITY;
1432 } else {
1433 string src_str = src_quality_combo.get_active_text();
1434 if (src_str == _("fastest")) {
1435 spec.src_quality = SRC_ZERO_ORDER_HOLD;
1436 } else if (src_str == _("linear")) {
1437 spec.src_quality = SRC_LINEAR;
1438 } else if (src_str == _("better")) {
1439 spec.src_quality = SRC_SINC_FASTEST;
1440 } else if (src_str == _("intermediate")) {
1441 spec.src_quality = SRC_SINC_MEDIUM_QUALITY;
1442 } else {
1443 spec.src_quality = SRC_SINC_BEST_QUALITY;
1447 string dither_str = dither_type_combo.get_active_text();
1448 if (dither_str == _("None")) {
1449 spec.dither_type = GDitherNone;
1450 } else if (dither_str == _("Rectangular")) {
1451 spec.dither_type = GDitherRect;
1452 } else if (dither_str == _("Triangular")) {
1453 spec.dither_type = GDitherTri;
1454 } else {
1455 spec.dither_type = GDitherShaped;
1458 write_track_and_master_selection_to_spec();
1462 void
1463 ExportDialog::write_track_and_master_selection_to_spec()
1465 if(!track_and_master_selection_allowed){
1466 return;
1469 uint32_t chan=0;
1470 Port *last_port = 0;
1472 TreeModel::Children rows = master_selector.get_model()->children();
1473 TreeModel::Children::iterator ri;
1474 TreeModel::Row row;
1475 for (ri = rows.begin(); ri != rows.end(); ++ri) {
1476 row = *ri;
1477 Port* port = row[exp_cols.port];
1479 if (last_port != port) {
1480 chan = 0;
1483 if (row[exp_cols.left]) {
1484 spec.port_map[0].push_back (std::pair<Port*,uint32_t>(port, chan));
1487 if (spec.channels == 2) {
1488 if (row[exp_cols.right]) {
1489 spec.port_map[1].push_back (std::pair<Port*,uint32_t>(port, chan));
1494 chan = 0;
1495 rows = track_selector.get_model()->children();
1497 for (ri = rows.begin(); ri != rows.end(); ++ri) {
1498 row = *ri;
1500 Port* port = row[exp_cols.port];
1502 if (last_port != port) {
1503 chan = 0;
1506 if (row[exp_cols.left]) {
1507 spec.port_map[0].push_back (std::pair<Port*,uint32_t>(port, chan));
1510 if (spec.channels == 2) {
1511 if (row[exp_cols.right]) {
1512 spec.port_map[1].push_back (std::pair<Port*,uint32_t>(port, chan));
1517 last_port = port;
1518 ++chan;
1523 gint
1524 ExportDialog::window_closed (GdkEventAny *ignored)
1526 end_dialog ();
1527 return TRUE;
1530 void
1531 ExportDialog::browse ()
1533 FileChooserDialog dialog("Export to file", browse_action());
1534 dialog.set_transient_for(*this);
1535 dialog.set_filename (file_entry.get_text());
1537 dialog.add_button(Gtk::Stock::CANCEL, Gtk::RESPONSE_CANCEL);
1538 dialog.add_button(Gtk::Stock::OK, Gtk::RESPONSE_OK);
1540 int result = dialog.run();
1542 if (result == Gtk::RESPONSE_OK) {
1543 string filename = dialog.get_filename();
1545 if (filename.length()) {
1546 file_entry.set_text (filename);
1551 void
1552 ExportDialog::track_selector_button_click ()
1554 if (track_scroll.is_visible ()) {
1555 track_scroll.hide ();
1556 } else {
1557 track_scroll.show_all ();