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