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