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