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