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