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 "ardour_ui.h"
50 #include "ardour_message.h"
51
52 #include "widgets/prompter.h"
53
54 #include "audio_region_view.h"
55 #include "audio_time_axis.h"
56 #include "editor.h"
57 #include "export_dialog.h"
58 #include "loudness_dialog.h"
59 #include "midi_export_dialog.h"
60 #include "midi_region_view.h"
61 #include "public_editor.h"
62 #include "selection.h"
63 #include "time_axis_view.h"
64 #include "utils.h"
65
66 #include "pbd/i18n.h"
67
68 using namespace std;
69 using namespace ARDOUR;
70 using namespace PBD;
71 using namespace Gtk;
72
73 void
export_audio()74 Editor::export_audio ()
75 {
76 ExportDialog dialog (*this, _("Export"), ExportProfileManager::RegularExport);
77 dialog.set_session (_session);
78 dialog.run();
79 }
80
81 void
stem_export()82 Editor::stem_export ()
83 {
84 StemExportDialog dialog (*this);
85 dialog.set_session (_session);
86 dialog.run();
87 }
88
89 void
export_selection()90 Editor::export_selection ()
91 {
92 ExportSelectionDialog dialog (*this);
93 dialog.set_session (_session);
94 dialog.run();
95 }
96
97 void
loudness_assistant_marker()98 Editor::loudness_assistant_marker ()
99 {
100 ArdourMarker* marker;
101
102 if ((marker = reinterpret_cast<ArdourMarker *> (marker_menu_item->get_data ("marker"))) == 0) {
103 fatal << _("programming error: marker canvas item has no marker object pointer!") << endmsg;
104 abort(); /*NOTREACHED*/
105 }
106
107 Location* l;
108 bool is_start;
109
110 if (((l = find_location_from_marker (marker, is_start)) != 0) && (l->end() > l->start())) {
111 measure_master_loudness (l->start(), l->end(), true);
112 }
113 }
114
115 void
loudness_assistant(bool range_selection)116 Editor::loudness_assistant (bool range_selection)
117 {
118 samplepos_t start, end;
119 TimeSelection const& ts (get_selection().time);
120 if (range_selection && !ts.empty ()) {
121 start = ts.start();
122 end = ts.end_sample();
123 } else {
124 start = _session->current_start_sample();
125 end = _session->current_end_sample();
126 }
127 measure_master_loudness (start, end, range_selection);
128 }
129
130 void
measure_master_loudness(samplepos_t start,samplepos_t end,bool is_range_selection)131 Editor::measure_master_loudness (samplepos_t start, samplepos_t end, bool is_range_selection)
132 {
133 if (!Config->get_use_master_volume ()) {
134 ArdourMessageDialog md (_("Master bus output gain control is disabled.\nVisit preferences to enable it?"), false,
135 MESSAGE_QUESTION, BUTTONS_YES_NO);
136 if (md.run () == RESPONSE_YES) {
137 ARDOUR_UI::instance()->show_mixer_prefs ();
138 }
139 return;
140 }
141
142 if (start >= end) {
143 if (is_range_selection) {
144 ArdourMessageDialog (_("Loudness Analysis requires a session-range or range-selection."), false, MESSAGE_ERROR).run ();
145 } else {
146 ArdourMessageDialog (_("Loudness Analysis requires a session-range."), false, MESSAGE_ERROR).run ();
147 }
148 return;
149 }
150
151 if (!_session->master_volume()) {
152 ArdourMessageDialog (_("Loudness Analysis is only available for sessions with a master-bus"), false, MESSAGE_ERROR).run ();
153 return;
154 }
155 assert (_session->master_out());
156 if (_session->master_out()->output()->n_ports().n_audio() != 2) {
157 ArdourMessageDialog (_("Loudness Analysis is only available for sessions with a stereo master-bus"), false, MESSAGE_ERROR).run ();
158 return;
159 }
160
161 ARDOUR::AudioRange ar (start, end, 0);
162
163 LoudnessDialog ld (_session, ar, is_range_selection);
164
165 if (own_window ()) {
166 ld.set_transient_for (*own_window ());
167 }
168
169 ld.run ();
170 }
171
172 void
export_range()173 Editor::export_range ()
174 {
175 ArdourMarker* marker;
176
177 if ((marker = reinterpret_cast<ArdourMarker *> (marker_menu_item->get_data ("marker"))) == 0) {
178 fatal << _("programming error: marker canvas item has no marker object pointer!") << endmsg;
179 abort(); /*NOTREACHED*/
180 }
181
182 Location* l;
183 bool is_start;
184
185 if (((l = find_location_from_marker (marker, is_start)) != 0) && (l->end() > l->start())) {
186 ExportRangeDialog dialog (*this, l->id().to_s());
187 dialog.set_session (_session);
188 dialog.run();
189 }
190 }
191
192 bool
process_midi_export_dialog(MidiExportDialog & dialog,boost::shared_ptr<MidiRegion> midi_region)193 Editor::process_midi_export_dialog (MidiExportDialog& dialog, boost::shared_ptr<MidiRegion> midi_region)
194 {
195 string path = dialog.get_path ();
196
197 if (Glib::file_test (path, Glib::FILE_TEST_EXISTS)) {
198 bool overwrite = ARDOUR_UI_UTILS::overwrite_file_dialog (dialog,
199 _("Confirm MIDI File Overwrite"),
200 _("A file with the same name already exists. Do you want to overwrite it?"));
201
202 if (!overwrite) {
203 return false;
204 }
205
206 /* force ::g_unlink because the backend code will
207 go wrong if it tries to open an existing
208 file for writing.
209 */
210 ::g_unlink (path.c_str());
211 }
212
213 return midi_region->do_export (path);
214 }
215
216 /** Export the first selected region */
217 void
export_region()218 Editor::export_region ()
219 {
220 if (selection->regions.empty()) {
221 return;
222 }
223
224 boost::shared_ptr<Region> r = selection->regions.front()->region();
225 boost::shared_ptr<AudioRegion> audio_region = boost::dynamic_pointer_cast<AudioRegion>(r);
226 boost::shared_ptr<MidiRegion> midi_region = boost::dynamic_pointer_cast<MidiRegion>(r);
227
228 if (audio_region) {
229
230 RouteTimeAxisView & rtv (dynamic_cast<RouteTimeAxisView &> (selection->regions.front()->get_time_axis_view()));
231 AudioTrack & track (dynamic_cast<AudioTrack &> (*rtv.route()));
232
233 ExportRegionDialog dialog (*this, *(audio_region.get()), track);
234 dialog.set_session (_session);
235 dialog.run ();
236
237 } else if (midi_region) {
238
239 MidiExportDialog dialog (*this, midi_region);
240 dialog.set_session (_session);
241
242 bool finished = false;
243 while (!finished) {
244 switch (dialog.run ()) {
245 case Gtk::RESPONSE_ACCEPT:
246 finished = process_midi_export_dialog (dialog, midi_region);
247 break;
248 default:
249 return;
250 }
251 }
252 }
253 }
254
255 int
write_region_selection(RegionSelection & regions)256 Editor::write_region_selection (RegionSelection& regions)
257 {
258 for (RegionSelection::iterator i = regions.begin(); i != regions.end(); ++i) {
259 AudioRegionView* arv = dynamic_cast<AudioRegionView*>(*i);
260 if (arv) {
261 if (write_region ("", arv->audio_region()) == false)
262 return -1;
263 }
264
265 MidiRegionView* mrv = dynamic_cast<MidiRegionView*>(*i);
266 if (mrv) {
267 warning << "MIDI region export not implemented" << endmsg;
268 }
269 }
270
271 return 0;
272 }
273
274 void
bounce_region_selection(bool with_processing)275 Editor::bounce_region_selection (bool with_processing)
276 {
277 /* no need to check for bounceable() because this operation never puts
278 * its results back in the playlist (only in the region list).
279 */
280
281 /*prompt the user for a new name*/
282 string bounce_name;
283 {
284 ArdourWidgets::Prompter dialog (true);
285
286 dialog.set_prompt (_("Name for Bounced Region:"));
287
288 dialog.set_name ("BounceNameWindow");
289 dialog.set_size_request (400, -1);
290 dialog.set_position (Gtk::WIN_POS_MOUSE);
291
292 dialog.add_button (_("Rename"), RESPONSE_ACCEPT);
293 dialog.set_initial_text (bounce_name);
294
295 Label label;
296 label.set_text (_("Bounced Region will appear in the Source list."));
297 dialog.get_vbox()->set_spacing (8);
298 dialog.get_vbox()->pack_start (label);
299 label.show();
300
301 dialog.show ();
302
303 switch (dialog.run ()) {
304 case RESPONSE_ACCEPT:
305 break;
306 default:
307 return;
308 }
309 dialog.get_result(bounce_name);
310 }
311
312 for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
313
314 boost::shared_ptr<Region> region ((*i)->region());
315 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*>(&(*i)->get_time_axis_view());
316 boost::shared_ptr<Track> track = boost::dynamic_pointer_cast<Track> (rtv->route());
317
318 InterThreadInfo itt;
319
320 boost::shared_ptr<Region> r;
321
322 if (with_processing) {
323 r = track->bounce_range (region->position(), region->position() + region->length(), itt, track->main_outs(), false, bounce_name);
324 } else {
325 r = track->bounce_range (region->position(), region->position() + region->length(), itt, boost::shared_ptr<Processor>(), false, bounce_name);
326 }
327 }
328 }
329
330 bool
write_region(string path,boost::shared_ptr<AudioRegion> region)331 Editor::write_region (string path, boost::shared_ptr<AudioRegion> region)
332 {
333 boost::shared_ptr<AudioFileSource> fs;
334 const samplepos_t chunk_size = 4096;
335 samplepos_t to_read;
336 Sample buf[chunk_size];
337 gain_t gain_buffer[chunk_size];
338 samplepos_t pos;
339 char s[PATH_MAX+1];
340 uint32_t cnt;
341 vector<boost::shared_ptr<AudioFileSource> > sources;
342 uint32_t nchans;
343
344 const string sound_directory = _session->session_directory().sound_path();
345
346 nchans = region->n_channels();
347
348 /* don't do duplicate of the entire source if that's what is going on here */
349
350 if (region->start() == 0 && region->length() == region->source_length(0)) {
351 /* XXX should link(2) to create a new inode with "path" */
352 return true;
353 }
354
355 if (path.length() == 0) {
356
357 for (uint32_t n=0; n < nchans; ++n) {
358
359 for (cnt = 0; cnt < 999999; ++cnt) {
360 if (nchans == 1) {
361 snprintf (s, sizeof(s), "%s/%s_%" PRIu32 ".wav", sound_directory.c_str(),
362 legalize_for_universal_path(region->name()).c_str(), cnt);
363 }
364 else {
365 snprintf (s, sizeof(s), "%s/%s_%" PRIu32 "-%" PRId32 ".wav", sound_directory.c_str(),
366 legalize_for_universal_path(region->name()).c_str(), cnt, n);
367 }
368
369 path = s;
370
371 if (!Glib::file_test (path, Glib::FILE_TEST_EXISTS)) {
372 break;
373 }
374 }
375
376 if (cnt == 999999) {
377 error << "" << endmsg;
378 goto error_out;
379 }
380
381
382
383 try {
384 fs = boost::dynamic_pointer_cast<AudioFileSource> (SourceFactory::createWritable (DataType::AUDIO, *_session, path, _session->sample_rate()));
385 }
386
387 catch (failed_constructor& err) {
388 goto error_out;
389 }
390
391 sources.push_back (fs);
392 }
393 }
394 else {
395 /* TODO: make filesources based on passed path */
396
397 }
398
399 to_read = region->length();
400 pos = region->position();
401
402 while (to_read) {
403 samplepos_t this_time;
404
405 this_time = min (to_read, chunk_size);
406
407 for (vector<boost::shared_ptr<AudioFileSource> >::iterator src=sources.begin(); src != sources.end(); ++src) {
408
409 fs = (*src);
410
411 if (region->read_at (buf, buf, gain_buffer, pos, this_time) != this_time) {
412 break;
413 }
414
415 if (fs->write (buf, this_time) != this_time) {
416 error << "" << endmsg;
417 goto error_out;
418 }
419 }
420
421 to_read -= this_time;
422 pos += this_time;
423 }
424
425 time_t tnow;
426 struct tm* now;
427 time (&tnow);
428 now = localtime (&tnow);
429
430 for (vector<boost::shared_ptr<AudioFileSource> >::iterator src = sources.begin(); src != sources.end(); ++src) {
431 (*src)->update_header (0, *now, tnow);
432 (*src)->mark_immutable ();
433 }
434
435 return true;
436
437 error_out:
438
439 for (vector<boost::shared_ptr<AudioFileSource> >::iterator i = sources.begin(); i != sources.end(); ++i) {
440 (*i)->mark_for_remove ();
441 }
442
443 return 0;
444 }
445
446 int
write_audio_selection(TimeSelection & ts)447 Editor::write_audio_selection (TimeSelection& ts)
448 {
449 int ret = 0;
450
451 if (selection->tracks.empty()) {
452 return 0;
453 }
454
455 for (TrackSelection::iterator i = selection->tracks.begin(); i != selection->tracks.end(); ++i) {
456
457 AudioTimeAxisView* atv;
458
459 if ((atv = dynamic_cast<AudioTimeAxisView*>(*i)) == 0) {
460 continue;
461 }
462
463 if (atv->is_audio_track()) {
464
465 boost::shared_ptr<AudioPlaylist> playlist = boost::dynamic_pointer_cast<AudioPlaylist>(atv->track()->playlist());
466
467 if (playlist && write_audio_range (*playlist, atv->track()->n_channels(), ts) == 0) {
468 ret = -1;
469 break;
470 }
471 }
472 }
473
474 return ret;
475 }
476
477 bool
write_audio_range(AudioPlaylist & playlist,const ChanCount & count,list<AudioRange> & range)478 Editor::write_audio_range (AudioPlaylist& playlist, const ChanCount& count, list<AudioRange>& range)
479 {
480 boost::shared_ptr<AudioFileSource> fs;
481 const samplepos_t chunk_size = 4096;
482 samplepos_t nframes;
483 Sample buf[chunk_size];
484 gain_t gain_buffer[chunk_size];
485 samplepos_t pos;
486 char s[PATH_MAX+1];
487 uint32_t cnt;
488 string path;
489 vector<boost::shared_ptr<AudioFileSource> > sources;
490
491 const string sound_directory = _session->session_directory().sound_path();
492
493 uint32_t channels = count.n_audio();
494
495 for (uint32_t n=0; n < channels; ++n) {
496
497 for (cnt = 0; cnt < 999999; ++cnt) {
498 if (channels == 1) {
499 snprintf (s, sizeof(s), "%s/%s_%" PRIu32 ".wav", sound_directory.c_str(),
500 legalize_for_universal_path(playlist.name()).c_str(), cnt);
501 }
502 else {
503 snprintf (s, sizeof(s), "%s/%s_%" PRIu32 "-%" PRId32 ".wav", sound_directory.c_str(),
504 legalize_for_universal_path(playlist.name()).c_str(), cnt, n);
505 }
506
507 if (!Glib::file_test (s, Glib::FILE_TEST_EXISTS)) {
508 break;
509 }
510 }
511
512 if (cnt == 999999) {
513 error << "" << endmsg;
514 goto error_out;
515 }
516
517 path = s;
518
519 try {
520 fs = boost::dynamic_pointer_cast<AudioFileSource> (SourceFactory::createWritable (DataType::AUDIO, *_session, path, _session->sample_rate()));
521 }
522
523 catch (failed_constructor& err) {
524 goto error_out;
525 }
526
527 sources.push_back (fs);
528
529 }
530
531
532 for (list<AudioRange>::iterator i = range.begin(); i != range.end();) {
533
534 nframes = (*i).length();
535 pos = (*i).start;
536
537 while (nframes) {
538 samplepos_t this_time;
539
540 this_time = min (nframes, chunk_size);
541
542 for (uint32_t n=0; n < channels; ++n) {
543
544 fs = sources[n];
545
546 if (playlist.read (buf, buf, gain_buffer, pos, this_time, n) != this_time) {
547 break;
548 }
549
550 if (fs->write (buf, this_time) != this_time) {
551 goto error_out;
552 }
553 }
554
555 nframes -= this_time;
556 pos += this_time;
557 }
558
559 list<AudioRange>::iterator tmp = i;
560 ++tmp;
561
562 if (tmp != range.end()) {
563
564 /* fill gaps with silence */
565
566 nframes = (*tmp).start - (*i).end;
567
568 while (nframes) {
569
570 samplepos_t this_time = min (nframes, chunk_size);
571 memset (buf, 0, sizeof (Sample) * this_time);
572
573 for (uint32_t n=0; n < channels; ++n) {
574
575 fs = sources[n];
576 if (fs->write (buf, this_time) != this_time) {
577 goto error_out;
578 }
579 }
580
581 nframes -= this_time;
582 }
583 }
584
585 i = tmp;
586 }
587
588 time_t tnow;
589 struct tm* now;
590 time (&tnow);
591 now = localtime (&tnow);
592
593 for (vector<boost::shared_ptr<AudioFileSource> >::iterator s = sources.begin(); s != sources.end(); ++s) {
594 (*s)->update_header (0, *now, tnow);
595 (*s)->mark_immutable ();
596 // do we need to ref it again?
597 }
598
599 return true;
600
601 error_out:
602 /* unref created files */
603
604 for (vector<boost::shared_ptr<AudioFileSource> >::iterator i = sources.begin(); i != sources.end(); ++i) {
605 (*i)->mark_for_remove ();
606 }
607
608 return false;
609 }
610
611 void
write_selection()612 Editor::write_selection ()
613 {
614 if (!selection->time.empty()) {
615 write_audio_selection (selection->time);
616 } else if (!selection->regions.empty()) {
617 write_region_selection (selection->regions);
618 }
619 }
620