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
);
981 ExportDialog::do_export ()
983 string filepath
= file_entry
.get_text();
985 if (!ARDOUR_UI::instance()->the_engine().connected()) {
986 MessageDialog
msg (*this,
987 _("Not connected to audioengine"),
991 msg
.set_secondary_text (_("Ardour cannot export audio when disconnected"));
999 /* maybe add suffix */
1001 int file_format
= sndfile_header_format_from_string (header_format_combo
.get_active_text ());
1003 if ((file_format
& SF_FORMAT_TYPEMASK
) == SF_FORMAT_WAV
) {
1004 if (filepath
.find (".wav") != filepath
.length() - 4) {
1007 } else if ((file_format
& SF_FORMAT_TYPEMASK
) == SF_FORMAT_AIFF
) {
1008 if (filepath
.find (".aiff") != filepath
.length() - 5) {
1009 filepath
+= ".aiff";
1011 } else if ((file_format
& SF_FORMAT_TYPEMASK
) == SF_FORMAT_W64
) {
1012 if (filepath
.find (".w64") != filepath
.length() - 4) {
1015 } else if ((file_format
& SF_FORMAT_TYPEMASK
) == SF_FORMAT_FLAC
) {
1016 if (filepath
.find (".flac") != filepath
.length() - 5) {
1017 filepath
+= ".flac";
1019 } else if ((file_format
& SF_FORMAT_TYPEMASK
) == SF_FORMAT_CAF
) {
1020 if (filepath
.find (".caf") != filepath
.length() - 4) {
1028 if(!is_filepath_valid(filepath
)){
1032 if (!Profile
->get_sae() && export_cd_markers_allowed
) {
1033 if (cue_file_combo
.get_active_text () != _("None")) {
1034 do_export_cd_markers (filepath
, cue_file_combo
.get_active_text ());
1037 if (cuefile_only_checkbox
.get_active()) {
1043 ok_button
->set_sensitive(false);
1048 // read user input into spec
1051 progress_connection
= Glib::signal_timeout().connect (mem_fun(*this, &ExportDialog::progress_timeout
), 100);
1052 cancel_label
.set_text (_("Stop Export"));
1054 session
->pre_export ();
1056 export_audio_data();
1058 progress_connection
.disconnect ();
1061 /* if not stopped early and not SAE, ask for money, maybe */
1063 if (!spec
.stop
&& !Profile
->get_sae()) {
1065 NagScreen
* ns
= NagScreen::maybe_nag (_("export"));
1075 ExportDialog::end_dialog ()
1080 while (spec
.running
) {
1081 if (gtk_events_pending()) {
1082 gtk_main_iteration ();
1089 session
->finalize_audio_export ();
1094 ok_button
->set_sensitive(true);
1098 ExportDialog::start_export ()
1104 /* If the filename hasn't been set before, use the
1105 current session's export directory as a default
1106 location for the export.
1109 if (file_entry
.get_text().length() == 0) {
1110 Glib::ustring export_path
= session
->export_dir();
1113 export_path
= Glib::build_filename (export_path
, "export.wav");
1116 file_entry
.set_text (export_path
);
1119 progress_bar
.set_fraction (0);
1120 cancel_label
.set_text (_("Cancel"));
1124 if (session
->master_out()) {
1125 track_scroll
.hide ();
1127 master_scroll
.hide ();
1128 track_selector_button
.hide ();
1133 ExportDialog::header_chosen ()
1135 if (sndfile_header_format_from_string (header_format_combo
.get_active_text ()) == SF_FORMAT_WAV
) {
1136 endian_format_combo
.set_sensitive (false);
1138 endian_format_combo
.set_sensitive (true);
1143 ExportDialog::bitdepth_chosen ()
1145 int format
= sndfile_bitdepth_format_from_string (bitdepth_format_combo
.get_active_text ());
1147 case SF_FORMAT_PCM_24
:
1148 case SF_FORMAT_PCM_32
:
1149 case SF_FORMAT_FLOAT
:
1150 dither_type_combo
.set_sensitive (false);
1154 dither_type_combo
.set_sensitive (true);
1160 ExportDialog::cue_file_type_chosen ()
1162 if (cue_file_combo
.get_active_text () != "None") {
1163 cuefile_only_checkbox
.set_sensitive (true);
1165 cuefile_only_checkbox
.set_active (false);
1166 cuefile_only_checkbox
.set_sensitive (false);
1171 ExportDialog::sample_rate_chosen ()
1173 string sr_str
= sample_rate_combo
.get_active_text();
1176 if (sr_str
== N_("22.05kHz")) {
1178 } else if (sr_str
== _("44.1kHz")) {
1180 } else if (sr_str
== _("48kHz")) {
1182 } else if (sr_str
== _("88.2kHz")) {
1184 } else if (sr_str
== _("96kHz")) {
1186 } else if (sr_str
== _("192kHz")) {
1189 rate
= session
->frame_rate();
1192 if (rate
!= session
->frame_rate()) {
1193 src_quality_combo
.set_sensitive (true);
1195 src_quality_combo
.set_sensitive (false);
1200 ExportDialog::channels_chosen ()
1204 mono
= (channel_count_combo
.get_active_text() == _("mono"));
1207 track_selector
.get_column(2)->set_visible(false);
1208 track_selector
.get_column(1)->set_title(_("Export"));
1210 if (session
->master_out()) {
1211 master_selector
.get_column(2)->set_visible(false);
1212 master_selector
.get_column(1)->set_title(_("Export"));
1216 track_selector
.get_column(2)->set_visible(true);
1217 track_selector
.get_column(1)->set_title(_("Left"));
1219 if (session
->master_out()) {
1220 master_selector
.get_column(2)->set_visible(true);
1221 master_selector
.get_column(1)->set_title(_("Left"));
1229 ExportDialog::fill_lists ()
1231 track_list
->clear();
1232 master_list
->clear();
1234 boost::shared_ptr
<Session::RouteList
> routes
= session
->get_routes ();
1236 for (Session::RouteList::iterator ri
= routes
->begin(); ri
!= routes
->end(); ++ri
) {
1238 boost::shared_ptr
<Route
> route
= (*ri
);
1240 if (route
->hidden()) {
1244 for (uint32_t i
=0; i
< route
->n_outputs(); ++i
) {
1246 if (route
->n_outputs() == 1) {
1247 name
= route
->name();
1249 name
= string_compose("%1: out-%2", route
->name(), i
+1);
1252 if (route
== session
->master_out()) {
1253 TreeModel::iterator iter
= master_list
->append();
1254 TreeModel::Row row
= *iter
;
1255 row
[exp_cols
.output
] = name
;
1256 row
[exp_cols
.left
] = false;
1257 row
[exp_cols
.right
] = false;
1258 row
[exp_cols
.port
] = route
->output (i
);
1260 TreeModel::iterator iter
= track_list
->append();
1261 TreeModel::Row row
= *iter
;
1262 row
[exp_cols
.output
] = name
;
1263 row
[exp_cols
.left
] = false;
1264 row
[exp_cols
.right
] = false;
1265 row
[exp_cols
.port
] = route
->output (i
);
1273 ExportDialog::is_filepath_valid(string
&filepath
)
1275 // sanity check file name first
1277 struct stat statbuf
;
1279 if (filepath
.empty()) {
1280 string txt
= _("Please enter a valid filename.");
1281 MessageDialog
msg (*this, txt
, false, MESSAGE_ERROR
, BUTTONS_OK
, true);
1286 // check if file exists already and warn
1288 if (stat (filepath
.c_str(), &statbuf
) == 0) {
1289 if (S_ISDIR (statbuf
.st_mode
)) {
1290 string txt
= _("Please specify a complete filename for the audio file.");
1291 MessageDialog
msg (*this, txt
, false, MESSAGE_ERROR
, BUTTONS_OK
, true);
1296 string txt
= _("File already exists, do you want to overwrite it?");
1297 MessageDialog
msg (*this, txt
, false, MESSAGE_QUESTION
, BUTTONS_YES_NO
, true);
1298 if ((ResponseType
) msg
.run() == Gtk::RESPONSE_NO
) {
1304 // directory needs to exist and be writable
1306 string dirpath
= Glib::path_get_dirname (filepath
);
1307 if (::access (dirpath
.c_str(), W_OK
) != 0) {
1308 string txt
= _("Cannot write file in: ") + dirpath
;
1309 MessageDialog
msg (*this, txt
, false, MESSAGE_ERROR
, BUTTONS_OK
, true);
1318 ExportDialog::initSpec(string
&filepath
)
1320 spec
.path
= filepath
;
1322 spec
.running
= false;
1324 spec
.port_map
.clear();
1326 if (channel_count_combo
.get_active_text() == _("mono")) {
1334 spec
.format
|= sndfile_header_format_from_string (header_format_combo
.get_active_text ());
1336 if (!Profile
->get_sae()) {
1337 if (((spec
.format
& SF_FORMAT_TYPEMASK
) != SF_FORMAT_WAV
)) {
1338 /* RIFF/WAV specifies endianess */
1339 spec
.format
|= sndfile_endian_format_from_string (endian_format_combo
.get_active_text ());
1343 spec
.format
|= sndfile_bitdepth_format_from_string (bitdepth_format_combo
.get_active_text ());
1345 string sr_str
= sample_rate_combo
.get_active_text();
1346 if (sr_str
== N_("22.05kHz")) {
1347 spec
.sample_rate
= 22050;
1348 } else if (sr_str
== _("44.1kHz")) {
1349 spec
.sample_rate
= 44100;
1350 } else if (sr_str
== _("48kHz")) {
1351 spec
.sample_rate
= 48000;
1352 } else if (sr_str
== _("88.2kHz")) {
1353 spec
.sample_rate
= 88200;
1354 } else if (sr_str
== _("96kHz")) {
1355 spec
.sample_rate
= 96000;
1356 } else if (sr_str
== _("192kHz")) {
1357 spec
.sample_rate
= 192000;
1359 spec
.sample_rate
= session
->frame_rate();
1362 if (Profile
->get_sae()) {
1363 spec
.src_quality
= SRC_SINC_BEST_QUALITY
;
1365 string src_str
= src_quality_combo
.get_active_text();
1366 if (src_str
== _("fastest")) {
1367 spec
.src_quality
= SRC_ZERO_ORDER_HOLD
;
1368 } else if (src_str
== _("linear")) {
1369 spec
.src_quality
= SRC_LINEAR
;
1370 } else if (src_str
== _("better")) {
1371 spec
.src_quality
= SRC_SINC_FASTEST
;
1372 } else if (src_str
== _("intermediate")) {
1373 spec
.src_quality
= SRC_SINC_MEDIUM_QUALITY
;
1375 spec
.src_quality
= SRC_SINC_BEST_QUALITY
;
1379 string dither_str
= dither_type_combo
.get_active_text();
1380 if (dither_str
== _("None")) {
1381 spec
.dither_type
= GDitherNone
;
1382 } else if (dither_str
== _("Rectangular")) {
1383 spec
.dither_type
= GDitherRect
;
1384 } else if (dither_str
== _("Triangular")) {
1385 spec
.dither_type
= GDitherTri
;
1387 spec
.dither_type
= GDitherShaped
;
1390 write_track_and_master_selection_to_spec();
1395 ExportDialog::write_track_and_master_selection_to_spec()
1397 if(!track_and_master_selection_allowed
){
1402 Port
*last_port
= 0;
1404 TreeModel::Children rows
= master_selector
.get_model()->children();
1405 TreeModel::Children::iterator ri
;
1407 for (ri
= rows
.begin(); ri
!= rows
.end(); ++ri
) {
1409 Port
* port
= row
[exp_cols
.port
];
1411 if (last_port
!= port
) {
1415 if (row
[exp_cols
.left
]) {
1416 spec
.port_map
[0].push_back (std::pair
<Port
*,uint32_t>(port
, chan
));
1419 if (spec
.channels
== 2) {
1420 if (row
[exp_cols
.right
]) {
1421 spec
.port_map
[1].push_back (std::pair
<Port
*,uint32_t>(port
, chan
));
1427 rows
= track_selector
.get_model()->children();
1429 for (ri
= rows
.begin(); ri
!= rows
.end(); ++ri
) {
1432 Port
* port
= row
[exp_cols
.port
];
1434 if (last_port
!= port
) {
1438 if (row
[exp_cols
.left
]) {
1439 spec
.port_map
[0].push_back (std::pair
<Port
*,uint32_t>(port
, chan
));
1442 if (spec
.channels
== 2) {
1443 if (row
[exp_cols
.right
]) {
1444 spec
.port_map
[1].push_back (std::pair
<Port
*,uint32_t>(port
, chan
));
1456 ExportDialog::window_closed (GdkEventAny
*ignored
)
1463 ExportDialog::browse ()
1465 FileChooserDialog
dialog("Export to file", browse_action());
1466 dialog
.set_transient_for(*this);
1467 dialog
.set_filename (file_entry
.get_text());
1469 dialog
.add_button(Gtk::Stock::CANCEL
, Gtk::RESPONSE_CANCEL
);
1470 dialog
.add_button(Gtk::Stock::OK
, Gtk::RESPONSE_OK
);
1472 int result
= dialog
.run();
1474 if (result
== Gtk::RESPONSE_OK
) {
1475 string filename
= dialog
.get_filename();
1477 if (filename
.length()) {
1478 file_entry
.set_text (filename
);
1484 ExportDialog::track_selector_button_click ()
1486 if (track_scroll
.is_visible ()) {
1487 track_scroll
.hide ();
1489 track_scroll
.show_all ();