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