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