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