1 /*
2  * Copyright (C) 2005-2017 Paul Davis <paul@linuxaudiosystems.com>
3  * Copyright (C) 2006-2016 David Robillard <d@drobilla.net>
4  * Copyright (C) 2006 Sampo Savolainen <v2@iki.fi>
5  * Copyright (C) 2007-2012 Carl Hetherington <carl@carlh.net>
6  * Copyright (C) 2014-2018 Ben Loftis <ben@harrisonconsoles.com>
7  * Copyright (C) 2014-2018 Colin Fletcher <colin.m.fletcher@googlemail.com>
8  * Copyright (C) 2014-2019 Robin Gareus <robin@gareus.org>
9  * Copyright (C) 2015-2017 Nick Mainsbridge <mainsbridge@gmail.com>
10  * Copyright (C) 2015 André Nusser <andre.nusser@googlemail.com>
11  * Copyright (C) 2016-2017 Tim Mayberry <mojofunk@gmail.com>
12  *
13  * This program is free software; you can redistribute it and/or modify
14  * it under the terms of the GNU General Public License as published by
15  * the Free Software Foundation; either version 2 of the License, or
16  * (at your option) any later version.
17  *
18  * This program is distributed in the hope that it will be useful,
19  * but WITHOUT ANY WARRANTY; without even the implied warranty of
20  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
21  * GNU General Public License for more details.
22  *
23  * You should have received a copy of the GNU General Public License along
24  * with this program; if not, write to the Free Software Foundation, Inc.,
25  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
26  */
27 
28 #include <algorithm>
29 #include <set>
30 #include <stdint.h>
31 #include <string>
32 
33 #include "pbd/stateful_diff_command.h"
34 #include "pbd/strsplit.h"
35 #include "pbd/types_convert.h"
36 #include "pbd/unwind.h"
37 #include "pbd/xml++.h"
38 
39 #include "ardour/debug.h"
40 #include "ardour/midi_region.h"
41 #include "ardour/playlist.h"
42 #include "ardour/playlist_factory.h"
43 #include "ardour/playlist_source.h"
44 #include "ardour/region.h"
45 #include "ardour/region_factory.h"
46 #include "ardour/region_sorters.h"
47 #include "ardour/session.h"
48 #include "ardour/session_playlists.h"
49 #include "ardour/source_factory.h"
50 #include "ardour/tempo.h"
51 #include "ardour/transient_detector.h"
52 #include "ardour/types_convert.h"
53 
54 #include "pbd/i18n.h"
55 
56 using namespace std;
57 using namespace ARDOUR;
58 using namespace PBD;
59 
60 namespace ARDOUR {
61 	namespace Properties {
62 		PBD::PropertyDescriptor<bool> regions;
63 	}
64 }
65 
66 struct ShowMeTheList {
ShowMeTheListShowMeTheList67 	ShowMeTheList (boost::shared_ptr<Playlist> pl, const string& n)
68 		: playlist (pl)
69 		, name (n)
70 	{}
71 
~ShowMeTheListShowMeTheList72 	~ShowMeTheList ()
73 	{
74 		cerr << ">>>>" << name << endl;
75 		playlist->dump ();
76 		cerr << "<<<<" << name << endl << endl;
77 	};
78 
79 	boost::shared_ptr<Playlist> playlist;
80 	string                      name;
81 };
82 
83 void
make_property_quarks()84 Playlist::make_property_quarks ()
85 {
86 	Properties::regions.property_id = g_quark_from_static_string (X_("regions"));
87 	DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for regions = %1\n",
88 	                                                Properties::regions.property_id));
89 }
90 
RegionListProperty(Playlist & pl)91 RegionListProperty::RegionListProperty (Playlist& pl)
92 	: SequenceProperty<std::list<boost::shared_ptr<Region> > > (Properties::regions.property_id, boost::bind (&Playlist::update, &pl, _1))
93 	, _playlist (pl)
94 {
95 }
96 
RegionListProperty(RegionListProperty const & p)97 RegionListProperty::RegionListProperty (RegionListProperty const& p)
98 	: PBD::SequenceProperty<std::list<boost::shared_ptr<Region> > > (p)
99 	, _playlist (p._playlist)
100 {
101 }
102 
103 RegionListProperty*
clone() const104 RegionListProperty::clone () const
105 {
106 	return new RegionListProperty (*this);
107 }
108 
109 RegionListProperty*
create() const110 RegionListProperty::create () const
111 {
112 	return new RegionListProperty (_playlist);
113 }
114 
115 void
get_content_as_xml(boost::shared_ptr<Region> region,XMLNode & node) const116 RegionListProperty::get_content_as_xml (boost::shared_ptr<Region> region, XMLNode& node) const
117 {
118 	/* All regions (even those which are deleted) have their state
119 	 * saved by other code, so we can just store ID here.
120 	 */
121 
122 	node.set_property ("id", region->id ());
123 }
124 
125 boost::shared_ptr<Region>
get_content_from_xml(XMLNode const & node) const126 RegionListProperty::get_content_from_xml (XMLNode const& node) const
127 {
128 	PBD::ID id;
129 	if (!node.get_property ("id", id)) {
130 		assert (false);
131 	}
132 
133 	boost::shared_ptr<Region> ret = _playlist.region_by_id (id);
134 
135 	if (!ret) {
136 		ret = RegionFactory::region_by_id (id);
137 	}
138 
139 	return ret;
140 }
141 
Playlist(Session & sess,string nom,DataType type,bool hide)142 Playlist::Playlist (Session& sess, string nom, DataType type, bool hide)
143 	: SessionObject (sess, nom)
144 	, regions (*this)
145 	, _type (type)
146 {
147 	init (hide);
148 	first_set_state = false;
149 	_name           = nom;
150 	_set_sort_id ();
151 }
152 
Playlist(Session & sess,const XMLNode & node,DataType type,bool hide)153 Playlist::Playlist (Session& sess, const XMLNode& node, DataType type, bool hide)
154 	: SessionObject (sess, "unnamed playlist")
155 	, regions (*this)
156 	, _type (type)
157 {
158 #ifndef NDEBUG
159 	XMLProperty const* prop = node.property ("type");
160 	assert (!prop || DataType (prop->value ()) == _type);
161 #endif
162 
163 	init (hide);
164 	_name = "unnamed"; /* reset by set_state */
165 	_set_sort_id ();
166 
167 	/* set state called by derived class */
168 }
169 
Playlist(boost::shared_ptr<const Playlist> other,string namestr,bool hide)170 Playlist::Playlist (boost::shared_ptr<const Playlist> other, string namestr, bool hide)
171 	: SessionObject (other->_session, namestr)
172 	, regions (*this)
173 	, _type (other->_type)
174 	, _orig_track_id (other->_orig_track_id)
175 	, _shared_with_ids (other->_shared_with_ids)
176 {
177 	init (hide);
178 
179 	RegionList tmp;
180 	ThawList   thawlist;
181 	other->copy_regions (tmp);
182 
183 	in_set_state++;
184 
185 	for (list<boost::shared_ptr<Region> >::iterator x = tmp.begin (); x != tmp.end (); ++x) {
186 		add_region_internal ((*x), (*x)->position (), thawlist);
187 	}
188 	thawlist.release ();
189 
190 	in_set_state--;
191 
192 	_splicing  = other->_splicing;
193 	_rippling  = other->_rippling;
194 	_nudging   = other->_nudging;
195 	_edit_mode = other->_edit_mode;
196 
197 	in_set_state    = 0;
198 	first_set_state = false;
199 	in_flush        = false;
200 	in_partition    = false;
201 	subcnt          = 0;
202 	_frozen         = other->_frozen;
203 }
204 
Playlist(boost::shared_ptr<const Playlist> other,samplepos_t start,samplecnt_t cnt,string str,bool hide)205 Playlist::Playlist (boost::shared_ptr<const Playlist> other, samplepos_t start, samplecnt_t cnt, string str, bool hide)
206 	: SessionObject (other->_session, str)
207 	, regions (*this)
208 	, _type (other->_type)
209 	, _orig_track_id (other->_orig_track_id)
210 	, _shared_with_ids (other->_shared_with_ids)
211 {
212 	RegionReadLock rlock2 (const_cast<Playlist*> (other.get ()));
213 
214 	samplepos_t end = start + cnt - 1;
215 
216 	init (hide);
217 
218 	in_set_state++;
219 
220 	ThawList thawlist;
221 	for (RegionList::const_iterator i = other->regions.begin (); i != other->regions.end (); ++i) {
222 		boost::shared_ptr<Region> region;
223 		boost::shared_ptr<Region> new_region;
224 		sampleoffset_t            offset   = 0;
225 		samplepos_t               position = 0;
226 		samplecnt_t               len      = 0;
227 		string                    new_name;
228 		Evoral::OverlapType       overlap;
229 
230 		region = *i;
231 
232 		overlap = region->coverage (start, end);
233 
234 		switch (overlap) {
235 			case Evoral::OverlapNone:
236 				continue;
237 
238 			case Evoral::OverlapInternal:
239 				offset   = start - region->position ();
240 				position = 0;
241 				len      = cnt;
242 				break;
243 
244 			case Evoral::OverlapStart:
245 				offset   = 0;
246 				position = region->position () - start;
247 				len      = end - region->position ();
248 				break;
249 
250 			case Evoral::OverlapEnd:
251 				offset   = start - region->position ();
252 				position = 0;
253 				len      = region->length () - offset;
254 				break;
255 
256 			case Evoral::OverlapExternal:
257 				offset   = 0;
258 				position = region->position () - start;
259 				len      = region->length ();
260 				break;
261 		}
262 
263 		RegionFactory::region_name (new_name, region->name (), false);
264 
265 		PropertyList plist;
266 
267 		if (_type == DataType::MIDI) {
268 			boost::shared_ptr<MidiRegion> mregion = boost::dynamic_pointer_cast<MidiRegion> (region);
269 			if (mregion && offset) {
270 				int32_t      division                  = 1; /*magic value that ignores the meter (right?)*/
271 				const double start_quarter_note        = _session.tempo_map ().exact_qn_at_sample (start, division);
272 				const double start_offset_quarter_note = start_quarter_note - region->quarter_note ();
273 				const double end_samples               = (overlap == Evoral::OverlapStart ? end :           /*end the new region at the end of the selection*/
274 				                                                                            region->position () + region->length () - 1); /*use the region's end*/
275 				const double length_quarter_note       = _session.tempo_map ().exact_qn_at_sample (end_samples, division) - start_quarter_note;
276 				plist.add (Properties::start_beats, mregion->start_beats () + start_offset_quarter_note);
277 				plist.add (Properties::length_beats, length_quarter_note);
278 			}
279 		}
280 
281 		plist.add (Properties::start, region->start () + offset);
282 		plist.add (Properties::length, len);
283 		plist.add (Properties::name, new_name);
284 		plist.add (Properties::layer, region->layer ());
285 		plist.add (Properties::layering_index, region->layering_index ());
286 
287 		new_region = RegionFactory::create (region, plist, true, &thawlist);
288 
289 		add_region_internal (new_region, position, thawlist);
290 	}
291 
292 	thawlist.release ();
293 
294 	/* keep track of any dead space at end (for pasting into Ripple or Splice mode)
295 	 * at the end of construction, any length of cnt beyond the extents of the regions is end_space
296 	 */
297 	_end_space = cnt - (get_extent ().second - get_extent ().first);
298 
299 	in_set_state--;
300 	first_set_state = false;
301 }
302 
303 void
use()304 Playlist::use ()
305 {
306 	++_refcnt;
307 	InUse (true); /* EMIT SIGNAL */
308 }
309 
310 void
release()311 Playlist::release ()
312 {
313 	if (_refcnt > 0) {
314 		_refcnt--;
315 	}
316 
317 	if (_refcnt == 0) {
318 		InUse (false); /* EMIT SIGNAL */
319 	}
320 }
321 
322 void
copy_regions(RegionList & newlist) const323 Playlist::copy_regions (RegionList& newlist) const
324 {
325 	RegionReadLock rlock (const_cast<Playlist*> (this));
326 
327 	for (RegionList::const_iterator i = regions.begin (); i != regions.end (); ++i) {
328 		newlist.push_back (RegionFactory::create (*i, true, true));
329 	}
330 }
331 
332 void
init(bool hide)333 Playlist::init (bool hide)
334 {
335 	add_property (regions);
336 	_xml_node_name = X_("Playlist");
337 
338 	g_atomic_int_set (&block_notifications, 0);
339 	g_atomic_int_set (&ignore_state_changes, 0);
340 	pending_contents_change     = false;
341 	pending_layering            = false;
342 	first_set_state             = true;
343 	_refcnt                     = 0;
344 	_hidden                     = hide;
345 	_splicing                   = false;
346 	_rippling                   = false;
347 	_shuffling                  = false;
348 	_nudging                    = false;
349 	in_set_state                = 0;
350 	in_undo                     = false;
351 	_edit_mode                  = Config->get_edit_mode ();
352 	in_flush                    = false;
353 	in_partition                = false;
354 	subcnt                      = 0;
355 	_frozen                     = false;
356 	_capture_insertion_underway = false;
357 	_combine_ops                = 0;
358 	_end_space                  = 0;
359 	_playlist_shift_active      = false;
360 
361 	_session.history ().BeginUndoRedo.connect_same_thread (*this, boost::bind (&Playlist::begin_undo, this));
362 	_session.history ().EndUndoRedo.connect_same_thread (*this, boost::bind (&Playlist::end_undo, this));
363 
364 	ContentsChanged.connect_same_thread (*this, boost::bind (&Playlist::mark_session_dirty, this));
365 }
366 
~Playlist()367 Playlist::~Playlist ()
368 {
369 	DEBUG_TRACE (DEBUG::Destruction, string_compose ("Playlist %1 destructor\n", _name));
370 
371 	{
372 		RegionReadLock rl (this);
373 
374 		for (set<boost::shared_ptr<Region> >::iterator i = all_regions.begin (); i != all_regions.end (); ++i) {
375 			(*i)->set_playlist (boost::shared_ptr<Playlist> ());
376 		}
377 	}
378 
379 	/* GoingAway must be emitted by derived classes */
380 }
381 
382 void
_set_sort_id()383 Playlist::_set_sort_id ()
384 {
385 	/* Playlists are given names like <track name>.<id>
386 	 * or <track name>.<edit group name>.<id> where id
387 	 * is an integer. We extract the id and sort by that.
388 	 */
389 
390 	size_t dot_position = _name.val ().find_last_of (".");
391 
392 	if (dot_position == string::npos) {
393 		_sort_id = 0;
394 	} else {
395 		string t = _name.val ().substr (dot_position + 1);
396 
397 		if (!string_to_uint32 (t, _sort_id)) {
398 			_sort_id = 0;
399 		}
400 	}
401 }
402 
403 bool
set_name(const string & str)404 Playlist::set_name (const string& str)
405 {
406 	bool ret = SessionObject::set_name (str);
407 	if (ret) {
408 		_set_sort_id ();
409 	}
410 	return ret;
411 }
412 
413 /***********************************************************************
414  * CHANGE NOTIFICATION HANDLING
415  *
416  * Notifications must be delayed till the region_lock is released. This
417  * is necessary because handlers for the signals may need to acquire
418  * the lock (e.g. to read from the playlist).
419  ***********************************************************************/
420 
421 void
begin_undo()422 Playlist::begin_undo ()
423 {
424 	in_undo = true;
425 	freeze ();
426 }
427 
428 void
end_undo()429 Playlist::end_undo ()
430 {
431 	thaw (true);
432 	in_undo = false;
433 }
434 
435 void
freeze()436 Playlist::freeze ()
437 {
438 	/* flush any ongoing reads, paricularly AudioPlaylist::read(),
439 	 * before beginning to modify the playlist.
440 	 */
441 	RegionWriteLock rlock (this);
442 	freeze_locked ();
443 }
444 
445 void
freeze_locked()446 Playlist::freeze_locked ()
447 {
448 	delay_notifications ();
449 	g_atomic_int_inc (&ignore_state_changes);
450 }
451 
452 /** @param from_undo true if this thaw is triggered by the end of an undo on this playlist */
453 void
thaw(bool from_undo)454 Playlist::thaw (bool from_undo)
455 {
456 	g_atomic_int_dec_and_test (&ignore_state_changes);
457 	release_notifications (from_undo);
458 }
459 
460 void
delay_notifications()461 Playlist::delay_notifications ()
462 {
463 	g_atomic_int_inc (&block_notifications);
464 }
465 
466 /** @param from_undo true if this release is triggered by the end of an undo on this playlist */
467 void
release_notifications(bool from_undo)468 Playlist::release_notifications (bool from_undo)
469 {
470 	if (g_atomic_int_dec_and_test (&block_notifications)) {
471 		flush_notifications (from_undo);
472 	}
473 }
474 
475 void
notify_contents_changed()476 Playlist::notify_contents_changed ()
477 {
478 	if (holding_state ()) {
479 		pending_contents_change = true;
480 	} else {
481 		pending_contents_change = false;
482 		ContentsChanged (); /* EMIT SIGNAL */
483 	}
484 }
485 
486 void
notify_layering_changed()487 Playlist::notify_layering_changed ()
488 {
489 	if (holding_state ()) {
490 		pending_layering = true;
491 	} else {
492 		pending_layering = false;
493 		LayeringChanged (); /* EMIT SIGNAL */
494 	}
495 }
496 
497 void
notify_region_removed(boost::shared_ptr<Region> r)498 Playlist::notify_region_removed (boost::shared_ptr<Region> r)
499 {
500 	if (holding_state ()) {
501 		pending_removes.insert (r);
502 		pending_contents_change = true;
503 	} else {
504 		/* this might not be true, but we have to act
505 		   as though it could be.
506 		*/
507 		pending_contents_change = false;
508 		RegionRemoved (boost::weak_ptr<Region> (r)); /* EMIT SIGNAL */
509 		ContentsChanged ();                          /* EMIT SIGNAL */
510 	}
511 }
512 
513 void
notify_region_moved(boost::shared_ptr<Region> r)514 Playlist::notify_region_moved (boost::shared_ptr<Region> r)
515 {
516 	Evoral::RangeMove<samplepos_t> const move (r->last_position (), r->length (), r->position ());
517 
518 	if (holding_state ()) {
519 		pending_range_moves.push_back (move);
520 
521 	} else {
522 		list<Evoral::RangeMove<samplepos_t> > m;
523 		m.push_back (move);
524 		RangesMoved (m, false);
525 	}
526 }
527 
528 void
notify_region_start_trimmed(boost::shared_ptr<Region> r)529 Playlist::notify_region_start_trimmed (boost::shared_ptr<Region> r)
530 {
531 	if (r->position () >= r->last_position ()) {
532 		/* trimmed shorter */
533 		return;
534 	}
535 
536 	Evoral::Range<samplepos_t> const extra (r->position (), r->last_position ());
537 
538 	if (holding_state ()) {
539 		pending_region_extensions.push_back (extra);
540 
541 	} else {
542 		list<Evoral::Range<samplepos_t> > r;
543 		r.push_back (extra);
544 		RegionsExtended (r);
545 	}
546 }
547 
548 void
notify_region_end_trimmed(boost::shared_ptr<Region> r)549 Playlist::notify_region_end_trimmed (boost::shared_ptr<Region> r)
550 {
551 	if (r->length () < r->last_length ()) {
552 		/* trimmed shorter */
553 	}
554 
555 	Evoral::Range<samplepos_t> const extra (r->position () + r->last_length (), r->position () + r->length ());
556 
557 	if (holding_state ()) {
558 		pending_region_extensions.push_back (extra);
559 
560 	} else {
561 		list<Evoral::Range<samplepos_t> > r;
562 		r.push_back (extra);
563 		RegionsExtended (r);
564 	}
565 }
566 
567 void
notify_region_added(boost::shared_ptr<Region> r)568 Playlist::notify_region_added (boost::shared_ptr<Region> r)
569 {
570 	/* the length change might not be true, but we have to act
571 	 * as though it could be.
572 	 */
573 
574 	if (holding_state ()) {
575 		pending_adds.insert (r);
576 		pending_contents_change = true;
577 	} else {
578 		r->clear_changes ();
579 		pending_contents_change = false;
580 		RegionAdded (boost::weak_ptr<Region> (r)); /* EMIT SIGNAL */
581 		ContentsChanged ();                        /* EMIT SIGNAL */
582 		RegionFactory::CheckNewRegion (r);         /* EMIT SIGNAL */
583 	}
584 }
585 
586 /** @param from_undo true if this flush is triggered by the end of an undo on this playlist */
587 void
flush_notifications(bool from_undo)588 Playlist::flush_notifications (bool from_undo)
589 {
590 	set<boost::shared_ptr<Region> >::iterator s;
591 	bool regions_changed = false;
592 
593 	if (in_flush) {
594 		return;
595 	}
596 
597 	in_flush = true;
598 
599 	if (!pending_bounds.empty () || !pending_removes.empty () || !pending_adds.empty ()) {
600 		regions_changed = true;
601 	}
602 
603 	/* XXX: it'd be nice if we could use pending_bounds for
604 	   RegionsExtended and RegionsMoved.
605 	*/
606 
607 	/* we have no idea what order the regions ended up in pending
608 	 * bounds (it could be based on selection order, for example).
609 	 * so, to preserve layering in the "most recently moved is higher"
610 	 * model, sort them by existing layer, then timestamp them.
611 	 */
612 
613 	list<Evoral::Range<samplepos_t> > crossfade_ranges;
614 
615 	for (RegionList::iterator r = pending_bounds.begin (); r != pending_bounds.end (); ++r) {
616 		crossfade_ranges.push_back ((*r)->last_range ());
617 		crossfade_ranges.push_back ((*r)->range ());
618 	}
619 
620 	boost::shared_ptr<RegionList> rl (new RegionList);
621 	for (s = pending_removes.begin (); s != pending_removes.end (); ++s) {
622 		crossfade_ranges.push_back ((*s)->range ());
623 		remove_dependents (*s);
624 		RegionRemoved (boost::weak_ptr<Region> (*s)); /* EMIT SIGNAL */
625 		rl->push_back (*s);
626 	}
627 	if (rl->size () > 0) {
628 		Region::RegionsPropertyChanged (rl, Properties::hidden);
629 	}
630 
631 	for (s = pending_adds.begin (); s != pending_adds.end (); ++s) {
632 		crossfade_ranges.push_back ((*s)->range ());
633 		/* don't emit RegionAdded signal until relayering is done,
634 		   so that the region is fully setup by the time
635 		   anyone hears that its been added
636 		*/
637 	}
638 
639 	/* notify about contents/region changes first so that layering changes
640 	 * in a UI will take place on the new contents.
641 	 */
642 
643 	if (regions_changed || pending_contents_change) {
644 		pending_layering = true;
645 		ContentsChanged (); /* EMIT SIGNAL */
646 	}
647 
648 	for (s = pending_adds.begin (); s != pending_adds.end (); ++s) {
649 		(*s)->clear_changes ();
650 		RegionAdded (boost::weak_ptr<Region> (*s)); /* EMIT SIGNAL */
651 		RegionFactory::CheckNewRegion (*s);         /* EMIT SIGNAL */
652 	}
653 
654 	if ((regions_changed && !in_set_state) || pending_layering) {
655 		relayer ();
656 	}
657 
658 	coalesce_and_check_crossfades (crossfade_ranges);
659 
660 	if (!pending_range_moves.empty ()) {
661 		/* We don't need to check crossfades for these as pending_bounds has
662 		   already covered it.
663 		*/
664 		RangesMoved (pending_range_moves, from_undo || _playlist_shift_active);
665 	}
666 
667 	if (!pending_region_extensions.empty ()) {
668 		RegionsExtended (pending_region_extensions);
669 	}
670 
671 	clear_pending ();
672 
673 	in_flush = false;
674 }
675 
676 void
clear_pending()677 Playlist::clear_pending ()
678 {
679 	pending_adds.clear ();
680 	pending_removes.clear ();
681 	pending_bounds.clear ();
682 	pending_range_moves.clear ();
683 	pending_region_extensions.clear ();
684 	pending_contents_change = false;
685 	pending_layering        = false;
686 }
687 
688 /*************************************************************
689  * PLAYLIST OPERATIONS
690  *************************************************************/
691 
692 /** Note: this calls set_layer (..., DBL_MAX) so it will reset the layering index of region */
693 void
add_region(boost::shared_ptr<Region> region,samplepos_t position,float times,bool auto_partition,int32_t sub_num,double quarter_note,bool for_music)694 Playlist::add_region (boost::shared_ptr<Region> region, samplepos_t position, float times, bool auto_partition, int32_t sub_num, double quarter_note, bool for_music)
695 {
696 	RegionWriteLock rlock (this);
697 	times = fabs (times);
698 
699 	int itimes = (int)floor (times);
700 
701 	samplepos_t pos = position;
702 
703 	if (times == 1 && auto_partition) {
704 		partition_internal (pos - 1, (pos + region->length ()), true, rlock.thawlist);
705 		for (RegionList::iterator i = rlock.thawlist.begin (); i != rlock.thawlist.end (); ++i) {
706 			_session.add_command (new StatefulDiffCommand (*i));
707 		}
708 	}
709 
710 	if (itimes >= 1) {
711 		add_region_internal (region, pos, rlock.thawlist, sub_num, quarter_note, for_music);
712 		set_layer (region, DBL_MAX);
713 		pos += region->length ();
714 		--itimes;
715 	}
716 
717 	/* note that itimes can be zero if we being asked to just
718 	 * insert a single fraction of the region.
719 	 */
720 
721 	for (int i = 0; i < itimes; ++i) {
722 		boost::shared_ptr<Region> copy = RegionFactory::create (region, true, false, &rlock.thawlist);
723 		add_region_internal (copy, pos, rlock.thawlist, sub_num);
724 		set_layer (copy, DBL_MAX);
725 		pos += region->length ();
726 	}
727 
728 	samplecnt_t length = 0;
729 
730 	if (floor (times) != times) {
731 		length = (samplecnt_t)floor (region->length () * (times - floor (times)));
732 		string name;
733 		RegionFactory::region_name (name, region->name (), false);
734 
735 		{
736 			PropertyList plist;
737 
738 			plist.add (Properties::start, region->start ());
739 			plist.add (Properties::length, length);
740 			plist.add (Properties::name, name);
741 			plist.add (Properties::layer, region->layer ());
742 
743 			boost::shared_ptr<Region> sub = RegionFactory::create (region, plist, true, &rlock.thawlist);
744 			add_region_internal (sub, pos, rlock.thawlist, sub_num);
745 			set_layer (sub, DBL_MAX);
746 		}
747 	}
748 
749 	possibly_splice_unlocked (position, (pos + length) - position, region, rlock.thawlist);
750 }
751 
752 void
set_region_ownership()753 Playlist::set_region_ownership ()
754 {
755 	RegionWriteLock           rl (this);
756 	RegionList::iterator      i;
757 	boost::weak_ptr<Playlist> pl (shared_from_this ());
758 
759 	for (i = regions.begin (); i != regions.end (); ++i) {
760 		(*i)->set_playlist (pl);
761 	}
762 }
763 
764 bool
add_region_internal(boost::shared_ptr<Region> region,samplepos_t position,ThawList & thawlist,int32_t sub_num,double quarter_note,bool for_music)765 Playlist::add_region_internal (boost::shared_ptr<Region> region, samplepos_t position, ThawList& thawlist, int32_t sub_num, double quarter_note, bool for_music)
766 {
767 	if (region->data_type () != _type) {
768 		return false;
769 	}
770 
771 	/* note, this will delay signal emission and trigger Playlist::region_changed_proxy
772 	 * via PropertyChanged subsciption below :(
773 	 */
774 	thawlist.add (region);
775 
776 	RegionSortByPosition cmp;
777 
778 	if (!first_set_state) {
779 		boost::shared_ptr<Playlist> foo (shared_from_this ());
780 		region->set_playlist (boost::weak_ptr<Playlist> (foo));
781 	}
782 	if (for_music) {
783 		region->set_position_music (quarter_note);
784 	} else {
785 		region->set_position (position, sub_num);
786 	}
787 
788 	regions.insert (upper_bound (regions.begin (), regions.end (), region, cmp), region);
789 	all_regions.insert (region);
790 
791 	possibly_splice_unlocked (position, region->length (), region, thawlist);
792 
793 	if (!holding_state ()) {
794 		/* layers get assigned from XML state, and are not reset during undo/redo */
795 		relayer ();
796 	}
797 
798 	/* we need to notify the existence of new region before checking dependents. Ick. */
799 
800 	notify_region_added (region);
801 
802 	region->PropertyChanged.connect_same_thread (region_state_changed_connections, boost::bind (&Playlist::region_changed_proxy, this, _1, boost::weak_ptr<Region> (region)));
803 	region->DropReferences.connect_same_thread (region_drop_references_connections, boost::bind (&Playlist::region_going_away, this, boost::weak_ptr<Region> (region)));
804 
805 	/* do not handle property changes of newly added regions.
806 	 * Otherwise this would triggger Playlist::notify_region_moved()
807 	 * -> RangesMoved() and move automation.
808 	 */
809 	region->clear_changes ();
810 
811 	return true;
812 }
813 
814 void
replace_region(boost::shared_ptr<Region> old,boost::shared_ptr<Region> newr,samplepos_t pos)815 Playlist::replace_region (boost::shared_ptr<Region> old, boost::shared_ptr<Region> newr, samplepos_t pos)
816 {
817 	RegionWriteLock rlock (this);
818 
819 	bool old_sp = _splicing;
820 	_splicing   = true;
821 
822 	remove_region_internal (old, rlock.thawlist);
823 	add_region_internal (newr, pos, rlock.thawlist);
824 	set_layer (newr, old->layer ());
825 
826 	_splicing = old_sp;
827 
828 	possibly_splice_unlocked (pos, old->length () - newr->length (), boost::shared_ptr<Region> (), rlock.thawlist);
829 }
830 
831 void
remove_region(boost::shared_ptr<Region> region)832 Playlist::remove_region (boost::shared_ptr<Region> region)
833 {
834 	RegionWriteLock rlock (this);
835 	remove_region_internal (region, rlock.thawlist);
836 }
837 
838 int
remove_region_internal(boost::shared_ptr<Region> region,ThawList & thawlist)839 Playlist::remove_region_internal (boost::shared_ptr<Region> region, ThawList& thawlist)
840 {
841 	RegionList::iterator i;
842 
843 	if (!in_set_state) {
844 		/* unset playlist */
845 		region->set_playlist (boost::weak_ptr<Playlist> ());
846 	}
847 
848 	/* XXX should probably freeze here .... */
849 
850 	for (i = regions.begin (); i != regions.end (); ++i) {
851 		if (*i == region) {
852 			samplepos_t pos      = (*i)->position ();
853 			samplecnt_t distance = (*i)->length ();
854 
855 			regions.erase (i);
856 
857 			possibly_splice_unlocked (pos, -distance, boost::shared_ptr<Region> (), thawlist);
858 
859 			if (!holding_state ()) {
860 				relayer ();
861 				remove_dependents (region);
862 			}
863 
864 			notify_region_removed (region);
865 			break;
866 		}
867 	}
868 
869 	return -1;
870 }
871 
872 void
remove_gaps(samplepos_t gap_threshold,samplepos_t leave_gap,boost::function<void (samplepos_t,samplecnt_t)> gap_callback)873 Playlist::remove_gaps (samplepos_t gap_threshold, samplepos_t leave_gap, boost::function<void (samplepos_t, samplecnt_t)> gap_callback)
874 {
875 	RegionWriteLock rlock (this);
876 	RegionList::iterator i;
877 	RegionList::iterator nxt (regions.end());
878 	bool closed = false;
879 
880 	if (regions.size() < 2) {
881 		return;
882 	}
883 
884 	for (i = regions.begin(); i != regions.end(); ++i) {
885 
886 		nxt = i;
887 		++nxt;
888 
889 		if (nxt == regions.end()) {
890 			break;
891 		}
892 
893 		samplepos_t end_of_this_region = (*i)->position() + (*i)->length();
894 
895 		if (end_of_this_region >= (*nxt)->position()) {
896 			continue;
897 		}
898 
899 		const samplepos_t gap = (*nxt)->position() - end_of_this_region;
900 
901 		if (gap < gap_threshold) {
902 			continue;
903 		}
904 
905 		const samplepos_t shift = gap - leave_gap;
906 
907 		ripple_unlocked ((*nxt)->position(), -shift, 0, rlock.thawlist, false);
908 
909 		gap_callback ((*nxt)->position(), shift);
910 
911 		closed = true;
912 	}
913 
914 	if (closed) {
915 		notify_contents_changed ();
916 	}
917 }
918 
919 void
get_equivalent_regions(boost::shared_ptr<Region> other,vector<boost::shared_ptr<Region>> & results)920 Playlist::get_equivalent_regions (boost::shared_ptr<Region> other, vector<boost::shared_ptr<Region> >& results)
921 {
922 	switch (Config->get_region_equivalence ()) {
923 		case Exact:
924 			for (RegionList::iterator i = regions.begin (); i != regions.end (); ++i) {
925 				if ((*i)->exact_equivalent (other)) {
926 					results.push_back (*i);
927 				}
928 			}
929 			break;
930 		case LayerTime:
931 			for (RegionList::iterator i = regions.begin (); i != regions.end (); ++i) {
932 				if ((*i)->layer_and_time_equivalent (other)) {
933 					results.push_back (*i);
934 				}
935 			}
936 			break;
937 		case Enclosed:
938 			for (RegionList::iterator i = regions.begin (); i != regions.end (); ++i) {
939 				if ((*i)->enclosed_equivalent (other)) {
940 					results.push_back (*i);
941 				}
942 			}
943 			break;
944 		case Overlap:
945 			for (RegionList::iterator i = regions.begin (); i != regions.end (); ++i) {
946 				if ((*i)->overlap_equivalent (other)) {
947 					results.push_back (*i);
948 				}
949 			}
950 			break;
951 	}
952 }
953 
954 void
partition(samplepos_t start,samplepos_t end,bool cut)955 Playlist::partition (samplepos_t start, samplepos_t end, bool cut)
956 {
957 	RegionWriteLock lock (this);
958 	partition_internal (start, end, cut, lock.thawlist);
959 }
960 
961 /* If a MIDI region is locked to musical-time, Properties::start is ignored
962  * and _start is overwritten using Properties::start_beats in
963  * add_region_internal() -> Region::set_position() -> MidiRegion::set_position_internal()
964  */
965 static void
maybe_add_start_beats(TempoMap const & tm,PropertyList & plist,boost::shared_ptr<Region> r,samplepos_t start,samplepos_t end)966 maybe_add_start_beats (TempoMap const& tm, PropertyList& plist, boost::shared_ptr<Region> r, samplepos_t start, samplepos_t end)
967 {
968 	boost::shared_ptr<MidiRegion> mr = boost::dynamic_pointer_cast<MidiRegion> (r);
969 	if (!mr) {
970 		return;
971 	}
972 	double delta_beats = tm.quarter_notes_between_samples (start, end);
973 	plist.add (Properties::start_beats, mr->start_beats () + delta_beats);
974 }
975 
976 /** Go through each region on the playlist and cut them at start and end, removing the section between
977  *  start and end if cutting == true.  Regions that lie entirely within start and end are always
978  *  removed.
979  */
980 void
partition_internal(samplepos_t start,samplepos_t end,bool cutting,ThawList & thawlist)981 Playlist::partition_internal (samplepos_t start, samplepos_t end, bool cutting, ThawList& thawlist)
982 {
983 	RegionList new_regions;
984 
985 	{
986 		boost::shared_ptr<Region> region;
987 		boost::shared_ptr<Region> current;
988 		string                    new_name;
989 		RegionList::iterator      tmp;
990 		Evoral::OverlapType       overlap;
991 		samplepos_t               pos1, pos2, pos3, pos4;
992 
993 		in_partition = true;
994 
995 		/* need to work from a copy, because otherwise the regions we add
996 		 * during the process get operated on as well.
997 		 */
998 
999 		RegionList copy = regions.rlist ();
1000 
1001 		for (RegionList::iterator i = copy.begin (); i != copy.end (); i = tmp) {
1002 			tmp = i;
1003 			++tmp;
1004 
1005 			current = *i;
1006 
1007 			if (current->first_sample () >= start && current->last_sample () < end) {
1008 				if (cutting) {
1009 					remove_region_internal (current, thawlist);
1010 				}
1011 
1012 				continue;
1013 			}
1014 
1015 			/* coverage will return OverlapStart if the start coincides
1016 			 * with the end point. we do not partition such a region,
1017 			 * so catch this special case.
1018 			 */
1019 
1020 			if (current->first_sample () >= end) {
1021 				continue;
1022 			}
1023 
1024 			if ((overlap = current->coverage (start, end)) == Evoral::OverlapNone) {
1025 				continue;
1026 			}
1027 
1028 			pos1 = current->position ();
1029 			pos2 = start;
1030 			pos3 = end;
1031 			pos4 = current->last_sample ();
1032 
1033 			if (overlap == Evoral::OverlapInternal) {
1034 				/* split: we need 3 new regions, the front, middle and end.
1035 				 * cut:   we need 2 regions, the front and end.
1036 				 *
1037 				 *
1038 				 *                start                 end
1039 				 * ---------------*************************------------
1040 				 *                P1  P2              P3  P4
1041 				 * SPLIT:
1042 				 * ---------------*****++++++++++++++++====------------
1043 				 * CUT
1044 				 * ---------------*****----------------====------------
1045 				 */
1046 
1047 				if (!cutting) {
1048 					/* "middle" ++++++ */
1049 
1050 					RegionFactory::region_name (new_name, current->name (), false);
1051 
1052 					PropertyList plist;
1053 
1054 					plist.add (Properties::start, current->start () + (pos2 - pos1));
1055 					plist.add (Properties::length, pos3 - pos2);
1056 					plist.add (Properties::name, new_name);
1057 					plist.add (Properties::layer, current->layer ());
1058 					plist.add (Properties::layering_index, current->layering_index ());
1059 					plist.add (Properties::automatic, true);
1060 					plist.add (Properties::left_of_split, true);
1061 					plist.add (Properties::right_of_split, true);
1062 					maybe_add_start_beats (_session.tempo_map (), plist, current, current->start (), current->start () + (pos2 - pos1));
1063 
1064 					/* see note in :_split_region()
1065 					 * for MusicSample is needed to offset region-gain
1066 					 */
1067 					region = RegionFactory::create (current, MusicSample (pos2 - pos1, 0), plist, true, &thawlist);
1068 					add_region_internal (region, start, thawlist);
1069 					new_regions.push_back (region);
1070 				}
1071 
1072 				/* "end" ====== */
1073 
1074 				RegionFactory::region_name (new_name, current->name (), false);
1075 
1076 				PropertyList plist;
1077 
1078 				plist.add (Properties::start, current->start () + (pos3 - pos1));
1079 				plist.add (Properties::length, pos4 - pos3);
1080 				plist.add (Properties::name, new_name);
1081 				plist.add (Properties::layer, current->layer ());
1082 				plist.add (Properties::layering_index, current->layering_index ());
1083 				plist.add (Properties::automatic, true);
1084 				plist.add (Properties::right_of_split, true);
1085 				maybe_add_start_beats (_session.tempo_map (), plist, current, current->start (), current->start () + (pos3 - pos1));
1086 
1087 				region = RegionFactory::create (current, MusicSample (pos3 - pos1, 0), plist, true, &thawlist);
1088 
1089 				add_region_internal (region, end, thawlist);
1090 				new_regions.push_back (region);
1091 
1092 				/* "front" ***** */
1093 
1094 				current->clear_changes ();
1095 				thawlist.add (current);
1096 				current->cut_end (pos2 - 1);
1097 
1098 			} else if (overlap == Evoral::OverlapEnd) {
1099 				/*
1100 				 *              start           end
1101 				 * ---------------*************************------------
1102 				 * P1           P2         P4   P3
1103 				 * SPLIT:
1104 				 * ---------------**************+++++++++++------------
1105 				 * CUT:
1106 				 * ---------------**************-----------------------
1107 				*/
1108 
1109 				if (!cutting) {
1110 					/* end +++++ */
1111 
1112 					RegionFactory::region_name (new_name, current->name (), false);
1113 
1114 					PropertyList plist;
1115 
1116 					plist.add (Properties::start, current->start () + (pos2 - pos1));
1117 					plist.add (Properties::length, pos4 - pos2);
1118 					plist.add (Properties::name, new_name);
1119 					plist.add (Properties::layer, current->layer ());
1120 					plist.add (Properties::layering_index, current->layering_index ());
1121 					plist.add (Properties::automatic, true);
1122 					plist.add (Properties::left_of_split, true);
1123 					maybe_add_start_beats (_session.tempo_map (), plist, current, current->start (), current->start () + (pos2 - pos1));
1124 
1125 					region = RegionFactory::create (current, MusicSample (pos2 - pos1, 0), plist, true, &thawlist);
1126 
1127 					add_region_internal (region, start, thawlist);
1128 					new_regions.push_back (region);
1129 				}
1130 
1131 				/* front ****** */
1132 
1133 				current->clear_changes ();
1134 				thawlist.add (current);
1135 				current->cut_end (pos2 - 1);
1136 
1137 			} else if (overlap == Evoral::OverlapStart) {
1138 				/* split: we need 2 regions: the front and the end.
1139 				 * cut: just trim current to skip the cut area
1140 				 *
1141 				 *
1142 				 * start           end
1143 				 * ---------------*************************------------
1144 				 * P2          P1 P3                   P4
1145 				 *
1146 				 * SPLIT:
1147 				 * ---------------****+++++++++++++++++++++------------
1148 				 * CUT:
1149 				 * -------------------*********************------------
1150 				 */
1151 
1152 				if (!cutting) {
1153 					/* front **** */
1154 					RegionFactory::region_name (new_name, current->name (), false);
1155 
1156 					PropertyList plist;
1157 
1158 					plist.add (Properties::start, current->start ());
1159 					plist.add (Properties::length, pos3 - pos1);
1160 					plist.add (Properties::name, new_name);
1161 					plist.add (Properties::layer, current->layer ());
1162 					plist.add (Properties::layering_index, current->layering_index ());
1163 					plist.add (Properties::automatic, true);
1164 					plist.add (Properties::right_of_split, true);
1165 					maybe_add_start_beats (_session.tempo_map (), plist, current, current->start (), current->start ());
1166 
1167 					region = RegionFactory::create (current, plist, true, &thawlist);
1168 
1169 					add_region_internal (region, pos1, thawlist);
1170 					new_regions.push_back (region);
1171 				}
1172 
1173 				/* end */
1174 
1175 				current->clear_changes ();
1176 				thawlist.add (current);
1177 				current->trim_front (pos3);
1178 			} else if (overlap == Evoral::OverlapExternal) {
1179 				/* split: no split required.
1180 				 * cut: remove the region.
1181 				 *
1182 				 *
1183 				 * start                                      end
1184 				 * ---------------*************************------------
1185 				 * P2          P1 P3                   P4
1186 				 *
1187 				 *
1188 				 * SPLIT:
1189 				 * ---------------*************************------------
1190 				 * CUT:
1191 				 * ----------------------------------------------------
1192 				 *
1193 				 */
1194 
1195 				if (cutting) {
1196 					remove_region_internal (current, thawlist);
1197 				}
1198 
1199 				new_regions.push_back (current);
1200 			}
1201 		}
1202 
1203 		in_partition = false;
1204 	}
1205 
1206 	/* keep track of any dead space at end (for pasting into Ripple or Splice mode) */
1207 	samplepos_t wanted_length = end - start;
1208 	_end_space                = wanted_length - _get_extent ().second - _get_extent ().first;
1209 }
1210 
1211 boost::shared_ptr<Playlist>
cut_copy(boost::shared_ptr<Playlist> (Playlist::* pmf)(samplepos_t,samplecnt_t,bool),list<AudioRange> & ranges,bool result_is_hidden)1212 Playlist::cut_copy (boost::shared_ptr<Playlist> (Playlist::*pmf) (samplepos_t, samplecnt_t, bool), list<AudioRange>& ranges, bool result_is_hidden)
1213 {
1214 	boost::shared_ptr<Playlist> ret;
1215 	boost::shared_ptr<Playlist> pl;
1216 	samplepos_t                 start;
1217 
1218 	if (ranges.empty ()) {
1219 		return boost::shared_ptr<Playlist> ();
1220 	}
1221 
1222 	start = ranges.front ().start;
1223 
1224 	for (list<AudioRange>::iterator i = ranges.begin (); i != ranges.end (); ++i) {
1225 		pl = (this->*pmf) ((*i).start, (*i).length (), result_is_hidden);
1226 
1227 		if (i == ranges.begin ()) {
1228 			ret = pl;
1229 		} else {
1230 			/* paste the next section into the nascent playlist,
1231 			 * offset to reflect the start of the first range we
1232 			 * chopped.
1233 			 */
1234 
1235 			ret->paste (pl, (*i).start - start, 1.0f, 0);
1236 		}
1237 	}
1238 
1239 	return ret;
1240 }
1241 
1242 boost::shared_ptr<Playlist>
cut(list<AudioRange> & ranges,bool result_is_hidden)1243 Playlist::cut (list<AudioRange>& ranges, bool result_is_hidden)
1244 {
1245 	boost::shared_ptr<Playlist> (Playlist::*pmf) (samplepos_t, samplecnt_t, bool) = &Playlist::cut;
1246 	return cut_copy (pmf, ranges, result_is_hidden);
1247 }
1248 
1249 boost::shared_ptr<Playlist>
copy(list<AudioRange> & ranges,bool result_is_hidden)1250 Playlist::copy (list<AudioRange>& ranges, bool result_is_hidden)
1251 {
1252 	boost::shared_ptr<Playlist> (Playlist::*pmf) (samplepos_t, samplecnt_t, bool) = &Playlist::copy;
1253 	return cut_copy (pmf, ranges, result_is_hidden);
1254 }
1255 
1256 boost::shared_ptr<Playlist>
cut(samplepos_t start,samplecnt_t cnt,bool result_is_hidden)1257 Playlist::cut (samplepos_t start, samplecnt_t cnt, bool result_is_hidden)
1258 {
1259 	boost::shared_ptr<Playlist> the_copy;
1260 	char                        buf[32];
1261 
1262 	snprintf (buf, sizeof (buf), "%" PRIu32, ++subcnt);
1263 	string new_name = _name;
1264 	new_name += '.';
1265 	new_name += buf;
1266 
1267 	if ((the_copy = PlaylistFactory::create (shared_from_this (), start, cnt, new_name, result_is_hidden)) == 0) {
1268 		return boost::shared_ptr<Playlist> ();
1269 	}
1270 
1271 	{
1272 		RegionWriteLock rlock (this);
1273 		partition_internal (start, start + cnt - 1, true, rlock.thawlist);
1274 	}
1275 
1276 	return the_copy;
1277 }
1278 
1279 boost::shared_ptr<Playlist>
copy(samplepos_t start,samplecnt_t cnt,bool result_is_hidden)1280 Playlist::copy (samplepos_t start, samplecnt_t cnt, bool result_is_hidden)
1281 {
1282 	char buf[32];
1283 
1284 	snprintf (buf, sizeof (buf), "%" PRIu32, ++subcnt);
1285 	string new_name = _name;
1286 	new_name += '.';
1287 	new_name += buf;
1288 
1289 	// cnt = min (_get_extent().second - start, cnt);  (We need the full range length when copy/pasting in Ripple.  Why was this limit here?  It's not in CUT... )
1290 
1291 	return PlaylistFactory::create (shared_from_this (), start, cnt, new_name, result_is_hidden);
1292 }
1293 
1294 int
paste(boost::shared_ptr<Playlist> other,samplepos_t position,float times,const int32_t sub_num)1295 Playlist::paste (boost::shared_ptr<Playlist> other, samplepos_t position, float times, const int32_t sub_num)
1296 {
1297 	times = fabs (times);
1298 
1299 	{
1300 		RegionReadLock rl2 (other.get ());
1301 
1302 		int               itimes = (int)floor (times);
1303 		samplepos_t       pos    = position;
1304 		samplecnt_t const shift  = other->_get_extent ().second;
1305 		layer_t           top    = top_layer ();
1306 
1307 		{
1308 			RegionWriteLock rl1 (this);
1309 			while (itimes--) {
1310 				for (RegionList::iterator i = other->regions.begin (); i != other->regions.end (); ++i) {
1311 					boost::shared_ptr<Region> copy_of_region = RegionFactory::create (*i, true, false, &rl1.thawlist);
1312 
1313 					/* put these new regions on top of all existing ones, but preserve
1314 					   the ordering they had in the original playlist.
1315 					*/
1316 
1317 					add_region_internal (copy_of_region, (*i)->position () + pos, rl1.thawlist, sub_num);
1318 					set_layer (copy_of_region, copy_of_region->layer () + top);
1319 				}
1320 				pos += shift;
1321 			}
1322 		}
1323 	}
1324 
1325 	return 0;
1326 }
1327 
1328 void
duplicate(boost::shared_ptr<Region> region,samplepos_t position,float times)1329 Playlist::duplicate (boost::shared_ptr<Region> region, samplepos_t position, float times)
1330 {
1331 	duplicate (region, position, region->length (), times);
1332 }
1333 
1334 /** @param gap from the beginning of the region to the next beginning */
1335 void
duplicate(boost::shared_ptr<Region> region,samplepos_t position,samplecnt_t gap,float times)1336 Playlist::duplicate (boost::shared_ptr<Region> region, samplepos_t position, samplecnt_t gap, float times)
1337 {
1338 	times = fabs (times);
1339 
1340 	RegionWriteLock rl (this);
1341 	int             itimes = (int)floor (times);
1342 
1343 	while (itimes--) {
1344 		boost::shared_ptr<Region> copy = RegionFactory::create (region, true, false, &rl.thawlist);
1345 		add_region_internal (copy, position, rl.thawlist);
1346 		set_layer (copy, DBL_MAX);
1347 		position += gap;
1348 	}
1349 
1350 	if (floor (times) != times) {
1351 		samplecnt_t length = (samplecnt_t)floor (region->length () * (times - floor (times)));
1352 		string      name;
1353 		RegionFactory::region_name (name, region->name (), false);
1354 
1355 		{
1356 			PropertyList plist;
1357 
1358 			plist.add (Properties::start, region->start ());
1359 			plist.add (Properties::length, length);
1360 			plist.add (Properties::name, name);
1361 
1362 			boost::shared_ptr<Region> sub = RegionFactory::create (region, plist, true, &rl.thawlist);
1363 			add_region_internal (sub, position, rl.thawlist);
1364 			set_layer (sub, DBL_MAX);
1365 		}
1366 	}
1367 }
1368 
1369 /** @param gap from the beginning of the region to the next beginning */
1370 /** @param end the first sample that does _not_ contain a duplicated sample */
1371 void
duplicate_until(boost::shared_ptr<Region> region,samplepos_t position,samplecnt_t gap,samplepos_t end)1372 Playlist::duplicate_until (boost::shared_ptr<Region> region, samplepos_t position, samplecnt_t gap, samplepos_t end)
1373 {
1374 	RegionWriteLock rl (this);
1375 
1376 	while (position + region->length () - 1 < end) {
1377 		boost::shared_ptr<Region> copy = RegionFactory::create (region, true, false, &rl.thawlist);
1378 		add_region_internal (copy, position, rl.thawlist);
1379 		set_layer (copy, DBL_MAX);
1380 		position += gap;
1381 	}
1382 
1383 	if (position < end) {
1384 		samplecnt_t length = min (region->length (), end - position);
1385 		string      name;
1386 		RegionFactory::region_name (name, region->name (), false);
1387 
1388 		{
1389 			PropertyList plist;
1390 
1391 			plist.add (Properties::start, region->start ());
1392 			plist.add (Properties::length, length);
1393 			plist.add (Properties::name, name);
1394 
1395 			boost::shared_ptr<Region> sub = RegionFactory::create (region, plist, false, &rl.thawlist);
1396 			add_region_internal (sub, position, rl.thawlist);
1397 			set_layer (sub, DBL_MAX);
1398 		}
1399 	}
1400 }
1401 
1402 void
duplicate_range(AudioRange & range,float times)1403 Playlist::duplicate_range (AudioRange& range, float times)
1404 {
1405 	boost::shared_ptr<Playlist> pl     = copy (range.start, range.length (), true);
1406 	samplecnt_t                 offset = range.end - range.start;
1407 	paste (pl, range.start + offset, times, 0);
1408 }
1409 
1410 void
duplicate_ranges(std::list<AudioRange> & ranges,float times)1411 Playlist::duplicate_ranges (std::list<AudioRange>& ranges, float times)
1412 {
1413 	if (ranges.empty ()) {
1414 		return;
1415 	}
1416 
1417 	samplepos_t min_pos = max_samplepos;
1418 	samplepos_t max_pos = 0;
1419 
1420 	for (std::list<AudioRange>::const_iterator i = ranges.begin ();
1421 	     i != ranges.end ();
1422 	     ++i) {
1423 		min_pos = min (min_pos, (*i).start);
1424 		max_pos = max (max_pos, (*i).end);
1425 	}
1426 
1427 	samplecnt_t offset = max_pos - min_pos;
1428 
1429 	int count  = 1;
1430 	int itimes = (int)floor (times);
1431 	while (itimes--) {
1432 		for (list<AudioRange>::iterator i = ranges.begin (); i != ranges.end (); ++i) {
1433 			boost::shared_ptr<Playlist> pl = copy ((*i).start, (*i).length (), true);
1434 			paste (pl, (*i).start + (offset * count), 1.0f, 0);
1435 		}
1436 		++count;
1437 	}
1438 }
1439 
1440 void
shift(samplepos_t at,sampleoffset_t distance,bool move_intersected,bool ignore_music_glue)1441 Playlist::shift (samplepos_t at, sampleoffset_t distance, bool move_intersected, bool ignore_music_glue)
1442 {
1443 	PBD::Unwinder<bool> uw (_playlist_shift_active, true);
1444 	RegionWriteLock     rlock (this);
1445 	RegionList          copy (regions.rlist ());
1446 	RegionList          fixup;
1447 
1448 	for (RegionList::iterator r = copy.begin (); r != copy.end (); ++r) {
1449 		if ((*r)->last_sample () < at) {
1450 			/* too early */
1451 			continue;
1452 		}
1453 
1454 		if (at > (*r)->first_sample () && at < (*r)->last_sample ()) {
1455 			/* intersected region */
1456 			if (!move_intersected) {
1457 				continue;
1458 			}
1459 		}
1460 
1461 		/* do not move regions glued to music time - that
1462 		 * has to be done separately.
1463 		 */
1464 
1465 		if (!ignore_music_glue && (*r)->position_lock_style () != AudioTime) {
1466 			fixup.push_back (*r);
1467 			continue;
1468 		}
1469 
1470 		rlock.thawlist.add (*r);
1471 		(*r)->set_position ((*r)->position () + distance);
1472 	}
1473 
1474 	/* XXX: may not be necessary; Region::post_set should do this, I think */
1475 	for (RegionList::iterator r = fixup.begin (); r != fixup.end (); ++r) {
1476 		(*r)->recompute_position_from_lock_style (0);
1477 	}
1478 }
1479 
1480 void
split(const MusicSample & at)1481 Playlist::split (const MusicSample& at)
1482 {
1483 	RegionWriteLock rlock (this);
1484 	RegionList      copy (regions.rlist ());
1485 
1486 	/* use a copy since this operation can modify the region list */
1487 
1488 	for (RegionList::iterator r = copy.begin (); r != copy.end (); ++r) {
1489 		_split_region (*r, at, rlock.thawlist);
1490 	}
1491 }
1492 
1493 void
split_region(boost::shared_ptr<Region> region,const MusicSample & playlist_position)1494 Playlist::split_region (boost::shared_ptr<Region> region, const MusicSample& playlist_position)
1495 {
1496 	RegionWriteLock rl (this);
1497 	_split_region (region, playlist_position, rl.thawlist);
1498 }
1499 
1500 void
_split_region(boost::shared_ptr<Region> region,const MusicSample & playlist_position,ThawList & thawlist)1501 Playlist::_split_region (boost::shared_ptr<Region> region, const MusicSample& playlist_position, ThawList& thawlist)
1502 {
1503 	if (!region->covers (playlist_position.sample)) {
1504 		return;
1505 	}
1506 
1507 	if (region->position () == playlist_position.sample ||
1508 	    region->last_sample () == playlist_position.sample) {
1509 		return;
1510 	}
1511 
1512 	boost::shared_ptr<Region> left;
1513 	boost::shared_ptr<Region> right;
1514 
1515 	MusicSample before (playlist_position.sample - region->position (), playlist_position.division);
1516 	MusicSample after (region->length () - before.sample, 0);
1517 	string      before_name;
1518 	string      after_name;
1519 
1520 	/* split doesn't change anything about length, so don't try to splice */
1521 
1522 	bool old_sp = _splicing;
1523 	_splicing   = true;
1524 
1525 	RegionFactory::region_name (before_name, region->name (), false);
1526 
1527 	{
1528 		PropertyList plist;
1529 
1530 		plist.add (Properties::length, before.sample);
1531 		plist.add (Properties::name, before_name);
1532 		plist.add (Properties::left_of_split, true);
1533 		plist.add (Properties::layering_index, region->layering_index ());
1534 		plist.add (Properties::layer, region->layer ());
1535 
1536 		/* note: we must use the version of ::create with an offset here,
1537 		 * since it supplies that offset to the Region constructor, which
1538 		 * is necessary to get audio region gain envelopes right.
1539 		 */
1540 		left = RegionFactory::create (region, MusicSample (0, 0), plist, true, &thawlist);
1541 	}
1542 
1543 	RegionFactory::region_name (after_name, region->name (), false);
1544 
1545 	{
1546 		PropertyList plist;
1547 
1548 		plist.add (Properties::length, after.sample);
1549 		plist.add (Properties::name, after_name);
1550 		plist.add (Properties::right_of_split, true);
1551 		plist.add (Properties::layering_index, region->layering_index ());
1552 		plist.add (Properties::layer, region->layer ());
1553 
1554 		/* same note as above */
1555 		right = RegionFactory::create (region, before, plist, true, &thawlist);
1556 	}
1557 
1558 	add_region_internal (left, region->position (), thawlist, 0);
1559 	add_region_internal (right, region->position () + before.sample, thawlist, before.division);
1560 
1561 	remove_region_internal (region, thawlist);
1562 
1563 	_splicing = old_sp;
1564 }
1565 
1566 void
AddToSoloSelectedList(const Region * r)1567 Playlist::AddToSoloSelectedList (const Region* r)
1568 {
1569 	_soloSelectedRegions.insert (r);
1570 }
1571 
1572 void
RemoveFromSoloSelectedList(const Region * r)1573 Playlist::RemoveFromSoloSelectedList (const Region* r)
1574 {
1575 	_soloSelectedRegions.erase (r);
1576 }
1577 
1578 bool
SoloSelectedListIncludes(const Region * r)1579 Playlist::SoloSelectedListIncludes (const Region* r)
1580 {
1581 	std::set<const Region*>::iterator i = _soloSelectedRegions.find (r);
1582 
1583 	return (i != _soloSelectedRegions.end ());
1584 }
1585 
1586 bool
SoloSelectedActive()1587 Playlist::SoloSelectedActive ()
1588 {
1589 	return !_soloSelectedRegions.empty ();
1590 }
1591 
1592 void
possibly_splice(samplepos_t at,samplecnt_t distance,boost::shared_ptr<Region> exclude)1593 Playlist::possibly_splice (samplepos_t at, samplecnt_t distance, boost::shared_ptr<Region> exclude)
1594 {
1595 	if (_splicing || in_set_state) {
1596 		/* don't respond to splicing moves or state setting */
1597 		return;
1598 	}
1599 
1600 	if (_edit_mode == Splice) {
1601 		splice_locked (at, distance, exclude);
1602 	}
1603 }
1604 
1605 void
possibly_splice_unlocked(samplepos_t at,samplecnt_t distance,boost::shared_ptr<Region> exclude,ThawList & thawlist)1606 Playlist::possibly_splice_unlocked (samplepos_t at, samplecnt_t distance, boost::shared_ptr<Region> exclude, ThawList& thawlist)
1607 {
1608 	if (_splicing || in_set_state) {
1609 		/* don't respond to splicing moves or state setting */
1610 		return;
1611 	}
1612 
1613 	if (_edit_mode == Splice) {
1614 		splice_unlocked (at, distance, exclude, thawlist);
1615 	}
1616 }
1617 
1618 void
splice_locked(samplepos_t at,samplecnt_t distance,boost::shared_ptr<Region> exclude)1619 Playlist::splice_locked (samplepos_t at, samplecnt_t distance, boost::shared_ptr<Region> exclude)
1620 {
1621 	RegionWriteLock rl (this);
1622 	splice_unlocked (at, distance, exclude, rl.thawlist);
1623 }
1624 
1625 void
splice_unlocked(samplepos_t at,samplecnt_t distance,boost::shared_ptr<Region> exclude,ThawList & thawlist)1626 Playlist::splice_unlocked (samplepos_t at, samplecnt_t distance, boost::shared_ptr<Region> exclude, ThawList& thawlist)
1627 {
1628 	_splicing = true;
1629 
1630 	for (RegionList::iterator i = regions.begin (); i != regions.end (); ++i) {
1631 		if (exclude && (*i) == exclude) {
1632 			continue;
1633 		}
1634 
1635 		if ((*i)->position () >= at) {
1636 			samplepos_t new_pos = (*i)->position () + distance;
1637 			if (new_pos < 0) {
1638 				new_pos = 0;
1639 			} else if (new_pos >= max_samplepos - (*i)->length ()) {
1640 				new_pos = max_samplepos - (*i)->length ();
1641 			}
1642 
1643 			thawlist.add (*i);
1644 			(*i)->set_position (new_pos);
1645 		}
1646 	}
1647 
1648 	_splicing = false;
1649 
1650 	notify_contents_changed ();
1651 }
1652 
1653 void
ripple_locked(samplepos_t at,samplecnt_t distance,RegionList * exclude)1654 Playlist::ripple_locked (samplepos_t at, samplecnt_t distance, RegionList* exclude)
1655 {
1656 	RegionWriteLock rl (this);
1657 	ripple_unlocked (at, distance, exclude, rl.thawlist);
1658 }
1659 
1660 void
ripple_unlocked(samplepos_t at,samplecnt_t distance,RegionList * exclude,ThawList & thawlist,bool notify)1661 Playlist::ripple_unlocked (samplepos_t at, samplecnt_t distance, RegionList* exclude, ThawList& thawlist, bool notify)
1662 {
1663 	if (distance == 0) {
1664 		return;
1665 	}
1666 
1667 	_rippling               = true;
1668 	RegionListProperty copy = regions;
1669 	for (RegionList::iterator i = copy.begin (); i != copy.end (); ++i) {
1670 		assert (i != copy.end ());
1671 
1672 		if (exclude) {
1673 			if (std::find (exclude->begin (), exclude->end (), (*i)) != exclude->end ()) {
1674 				continue;
1675 			}
1676 		}
1677 
1678 		if ((*i)->position () >= at) {
1679 			samplepos_t new_pos = (*i)->position () + distance;
1680 			samplepos_t limit   = max_samplepos - (*i)->length ();
1681 			if (new_pos < 0) {
1682 				new_pos = 0;
1683 			} else if (new_pos >= limit) {
1684 				new_pos = limit;
1685 			}
1686 
1687 			thawlist.add (*i);
1688 			(*i)->set_position (new_pos);
1689 		}
1690 	}
1691 
1692 	_rippling = false;
1693 
1694 	if (notify) {
1695 		notify_contents_changed ();
1696 	}
1697 }
1698 
1699 void
region_bounds_changed(const PropertyChange & what_changed,boost::shared_ptr<Region> region)1700 Playlist::region_bounds_changed (const PropertyChange& what_changed, boost::shared_ptr<Region> region)
1701 {
1702 	if (in_set_state || _splicing || _rippling || _nudging || _shuffling) {
1703 		return;
1704 	}
1705 
1706 	if (what_changed.contains (Properties::position)) {
1707 		/* remove it from the list then add it back in
1708 		 * the right place again.
1709 		 */
1710 
1711 		RegionSortByPosition cmp;
1712 
1713 		RegionList::iterator i = find (regions.begin (), regions.end (), region);
1714 
1715 		if (i == regions.end ()) {
1716 			/* the region bounds are being modified but its not currently
1717 			 * in the region list. we will use its bounds correctly when/if
1718 			 * it is added
1719 			 */
1720 			return;
1721 		}
1722 
1723 		regions.erase (i);
1724 		regions.insert (upper_bound (regions.begin (), regions.end (), region, cmp), region);
1725 	}
1726 
1727 	if (what_changed.contains (Properties::position) || what_changed.contains (Properties::length)) {
1728 		sampleoffset_t delta = 0;
1729 
1730 		if (what_changed.contains (Properties::position)) {
1731 			delta = region->position () - region->last_position ();
1732 		}
1733 
1734 		if (what_changed.contains (Properties::length)) {
1735 			delta += region->length () - region->last_length ();
1736 		}
1737 
1738 		if (delta) {
1739 			possibly_splice (region->last_position () + region->last_length (), delta, region);
1740 		}
1741 
1742 		if (holding_state ()) {
1743 			pending_bounds.push_back (region);
1744 		} else {
1745 			notify_contents_changed ();
1746 			relayer ();
1747 			list<Evoral::Range<samplepos_t> > xf;
1748 			xf.push_back (Evoral::Range<samplepos_t> (region->last_range ()));
1749 			xf.push_back (Evoral::Range<samplepos_t> (region->range ()));
1750 			coalesce_and_check_crossfades (xf);
1751 		}
1752 	}
1753 }
1754 
1755 void
region_changed_proxy(const PropertyChange & what_changed,boost::weak_ptr<Region> weak_region)1756 Playlist::region_changed_proxy (const PropertyChange& what_changed, boost::weak_ptr<Region> weak_region)
1757 {
1758 	boost::shared_ptr<Region> region (weak_region.lock ());
1759 
1760 	if (!region) {
1761 		return;
1762 	}
1763 
1764 	/* this makes a virtual call to the right kind of playlist ... */
1765 
1766 	region_changed (what_changed, region);
1767 }
1768 
1769 bool
region_changed(const PropertyChange & what_changed,boost::shared_ptr<Region> region)1770 Playlist::region_changed (const PropertyChange& what_changed, boost::shared_ptr<Region> region)
1771 {
1772 	PropertyChange our_interests;
1773 	PropertyChange bounds;
1774 	bool           save = false;
1775 
1776 	if (in_set_state || in_flush) {
1777 		return false;
1778 	}
1779 
1780 	our_interests.add (Properties::muted);
1781 	our_interests.add (Properties::layer);
1782 	our_interests.add (Properties::opaque);
1783 	our_interests.add (Properties::contents);
1784 
1785 	bounds.add (Properties::start);
1786 	bounds.add (Properties::position);
1787 	bounds.add (Properties::length);
1788 
1789 	bool send_contents = false;
1790 
1791 	if (what_changed.contains (bounds)) {
1792 		region_bounds_changed (what_changed, region);
1793 		save          = !(_splicing || _nudging);
1794 		send_contents = true;
1795 	}
1796 
1797 	if (what_changed.contains (Properties::contents)) {
1798 		send_contents = true;
1799 	}
1800 
1801 	if (what_changed.contains (Properties::position) && !what_changed.contains (Properties::length)) {
1802 		notify_region_moved (region);
1803 	} else if (!what_changed.contains (Properties::position) && what_changed.contains (Properties::length)) {
1804 		notify_region_end_trimmed (region);
1805 	} else if (what_changed.contains (Properties::position) && what_changed.contains (Properties::length)) {
1806 		notify_region_start_trimmed (region);
1807 	}
1808 
1809 	/* don't notify about layer changes, since we are the only object that can initiate
1810 	 * them, and we notify in ::relayer()
1811 	 */
1812 
1813 	if (what_changed.contains (our_interests)) {
1814 		save = true;
1815 	}
1816 
1817 	if (send_contents || save) {
1818 		notify_contents_changed ();
1819 	}
1820 
1821 	mark_session_dirty ();
1822 
1823 	return save;
1824 }
1825 
1826 void
drop_regions()1827 Playlist::drop_regions ()
1828 {
1829 	RegionWriteLock rl (this);
1830 	regions.clear ();
1831 	all_regions.clear ();
1832 }
1833 
1834 void
sync_all_regions_with_regions()1835 Playlist::sync_all_regions_with_regions ()
1836 {
1837 	RegionWriteLock rl (this);
1838 
1839 	all_regions.clear ();
1840 
1841 	for (RegionList::iterator i = regions.begin (); i != regions.end (); ++i) {
1842 		all_regions.insert (*i);
1843 	}
1844 }
1845 
1846 void
clear(bool with_signals)1847 Playlist::clear (bool with_signals)
1848 {
1849 	{
1850 		RegionWriteLock rl (this);
1851 
1852 		region_state_changed_connections.drop_connections ();
1853 		region_drop_references_connections.drop_connections ();
1854 
1855 		for (RegionList::iterator i = regions.begin (); i != regions.end (); ++i) {
1856 			pending_removes.insert (*i);
1857 		}
1858 
1859 		regions.clear ();
1860 
1861 		for (set<boost::shared_ptr<Region> >::iterator s = pending_removes.begin (); s != pending_removes.end (); ++s) {
1862 			remove_dependents (*s);
1863 		}
1864 	}
1865 
1866 	if (with_signals) {
1867 		for (set<boost::shared_ptr<Region> >::iterator s = pending_removes.begin (); s != pending_removes.end (); ++s) {
1868 			RegionRemoved (boost::weak_ptr<Region> (*s)); /* EMIT SIGNAL */
1869 		}
1870 
1871 		pending_removes.clear ();
1872 		pending_contents_change = false;
1873 		ContentsChanged ();
1874 	}
1875 }
1876 
1877 /* *********************************************************************
1878 FINDING THINGS
1879 **********************************************************************/
1880 
1881 boost::shared_ptr<RegionList>
region_list()1882 Playlist::region_list ()
1883 {
1884 	RegionReadLock                rlock (this);
1885 	boost::shared_ptr<RegionList> rlist (new RegionList (regions.rlist ()));
1886 	return rlist;
1887 }
1888 
1889 void
deep_sources(std::set<boost::shared_ptr<Source>> & sources) const1890 Playlist::deep_sources (std::set<boost::shared_ptr<Source> >& sources) const
1891 {
1892 	RegionReadLock rlock (const_cast<Playlist*> (this));
1893 
1894 	for (RegionList::const_iterator i = regions.begin (); i != regions.end (); ++i) {
1895 		(*i)->deep_sources (sources);
1896 	}
1897 }
1898 
1899 boost::shared_ptr<RegionList>
regions_at(samplepos_t sample)1900 Playlist::regions_at (samplepos_t sample)
1901 {
1902 	RegionReadLock rlock (this);
1903 	return find_regions_at (sample);
1904 }
1905 
1906 uint32_t
count_regions_at(samplepos_t sample) const1907 Playlist::count_regions_at (samplepos_t sample) const
1908 {
1909 	RegionReadLock rlock (const_cast<Playlist*> (this));
1910 	uint32_t       cnt = 0;
1911 
1912 	for (RegionList::const_iterator i = regions.begin (); i != regions.end (); ++i) {
1913 		if ((*i)->covers (sample)) {
1914 			cnt++;
1915 		}
1916 	}
1917 
1918 	return cnt;
1919 }
1920 
1921 boost::shared_ptr<Region>
top_region_at(samplepos_t sample)1922 Playlist::top_region_at (samplepos_t sample)
1923 {
1924 	RegionReadLock                rlock (this);
1925 	boost::shared_ptr<RegionList> rlist = find_regions_at (sample);
1926 	boost::shared_ptr<Region>     region;
1927 
1928 	if (rlist->size ()) {
1929 		RegionSortByLayer cmp;
1930 		rlist->sort (cmp);
1931 		region = rlist->back ();
1932 	}
1933 
1934 	return region;
1935 }
1936 
1937 boost::shared_ptr<Region>
top_unmuted_region_at(samplepos_t sample)1938 Playlist::top_unmuted_region_at (samplepos_t sample)
1939 {
1940 	RegionReadLock                rlock (this);
1941 	boost::shared_ptr<RegionList> rlist = find_regions_at (sample);
1942 
1943 	for (RegionList::iterator i = rlist->begin (); i != rlist->end ();) {
1944 		RegionList::iterator tmp = i;
1945 
1946 		++tmp;
1947 
1948 		if ((*i)->muted ()) {
1949 			rlist->erase (i);
1950 		}
1951 
1952 		i = tmp;
1953 	}
1954 
1955 	boost::shared_ptr<Region> region;
1956 
1957 	if (rlist->size ()) {
1958 		RegionSortByLayer cmp;
1959 		rlist->sort (cmp);
1960 		region = rlist->back ();
1961 	}
1962 
1963 	return region;
1964 }
1965 
1966 boost::shared_ptr<RegionList>
find_regions_at(samplepos_t sample)1967 Playlist::find_regions_at (samplepos_t sample)
1968 {
1969 	/* Caller must hold lock */
1970 
1971 	boost::shared_ptr<RegionList> rlist (new RegionList);
1972 
1973 	for (RegionList::iterator i = regions.begin (); i != regions.end (); ++i) {
1974 		if ((*i)->covers (sample)) {
1975 			rlist->push_back (*i);
1976 		}
1977 	}
1978 
1979 	return rlist;
1980 }
1981 
1982 boost::shared_ptr<RegionList>
regions_with_start_within(Evoral::Range<samplepos_t> range)1983 Playlist::regions_with_start_within (Evoral::Range<samplepos_t> range)
1984 {
1985 	RegionReadLock                rlock (this);
1986 	boost::shared_ptr<RegionList> rlist (new RegionList);
1987 
1988 	for (RegionList::iterator i = regions.begin (); i != regions.end (); ++i) {
1989 		if ((*i)->first_sample () >= range.from && (*i)->first_sample () <= range.to) {
1990 			rlist->push_back (*i);
1991 		}
1992 	}
1993 
1994 	return rlist;
1995 }
1996 
1997 boost::shared_ptr<RegionList>
regions_with_end_within(Evoral::Range<samplepos_t> range)1998 Playlist::regions_with_end_within (Evoral::Range<samplepos_t> range)
1999 {
2000 	RegionReadLock                rlock (this);
2001 	boost::shared_ptr<RegionList> rlist (new RegionList);
2002 
2003 	for (RegionList::iterator i = regions.begin (); i != regions.end (); ++i) {
2004 		if ((*i)->last_sample () >= range.from && (*i)->last_sample () <= range.to) {
2005 			rlist->push_back (*i);
2006 		}
2007 	}
2008 
2009 	return rlist;
2010 }
2011 
2012 boost::shared_ptr<RegionList>
regions_touched(samplepos_t start,samplepos_t end)2013 Playlist::regions_touched (samplepos_t start, samplepos_t end)
2014 {
2015 	RegionReadLock rlock (this);
2016 	return regions_touched_locked (start, end);
2017 }
2018 
2019 boost::shared_ptr<RegionList>
regions_touched_locked(samplepos_t start,samplepos_t end)2020 Playlist::regions_touched_locked (samplepos_t start, samplepos_t end)
2021 {
2022 	boost::shared_ptr<RegionList> rlist (new RegionList);
2023 
2024 	for (RegionList::iterator i = regions.begin (); i != regions.end (); ++i) {
2025 		if ((*i)->coverage (start, end) != Evoral::OverlapNone) {
2026 			rlist->push_back (*i);
2027 		}
2028 	}
2029 
2030 	return rlist;
2031 }
2032 
2033 samplepos_t
find_next_transient(samplepos_t from,int dir)2034 Playlist::find_next_transient (samplepos_t from, int dir)
2035 {
2036 	RegionReadLock      rlock (this);
2037 	AnalysisFeatureList points;
2038 	AnalysisFeatureList these_points;
2039 
2040 	for (RegionList::iterator i = regions.begin (); i != regions.end (); ++i) {
2041 		if (dir > 0) {
2042 			if ((*i)->last_sample () < from) {
2043 				continue;
2044 			}
2045 		} else {
2046 			if ((*i)->first_sample () > from) {
2047 				continue;
2048 			}
2049 		}
2050 
2051 		(*i)->get_transients (these_points);
2052 
2053 		/* add first sample, just, err, because */
2054 
2055 		these_points.push_back ((*i)->first_sample ());
2056 
2057 		points.insert (points.end (), these_points.begin (), these_points.end ());
2058 		these_points.clear ();
2059 	}
2060 
2061 	if (points.empty ()) {
2062 		return -1;
2063 	}
2064 
2065 	TransientDetector::cleanup_transients (points, _session.sample_rate (), 3.0);
2066 	bool reached = false;
2067 
2068 	if (dir > 0) {
2069 		for (AnalysisFeatureList::const_iterator x = points.begin (); x != points.end (); ++x) {
2070 			if ((*x) >= from) {
2071 				reached = true;
2072 			}
2073 
2074 			if (reached && (*x) > from) {
2075 				return *x;
2076 			}
2077 		}
2078 	} else {
2079 		for (AnalysisFeatureList::reverse_iterator x = points.rbegin (); x != points.rend (); ++x) {
2080 			if ((*x) <= from) {
2081 				reached = true;
2082 			}
2083 
2084 			if (reached && (*x) < from) {
2085 				return *x;
2086 			}
2087 		}
2088 	}
2089 
2090 	return -1;
2091 }
2092 
2093 boost::shared_ptr<Region>
find_next_region(samplepos_t sample,RegionPoint point,int dir)2094 Playlist::find_next_region (samplepos_t sample, RegionPoint point, int dir)
2095 {
2096 	RegionReadLock            rlock (this);
2097 	boost::shared_ptr<Region> ret;
2098 	samplepos_t               closest = max_samplepos;
2099 
2100 	bool end_iter = false;
2101 
2102 	for (RegionList::iterator i = regions.begin (); i != regions.end (); ++i) {
2103 		if (end_iter)
2104 			break;
2105 
2106 		sampleoffset_t            distance;
2107 		boost::shared_ptr<Region> r   = (*i);
2108 		samplepos_t               pos = 0;
2109 
2110 		switch (point) {
2111 			case Start:
2112 				pos = r->first_sample ();
2113 				break;
2114 			case End:
2115 				pos = r->last_sample ();
2116 				break;
2117 			case SyncPoint:
2118 				pos = r->sync_position ();
2119 				break;
2120 		}
2121 
2122 		switch (dir) {
2123 			case 1: /* forwards */
2124 
2125 				if (pos > sample) {
2126 					if ((distance = pos - sample) < closest) {
2127 						closest  = distance;
2128 						ret      = r;
2129 						end_iter = true;
2130 					}
2131 				}
2132 
2133 				break;
2134 
2135 			default: /* backwards */
2136 
2137 				if (pos < sample) {
2138 					if ((distance = sample - pos) < closest) {
2139 						closest = distance;
2140 						ret     = r;
2141 					}
2142 				} else {
2143 					end_iter = true;
2144 				}
2145 
2146 				break;
2147 		}
2148 	}
2149 
2150 	return ret;
2151 }
2152 
2153 samplepos_t
find_next_region_boundary(samplepos_t sample,int dir)2154 Playlist::find_next_region_boundary (samplepos_t sample, int dir)
2155 {
2156 	RegionReadLock rlock (this);
2157 
2158 	samplepos_t closest = max_samplepos;
2159 	samplepos_t ret     = -1;
2160 
2161 	if (dir > 0) {
2162 		for (RegionList::iterator i = regions.begin (); i != regions.end (); ++i) {
2163 			boost::shared_ptr<Region> r = (*i);
2164 			sampleoffset_t            distance;
2165 			const samplepos_t         first_sample = r->first_sample ();
2166 			const samplepos_t         last_sample  = r->last_sample ();
2167 
2168 			if (first_sample > sample) {
2169 				distance = first_sample - sample;
2170 
2171 				if (distance < closest) {
2172 					ret     = first_sample;
2173 					closest = distance;
2174 				}
2175 			}
2176 
2177 			if (last_sample > sample) {
2178 				distance = last_sample - sample;
2179 
2180 				if (distance < closest) {
2181 					ret     = last_sample;
2182 					closest = distance;
2183 				}
2184 			}
2185 		}
2186 
2187 	} else {
2188 		for (RegionList::reverse_iterator i = regions.rbegin (); i != regions.rend (); ++i) {
2189 			boost::shared_ptr<Region> r = (*i);
2190 			sampleoffset_t            distance;
2191 			const samplepos_t         first_sample = r->first_sample ();
2192 			const samplepos_t         last_sample  = r->last_sample ();
2193 
2194 			if (last_sample < sample) {
2195 				distance = sample - last_sample;
2196 
2197 				if (distance < closest) {
2198 					ret     = last_sample;
2199 					closest = distance;
2200 				}
2201 			}
2202 
2203 			if (first_sample < sample) {
2204 				distance = sample - first_sample;
2205 
2206 				if (distance < closest) {
2207 					ret     = first_sample;
2208 					closest = distance;
2209 				}
2210 			}
2211 		}
2212 	}
2213 
2214 	return ret;
2215 }
2216 
2217 /***********************************************************************/
2218 
2219 void
mark_session_dirty()2220 Playlist::mark_session_dirty ()
2221 {
2222 	_cached_extent.reset ();
2223 
2224 	if (!in_set_state && !holding_state ()) {
2225 		_session.set_dirty ();
2226 	}
2227 }
2228 
2229 void
rdiff(vector<Command * > & cmds) const2230 Playlist::rdiff (vector<Command*>& cmds) const
2231 {
2232 	RegionReadLock rlock (const_cast<Playlist*> (this));
2233 	Stateful::rdiff (cmds);
2234 }
2235 
2236 void
clear_owned_changes()2237 Playlist::clear_owned_changes ()
2238 {
2239 	RegionReadLock rlock (this);
2240 	Stateful::clear_owned_changes ();
2241 }
2242 
2243 void
update(const RegionListProperty::ChangeRecord & change)2244 Playlist::update (const RegionListProperty::ChangeRecord& change)
2245 {
2246 	DEBUG_TRACE (DEBUG::Properties, string_compose ("Playlist %1 updates from a change record with %2 adds %3 removes\n",
2247 	                                                name (), change.added.size (), change.removed.size ()));
2248 
2249 	{
2250 		RegionWriteLock rlock (this);
2251 		freeze_locked ();
2252 		/* add the added regions */
2253 		for (RegionListProperty::ChangeContainer::const_iterator i = change.added.begin (); i != change.added.end (); ++i) {
2254 			add_region_internal ((*i), (*i)->position (), rlock.thawlist);
2255 		}
2256 		/* remove the removed regions */
2257 		for (RegionListProperty::ChangeContainer::const_iterator i = change.removed.begin (); i != change.removed.end (); ++i) {
2258 			remove_region_internal (*i, rlock.thawlist);
2259 		}
2260 	}
2261 
2262 	thaw ();
2263 }
2264 
2265 int
set_state(const XMLNode & node,int version)2266 Playlist::set_state (const XMLNode& node, int version)
2267 {
2268 	XMLNode*                  child;
2269 	XMLNodeList               nlist;
2270 	XMLNodeConstIterator      niter;
2271 	boost::shared_ptr<Region> region;
2272 	string                    region_name;
2273 	bool                      seen_region_nodes = false;
2274 	int                       ret               = 0;
2275 
2276 	in_set_state++;
2277 
2278 	if (node.name () != "Playlist") {
2279 		in_set_state--;
2280 		return -1;
2281 	}
2282 
2283 	freeze ();
2284 
2285 	set_id (node);
2286 
2287 	std::string name;
2288 	if (node.get_property (X_("name"), name)) {
2289 		_name = name;
2290 		_set_sort_id ();
2291 	}
2292 
2293 	/* XXX legacy session: fix up later - :: update_orig_2X() */
2294 	node.get_property (X_("orig-diskstream-id"), _orig_track_id);
2295 	node.get_property (X_("orig_diskstream_id"), _orig_track_id);
2296 
2297 	node.get_property (X_("orig-track-id"), _orig_track_id);
2298 	node.get_property (X_("frozen"), _frozen);
2299 
2300 	node.get_property (X_("pgroup-id"), _pgroup_id);
2301 
2302 	node.get_property (X_("combine-ops"), _combine_ops);
2303 
2304 	string shared_ids;
2305 	if (node.get_property (X_("shared-with-ids"), shared_ids)) {
2306 		if (!shared_ids.empty ()) {
2307 			vector<string> result;
2308 			::split (shared_ids, result, ',');
2309 			vector<string>::iterator it = result.begin ();
2310 			for (; it != result.end (); ++it) {
2311 				_shared_with_ids.push_back (PBD::ID (*it));
2312 			}
2313 		}
2314 	}
2315 
2316 	clear (true);
2317 
2318 	nlist = node.children ();
2319 
2320 	for (niter = nlist.begin (); niter != nlist.end (); ++niter) {
2321 		child = *niter;
2322 
2323 		if (child->name () == "Region") {
2324 			seen_region_nodes = true;
2325 
2326 			ID id;
2327 			if (!child->get_property ("id", id)) {
2328 				error << _("region state node has no ID, ignored") << endmsg;
2329 				continue;
2330 			}
2331 
2332 			if ((region = region_by_id (id))) {
2333 				region->suspend_property_changes ();
2334 
2335 				if (region->set_state (*child, version)) {
2336 					region->resume_property_changes ();
2337 					continue;
2338 				}
2339 
2340 			} else if ((region = RegionFactory::create (_session, *child, true)) != 0) {
2341 				region->suspend_property_changes ();
2342 			} else {
2343 				error << _("Playlist: cannot create region from XML") << endmsg;
2344 				return -1;
2345 			}
2346 
2347 			{
2348 				RegionWriteLock rlock (this);
2349 				add_region_internal (region, region->position (), rlock.thawlist);
2350 			}
2351 
2352 			region->resume_property_changes ();
2353 		}
2354 	}
2355 
2356 	if (seen_region_nodes && regions.empty ()) {
2357 		ret = -1;
2358 	}
2359 
2360 	thaw ();
2361 	notify_contents_changed ();
2362 
2363 	in_set_state--;
2364 	first_set_state = false;
2365 
2366 	return ret;
2367 }
2368 
2369 XMLNode&
get_state()2370 Playlist::get_state ()
2371 {
2372 	return state (true);
2373 }
2374 
2375 XMLNode&
get_template()2376 Playlist::get_template ()
2377 {
2378 	return state (false);
2379 }
2380 
2381 /** @param full_state true to include regions in the returned state, otherwise false.
2382  */
2383 XMLNode&
state(bool full_state)2384 Playlist::state (bool full_state)
2385 {
2386 	XMLNode* node = new XMLNode (X_("Playlist"));
2387 
2388 	node->set_property (X_("id"), id ());
2389 	node->set_property (X_("name"), name ());
2390 	node->set_property (X_("type"), _type);
2391 	node->set_property (X_("orig-track-id"), _orig_track_id);
2392 	node->set_property (X_("pgroup-id"), _pgroup_id);
2393 
2394 	string                        shared_ids;
2395 	list<PBD::ID>::const_iterator it = _shared_with_ids.begin ();
2396 	for (; it != _shared_with_ids.end (); ++it) {
2397 		shared_ids += "," + (*it).to_s ();
2398 	}
2399 	if (!shared_ids.empty ()) {
2400 		shared_ids.erase (0, 1);
2401 	}
2402 
2403 	node->set_property (X_("shared-with-ids"), shared_ids);
2404 	node->set_property (X_("frozen"), _frozen);
2405 
2406 	if (full_state) {
2407 		RegionReadLock rlock (this);
2408 
2409 		node->set_property ("combine-ops", _combine_ops);
2410 
2411 		for (RegionList::iterator i = regions.begin (); i != regions.end (); ++i) {
2412 			assert ((*i)->sources ().size () > 0 && (*i)->master_sources ().size () > 0);
2413 			node->add_child_nocopy ((*i)->get_state ());
2414 		}
2415 	}
2416 
2417 	if (_extra_xml) {
2418 		node->add_child_copy (*_extra_xml);
2419 	}
2420 
2421 	return *node;
2422 }
2423 
2424 bool
empty() const2425 Playlist::empty () const
2426 {
2427 	RegionReadLock rlock (const_cast<Playlist*> (this));
2428 	return regions.empty ();
2429 }
2430 
2431 uint32_t
n_regions() const2432 Playlist::n_regions () const
2433 {
2434 	RegionReadLock rlock (const_cast<Playlist*> (this));
2435 	return regions.size ();
2436 }
2437 
2438 /** @return true if the all_regions list is empty, ie this playlist
2439  *  has never had a region added to it.
2440  */
2441 bool
all_regions_empty() const2442 Playlist::all_regions_empty () const
2443 {
2444 	RegionReadLock rl (const_cast<Playlist*> (this));
2445 	return all_regions.empty ();
2446 }
2447 
2448 pair<samplepos_t, samplepos_t>
get_extent() const2449 Playlist::get_extent () const
2450 {
2451 	if (_cached_extent) {
2452 		return _cached_extent.value ();
2453 	}
2454 
2455 	RegionReadLock rlock (const_cast<Playlist*> (this));
2456 	_cached_extent = _get_extent ();
2457 	return _cached_extent.value ();
2458 }
2459 
2460 pair<samplepos_t, samplepos_t>
get_extent_with_endspace() const2461 Playlist::get_extent_with_endspace () const
2462 {
2463 	pair<samplepos_t, samplepos_t> l = get_extent ();
2464 	l.second += _end_space;
2465 	return l;
2466 }
2467 
2468 pair<samplepos_t, samplepos_t>
_get_extent() const2469 Playlist::_get_extent () const
2470 {
2471 	pair<samplepos_t, samplepos_t> ext (max_samplepos, 0);
2472 
2473 	if (regions.empty ()) {
2474 		ext.first = 0;
2475 		return ext;
2476 	}
2477 
2478 	for (RegionList::const_iterator i = regions.begin (); i != regions.end (); ++i) {
2479 		pair<samplepos_t, samplepos_t> const e ((*i)->position (), (*i)->position () + (*i)->length ());
2480 		if (e.first < ext.first) {
2481 			ext.first = e.first;
2482 		}
2483 		if (e.second > ext.second) {
2484 			ext.second = e.second;
2485 		}
2486 	}
2487 
2488 	return ext;
2489 }
2490 
2491 string
bump_name(string name,Session & session)2492 Playlist::bump_name (string name, Session& session)
2493 {
2494 	string newname = name;
2495 
2496 	do {
2497 		newname = bump_name_once (newname, '.');
2498 	} while (session.playlists ()->by_name (newname) != NULL);
2499 
2500 	return newname;
2501 }
2502 
2503 layer_t
top_layer() const2504 Playlist::top_layer () const
2505 {
2506 	RegionReadLock rlock (const_cast<Playlist*> (this));
2507 	layer_t        top = 0;
2508 
2509 	for (RegionList::const_iterator i = regions.begin (); i != regions.end (); ++i) {
2510 		top = max (top, (*i)->layer ());
2511 	}
2512 	return top;
2513 }
2514 
2515 void
set_edit_mode(EditMode mode)2516 Playlist::set_edit_mode (EditMode mode)
2517 {
2518 	_edit_mode = mode;
2519 }
2520 
2521 struct RelayerSort {
operator ()RelayerSort2522 	bool operator() (boost::shared_ptr<Region> a, boost::shared_ptr<Region> b)
2523 	{
2524 		return a->layering_index () < b->layering_index ();
2525 	}
2526 };
2527 
2528 /** Set a new layer for a region.  This adjusts the layering indices of all
2529  *  regions in the playlist to put the specified region in the appropriate
2530  *  place.  The actual layering will be fixed up when relayer() happens.
2531  */
2532 void
set_layer(boost::shared_ptr<Region> region,double new_layer)2533 Playlist::set_layer (boost::shared_ptr<Region> region, double new_layer)
2534 {
2535 	/* Remove the layer we are setting from our region list, and sort it
2536 	 *  using the layer indeces.
2537 	 */
2538 
2539 	RegionList copy = regions.rlist ();
2540 	copy.remove (region);
2541 	copy.sort (RelayerSort ());
2542 
2543 	/* Put region back in the right place */
2544 	RegionList::iterator i = copy.begin ();
2545 	while (i != copy.end ()) {
2546 		if ((*i)->layer () > new_layer) {
2547 			break;
2548 		}
2549 		++i;
2550 	}
2551 
2552 	copy.insert (i, region);
2553 
2554 	setup_layering_indices (copy);
2555 }
2556 
2557 void
setup_layering_indices(RegionList const & regions)2558 Playlist::setup_layering_indices (RegionList const& regions)
2559 {
2560 	uint64_t j = 0;
2561 
2562 	for (RegionList::const_iterator k = regions.begin (); k != regions.end (); ++k) {
2563 		(*k)->set_layering_index (j++);
2564 	}
2565 }
2566 
2567 struct LaterHigherSort {
operator ()LaterHigherSort2568 	bool operator() (boost::shared_ptr<Region> a, boost::shared_ptr<Region> b)
2569 	{
2570 		return a->position () < b->position ();
2571 	}
2572 };
2573 
2574 /** Take the layering indices of each of our regions, compute the layers
2575  *  that they should be on, and write the layers back to the regions.
2576  */
2577 void
relayer()2578 Playlist::relayer ()
2579 {
2580 	/* never compute layers when setting from XML */
2581 
2582 	if (in_set_state) {
2583 		return;
2584 	}
2585 
2586 	/* Build up a new list of regions on each layer, stored in a set of lists
2587 	 * each of which represent some period of time on some layer.  The idea
2588 	 * is to avoid having to search the entire region list to establish whether
2589 	 * each region overlaps another */
2590 
2591 	/* how many pieces to divide this playlist's time up into */
2592 	int const divisions = 512;
2593 
2594 	/* find the start and end positions of the regions on this playlist */
2595 	samplepos_t start = INT64_MAX;
2596 	samplepos_t end   = 0;
2597 	for (RegionList::const_iterator i = regions.begin (); i != regions.end (); ++i) {
2598 		start = min (start, (*i)->position ());
2599 		end   = max (end, (*i)->position () + (*i)->length ());
2600 	}
2601 
2602 	/* hence the size of each time division */
2603 	double const division_size = (end - start) / double(divisions);
2604 
2605 	vector<vector<RegionList> > layers;
2606 	layers.push_back (vector<RegionList> (divisions));
2607 
2608 	/* Sort our regions into layering index order (for manual layering) or position order (for later is higher)*/
2609 	RegionList copy = regions.rlist ();
2610 	switch (Config->get_layer_model ()) {
2611 		case LaterHigher:
2612 			copy.sort (LaterHigherSort ());
2613 			break;
2614 		case Manual:
2615 			copy.sort (RelayerSort ());
2616 			break;
2617 	}
2618 
2619 	DEBUG_TRACE (DEBUG::Layering, "relayer() using:\n");
2620 	for (RegionList::iterator i = copy.begin (); i != copy.end (); ++i) {
2621 		DEBUG_TRACE (DEBUG::Layering, string_compose ("\t%1 %2\n", (*i)->name (), (*i)->layering_index ()));
2622 	}
2623 
2624 	for (RegionList::iterator i = copy.begin (); i != copy.end (); ++i) {
2625 		/* find the time divisions that this region covers; if there are no regions on the list,
2626 		 * division_size will equal 0 and in this case we'll just say that
2627 		 * start_division = end_division = 0.
2628 		 */
2629 		int start_division = 0;
2630 		int end_division   = 0;
2631 
2632 		if (division_size > 0) {
2633 			start_division = floor (((*i)->position () - start) / division_size);
2634 			end_division   = floor (((*i)->position () + (*i)->length () - start) / division_size);
2635 			if (end_division == divisions) {
2636 				end_division--;
2637 			}
2638 		}
2639 
2640 		assert (divisions == 0 || end_division < divisions);
2641 
2642 		/* find the lowest layer that this region can go on */
2643 		size_t j = layers.size ();
2644 		while (j > 0) {
2645 			/* try layer j - 1; it can go on if it overlaps no other region
2646 			 * that is already on that layer
2647 			 */
2648 
2649 			bool overlap = false;
2650 			for (int k = start_division; k <= end_division; ++k) {
2651 				RegionList::iterator l = layers[j - 1][k].begin ();
2652 				while (l != layers[j - 1][k].end ()) {
2653 					if ((*l)->overlap_equivalent (*i)) {
2654 						overlap = true;
2655 						break;
2656 					}
2657 					l++;
2658 				}
2659 
2660 				if (overlap) {
2661 					break;
2662 				}
2663 			}
2664 
2665 			if (overlap) {
2666 				/* overlap, so we must use layer j */
2667 				break;
2668 			}
2669 
2670 			--j;
2671 		}
2672 
2673 		if (j == layers.size ()) {
2674 			/* we need a new layer for this region */
2675 			layers.push_back (vector<RegionList> (divisions));
2676 		}
2677 
2678 		/* put a reference to this region in each of the divisions that it exists in */
2679 		for (int k = start_division; k <= end_division; ++k) {
2680 			layers[j][k].push_back (*i);
2681 		}
2682 
2683 		(*i)->set_layer (j);
2684 	}
2685 
2686 	/* It's a little tricky to know when we could avoid calling this; e.g. if we are
2687 	 * relayering because we just removed the only region on the top layer, nothing will
2688 	 * appear to have changed, but the StreamView must still sort itself out.  We could
2689 	 * probably keep a note of the top layer last time we relayered, and check that,
2690 	 * but premature optimisation &c...
2691 	 */
2692 	notify_layering_changed ();
2693 
2694 	/* This relayer() may have been called as a result of a region removal, in which
2695 	 * case we need to setup layering indices to account for the one that has just
2696 	 * gone away.
2697 	 */
2698 	setup_layering_indices (copy);
2699 }
2700 
2701 void
raise_region(boost::shared_ptr<Region> region)2702 Playlist::raise_region (boost::shared_ptr<Region> region)
2703 {
2704 	set_layer (region, region->layer () + 1.5);
2705 	relayer ();
2706 }
2707 
2708 void
lower_region(boost::shared_ptr<Region> region)2709 Playlist::lower_region (boost::shared_ptr<Region> region)
2710 {
2711 	set_layer (region, region->layer () - 1.5);
2712 	relayer ();
2713 }
2714 
2715 void
raise_region_to_top(boost::shared_ptr<Region> region)2716 Playlist::raise_region_to_top (boost::shared_ptr<Region> region)
2717 {
2718 	set_layer (region, DBL_MAX);
2719 	relayer ();
2720 }
2721 
2722 void
lower_region_to_bottom(boost::shared_ptr<Region> region)2723 Playlist::lower_region_to_bottom (boost::shared_ptr<Region> region)
2724 {
2725 	set_layer (region, -0.5);
2726 	relayer ();
2727 }
2728 
2729 void
nudge_after(samplepos_t start,samplecnt_t distance,bool forwards)2730 Playlist::nudge_after (samplepos_t start, samplecnt_t distance, bool forwards)
2731 {
2732 	RegionList::iterator i;
2733 	bool                 moved = false;
2734 
2735 	_nudging = true;
2736 
2737 	{
2738 		RegionWriteLock rlock (const_cast<Playlist*> (this));
2739 
2740 		for (i = regions.begin (); i != regions.end (); ++i) {
2741 			if ((*i)->position () >= start) {
2742 				samplepos_t new_pos;
2743 
2744 				if (forwards) {
2745 					if ((*i)->last_sample () > max_samplepos - distance) {
2746 						new_pos = max_samplepos - (*i)->length ();
2747 					} else {
2748 						new_pos = (*i)->position () + distance;
2749 					}
2750 
2751 				} else {
2752 					if ((*i)->position () > distance) {
2753 						new_pos = (*i)->position () - distance;
2754 					} else {
2755 						new_pos = 0;
2756 					}
2757 				}
2758 
2759 				rlock.thawlist.add (*i);
2760 				(*i)->set_position (new_pos);
2761 				moved = true;
2762 			}
2763 		}
2764 	}
2765 
2766 	if (moved) {
2767 		_nudging = false;
2768 		notify_contents_changed ();
2769 	}
2770 }
2771 
2772 bool
uses_source(boost::shared_ptr<const Source> src,bool shallow) const2773 Playlist::uses_source (boost::shared_ptr<const Source> src, bool shallow) const
2774 {
2775 	RegionReadLock rlock (const_cast<Playlist*> (this));
2776 
2777 	for (set<boost::shared_ptr<Region> >::const_iterator r = all_regions.begin (); r != all_regions.end (); ++r) {
2778 		/* Note: passing the second argument as false can cause at best
2779 		 * incredibly deep and time-consuming recursion, and at worst
2780 		 * cycles if the user has managed to create cycles of reference
2781 		 * between compound regions. We generally only this during
2782 		 * cleanup, and @param shallow is passed as true.
2783 		 */
2784 		if ((*r)->uses_source (src, shallow)) {
2785 			return true;
2786 		}
2787 	}
2788 
2789 	return false;
2790 }
2791 
2792 boost::shared_ptr<Region>
find_region(const ID & id) const2793 Playlist::find_region (const ID& id) const
2794 {
2795 	RegionReadLock rlock (const_cast<Playlist*> (this));
2796 
2797 	/* searches all regions currently in use by the playlist */
2798 
2799 	for (RegionList::const_iterator i = regions.begin (); i != regions.end (); ++i) {
2800 		if ((*i)->id () == id) {
2801 			return *i;
2802 		}
2803 	}
2804 
2805 	return boost::shared_ptr<Region> ();
2806 }
2807 
2808 uint32_t
region_use_count(boost::shared_ptr<Region> r) const2809 Playlist::region_use_count (boost::shared_ptr<Region> r) const
2810 {
2811 	RegionReadLock rlock (const_cast<Playlist*> (this));
2812 	uint32_t       cnt = 0;
2813 
2814 	for (RegionList::const_iterator i = regions.begin (); i != regions.end (); ++i) {
2815 		if ((*i) == r) {
2816 			cnt++;
2817 		}
2818 	}
2819 
2820 	RegionFactory::CompoundAssociations& cassocs (RegionFactory::compound_associations ());
2821 	for (RegionFactory::CompoundAssociations::iterator it = cassocs.begin (); it != cassocs.end (); ++it) {
2822 		/* check if region is used in a compound */
2823 		if (it->second == r) {
2824 			/* region is referenced as 'original' of a compound */
2825 			++cnt;
2826 			break;
2827 		}
2828 		if (r->whole_file () && r->max_source_level () > 0) {
2829 			/* region itself ia a compound.
2830 			 * the compound regions are not referenced -> check regions inside compound
2831 			 */
2832 			const SourceList& sl = r->sources ();
2833 			for (SourceList::const_iterator s = sl.begin (); s != sl.end (); ++s) {
2834 				boost::shared_ptr<PlaylistSource> ps = boost::dynamic_pointer_cast<PlaylistSource> (*s);
2835 				if (!ps)
2836 					continue;
2837 				if (ps->playlist ()->region_use_count (it->first)) {
2838 					/* break out of both loops */
2839 					return ++cnt;
2840 				}
2841 			}
2842 		}
2843 	}
2844 	return cnt;
2845 }
2846 
2847 boost::shared_ptr<Region>
region_by_id(const ID & id) const2848 Playlist::region_by_id (const ID& id) const
2849 {
2850 	/* searches all regions ever added to this playlist */
2851 
2852 	for (set<boost::shared_ptr<Region> >::const_iterator i = all_regions.begin (); i != all_regions.end (); ++i) {
2853 		if ((*i)->id () == id) {
2854 			return *i;
2855 		}
2856 	}
2857 	return boost::shared_ptr<Region> ();
2858 }
2859 
2860 void
dump() const2861 Playlist::dump () const
2862 {
2863 	boost::shared_ptr<Region> r;
2864 
2865 	cerr << "Playlist \"" << _name << "\" " << endl
2866 	     << regions.size () << " regions "
2867 	     << endl;
2868 
2869 	for (RegionList::const_iterator i = regions.begin (); i != regions.end (); ++i) {
2870 		r = *i;
2871 		cerr << "  " << r->name () << " ["
2872 		     << r->start () << "+" << r->length ()
2873 		     << "] at "
2874 		     << r->position ()
2875 		     << " on layer "
2876 		     << r->layer ()
2877 		     << endl;
2878 	}
2879 }
2880 
2881 void
set_frozen(bool yn)2882 Playlist::set_frozen (bool yn)
2883 {
2884 	_frozen = yn;
2885 }
2886 
2887 void
shuffle(boost::shared_ptr<Region> region,int dir)2888 Playlist::shuffle (boost::shared_ptr<Region> region, int dir)
2889 {
2890 	bool moved = false;
2891 
2892 	if (region->locked ()) {
2893 		return;
2894 	}
2895 
2896 	_shuffling = true;
2897 
2898 	{
2899 		RegionWriteLock rlock (const_cast<Playlist*> (this));
2900 
2901 		if (dir > 0) {
2902 			RegionList::iterator next;
2903 
2904 			for (RegionList::iterator i = regions.begin (); i != regions.end (); ++i) {
2905 				if ((*i) == region) {
2906 					next = i;
2907 					++next;
2908 
2909 					if (next != regions.end ()) {
2910 						if ((*next)->locked ()) {
2911 							break;
2912 						}
2913 
2914 						samplepos_t new_pos;
2915 
2916 						if ((*next)->position () != region->last_sample () + 1) {
2917 							/* they didn't used to touch, so after shuffle,
2918 							 * just have them swap positions.
2919 							 */
2920 							new_pos = (*next)->position ();
2921 						} else {
2922 							/* they used to touch, so after shuffle,
2923 							 * make sure they still do. put the earlier
2924 							 * region where the later one will end after
2925 							 * it is moved.
2926 							 */
2927 							new_pos = region->position () + (*next)->length ();
2928 						}
2929 
2930 						rlock.thawlist.add (*next);
2931 						rlock.thawlist.add (region);
2932 
2933 						(*next)->set_position (region->position ());
2934 						region->set_position (new_pos);
2935 
2936 						/* avoid a full sort */
2937 
2938 						regions.erase (i); /* removes the region from the list */
2939 						next++;
2940 						regions.insert (next, region); /* adds it back after next */
2941 
2942 						moved = true;
2943 					}
2944 					break;
2945 				}
2946 			}
2947 		} else {
2948 			RegionList::iterator prev = regions.end ();
2949 
2950 			for (RegionList::iterator i = regions.begin (); i != regions.end (); prev = i, ++i) {
2951 				if ((*i) == region) {
2952 					if (prev != regions.end ()) {
2953 						if ((*prev)->locked ()) {
2954 							break;
2955 						}
2956 
2957 						samplepos_t new_pos;
2958 						if (region->position () != (*prev)->last_sample () + 1) {
2959 							/* they didn't used to touch, so after shuffle,
2960 							 * just have them swap positions.
2961 							 */
2962 							new_pos = region->position ();
2963 						} else {
2964 							/* they used to touch, so after shuffle,
2965 							 * make sure they still do. put the earlier
2966 							 * one where the later one will end after
2967 							 */
2968 							new_pos = (*prev)->position () + region->length ();
2969 						}
2970 
2971 						rlock.thawlist.add (region);
2972 						rlock.thawlist.add (*prev);
2973 
2974 						region->set_position ((*prev)->position ());
2975 						(*prev)->set_position (new_pos);
2976 
2977 						/* avoid a full sort */
2978 
2979 						regions.erase (i);             /* remove region */
2980 						regions.insert (prev, region); /* insert region before prev */
2981 
2982 						moved = true;
2983 					}
2984 
2985 					break;
2986 				}
2987 			}
2988 		}
2989 	}
2990 
2991 	_shuffling = false;
2992 
2993 	if (moved) {
2994 		relayer ();
2995 		notify_contents_changed ();
2996 	}
2997 }
2998 
2999 bool
region_is_shuffle_constrained(boost::shared_ptr<Region>)3000     Playlist::region_is_shuffle_constrained (boost::shared_ptr<Region>)
3001 {
3002 	RegionReadLock rlock (const_cast<Playlist*> (this));
3003 
3004 	if (regions.size () > 1) {
3005 		return true;
3006 	}
3007 
3008 	return false;
3009 }
3010 
3011 void
ripple(samplepos_t at,samplecnt_t distance,RegionList * exclude)3012 Playlist::ripple (samplepos_t at, samplecnt_t distance, RegionList* exclude)
3013 {
3014 	ripple_locked (at, distance, exclude);
3015 }
3016 
3017 void
update_after_tempo_map_change()3018 Playlist::update_after_tempo_map_change ()
3019 {
3020 	{
3021 		RegionWriteLock rlock (const_cast<Playlist*> (this));
3022 		RegionList      copy (regions.rlist ());
3023 
3024 		freeze_locked ();
3025 
3026 		for (RegionList::iterator i = copy.begin (); i != copy.end (); ++i) {
3027 			rlock.thawlist.add (*i);
3028 			(*i)->update_after_tempo_map_change ();
3029 		}
3030 	}
3031 	/* possibly causes a contents changed notification (flush_notifications()) */
3032 	thaw ();
3033 }
3034 
3035 void
foreach_region(boost::function<void (boost::shared_ptr<Region>)> s)3036 Playlist::foreach_region (boost::function<void(boost::shared_ptr<Region>)> s)
3037 {
3038 	RegionReadLock rl (this);
3039 	for (RegionList::iterator i = regions.begin (); i != regions.end (); ++i) {
3040 		s (*i);
3041 	}
3042 }
3043 
3044 bool
has_region_at(samplepos_t const p) const3045 Playlist::has_region_at (samplepos_t const p) const
3046 {
3047 	RegionReadLock (const_cast<Playlist*> (this));
3048 
3049 	RegionList::const_iterator i = regions.begin ();
3050 	while (i != regions.end () && !(*i)->covers (p)) {
3051 		++i;
3052 	}
3053 
3054 	return (i != regions.end ());
3055 }
3056 
3057 /** Look from a session sample time and find the start time of the next region
3058  *  which is on the top layer of this playlist.
3059  *  @param t Time to look from.
3060  *  @return Position of next top-layered region, or max_samplepos if there isn't one.
3061  */
3062 samplepos_t
find_next_top_layer_position(samplepos_t t) const3063 Playlist::find_next_top_layer_position (samplepos_t t) const
3064 {
3065 	RegionReadLock rlock (const_cast<Playlist*> (this));
3066 
3067 	layer_t const top = top_layer ();
3068 
3069 	RegionList copy = regions.rlist ();
3070 	copy.sort (RegionSortByPosition ());
3071 
3072 	for (RegionList::const_iterator i = copy.begin (); i != copy.end (); ++i) {
3073 		if ((*i)->position () >= t && (*i)->layer () == top) {
3074 			return (*i)->position ();
3075 		}
3076 	}
3077 
3078 	return max_samplepos;
3079 }
3080 
3081 boost::shared_ptr<Region>
combine(const RegionList & r)3082 Playlist::combine (const RegionList& r)
3083 {
3084 	ThawList                           thawlist;
3085 	PropertyList                       plist;
3086 	uint32_t                           channels          = 0;
3087 	uint32_t                           layer             = 0;
3088 	samplepos_t                        earliest_position = max_samplepos;
3089 	vector<TwoRegions>                 old_and_new_regions;
3090 	vector<boost::shared_ptr<Region> > originals;
3091 	vector<boost::shared_ptr<Region> > copies;
3092 	string                             parent_name;
3093 	string                             child_name;
3094 	uint32_t                           max_level = 0;
3095 
3096 	/* find the maximum depth of all the regions we're combining */
3097 
3098 	for (RegionList::const_iterator i = r.begin (); i != r.end (); ++i) {
3099 		max_level = max (max_level, (*i)->max_source_level ());
3100 	}
3101 
3102 	parent_name = RegionFactory::compound_region_name (name (), combine_ops (), max_level, true);
3103 	child_name  = RegionFactory::compound_region_name (name (), combine_ops (), max_level, false);
3104 
3105 	boost::shared_ptr<Playlist> pl = PlaylistFactory::create (_type, _session, parent_name, true);
3106 
3107 	for (RegionList::const_iterator i = r.begin (); i != r.end (); ++i) {
3108 		earliest_position = min (earliest_position, (*i)->position ());
3109 	}
3110 
3111 	/* enable this so that we do not try to create xfades etc. as we add
3112 	 * regions
3113 	 */
3114 
3115 	pl->in_partition = true;
3116 
3117 	/* sort by position then layer.
3118 	 * route_time_axis passes 'selected_regions' - which is not sorted.
3119 	 * here we need the top-most first, then every layer's region sorted by position.
3120 	 */
3121 	RegionList sorted (r);
3122 	sorted.sort (RegionSortByLayerAndPosition ());
3123 
3124 	for (RegionList::const_iterator i = sorted.begin (); i != sorted.end (); ++i) {
3125 		/* copy the region */
3126 
3127 		boost::shared_ptr<Region> original_region = (*i);
3128 		boost::shared_ptr<Region> copied_region   = RegionFactory::create (original_region, false, false, &thawlist);
3129 
3130 		old_and_new_regions.push_back (TwoRegions (original_region, copied_region));
3131 		originals.push_back (original_region);
3132 		copies.push_back (copied_region);
3133 
3134 		RegionFactory::add_compound_association (original_region, copied_region);
3135 
3136 		/* make position relative to zero */
3137 		pl->add_region_internal (copied_region, original_region->position () - earliest_position, thawlist);
3138 
3139 		/* use the maximum number of channels for any region */
3140 
3141 		channels = max (channels, original_region->n_channels ());
3142 
3143 		/* it will go above the layer of the highest existing region */
3144 
3145 		layer = max (layer, original_region->layer ());
3146 	}
3147 
3148 	pl->in_partition = false;
3149 
3150 	/* pre-process. e.g. disable audio region fades */
3151 	pre_combine (copies);
3152 
3153 	/* now create a new PlaylistSource for each channel in the new playlist */
3154 
3155 	SourceList                     sources;
3156 	pair<samplepos_t, samplepos_t> extent = pl->_get_extent ();
3157 
3158 	for (uint32_t chn = 0; chn < channels; ++chn) {
3159 		sources.push_back (SourceFactory::createFromPlaylist (_type, _session, pl, id (), parent_name, chn, 0, extent.second, false, false));
3160 	}
3161 
3162 	/* now a new whole-file region using the list of sources */
3163 
3164 	plist.add (Properties::start, 0);
3165 	plist.add (Properties::length, extent.second);
3166 	plist.add (Properties::name, parent_name);
3167 	plist.add (Properties::whole_file, true);
3168 
3169 	boost::shared_ptr<Region> parent_region = RegionFactory::create (sources, plist, true, &thawlist);
3170 
3171 	/* now the non-whole-file region that we will actually use in the playlist */
3172 
3173 	plist.clear ();
3174 	plist.add (Properties::start, 0);
3175 	plist.add (Properties::length, extent.second);
3176 	plist.add (Properties::name, child_name);
3177 	plist.add (Properties::layer, layer + 1);
3178 
3179 	boost::shared_ptr<Region> compound_region = RegionFactory::create (parent_region, plist, true, &thawlist);
3180 
3181 	for (SourceList::iterator s = sources.begin (); s != sources.end (); ++s) {
3182 		boost::dynamic_pointer_cast<PlaylistSource> (*s)->set_owner (compound_region->id ());
3183 	}
3184 
3185 	/* remove all the selected regions from the current playlist */
3186 
3187 	freeze ();
3188 
3189 	for (RegionList::const_iterator i = r.begin (); i != r.end (); ++i) {
3190 		remove_region (*i);
3191 	}
3192 
3193 	/* do type-specific stuff with the originals and the new compound region */
3194 
3195 	post_combine (originals, compound_region);
3196 
3197 	/* add the new region at the right location */
3198 
3199 	add_region (compound_region, earliest_position);
3200 
3201 	_combine_ops++;
3202 
3203 	thawlist.release ();
3204 	thaw ();
3205 
3206 	return compound_region;
3207 }
3208 
3209 void
uncombine(boost::shared_ptr<Region> target)3210 Playlist::uncombine (boost::shared_ptr<Region> target)
3211 {
3212 	boost::shared_ptr<PlaylistSource>  pls;
3213 	boost::shared_ptr<const Playlist>  pl;
3214 	vector<boost::shared_ptr<Region> > originals;
3215 	vector<TwoRegions>                 old_and_new_regions;
3216 
3217 	/* (1) check that its really a compound region */
3218 
3219 	if ((pls = boost::dynamic_pointer_cast<PlaylistSource> (target->source (0))) == 0) {
3220 		return;
3221 	}
3222 
3223 	pl = pls->playlist ();
3224 
3225 	samplepos_t adjusted_start = 0; /* gcc isn't smart enough */
3226 	samplepos_t adjusted_end   = 0; /* gcc isn't smart enough */
3227 
3228 	/* the leftmost (earliest) edge of the compound region
3229 	 * starts at zero in its source, or larger if it
3230 	 * has been trimmed or content-scrolled.
3231 	 *
3232 	 * the rightmost (latest) edge of the compound region
3233 	 * relative to its source is the starting point plus
3234 	 * the length of the region.
3235 	 */
3236 
3237 	/* (2) get all the original regions */
3238 
3239 	const RegionList&                    rl (pl->region_list_property ().rlist ());
3240 	RegionFactory::CompoundAssociations& cassocs (RegionFactory::compound_associations ());
3241 	sampleoffset_t                       move_offset = 0;
3242 
3243 	/* there are three possibilities here:
3244 	   1) the playlist that the playlist source was based on
3245 	   is us, so just add the originals (which belonged to
3246 	   us anyway) back in the right place.
3247 
3248 	   2) the playlist that the playlist source was based on
3249 	   is NOT us, so we need to make copies of each of
3250 	   the original regions that we find, and add them
3251 	   instead.
3252 
3253 	   3) target region is a copy of a compount region previously
3254 	   created. In this case we will also need to make copies ot each of
3255 	   the original regions, and add them instead.
3256 	*/
3257 
3258 	const bool need_copies = (boost::dynamic_pointer_cast<PlaylistSource> (pls)->owner () != target->id ()) ||
3259 	                         (pls->original () != id ());
3260 
3261 	ThawList thawlist;
3262 
3263 	for (RegionList::const_iterator i = rl.begin (); i != rl.end (); ++i) {
3264 		boost::shared_ptr<Region> current (*i);
3265 
3266 		RegionFactory::CompoundAssociations::iterator ca = cassocs.find (*i);
3267 
3268 		if (ca == cassocs.end ()) {
3269 			continue;
3270 		}
3271 
3272 		boost::shared_ptr<Region> original (ca->second);
3273 
3274 		bool modified_region;
3275 
3276 		if (i == rl.begin ()) {
3277 			move_offset    = (target->position () - original->position ()) - target->start ();
3278 			adjusted_start = original->position () + target->start ();
3279 			adjusted_end   = adjusted_start + target->length ();
3280 		}
3281 
3282 		if (!need_copies) {
3283 			thawlist.add (original);
3284 		} else {
3285 			samplepos_t pos = original->position ();
3286 			/* make a copy, but don't announce it */
3287 			original = RegionFactory::create (original, false, false, &thawlist);
3288 			/* the pure copy constructor resets position() to zero, so fix that up.  */
3289 			original->set_position (pos);
3290 		}
3291 
3292 		/* check to see how the original region (in the
3293 		 * playlist before compounding occurred) overlaps
3294 		 * with the new state of the compound region.
3295 		 */
3296 
3297 		original->clear_changes ();
3298 		modified_region = false;
3299 
3300 		switch (original->coverage (adjusted_start, adjusted_end)) {
3301 			case Evoral::OverlapNone:
3302 				/* original region does not cover any part
3303 			 * of the current state of the compound region
3304 			 */
3305 				continue;
3306 
3307 			case Evoral::OverlapInternal:
3308 				/* overlap is just a small piece inside the
3309 			 * original so trim both ends
3310 			 */
3311 				original->trim_to (adjusted_start, adjusted_end - adjusted_start);
3312 				modified_region = true;
3313 				break;
3314 
3315 			case Evoral::OverlapExternal:
3316 				/* overlap fully covers original, so leave it as is */
3317 				break;
3318 
3319 			case Evoral::OverlapEnd:
3320 				/* overlap starts within but covers end, so trim the front of the region */
3321 				original->trim_front (adjusted_start);
3322 				modified_region = true;
3323 				break;
3324 
3325 			case Evoral::OverlapStart:
3326 				/* overlap covers start but ends within, so
3327 			 * trim the end of the region.
3328 			 */
3329 				original->trim_end (adjusted_end);
3330 				modified_region = true;
3331 				break;
3332 		}
3333 
3334 		if (move_offset) {
3335 			/* fix the position to match any movement of the compound region. */
3336 			original->set_position (original->position () + move_offset);
3337 			modified_region = true;
3338 		}
3339 
3340 		if (modified_region) {
3341 			_session.add_command (new StatefulDiffCommand (original));
3342 		}
3343 
3344 		/* and add to the list of regions waiting to be
3345 		 * re-inserted
3346 		 */
3347 
3348 		originals.push_back (original);
3349 		old_and_new_regions.push_back (TwoRegions (*i, original));
3350 	}
3351 
3352 	pre_uncombine (originals, target);
3353 
3354 	in_partition = true;
3355 	freeze ();
3356 
3357 	/* (3) remove the compound region */
3358 
3359 	remove_region (target);
3360 
3361 	/* (4) add the constituent regions */
3362 
3363 	for (vector<boost::shared_ptr<Region> >::iterator i = originals.begin (); i != originals.end (); ++i) {
3364 		add_region ((*i), (*i)->position ());
3365 		set_layer ((*i), (*i)->layer ());
3366 		if (!RegionFactory::region_by_id ((*i)->id ())) {
3367 			RegionFactory::map_add (*i);
3368 		}
3369 	}
3370 
3371 	in_partition = false;
3372 	thaw ();
3373 	thawlist.release ();
3374 }
3375 
3376 void
fade_range(list<AudioRange> & ranges)3377 Playlist::fade_range (list<AudioRange>& ranges)
3378 {
3379 	RegionReadLock rlock (this);
3380 	for (list<AudioRange>::iterator r = ranges.begin (); r != ranges.end ();) {
3381 		list<AudioRange>::iterator tmpr = r;
3382 		++tmpr;
3383 		for (RegionList::const_iterator i = regions.begin (); i != regions.end ();) {
3384 			RegionList::const_iterator tmpi = i;
3385 			++tmpi;
3386 			(*i)->fade_range ((*r).start, (*r).end);
3387 			i = tmpi;
3388 		}
3389 		r = tmpr;
3390 	}
3391 }
3392 
3393 uint32_t
max_source_level() const3394 Playlist::max_source_level () const
3395 {
3396 	RegionReadLock rlock (const_cast<Playlist*> (this));
3397 	uint32_t       lvl = 0;
3398 
3399 	for (RegionList::const_iterator i = regions.begin (); i != regions.end (); ++i) {
3400 		lvl = max (lvl, (*i)->max_source_level ());
3401 	}
3402 
3403 	return lvl;
3404 }
3405 
3406 void
set_orig_track_id(const PBD::ID & id)3407 Playlist::set_orig_track_id (const PBD::ID& id)
3408 {
3409 	if (shared_with (id)) {
3410 		/* Swap 'shared_id' / origin_track_id */
3411 		unshare_with (id);
3412 		share_with (_orig_track_id);
3413 	}
3414 	_orig_track_id = id;
3415 }
3416 
3417 void
share_with(const PBD::ID & id)3418 Playlist::share_with (const PBD::ID& id)
3419 {
3420 	if (!shared_with (id)) {
3421 		_shared_with_ids.push_back (id);
3422 	}
3423 }
3424 
3425 void
unshare_with(const PBD::ID & id)3426 Playlist::unshare_with (const PBD::ID& id)
3427 {
3428 	list<PBD::ID>::iterator it = _shared_with_ids.begin ();
3429 	while (it != _shared_with_ids.end ()) {
3430 		if (*it == id) {
3431 			_shared_with_ids.erase (it);
3432 			break;
3433 		}
3434 		++it;
3435 	}
3436 }
3437 
3438 bool
shared_with(const PBD::ID & id) const3439 Playlist::shared_with (const PBD::ID& id) const
3440 {
3441 	bool                          shared = false;
3442 	list<PBD::ID>::const_iterator it     = _shared_with_ids.begin ();
3443 	while (it != _shared_with_ids.end () && !shared) {
3444 		if (*it == id) {
3445 			shared = true;
3446 		}
3447 		++it;
3448 	}
3449 
3450 	return shared;
3451 }
3452 
3453 void
reset_shares()3454 Playlist::reset_shares ()
3455 {
3456 	_shared_with_ids.clear ();
3457 }
3458 
3459 /** Take a list of ranges, coalesce any that can be coalesced, then call
3460  *  check_crossfades for each one.
3461  */
3462 void
coalesce_and_check_crossfades(list<Evoral::Range<samplepos_t>> ranges)3463 Playlist::coalesce_and_check_crossfades (list<Evoral::Range<samplepos_t> > ranges)
3464 {
3465 	/* XXX: it's a shame that this coalesce algorithm also exists in
3466 	 * TimeSelection::consolidate().
3467 	 */
3468 
3469 	/* XXX: xfade: this is implemented in Evoral::RangeList */
3470 
3471 restart:
3472 	for (list<Evoral::Range<samplepos_t> >::iterator i = ranges.begin (); i != ranges.end (); ++i) {
3473 		for (list<Evoral::Range<samplepos_t> >::iterator j = ranges.begin (); j != ranges.end (); ++j) {
3474 			if (i == j) {
3475 				continue;
3476 			}
3477 
3478 			/* XXX i->from can be > i->to - is this right? coverage() will return OverlapNone in this case */
3479 			if (Evoral::coverage (i->from, i->to, j->from, j->to) != Evoral::OverlapNone) {
3480 				i->from = min (i->from, j->from);
3481 				i->to   = max (i->to, j->to);
3482 				ranges.erase (j);
3483 				goto restart;
3484 			}
3485 		}
3486 	}
3487 }
3488 
3489 void
set_capture_insertion_in_progress(bool yn)3490 Playlist::set_capture_insertion_in_progress (bool yn)
3491 {
3492 	_capture_insertion_underway = yn;
3493 }
3494