fix errors in multi-range export (and possibly other export styles); compiler warning...
[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
28 #include <pbd/convert.h>
29 #include <pbd/xml++.h>
30
31 #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
40 #include "export_dialog.h"
41 #include "ardour_ui.h"
42 #include "public_editor.h"
43 #include "keyboard.h"
44
45 #include "i18n.h"
46
47 #define FRAME_NAME "BaseFrame"
48
49 using namespace std;
50 using namespace ARDOUR;
51 using namespace PBD;
52 using namespace sigc;
53 using namespace Gtk;
54
55 static const gchar *sample_rates[] = {
56         N_("22.05kHz"),
57         N_("44.1kHz"),
58         N_("48kHz"),
59         N_("88.2kHz"),
60         N_("96kHz"),
61         N_("192kHz"),
62         0
63 };
64
65 static const gchar *src_quality[] = {
66         N_("best"),
67         N_("fastest"),
68         N_("linear"),
69         N_("better"),
70         N_("intermediate"),
71         0
72 };
73
74 static const gchar *dither_types[] = {
75         N_("None"),
76         N_("Rectangular"),
77         N_("Shaped Noise"),
78         N_("Triangular"),
79         0
80 };
81
82 static const gchar* channel_strings[] = {
83         N_("stereo"), 
84         N_("mono"), 
85         0
86 };
87
88 static const gchar* cue_file_types[] = {
89         N_("None"), 
90         N_("CUE"),
91         N_("TOC"),
92         0
93 };
94
95 ExportDialog::ExportDialog(PublicEditor& e)
96         : ArdourDialog ("export dialog"),
97           editor (e),
98           format_table (9, 2),
99           format_frame (_("Format")),
100           cue_file_label (_("CD Marker File Type"), 1.0, 0.5),
101           channel_count_label (_("Channels"), 1.0, 0.5),
102           header_format_label (_("File Type"), 1.0, 0.5),
103           bitdepth_format_label (_("Sample Format"), 1.0, 0.5),
104           endian_format_label (_("Sample Endianness"), 1.0, 0.5),
105           sample_rate_label (_("Sample Rate"), 1.0, 0.5),
106           src_quality_label (_("Conversion Quality"), 1.0, 0.5),
107           dither_type_label (_("Dither Type"), 1.0, 0.5),
108           cuefile_only_checkbox (_("Export CD Marker File Only")),
109           file_browse_button (_("Browse")),
110           track_selector_button (_("Specific tracks ..."))
111 {
112         guint32 n;
113         guint32 len;
114         guint32 maxlen;
115
116         session = 0;
117         track_and_master_selection_allowed = true;
118         channel_count_selection_allowed = true;
119         export_cd_markers_allowed = true;
120         
121         set_title (_("ardour: export"));
122         set_wmclass (X_("ardour_export"), "Ardour");
123         set_name ("ExportWindow");
124         add_events (Gdk::KEY_PRESS_MASK|Gdk::KEY_RELEASE_MASK);
125
126         spec.running = false;
127
128         file_entry.set_name ("ExportFileNameEntry");
129
130         master_list = ListStore::create (exp_cols);
131         master_selector.set_model (master_list);
132
133         master_selector.set_name ("ExportTrackSelector");
134         master_selector.set_size_request (-1, 100);
135         master_selector.append_column(_("Output"), exp_cols.output);
136         master_selector.append_column_editable(_("Left"), exp_cols.left);
137         master_selector.append_column_editable(_("Right"), exp_cols.right);
138         master_selector.get_column(0)->set_min_width(100);
139         
140         master_selector.get_column(1)->set_min_width(40);
141         master_selector.get_column(1)->set_sizing(Gtk::TREE_VIEW_COLUMN_AUTOSIZE);
142         master_selector.get_column(2)->set_min_width(40);
143         master_selector.get_column(2)->set_sizing(Gtk::TREE_VIEW_COLUMN_AUTOSIZE);
144         master_selector.get_selection()->set_mode (Gtk::SELECTION_NONE);
145
146         track_list = ListStore::create (exp_cols);
147         track_selector.set_model (track_list);
148
149         track_selector.set_name ("ExportTrackSelector");
150         track_selector.set_size_request (-1, 130);
151         track_selector.append_column(_("Output"), exp_cols.output);
152         track_selector.append_column_editable(_("Left"), exp_cols.left);
153         track_selector.append_column_editable(_("Right"), exp_cols.right);
154
155         track_selector.get_column(0)->set_min_width(100);
156         track_selector.get_column(1)->set_min_width(40);
157         track_selector.get_column(1)->set_sizing(Gtk::TREE_VIEW_COLUMN_AUTOSIZE);
158         track_selector.get_column(2)->set_min_width(40);
159         track_selector.get_column(2)->set_sizing(Gtk::TREE_VIEW_COLUMN_AUTOSIZE);
160         track_selector.get_selection()->set_mode (Gtk::SELECTION_NONE);
161
162         progress_bar.set_name ("ExportProgress");
163
164         format_frame.add (format_table);
165         format_frame.set_name (FRAME_NAME);
166
167         track_scroll.set_policy(Gtk::POLICY_AUTOMATIC, Gtk::POLICY_AUTOMATIC);
168         master_scroll.set_policy(Gtk::POLICY_AUTOMATIC, Gtk::POLICY_AUTOMATIC);
169
170         get_vbox()->pack_start (file_frame, false, false);
171
172         hpacker.set_spacing (5);
173         hpacker.set_border_width (5);
174         hpacker.pack_start (format_frame, false, false);
175
176         master_scroll.add (master_selector);
177         track_scroll.add (track_selector);
178
179         master_scroll.set_size_request (220, 100);
180         track_scroll.set_size_request (220, 100);
181                 
182         /* we may hide some of these later */
183         track_vpacker.pack_start (master_scroll);
184         track_vpacker.pack_start (track_scroll);
185         track_vpacker.pack_start (track_selector_button, Gtk::PACK_EXPAND_PADDING);
186
187         hpacker.pack_start (track_vpacker);
188
189         get_vbox()->pack_start (hpacker);
190         
191         track_selector_button.set_name ("EditorGTKButton");
192         track_selector_button.signal_clicked().connect (mem_fun(*this, &ExportDialog::track_selector_button_click));
193
194         get_vbox()->pack_start (progress_bar, false, false);
195
196         Gtkmm2ext::set_size_request_to_display_given_text (file_entry, X_("Kg/quite/a/reasonable/size/for/files/i/think"), 5, 8);
197
198         file_hbox.set_spacing (5);
199         file_hbox.set_border_width (5);
200         file_hbox.pack_start (file_entry, true, true);
201         file_hbox.pack_start (file_browse_button, false, false);
202
203         file_frame.add (file_hbox);
204         file_frame.set_border_width (5);
205         file_frame.set_name (FRAME_NAME);
206
207         /* pop_strings needs to be created on the stack because set_popdown_strings()
208            takes a reference. 
209         */
210
211         vector<string> pop_strings = I18N (sample_rates);
212         Gtkmm2ext::set_popdown_strings (sample_rate_combo, pop_strings);
213         sample_rate_combo.set_active_text (pop_strings.front());
214         pop_strings = I18N (src_quality);
215         Gtkmm2ext::set_popdown_strings (src_quality_combo, pop_strings);
216         src_quality_combo.set_active_text (pop_strings.front());
217         pop_strings = I18N (dither_types);
218         Gtkmm2ext::set_popdown_strings (dither_type_combo, pop_strings);
219         dither_type_combo.set_active_text (pop_strings.front());
220         pop_strings = I18N (channel_strings);
221         Gtkmm2ext::set_popdown_strings (channel_count_combo, pop_strings);
222         channel_count_combo.set_active_text (pop_strings.front());
223         pop_strings = I18N ((const char **) sndfile_header_formats_strings);
224         Gtkmm2ext::set_popdown_strings (header_format_combo, pop_strings);
225         header_format_combo.set_active_text (pop_strings.front());
226         pop_strings = I18N ((const char **) sndfile_bitdepth_formats_strings);
227         Gtkmm2ext::set_popdown_strings (bitdepth_format_combo, pop_strings);
228         bitdepth_format_combo.set_active_text (pop_strings.front());
229         pop_strings = I18N ((const char **) sndfile_endian_formats_strings);
230         Gtkmm2ext::set_popdown_strings (endian_format_combo, pop_strings);
231         endian_format_combo.set_active_text (pop_strings.front());
232         pop_strings = I18N (cue_file_types);
233         Gtkmm2ext::set_popdown_strings (cue_file_combo, pop_strings);
234         cue_file_combo.set_active_text (pop_strings.front());
235
236         /* this will re-sensitized as soon as a non RIFF/WAV
237            header format is chosen.
238         */
239
240         endian_format_combo.set_sensitive (false);
241
242         /* determine longest strings at runtime */
243
244         maxlen = 0;
245         const char *longest = X_("gl"); /* translators: one ascender, one descender */
246         string longest_str;
247
248         for (n = 0; n < SNDFILE_HEADER_FORMATS; ++n) {
249                 if ((len = strlen (sndfile_header_formats_strings[n])) > maxlen) {
250                         maxlen = len;
251                         longest = sndfile_header_formats_strings[n];
252                 }
253         }
254
255         for (n = 0; n < SNDFILE_BITDEPTH_FORMATS; ++n) {
256                 if ((len = strlen (sndfile_bitdepth_formats_strings[n])) > maxlen) {
257                         maxlen = len;
258                         longest = sndfile_bitdepth_formats_strings[n];
259                 }
260         }
261
262         for (n = 0; n < SNDFILE_ENDIAN_FORMATS; ++n) {
263                 if ((len = strlen (sndfile_endian_formats_strings[n])) > maxlen) {
264                         maxlen = len;
265                         longest = sndfile_endian_formats_strings[n];
266                 }
267         }
268
269         longest_str = longest;
270
271         /* force ascender + descender */
272
273         longest_str[0] = 'g';
274         longest_str[1] = 'l';
275
276         //Gtkmm2ext::set_size_request_to_display_given_text (header_format_combo, longest_str.c_str(), 5+FUDGE, 5);
277
278         // TRANSLATORS: "slereg" is "stereo" with ascender and descender substituted
279         //Gtkmm2ext::set_size_request_to_display_given_text (channel_count_combo, _("slereg"), 5+FUDGE, 5);
280
281 /*      header_format_combo.set_focus_on_click (true);
282         bitdepth_format_combo.set_focus_on_click (true);
283         endian_format_combo.set_focus_on_click (true);
284         channel_count_combo.set_focus_on_click (true);
285         src_quality_combo.set_focus_on_click (true);
286         dither_type_combo.set_focus_on_click (true);
287         sample_rate_combo.set_focus_on_click (true);
288         cue_file_combo.set_focus_on_click (true);
289 */
290         dither_type_label.set_name ("ExportFormatLabel");
291         sample_rate_label.set_name ("ExportFormatLabel");
292         src_quality_label.set_name ("ExportFormatLabel");
293         channel_count_label.set_name ("ExportFormatLabel");
294         header_format_label.set_name ("ExportFormatLabel");
295         bitdepth_format_label.set_name ("ExportFormatLabel");
296         endian_format_label.set_name ("ExportFormatLabel");
297         cue_file_label.set_name ("ExportFormatLabel");
298
299         header_format_combo.set_name ("ExportFormatDisplay");
300         bitdepth_format_combo.set_name ("ExportFormatDisplay");
301         endian_format_combo.set_name ("ExportFormatDisplay");
302         channel_count_combo.set_name ("ExportFormatDisplay");
303         dither_type_combo.set_name ("ExportFormatDisplay");
304         src_quality_combo.set_name ("ExportFormatDisplay");
305         sample_rate_combo.set_name ("ExportFormatDisplay");
306         cue_file_combo.set_name ("ExportFormatDisplay");
307
308         cuefile_only_checkbox.set_name ("ExportCheckbox");
309
310         format_table.set_homogeneous (false);
311         format_table.set_border_width (5);
312         format_table.set_col_spacings (5);
313         format_table.set_row_spacings (5);
314
315         format_table.attach (channel_count_label, 0, 1, 0, 1);
316         format_table.attach (channel_count_combo, 1, 2, 0, 1);
317         
318         format_table.attach (header_format_label, 0, 1, 1, 2);
319         format_table.attach (header_format_combo, 1, 2, 1, 2);
320
321         format_table.attach (bitdepth_format_label, 0, 1, 2, 3);
322         format_table.attach (bitdepth_format_combo, 1, 2, 2, 3);
323
324         format_table.attach (endian_format_label, 0, 1, 3, 4);
325         format_table.attach (endian_format_combo, 1, 2, 3, 4);
326
327         format_table.attach (sample_rate_label, 0, 1, 4, 5);
328         format_table.attach (sample_rate_combo, 1, 2, 4, 5);
329
330         format_table.attach (src_quality_label, 0, 1, 5, 6);
331         format_table.attach (src_quality_combo, 1, 2, 5, 6);
332
333         format_table.attach (dither_type_label, 0, 1, 6, 7);
334         format_table.attach (dither_type_combo, 1, 2, 6, 7);
335
336         format_table.attach (cue_file_label, 0, 1, 7, 8);
337         format_table.attach (cue_file_combo, 1, 2, 7, 8);
338         format_table.attach (cuefile_only_checkbox, 0, 2, 8, 9);
339
340         file_entry.set_name ("ExportFileDisplay");
341
342         signal_delete_event().connect (mem_fun(*this, &ExportDialog::window_closed));
343
344         cancel_button = add_button (Stock::CANCEL, RESPONSE_CANCEL);
345         cancel_button->signal_clicked().connect (mem_fun(*this, &ExportDialog::end_dialog));
346         ok_button = add_button (_("Export"), RESPONSE_ACCEPT);
347         ok_button->signal_clicked().connect (mem_fun(*this, &ExportDialog::do_export));
348         
349         file_browse_button.set_name ("EditorGTKButton");
350         file_browse_button.signal_clicked().connect (mem_fun(*this, &ExportDialog::browse));
351
352         channel_count_combo.signal_changed().connect (mem_fun(*this, &ExportDialog::channels_chosen));
353         bitdepth_format_combo.signal_changed().connect (mem_fun(*this, &ExportDialog::bitdepth_chosen));
354         header_format_combo.signal_changed().connect (mem_fun(*this, &ExportDialog::header_chosen));
355         sample_rate_combo.signal_changed().connect (mem_fun(*this, &ExportDialog::sample_rate_chosen));
356         cue_file_combo.signal_changed().connect (mem_fun(*this, &ExportDialog::cue_file_type_chosen));
357 }
358
359 ExportDialog::~ExportDialog()
360 {
361 }
362
363 void
364 ExportDialog::do_not_allow_track_and_master_selection()
365 {
366         track_and_master_selection_allowed = false;
367         track_vpacker.set_no_show_all();
368 }
369
370 void
371 ExportDialog::do_not_allow_channel_count_selection()
372 {
373         channel_count_selection_allowed = false;
374         channel_count_combo.set_no_show_all();
375         channel_count_label.set_no_show_all();
376 }
377
378 void
379 ExportDialog::do_not_allow_export_cd_markers()
380 {
381         export_cd_markers_allowed = false;
382         cue_file_label.set_no_show_all();
383         cue_file_combo.set_no_show_all();
384         cuefile_only_checkbox.set_no_show_all();
385 }
386
387 void
388 ExportDialog::connect_to_session (Session *s)
389 {
390         session = s;
391         session->GoingAway.connect (mem_fun(*this, &Window::hide_all));
392
393         switch (session->frame_rate()) {
394         case 22050:
395                 sample_rate_combo.set_active_text (N_("22.05kHz"));
396                 break;
397         case 44100:
398                 sample_rate_combo.set_active_text (N_("44.1kHz"));
399                 break;
400         case 48000:
401                 sample_rate_combo.set_active_text (N_("48kHz"));
402                 break;
403         case 88200:
404                 sample_rate_combo.set_active_text (N_("88.2kHz"));
405                 break;
406         case 96000:
407                 sample_rate_combo.set_active_text (N_("96kHz"));
408                 break;
409         case 192000:
410                 sample_rate_combo.set_active_text (N_("192kHz"));
411                 break;
412         default:
413                 sample_rate_combo.set_active_text (N_("44.1kHz"));
414                 break;
415         }
416
417         src_quality_combo.set_sensitive (false);
418
419         set_state();
420 }
421
422 void
423 ExportDialog::set_state()
424 {
425         XMLNode* node = session->instant_xml(X_("ExportDialog"), session->path());
426         XMLProperty* prop;
427
428         if (node) {
429
430                 if ((prop = node->property (X_("sample_rate"))) != 0) {
431                         sample_rate_combo.set_active_text(prop->value());
432                 }
433                 if ((prop = node->property (X_("src_quality"))) != 0) {
434                         src_quality_combo.set_active_text(prop->value());
435                 }
436                 if ((prop = node->property (X_("dither_type"))) != 0) {
437                         dither_type_combo.set_active_text(prop->value());
438                 }
439                 if ((prop = node->property (X_("channel_count"))) != 0) {
440                         channel_count_combo.set_active_text(prop->value());
441                 }
442                 if ((prop = node->property (X_("header_format"))) != 0) {
443                         header_format_combo.set_active_text(prop->value());
444                 }
445                 if ((prop = node->property (X_("bitdepth_format"))) != 0) {
446                         bitdepth_format_combo.set_active_text(prop->value());
447                 }
448                 if ((prop = node->property (X_("endian_format"))) != 0) {
449                         endian_format_combo.set_active_text(prop->value());
450                 }
451                 if ((prop = node->property (X_("filename"))) != 0) {
452                         file_entry.set_text(prop->value());
453                 }
454                 if ((prop = node->property (X_("cue_file_type"))) != 0) {
455                         cue_file_combo.set_active_text(prop->value());
456                 }
457         }
458
459         header_chosen ();
460         bitdepth_chosen();
461         channels_chosen();
462         sample_rate_chosen();
463
464         if (session->master_out()) {
465                 track_scroll.hide ();
466         } else {
467                 master_scroll.hide ();
468                 track_selector_button.hide ();
469         }
470
471         if (!node) {
472                 return;
473         }
474
475         if (session->master_out()) {
476                 XMLNode* master = find_named_node(*node, (X_("Master")));
477                 int nchns;
478
479                 if (!master) {
480                         
481                         /* default is to use all */
482                         if (channel_count_combo.get_active_text() == _("mono")) {
483                                 nchns = 1;
484                         } else {
485                                 nchns = 2;
486                         }
487
488                         TreeModel::Children rows = master_selector.get_model()->children();
489                         for (uint32_t r = 0; r < session->master_out()->n_outputs(); ++r) {
490                                 if (nchns == 2) {
491                                         if (r % 2) {
492                                                 rows[r][exp_cols.right] = true;
493                                         } else {
494                                                 rows[r][exp_cols.left] = true;
495                                         }
496                                 } else {
497                                         rows[r][exp_cols.left] = true;
498                                 }
499                         }
500
501                 } else {
502                         /* XXX use XML state */
503                 }
504         }
505
506         XMLNode* tracks = find_named_node(*node, (X_("Tracks")));
507         if (!tracks) {
508                 return;
509         }
510         
511         XMLNodeList track_list = tracks->children(X_("Track"));
512         TreeModel::Children rows = track_selector.get_model()->children();
513         TreeModel::Children::iterator ri = rows.begin();
514         TreeModel::Row row;
515
516         for (XMLNodeIterator it = track_list.begin(); it != track_list.end(); ++it, ++ri) {
517                 if (ri == rows.end()){
518                         break;
519                 }
520
521                 XMLNode* track = *it;
522                 row = *ri;
523
524                 if ((prop = track->property(X_("channel1"))) != 0) {
525                         if (prop->value() == X_("on")) {
526                                 row[exp_cols.left] = true;
527                         } else {
528                                 row[exp_cols.left] = false;
529                         }
530                 }
531
532                 if ((prop = track->property(X_("channel2"))) != 0) {
533                         if (prop->value() == X_("on")) {
534                                 row[exp_cols.right] = true;
535                         } else {
536                                 row[exp_cols.right] = false;
537                         }
538                 }
539         }
540 }
541
542 void
543 ExportDialog::save_state()
544 {
545         if (!session) {
546                 return;
547         }
548
549         XMLNode* node = new XMLNode(X_("ExportDialog"));
550
551         node->add_property(X_("sample_rate"), sample_rate_combo.get_active_text());
552         node->add_property(X_("src_quality"), src_quality_combo.get_active_text());
553         node->add_property(X_("dither_type"), dither_type_combo.get_active_text());
554         node->add_property(X_("channel_count"), channel_count_combo.get_active_text());
555         node->add_property(X_("header_format"), header_format_combo.get_active_text());
556         node->add_property(X_("bitdepth_format"), bitdepth_format_combo.get_active_text());
557         node->add_property(X_("endian_format"), endian_format_combo.get_active_text());
558         node->add_property(X_("filename"), file_entry.get_text());
559         node->add_property(X_("cue_file_type"), cue_file_combo.get_active_text());
560
561         XMLNode* tracks = new XMLNode(X_("Tracks"));
562
563         TreeModel::Children rows = track_selector.get_model()->children();
564         TreeModel::Row row;
565         for (TreeModel::Children::iterator ri = rows.begin(); ri != rows.end(); ++ri) {
566                 XMLNode* track = new XMLNode(X_("Track"));
567
568                 row = *ri;
569                 track->add_property(X_("channel1"), row[exp_cols.left] ? X_("on") : X_("off"));
570                 track->add_property(X_("channel2"), row[exp_cols.right] ? X_("on") : X_("off"));
571
572                 tracks->add_child_nocopy(*track);
573         }
574         node->add_child_nocopy(*tracks);
575         
576         session->add_instant_xml(*node, session->path());
577 }
578
579 void
580 ExportDialog::set_range (nframes_t start, nframes_t end)
581 {
582         spec.start_frame = start;
583         spec.end_frame = end;
584 }
585
586 gint
587 ExportDialog::progress_timeout ()
588 {
589         progress_bar.set_fraction (spec.progress);
590         return TRUE;
591 }
592
593 void
594 frames_to_cd_frames_string (char* buf, nframes_t when, nframes_t fr)
595 {
596
597   long unsigned int remainder;
598   int mins, secs, frames;
599
600         mins = when / (60 * fr);
601         remainder = when - (mins * 60 * fr);
602         secs = remainder / fr;
603         remainder -= secs * fr;
604         frames = remainder / (fr / 75);
605         sprintf (buf, " %02d:%02d:%02d", mins, secs, frames);
606
607 }
608
609 struct LocationSortByStart {
610     bool operator() (Location *a, Location *b) {
611             return a->start() < b->start();
612     }
613 };
614
615 void
616 ExportDialog::export_toc_file (Locations::LocationList& locations, const string& path)
617 {
618         if(!export_cd_markers_allowed){
619                 return;
620         }
621         
622     string filepath = path + ".toc";
623         ofstream out (filepath.c_str());
624         long unsigned int last_end_time = spec.start_frame, last_start_time = spec.start_frame;
625         int numtracks = 0;
626         gchar buf[18];
627
628         if (!out) {
629                 error << string_compose(_("Editor: cannot open \"%1\" as export file for CD toc file"), filepath) << endmsg;
630                 return;
631         }
632         out << "CD_DA" << endl;
633         out << "CD_TEXT {" << endl << "  LANGUAGE_MAP {" << endl << "    0 : EN" << endl << "  }" << endl;
634         out << "  LANGUAGE 0 {" << endl << "    TITLE \"" << session->name() << "\"" << endl << "  }" << endl << "}" << endl;
635
636         Locations::LocationList::iterator i;
637         Locations::LocationList temp;
638
639         for (i = locations.begin(); i != locations.end(); ++i) {
640           if ((*i)->start() >= spec.start_frame && (*i)->end() <= spec.end_frame && (*i)->is_cd_marker() && !(*i)->is_end()) {
641             temp.push_back (*i);
642             if (!(*i)->is_mark()) {
643               numtracks ++;
644             }
645           }
646         }
647
648         if (numtracks == 0 ) {
649                     /* the user supplied no track markers.
650                        we now treat the session as one track.*/
651
652                     out << endl << "TRACK AUDIO" << endl;
653                    
654                     out << "COPY" << endl;
655
656                     out << "NO PRE_EMPHASIS" << endl;
657    
658                     /* XXX add session properties for catalog etc.
659                        (so far only the session name is used) */
660                     
661                     out << "CD_TEXT {" << endl << "  LANGUAGE 0 {" << endl << "     TITLE \"" << session->name() << "\"" << endl;
662                     out << "  }" << endl << "}" << endl;
663
664                     out << "FILE \"" << path << "\" ";
665                     out << "00:00:00 " ;
666                     frames_to_cd_frames_string (buf, spec.end_frame - spec.start_frame, session->frame_rate());
667                     out << buf << endl;
668                     out << "START 00:00:00" << endl;
669
670                     last_start_time = spec.start_frame;
671                     last_end_time = spec.end_frame;
672         } 
673
674         if (temp.size()) {
675                 LocationSortByStart cmp;
676                 temp.sort (cmp);
677
678                 for (i = temp.begin(); i != temp.end(); ++i) {
679         
680                       if (!(*i)->is_mark()) {
681                         /*this is a track */
682                         out << endl << "TRACK AUDIO" << endl;
683
684                         if ((*i)->cd_info.find("scms") != (*i)->cd_info.end())  {
685                           out << "NO ";
686                         }
687                         out << "COPY" << endl;
688
689                         if ((*i)->cd_info.find("preemph") != (*i)->cd_info.end())  {
690                           out << "PRE_EMPHASIS" << endl;
691                         } else {
692                           out << "NO PRE_EMPHASIS" << endl;
693                         }
694
695                         if ((*i)->cd_info.find("isrc") != (*i)->cd_info.end())  {
696                           out << "ISRC \"" << (*i)->cd_info["isrc"] << "\"" << endl;
697                         }
698
699                         out << "CD_TEXT {" << endl << "  LANGUAGE 0 {" << endl << "     TITLE \"" << (*i)->name() << "\"" << endl;
700                         if ((*i)->cd_info.find("performer") != (*i)->cd_info.end()) {
701                           out << "     PERFORMER \"" << (*i)->cd_info["performer"]  << "\"" << endl;
702                         }
703                         if ((*i)->cd_info.find("string_composer") != (*i)->cd_info.end()) {
704                           out  << "     COMPOSER \"" << (*i)->cd_info["string_composer"] << "\"" << endl;
705                         }
706
707                         if ((*i)->cd_info.find("isrc") != (*i)->cd_info.end()) {                          
708                           out  << "     ISRC \"";
709                           out << (*i)->cd_info["isrc"].substr(0,2) << "-";
710                           out << (*i)->cd_info["isrc"].substr(2,3) << "-";
711                           out << (*i)->cd_info["isrc"].substr(5,2) << "-";
712                           out << (*i)->cd_info["isrc"].substr(7,5) << "\"" << endl;
713                         }
714
715                         out << "  }" << endl << "}" << endl;
716
717                         frames_to_cd_frames_string (buf, last_end_time - spec.start_frame, session->frame_rate());
718                         out << "FILE \"" << path << "\" " << buf;
719
720                         frames_to_cd_frames_string (buf, (*i)->end() - last_end_time, session->frame_rate());
721                         out << buf << endl;
722
723                         frames_to_cd_frames_string (buf, (*i)->start() - last_end_time, session->frame_rate());
724                         out << "START" << buf << endl;
725                         
726                         last_start_time = (*i)->start();
727                         last_end_time = (*i)->end();
728                  
729
730                       } else  if ((*i)->start() < last_end_time) {
731                         /* this is an index within a track */
732                         
733                         frames_to_cd_frames_string (buf, (*i)->start() - last_start_time, session->frame_rate());
734                         out << "INDEX" << buf << endl;
735                       }
736                 }
737         }
738         
739 }
740
741 void
742 ExportDialog::export_cue_file (Locations::LocationList& locations, const string& path)
743 {
744         if(!export_cd_markers_allowed){
745                 return;
746         }
747         
748     string filepath = path + ".cue";
749         ofstream out (filepath.c_str());
750         gchar buf[18];
751         long unsigned int last_track_end = spec.start_frame;
752         int numtracks = 0, tracknum = 0, indexnum = 0;
753
754         if (!out) {
755                 error << string_compose(_("Editor: cannot open \"%1\" as export file for CD cue file"), filepath) << endmsg;
756                 return;
757         }
758
759         Locations::LocationList::iterator i;
760         Locations::LocationList temp;
761
762         for (i = locations.begin(); i != locations.end(); ++i) {
763                 if ((*i)->start() >= spec.start_frame && (*i)->end() <= spec.end_frame && (*i)->is_cd_marker() && !(*i)->is_end()) {
764                         temp.push_back (*i);
765                         if (!(*i)->is_mark()) {
766                                 numtracks++;
767                         }
768                 }
769         }
770         
771         out << "REM Cue file generated by Ardour" << endl;
772         out << "TITLE \"" << session->name() << "\"" << endl;
773
774         if ((header_format_combo.get_active_text() == N_("WAV"))) {
775                   out << "FILE " << path  << " WAVE" << endl;
776         } else {
777                   out << "FILE " << path  << ' ' << (header_format_combo.get_active_text()) << endl;
778         }
779
780         if (numtracks == 0) {
781                     /* the user has supplied no track markers.
782                        the entire export is treated as one track. 
783                     */
784
785                   numtracks++;
786                   tracknum++;
787                   indexnum = 0;
788                   out << endl << "TRACK " << tracknum << " AUDIO" << endl;
789                   out << "FLAGS " ;
790                   
791                   out << "DCP " << endl;                   
792                   
793                   /* use the session name*/
794                   
795                   if (session->name() != "") {
796                     out << "TITLE \"" << session->name() << "\"" << endl;
797                   }           
798                   
799                   /* no pregap in this case */
800
801                   out << "INDEX 00 00:00:00" << endl;
802                   indexnum++;
803                   out << "INDEX 01 00:00:00" << endl;
804                   indexnum++;
805                   last_track_end = spec.end_frame;
806         }
807
808         if (temp.size()) {
809                 LocationSortByStart cmp;
810                 temp.sort (cmp);
811
812                 for ( i = temp.begin(); i != temp.end(); ++i) {
813
814                     if (!(*i)->is_mark() && ((*i)->start() >= last_track_end)) {
815                       /* this is a track and it doesn't start inside another one*/
816                       
817                       tracknum++;
818                       indexnum = 0;
819                       out << endl << "TRACK " << tracknum << " AUDIO" << endl;
820                       out << "FLAGS " ;
821                       
822                       if ((*i)->cd_info.find("scms") != (*i)->cd_info.end())  {
823                         out << "SCMS ";
824                       } else {
825                         out << "DCP ";
826                       }
827                       
828                       if ((*i)->cd_info.find("preemph") != (*i)->cd_info.end())  {
829                         out << "PRE";
830                       }
831                       out << endl;
832                       
833                       if ((*i)->cd_info.find("isrc") != (*i)->cd_info.end())  {
834                         out << "ISRC " << (*i)->cd_info["isrc"] << endl;
835                         
836                       }
837                       if ((*i)->name() != "") {
838                         out << "TITLE \"" << (*i)->name() << "\"" << endl;
839                       }       
840                       
841                       if ((*i)->cd_info.find("performer") != (*i)->cd_info.end()) {
842                         out << "PERFORMER \"" <<  (*i)->cd_info["performer"] << "\"" << endl;
843                       }
844                       
845                       if ((*i)->cd_info.find("string_composer") != (*i)->cd_info.end()) {
846                         out << "SONGWRITER \"" << (*i)->cd_info["string_composer"]  << "\"" << endl;
847                       }
848                         snprintf (buf, sizeof(buf), "INDEX %02d", indexnum);
849                         out << buf;
850                         frames_to_cd_frames_string (buf, last_track_end - spec.start_frame, session->frame_rate());
851                         out << buf << endl;
852                         indexnum++;
853                         last_track_end = (*i)->end();
854                     } 
855                     if ((tracknum > 0) && ((*i)->start() < last_track_end)) {
856                       /*this is an index and it lies within a track*/
857                       snprintf (buf, sizeof(buf), "INDEX %02d", indexnum);
858                       out << buf;
859                       frames_to_cd_frames_string (buf,(*i)->start() - spec.start_frame, session->frame_rate());
860                       out << buf << endl;
861                       indexnum++;
862                     }
863                 }
864         }
865         
866 }
867         
868 void
869 ExportDialog::do_export_cd_markers (const string& path,const string& cuefile_type)
870 {
871         if (cuefile_type == "TOC") {
872                 session->locations()->apply (*this, &ExportDialog::export_toc_file, path);      
873         } else {
874                 session->locations()->apply (*this, &ExportDialog::export_cue_file, path);
875         }
876 }
877
878
879 void
880 ExportDialog::do_export ()
881 {
882         string filepath = file_entry.get_text();
883         
884         if(!is_filepath_valid(filepath)){
885                 return;
886         }
887
888         if (export_cd_markers_allowed) {
889                 if (cue_file_combo.get_active_text () != _("None")) {
890                         do_export_cd_markers (file_entry.get_text(), cue_file_combo.get_active_text ());
891                 }
892
893                 if (cuefile_only_checkbox.get_active()) {
894                         end_dialog ();
895                         return;
896                 }
897         }
898
899         ok_button->set_sensitive(false);
900         save_state();
901
902         set_modal (true);
903         
904         // read user input into spec
905         initSpec(filepath);
906         
907         progress_connection = Glib::signal_timeout().connect (mem_fun(*this, &ExportDialog::progress_timeout), 100);
908         cancel_label.set_text (_("Stop Export"));
909
910         export_audio_data();
911         
912         progress_connection.disconnect ();
913         end_dialog ();
914 }
915         
916 void
917 ExportDialog::end_dialog ()
918 {
919         if (spec.running) {
920                 spec.stop = true;
921
922                 while (spec.running) {
923                         if (gtk_events_pending()) {
924                                 gtk_main_iteration ();
925                         } else {
926                                 usleep (10000);
927                         }
928                 }
929         }
930
931         session->finalize_audio_export ();
932
933         hide_all ();
934
935         set_modal (false);
936         ok_button->set_sensitive(true);
937 }
938
939 void
940 ExportDialog::start_export ()
941 {
942         if (session == 0) {
943                 return;
944         }
945
946         /* If the filename hasn't been set before, use the
947            current session's export directory as a default
948            location for the export.  
949         */
950         
951         if (file_entry.get_text().length() == 0) {
952                 string dir = session->export_dir();
953                 string::size_type last_slash;
954                 
955                 if ((last_slash = dir.find_last_of ('/')) != string::npos && last_slash != 0) {
956                         dir = dir.substr (0, last_slash+1);
957                 }
958
959                 if (!wants_dir()) {
960                         dir = dir + "export.wav";
961                 }
962                 
963                 file_entry.set_text (dir);
964         }
965         
966         progress_bar.set_fraction (0);
967         cancel_label.set_text (_("Cancel"));
968
969         show_all ();
970
971         if (session->master_out()) {
972                 track_scroll.hide ();
973         } else {
974                 master_scroll.hide ();
975                 track_selector_button.hide ();
976         }
977 }
978
979 void
980 ExportDialog::header_chosen ()
981 {
982         if (sndfile_header_format_from_string (header_format_combo.get_active_text ()) == SF_FORMAT_WAV) {
983                 endian_format_combo.set_sensitive (false);
984         } else {
985                 endian_format_combo.set_sensitive (true);
986         }
987 }
988
989 void
990 ExportDialog::bitdepth_chosen ()
991 {
992         int format = sndfile_bitdepth_format_from_string (bitdepth_format_combo.get_active_text ());    
993         switch (format) {
994         case SF_FORMAT_PCM_24:
995         case SF_FORMAT_PCM_32:
996         case SF_FORMAT_FLOAT:
997                 dither_type_combo.set_sensitive (false);
998                 break;
999
1000         default:
1001                 dither_type_combo.set_sensitive (true);
1002                 break;
1003         }
1004 }
1005
1006 void
1007 ExportDialog::cue_file_type_chosen ()
1008 {
1009         if (cue_file_combo.get_active_text () != "None") {
1010                 cuefile_only_checkbox.set_sensitive (true);
1011         } else {
1012                 cuefile_only_checkbox.set_active (false);
1013                 cuefile_only_checkbox.set_sensitive (false);
1014         }
1015 }
1016
1017 void
1018 ExportDialog::sample_rate_chosen ()
1019 {
1020         string sr_str = sample_rate_combo.get_active_text();
1021         nframes_t rate;
1022
1023         if (sr_str == N_("22.05kHz")) {
1024                 rate = 22050;
1025         } else if (sr_str == N_("44.1kHz")) {
1026                 rate = 44100;
1027         } else if (sr_str == N_("48kHz")) {
1028                 rate = 48000;
1029         } else if (sr_str == N_("88.2kHz")) {
1030                 rate = 88200;
1031         } else if (sr_str == N_("96kHz")) {
1032                 rate = 96000;
1033         } else if (sr_str == N_("192kHz")) {
1034                 rate = 192000;
1035         } else {
1036                 rate = session->frame_rate();
1037         }
1038                 
1039         if (rate != session->frame_rate()) {
1040                 src_quality_combo.set_sensitive (true);
1041         } else {
1042                 src_quality_combo.set_sensitive (false);
1043         }
1044 }
1045
1046 void
1047 ExportDialog::channels_chosen ()
1048 {
1049         bool mono;
1050
1051         mono = (channel_count_combo.get_active_text() == _("mono"));
1052
1053         if (mono) {
1054                 track_selector.get_column(2)->set_visible(false);
1055                 track_selector.get_column(1)->set_title(_("Export"));
1056
1057                 if (session->master_out()) {
1058                         master_selector.get_column(2)->set_visible(false);
1059                         master_selector.get_column(1)->set_title(_("Export"));
1060                 }
1061
1062         } else {
1063                 track_selector.get_column(2)->set_visible(true);
1064                 track_selector.get_column(1)->set_title(_("Left"));
1065
1066                 if (session->master_out()) {
1067                         master_selector.get_column(2)->set_visible(true);
1068                         master_selector.get_column(1)->set_title(_("Left"));
1069                 }
1070         }
1071
1072         fill_lists();
1073 }
1074
1075 void
1076 ExportDialog::fill_lists ()
1077 {
1078         track_list->clear();
1079         master_list->clear();
1080         
1081         boost::shared_ptr<Session::RouteList> routes = session->get_routes ();
1082
1083         for (Session::RouteList::iterator ri = routes->begin(); ri != routes->end(); ++ri) {
1084                 
1085                 boost::shared_ptr<Route> route = (*ri);
1086                 
1087                 if (route->hidden()) {
1088                         continue;
1089                 }
1090
1091                 for (uint32_t i=0; i < route->n_outputs(); ++i) {
1092                         string name;
1093                         if (route->n_outputs() == 1) {
1094                                 name = route->name();
1095                         } else {
1096                                 name = string_compose("%1: out-%2", route->name(), i+1);
1097                         }
1098
1099                         if (route == session->master_out()) {
1100                                 TreeModel::iterator iter = master_list->append();
1101                                 TreeModel::Row row = *iter;
1102                                 row[exp_cols.output] = name;
1103                                 row[exp_cols.left] = false;
1104                                 row[exp_cols.right] = false;
1105                                 row[exp_cols.port] = route->output (i);
1106                         } else {
1107                                 TreeModel::iterator iter = track_list->append();
1108                                 TreeModel::Row row = *iter;
1109                                 row[exp_cols.output] = name;
1110                                 row[exp_cols.left] = false;
1111                                 row[exp_cols.right] = false;
1112                                 row[exp_cols.port] = route->output (i);
1113                         }
1114                 }
1115         }
1116 }
1117
1118
1119 bool
1120 ExportDialog::is_filepath_valid(string &filepath)
1121 {
1122         // sanity check file name first
1123
1124         struct stat statbuf;
1125   
1126         if (filepath.empty()) {
1127                 string txt = _("Please enter a valid filename.");
1128                 MessageDialog msg (*this, txt, false, MESSAGE_ERROR, BUTTONS_OK, true);
1129                 msg.run();
1130                 return false;
1131         }
1132         
1133         // check if file exists already and warn
1134
1135         if (stat (filepath.c_str(), &statbuf) == 0) {
1136                 if (S_ISDIR (statbuf.st_mode)) {
1137                         string txt = _("Please specify a complete filename for the audio file.");
1138                         MessageDialog msg (*this, txt, false, MESSAGE_ERROR, BUTTONS_OK, true);
1139                         msg.run();
1140                         return false;
1141                 }
1142                 else {
1143                         string txt = _("File already exists, do you want to overwrite it?");
1144                         MessageDialog msg (*this, txt, false, MESSAGE_QUESTION, BUTTONS_YES_NO, true);
1145                         if ((ResponseType) msg.run() == Gtk::RESPONSE_NO) {
1146                                 return false;
1147                         }
1148                 }
1149         }
1150         
1151         // directory needs to exist and be writable
1152
1153         string dirpath = Glib::path_get_dirname (filepath);
1154         if (::access (dirpath.c_str(), W_OK) != 0) {
1155                 string txt = _("Cannot write file in: ") + dirpath;
1156                 MessageDialog msg (*this, txt, false, MESSAGE_ERROR, BUTTONS_OK, true);
1157                 msg.run();
1158                 return false;
1159         }
1160         
1161         return true;
1162 }
1163
1164 void
1165 ExportDialog::initSpec(string &filepath)
1166 {
1167         spec.path = filepath;
1168         spec.progress = 0;
1169         spec.running = true;
1170         spec.stop = false;
1171         spec.port_map.clear();
1172         
1173         if (channel_count_combo.get_active_text() == _("mono")) {
1174                 spec.channels = 1;
1175         } else {
1176                 spec.channels = 2;
1177         }
1178
1179         spec.format = 0;
1180
1181         spec.format |= sndfile_header_format_from_string (header_format_combo.get_active_text ());
1182         
1183         if ((spec.format & SF_FORMAT_WAV) == 0) {
1184                 /* RIFF/WAV specifies endianess */
1185                 spec.format |= sndfile_endian_format_from_string (endian_format_combo.get_active_text ());
1186         }
1187
1188         spec.format |= sndfile_bitdepth_format_from_string (bitdepth_format_combo.get_active_text ());
1189
1190         string sr_str = sample_rate_combo.get_active_text();
1191         if (sr_str == N_("22.05kHz")) {
1192                 spec.sample_rate = 22050;
1193         } else if (sr_str == N_("44.1kHz")) {
1194                 spec.sample_rate = 44100;
1195         } else if (sr_str == N_("48kHz")) {
1196                 spec.sample_rate = 48000;
1197         } else if (sr_str == N_("88.2kHz")) {
1198                 spec.sample_rate = 88200;
1199         } else if (sr_str == N_("96kHz")) {
1200                 spec.sample_rate = 96000;
1201         } else if (sr_str == N_("192kHz")) {
1202                 spec.sample_rate = 192000;
1203         } else {
1204                 spec.sample_rate = session->frame_rate();
1205         }
1206         
1207         string src_str = src_quality_combo.get_active_text();
1208         if (src_str == _("fastest")) {
1209                 spec.src_quality = SRC_ZERO_ORDER_HOLD;
1210         } else if (src_str == _("linear")) {
1211                 spec.src_quality = SRC_LINEAR;
1212         } else if (src_str == _("better")) {
1213                 spec.src_quality = SRC_SINC_FASTEST;
1214         } else if (src_str == _("intermediate")) {
1215                 spec.src_quality = SRC_SINC_MEDIUM_QUALITY;
1216         } else {
1217                 spec.src_quality = SRC_SINC_BEST_QUALITY;
1218         }
1219
1220         string dither_str = dither_type_combo.get_active_text();
1221         if (dither_str == _("None")) {
1222                 spec.dither_type = GDitherNone;
1223         } else if (dither_str == _("Rectangular")) {
1224                 spec.dither_type = GDitherRect;
1225         } else if (dither_str == _("Triangular")) {
1226                 spec.dither_type = GDitherTri;
1227         } else {
1228                 spec.dither_type = GDitherShaped;
1229         } 
1230
1231         write_track_and_master_selection_to_spec();
1232 }
1233
1234
1235 void
1236 ExportDialog::write_track_and_master_selection_to_spec()
1237 {
1238         if(!track_and_master_selection_allowed){
1239                 return;
1240         }
1241
1242         uint32_t chan=0;
1243         Port *last_port = 0;
1244                 
1245         TreeModel::Children rows = master_selector.get_model()->children();
1246         TreeModel::Children::iterator ri;
1247         TreeModel::Row row;
1248         for (ri = rows.begin(); ri != rows.end(); ++ri) {
1249                 row = *ri;
1250                 Port* port = row[exp_cols.port];
1251                 
1252                 if (last_port != port) {
1253                         chan = 0;
1254                 }
1255                 
1256                 if (row[exp_cols.left]) {
1257                         spec.port_map[0].push_back (std::pair<Port*,uint32_t>(port, chan));
1258                 } 
1259                 
1260                 if (spec.channels == 2) {
1261                         if (row[exp_cols.right]) {
1262                                 spec.port_map[1].push_back (std::pair<Port*,uint32_t>(port, chan));
1263                         }
1264                 }
1265         }
1266         
1267         chan = 0;
1268         rows = track_selector.get_model()->children();
1269
1270         for (ri = rows.begin(); ri != rows.end(); ++ri) {
1271                 row = *ri;
1272                 
1273                 Port* port = row[exp_cols.port];
1274                 
1275                 if (last_port != port) {
1276                         chan = 0;
1277                 }
1278                 
1279                 if (row[exp_cols.left]) {
1280                         spec.port_map[0].push_back (std::pair<Port*,uint32_t>(port, chan));
1281                 } 
1282                 
1283                 if (spec.channels == 2) {
1284                         if (row[exp_cols.right]) {
1285                                 spec.port_map[1].push_back (std::pair<Port*,uint32_t>(port, chan));
1286                         }
1287                         
1288                 }
1289                 
1290                 last_port = port;
1291                 ++chan;
1292         }
1293 }
1294
1295
1296 gint
1297 ExportDialog::window_closed (GdkEventAny *ignored)
1298 {
1299         end_dialog ();
1300         return TRUE;
1301 }
1302
1303 void
1304 ExportDialog::browse ()
1305 {
1306         FileChooserDialog dialog("Export to file", browse_action());
1307         dialog.set_transient_for(*this);
1308         dialog.set_filename (file_entry.get_text());
1309
1310         dialog.add_button(Gtk::Stock::CANCEL, Gtk::RESPONSE_CANCEL);
1311         dialog.add_button(Gtk::Stock::OK, Gtk::RESPONSE_OK);
1312   
1313         int result = dialog.run();
1314
1315         if (result == Gtk::RESPONSE_OK) {
1316                 string filename = dialog.get_filename();
1317         
1318                 if (filename.length()) {
1319                         file_entry.set_text (filename);
1320                 }
1321         }
1322 }
1323
1324 void
1325 ExportDialog::track_selector_button_click ()
1326 {
1327         if (track_scroll.is_visible ()) {
1328                 track_scroll.hide ();
1329         } else {
1330                 track_scroll.show_all ();
1331         }
1332 }