clean up Window titles for consistency
[ardour.git] / gtk2_ardour / export_dialog.cc
1 /*
2     Copyright (C) 1999-2005 Paul Davis 
3
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.
8
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.
13
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.
17
18
19 */
20
21 #include <unistd.h>
22 #include <utility>
23 #include <sys/stat.h>
24 #include <fstream>
25
26 #include <samplerate.h>
27 #include <pbd/convert.h>
28 #include <pbd/xml++.h>
29
30 #include <gtkmm2ext/utils.h>
31
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>
40
41 #include "export_dialog.h"
42 #include "ardour_ui.h"
43 #include "public_editor.h"
44 #include "keyboard.h"
45 #include "nag.h"
46
47 #include "i18n.h"
48
49 #define FRAME_NAME "BaseFrame"
50
51 using namespace std;
52 using namespace ARDOUR;
53 using namespace PBD;
54 using namespace sigc;
55 using namespace Gtk;
56 using namespace Gtkmm2ext;
57
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"),
65         0
66 };
67
68 static const gchar *src_quality[] = {
69         N_("best"),
70         N_("fastest"),
71         N_("linear"),
72         N_("better"),
73         N_("intermediate"),
74         0
75 };
76
77 static const gchar *dither_types[] = {
78         N_("None"),
79         N_("Rectangular"),
80         N_("Shaped Noise"),
81         N_("Triangular"),
82         0
83 };
84
85 static const gchar* channel_strings[] = {
86         N_("stereo"), 
87         N_("mono"), 
88         0
89 };
90
91 static const gchar* cue_file_types[] = {
92         N_("None"), 
93         N_("CUE"),
94         N_("TOC"),
95         0
96 };
97
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 ..."))
114 {
115         guint32 n;
116         guint32 len;
117         guint32 maxlen;
118
119         session = 0;
120         track_and_master_selection_allowed = true;
121         channel_count_selection_allowed = true;
122         export_cd_markers_allowed = true;
123
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);
128         
129         spec.running = false;
130
131         file_entry.set_name ("ExportFileNameEntry");
132
133         master_list = ListStore::create (exp_cols);
134         master_selector.set_model (master_list);
135
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);
142
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);
148
149         track_list = ListStore::create (exp_cols);
150         track_selector.set_model (track_list);
151
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);
157
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);
164
165         progress_bar.set_name ("ExportProgress");
166
167         format_frame.add (format_table);
168         format_frame.set_name (FRAME_NAME);
169
170         track_scroll.set_policy(Gtk::POLICY_AUTOMATIC, Gtk::POLICY_AUTOMATIC);
171         master_scroll.set_policy(Gtk::POLICY_AUTOMATIC, Gtk::POLICY_AUTOMATIC);
172
173         get_vbox()->pack_start (file_frame, false, false);
174
175         hpacker.set_spacing (5);
176         hpacker.set_border_width (5);
177         hpacker.pack_start (format_frame, false, false);
178
179         master_scroll.add (master_selector);
180         track_scroll.add (track_selector);
181
182         master_scroll.set_size_request (220, 100);
183         track_scroll.set_size_request (220, 100);
184                 
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);
189
190         hpacker.pack_start (track_vpacker);
191
192         get_vbox()->pack_start (hpacker);
193         
194         track_selector_button.set_name ("EditorGTKButton");
195         track_selector_button.signal_clicked().connect (mem_fun(*this, &ExportDialog::track_selector_button_click));
196
197         get_vbox()->pack_start (progress_bar, false, false);
198
199         Gtkmm2ext::set_size_request_to_display_given_text (file_entry, X_("Kg/quite/a/reasonable/size/for/files/i/think"), 5, 8);
200
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);
205
206         file_frame.add (file_hbox);
207         file_frame.set_border_width (5);
208         file_frame.set_name (FRAME_NAME);
209
210         /* pop_strings needs to be created on the stack because set_popdown_strings()
211            takes a reference. 
212         */
213
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());
238
239         /* this will re-sensitized as soon as a non RIFF/WAV
240            header format is chosen.
241         */
242
243         endian_format_combo.set_sensitive (false);
244
245         /* determine longest strings at runtime */
246
247         maxlen = 0;
248         const char *longest = X_("gl"); /* translators: one ascender, one descender */
249         string longest_str;
250
251         for (n = 0; n < SNDFILE_HEADER_FORMATS; ++n) {
252                 if ((len = strlen (sndfile_header_formats_strings[n])) > maxlen) {
253                         maxlen = len;
254                         longest = sndfile_header_formats_strings[n];
255                 }
256         }
257
258         for (n = 0; n < SNDFILE_BITDEPTH_FORMATS; ++n) {
259                 if ((len = strlen (sndfile_bitdepth_formats_strings[n])) > maxlen) {
260                         maxlen = len;
261                         longest = sndfile_bitdepth_formats_strings[n];
262                 }
263         }
264
265         for (n = 0; n < SNDFILE_ENDIAN_FORMATS; ++n) {
266                 if ((len = strlen (sndfile_endian_formats_strings[n])) > maxlen) {
267                         maxlen = len;
268                         longest = sndfile_endian_formats_strings[n];
269                 }
270         }
271
272         longest_str = longest;
273
274         /* force ascender + descender */
275
276         longest_str[0] = 'g';
277         longest_str[1] = 'l';
278
279         //Gtkmm2ext::set_size_request_to_display_given_text (header_format_combo, longest_str.c_str(), 5+FUDGE, 5);
280
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);
283
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);
292 */
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");
301
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");
310
311         cuefile_only_checkbox.set_name ("ExportCheckbox");
312
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);
317
318         int row = 0;
319
320         format_table.attach (channel_count_label, 0, 1, row, row+1);
321         format_table.attach (channel_count_combo, 1, 2, row, row+1);
322
323         row++;
324         
325         format_table.attach (header_format_label, 0, 1, row, row+1);
326         format_table.attach (header_format_combo, 1, 2, row, row+1);
327
328         row++;
329
330         format_table.attach (bitdepth_format_label, 0, 1, row, row+1);
331         format_table.attach (bitdepth_format_combo, 1, 2, row, row+1);
332
333         row++;
334
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);
338                 row++;
339         }
340
341         format_table.attach (sample_rate_label, 0, 1, row, row+1);
342         format_table.attach (sample_rate_combo, 1, 2, row, row+1);
343
344         row++;
345
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);
349                 row++;
350         }
351
352         format_table.attach (dither_type_label, 0, 1, row, row+1);
353         format_table.attach (dither_type_combo, 1, 2, row, row+1);
354
355         row++;
356
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);
360                 row++;
361         
362                 format_table.attach (cuefile_only_checkbox, 0, 2, row, row+1);
363         }
364
365         file_entry.set_name ("ExportFileDisplay");
366
367         signal_delete_event().connect (mem_fun(*this, &ExportDialog::window_closed));
368
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));
373         
374         file_browse_button.set_name ("EditorGTKButton");
375         file_browse_button.signal_clicked().connect (mem_fun(*this, &ExportDialog::browse));
376
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));
382 }
383
384 ExportDialog::~ExportDialog()
385 {
386 }
387
388 void
389 ExportDialog::do_not_allow_track_and_master_selection()
390 {
391         track_and_master_selection_allowed = false;
392         track_vpacker.set_no_show_all();
393 }
394
395 void
396 ExportDialog::do_not_allow_channel_count_selection()
397 {
398         channel_count_selection_allowed = false;
399         channel_count_combo.set_no_show_all();
400         channel_count_label.set_no_show_all();
401 }
402
403 void
404 ExportDialog::do_not_allow_export_cd_markers()
405 {
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();
410 }
411
412 void
413 ExportDialog::connect_to_session (Session *s)
414 {
415         session = s;
416         session->GoingAway.connect (mem_fun(*this, &Window::hide_all));
417
418         switch (session->frame_rate()) {
419         case 22050:
420                 sample_rate_combo.set_active_text (_("22.05kHz"));
421                 break;
422         case 44100:
423                 sample_rate_combo.set_active_text (_("44.1kHz"));
424                 break;
425         case 48000:
426                 sample_rate_combo.set_active_text (_("48kHz"));
427                 break;
428         case 88200:
429                 sample_rate_combo.set_active_text (_("88.2kHz"));
430                 break;
431         case 96000:
432                 sample_rate_combo.set_active_text (_("96kHz"));
433                 break;
434         case 192000:
435                 sample_rate_combo.set_active_text (_("192kHz"));
436                 break;
437         default:
438                 sample_rate_combo.set_active_text (_("44.1kHz"));
439                 break;
440         }
441
442         src_quality_combo.set_sensitive (false);
443
444         set_state();
445 }
446
447 void
448 ExportDialog::set_state()
449 {
450         XMLNode* node = session->instant_xml(X_("ExportDialog"), session->path());
451         XMLProperty* prop;
452
453         if (node) {
454
455                 if ((prop = node->property (X_("sample_rate"))) != 0) {
456                         sample_rate_combo.set_active_text(prop->value());
457                 }
458                 if ((prop = node->property (X_("src_quality"))) != 0) {
459                         src_quality_combo.set_active_text(prop->value());
460                 }
461                 if ((prop = node->property (X_("dither_type"))) != 0) {
462                         dither_type_combo.set_active_text(prop->value());
463                 }
464                 if ((prop = node->property (X_("channel_count"))) != 0) {
465                         channel_count_combo.set_active_text(prop->value());
466                 }
467                 if ((prop = node->property (X_("header_format"))) != 0) {
468                         header_format_combo.set_active_text(prop->value());
469                 }
470                 if ((prop = node->property (X_("bitdepth_format"))) != 0) {
471                         bitdepth_format_combo.set_active_text(prop->value());
472                 }
473                 if ((prop = node->property (X_("endian_format"))) != 0) {
474                         endian_format_combo.set_active_text(prop->value());
475                 }
476                 if ((prop = node->property (X_("filename"))) != 0) {
477                         file_entry.set_text(prop->value());
478                 }
479                 if ((prop = node->property (X_("cue_file_type"))) != 0) {
480                         cue_file_combo.set_active_text(prop->value());
481                 }
482         }
483
484         header_chosen ();
485         bitdepth_chosen();
486         channels_chosen();
487         sample_rate_chosen();
488
489         if (session->master_out()) {
490                 track_scroll.hide ();
491         } else {
492                 master_scroll.hide ();
493                 track_selector_button.hide ();
494         }
495
496         if (!node) {
497                 return;
498         }
499
500         if (session->master_out()) {
501                 XMLNode* master = find_named_node(*node, (X_("Master")));
502                 int nchns;
503
504                 if (!master) {
505                         
506                         /* default is to use all */
507                         if (channel_count_combo.get_active_text() == _("mono")) {
508                                 nchns = 1;
509                         } else {
510                                 nchns = 2;
511                         }
512
513                         TreeModel::Children rows = master_selector.get_model()->children();
514                         for (uint32_t r = 0; r < session->master_out()->n_outputs(); ++r) {
515                                 if (nchns == 2) {
516                                         if (r % 2) {
517                                                 rows[r][exp_cols.right] = true;
518                                         } else {
519                                                 rows[r][exp_cols.left] = true;
520                                         }
521                                 } else {
522                                         rows[r][exp_cols.left] = true;
523                                 }
524                         }
525
526                 } else {
527                         /* XXX use XML state */
528                 }
529         }
530
531         XMLNode* tracks = find_named_node(*node, (X_("Tracks")));
532         if (!tracks) {
533                 return;
534         }
535         
536         XMLNodeList track_list = tracks->children(X_("Track"));
537         TreeModel::Children rows = track_selector.get_model()->children();
538         TreeModel::Children::iterator ri = rows.begin();
539         TreeModel::Row row;
540
541         for (XMLNodeIterator it = track_list.begin(); it != track_list.end(); ++it, ++ri) {
542                 if (ri == rows.end()){
543                         break;
544                 }
545
546                 XMLNode* track = *it;
547                 row = *ri;
548
549                 if ((prop = track->property(X_("channel1"))) != 0) {
550                         if (prop->value() == X_("on")) {
551                                 row[exp_cols.left] = true;
552                         } else {
553                                 row[exp_cols.left] = false;
554                         }
555                 }
556
557                 if ((prop = track->property(X_("channel2"))) != 0) {
558                         if (prop->value() == X_("on")) {
559                                 row[exp_cols.right] = true;
560                         } else {
561                                 row[exp_cols.right] = false;
562                         }
563                 }
564         }
565 }
566
567 void
568 ExportDialog::save_state()
569 {
570         if (!session) {
571                 return;
572         }
573
574         XMLNode* node = new XMLNode(X_("ExportDialog"));
575
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());
585
586         XMLNode* tracks = new XMLNode(X_("Tracks"));
587
588         TreeModel::Children rows = track_selector.get_model()->children();
589         TreeModel::Row row;
590         for (TreeModel::Children::iterator ri = rows.begin(); ri != rows.end(); ++ri) {
591                 XMLNode* track = new XMLNode(X_("Track"));
592
593                 row = *ri;
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"));
596
597                 tracks->add_child_nocopy(*track);
598         }
599         node->add_child_nocopy(*tracks);
600         
601         session->add_instant_xml(*node, session->path());
602 }
603
604 void
605 ExportDialog::set_range (nframes_t start, nframes_t end)
606 {
607         spec.start_frame = start;
608         spec.end_frame = end;
609 }
610
611 gint
612 ExportDialog::progress_timeout ()
613 {
614         progress_bar.set_fraction (spec.progress);
615         return TRUE;
616 }
617
618 void
619 frames_to_cd_frames_string (char* buf, nframes_t when, nframes_t fr)
620 {
621
622   long unsigned int remainder;
623   int mins, secs, frames;
624
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);
631
632 }
633
634 struct LocationSortByStart {
635     bool operator() (Location *a, Location *b) {
636             return a->start() < b->start();
637     }
638 };
639
640 void
641 ExportDialog::export_toc_file (Locations::LocationList& locations, const string& path)
642 {
643         if(!export_cd_markers_allowed){
644                 return;
645         }
646         
647         long unsigned int last_end_time = spec.start_frame, last_start_time = spec.start_frame;
648         gchar buf[18];
649         
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 */
655         }
656         string filepath = Glib::build_filename(Glib::path_get_dirname(path), basename + ".toc");
657         
658         ofstream out (filepath.c_str());
659         if (!out) {
660                 error << string_compose(_("Editor: cannot open \"%1\" as export file for CD toc file"), filepath) << endmsg;
661                 return;
662         }
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;
666
667         Locations::LocationList::iterator i;
668         Locations::LocationList temp;
669
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()) {
672                         temp.push_back (*i);
673                 }
674         }
675
676         if (temp.size() > 0) {
677                 LocationSortByStart cmp;
678                 temp.sort (cmp);
679                 Location * curr_range = 0;
680                 Locations::LocationList::iterator nexti;
681
682                 for (i = temp.begin(); i != temp.end(); ++i) {
683
684                         if ((*i)->start() >= last_end_time)
685                         {
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;
688                                 
689                                 if ((*i)->cd_info.find("scms") != (*i)->cd_info.end())  {
690                                         out << "NO ";
691                                 }
692                                 out << "COPY" << endl;
693                                 
694                                 if ((*i)->cd_info.find("preemph") != (*i)->cd_info.end())  {
695                                         out << "PRE_EMPHASIS" << endl;
696                                 } else {
697                                         out << "NO PRE_EMPHASIS" << endl;
698                                 }
699                                 
700                                 if ((*i)->cd_info.find("isrc") != (*i)->cd_info.end())  {
701                                         out << "ISRC \"" << (*i)->cd_info["isrc"] << "\"" << endl;
702                                 }
703                                 
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;
707                                 }
708                                 if ((*i)->cd_info.find("string_composer") != (*i)->cd_info.end()) {
709                                         out  << "     COMPOSER \"" << (*i)->cd_info["string_composer"] << "\"" << endl;
710                                 }
711                                 
712                                 if ((*i)->cd_info.find("isrc") != (*i)->cd_info.end()) {                          
713                                         out  << "     ISRC \"";
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;
718                                 }
719                                 
720                                 out << "  }" << endl << "}" << endl;
721                                 
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;
724                                 
725                                 if ((*i)->is_mark()) {
726                                         // a mark track location needs to look ahead to the next marker's start to determine length
727                                         nexti = i;
728                                         ++nexti;
729                                         if (nexti != temp.end()) {
730                                                 frames_to_cd_frames_string (buf, (*nexti)->start() - last_end_time, session->frame_rate());
731                                                 out << buf << endl;
732                                                 
733                                                 frames_to_cd_frames_string (buf, (*i)->start() - last_end_time, session->frame_rate());
734                                                 out << "START" << buf << endl;
735                                                 
736                                                 last_start_time = (*i)->start();
737                                                 last_end_time = (*nexti)->start();
738                                         }
739                                         else {
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());
742                                                 out << buf << endl;
743                                                 
744                                                 frames_to_cd_frames_string (buf, (*i)->start() - last_end_time, session->frame_rate());
745                                                 out << "START" << buf << endl;
746                                                 
747                                                 last_start_time = (*i)->start();
748                                                 last_end_time = spec.end_frame;
749                                         }
750
751                                         curr_range = 0;
752                                 }
753                                 else {
754                                         // range
755                                         frames_to_cd_frames_string (buf, (*i)->end() - last_end_time, session->frame_rate());
756                                         out << buf << endl;
757                                         
758                                         frames_to_cd_frames_string (buf, (*i)->start() - last_end_time, session->frame_rate());
759                                         out << "START" << buf << endl;
760                                         
761                                         last_start_time = (*i)->start();
762                                         last_end_time = (*i)->end();
763
764                                         curr_range = (*i);
765                                 }
766                                 
767                         }
768                         else if ((*i)->is_mark()) 
769                         {
770                                 /* this is an index within a track */
771                                 
772                                 frames_to_cd_frames_string (buf, (*i)->start() - last_start_time, session->frame_rate());
773                                 out << "INDEX" << buf << endl;
774                         }
775                 }
776         }
777         
778 }
779
780 void
781 ExportDialog::export_cue_file (Locations::LocationList& locations, const string& path)
782 {
783         if(!export_cd_markers_allowed){
784                 return;
785         }
786         
787         gchar buf[18];
788         long unsigned int last_track_end = spec.start_frame;
789         int numtracks = 0, tracknum = 0, indexnum = 0;
790         
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 */
796         }
797         string filepath = Glib::build_filename(Glib::path_get_dirname(path), basename + ".cue");
798         
799         ofstream out (filepath.c_str());
800         if (!out) {
801                 error << string_compose(_("Editor: cannot open \"%1\" as export file for CD cue file"), filepath) << endmsg;
802                 return;
803         }
804
805         Locations::LocationList::iterator i;
806         Locations::LocationList temp;
807
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()) {
810                         temp.push_back (*i);
811                         if (!(*i)->is_mark()) {
812                                 numtracks++;
813                         }
814                 }
815         }
816         
817         out << "REM Cue file generated by Ardour" << endl;
818         out << "TITLE \"" << session->name() << "\"" << endl;
819         
820         out << "FILE \"" << Glib::path_get_basename(path) << "\" ";
821         
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)
827                         MP3
828         
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.
833
834                 For all other formats we just make up our own file type.  MP3 is not supported 
835                 at the moment.
836         */
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) {
839                 out << "WAVE";
840         } else if ((file_format & SF_FORMAT_TYPEMASK) == SF_FORMAT_AIFF) {
841                 out << "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) {
847                         out << "BINARY";
848                 } else {
849                         out << "MOTOROLA";
850                 }
851         } else {
852                 out << (header_format_combo.get_active_text());
853         }
854         out << endl;
855
856         if (false && numtracks == 0) {
857                 /* the user has supplied no track markers.
858                    the entire export is treated as one track. 
859                 */
860
861                 numtracks++;
862                 tracknum++;
863                 indexnum = 0;
864
865                 snprintf (buf, sizeof(buf), "  TRACK %02d AUDIO", tracknum);
866                 out << buf << endl;
867                 out << "    FLAGS DCP" << endl;            
868
869                 /* use the session name*/
870
871                 out << "    TITLE \"" << session->name() << "\"" << endl;
872
873                 /* No pregap is specified in this case, adding the default pregap
874                    is left to the burning application. */
875
876                 out << "    INDEX 01 00:00:00" << endl;
877                 indexnum = 2;
878                 last_track_end = spec.end_frame;
879         }
880
881         if (temp.size()) {
882                 LocationSortByStart cmp;
883                 temp.sort (cmp);
884                 Location * curr_range = 0;
885                 Locations::LocationList::iterator nexti;
886
887                 for ( i = temp.begin(); i != temp.end(); ++i) {
888
889                         if ((*i)->start() >= last_track_end)
890                         {
891                                 /* this is a track and it doesn't start inside another one*/
892                                 
893                                 tracknum++;
894                                 indexnum = 0;
895
896                                 snprintf (buf, sizeof(buf), "  TRACK %02d AUDIO", tracknum);
897                                 out << buf << endl;
898
899                                 out << "    FLAGS" ;
900                                 if ((*i)->cd_info.find("scms") != (*i)->cd_info.end())  {
901                                         out << " SCMS";
902                                 } else {
903                                         out << " DCP";
904                                 }
905                                 if ((*i)->cd_info.find("preemph") != (*i)->cd_info.end())  {
906                                         out << " PRE";
907                                 }
908                                 out << endl;
909                                 
910                                 if ((*i)->cd_info.find("isrc") != (*i)->cd_info.end())  {
911                                         out << "    ISRC " << (*i)->cd_info["isrc"] << endl;
912                                 }
913
914                                 if ((*i)->name() != "") {
915                                         out << "    TITLE \"" << (*i)->name() << "\"" << endl;
916                                 }             
917                                 
918                                 if ((*i)->cd_info.find("performer") != (*i)->cd_info.end()) {
919                                         out << "    PERFORMER \"" <<  (*i)->cd_info["performer"] << "\"" << endl;
920                                 }
921                                 
922                                 if ((*i)->cd_info.find("string_composer") != (*i)->cd_info.end()) {
923                                         out << "    SONGWRITER \"" << (*i)->cd_info["string_composer"]  << "\"" << endl;
924                                 }
925
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;
930                                 }
931
932                                 indexnum++;
933
934                                 if ((*i)->is_mark()) {
935                                         // need to find the next start to define the end
936                                         nexti = i;
937                                         ++nexti;
938                                         if (nexti != temp.end()) {
939                                                 last_track_end = (*nexti)->start();
940                                         }
941                                         else {
942                                                 last_track_end = spec.end_frame;
943                                         }
944                                         curr_range = 0;
945                                 }
946                                 else {
947                                         last_track_end = (*i)->end();
948                                         curr_range = (*i);
949                                 }
950                         } 
951         
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);
955                                 out << buf;
956                                 frames_to_cd_frames_string (buf,(*i)->start() - spec.start_frame, session->frame_rate());
957                                 out << buf << endl;
958                                 indexnum++;
959                         }
960                 }
961         }
962         
963 }
964         
965 void
966 ExportDialog::do_export_cd_markers (const string& path,const string& cuefile_type)
967 {
968         if (cuefile_type == _("TOC")) {
969                 session->locations()->apply (*this, &ExportDialog::export_toc_file, path);      
970         } else {
971                 session->locations()->apply (*this, &ExportDialog::export_cue_file, path);
972         }
973 }
974
975 string
976 ExportDialog::get_suffixed_filepath ()
977 {
978         string filepath = file_entry.get_text();
979
980         if (wants_dir()) {
981                 return filepath;
982         }
983
984         string::size_type dotpos;
985         
986         /* maybe add suffix */
987         
988         int file_format = sndfile_header_format_from_string (header_format_combo.get_active_text ());
989         
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);
994                         }
995                         filepath += ".wav";
996                 }
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);
1001                         }
1002                         filepath += ".aiff";
1003                 }
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);
1008                         }
1009                         filepath += ".caf";
1010                 }
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);
1015                         }
1016                         filepath += ".w64";
1017                 }
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);
1022                         }
1023                         filepath += ".flac";
1024                 }
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);
1029                         }
1030                         filepath += ".ogg";
1031                 }
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);
1036                         }
1037                         filepath += ".raw";
1038                 }
1039         }
1040         return filepath;
1041 }
1042
1043 void
1044 ExportDialog::do_export ()
1045 {
1046         if (!ARDOUR_UI::instance()->the_engine().connected()) {
1047                 MessageDialog msg (*this, 
1048                                    _("Not connected to audioengine"),
1049                                    true,
1050                                    MESSAGE_ERROR,
1051                                    BUTTONS_OK);
1052                 msg.set_secondary_text (_("Ardour cannot export audio when disconnected"));
1053                 msg.present ();
1054                 msg.run ();
1055                 return;
1056         }
1057
1058         string filepath;
1059
1060         filepath = get_suffixed_filepath ();
1061
1062         if(!is_filepath_valid(filepath)){
1063                 return;
1064         }
1065
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 ());
1069                 }
1070
1071                 if (cuefile_only_checkbox.get_active()) {
1072                         end_dialog ();
1073                         return;
1074                 }
1075         }
1076
1077         ok_button->set_sensitive(false);
1078         save_state();
1079
1080         set_modal (true);
1081         
1082         // read user input into spec
1083         initSpec(filepath);
1084         
1085         progress_connection = Glib::signal_timeout().connect (mem_fun(*this, &ExportDialog::progress_timeout), 100);
1086         cancel_label.set_text (_("Stop Export"));
1087
1088         session->pre_export ();
1089
1090         export_audio_data();
1091         
1092         progress_connection.disconnect ();
1093         end_dialog ();
1094
1095         /* if not stopped early and not SAE, ask for money, maybe */
1096
1097         if (!spec.stop && !Profile->get_sae()) {
1098
1099                 NagScreen* ns = NagScreen::maybe_nag (_("export"));
1100                 
1101                 if (ns) {
1102                         ns->nag ();
1103                         delete ns;
1104                 }
1105         }
1106 }
1107         
1108 void
1109 ExportDialog::end_dialog ()
1110 {
1111         if (spec.running) {
1112                 spec.stop = true;
1113
1114                 while (spec.running) {
1115                         if (gtk_events_pending()) {
1116                                 gtk_main_iteration ();
1117                         } else {
1118                                 usleep (10000);
1119                         }
1120                 }
1121         }
1122
1123         session->finalize_audio_export ();
1124
1125         hide_all ();
1126
1127         set_modal (false);
1128         ok_button->set_sensitive(true);
1129 }
1130
1131 void
1132 ExportDialog::start_export ()
1133 {
1134         if (session == 0) {
1135                 return;
1136         }
1137
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.  
1141         */
1142         
1143         if (file_entry.get_text().length() == 0) {
1144                 Glib::ustring export_path = session->export_dir();
1145
1146                 if (!wants_dir()) {
1147                         export_path = Glib::build_filename (export_path, "export.wav");
1148                 }
1149                 
1150                 file_entry.set_text (export_path);
1151         }
1152         
1153         progress_bar.set_fraction (0);
1154         cancel_label.set_text (_("Cancel"));
1155
1156         show_all ();
1157
1158         if (session->master_out()) {
1159                 track_scroll.hide ();
1160         } else {
1161                 master_scroll.hide ();
1162                 track_selector_button.hide ();
1163         }
1164 }
1165
1166 void
1167 ExportDialog::header_chosen ()
1168 {
1169         int fmt = sndfile_header_format_from_string (header_format_combo.get_active_text ());
1170         
1171         if ((fmt & SF_FORMAT_TYPEMASK) == SF_FORMAT_OGG) {
1172                 endian_format_combo.set_sensitive (false);
1173                 bitdepth_format_combo.set_sensitive (false);     
1174         } else {
1175                 if ((fmt & SF_FORMAT_TYPEMASK) == SF_FORMAT_WAV) {
1176                        endian_format_combo.set_sensitive (false);
1177                 } else {
1178                        endian_format_combo.set_sensitive (true);
1179                 }
1180                 bitdepth_format_combo.set_sensitive (true);     
1181         }
1182
1183         file_entry.set_text (get_suffixed_filepath());
1184 }
1185
1186 void
1187 ExportDialog::bitdepth_chosen ()
1188 {
1189         int format = sndfile_bitdepth_format_from_string (bitdepth_format_combo.get_active_text ());    
1190         switch (format) {
1191         case SF_FORMAT_PCM_24:
1192         case SF_FORMAT_PCM_32:
1193         case SF_FORMAT_FLOAT:
1194                 dither_type_combo.set_sensitive (false);
1195                 break;
1196
1197         default:
1198                 dither_type_combo.set_sensitive (true);
1199                 break;
1200         }
1201 }
1202
1203 void
1204 ExportDialog::cue_file_type_chosen ()
1205 {
1206         if (cue_file_combo.get_active_text () != "None") {
1207                 cuefile_only_checkbox.set_sensitive (true);
1208         } else {
1209                 cuefile_only_checkbox.set_active (false);
1210                 cuefile_only_checkbox.set_sensitive (false);
1211         }
1212 }
1213
1214 void
1215 ExportDialog::sample_rate_chosen ()
1216 {
1217         string sr_str = sample_rate_combo.get_active_text();
1218         nframes_t rate;
1219
1220         if (sr_str == N_("22.05kHz")) {
1221                 rate = 22050;
1222         } else if (sr_str == _("44.1kHz")) {
1223                 rate = 44100;
1224         } else if (sr_str == _("48kHz")) {
1225                 rate = 48000;
1226         } else if (sr_str == _("88.2kHz")) {
1227                 rate = 88200;
1228         } else if (sr_str == _("96kHz")) {
1229                 rate = 96000;
1230         } else if (sr_str == _("192kHz")) {
1231                 rate = 192000;
1232         } else {
1233                 rate = session->frame_rate();
1234         }
1235                 
1236         if (rate != session->frame_rate()) {
1237                 src_quality_combo.set_sensitive (true);
1238         } else {
1239                 src_quality_combo.set_sensitive (false);
1240         }
1241 }
1242
1243 void
1244 ExportDialog::channels_chosen ()
1245 {
1246         bool mono;
1247
1248         mono = (channel_count_combo.get_active_text() == _("mono"));
1249
1250         if (mono) {
1251                 track_selector.get_column(2)->set_visible(false);
1252                 track_selector.get_column(1)->set_title(_("Export"));
1253
1254                 if (session->master_out()) {
1255                         master_selector.get_column(2)->set_visible(false);
1256                         master_selector.get_column(1)->set_title(_("Export"));
1257                 }
1258
1259         } else {
1260                 track_selector.get_column(2)->set_visible(true);
1261                 track_selector.get_column(1)->set_title(_("Left"));
1262
1263                 if (session->master_out()) {
1264                         master_selector.get_column(2)->set_visible(true);
1265                         master_selector.get_column(1)->set_title(_("Left"));
1266                 }
1267         }
1268
1269         fill_lists();
1270 }
1271
1272 void
1273 ExportDialog::fill_lists ()
1274 {
1275         track_list->clear();
1276         master_list->clear();
1277         
1278         boost::shared_ptr<Session::RouteList> routes = session->get_routes ();
1279
1280         for (Session::RouteList::iterator ri = routes->begin(); ri != routes->end(); ++ri) {
1281                 
1282                 boost::shared_ptr<Route> route = (*ri);
1283                 
1284                 if (route->hidden()) {
1285                         continue;
1286                 }
1287
1288                 for (uint32_t i=0; i < route->n_outputs(); ++i) {
1289                         string name;
1290                         if (route->n_outputs() == 1) {
1291                                 name = route->name();
1292                         } else {
1293                                 name = string_compose("%1: out-%2", route->name(), i+1);
1294                         }
1295
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);
1303                         } else {
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);
1310                         }
1311                 }
1312         }
1313 }
1314
1315
1316 bool
1317 ExportDialog::is_filepath_valid(string &filepath)
1318 {
1319         // sanity check file name first
1320
1321         struct stat statbuf;
1322   
1323         if (filepath.empty()) {
1324                 string txt = _("Please enter a valid filename.");
1325                 MessageDialog msg (*this, txt, false, MESSAGE_ERROR, BUTTONS_OK, true);
1326                 msg.run();
1327                 return false;
1328         }
1329         
1330         // check if file exists already and warn
1331
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);
1336                         msg.run();
1337                         return false;
1338                 }
1339                 else {
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) {
1343                                 return false;
1344                         }
1345                 }
1346         }
1347         
1348         // directory needs to exist and be writable
1349
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);
1354                 msg.run();
1355                 return false;
1356         }
1357         
1358         return true;
1359 }
1360
1361 void
1362 ExportDialog::initSpec(string &filepath)
1363 {
1364         spec.path = filepath;
1365         spec.progress = 0;
1366         spec.running = false;
1367         spec.stop = false;
1368         spec.port_map.clear();
1369         
1370         if (channel_count_combo.get_active_text() == _("mono")) {
1371                 spec.channels = 1;
1372         } else {
1373                 spec.channels = 2;
1374         }
1375
1376         spec.format = 0;
1377
1378         spec.format |= sndfile_header_format_from_string (header_format_combo.get_active_text ());
1379
1380         /* if they picked Ogg, give them Ogg/Vorbis */
1381
1382         if ((spec.format & SF_FORMAT_TYPEMASK) == SF_FORMAT_OGG) {
1383           spec.format |= SF_FORMAT_VORBIS;
1384         }
1385
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 ());
1390                 }
1391         }
1392
1393         if ((spec.format & SF_FORMAT_TYPEMASK) != SF_FORMAT_OGG) {
1394           spec.format |= sndfile_bitdepth_format_from_string (bitdepth_format_combo.get_active_text ());
1395         }
1396
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;
1410         } else {
1411                 spec.sample_rate = session->frame_rate();
1412         }
1413         
1414         if (Profile->get_sae()) {
1415                 spec.src_quality = SRC_SINC_BEST_QUALITY;
1416         } else {
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;
1426                 } else {
1427                         spec.src_quality = SRC_SINC_BEST_QUALITY;
1428                 }
1429         }
1430
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;
1438         } else {
1439                 spec.dither_type = GDitherShaped;
1440         } 
1441
1442         write_track_and_master_selection_to_spec();
1443 }
1444
1445
1446 void
1447 ExportDialog::write_track_and_master_selection_to_spec()
1448 {
1449         if(!track_and_master_selection_allowed){
1450                 return;
1451         }
1452
1453         uint32_t chan=0;
1454         Port *last_port = 0;
1455                 
1456         TreeModel::Children rows = master_selector.get_model()->children();
1457         TreeModel::Children::iterator ri;
1458         TreeModel::Row row;
1459         for (ri = rows.begin(); ri != rows.end(); ++ri) {
1460                 row = *ri;
1461                 Port* port = row[exp_cols.port];
1462                 
1463                 if (last_port != port) {
1464                         chan = 0;
1465                 }
1466                 
1467                 if (row[exp_cols.left]) {
1468                         spec.port_map[0].push_back (std::pair<Port*,uint32_t>(port, chan));
1469                 } 
1470                 
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));
1474                         }
1475                 }
1476         }
1477         
1478         chan = 0;
1479         rows = track_selector.get_model()->children();
1480
1481         for (ri = rows.begin(); ri != rows.end(); ++ri) {
1482                 row = *ri;
1483                 
1484                 Port* port = row[exp_cols.port];
1485                 
1486                 if (last_port != port) {
1487                         chan = 0;
1488                 }
1489                 
1490                 if (row[exp_cols.left]) {
1491                         spec.port_map[0].push_back (std::pair<Port*,uint32_t>(port, chan));
1492                 } 
1493                 
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));
1497                         }
1498                         
1499                 }
1500                 
1501                 last_port = port;
1502                 ++chan;
1503         }
1504 }
1505
1506
1507 gint
1508 ExportDialog::window_closed (GdkEventAny *ignored)
1509 {
1510         end_dialog ();
1511         return TRUE;
1512 }
1513
1514 void
1515 ExportDialog::browse ()
1516 {
1517         FileChooserDialog dialog("Export to file", browse_action());
1518         dialog.set_transient_for(*this);
1519         dialog.set_filename (file_entry.get_text());
1520
1521         dialog.add_button(Gtk::Stock::CANCEL, Gtk::RESPONSE_CANCEL);
1522         dialog.add_button(Gtk::Stock::OK, Gtk::RESPONSE_OK);
1523   
1524         int result = dialog.run();
1525
1526         if (result == Gtk::RESPONSE_OK) {
1527                 string filename = dialog.get_filename();
1528         
1529                 if (filename.length()) {
1530                         file_entry.set_text (filename);
1531                 }
1532         }
1533 }
1534
1535 void
1536 ExportDialog::track_selector_button_click ()
1537 {
1538         if (track_scroll.is_visible ()) {
1539                 track_scroll.hide ();
1540         } else {
1541                 track_scroll.show_all ();
1542         }
1543 }