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 string filepath
= path
+ ".toc";
652 ofstream
out (filepath
.c_str());
653 long unsigned int last_end_time
= spec
.start_frame
, last_start_time
= spec
.start_frame
;
657 error
<< string_compose(_("Editor: cannot open \"%1\" as export file for CD toc file"), filepath
) << endmsg
;
660 out
<< "CD_DA" << endl
;
661 out
<< "CD_TEXT {" << endl
<< " LANGUAGE_MAP {" << endl
<< " 0 : EN" << endl
<< " }" << endl
;
662 out
<< " LANGUAGE 0 {" << endl
<< " TITLE \"" << session
->name() << "\"" << endl
<< " }" << endl
<< "}" << endl
;
664 Locations::LocationList::iterator i
;
665 Locations::LocationList temp
;
667 for (i
= locations
.begin(); i
!= locations
.end(); ++i
) {
668 if ((*i
)->start() >= spec
.start_frame
&& (*i
)->end() <= spec
.end_frame
&& (*i
)->is_cd_marker() && !(*i
)->is_end()) {
673 if (temp
.size() > 0) {
674 LocationSortByStart cmp
;
676 Location
* curr_range
= 0;
677 Locations::LocationList::iterator nexti
;
679 for (i
= temp
.begin(); i
!= temp
.end(); ++i
) {
681 if ((*i
)->start() >= last_end_time
)
683 /* this is a track, defined by a cd range marker or a cd location marker outside of a cd range */
684 out
<< endl
<< "TRACK AUDIO" << endl
;
686 if ((*i
)->cd_info
.find("scms") != (*i
)->cd_info
.end()) {
689 out
<< "COPY" << endl
;
691 if ((*i
)->cd_info
.find("preemph") != (*i
)->cd_info
.end()) {
692 out
<< "PRE_EMPHASIS" << endl
;
694 out
<< "NO PRE_EMPHASIS" << endl
;
697 if ((*i
)->cd_info
.find("isrc") != (*i
)->cd_info
.end()) {
698 out
<< "ISRC \"" << (*i
)->cd_info
["isrc"] << "\"" << endl
;
701 out
<< "CD_TEXT {" << endl
<< " LANGUAGE 0 {" << endl
<< " TITLE \"" << (*i
)->name() << "\"" << endl
;
702 if ((*i
)->cd_info
.find("performer") != (*i
)->cd_info
.end()) {
703 out
<< " PERFORMER \"" << (*i
)->cd_info
["performer"] << "\"" << endl
;
705 if ((*i
)->cd_info
.find("string_composer") != (*i
)->cd_info
.end()) {
706 out
<< " COMPOSER \"" << (*i
)->cd_info
["string_composer"] << "\"" << endl
;
709 if ((*i
)->cd_info
.find("isrc") != (*i
)->cd_info
.end()) {
711 out
<< (*i
)->cd_info
["isrc"].substr(0,2) << "-";
712 out
<< (*i
)->cd_info
["isrc"].substr(2,3) << "-";
713 out
<< (*i
)->cd_info
["isrc"].substr(5,2) << "-";
714 out
<< (*i
)->cd_info
["isrc"].substr(7,5) << "\"" << endl
;
717 out
<< " }" << endl
<< "}" << endl
;
719 frames_to_cd_frames_string (buf
, last_end_time
- spec
.start_frame
, session
->frame_rate());
720 out
<< "FILE \"" << path
<< "\" " << buf
;
722 if ((*i
)->is_mark()) {
723 // a mark track location needs to look ahead to the next marker's start to determine length
726 if (nexti
!= temp
.end()) {
727 frames_to_cd_frames_string (buf
, (*nexti
)->start() - last_end_time
, session
->frame_rate());
730 frames_to_cd_frames_string (buf
, (*i
)->start() - last_end_time
, session
->frame_rate());
731 out
<< "START" << buf
<< endl
;
733 last_start_time
= (*i
)->start();
734 last_end_time
= (*nexti
)->start();
737 // this was the last marker, use session end
738 frames_to_cd_frames_string (buf
, spec
.end_frame
- last_end_time
, session
->frame_rate());
741 frames_to_cd_frames_string (buf
, (*i
)->start() - last_end_time
, session
->frame_rate());
742 out
<< "START" << buf
<< endl
;
744 last_start_time
= (*i
)->start();
745 last_end_time
= spec
.end_frame
;
752 frames_to_cd_frames_string (buf
, (*i
)->end() - last_end_time
, session
->frame_rate());
755 frames_to_cd_frames_string (buf
, (*i
)->start() - last_end_time
, session
->frame_rate());
756 out
<< "START" << buf
<< endl
;
758 last_start_time
= (*i
)->start();
759 last_end_time
= (*i
)->end();
765 else if ((*i
)->is_mark())
767 /* this is an index within a track */
769 frames_to_cd_frames_string (buf
, (*i
)->start() - last_start_time
, session
->frame_rate());
770 out
<< "INDEX" << buf
<< endl
;
778 ExportDialog::export_cue_file (Locations::LocationList
& locations
, const string
& path
)
780 if(!export_cd_markers_allowed
){
784 string filepath
= path
+ ".cue";
785 ofstream
out (filepath
.c_str());
787 long unsigned int last_track_end
= spec
.start_frame
;
788 int numtracks
= 0, tracknum
= 0, indexnum
= 0;
791 error
<< string_compose(_("Editor: cannot open \"%1\" as export file for CD cue file"), filepath
) << endmsg
;
795 Locations::LocationList::iterator i
;
796 Locations::LocationList temp
;
798 for (i
= locations
.begin(); i
!= locations
.end(); ++i
) {
799 if ((*i
)->start() >= spec
.start_frame
&& (*i
)->end() <= spec
.end_frame
&& (*i
)->is_cd_marker() && !(*i
)->is_end()) {
801 if (!(*i
)->is_mark()) {
807 out
<< "REM Cue file generated by Ardour" << endl
;
808 out
<< "TITLE \"" << session
->name() << "\"" << endl
;
810 if ((header_format_combo
.get_active_text() == N_("WAV"))) {
811 out
<< "FILE \"" << path
<< "\" WAVE" << endl
;
813 out
<< "FILE \"" << path
<< "\" " << (header_format_combo
.get_active_text()) << endl
;
816 if (false && numtracks
== 0) {
817 /* the user has supplied no track markers.
818 the entire export is treated as one track.
824 out
<< endl
<< "TRACK " << tracknum
<< " AUDIO" << endl
;
827 out
<< "DCP " << endl
;
829 /* use the session name*/
831 if (session
->name() != "") {
832 out
<< "TITLE \"" << session
->name() << "\"" << endl
;
835 /* no pregap in this case */
837 out
<< "INDEX 00 00:00:00" << endl
;
839 out
<< "INDEX 01 00:00:00" << endl
;
841 last_track_end
= spec
.end_frame
;
845 LocationSortByStart cmp
;
847 Location
* curr_range
= 0;
848 Locations::LocationList::iterator nexti
;
850 for ( i
= temp
.begin(); i
!= temp
.end(); ++i
) {
852 if ((*i
)->start() >= last_track_end
)
854 /* this is a track and it doesn't start inside another one*/
858 out
<< endl
<< "TRACK " << tracknum
<< " AUDIO" << endl
;
861 if ((*i
)->cd_info
.find("scms") != (*i
)->cd_info
.end()) {
867 if ((*i
)->cd_info
.find("preemph") != (*i
)->cd_info
.end()) {
872 if ((*i
)->cd_info
.find("isrc") != (*i
)->cd_info
.end()) {
873 out
<< "ISRC " << (*i
)->cd_info
["isrc"] << endl
;
876 if ((*i
)->name() != "") {
877 out
<< "TITLE \"" << (*i
)->name() << "\"" << endl
;
880 if ((*i
)->cd_info
.find("performer") != (*i
)->cd_info
.end()) {
881 out
<< "PERFORMER \"" << (*i
)->cd_info
["performer"] << "\"" << endl
;
884 if ((*i
)->cd_info
.find("string_composer") != (*i
)->cd_info
.end()) {
885 out
<< "SONGWRITER \"" << (*i
)->cd_info
["string_composer"] << "\"" << endl
;
887 snprintf (buf
, sizeof(buf
), "INDEX %02d", indexnum
);
889 frames_to_cd_frames_string (buf
, last_track_end
- spec
.start_frame
, session
->frame_rate());
893 if ((*i
)->is_mark()) {
894 // need to find the next start to define the end
897 if (nexti
!= temp
.end()) {
898 last_track_end
= (*nexti
)->start();
901 last_track_end
= spec
.end_frame
;
906 last_track_end
= (*i
)->end();
911 if ((tracknum
> 0) && ((*i
)->start() < last_track_end
)) {
912 /*this is an index and it lies within a track*/
913 snprintf (buf
, sizeof(buf
), "INDEX %02d", indexnum
);
915 frames_to_cd_frames_string (buf
,(*i
)->start() - spec
.start_frame
, session
->frame_rate());
925 ExportDialog::do_export_cd_markers (const string
& path
,const string
& cuefile_type
)
927 if (cuefile_type
== _("TOC")) {
928 session
->locations()->apply (*this, &ExportDialog::export_toc_file
, path
);
930 session
->locations()->apply (*this, &ExportDialog::export_cue_file
, path
);
936 ExportDialog::do_export ()
938 string filepath
= file_entry
.get_text();
940 if (!ARDOUR_UI::instance()->the_engine().connected()) {
941 MessageDialog
msg (*this,
942 _("Not connected to audioengine"),
946 msg
.set_secondary_text (_("Ardour cannot export audio when disconnected"));
954 /* maybe add suffix */
956 int file_format
= sndfile_header_format_from_string (header_format_combo
.get_active_text ());
958 if ((file_format
& SF_FORMAT_TYPEMASK
) == SF_FORMAT_WAV
) {
959 if (filepath
.find (".wav") != filepath
.length() - 4) {
962 } else if ((file_format
& SF_FORMAT_TYPEMASK
) == SF_FORMAT_AIFF
) {
963 if (filepath
.find (".aiff") != filepath
.length() - 5) {
966 } else if ((file_format
& SF_FORMAT_TYPEMASK
) == SF_FORMAT_W64
) {
967 if (filepath
.find (".w64") != filepath
.length() - 5) {
970 } else if ((file_format
& SF_FORMAT_TYPEMASK
) == SF_FORMAT_FLAC
) {
971 if (filepath
.find (".flac") != filepath
.length() - 5) {
974 } else if ((file_format
& SF_FORMAT_TYPEMASK
) == SF_FORMAT_CAF
) {
975 if (filepath
.find (".caf") != filepath
.length() - 4) {
983 if(!is_filepath_valid(filepath
)){
987 if (!Profile
->get_sae() && export_cd_markers_allowed
) {
988 if (cue_file_combo
.get_active_text () != _("None")) {
989 do_export_cd_markers (file_entry
.get_text(), cue_file_combo
.get_active_text ());
992 if (cuefile_only_checkbox
.get_active()) {
998 ok_button
->set_sensitive(false);
1003 // read user input into spec
1006 progress_connection
= Glib::signal_timeout().connect (mem_fun(*this, &ExportDialog::progress_timeout
), 100);
1007 cancel_label
.set_text (_("Stop Export"));
1009 session
->pre_export ();
1011 export_audio_data();
1013 progress_connection
.disconnect ();
1016 /* if not stopped early and not SAE, ask for money, maybe */
1018 if (!spec
.stop
&& !Profile
->get_sae()) {
1020 NagScreen
* ns
= NagScreen::maybe_nag (_("export"));
1030 ExportDialog::end_dialog ()
1035 while (spec
.running
) {
1036 if (gtk_events_pending()) {
1037 gtk_main_iteration ();
1044 session
->finalize_audio_export ();
1049 ok_button
->set_sensitive(true);
1053 ExportDialog::start_export ()
1059 /* If the filename hasn't been set before, use the
1060 current session's export directory as a default
1061 location for the export.
1064 if (file_entry
.get_text().length() == 0) {
1065 Glib::ustring export_path
= session
->export_dir();
1068 export_path
= Glib::build_filename (export_path
, "export.wav");
1071 file_entry
.set_text (export_path
);
1074 progress_bar
.set_fraction (0);
1075 cancel_label
.set_text (_("Cancel"));
1079 if (session
->master_out()) {
1080 track_scroll
.hide ();
1082 master_scroll
.hide ();
1083 track_selector_button
.hide ();
1088 ExportDialog::header_chosen ()
1090 if (sndfile_header_format_from_string (header_format_combo
.get_active_text ()) == SF_FORMAT_WAV
) {
1091 endian_format_combo
.set_sensitive (false);
1093 endian_format_combo
.set_sensitive (true);
1098 ExportDialog::bitdepth_chosen ()
1100 int format
= sndfile_bitdepth_format_from_string (bitdepth_format_combo
.get_active_text ());
1102 case SF_FORMAT_PCM_24
:
1103 case SF_FORMAT_PCM_32
:
1104 case SF_FORMAT_FLOAT
:
1105 dither_type_combo
.set_sensitive (false);
1109 dither_type_combo
.set_sensitive (true);
1115 ExportDialog::cue_file_type_chosen ()
1117 if (cue_file_combo
.get_active_text () != "None") {
1118 cuefile_only_checkbox
.set_sensitive (true);
1120 cuefile_only_checkbox
.set_active (false);
1121 cuefile_only_checkbox
.set_sensitive (false);
1126 ExportDialog::sample_rate_chosen ()
1128 string sr_str
= sample_rate_combo
.get_active_text();
1131 if (sr_str
== N_("22.05kHz")) {
1133 } else if (sr_str
== _("44.1kHz")) {
1135 } else if (sr_str
== _("48kHz")) {
1137 } else if (sr_str
== _("88.2kHz")) {
1139 } else if (sr_str
== _("96kHz")) {
1141 } else if (sr_str
== _("192kHz")) {
1144 rate
= session
->frame_rate();
1147 if (rate
!= session
->frame_rate()) {
1148 src_quality_combo
.set_sensitive (true);
1150 src_quality_combo
.set_sensitive (false);
1155 ExportDialog::channels_chosen ()
1159 mono
= (channel_count_combo
.get_active_text() == _("mono"));
1162 track_selector
.get_column(2)->set_visible(false);
1163 track_selector
.get_column(1)->set_title(_("Export"));
1165 if (session
->master_out()) {
1166 master_selector
.get_column(2)->set_visible(false);
1167 master_selector
.get_column(1)->set_title(_("Export"));
1171 track_selector
.get_column(2)->set_visible(true);
1172 track_selector
.get_column(1)->set_title(_("Left"));
1174 if (session
->master_out()) {
1175 master_selector
.get_column(2)->set_visible(true);
1176 master_selector
.get_column(1)->set_title(_("Left"));
1184 ExportDialog::fill_lists ()
1186 track_list
->clear();
1187 master_list
->clear();
1189 boost::shared_ptr
<Session::RouteList
> routes
= session
->get_routes ();
1191 for (Session::RouteList::iterator ri
= routes
->begin(); ri
!= routes
->end(); ++ri
) {
1193 boost::shared_ptr
<Route
> route
= (*ri
);
1195 if (route
->hidden()) {
1199 for (uint32_t i
=0; i
< route
->n_outputs(); ++i
) {
1201 if (route
->n_outputs() == 1) {
1202 name
= route
->name();
1204 name
= string_compose("%1: out-%2", route
->name(), i
+1);
1207 if (route
== session
->master_out()) {
1208 TreeModel::iterator iter
= master_list
->append();
1209 TreeModel::Row row
= *iter
;
1210 row
[exp_cols
.output
] = name
;
1211 row
[exp_cols
.left
] = false;
1212 row
[exp_cols
.right
] = false;
1213 row
[exp_cols
.port
] = route
->output (i
);
1215 TreeModel::iterator iter
= track_list
->append();
1216 TreeModel::Row row
= *iter
;
1217 row
[exp_cols
.output
] = name
;
1218 row
[exp_cols
.left
] = false;
1219 row
[exp_cols
.right
] = false;
1220 row
[exp_cols
.port
] = route
->output (i
);
1228 ExportDialog::is_filepath_valid(string
&filepath
)
1230 // sanity check file name first
1232 struct stat statbuf
;
1234 if (filepath
.empty()) {
1235 string txt
= _("Please enter a valid filename.");
1236 MessageDialog
msg (*this, txt
, false, MESSAGE_ERROR
, BUTTONS_OK
, true);
1241 // check if file exists already and warn
1243 if (stat (filepath
.c_str(), &statbuf
) == 0) {
1244 if (S_ISDIR (statbuf
.st_mode
)) {
1245 string txt
= _("Please specify a complete filename for the audio file.");
1246 MessageDialog
msg (*this, txt
, false, MESSAGE_ERROR
, BUTTONS_OK
, true);
1251 string txt
= _("File already exists, do you want to overwrite it?");
1252 MessageDialog
msg (*this, txt
, false, MESSAGE_QUESTION
, BUTTONS_YES_NO
, true);
1253 if ((ResponseType
) msg
.run() == Gtk::RESPONSE_NO
) {
1259 // directory needs to exist and be writable
1261 string dirpath
= Glib::path_get_dirname (filepath
);
1262 if (::access (dirpath
.c_str(), W_OK
) != 0) {
1263 string txt
= _("Cannot write file in: ") + dirpath
;
1264 MessageDialog
msg (*this, txt
, false, MESSAGE_ERROR
, BUTTONS_OK
, true);
1273 ExportDialog::initSpec(string
&filepath
)
1275 spec
.path
= filepath
;
1277 spec
.running
= false;
1279 spec
.port_map
.clear();
1281 if (channel_count_combo
.get_active_text() == _("mono")) {
1289 spec
.format
|= sndfile_header_format_from_string (header_format_combo
.get_active_text ());
1291 if (!Profile
->get_sae()) {
1292 if (((spec
.format
& SF_FORMAT_TYPEMASK
) != SF_FORMAT_WAV
)) {
1293 /* RIFF/WAV specifies endianess */
1294 spec
.format
|= sndfile_endian_format_from_string (endian_format_combo
.get_active_text ());
1298 spec
.format
|= sndfile_bitdepth_format_from_string (bitdepth_format_combo
.get_active_text ());
1300 string sr_str
= sample_rate_combo
.get_active_text();
1301 if (sr_str
== N_("22.05kHz")) {
1302 spec
.sample_rate
= 22050;
1303 } else if (sr_str
== _("44.1kHz")) {
1304 spec
.sample_rate
= 44100;
1305 } else if (sr_str
== _("48kHz")) {
1306 spec
.sample_rate
= 48000;
1307 } else if (sr_str
== _("88.2kHz")) {
1308 spec
.sample_rate
= 88200;
1309 } else if (sr_str
== _("96kHz")) {
1310 spec
.sample_rate
= 96000;
1311 } else if (sr_str
== _("192kHz")) {
1312 spec
.sample_rate
= 192000;
1314 spec
.sample_rate
= session
->frame_rate();
1317 if (Profile
->get_sae()) {
1318 spec
.src_quality
= SRC_SINC_BEST_QUALITY
;
1320 string src_str
= src_quality_combo
.get_active_text();
1321 if (src_str
== _("fastest")) {
1322 spec
.src_quality
= SRC_ZERO_ORDER_HOLD
;
1323 } else if (src_str
== _("linear")) {
1324 spec
.src_quality
= SRC_LINEAR
;
1325 } else if (src_str
== _("better")) {
1326 spec
.src_quality
= SRC_SINC_FASTEST
;
1327 } else if (src_str
== _("intermediate")) {
1328 spec
.src_quality
= SRC_SINC_MEDIUM_QUALITY
;
1330 spec
.src_quality
= SRC_SINC_BEST_QUALITY
;
1334 string dither_str
= dither_type_combo
.get_active_text();
1335 if (dither_str
== _("None")) {
1336 spec
.dither_type
= GDitherNone
;
1337 } else if (dither_str
== _("Rectangular")) {
1338 spec
.dither_type
= GDitherRect
;
1339 } else if (dither_str
== _("Triangular")) {
1340 spec
.dither_type
= GDitherTri
;
1342 spec
.dither_type
= GDitherShaped
;
1345 write_track_and_master_selection_to_spec();
1350 ExportDialog::write_track_and_master_selection_to_spec()
1352 if(!track_and_master_selection_allowed
){
1357 Port
*last_port
= 0;
1359 TreeModel::Children rows
= master_selector
.get_model()->children();
1360 TreeModel::Children::iterator ri
;
1362 for (ri
= rows
.begin(); ri
!= rows
.end(); ++ri
) {
1364 Port
* port
= row
[exp_cols
.port
];
1366 if (last_port
!= port
) {
1370 if (row
[exp_cols
.left
]) {
1371 spec
.port_map
[0].push_back (std::pair
<Port
*,uint32_t>(port
, chan
));
1374 if (spec
.channels
== 2) {
1375 if (row
[exp_cols
.right
]) {
1376 spec
.port_map
[1].push_back (std::pair
<Port
*,uint32_t>(port
, chan
));
1382 rows
= track_selector
.get_model()->children();
1384 for (ri
= rows
.begin(); ri
!= rows
.end(); ++ri
) {
1387 Port
* port
= row
[exp_cols
.port
];
1389 if (last_port
!= port
) {
1393 if (row
[exp_cols
.left
]) {
1394 spec
.port_map
[0].push_back (std::pair
<Port
*,uint32_t>(port
, chan
));
1397 if (spec
.channels
== 2) {
1398 if (row
[exp_cols
.right
]) {
1399 spec
.port_map
[1].push_back (std::pair
<Port
*,uint32_t>(port
, chan
));
1411 ExportDialog::window_closed (GdkEventAny
*ignored
)
1418 ExportDialog::browse ()
1420 FileChooserDialog
dialog("Export to file", browse_action());
1421 dialog
.set_transient_for(*this);
1422 dialog
.set_filename (file_entry
.get_text());
1424 dialog
.add_button(Gtk::Stock::CANCEL
, Gtk::RESPONSE_CANCEL
);
1425 dialog
.add_button(Gtk::Stock::OK
, Gtk::RESPONSE_OK
);
1427 int result
= dialog
.run();
1429 if (result
== Gtk::RESPONSE_OK
) {
1430 string filename
= dialog
.get_filename();
1432 if (filename
.length()) {
1433 file_entry
.set_text (filename
);
1439 ExportDialog::track_selector_button_click ()
1441 if (track_scroll
.is_visible ()) {
1442 track_scroll
.hide ();
1444 track_scroll
.show_all ();