2 Copyright (C) 2001 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.
20 /* Note: public Editor methods are documented in public_editor.h */
26 #include <gtkmm/messagedialog.h>
28 #include "gtkmm2ext/choice.h"
30 #include "export_dialog.h"
32 #include "public_editor.h"
33 #include "selection.h"
34 #include "time_axis_view.h"
35 #include "audio_time_axis.h"
36 #include "audio_region_view.h"
37 #include "midi_region_view.h"
39 #include "pbd/pthread_utils.h"
40 #include "ardour/types.h"
41 #include "ardour/audio_track.h"
42 #include "ardour/audiofilesource.h"
43 #include "ardour/audio_diskstream.h"
44 #include "ardour/audioregion.h"
45 #include "ardour/audioplaylist.h"
46 #include "ardour/chan_count.h"
47 #include "ardour/session_directory.h"
48 #include "ardour/source_factory.h"
49 #include "ardour/audiofilesource.h"
50 #include "ardour/session.h"
55 using namespace ARDOUR
;
60 Editor::export_audio ()
62 ExportDialog
dialog (*this, _("Export"), X_("ExportProfile"));
63 dialog
.set_session (_session
);
68 Editor::stem_export ()
70 StemExportDialog
dialog (*this);
71 dialog
.set_session (_session
);
76 Editor::export_selection ()
78 ExportSelectionDialog
dialog (*this);
79 dialog
.set_session (_session
);
84 Editor::export_range ()
88 if ((marker
= reinterpret_cast<Marker
*> (marker_menu_item
->get_data ("marker"))) == 0) {
89 fatal
<< _("programming error: marker canvas item has no marker object pointer!") << endmsg
;
96 if (((l
= find_location_from_marker (marker
, is_start
)) != 0) && (l
->end() > l
->start())) {
97 ExportRangeDialog
dialog (*this, l
->id().to_s());
98 dialog
.set_session (_session
);
103 /** Export the first selected region */
105 Editor::export_region ()
107 if (selection
->regions
.empty()) {
112 boost::shared_ptr
<Region
> r
= selection
->regions
.front()->region();
113 AudioRegion
& region (dynamic_cast<AudioRegion
&> (*r
));
115 RouteTimeAxisView
& rtv (dynamic_cast<RouteTimeAxisView
&> (selection
->regions
.front()->get_time_axis_view()));
116 AudioTrack
& track (dynamic_cast<AudioTrack
&> (*rtv
.route()));
118 ExportRegionDialog
dialog (*this, region
, track
);
119 dialog
.set_session (_session
);
122 } catch (std::bad_cast
& e
) {
123 error
<< "Exporting Region failed!" << endmsg
;
129 Editor::write_region_selection (RegionSelection
& regions
)
131 for (RegionSelection::iterator i
= regions
.begin(); i
!= regions
.end(); ++i
) {
132 AudioRegionView
* arv
= dynamic_cast<AudioRegionView
*>(*i
);
134 if (write_region ("", arv
->audio_region()) == false)
138 MidiRegionView
* mrv
= dynamic_cast<MidiRegionView
*>(*i
);
140 warning
<< "MIDI region export not implemented" << endmsg
;
148 Editor::bounce_region_selection ()
150 for (RegionSelection::iterator i
= selection
->regions
.begin(); i
!= selection
->regions
.end(); ++i
) {
152 RouteTimeAxisView
* rtv
= dynamic_cast<RouteTimeAxisView
*> (&(*i
)->get_time_axis_view());
153 boost::shared_ptr
<Track
> track
= boost::dynamic_pointer_cast
<Track
> (rtv
->route());
155 if (!track
->bounceable()) {
157 _("One or more of the selected regions' tracks cannot be bounced because it has more outputs than inputs. "
158 "You can fix this by increasing the number of inputs on that track.")
160 d
.set_title (_("Cannot bounce"));
166 for (RegionSelection::iterator i
= selection
->regions
.begin(); i
!= selection
->regions
.end(); ++i
) {
168 boost::shared_ptr
<Region
> region ((*i
)->region());
169 RouteTimeAxisView
* rtv
= dynamic_cast<RouteTimeAxisView
*>(&(*i
)->get_time_axis_view());
170 boost::shared_ptr
<Track
> track
= boost::dynamic_pointer_cast
<Track
> (rtv
->route());
174 boost::shared_ptr
<Region
> r
= track
->bounce_range (region
->position(), region
->position() + region
->length(), itt
);
175 cerr
<< "Result of bounce of "
176 << region
->name() << " len = " << region
->length()
178 << r
->name() << " len = " << r
->length()
184 Editor::write_region (string path
, boost::shared_ptr
<AudioRegion
> region
)
186 boost::shared_ptr
<AudioFileSource
> fs
;
187 const framepos_t chunk_size
= 4096;
189 Sample buf
[chunk_size
];
190 gain_t gain_buffer
[chunk_size
];
194 vector
<boost::shared_ptr
<AudioFileSource
> > sources
;
197 const string sound_directory
= _session
->session_directory().sound_path().to_string();
199 nchans
= region
->n_channels();
201 /* don't do duplicate of the entire source if that's what is going on here */
203 if (region
->start() == 0 && region
->length() == region
->source_length(0)) {
204 /* XXX should link(2) to create a new inode with "path" */
208 if (path
.length() == 0) {
210 for (uint32_t n
=0; n
< nchans
; ++n
) {
212 for (cnt
= 0; cnt
< 999999; ++cnt
) {
214 snprintf (s
, sizeof(s
), "%s/%s_%" PRIu32
".wav", sound_directory
.c_str(),
215 legalize_for_path(region
->name()).c_str(), cnt
);
218 snprintf (s
, sizeof(s
), "%s/%s_%" PRIu32
"-%" PRId32
".wav", sound_directory
.c_str(),
219 legalize_for_path(region
->name()).c_str(), cnt
, n
);
224 if (!Glib::file_test (path
, Glib::FILE_TEST_EXISTS
)) {
230 error
<< "" << endmsg
;
237 fs
= boost::dynamic_pointer_cast
<AudioFileSource
> (
238 SourceFactory::createWritable (DataType::AUDIO
, *_session
,
239 path
, string(), true,
240 false, _session
->frame_rate()));
243 catch (failed_constructor
& err
) {
247 sources
.push_back (fs
);
251 /* TODO: make filesources based on passed path */
255 to_read
= region
->length();
256 pos
= region
->position();
259 framepos_t this_time
;
261 this_time
= min (to_read
, chunk_size
);
263 for (vector
<boost::shared_ptr
<AudioFileSource
> >::iterator src
=sources
.begin(); src
!= sources
.end(); ++src
) {
267 if (region
->read_at (buf
, buf
, gain_buffer
, pos
, this_time
) != this_time
) {
271 if (fs
->write (buf
, this_time
) != this_time
) {
272 error
<< "" << endmsg
;
277 to_read
-= this_time
;
284 now
= localtime (&tnow
);
286 for (vector
<boost::shared_ptr
<AudioFileSource
> >::iterator src
= sources
.begin(); src
!= sources
.end(); ++src
) {
287 (*src
)->update_header (0, *now
, tnow
);
288 (*src
)->mark_immutable ();
295 for (vector
<boost::shared_ptr
<AudioFileSource
> >::iterator i
= sources
.begin(); i
!= sources
.end(); ++i
) {
296 (*i
)->mark_for_remove ();
303 Editor::write_audio_selection (TimeSelection
& ts
)
307 if (selection
->tracks
.empty()) {
311 for (TrackSelection::iterator i
= selection
->tracks
.begin(); i
!= selection
->tracks
.end(); ++i
) {
313 AudioTimeAxisView
* atv
;
315 if ((atv
= dynamic_cast<AudioTimeAxisView
*>(*i
)) == 0) {
319 if (atv
->is_audio_track()) {
321 boost::shared_ptr
<AudioPlaylist
> playlist
= boost::dynamic_pointer_cast
<AudioPlaylist
>(atv
->track()->playlist());
323 if (playlist
&& write_audio_range (*playlist
, atv
->track()->n_channels(), ts
) == 0) {
334 Editor::write_audio_range (AudioPlaylist
& playlist
, const ChanCount
& count
, list
<AudioRange
>& range
)
336 boost::shared_ptr
<AudioFileSource
> fs
;
337 const framepos_t chunk_size
= 4096;
339 Sample buf
[chunk_size
];
340 gain_t gain_buffer
[chunk_size
];
345 vector
<boost::shared_ptr
<AudioFileSource
> > sources
;
347 const string sound_directory
= _session
->session_directory().sound_path().to_string();
349 uint32_t channels
= count
.n_audio();
351 for (uint32_t n
=0; n
< channels
; ++n
) {
353 for (cnt
= 0; cnt
< 999999; ++cnt
) {
355 snprintf (s
, sizeof(s
), "%s/%s_%" PRIu32
".wav", sound_directory
.c_str(),
356 legalize_for_path(playlist
.name()).c_str(), cnt
);
359 snprintf (s
, sizeof(s
), "%s/%s_%" PRIu32
"-%" PRId32
".wav", sound_directory
.c_str(),
360 legalize_for_path(playlist
.name()).c_str(), cnt
, n
);
363 if (!Glib::file_test (s
, Glib::FILE_TEST_EXISTS
)) {
369 error
<< "" << endmsg
;
376 fs
= boost::dynamic_pointer_cast
<AudioFileSource
> (
377 SourceFactory::createWritable (DataType::AUDIO
, *_session
,
378 path
, string(), true,
379 false, _session
->frame_rate()));
382 catch (failed_constructor
& err
) {
386 sources
.push_back (fs
);
391 for (list
<AudioRange
>::iterator i
= range
.begin(); i
!= range
.end();) {
393 nframes
= (*i
).length();
397 framepos_t this_time
;
399 this_time
= min (nframes
, chunk_size
);
401 for (uint32_t n
=0; n
< channels
; ++n
) {
405 if (playlist
.read (buf
, buf
, gain_buffer
, pos
, this_time
, n
) != this_time
) {
409 if (fs
->write (buf
, this_time
) != this_time
) {
414 nframes
-= this_time
;
418 list
<AudioRange
>::iterator tmp
= i
;
421 if (tmp
!= range
.end()) {
423 /* fill gaps with silence */
425 nframes
= (*tmp
).start
- (*i
).end
;
429 framepos_t this_time
= min (nframes
, chunk_size
);
430 memset (buf
, 0, sizeof (Sample
) * this_time
);
432 for (uint32_t n
=0; n
< channels
; ++n
) {
435 if (fs
->write (buf
, this_time
) != this_time
) {
440 nframes
-= this_time
;
450 now
= localtime (&tnow
);
452 for (vector
<boost::shared_ptr
<AudioFileSource
> >::iterator s
= sources
.begin(); s
!= sources
.end(); ++s
) {
453 (*s
)->update_header (0, *now
, tnow
);
454 (*s
)->mark_immutable ();
455 // do we need to ref it again?
461 /* unref created files */
463 for (vector
<boost::shared_ptr
<AudioFileSource
> >::iterator i
= sources
.begin(); i
!= sources
.end(); ++i
) {
464 (*i
)->mark_for_remove ();
471 Editor::write_selection ()
473 if (!selection
->time
.empty()) {
474 write_audio_selection (selection
->time
);
475 } else if (!selection
->regions
.empty()) {
476 write_region_selection (selection
->regions
);