ardour
editor_export_audio.cc
Go to the documentation of this file.
1 /*
2  Copyright (C) 2001 Paul Davis
3 
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.
8 
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.
13 
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.
17 
18 */
19 
20 /* Note: public Editor methods are documented in public_editor.h */
21 
22 #include <inttypes.h>
23 #include <unistd.h>
24 #include <climits>
25 
26 #include <gtkmm/messagedialog.h>
27 
28 #include <glib/gstdio.h>
29 
30 #include "gtkmm2ext/choice.h"
31 
32 #include "pbd/pthread_utils.h"
33 
34 #include "ardour/audio_track.h"
35 #include "ardour/audiofilesource.h"
36 #include "ardour/audioplaylist.h"
37 #include "ardour/audioregion.h"
38 #include "ardour/chan_count.h"
39 #include "ardour/midi_region.h"
40 #include "ardour/session.h"
42 #include "ardour/source_factory.h"
43 #include "ardour/types.h"
44 
45 #include "audio_region_view.h"
46 #include "audio_time_axis.h"
47 #include "editor.h"
48 #include "export_dialog.h"
49 #include "midi_export_dialog.h"
50 #include "midi_region_view.h"
51 #include "public_editor.h"
52 #include "selection.h"
53 #include "time_axis_view.h"
54 
55 #include "i18n.h"
56 
57 using namespace std;
58 using namespace ARDOUR;
59 using namespace PBD;
60 using namespace Gtk;
61 
62 void
64 {
65  ExportDialog dialog (*this, _("Export"), ExportProfileManager::RegularExport);
66  dialog.set_session (_session);
67  dialog.run();
68 }
69 
70 void
72 {
73  StemExportDialog dialog (*this);
74  dialog.set_session (_session);
75  dialog.run();
76 }
77 
78 void
80 {
81  ExportSelectionDialog dialog (*this);
82  dialog.set_session (_session);
83  dialog.run();
84 }
85 
86 void
88 {
89  Marker* marker;
90 
91  if ((marker = reinterpret_cast<Marker *> (marker_menu_item->get_data ("marker"))) == 0) {
92  fatal << _("programming error: marker canvas item has no marker object pointer!") << endmsg;
93  abort(); /*NOTREACHED*/
94  }
95 
96  Location* l;
97  bool is_start;
98 
99  if (((l = find_location_from_marker (marker, is_start)) != 0) && (l->end() > l->start())) {
100  ExportRangeDialog dialog (*this, l->id().to_s());
101  dialog.set_session (_session);
102  dialog.run();
103  }
104 }
105 
107 void
109 {
110  if (selection->regions.empty()) {
111  return;
112  }
113 
114  boost::shared_ptr<Region> r = selection->regions.front()->region();
117 
118  if (audio_region) {
119 
120  RouteTimeAxisView & rtv (dynamic_cast<RouteTimeAxisView &> (selection->regions.front()->get_time_axis_view()));
121  AudioTrack & track (dynamic_cast<AudioTrack &> (*rtv.route()));
122 
123  ExportRegionDialog dialog (*this, *(audio_region.get()), track);
124  dialog.set_session (_session);
125  dialog.run ();
126 
127  } else if (midi_region) {
128 
129  MidiExportDialog dialog (*this, midi_region);
130  dialog.set_session (_session);
131  int ret = dialog.run ();
132  switch (ret) {
133  case Gtk::RESPONSE_ACCEPT:
134  break;
135  default:
136  return;
137  }
138 
139  dialog.hide ();
140 
141  string path = dialog.get_path ();
142 
143  if (Glib::file_test (path, Glib::FILE_TEST_EXISTS)) {
144 
145  MessageDialog checker (_("File Exists!"),
146  true,
147  Gtk::MESSAGE_WARNING,
148  Gtk::BUTTONS_NONE);
149 
150  checker.set_title (_("File Exists!"));
151 
152  checker.add_button (Stock::CANCEL, RESPONSE_CANCEL);
153  checker.add_button (_("Overwrite Existing File"), RESPONSE_ACCEPT);
154  checker.set_default_response (RESPONSE_CANCEL);
155 
156  checker.set_wmclass (X_("midi_export_file_exists"), PROGRAM_NAME);
157  checker.set_position (Gtk::WIN_POS_MOUSE);
158 
159  ret = checker.run ();
160 
161  switch (ret) {
162  case Gtk::RESPONSE_ACCEPT:
163  /* force ::g_unlink because the backend code will
164  go wrong if it tries to open an existing
165  file for writing.
166  */
167  ::g_unlink (path.c_str());
168  break;
169  default:
170  return;
171  }
172 
173  }
174 
175  (void) midi_region->clone (path);
176  }
177 }
178 
179 int
181 {
182  for (RegionSelection::iterator i = regions.begin(); i != regions.end(); ++i) {
183  AudioRegionView* arv = dynamic_cast<AudioRegionView*>(*i);
184  if (arv) {
185  if (write_region ("", arv->audio_region()) == false)
186  return -1;
187  }
188 
189  MidiRegionView* mrv = dynamic_cast<MidiRegionView*>(*i);
190  if (mrv) {
191  warning << "MIDI region export not implemented" << endmsg;
192  }
193  }
194 
195  return 0;
196 }
197 
198 void
199 Editor::bounce_region_selection (bool with_processing)
200 {
201  /* no need to check for bounceable() because this operation never puts
202  * its results back in the playlist (only in the region list).
203  */
204 
205  for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
206 
207  boost::shared_ptr<Region> region ((*i)->region());
208  RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*>(&(*i)->get_time_axis_view());
210 
211  InterThreadInfo itt;
212 
214 
215  if (with_processing) {
216  r = track->bounce_range (region->position(), region->position() + region->length(), itt, track->main_outs(), false);
217  } else {
218  r = track->bounce_range (region->position(), region->position() + region->length(), itt, boost::shared_ptr<Processor>(), false);
219  }
220  }
221 }
222 
223 bool
225 {
227  const framepos_t chunk_size = 4096;
228  framepos_t to_read;
229  Sample buf[chunk_size];
230  gain_t gain_buffer[chunk_size];
231  framepos_t pos;
232  char s[PATH_MAX+1];
233  uint32_t cnt;
234  vector<boost::shared_ptr<AudioFileSource> > sources;
235  uint32_t nchans;
236 
237  const string sound_directory = _session->session_directory().sound_path();
238 
239  nchans = region->n_channels();
240 
241  /* don't do duplicate of the entire source if that's what is going on here */
242 
243  if (region->start() == 0 && region->length() == region->source_length(0)) {
244  /* XXX should link(2) to create a new inode with "path" */
245  return true;
246  }
247 
248  if (path.length() == 0) {
249 
250  for (uint32_t n=0; n < nchans; ++n) {
251 
252  for (cnt = 0; cnt < 999999; ++cnt) {
253  if (nchans == 1) {
254  snprintf (s, sizeof(s), "%s/%s_%" PRIu32 ".wav", sound_directory.c_str(),
255  legalize_for_path(region->name()).c_str(), cnt);
256  }
257  else {
258  snprintf (s, sizeof(s), "%s/%s_%" PRIu32 "-%" PRId32 ".wav", sound_directory.c_str(),
259  legalize_for_path(region->name()).c_str(), cnt, n);
260  }
261 
262  path = s;
263 
264  if (!Glib::file_test (path, Glib::FILE_TEST_EXISTS)) {
265  break;
266  }
267  }
268 
269  if (cnt == 999999) {
270  error << "" << endmsg;
271  goto error_out;
272  }
273 
274 
275 
276  try {
278  SourceFactory::createWritable (DataType::AUDIO, *_session,
279  path, true,
280  false, _session->frame_rate()));
281  }
282 
283  catch (failed_constructor& err) {
284  goto error_out;
285  }
286 
287  sources.push_back (fs);
288  }
289  }
290  else {
291  /* TODO: make filesources based on passed path */
292 
293  }
294 
295  to_read = region->length();
296  pos = region->position();
297 
298  while (to_read) {
299  framepos_t this_time;
300 
301  this_time = min (to_read, chunk_size);
302 
303  for (vector<boost::shared_ptr<AudioFileSource> >::iterator src=sources.begin(); src != sources.end(); ++src) {
304 
305  fs = (*src);
306 
307  if (region->read_at (buf, buf, gain_buffer, pos, this_time) != this_time) {
308  break;
309  }
310 
311  if (fs->write (buf, this_time) != this_time) {
312  error << "" << endmsg;
313  goto error_out;
314  }
315  }
316 
317  to_read -= this_time;
318  pos += this_time;
319  }
320 
321  time_t tnow;
322  struct tm* now;
323  time (&tnow);
324  now = localtime (&tnow);
325 
326  for (vector<boost::shared_ptr<AudioFileSource> >::iterator src = sources.begin(); src != sources.end(); ++src) {
327  (*src)->update_header (0, *now, tnow);
328  (*src)->mark_immutable ();
329  }
330 
331  return true;
332 
333 error_out:
334 
335  for (vector<boost::shared_ptr<AudioFileSource> >::iterator i = sources.begin(); i != sources.end(); ++i) {
336  (*i)->mark_for_remove ();
337  }
338 
339  return 0;
340 }
341 
342 int
344 {
345  int ret = 0;
346 
347  if (selection->tracks.empty()) {
348  return 0;
349  }
350 
351  for (TrackSelection::iterator i = selection->tracks.begin(); i != selection->tracks.end(); ++i) {
352 
353  AudioTimeAxisView* atv;
354 
355  if ((atv = dynamic_cast<AudioTimeAxisView*>(*i)) == 0) {
356  continue;
357  }
358 
359  if (atv->is_audio_track()) {
360 
362 
363  if (playlist && write_audio_range (*playlist, atv->track()->n_channels(), ts) == 0) {
364  ret = -1;
365  break;
366  }
367  }
368  }
369 
370  return ret;
371 }
372 
373 bool
374 Editor::write_audio_range (AudioPlaylist& playlist, const ChanCount& count, list<AudioRange>& range)
375 {
377  const framepos_t chunk_size = 4096;
378  framepos_t nframes;
379  Sample buf[chunk_size];
380  gain_t gain_buffer[chunk_size];
381  framepos_t pos;
382  char s[PATH_MAX+1];
383  uint32_t cnt;
384  string path;
385  vector<boost::shared_ptr<AudioFileSource> > sources;
386 
387  const string sound_directory = _session->session_directory().sound_path();
388 
389  uint32_t channels = count.n_audio();
390 
391  for (uint32_t n=0; n < channels; ++n) {
392 
393  for (cnt = 0; cnt < 999999; ++cnt) {
394  if (channels == 1) {
395  snprintf (s, sizeof(s), "%s/%s_%" PRIu32 ".wav", sound_directory.c_str(),
396  legalize_for_path(playlist.name()).c_str(), cnt);
397  }
398  else {
399  snprintf (s, sizeof(s), "%s/%s_%" PRIu32 "-%" PRId32 ".wav", sound_directory.c_str(),
400  legalize_for_path(playlist.name()).c_str(), cnt, n);
401  }
402 
403  if (!Glib::file_test (s, Glib::FILE_TEST_EXISTS)) {
404  break;
405  }
406  }
407 
408  if (cnt == 999999) {
409  error << "" << endmsg;
410  goto error_out;
411  }
412 
413  path = s;
414 
415  try {
417  SourceFactory::createWritable (DataType::AUDIO, *_session,
418  path, true,
419  false, _session->frame_rate()));
420  }
421 
422  catch (failed_constructor& err) {
423  goto error_out;
424  }
425 
426  sources.push_back (fs);
427 
428  }
429 
430 
431  for (list<AudioRange>::iterator i = range.begin(); i != range.end();) {
432 
433  nframes = (*i).length();
434  pos = (*i).start;
435 
436  while (nframes) {
437  framepos_t this_time;
438 
439  this_time = min (nframes, chunk_size);
440 
441  for (uint32_t n=0; n < channels; ++n) {
442 
443  fs = sources[n];
444 
445  if (playlist.read (buf, buf, gain_buffer, pos, this_time, n) != this_time) {
446  break;
447  }
448 
449  if (fs->write (buf, this_time) != this_time) {
450  goto error_out;
451  }
452  }
453 
454  nframes -= this_time;
455  pos += this_time;
456  }
457 
458  list<AudioRange>::iterator tmp = i;
459  ++tmp;
460 
461  if (tmp != range.end()) {
462 
463  /* fill gaps with silence */
464 
465  nframes = (*tmp).start - (*i).end;
466 
467  while (nframes) {
468 
469  framepos_t this_time = min (nframes, chunk_size);
470  memset (buf, 0, sizeof (Sample) * this_time);
471 
472  for (uint32_t n=0; n < channels; ++n) {
473 
474  fs = sources[n];
475  if (fs->write (buf, this_time) != this_time) {
476  goto error_out;
477  }
478  }
479 
480  nframes -= this_time;
481  }
482  }
483 
484  i = tmp;
485  }
486 
487  time_t tnow;
488  struct tm* now;
489  time (&tnow);
490  now = localtime (&tnow);
491 
492  for (vector<boost::shared_ptr<AudioFileSource> >::iterator s = sources.begin(); s != sources.end(); ++s) {
493  (*s)->update_header (0, *now, tnow);
494  (*s)->mark_immutable ();
495  // do we need to ref it again?
496  }
497 
498  return true;
499 
500 error_out:
501  /* unref created files */
502 
503  for (vector<boost::shared_ptr<AudioFileSource> >::iterator i = sources.begin(); i != sources.end(); ++i) {
504  (*i)->mark_for_remove ();
505  }
506 
507  return false;
508 }
509 
510 void
512 {
513  if (!selection->time.empty()) {
514  write_audio_selection (selection->time);
515  } else if (!selection->regions.empty()) {
516  write_region_selection (selection->regions);
517  }
518 }
framecnt_t read(Sample *dst, Sample *mixdown, float *gain_buffer, framepos_t start, framecnt_t cnt, uint32_t chan_n=0)
LIBPBD_API Transmitter fatal
void export_selection()
int write_region_selection(RegionSelection &)
bool is_audio_track() const
Definition: route_ui.cc:1744
LIBARDOUR_API std::string legalize_for_path(const std::string &str)
Definition: ardour_ui.h:130
shared_ptr< T > dynamic_pointer_cast(shared_ptr< U > const &r)
Definition: shared_ptr.hpp:396
Definition: marker.h:41
uint32_t n_audio() const
Definition: chan_count.h:63
void set_session(ARDOUR::Session *s)
void set_session(ARDOUR::Session *)
framepos_t end() const
Definition: location.h:72
Definition: Beats.hpp:239
LIBPBD_API Transmitter error
LIBPBD_API Transmitter warning
float gain_t
Definition: types.h:58
std::ostream & endmsg(std::ostream &ostr)
Definition: transmitter.h:71
boost::shared_ptr< ARDOUR::AudioRegion > audio_region() const
void stem_export()
#define _(Text)
Definition: i18n.h:11
#define PATH_MAX
Definition: lv2_plugin.h:34
#define X_(Text)
Definition: i18n.h:13
float Sample
Definition: types.h:54
bool write_audio_range(ARDOUR::AudioPlaylist &, const ARDOUR::ChanCount &channels, std::list< ARDOUR::AudioRange > &)
std::string get_path() const
boost::shared_ptr< Delivery > main_outs() const
Definition: route.h:237
Definition: amp.h:29
const PBD::ID & id() const
Definition: stateful.h:68
std::string to_s() const
Definition: id.cc:78
bool write_region(std::string path, boost::shared_ptr< ARDOUR::AudioRegion >)
boost::shared_ptr< ARDOUR::Track > track() const
Definition: route_ui.cc:1738
boost::shared_ptr< Playlist > playlist()
Definition: track.cc:590
int64_t framepos_t
Definition: types.h:66
LIBARDOUR_API PBD::PropertyDescriptor< bool > regions
Definition: playlist.cc:51
T * get() const
Definition: shared_ptr.hpp:268
void bounce_region_selection(bool with_processing)
framecnt_t source_length(uint32_t n) const
Definition: region.cc:1511
void export_region()
framepos_t position() const
Definition: region.h:112
void export_audio()
uint32_t n_channels() const
Definition: region.h:259
ChanCount n_channels()
Definition: track.cc:777
std::string name() const
Definition: debug.h:30
virtual boost::shared_ptr< Region > bounce_range(framepos_t start, framepos_t end, InterThreadInfo &, boost::shared_ptr< Processor > endpoint, bool include_endpoint)=0
virtual framecnt_t write(Sample *src, framecnt_t cnt)
Definition: audiosource.cc:321
virtual framecnt_t read_at(Sample *buf, Sample *mixdown_buf, float *gain_buf, framepos_t position, framecnt_t cnt, uint32_t chan_n=0) const
Definition: audioregion.cc:473
framepos_t start() const
Definition: location.h:71
framecnt_t length() const
Definition: region.h:114
void write_selection()
framepos_t start() const
Definition: region.h:113
void export_range()
boost::shared_ptr< ARDOUR::Route > route() const
Definition: route_ui.h:76
int write_audio_selection(TimeSelection &)