Fix thinkos in cubasish theme
[ardour.git] / gtk2_ardour / editor_export_audio.cc
1 /*
2  * Copyright (C) 2005-2017 Paul Davis <paul@linuxaudiosystems.com>
3  * Copyright (C) 2005 Taybin Rutkin <taybin@taybin.com>
4  * Copyright (C) 2006-2012 David Robillard <d@drobilla.net>
5  * Copyright (C) 2007-2010 Carl Hetherington <carl@carlh.net>
6  * Copyright (C) 2007-2017 Tim Mayberry <mojofunk@gmail.com>
7  * Copyright (C) 2008-2012 Sakari Bergen <sakari.bergen@beatwaves.net>
8  * Copyright (C) 2014-2017 Robin Gareus <robin@gareus.org>
9  * Copyright (C) 2015 AndrĂ© Nusser <andre.nusser@googlemail.com>
10  *
11  * This program is free software; you can redistribute it and/or modify
12  * it under the terms of the GNU General Public License as published by
13  * the Free Software Foundation; either version 2 of the License, or
14  * (at your option) any later version.
15  *
16  * This program is distributed in the hope that it will be useful,
17  * but WITHOUT ANY WARRANTY; without even the implied warranty of
18  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
19  * GNU General Public License for more details.
20  *
21  * You should have received a copy of the GNU General Public License along
22  * with this program; if not, write to the Free Software Foundation, Inc.,
23  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
24  */
25
26 /* Note: public Editor methods are documented in public_editor.h */
27
28 #include <inttypes.h>
29 #include <unistd.h>
30 #include <climits>
31
32 #include <gtkmm/messagedialog.h>
33
34 #include "pbd/gstdio_compat.h"
35
36 #include "pbd/pthread_utils.h"
37
38 #include "ardour/audio_track.h"
39 #include "ardour/audiofilesource.h"
40 #include "ardour/audioplaylist.h"
41 #include "ardour/audioregion.h"
42 #include "ardour/chan_count.h"
43 #include "ardour/midi_region.h"
44 #include "ardour/session.h"
45 #include "ardour/session_directory.h"
46 #include "ardour/source_factory.h"
47 #include "ardour/types.h"
48
49 #include "audio_region_view.h"
50 #include "audio_time_axis.h"
51 #include "editor.h"
52 #include "export_dialog.h"
53 #include "midi_export_dialog.h"
54 #include "midi_region_view.h"
55 #include "public_editor.h"
56 #include "selection.h"
57 #include "time_axis_view.h"
58 #include "utils.h"
59
60 #include "pbd/i18n.h"
61
62 using namespace std;
63 using namespace ARDOUR;
64 using namespace PBD;
65 using namespace Gtk;
66
67 void
68 Editor::export_audio ()
69 {
70         ExportDialog dialog (*this, _("Export"), ExportProfileManager::RegularExport);
71         dialog.set_session (_session);
72         dialog.run();
73 }
74
75 void
76 Editor::stem_export ()
77 {
78         StemExportDialog dialog (*this);
79         dialog.set_session (_session);
80         dialog.run();
81 }
82
83 void
84 Editor::export_selection ()
85 {
86         ExportSelectionDialog dialog (*this);
87         dialog.set_session (_session);
88         dialog.run();
89 }
90
91 void
92 Editor::export_range ()
93 {
94         ArdourMarker* marker;
95
96         if ((marker = reinterpret_cast<ArdourMarker *> (marker_menu_item->get_data ("marker"))) == 0) {
97                 fatal << _("programming error: marker canvas item has no marker object pointer!") << endmsg;
98                 abort(); /*NOTREACHED*/
99         }
100
101         Location* l;
102         bool is_start;
103
104         if (((l = find_location_from_marker (marker, is_start)) != 0) && (l->end() > l->start())) {
105                 ExportRangeDialog dialog (*this, l->id().to_s());
106                 dialog.set_session (_session);
107                 dialog.run();
108         }
109 }
110
111 bool
112 Editor::process_midi_export_dialog (MidiExportDialog& dialog, boost::shared_ptr<MidiRegion> midi_region)
113 {
114         string path = dialog.get_path ();
115
116         if (Glib::file_test (path, Glib::FILE_TEST_EXISTS)) {
117                 bool overwrite = ARDOUR_UI_UTILS::overwrite_file_dialog (dialog,
118                                                                          _("Confirm MIDI File Overwrite"),
119                                                                          _("A file with the same name already exists. Do you want to overwrite it?"));
120
121                 if (!overwrite) {
122                         return false;
123                 }
124
125                 /* force ::g_unlink because the backend code will
126                    go wrong if it tries to open an existing
127                    file for writing.
128                 */
129                 ::g_unlink (path.c_str());
130         }
131
132         return midi_region->do_export (path);
133 }
134
135 /** Export the first selected region */
136 void
137 Editor::export_region ()
138 {
139         if (selection->regions.empty()) {
140                 return;
141         }
142
143         boost::shared_ptr<Region> r = selection->regions.front()->region();
144         boost::shared_ptr<AudioRegion> audio_region = boost::dynamic_pointer_cast<AudioRegion>(r);
145         boost::shared_ptr<MidiRegion> midi_region = boost::dynamic_pointer_cast<MidiRegion>(r);
146
147         if (audio_region) {
148
149                 RouteTimeAxisView & rtv (dynamic_cast<RouteTimeAxisView &> (selection->regions.front()->get_time_axis_view()));
150                 AudioTrack & track (dynamic_cast<AudioTrack &> (*rtv.route()));
151
152                 ExportRegionDialog dialog (*this, *(audio_region.get()), track);
153                 dialog.set_session (_session);
154                 dialog.run ();
155
156         } else if (midi_region) {
157
158                 MidiExportDialog dialog (*this, midi_region);
159                 dialog.set_session (_session);
160
161                 bool finished = false;
162                 while (!finished) {
163                         switch (dialog.run ()) {
164                         case Gtk::RESPONSE_ACCEPT:
165                                 finished = process_midi_export_dialog (dialog, midi_region);
166                                 break;
167                         default:
168                                 return;
169                         }
170                 }
171         }
172 }
173
174 int
175 Editor::write_region_selection (RegionSelection& regions)
176 {
177         for (RegionSelection::iterator i = regions.begin(); i != regions.end(); ++i) {
178                 AudioRegionView* arv = dynamic_cast<AudioRegionView*>(*i);
179                 if (arv) {
180                         if (write_region ("", arv->audio_region()) == false)
181                                 return -1;
182                 }
183
184                 MidiRegionView* mrv = dynamic_cast<MidiRegionView*>(*i);
185                 if (mrv) {
186                         warning << "MIDI region export not implemented" << endmsg;
187                 }
188         }
189
190         return 0;
191 }
192
193 void
194 Editor::bounce_region_selection (bool with_processing)
195 {
196         /* no need to check for bounceable() because this operation never puts
197          * its results back in the playlist (only in the region list).
198          */
199
200         for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
201
202                 boost::shared_ptr<Region> region ((*i)->region());
203                 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*>(&(*i)->get_time_axis_view());
204                 boost::shared_ptr<Track> track = boost::dynamic_pointer_cast<Track> (rtv->route());
205
206                 InterThreadInfo itt;
207
208                 boost::shared_ptr<Region> r;
209
210                 if (with_processing) {
211                         r = track->bounce_range (region->position(), region->position() + region->length(), itt, track->main_outs(), false);
212                 } else {
213                         r = track->bounce_range (region->position(), region->position() + region->length(), itt, boost::shared_ptr<Processor>(), false);
214                 }
215         }
216 }
217
218 bool
219 Editor::write_region (string path, boost::shared_ptr<AudioRegion> region)
220 {
221         boost::shared_ptr<AudioFileSource> fs;
222         const samplepos_t chunk_size = 4096;
223         samplepos_t to_read;
224         Sample buf[chunk_size];
225         gain_t gain_buffer[chunk_size];
226         samplepos_t pos;
227         char s[PATH_MAX+1];
228         uint32_t cnt;
229         vector<boost::shared_ptr<AudioFileSource> > sources;
230         uint32_t nchans;
231
232         const string sound_directory = _session->session_directory().sound_path();
233
234         nchans = region->n_channels();
235
236         /* don't do duplicate of the entire source if that's what is going on here */
237
238         if (region->start() == 0 && region->length() == region->source_length(0)) {
239                 /* XXX should link(2) to create a new inode with "path" */
240                 return true;
241         }
242
243         if (path.length() == 0) {
244
245                 for (uint32_t n=0; n < nchans; ++n) {
246
247                         for (cnt = 0; cnt < 999999; ++cnt) {
248                                 if (nchans == 1) {
249                                         snprintf (s, sizeof(s), "%s/%s_%" PRIu32 ".wav", sound_directory.c_str(),
250                                                   legalize_for_path(region->name()).c_str(), cnt);
251                                 }
252                                 else {
253                                         snprintf (s, sizeof(s), "%s/%s_%" PRIu32 "-%" PRId32 ".wav", sound_directory.c_str(),
254                                                   legalize_for_path(region->name()).c_str(), cnt, n);
255                                 }
256
257                                 path = s;
258
259                                 if (!Glib::file_test (path, Glib::FILE_TEST_EXISTS)) {
260                                         break;
261                                 }
262                         }
263
264                         if (cnt == 999999) {
265                                 error << "" << endmsg;
266                                 goto error_out;
267                         }
268
269
270
271                         try {
272                                 fs = boost::dynamic_pointer_cast<AudioFileSource> (
273                                         SourceFactory::createWritable (DataType::AUDIO, *_session,
274                                                                        path, true,
275                                                                        false, _session->sample_rate()));
276                         }
277
278                         catch (failed_constructor& err) {
279                                 goto error_out;
280                         }
281
282                         sources.push_back (fs);
283                 }
284         }
285         else {
286                 /* TODO: make filesources based on passed path */
287
288         }
289
290         to_read = region->length();
291         pos = region->position();
292
293         while (to_read) {
294                 samplepos_t this_time;
295
296                 this_time = min (to_read, chunk_size);
297
298                 for (vector<boost::shared_ptr<AudioFileSource> >::iterator src=sources.begin(); src != sources.end(); ++src) {
299
300                         fs = (*src);
301
302                         if (region->read_at (buf, buf, gain_buffer, pos, this_time) != this_time) {
303                                 break;
304                         }
305
306                         if (fs->write (buf, this_time) != this_time) {
307                                 error << "" << endmsg;
308                                 goto error_out;
309                         }
310                 }
311
312                 to_read -= this_time;
313                 pos += this_time;
314         }
315
316         time_t tnow;
317         struct tm* now;
318         time (&tnow);
319         now = localtime (&tnow);
320
321         for (vector<boost::shared_ptr<AudioFileSource> >::iterator src = sources.begin(); src != sources.end(); ++src) {
322                 (*src)->update_header (0, *now, tnow);
323                 (*src)->mark_immutable ();
324         }
325
326         return true;
327
328 error_out:
329
330         for (vector<boost::shared_ptr<AudioFileSource> >::iterator i = sources.begin(); i != sources.end(); ++i) {
331                 (*i)->mark_for_remove ();
332         }
333
334         return 0;
335 }
336
337 int
338 Editor::write_audio_selection (TimeSelection& ts)
339 {
340         int ret = 0;
341
342         if (selection->tracks.empty()) {
343                 return 0;
344         }
345
346         for (TrackSelection::iterator i = selection->tracks.begin(); i != selection->tracks.end(); ++i) {
347
348                 AudioTimeAxisView* atv;
349
350                 if ((atv = dynamic_cast<AudioTimeAxisView*>(*i)) == 0) {
351                         continue;
352                 }
353
354                 if (atv->is_audio_track()) {
355
356                         boost::shared_ptr<AudioPlaylist> playlist = boost::dynamic_pointer_cast<AudioPlaylist>(atv->track()->playlist());
357
358                         if (playlist && write_audio_range (*playlist, atv->track()->n_channels(), ts) == 0) {
359                                 ret = -1;
360                                 break;
361                         }
362                 }
363         }
364
365         return ret;
366 }
367
368 bool
369 Editor::write_audio_range (AudioPlaylist& playlist, const ChanCount& count, list<AudioRange>& range)
370 {
371         boost::shared_ptr<AudioFileSource> fs;
372         const samplepos_t chunk_size = 4096;
373         samplepos_t nframes;
374         Sample buf[chunk_size];
375         gain_t gain_buffer[chunk_size];
376         samplepos_t pos;
377         char s[PATH_MAX+1];
378         uint32_t cnt;
379         string path;
380         vector<boost::shared_ptr<AudioFileSource> > sources;
381
382         const string sound_directory = _session->session_directory().sound_path();
383
384         uint32_t channels = count.n_audio();
385
386         for (uint32_t n=0; n < channels; ++n) {
387
388                 for (cnt = 0; cnt < 999999; ++cnt) {
389                         if (channels == 1) {
390                                 snprintf (s, sizeof(s), "%s/%s_%" PRIu32 ".wav", sound_directory.c_str(),
391                                           legalize_for_path(playlist.name()).c_str(), cnt);
392                         }
393                         else {
394                                 snprintf (s, sizeof(s), "%s/%s_%" PRIu32 "-%" PRId32 ".wav", sound_directory.c_str(),
395                                           legalize_for_path(playlist.name()).c_str(), cnt, n);
396                         }
397
398                         if (!Glib::file_test (s, Glib::FILE_TEST_EXISTS)) {
399                                 break;
400                         }
401                 }
402
403                 if (cnt == 999999) {
404                         error << "" << endmsg;
405                         goto error_out;
406                 }
407
408                 path = s;
409
410                 try {
411                         fs = boost::dynamic_pointer_cast<AudioFileSource> (
412                                 SourceFactory::createWritable (DataType::AUDIO, *_session,
413                                                                path, true,
414                                                                false, _session->sample_rate()));
415                 }
416
417                 catch (failed_constructor& err) {
418                         goto error_out;
419                 }
420
421                 sources.push_back (fs);
422
423         }
424
425
426         for (list<AudioRange>::iterator i = range.begin(); i != range.end();) {
427
428                 nframes = (*i).length();
429                 pos = (*i).start;
430
431                 while (nframes) {
432                         samplepos_t this_time;
433
434                         this_time = min (nframes, chunk_size);
435
436                         for (uint32_t n=0; n < channels; ++n) {
437
438                                 fs = sources[n];
439
440                                 if (playlist.read (buf, buf, gain_buffer, pos, this_time, n) != this_time) {
441                                         break;
442                                 }
443
444                                 if (fs->write (buf, this_time) != this_time) {
445                                         goto error_out;
446                                 }
447                         }
448
449                         nframes -= this_time;
450                         pos += this_time;
451                 }
452
453                 list<AudioRange>::iterator tmp = i;
454                 ++tmp;
455
456                 if (tmp != range.end()) {
457
458                         /* fill gaps with silence */
459
460                         nframes = (*tmp).start - (*i).end;
461
462                         while (nframes) {
463
464                                 samplepos_t this_time = min (nframes, chunk_size);
465                                 memset (buf, 0, sizeof (Sample) * this_time);
466
467                                 for (uint32_t n=0; n < channels; ++n) {
468
469                                         fs = sources[n];
470                                         if (fs->write (buf, this_time) != this_time) {
471                                                 goto error_out;
472                                         }
473                                 }
474
475                                 nframes -= this_time;
476                         }
477                 }
478
479                 i = tmp;
480         }
481
482         time_t tnow;
483         struct tm* now;
484         time (&tnow);
485         now = localtime (&tnow);
486
487         for (vector<boost::shared_ptr<AudioFileSource> >::iterator s = sources.begin(); s != sources.end(); ++s) {
488                 (*s)->update_header (0, *now, tnow);
489                 (*s)->mark_immutable ();
490                 // do we need to ref it again?
491         }
492
493         return true;
494
495 error_out:
496         /* unref created files */
497
498         for (vector<boost::shared_ptr<AudioFileSource> >::iterator i = sources.begin(); i != sources.end(); ++i) {
499                 (*i)->mark_for_remove ();
500         }
501
502         return false;
503 }
504
505 void
506 Editor::write_selection ()
507 {
508         if (!selection->time.empty()) {
509                 write_audio_selection (selection->time);
510         } else if (!selection->regions.empty()) {
511                 write_region_selection (selection->regions);
512         }
513 }