2 Copyright (C) 1999-2005 Paul Davis
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.
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.
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.
26 #include <samplerate.h>
27 #include <pbd/convert.h>
28 #include <pbd/xml++.h>
30 #include <gtkmm2ext/utils.h>
31 #include <gtkmm2ext/window_title.h>
33 #include <ardour/export.h>
34 #include <ardour/sndfile_helpers.h>
35 #include <ardour/audio_track.h>
36 #include <ardour/audioregion.h>
37 #include <ardour/audioengine.h>
38 #include <ardour/gdither.h>
39 #include <ardour/utils.h>
40 #include <ardour/profile.h>
42 #include "export_dialog.h"
43 #include "ardour_ui.h"
44 #include "public_editor.h"
50 #define FRAME_NAME "BaseFrame"
53 using namespace ARDOUR
;
57 using namespace Gtkmm2ext
;
59 static const gchar
*sample_rates
[] = {
69 static const gchar
*src_quality
[] = {
78 static const gchar
*dither_types
[] = {
86 static const gchar
* channel_strings
[] = {
92 static const gchar
* cue_file_types
[] = {
99 ExportDialog::ExportDialog(PublicEditor
& e
)
100 : ArdourDialog ("export dialog"),
103 format_frame (_("Format")),
104 cue_file_label (_("CD Marker File Type"), 1.0, 0.5),
105 channel_count_label (_("Channels"), 1.0, 0.5),
106 header_format_label (_("File Type"), 1.0, 0.5),
107 bitdepth_format_label (_("Sample Format"), 1.0, 0.5),
108 endian_format_label (_("Sample Endianness"), 1.0, 0.5),
109 sample_rate_label (_("Sample Rate"), 1.0, 0.5),
110 src_quality_label (_("Conversion Quality"), 1.0, 0.5),
111 dither_type_label (_("Dither Type"), 1.0, 0.5),
112 cuefile_only_checkbox (_("Export CD Marker File Only")),
113 file_browse_button (_("Browse")),
114 track_selector_button (_("Specific tracks ..."))
121 track_and_master_selection_allowed
= true;
122 channel_count_selection_allowed
= true;
123 export_cd_markers_allowed
= true;
125 WindowTitle
title(Glib::get_application_name());
126 title
+= _("Export");
128 set_title (title
.get_string());
129 set_wmclass (X_("ardour_export"), "Ardour");
130 set_name ("ExportWindow");
131 add_events (Gdk::KEY_PRESS_MASK
|Gdk::KEY_RELEASE_MASK
);
133 spec
.running
= false;
135 file_entry
.set_name ("ExportFileNameEntry");
137 master_list
= ListStore::create (exp_cols
);
138 master_selector
.set_model (master_list
);
140 master_selector
.set_name ("ExportTrackSelector");
141 master_selector
.set_size_request (-1, 100);
142 master_selector
.append_column(_("Output"), exp_cols
.output
);
143 master_selector
.append_column_editable(_("Left"), exp_cols
.left
);
144 master_selector
.append_column_editable(_("Right"), exp_cols
.right
);
145 master_selector
.get_column(0)->set_min_width(100);
147 master_selector
.get_column(1)->set_min_width(40);
148 master_selector
.get_column(1)->set_sizing(Gtk::TREE_VIEW_COLUMN_AUTOSIZE
);
149 master_selector
.get_column(2)->set_min_width(40);
150 master_selector
.get_column(2)->set_sizing(Gtk::TREE_VIEW_COLUMN_AUTOSIZE
);
151 master_selector
.get_selection()->set_mode (Gtk::SELECTION_NONE
);
153 track_list
= ListStore::create (exp_cols
);
154 track_selector
.set_model (track_list
);
156 track_selector
.set_name ("ExportTrackSelector");
157 track_selector
.set_size_request (-1, 130);
158 track_selector
.append_column(_("Output"), exp_cols
.output
);
159 track_selector
.append_column_editable(_("Left"), exp_cols
.left
);
160 track_selector
.append_column_editable(_("Right"), exp_cols
.right
);
162 track_selector
.get_column(0)->set_min_width(100);
163 track_selector
.get_column(1)->set_min_width(40);
164 track_selector
.get_column(1)->set_sizing(Gtk::TREE_VIEW_COLUMN_AUTOSIZE
);
165 track_selector
.get_column(2)->set_min_width(40);
166 track_selector
.get_column(2)->set_sizing(Gtk::TREE_VIEW_COLUMN_AUTOSIZE
);
167 track_selector
.get_selection()->set_mode (Gtk::SELECTION_NONE
);
169 progress_bar
.set_name ("ExportProgress");
171 format_frame
.add (format_table
);
172 format_frame
.set_name (FRAME_NAME
);
174 track_scroll
.set_policy(Gtk::POLICY_AUTOMATIC
, Gtk::POLICY_AUTOMATIC
);
175 master_scroll
.set_policy(Gtk::POLICY_AUTOMATIC
, Gtk::POLICY_AUTOMATIC
);
177 get_vbox()->pack_start (file_frame
, false, false);
179 hpacker
.set_spacing (5);
180 hpacker
.set_border_width (5);
181 hpacker
.pack_start (format_frame
, false, false);
183 master_scroll
.add (master_selector
);
184 track_scroll
.add (track_selector
);
186 master_scroll
.set_size_request (220, 100);
187 track_scroll
.set_size_request (220, 100);
189 /* we may hide some of these later */
190 track_vpacker
.pack_start (master_scroll
);
191 track_vpacker
.pack_start (track_scroll
);
192 track_vpacker
.pack_start (track_selector_button
, Gtk::PACK_EXPAND_PADDING
);
194 hpacker
.pack_start (track_vpacker
);
196 get_vbox()->pack_start (hpacker
);
198 track_selector_button
.set_name ("EditorGTKButton");
199 track_selector_button
.signal_clicked().connect (mem_fun(*this, &ExportDialog::track_selector_button_click
));
201 get_vbox()->pack_start (progress_bar
, false, false);
203 Gtkmm2ext::set_size_request_to_display_given_text (file_entry
, X_("Kg/quite/a/reasonable/size/for/files/i/think"), 5, 8);
205 file_hbox
.set_spacing (5);
206 file_hbox
.set_border_width (5);
207 file_hbox
.pack_start (file_entry
, true, true);
208 file_hbox
.pack_start (file_browse_button
, false, false);
210 file_frame
.add (file_hbox
);
211 file_frame
.set_border_width (5);
212 file_frame
.set_name (FRAME_NAME
);
214 /* pop_strings needs to be created on the stack because set_popdown_strings()
218 vector
<string
> pop_strings
= I18N (sample_rates
);
219 Gtkmm2ext::set_popdown_strings (sample_rate_combo
, pop_strings
);
220 sample_rate_combo
.set_active_text (pop_strings
.front());
221 pop_strings
= I18N (src_quality
);
222 Gtkmm2ext::set_popdown_strings (src_quality_combo
, pop_strings
);
223 src_quality_combo
.set_active_text (pop_strings
.front());
224 pop_strings
= I18N (dither_types
);
225 Gtkmm2ext::set_popdown_strings (dither_type_combo
, pop_strings
);
226 dither_type_combo
.set_active_text (pop_strings
.front());
227 pop_strings
= I18N (channel_strings
);
228 Gtkmm2ext::set_popdown_strings (channel_count_combo
, pop_strings
);
229 channel_count_combo
.set_active_text (pop_strings
.front());
230 pop_strings
= I18N ((const char **) sndfile_header_formats_strings
);
231 Gtkmm2ext::set_popdown_strings (header_format_combo
, pop_strings
);
232 header_format_combo
.set_active_text (pop_strings
.front());
233 pop_strings
= I18N ((const char **) sndfile_bitdepth_formats_strings
);
234 Gtkmm2ext::set_popdown_strings (bitdepth_format_combo
, pop_strings
);
235 bitdepth_format_combo
.set_active_text (pop_strings
.front());
236 pop_strings
= I18N ((const char **) sndfile_endian_formats_strings
);
237 Gtkmm2ext::set_popdown_strings (endian_format_combo
, pop_strings
);
238 endian_format_combo
.set_active_text (pop_strings
.front());
239 pop_strings
= I18N (cue_file_types
);
240 Gtkmm2ext::set_popdown_strings (cue_file_combo
, pop_strings
);
241 cue_file_combo
.set_active_text (pop_strings
.front());
243 /* this will re-sensitized as soon as a non RIFF/WAV
244 header format is chosen.
247 endian_format_combo
.set_sensitive (false);
249 /* determine longest strings at runtime */
252 const char *longest
= X_("gl"); /* translators: one ascender, one descender */
255 for (n
= 0; n
< SNDFILE_HEADER_FORMATS
; ++n
) {
256 if ((len
= strlen (sndfile_header_formats_strings
[n
])) > maxlen
) {
258 longest
= sndfile_header_formats_strings
[n
];
262 for (n
= 0; n
< SNDFILE_BITDEPTH_FORMATS
; ++n
) {
263 if ((len
= strlen (sndfile_bitdepth_formats_strings
[n
])) > maxlen
) {
265 longest
= sndfile_bitdepth_formats_strings
[n
];
269 for (n
= 0; n
< SNDFILE_ENDIAN_FORMATS
; ++n
) {
270 if ((len
= strlen (sndfile_endian_formats_strings
[n
])) > maxlen
) {
272 longest
= sndfile_endian_formats_strings
[n
];
276 longest_str
= longest
;
278 /* force ascender + descender */
280 longest_str
[0] = 'g';
281 longest_str
[1] = 'l';
283 //Gtkmm2ext::set_size_request_to_display_given_text (header_format_combo, longest_str.c_str(), 5+FUDGE, 5);
285 // TRANSLATORS: "slereg" is "stereo" with ascender and descender substituted
286 //Gtkmm2ext::set_size_request_to_display_given_text (channel_count_combo, _("slereg"), 5+FUDGE, 5);
288 /* header_format_combo.set_focus_on_click (true);
289 bitdepth_format_combo.set_focus_on_click (true);
290 endian_format_combo.set_focus_on_click (true);
291 channel_count_combo.set_focus_on_click (true);
292 src_quality_combo.set_focus_on_click (true);
293 dither_type_combo.set_focus_on_click (true);
294 sample_rate_combo.set_focus_on_click (true);
295 cue_file_combo.set_focus_on_click (true);
297 dither_type_label
.set_name ("ExportFormatLabel");
298 sample_rate_label
.set_name ("ExportFormatLabel");
299 src_quality_label
.set_name ("ExportFormatLabel");
300 channel_count_label
.set_name ("ExportFormatLabel");
301 header_format_label
.set_name ("ExportFormatLabel");
302 bitdepth_format_label
.set_name ("ExportFormatLabel");
303 endian_format_label
.set_name ("ExportFormatLabel");
304 cue_file_label
.set_name ("ExportFormatLabel");
306 header_format_combo
.set_name ("ExportFormatDisplay");
307 bitdepth_format_combo
.set_name ("ExportFormatDisplay");
308 endian_format_combo
.set_name ("ExportFormatDisplay");
309 channel_count_combo
.set_name ("ExportFormatDisplay");
310 dither_type_combo
.set_name ("ExportFormatDisplay");
311 src_quality_combo
.set_name ("ExportFormatDisplay");
312 sample_rate_combo
.set_name ("ExportFormatDisplay");
313 cue_file_combo
.set_name ("ExportFormatDisplay");
315 cuefile_only_checkbox
.set_name ("ExportCheckbox");
317 format_table
.set_homogeneous (false);
318 format_table
.set_border_width (5);
319 format_table
.set_col_spacings (5);
320 format_table
.set_row_spacings (5);
324 format_table
.attach (channel_count_label
, 0, 1, row
, row
+1);
325 format_table
.attach (channel_count_combo
, 1, 2, row
, row
+1);
329 format_table
.attach (header_format_label
, 0, 1, row
, row
+1);
330 format_table
.attach (header_format_combo
, 1, 2, row
, row
+1);
334 format_table
.attach (bitdepth_format_label
, 0, 1, row
, row
+1);
335 format_table
.attach (bitdepth_format_combo
, 1, 2, row
, row
+1);
339 if (!Profile
->get_sae()) {
340 format_table
.attach (endian_format_label
, 0, 1, row
, row
+1);
341 format_table
.attach (endian_format_combo
, 1, 2, row
, row
+1);
345 format_table
.attach (sample_rate_label
, 0, 1, row
, row
+1);
346 format_table
.attach (sample_rate_combo
, 1, 2, row
, row
+1);
350 if (!Profile
->get_sae()) {
351 format_table
.attach (src_quality_label
, 0, 1, row
, row
+1);
352 format_table
.attach (src_quality_combo
, 1, 2, row
, row
+1);
356 format_table
.attach (dither_type_label
, 0, 1, row
, row
+1);
357 format_table
.attach (dither_type_combo
, 1, 2, row
, row
+1);
361 if (!Profile
->get_sae()) {
362 format_table
.attach (cue_file_label
, 0, 1, row
, row
+1);
363 format_table
.attach (cue_file_combo
, 1, 2, row
, row
+1);
366 format_table
.attach (cuefile_only_checkbox
, 0, 2, row
, row
+1);
369 file_entry
.set_name ("ExportFileDisplay");
371 signal_delete_event().connect (mem_fun(*this, &ExportDialog::window_closed
));
373 cancel_button
= add_button (Stock::CANCEL
, RESPONSE_CANCEL
);
374 cancel_button
->signal_clicked().connect (mem_fun(*this, &ExportDialog::end_dialog
));
375 ok_button
= add_button (_("Export"), RESPONSE_ACCEPT
);
376 ok_button
->signal_clicked().connect (mem_fun(*this, &ExportDialog::do_export
));
378 file_browse_button
.set_name ("EditorGTKButton");
379 file_browse_button
.signal_clicked().connect (mem_fun(*this, &ExportDialog::browse
));
381 channel_count_combo
.signal_changed().connect (mem_fun(*this, &ExportDialog::channels_chosen
));
382 bitdepth_format_combo
.signal_changed().connect (mem_fun(*this, &ExportDialog::bitdepth_chosen
));
383 header_format_combo
.signal_changed().connect (mem_fun(*this, &ExportDialog::header_chosen
));
384 sample_rate_combo
.signal_changed().connect (mem_fun(*this, &ExportDialog::sample_rate_chosen
));
385 cue_file_combo
.signal_changed().connect (mem_fun(*this, &ExportDialog::cue_file_type_chosen
));
388 ExportDialog::~ExportDialog()
393 ExportDialog::do_not_allow_track_and_master_selection()
395 track_and_master_selection_allowed
= false;
396 track_vpacker
.set_no_show_all();
400 ExportDialog::do_not_allow_channel_count_selection()
402 channel_count_selection_allowed
= false;
403 channel_count_combo
.set_no_show_all();
404 channel_count_label
.set_no_show_all();
408 ExportDialog::do_not_allow_export_cd_markers()
410 export_cd_markers_allowed
= false;
411 cue_file_label
.set_no_show_all();
412 cue_file_combo
.set_no_show_all();
413 cuefile_only_checkbox
.set_no_show_all();
417 ExportDialog::connect_to_session (Session
*s
)
420 session
->GoingAway
.connect (mem_fun(*this, &Window::hide_all
));
422 switch (session
->frame_rate()) {
424 sample_rate_combo
.set_active_text (_("22.05kHz"));
427 sample_rate_combo
.set_active_text (_("44.1kHz"));
430 sample_rate_combo
.set_active_text (_("48kHz"));
433 sample_rate_combo
.set_active_text (_("88.2kHz"));
436 sample_rate_combo
.set_active_text (_("96kHz"));
439 sample_rate_combo
.set_active_text (_("192kHz"));
442 sample_rate_combo
.set_active_text (_("44.1kHz"));
446 src_quality_combo
.set_sensitive (false);
452 ExportDialog::set_state()
454 XMLNode
* node
= session
->instant_xml(X_("ExportDialog"), session
->path());
459 if ((prop
= node
->property (X_("sample_rate"))) != 0) {
460 sample_rate_combo
.set_active_text(prop
->value());
462 if ((prop
= node
->property (X_("src_quality"))) != 0) {
463 src_quality_combo
.set_active_text(prop
->value());
465 if ((prop
= node
->property (X_("dither_type"))) != 0) {
466 dither_type_combo
.set_active_text(prop
->value());
468 if ((prop
= node
->property (X_("channel_count"))) != 0) {
469 channel_count_combo
.set_active_text(prop
->value());
471 if ((prop
= node
->property (X_("header_format"))) != 0) {
472 header_format_combo
.set_active_text(prop
->value());
474 if ((prop
= node
->property (X_("bitdepth_format"))) != 0) {
475 bitdepth_format_combo
.set_active_text(prop
->value());
477 if ((prop
= node
->property (X_("endian_format"))) != 0) {
478 endian_format_combo
.set_active_text(prop
->value());
480 if ((prop
= node
->property (X_("filename"))) != 0) {
481 file_entry
.set_text(prop
->value());
483 if ((prop
= node
->property (X_("cue_file_type"))) != 0) {
484 cue_file_combo
.set_active_text(prop
->value());
491 sample_rate_chosen();
493 if (session
->master_out()) {
494 track_scroll
.hide ();
496 master_scroll
.hide ();
497 track_selector_button
.hide ();
504 if (session
->master_out()) {
505 XMLNode
* master
= find_named_node(*node
, (X_("Master")));
510 /* default is to use all */
511 if (channel_count_combo
.get_active_text() == _("mono")) {
517 TreeModel::Children rows
= master_selector
.get_model()->children();
518 for (uint32_t r
= 0; r
< session
->master_out()->n_outputs(); ++r
) {
521 rows
[r
][exp_cols
.right
] = true;
523 rows
[r
][exp_cols
.left
] = true;
526 rows
[r
][exp_cols
.left
] = true;
531 /* XXX use XML state */
535 XMLNode
* tracks
= find_named_node(*node
, (X_("Tracks")));
540 XMLNodeList track_list
= tracks
->children(X_("Track"));
541 TreeModel::Children rows
= track_selector
.get_model()->children();
542 TreeModel::Children::iterator ri
= rows
.begin();
545 for (XMLNodeIterator it
= track_list
.begin(); it
!= track_list
.end(); ++it
, ++ri
) {
546 if (ri
== rows
.end()){
550 XMLNode
* track
= *it
;
553 if ((prop
= track
->property(X_("channel1"))) != 0) {
554 if (prop
->value() == X_("on")) {
555 row
[exp_cols
.left
] = true;
557 row
[exp_cols
.left
] = false;
561 if ((prop
= track
->property(X_("channel2"))) != 0) {
562 if (prop
->value() == X_("on")) {
563 row
[exp_cols
.right
] = true;
565 row
[exp_cols
.right
] = false;
572 ExportDialog::save_state()
578 XMLNode
* node
= new XMLNode(X_("ExportDialog"));
580 node
->add_property(X_("sample_rate"), sample_rate_combo
.get_active_text());
581 node
->add_property(X_("src_quality"), src_quality_combo
.get_active_text());
582 node
->add_property(X_("dither_type"), dither_type_combo
.get_active_text());
583 node
->add_property(X_("channel_count"), channel_count_combo
.get_active_text());
584 node
->add_property(X_("header_format"), header_format_combo
.get_active_text());
585 node
->add_property(X_("bitdepth_format"), bitdepth_format_combo
.get_active_text());
586 node
->add_property(X_("endian_format"), endian_format_combo
.get_active_text());
587 node
->add_property(X_("filename"), file_entry
.get_text());
588 node
->add_property(X_("cue_file_type"), cue_file_combo
.get_active_text());
590 XMLNode
* tracks
= new XMLNode(X_("Tracks"));
592 TreeModel::Children rows
= track_selector
.get_model()->children();
594 for (TreeModel::Children::iterator ri
= rows
.begin(); ri
!= rows
.end(); ++ri
) {
595 XMLNode
* track
= new XMLNode(X_("Track"));
598 track
->add_property(X_("channel1"), row
[exp_cols
.left
] ? X_("on") : X_("off"));
599 track
->add_property(X_("channel2"), row
[exp_cols
.right
] ? X_("on") : X_("off"));
601 tracks
->add_child_nocopy(*track
);
603 node
->add_child_nocopy(*tracks
);
605 session
->add_instant_xml(*node
, session
->path());
609 ExportDialog::set_range (nframes_t start
, nframes_t end
)
611 spec
.start_frame
= start
;
612 spec
.end_frame
= end
;
616 ExportDialog::progress_timeout ()
618 progress_bar
.set_fraction (spec
.progress
);
623 frames_to_cd_frames_string (char* buf
, nframes_t when
, nframes_t fr
)
626 long unsigned int remainder
;
627 int mins
, secs
, frames
;
629 mins
= when
/ (60 * fr
);
630 remainder
= when
- (mins
* 60 * fr
);
631 secs
= remainder
/ fr
;
632 remainder
-= secs
* fr
;
633 frames
= remainder
/ (fr
/ 75);
634 sprintf (buf
, " %02d:%02d:%02d", mins
, secs
, frames
);
638 struct LocationSortByStart
{
639 bool operator() (Location
*a
, Location
*b
) {
640 return a
->start() < b
->start();
645 ExportDialog::export_toc_file (Locations::LocationList
& locations
, const string
& path
)
647 if(!export_cd_markers_allowed
){
651 long unsigned int last_end_time
= spec
.start_frame
, last_start_time
= spec
.start_frame
;
654 /* Build the toc's file name from the specified audio file name. */
655 string basename
= Glib::path_get_basename(path
);
656 size_t ext_pos
= basename
.rfind('.');
657 if (ext_pos
!= string::npos
) {
658 basename
= basename
.substr(0, ext_pos
); /* strip file extension, if there is one */
660 string filepath
= Glib::build_filename(Glib::path_get_dirname(path
), basename
+ ".toc");
662 ofstream
out (filepath
.c_str());
664 error
<< string_compose(_("Editor: cannot open \"%1\" as export file for CD toc file"), filepath
) << endmsg
;
667 out
<< "CD_DA" << endl
;
668 out
<< "CD_TEXT {" << endl
<< " LANGUAGE_MAP {" << endl
<< " 0 : EN" << endl
<< " }" << endl
;
669 out
<< " LANGUAGE 0 {" << endl
<< " TITLE \"" << session
->name() << "\"" << endl
<< " }" << endl
<< "}" << endl
;
671 Locations::LocationList::iterator i
;
672 Locations::LocationList temp
;
674 for (i
= locations
.begin(); i
!= locations
.end(); ++i
) {
675 if ((*i
)->start() >= spec
.start_frame
&& (*i
)->end() <= spec
.end_frame
&& (*i
)->is_cd_marker() && !(*i
)->is_end()) {
680 if (temp
.size() > 0) {
681 LocationSortByStart cmp
;
683 Location
* curr_range
= 0;
684 Locations::LocationList::iterator nexti
;
686 for (i
= temp
.begin(); i
!= temp
.end(); ++i
) {
688 if ((*i
)->start() >= last_end_time
)
690 /* this is a track, defined by a cd range marker or a cd location marker outside of a cd range */
691 out
<< endl
<< "TRACK AUDIO" << endl
;
693 if ((*i
)->cd_info
.find("scms") != (*i
)->cd_info
.end()) {
696 out
<< "COPY" << endl
;
698 if ((*i
)->cd_info
.find("preemph") != (*i
)->cd_info
.end()) {
699 out
<< "PRE_EMPHASIS" << endl
;
701 out
<< "NO PRE_EMPHASIS" << endl
;
704 if ((*i
)->cd_info
.find("isrc") != (*i
)->cd_info
.end()) {
705 out
<< "ISRC \"" << (*i
)->cd_info
["isrc"] << "\"" << endl
;
708 out
<< "CD_TEXT {" << endl
<< " LANGUAGE 0 {" << endl
<< " TITLE \"" << (*i
)->name() << "\"" << endl
;
709 if ((*i
)->cd_info
.find("performer") != (*i
)->cd_info
.end()) {
710 out
<< " PERFORMER \"" << (*i
)->cd_info
["performer"] << "\"" << endl
;
712 if ((*i
)->cd_info
.find("string_composer") != (*i
)->cd_info
.end()) {
713 out
<< " COMPOSER \"" << (*i
)->cd_info
["string_composer"] << "\"" << endl
;
716 if ((*i
)->cd_info
.find("isrc") != (*i
)->cd_info
.end()) {
718 out
<< (*i
)->cd_info
["isrc"].substr(0,2) << "-";
719 out
<< (*i
)->cd_info
["isrc"].substr(2,3) << "-";
720 out
<< (*i
)->cd_info
["isrc"].substr(5,2) << "-";
721 out
<< (*i
)->cd_info
["isrc"].substr(7,5) << "\"" << endl
;
724 out
<< " }" << endl
<< "}" << endl
;
726 frames_to_cd_frames_string (buf
, last_end_time
- spec
.start_frame
, session
->frame_rate());
727 out
<< "FILE \"" << Glib::path_get_basename(path
) << "\"" << buf
;
729 if ((*i
)->is_mark()) {
730 // a mark track location needs to look ahead to the next marker's start to determine length
733 if (nexti
!= temp
.end()) {
734 frames_to_cd_frames_string (buf
, (*nexti
)->start() - last_end_time
, session
->frame_rate());
737 frames_to_cd_frames_string (buf
, (*i
)->start() - last_end_time
, session
->frame_rate());
738 out
<< "START" << buf
<< endl
;
740 last_start_time
= (*i
)->start();
741 last_end_time
= (*nexti
)->start();
744 // this was the last marker, use session end
745 frames_to_cd_frames_string (buf
, spec
.end_frame
- last_end_time
, session
->frame_rate());
748 frames_to_cd_frames_string (buf
, (*i
)->start() - last_end_time
, session
->frame_rate());
749 out
<< "START" << buf
<< endl
;
751 last_start_time
= (*i
)->start();
752 last_end_time
= spec
.end_frame
;
759 frames_to_cd_frames_string (buf
, (*i
)->end() - last_end_time
, session
->frame_rate());
762 frames_to_cd_frames_string (buf
, (*i
)->start() - last_end_time
, session
->frame_rate());
763 out
<< "START" << buf
<< endl
;
765 last_start_time
= (*i
)->start();
766 last_end_time
= (*i
)->end();
772 else if ((*i
)->is_mark())
774 /* this is an index within a track */
776 frames_to_cd_frames_string (buf
, (*i
)->start() - last_start_time
, session
->frame_rate());
777 out
<< "INDEX" << buf
<< endl
;
785 ExportDialog::export_cue_file (Locations::LocationList
& locations
, const string
& path
)
787 if(!export_cd_markers_allowed
){
792 long unsigned int last_track_end
= spec
.start_frame
;
793 int numtracks
= 0, tracknum
= 0, indexnum
= 0;
795 /* Build the cue sheet's file name from the specified audio file name. */
796 string basename
= Glib::path_get_basename(path
);
797 size_t ext_pos
= basename
.rfind('.');
798 if (ext_pos
!= string::npos
) {
799 basename
= basename
.substr(0, ext_pos
); /* strip file extension, if there is one */
801 string filepath
= Glib::build_filename(Glib::path_get_dirname(path
), basename
+ ".cue");
803 ofstream
out (filepath
.c_str());
805 error
<< string_compose(_("Editor: cannot open \"%1\" as export file for CD cue file"), filepath
) << endmsg
;
809 Locations::LocationList::iterator i
;
810 Locations::LocationList temp
;
812 for (i
= locations
.begin(); i
!= locations
.end(); ++i
) {
813 if ((*i
)->start() >= spec
.start_frame
&& (*i
)->end() <= spec
.end_frame
&& (*i
)->is_cd_marker() && !(*i
)->is_end()) {
815 if (!(*i
)->is_mark()) {
821 out
<< "REM Cue file generated by Ardour" << endl
;
822 out
<< "TITLE \"" << session
->name() << "\"" << endl
;
824 out
<< "FILE \"" << Glib::path_get_basename(path
) << "\" ";
826 /* The cue sheet syntax has originally five file types:
827 WAVE : 44.1 kHz, 16 Bit (little endian)
828 AIFF : 44.1 kHz, 16 Bit (big endian)
829 BINARY : 44.1 kHz, 16 Bit (little endian)
830 MOTOROLA : 44.1 kHz, 16 Bit (big endian)
833 We want to use cue sheets not only as CD images but also as general playlyist
834 format, thus for WAVE and AIFF we don't care if it's really 44.1 kHz/16 Bit, the
835 soundfile's header shows it anyway. But for the raw formats, i.e. BINARY
836 and MOTOROLA we do care, because no header would tell us about a different format.
838 For all other formats we just make up our own file type. MP3 is not supported
841 int file_format
= sndfile_header_format_from_string (header_format_combo
.get_active_text ());
842 if ((file_format
& SF_FORMAT_TYPEMASK
) == SF_FORMAT_WAV
) {
844 } else if ((file_format
& SF_FORMAT_TYPEMASK
) == SF_FORMAT_AIFF
) {
846 } else if ( ((file_format
& SF_FORMAT_TYPEMASK
) == SF_FORMAT_RAW
)
847 && (sndfile_bitdepth_format_from_string(bitdepth_format_combo
.get_active_text()) == SF_FORMAT_PCM_16
)
848 && (sample_rate_combo
.get_active_text() == _("44.1kHz")) ) {
849 /* raw audio, 16 Bit, 44.1 kHz */
850 if (sndfile_endian_format_from_string(endian_format_combo
.get_active_text()) == SF_ENDIAN_LITTLE
) {
856 out
<< (header_format_combo
.get_active_text());
860 if (false && numtracks
== 0) {
861 /* the user has supplied no track markers.
862 the entire export is treated as one track.
869 snprintf (buf
, sizeof(buf
), " TRACK %02d AUDIO", tracknum
);
871 out
<< " FLAGS DCP" << endl
;
873 /* use the session name*/
875 out
<< " TITLE \"" << session
->name() << "\"" << endl
;
877 /* No pregap is specified in this case, adding the default pregap
878 is left to the burning application. */
880 out
<< " INDEX 01 00:00:00" << endl
;
882 last_track_end
= spec
.end_frame
;
886 LocationSortByStart cmp
;
888 Location
* curr_range
= 0;
889 Locations::LocationList::iterator nexti
;
891 for ( i
= temp
.begin(); i
!= temp
.end(); ++i
) {
893 if ((*i
)->start() >= last_track_end
)
895 /* this is a track and it doesn't start inside another one*/
900 snprintf (buf
, sizeof(buf
), " TRACK %02d AUDIO", tracknum
);
904 if ((*i
)->cd_info
.find("scms") != (*i
)->cd_info
.end()) {
909 if ((*i
)->cd_info
.find("preemph") != (*i
)->cd_info
.end()) {
914 if ((*i
)->cd_info
.find("isrc") != (*i
)->cd_info
.end()) {
915 out
<< " ISRC " << (*i
)->cd_info
["isrc"] << endl
;
918 if ((*i
)->name() != "") {
919 out
<< " TITLE \"" << (*i
)->name() << "\"" << endl
;
922 if ((*i
)->cd_info
.find("performer") != (*i
)->cd_info
.end()) {
923 out
<< " PERFORMER \"" << (*i
)->cd_info
["performer"] << "\"" << endl
;
926 if ((*i
)->cd_info
.find("string_composer") != (*i
)->cd_info
.end()) {
927 out
<< " SONGWRITER \"" << (*i
)->cd_info
["string_composer"] << "\"" << endl
;
930 /* only print "Index 00" if not at the same position as "Index 01" */
931 if (last_track_end
!= (*i
)->start()) {
932 frames_to_cd_frames_string (buf
, last_track_end
- spec
.start_frame
, session
->frame_rate());
933 out
<< " INDEX 00" << buf
<< endl
;
938 if ((*i
)->is_mark()) {
939 // need to find the next start to define the end
942 if (nexti
!= temp
.end()) {
943 last_track_end
= (*nexti
)->start();
946 last_track_end
= spec
.end_frame
;
951 last_track_end
= (*i
)->end();
956 if ((tracknum
> 0) && ((*i
)->start() < last_track_end
)) {
957 /*this is an index and it lies within a track*/
958 snprintf (buf
, sizeof(buf
), " INDEX %02d", indexnum
);
960 frames_to_cd_frames_string (buf
,(*i
)->start() - spec
.start_frame
, session
->frame_rate());
970 ExportDialog::do_export_cd_markers (const string
& path
,const string
& cuefile_type
)
972 if (cuefile_type
== _("TOC")) {
973 session
->locations()->apply (*this, &ExportDialog::export_toc_file
, path
);
975 session
->locations()->apply (*this, &ExportDialog::export_cue_file
, path
);
980 ExportDialog::get_suffixed_filepath ()
982 string filepath
= file_entry
.get_text();
988 string::size_type dotpos
;
990 /* maybe add suffix */
992 int file_format
= sndfile_header_format_from_string (header_format_combo
.get_active_text ());
994 if ((file_format
& SF_FORMAT_TYPEMASK
) == SF_FORMAT_WAV
) {
995 if (filepath
.find (".wav") != filepath
.length() - 4) {
996 if ((dotpos
= filepath
.rfind ('.')) != string::npos
) {
997 filepath
= filepath
.substr (0, dotpos
);
1001 } else if ((file_format
& SF_FORMAT_TYPEMASK
) == SF_FORMAT_AIFF
) {
1002 if (filepath
.find (".aiff") != filepath
.length() - 5) {
1003 if ((dotpos
= filepath
.rfind ('.')) != string::npos
) {
1004 filepath
= filepath
.substr (0, dotpos
);
1006 filepath
+= ".aiff";
1008 } else if ((file_format
& SF_FORMAT_TYPEMASK
) == SF_FORMAT_CAF
) {
1009 if (filepath
.find (".caf") != filepath
.length() - 4) {
1010 if ((dotpos
= filepath
.rfind ('.')) != string::npos
) {
1011 filepath
= filepath
.substr (0, dotpos
);
1015 } else if ((file_format
& SF_FORMAT_TYPEMASK
) == SF_FORMAT_W64
) {
1016 if (filepath
.find (".w64") != filepath
.length() - 4) {
1017 if ((dotpos
= filepath
.rfind ('.')) != string::npos
) {
1018 filepath
= filepath
.substr (0, dotpos
);
1022 } else if ((file_format
& SF_FORMAT_TYPEMASK
) == SF_FORMAT_FLAC
) {
1023 if (filepath
.find (".flac") != filepath
.length() - 5) {
1024 if ((dotpos
= filepath
.rfind ('.')) != string::npos
) {
1025 filepath
= filepath
.substr (0, dotpos
);
1027 filepath
+= ".flac";
1029 } else if ((file_format
& SF_FORMAT_TYPEMASK
) == SF_FORMAT_OGG
) {
1030 if (filepath
.find (".ogg") != filepath
.length() - 4) {
1031 if ((dotpos
= filepath
.rfind ('.')) != string::npos
) {
1032 filepath
= filepath
.substr (0, dotpos
);
1036 } else if ((file_format
& SF_FORMAT_TYPEMASK
) == SF_FORMAT_RAW
) {
1037 if (filepath
.find (".raw") != filepath
.length() - 4) {
1038 if ((dotpos
= filepath
.rfind ('.')) != string::npos
) {
1039 filepath
= filepath
.substr (0, dotpos
);
1048 ExportDialog::do_export ()
1050 if (!ARDOUR_UI::instance()->the_engine().connected()) {
1051 MessageDialog
msg (*this,
1052 _("Not connected to audioengine"),
1056 msg
.set_secondary_text (_("Ardour cannot export audio when disconnected"));
1064 filepath
= get_suffixed_filepath ();
1066 if(!is_filepath_valid(filepath
)){
1070 if (!Profile
->get_sae() && export_cd_markers_allowed
) {
1071 if (cue_file_combo
.get_active_text () != _("None")) {
1072 do_export_cd_markers (filepath
, cue_file_combo
.get_active_text ());
1075 if (cuefile_only_checkbox
.get_active()) {
1081 ok_button
->set_sensitive(false);
1086 // read user input into spec
1089 progress_connection
= Glib::signal_timeout().connect (mem_fun(*this, &ExportDialog::progress_timeout
), 100);
1090 cancel_label
.set_text (_("Stop Export"));
1092 session
->pre_export ();
1094 export_audio_data();
1096 progress_connection
.disconnect ();
1099 /* if not stopped early and not SAE, ask for money, maybe */
1101 if (!spec
.stop
&& !Profile
->get_sae()) {
1103 NagScreen
* ns
= NagScreen::maybe_nag (_("export"));
1113 ExportDialog::end_dialog ()
1118 while (spec
.running
) {
1119 if (gtk_events_pending()) {
1120 gtk_main_iteration ();
1127 session
->finalize_audio_export ();
1132 ok_button
->set_sensitive(true);
1136 ExportDialog::start_export ()
1142 /* If the filename hasn't been set before, use the
1143 current session's export directory as a default
1144 location for the export.
1147 if (file_entry
.get_text().length() == 0) {
1148 Glib::ustring export_path
= session
->export_dir();
1151 export_path
= Glib::build_filename (export_path
, "export.wav");
1154 file_entry
.set_text (export_path
);
1157 progress_bar
.set_fraction (0);
1158 cancel_label
.set_text (_("Cancel"));
1162 if (session
->master_out()) {
1163 track_scroll
.hide ();
1165 master_scroll
.hide ();
1166 track_selector_button
.hide ();
1171 ExportDialog::header_chosen ()
1173 int fmt
= sndfile_header_format_from_string (header_format_combo
.get_active_text ());
1175 if ((fmt
& SF_FORMAT_TYPEMASK
) == SF_FORMAT_OGG
) {
1176 endian_format_combo
.set_sensitive (false);
1177 bitdepth_format_combo
.set_sensitive (false);
1179 if ((fmt
& SF_FORMAT_TYPEMASK
) == SF_FORMAT_WAV
) {
1180 endian_format_combo
.set_sensitive (false);
1182 endian_format_combo
.set_sensitive (true);
1184 bitdepth_format_combo
.set_sensitive (true);
1187 file_entry
.set_text (get_suffixed_filepath());
1191 ExportDialog::bitdepth_chosen ()
1193 int format
= sndfile_bitdepth_format_from_string (bitdepth_format_combo
.get_active_text ());
1195 case SF_FORMAT_PCM_24
:
1196 case SF_FORMAT_PCM_32
:
1197 case SF_FORMAT_FLOAT
:
1198 dither_type_combo
.set_sensitive (false);
1202 dither_type_combo
.set_sensitive (true);
1208 ExportDialog::cue_file_type_chosen ()
1210 if (cue_file_combo
.get_active_text () != "None") {
1211 cuefile_only_checkbox
.set_sensitive (true);
1213 cuefile_only_checkbox
.set_active (false);
1214 cuefile_only_checkbox
.set_sensitive (false);
1219 ExportDialog::sample_rate_chosen ()
1221 string sr_str
= sample_rate_combo
.get_active_text();
1224 if (sr_str
== N_("22.05kHz")) {
1226 } else if (sr_str
== _("44.1kHz")) {
1228 } else if (sr_str
== _("48kHz")) {
1230 } else if (sr_str
== _("88.2kHz")) {
1232 } else if (sr_str
== _("96kHz")) {
1234 } else if (sr_str
== _("192kHz")) {
1237 rate
= session
->frame_rate();
1240 if (rate
!= session
->frame_rate()) {
1241 src_quality_combo
.set_sensitive (true);
1243 src_quality_combo
.set_sensitive (false);
1248 ExportDialog::channels_chosen ()
1252 mono
= (channel_count_combo
.get_active_text() == _("mono"));
1255 track_selector
.get_column(2)->set_visible(false);
1256 track_selector
.get_column(1)->set_title(_("Export"));
1258 if (session
->master_out()) {
1259 master_selector
.get_column(2)->set_visible(false);
1260 master_selector
.get_column(1)->set_title(_("Export"));
1264 track_selector
.get_column(2)->set_visible(true);
1265 track_selector
.get_column(1)->set_title(_("Left"));
1267 if (session
->master_out()) {
1268 master_selector
.get_column(2)->set_visible(true);
1269 master_selector
.get_column(1)->set_title(_("Left"));
1277 ExportDialog::fill_lists ()
1279 track_list
->clear();
1280 master_list
->clear();
1282 boost::shared_ptr
<Session::RouteList
> routes
= session
->get_routes ();
1284 for (Session::RouteList::iterator ri
= routes
->begin(); ri
!= routes
->end(); ++ri
) {
1286 boost::shared_ptr
<Route
> route
= (*ri
);
1288 if (route
->hidden()) {
1292 for (uint32_t i
=0; i
< route
->n_outputs(); ++i
) {
1294 if (route
->n_outputs() == 1) {
1295 name
= route
->name();
1297 name
= string_compose("%1: out-%2", route
->name(), i
+1);
1300 if (route
== session
->master_out()) {
1301 TreeModel::iterator iter
= master_list
->append();
1302 TreeModel::Row row
= *iter
;
1303 row
[exp_cols
.output
] = name
;
1304 row
[exp_cols
.left
] = false;
1305 row
[exp_cols
.right
] = false;
1306 row
[exp_cols
.port
] = route
->output (i
);
1308 TreeModel::iterator iter
= track_list
->append();
1309 TreeModel::Row row
= *iter
;
1310 row
[exp_cols
.output
] = name
;
1311 row
[exp_cols
.left
] = false;
1312 row
[exp_cols
.right
] = false;
1313 row
[exp_cols
.port
] = route
->output (i
);
1321 ExportDialog::is_filepath_valid(string
&filepath
)
1323 // sanity check file name first
1325 struct stat statbuf
;
1327 if (filepath
.empty()) {
1328 string txt
= _("Please enter a valid filename.");
1329 MessageDialog
msg (*this, txt
, false, MESSAGE_ERROR
, BUTTONS_OK
, true);
1334 // check if file exists already and warn
1336 if (stat (filepath
.c_str(), &statbuf
) == 0) {
1337 if (S_ISDIR (statbuf
.st_mode
)) {
1338 string txt
= _("Please specify a complete filename for the audio file.");
1339 MessageDialog
msg (*this, txt
, false, MESSAGE_ERROR
, BUTTONS_OK
, true);
1344 string txt
= _("File already exists, do you want to overwrite it?");
1345 MessageDialog
msg (*this, txt
, false, MESSAGE_QUESTION
, BUTTONS_YES_NO
, true);
1346 if ((ResponseType
) msg
.run() == Gtk::RESPONSE_NO
) {
1352 // directory needs to exist and be writable
1354 string dirpath
= Glib::path_get_dirname (filepath
);
1355 if (::access (dirpath
.c_str(), W_OK
) != 0) {
1356 string txt
= _("Cannot write file in: ") + dirpath
;
1357 MessageDialog
msg (*this, txt
, false, MESSAGE_ERROR
, BUTTONS_OK
, true);
1366 ExportDialog::initSpec(string
&filepath
)
1368 spec
.path
= filepath
;
1370 spec
.running
= false;
1372 spec
.port_map
.clear();
1374 if (channel_count_combo
.get_active_text() == _("mono")) {
1382 spec
.format
|= sndfile_header_format_from_string (header_format_combo
.get_active_text ());
1384 /* if they picked Ogg, give them Ogg/Vorbis */
1386 if ((spec
.format
& SF_FORMAT_TYPEMASK
) == SF_FORMAT_OGG
) {
1387 spec
.format
|= SF_FORMAT_VORBIS
;
1390 if (!Profile
->get_sae()) {
1391 if (((spec
.format
& SF_FORMAT_TYPEMASK
) != SF_FORMAT_WAV
) && ((spec
.format
& SF_FORMAT_TYPEMASK
) != SF_FORMAT_OGG
)) {
1392 /* RIFF/WAV specifies endianess and O/V has no such concept */
1393 spec
.format
|= sndfile_endian_format_from_string (endian_format_combo
.get_active_text ());
1397 if ((spec
.format
& SF_FORMAT_TYPEMASK
) != SF_FORMAT_OGG
) {
1398 spec
.format
|= sndfile_bitdepth_format_from_string (bitdepth_format_combo
.get_active_text ());
1401 string sr_str
= sample_rate_combo
.get_active_text();
1402 if (sr_str
== N_("22.05kHz")) {
1403 spec
.sample_rate
= 22050;
1404 } else if (sr_str
== _("44.1kHz")) {
1405 spec
.sample_rate
= 44100;
1406 } else if (sr_str
== _("48kHz")) {
1407 spec
.sample_rate
= 48000;
1408 } else if (sr_str
== _("88.2kHz")) {
1409 spec
.sample_rate
= 88200;
1410 } else if (sr_str
== _("96kHz")) {
1411 spec
.sample_rate
= 96000;
1412 } else if (sr_str
== _("192kHz")) {
1413 spec
.sample_rate
= 192000;
1415 spec
.sample_rate
= session
->frame_rate();
1418 if (Profile
->get_sae()) {
1419 spec
.src_quality
= SRC_SINC_BEST_QUALITY
;
1421 string src_str
= src_quality_combo
.get_active_text();
1422 if (src_str
== _("fastest")) {
1423 spec
.src_quality
= SRC_ZERO_ORDER_HOLD
;
1424 } else if (src_str
== _("linear")) {
1425 spec
.src_quality
= SRC_LINEAR
;
1426 } else if (src_str
== _("better")) {
1427 spec
.src_quality
= SRC_SINC_FASTEST
;
1428 } else if (src_str
== _("intermediate")) {
1429 spec
.src_quality
= SRC_SINC_MEDIUM_QUALITY
;
1431 spec
.src_quality
= SRC_SINC_BEST_QUALITY
;
1435 string dither_str
= dither_type_combo
.get_active_text();
1436 if (dither_str
== _("None")) {
1437 spec
.dither_type
= GDitherNone
;
1438 } else if (dither_str
== _("Rectangular")) {
1439 spec
.dither_type
= GDitherRect
;
1440 } else if (dither_str
== _("Triangular")) {
1441 spec
.dither_type
= GDitherTri
;
1443 spec
.dither_type
= GDitherShaped
;
1446 write_track_and_master_selection_to_spec();
1451 ExportDialog::write_track_and_master_selection_to_spec()
1453 if(!track_and_master_selection_allowed
){
1458 Port
*last_port
= 0;
1460 TreeModel::Children rows
= master_selector
.get_model()->children();
1461 TreeModel::Children::iterator ri
;
1463 for (ri
= rows
.begin(); ri
!= rows
.end(); ++ri
) {
1465 Port
* port
= row
[exp_cols
.port
];
1467 if (last_port
!= port
) {
1471 if (row
[exp_cols
.left
]) {
1472 spec
.port_map
[0].push_back (std::pair
<Port
*,uint32_t>(port
, chan
));
1475 if (spec
.channels
== 2) {
1476 if (row
[exp_cols
.right
]) {
1477 spec
.port_map
[1].push_back (std::pair
<Port
*,uint32_t>(port
, chan
));
1483 rows
= track_selector
.get_model()->children();
1485 for (ri
= rows
.begin(); ri
!= rows
.end(); ++ri
) {
1488 Port
* port
= row
[exp_cols
.port
];
1490 if (last_port
!= port
) {
1494 if (row
[exp_cols
.left
]) {
1495 spec
.port_map
[0].push_back (std::pair
<Port
*,uint32_t>(port
, chan
));
1498 if (spec
.channels
== 2) {
1499 if (row
[exp_cols
.right
]) {
1500 spec
.port_map
[1].push_back (std::pair
<Port
*,uint32_t>(port
, chan
));
1512 ExportDialog::window_closed (GdkEventAny
*ignored
)
1519 ExportDialog::browse ()
1521 FileChooserDialog
dialog("Export to file", browse_action());
1522 dialog
.set_transient_for(*this);
1523 dialog
.set_filename (file_entry
.get_text());
1525 dialog
.add_button(Gtk::Stock::CANCEL
, Gtk::RESPONSE_CANCEL
);
1526 dialog
.add_button(Gtk::Stock::OK
, Gtk::RESPONSE_OK
);
1528 int result
= dialog
.run();
1530 if (result
== Gtk::RESPONSE_OK
) {
1531 string filename
= dialog
.get_filename();
1533 if (filename
.length()) {
1534 file_entry
.set_text (filename
);
1540 ExportDialog::track_selector_button_click ()
1542 if (track_scroll
.is_visible ()) {
1543 track_scroll
.hide ();
1545 track_scroll
.show_all ();