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 (bool with_processing
)
150 /* no need to check for bounceable() because this operation never puts
151 * its results back in the playlist (only in the region list).
154 for (RegionSelection::iterator i
= selection
->regions
.begin(); i
!= selection
->regions
.end(); ++i
) {
156 boost::shared_ptr
<Region
> region ((*i
)->region());
157 RouteTimeAxisView
* rtv
= dynamic_cast<RouteTimeAxisView
*>(&(*i
)->get_time_axis_view());
158 boost::shared_ptr
<Track
> track
= boost::dynamic_pointer_cast
<Track
> (rtv
->route());
162 boost::shared_ptr
<Region
> r
= track
->bounce_range (region
->position(), region
->position() + region
->length(), itt
, with_processing
);
167 Editor::write_region (string path
, boost::shared_ptr
<AudioRegion
> region
)
169 boost::shared_ptr
<AudioFileSource
> fs
;
170 const framepos_t chunk_size
= 4096;
172 Sample buf
[chunk_size
];
173 gain_t gain_buffer
[chunk_size
];
177 vector
<boost::shared_ptr
<AudioFileSource
> > sources
;
180 const string sound_directory
= _session
->session_directory().sound_path().to_string();
182 nchans
= region
->n_channels();
184 /* don't do duplicate of the entire source if that's what is going on here */
186 if (region
->start() == 0 && region
->length() == region
->source_length(0)) {
187 /* XXX should link(2) to create a new inode with "path" */
191 if (path
.length() == 0) {
193 for (uint32_t n
=0; n
< nchans
; ++n
) {
195 for (cnt
= 0; cnt
< 999999; ++cnt
) {
197 snprintf (s
, sizeof(s
), "%s/%s_%" PRIu32
".wav", sound_directory
.c_str(),
198 legalize_for_path(region
->name()).c_str(), cnt
);
201 snprintf (s
, sizeof(s
), "%s/%s_%" PRIu32
"-%" PRId32
".wav", sound_directory
.c_str(),
202 legalize_for_path(region
->name()).c_str(), cnt
, n
);
207 if (!Glib::file_test (path
, Glib::FILE_TEST_EXISTS
)) {
213 error
<< "" << endmsg
;
220 fs
= boost::dynamic_pointer_cast
<AudioFileSource
> (
221 SourceFactory::createWritable (DataType::AUDIO
, *_session
,
222 path
, string(), true,
223 false, _session
->frame_rate()));
226 catch (failed_constructor
& err
) {
230 sources
.push_back (fs
);
234 /* TODO: make filesources based on passed path */
238 to_read
= region
->length();
239 pos
= region
->position();
242 framepos_t this_time
;
244 this_time
= min (to_read
, chunk_size
);
246 for (vector
<boost::shared_ptr
<AudioFileSource
> >::iterator src
=sources
.begin(); src
!= sources
.end(); ++src
) {
250 if (region
->read_at (buf
, buf
, gain_buffer
, pos
, this_time
) != this_time
) {
254 if (fs
->write (buf
, this_time
) != this_time
) {
255 error
<< "" << endmsg
;
260 to_read
-= this_time
;
267 now
= localtime (&tnow
);
269 for (vector
<boost::shared_ptr
<AudioFileSource
> >::iterator src
= sources
.begin(); src
!= sources
.end(); ++src
) {
270 (*src
)->update_header (0, *now
, tnow
);
271 (*src
)->mark_immutable ();
278 for (vector
<boost::shared_ptr
<AudioFileSource
> >::iterator i
= sources
.begin(); i
!= sources
.end(); ++i
) {
279 (*i
)->mark_for_remove ();
286 Editor::write_audio_selection (TimeSelection
& ts
)
290 if (selection
->tracks
.empty()) {
294 for (TrackSelection::iterator i
= selection
->tracks
.begin(); i
!= selection
->tracks
.end(); ++i
) {
296 AudioTimeAxisView
* atv
;
298 if ((atv
= dynamic_cast<AudioTimeAxisView
*>(*i
)) == 0) {
302 if (atv
->is_audio_track()) {
304 boost::shared_ptr
<AudioPlaylist
> playlist
= boost::dynamic_pointer_cast
<AudioPlaylist
>(atv
->track()->playlist());
306 if (playlist
&& write_audio_range (*playlist
, atv
->track()->n_channels(), ts
) == 0) {
317 Editor::write_audio_range (AudioPlaylist
& playlist
, const ChanCount
& count
, list
<AudioRange
>& range
)
319 boost::shared_ptr
<AudioFileSource
> fs
;
320 const framepos_t chunk_size
= 4096;
322 Sample buf
[chunk_size
];
323 gain_t gain_buffer
[chunk_size
];
328 vector
<boost::shared_ptr
<AudioFileSource
> > sources
;
330 const string sound_directory
= _session
->session_directory().sound_path().to_string();
332 uint32_t channels
= count
.n_audio();
334 for (uint32_t n
=0; n
< channels
; ++n
) {
336 for (cnt
= 0; cnt
< 999999; ++cnt
) {
338 snprintf (s
, sizeof(s
), "%s/%s_%" PRIu32
".wav", sound_directory
.c_str(),
339 legalize_for_path(playlist
.name()).c_str(), cnt
);
342 snprintf (s
, sizeof(s
), "%s/%s_%" PRIu32
"-%" PRId32
".wav", sound_directory
.c_str(),
343 legalize_for_path(playlist
.name()).c_str(), cnt
, n
);
346 if (!Glib::file_test (s
, Glib::FILE_TEST_EXISTS
)) {
352 error
<< "" << endmsg
;
359 fs
= boost::dynamic_pointer_cast
<AudioFileSource
> (
360 SourceFactory::createWritable (DataType::AUDIO
, *_session
,
361 path
, string(), true,
362 false, _session
->frame_rate()));
365 catch (failed_constructor
& err
) {
369 sources
.push_back (fs
);
374 for (list
<AudioRange
>::iterator i
= range
.begin(); i
!= range
.end();) {
376 nframes
= (*i
).length();
380 framepos_t this_time
;
382 this_time
= min (nframes
, chunk_size
);
384 for (uint32_t n
=0; n
< channels
; ++n
) {
388 if (playlist
.read (buf
, buf
, gain_buffer
, pos
, this_time
, n
) != this_time
) {
392 if (fs
->write (buf
, this_time
) != this_time
) {
397 nframes
-= this_time
;
401 list
<AudioRange
>::iterator tmp
= i
;
404 if (tmp
!= range
.end()) {
406 /* fill gaps with silence */
408 nframes
= (*tmp
).start
- (*i
).end
;
412 framepos_t this_time
= min (nframes
, chunk_size
);
413 memset (buf
, 0, sizeof (Sample
) * this_time
);
415 for (uint32_t n
=0; n
< channels
; ++n
) {
418 if (fs
->write (buf
, this_time
) != this_time
) {
423 nframes
-= this_time
;
433 now
= localtime (&tnow
);
435 for (vector
<boost::shared_ptr
<AudioFileSource
> >::iterator s
= sources
.begin(); s
!= sources
.end(); ++s
) {
436 (*s
)->update_header (0, *now
, tnow
);
437 (*s
)->mark_immutable ();
438 // do we need to ref it again?
444 /* unref created files */
446 for (vector
<boost::shared_ptr
<AudioFileSource
> >::iterator i
= sources
.begin(); i
!= sources
.end(); ++i
) {
447 (*i
)->mark_for_remove ();
454 Editor::write_selection ()
456 if (!selection
->time
.empty()) {
457 write_audio_selection (selection
->time
);
458 } else if (!selection
->regions
.empty()) {
459 write_region_selection (selection
->regions
);