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.
23 #include <gtkmm/messagedialog.h>
25 #include "export_session_dialog.h"
26 #include "export_region_dialog.h"
27 #include "export_range_markers_dialog.h"
29 #include "public_editor.h"
30 #include "selection.h"
31 #include "time_axis_view.h"
32 #include "audio_time_axis.h"
33 #include "audio_region_view.h"
35 #include <pbd/pthread_utils.h>
36 #include <ardour/types.h>
37 #include <ardour/export.h>
38 #include <ardour/audio_track.h>
39 #include <ardour/audiofilesource.h>
40 #include <ardour/audio_diskstream.h>
41 #include <ardour/audioregion.h>
42 #include <ardour/audioplaylist.h>
43 #include <ardour/source_factory.h>
44 #include <ardour/audiofilesource.h>
49 using namespace ARDOUR
;
54 Editor::export_session()
57 export_range (session
->current_start_frame(), session
->current_end_frame());
62 Editor::export_selection ()
65 if (selection
->time
.empty()) {
66 MessageDialog
message (*this, _("There is no selection to export.\n\nSelect a selection using the range mouse mode"));
71 export_range (selection
->time
.front().start
, selection
->time
.front().end
);
76 Editor::export_range (nframes64_t start
, nframes64_t end
)
79 if (export_dialog
== 0) {
80 export_dialog
= new ExportSessionDialog (*this);
83 export_dialog
->connect_to_session (session
);
84 export_dialog
->set_range (start
, end
);
85 export_dialog
->start_export();
90 Editor::export_region ()
92 if (clicked_regionview
== 0) {
96 ExportDialog
* dialog
= new ExportRegionDialog (*this, clicked_regionview
->region());
98 dialog
->connect_to_session (session
);
99 dialog
->set_range (clicked_regionview
->region()->first_frame(), clicked_regionview
->region()->last_frame());
100 dialog
->start_export();
104 Editor::export_range_markers ()
108 if (session
->locations()->num_range_markers() == 0) {
109 MessageDialog
message (*this, _("There are no ranges to export.\n\nCreate 1 or more ranges by dragging the mouse in the range bar"));
115 if (export_range_markers_dialog
== 0) {
116 export_range_markers_dialog
= new ExportRangeMarkersDialog(*this);
119 export_range_markers_dialog
->connect_to_session (session
);
120 export_range_markers_dialog
->start_export();
125 Editor::write_region_selection (RegionSelection
& regions
)
127 for (RegionSelection::iterator i
= regions
.begin(); i
!= regions
.end(); ++i
) {
128 AudioRegionView
* arv
= dynamic_cast<AudioRegionView
*>(*i
);
130 if (write_region ("", arv
->audio_region()) == false)
138 Editor::bounce_region_selection ()
140 for (RegionSelection::iterator i
= selection
->regions
.begin(); i
!= selection
->regions
.end(); ++i
) {
142 boost::shared_ptr
<Region
> region ((*i
)->region());
143 RouteTimeAxisView
* rtv
= dynamic_cast<RouteTimeAxisView
*>(&(*i
)->get_time_axis_view());
144 Track
* track
= dynamic_cast<Track
*>(rtv
->route().get());
152 boost::shared_ptr
<Region
> r
= track
->bounce_range (region
->position(), region
->position() + region
->length(), itt
);
153 cerr
<< "Result of bounce of "
154 << region
->name() << " len = " << region
->length()
156 << r
->name() << " len = " << r
->length()
162 Editor::write_region (string path
, boost::shared_ptr
<AudioRegion
> region
)
164 boost::shared_ptr
<AudioFileSource
> fs
;
165 const nframes64_t chunk_size
= 4096;
167 Sample buf
[chunk_size
];
168 gain_t gain_buffer
[chunk_size
];
172 vector
<boost::shared_ptr
<AudioFileSource
> > sources
;
175 nchans
= region
->n_channels();
177 /* don't do duplicate of the entire source if that's what is going on here */
179 if (region
->start() == 0 && region
->length() == region
->source()->length()) {
180 /* XXX should link(2) to create a new inode with "path" */
184 if (path
.length() == 0) {
186 for (uint32_t n
=0; n
< nchans
; ++n
) {
188 for (cnt
= 0; cnt
< 999999; ++cnt
) {
190 snprintf (s
, sizeof(s
), "%s/%s_%" PRIu32
".wav", session
->sound_dir().c_str(),
191 legalize_for_path(region
->name()).c_str(), cnt
);
194 snprintf (s
, sizeof(s
), "%s/%s_%" PRIu32
"-%" PRId32
".wav", session
->sound_dir().c_str(),
195 legalize_for_path(region
->name()).c_str(), cnt
, n
);
200 if (::access (path
.c_str(), F_OK
) != 0) {
206 error
<< "" << endmsg
;
213 fs
= boost::dynamic_pointer_cast
<AudioFileSource
> (SourceFactory::createWritable (*session
, path
, false, session
->frame_rate()));
216 catch (failed_constructor
& err
) {
220 sources
.push_back (fs
);
224 /* TODO: make filesources based on passed path */
228 to_read
= region
->length();
229 pos
= region
->position();
232 nframes64_t this_time
;
234 this_time
= min (to_read
, chunk_size
);
236 for (vector
<boost::shared_ptr
<AudioFileSource
> >::iterator src
=sources
.begin(); src
!= sources
.end(); ++src
) {
240 if (region
->read_at (buf
, buf
, gain_buffer
, pos
, this_time
) != this_time
) {
244 if (fs
->write (buf
, this_time
) != this_time
) {
245 error
<< "" << endmsg
;
250 to_read
-= this_time
;
257 now
= localtime (&tnow
);
259 for (vector
<boost::shared_ptr
<AudioFileSource
> >::iterator src
= sources
.begin(); src
!= sources
.end(); ++src
) {
260 (*src
)->update_header (0, *now
, tnow
);
261 (*src
)->mark_immutable ();
268 for (vector
<boost::shared_ptr
<AudioFileSource
> >::iterator i
= sources
.begin(); i
!= sources
.end(); ++i
) {
269 (*i
)->mark_for_remove ();
276 Editor::write_audio_selection (TimeSelection
& ts
)
280 if (selection
->tracks
.empty()) {
284 for (TrackSelection::iterator i
= selection
->tracks
.begin(); i
!= selection
->tracks
.end(); ++i
) {
286 AudioTimeAxisView
* atv
;
288 if ((atv
= dynamic_cast<AudioTimeAxisView
*>(*i
)) == 0) {
292 if (atv
->is_audio_track()) {
294 boost::shared_ptr
<AudioPlaylist
> playlist
= boost::dynamic_pointer_cast
<AudioPlaylist
>(atv
->get_diskstream()->playlist());
296 if (playlist
&& write_audio_range (*playlist
, atv
->get_diskstream()->n_channels(), ts
) == 0) {
307 Editor::write_audio_range (AudioPlaylist
& playlist
, uint32_t channels
, list
<AudioRange
>& range
)
309 boost::shared_ptr
<AudioFileSource
> fs
;
310 const nframes64_t chunk_size
= 4096;
312 Sample buf
[chunk_size
];
313 gain_t gain_buffer
[chunk_size
];
318 vector
<boost::shared_ptr
<AudioFileSource
> > sources
;
320 for (uint32_t n
=0; n
< channels
; ++n
) {
322 for (cnt
= 0; cnt
< 999999; ++cnt
) {
324 snprintf (s
, sizeof(s
), "%s/%s_%" PRIu32
".wav", session
->sound_dir().c_str(),
325 legalize_for_path(playlist
.name()).c_str(), cnt
);
328 snprintf (s
, sizeof(s
), "%s/%s_%" PRIu32
"-%" PRId32
".wav", session
->sound_dir().c_str(),
329 legalize_for_path(playlist
.name()).c_str(), cnt
, n
);
332 if (::access (s
, F_OK
) != 0) {
338 error
<< "" << endmsg
;
345 fs
= boost::dynamic_pointer_cast
<AudioFileSource
> (SourceFactory::createWritable (*session
, path
, false, session
->frame_rate()));
348 catch (failed_constructor
& err
) {
352 sources
.push_back (fs
);
357 for (list
<AudioRange
>::iterator i
= range
.begin(); i
!= range
.end();) {
359 nframes
= (*i
).length();
363 nframes64_t this_time
;
365 this_time
= min (nframes
, chunk_size
);
367 for (uint32_t n
=0; n
< channels
; ++n
) {
371 if (playlist
.read (buf
, buf
, gain_buffer
, pos
, this_time
, n
) != this_time
) {
375 if (fs
->write (buf
, this_time
) != this_time
) {
380 nframes
-= this_time
;
384 list
<AudioRange
>::iterator tmp
= i
;
387 if (tmp
!= range
.end()) {
389 /* fill gaps with silence */
391 nframes
= (*tmp
).start
- (*i
).end
;
395 nframes64_t this_time
= min (nframes
, chunk_size
);
396 memset (buf
, 0, sizeof (Sample
) * this_time
);
398 for (uint32_t n
=0; n
< channels
; ++n
) {
401 if (fs
->write (buf
, this_time
) != this_time
) {
406 nframes
-= this_time
;
416 now
= localtime (&tnow
);
418 for (vector
<boost::shared_ptr
<AudioFileSource
> >::iterator s
= sources
.begin(); s
!= sources
.end(); ++s
) {
419 (*s
)->update_header (0, *now
, tnow
);
420 (*s
)->mark_immutable ();
421 // do we need to ref it again?
427 /* unref created files */
429 for (vector
<boost::shared_ptr
<AudioFileSource
> >::iterator i
= sources
.begin(); i
!= sources
.end(); ++i
) {
430 (*i
)->mark_for_remove ();
437 Editor::write_selection ()
439 if (!selection
->time
.empty()) {
440 write_audio_selection (selection
->time
);
441 } else if (!selection
->regions
.empty()) {
442 write_region_selection (selection
->regions
);