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