Set up layering_index immediately on an explicit layer, so that undo
[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         for (RegionList::iterator i = copy.begin(); i != copy.end(); ++i) {
2422
2423                 /* find the time divisions that this region covers; if there are no regions on the list,
2424                    division_size will equal 0 and in this case we'll just say that
2425                    start_division = end_division = 0.
2426                 */
2427                 int start_division = 0;
2428                 int end_division = 0;
2429
2430                 if (division_size > 0) {
2431                         start_division = floor ( ((*i)->position() - start) / division_size);
2432                         end_division = floor ( ((*i)->position() + (*i)->length() - start) / division_size );
2433                         if (end_division == divisions) {
2434                                 end_division--;
2435                         }
2436                 }
2437
2438                 assert (divisions == 0 || end_division < divisions);
2439
2440                 /* find the lowest layer that this region can go on */
2441                 size_t j = layers.size();
2442                 while (j > 0) {
2443                         /* try layer j - 1; it can go on if it overlaps no other region
2444                            that is already on that layer
2445                         */
2446
2447                         bool overlap = false;
2448                         for (int k = start_division; k <= end_division; ++k) {
2449                                 RegionList::iterator l = layers[j-1][k].begin ();
2450                                 while (l != layers[j-1][k].end()) {
2451                                         if ((*l)->overlap_equivalent (*i)) {
2452                                                 overlap = true;
2453                                                 break;
2454                                         }
2455                                         l++;
2456                                 }
2457
2458                                 if (overlap) {
2459                                         break;
2460                                 }
2461                         }
2462
2463                         if (overlap) {
2464                                 /* overlap, so we must use layer j */
2465                                 break;
2466                         }
2467
2468                         --j;
2469                 }
2470
2471                 if (j == layers.size()) {
2472                         /* we need a new layer for this region */
2473                         layers.push_back (vector<RegionList> (divisions));
2474                 }
2475
2476                 /* put a reference to this region in each of the divisions that it exists in */
2477                 for (int k = start_division; k <= end_division; ++k) {
2478                         layers[j][k].push_back (*i);
2479                 }
2480
2481                 if ((*i)->layer() != j) {
2482                         changed = true;
2483                 }
2484
2485                 (*i)->set_layer (j);
2486         }
2487
2488         if (changed) {
2489                 notify_layering_changed ();
2490         }
2491 }
2492
2493 void
2494 Playlist::raise_region (boost::shared_ptr<Region> region)
2495 {
2496         set_layer (region, region->layer() + 1.5);
2497         relayer ();
2498 }
2499
2500 void
2501 Playlist::lower_region (boost::shared_ptr<Region> region)
2502 {
2503         set_layer (region, region->layer() - 1.5);
2504         relayer ();
2505 }
2506
2507 void
2508 Playlist::raise_region_to_top (boost::shared_ptr<Region> region)
2509 {
2510         set_layer (region, DBL_MAX);
2511         relayer ();
2512 }
2513
2514 void
2515 Playlist::lower_region_to_bottom (boost::shared_ptr<Region> region)
2516 {
2517         set_layer (region, -0.5);
2518         relayer ();
2519 }
2520
2521 void
2522 Playlist::nudge_after (framepos_t start, framecnt_t distance, bool forwards)
2523 {
2524         RegionList::iterator i;
2525         bool moved = false;
2526
2527         _nudging = true;
2528
2529         {
2530                 RegionLock rlock (const_cast<Playlist *> (this));
2531
2532                 for (i = regions.begin(); i != regions.end(); ++i) {
2533
2534                         if ((*i)->position() >= start) {
2535
2536                                 framepos_t new_pos;
2537
2538                                 if (forwards) {
2539
2540                                         if ((*i)->last_frame() > max_framepos - distance) {
2541                                                 new_pos = max_framepos - (*i)->length();
2542                                         } else {
2543                                                 new_pos = (*i)->position() + distance;
2544                                         }
2545
2546                                 } else {
2547
2548                                         if ((*i)->position() > distance) {
2549                                                 new_pos = (*i)->position() - distance;
2550                                         } else {
2551                                                 new_pos = 0;
2552                                         }
2553                                 }
2554
2555                                 (*i)->set_position (new_pos);
2556                                 moved = true;
2557                         }
2558                 }
2559         }
2560
2561         if (moved) {
2562                 _nudging = false;
2563                 notify_contents_changed ();
2564         }
2565
2566 }
2567
2568 bool
2569 Playlist::uses_source (boost::shared_ptr<const Source> src) const
2570 {
2571         RegionLock rlock (const_cast<Playlist*> (this));
2572
2573         for (set<boost::shared_ptr<Region> >::iterator r = all_regions.begin(); r != all_regions.end(); ++r) {
2574                 if ((*r)->uses_source (src)) {
2575                         return true;
2576                 }
2577         }
2578
2579         return false;
2580 }
2581
2582 boost::shared_ptr<Region>
2583 Playlist::find_region (const ID& id) const
2584 {
2585         RegionLock rlock (const_cast<Playlist*> (this));
2586
2587         /* searches all regions currently in use by the playlist */
2588
2589         for (RegionList::const_iterator i = regions.begin(); i != regions.end(); ++i) {
2590                 if ((*i)->id() == id) {
2591                         return *i;
2592                 }
2593         }
2594
2595         return boost::shared_ptr<Region> ();
2596 }
2597
2598 uint32_t
2599 Playlist::region_use_count (boost::shared_ptr<Region> r) const
2600 {
2601         RegionLock rlock (const_cast<Playlist*> (this));
2602         uint32_t cnt = 0;
2603
2604         for (RegionList::const_iterator i = regions.begin(); i != regions.end(); ++i) {
2605                 if ((*i) == r) {
2606                         cnt++;
2607                 }
2608         }
2609
2610         return cnt;
2611 }
2612
2613 boost::shared_ptr<Region>
2614 Playlist::region_by_id (const ID& id) const
2615 {
2616         /* searches all regions ever added to this playlist */
2617
2618         for (set<boost::shared_ptr<Region> >::iterator i = all_regions.begin(); i != all_regions.end(); ++i) {
2619                 if ((*i)->id() == id) {
2620                         return *i;
2621                 }
2622         }
2623         return boost::shared_ptr<Region> ();
2624 }
2625
2626 void
2627 Playlist::dump () const
2628 {
2629         boost::shared_ptr<Region> r;
2630
2631         cerr << "Playlist \"" << _name << "\" " << endl
2632              << regions.size() << " regions "
2633              << endl;
2634
2635         for (RegionList::const_iterator i = regions.begin(); i != regions.end(); ++i) {
2636                 r = *i;
2637                 cerr << "  " << r->name() << " ["
2638                      << r->start() << "+" << r->length()
2639                      << "] at "
2640                      << r->position()
2641                      << " on layer "
2642                      << r->layer ()
2643                      << endl;
2644         }
2645 }
2646
2647 void
2648 Playlist::set_frozen (bool yn)
2649 {
2650         _frozen = yn;
2651 }
2652
2653 void
2654 Playlist::shuffle (boost::shared_ptr<Region> region, int dir)
2655 {
2656         bool moved = false;
2657
2658         if (region->locked()) {
2659                 return;
2660         }
2661
2662         _shuffling = true;
2663
2664         {
2665                 RegionLock rlock (const_cast<Playlist*> (this));
2666
2667
2668                 if (dir > 0) {
2669
2670                         RegionList::iterator next;
2671
2672                         for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
2673                                 if ((*i) == region) {
2674                                         next = i;
2675                                         ++next;
2676
2677                                         if (next != regions.end()) {
2678
2679                                                 if ((*next)->locked()) {
2680                                                         break;
2681                                                 }
2682
2683                                                 framepos_t new_pos;
2684
2685                                                 if ((*next)->position() != region->last_frame() + 1) {
2686                                                         /* they didn't used to touch, so after shuffle,
2687                                                            just have them swap positions.
2688                                                         */
2689                                                         new_pos = (*next)->position();
2690                                                 } else {
2691                                                         /* they used to touch, so after shuffle,
2692                                                            make sure they still do. put the earlier
2693                                                            region where the later one will end after
2694                                                            it is moved.
2695                                                         */
2696                                                         new_pos = region->position() + (*next)->length();
2697                                                 }
2698
2699                                                 (*next)->set_position (region->position());
2700                                                 region->set_position (new_pos);
2701
2702                                                 /* avoid a full sort */
2703
2704                                                 regions.erase (i); // removes the region from the list */
2705                                                 next++;
2706                                                 regions.insert (next, region); // adds it back after next
2707
2708                                                 moved = true;
2709                                         }
2710                                         break;
2711                                 }
2712                         }
2713                 } else {
2714
2715                         RegionList::iterator prev = regions.end();
2716
2717                         for (RegionList::iterator i = regions.begin(); i != regions.end(); prev = i, ++i) {
2718                                 if ((*i) == region) {
2719
2720                                         if (prev != regions.end()) {
2721
2722                                                 if ((*prev)->locked()) {
2723                                                         break;
2724                                                 }
2725
2726                                                 framepos_t new_pos;
2727                                                 if (region->position() != (*prev)->last_frame() + 1) {
2728                                                         /* they didn't used to touch, so after shuffle,
2729                                                            just have them swap positions.
2730                                                         */
2731                                                         new_pos = region->position();
2732                                                 } else {
2733                                                         /* they used to touch, so after shuffle,
2734                                                            make sure they still do. put the earlier
2735                                                            one where the later one will end after
2736                                                         */
2737                                                         new_pos = (*prev)->position() + region->length();
2738                                                 }
2739
2740                                                 region->set_position ((*prev)->position());
2741                                                 (*prev)->set_position (new_pos);
2742
2743                                                 /* avoid a full sort */
2744
2745                                                 regions.erase (i); // remove region
2746                                                 regions.insert (prev, region); // insert region before prev
2747
2748                                                 moved = true;
2749                                         }
2750
2751                                         break;
2752                                 }
2753                         }
2754                 }
2755         }
2756
2757         _shuffling = false;
2758
2759         if (moved) {
2760
2761                 relayer ();
2762                 check_dependents (region, false);
2763
2764                 notify_contents_changed();
2765         }
2766
2767 }
2768
2769 bool
2770 Playlist::region_is_shuffle_constrained (boost::shared_ptr<Region>)
2771 {
2772         RegionLock rlock (const_cast<Playlist*> (this));
2773
2774         if (regions.size() > 1) {
2775                 return true;
2776         }
2777
2778         return false;
2779 }
2780
2781 void
2782 Playlist::update_after_tempo_map_change ()
2783 {
2784         RegionLock rlock (const_cast<Playlist*> (this));
2785         RegionList copy (regions.rlist());
2786
2787         freeze ();
2788
2789         for (RegionList::iterator i = copy.begin(); i != copy.end(); ++i) {
2790                 (*i)->update_after_tempo_map_change ();
2791         }
2792
2793         thaw ();
2794 }
2795
2796 void
2797 Playlist::foreach_region (boost::function<void(boost::shared_ptr<Region>)> s)
2798 {
2799         RegionLock rl (this, false);
2800         for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
2801                 s (*i);
2802         }
2803 }
2804
2805 bool
2806 Playlist::has_region_at (framepos_t const p) const
2807 {
2808         RegionLock (const_cast<Playlist *> (this));
2809
2810         RegionList::const_iterator i = regions.begin ();
2811         while (i != regions.end() && !(*i)->covers (p)) {
2812                 ++i;
2813         }
2814
2815         return (i != regions.end());
2816 }
2817
2818 /** Remove any region that uses a given source */
2819 void
2820 Playlist::remove_region_by_source (boost::shared_ptr<Source> s)
2821 {
2822         RegionLock rl (this);
2823
2824         RegionList::iterator i = regions.begin();
2825         while (i != regions.end()) {
2826                 RegionList::iterator j = i;
2827                 ++j;
2828
2829                 if ((*i)->uses_source (s)) {
2830                         remove_region_internal (*i);
2831                 }
2832
2833                 i = j;
2834         }
2835 }
2836
2837 /** Look from a session frame time and find the start time of the next region
2838  *  which is on the top layer of this playlist.
2839  *  @param t Time to look from.
2840  *  @return Position of next top-layered region, or max_framepos if there isn't one.
2841  */
2842 framepos_t
2843 Playlist::find_next_top_layer_position (framepos_t t) const
2844 {
2845         RegionLock rlock (const_cast<Playlist *> (this));
2846
2847         layer_t const top = top_layer ();
2848
2849         RegionList copy = regions.rlist ();
2850         copy.sort (RegionSortByPosition ());
2851
2852         for (RegionList::const_iterator i = copy.begin(); i != copy.end(); ++i) {
2853                 if ((*i)->position() >= t && (*i)->layer() == top) {
2854                         return (*i)->position();
2855                 }
2856         }
2857
2858         return max_framepos;
2859 }
2860
2861 boost::shared_ptr<Region>
2862 Playlist::combine (const RegionList& r)
2863 {
2864         PropertyList plist;
2865         uint32_t channels = 0;
2866         uint32_t layer = 0;
2867         framepos_t earliest_position = max_framepos;
2868         vector<TwoRegions> old_and_new_regions;
2869         vector<boost::shared_ptr<Region> > originals;
2870         vector<boost::shared_ptr<Region> > copies;
2871         string parent_name;
2872         string child_name;
2873         uint32_t max_level = 0;
2874
2875         /* find the maximum depth of all the regions we're combining */
2876
2877         for (RegionList::const_iterator i = r.begin(); i != r.end(); ++i) {
2878                 max_level = max (max_level, (*i)->max_source_level());
2879         }
2880
2881         parent_name = RegionFactory::compound_region_name (name(), combine_ops(), max_level, true);
2882         child_name = RegionFactory::compound_region_name (name(), combine_ops(), max_level, false);
2883
2884         boost::shared_ptr<Playlist> pl = PlaylistFactory::create (_type, _session, parent_name, true);
2885
2886         for (RegionList::const_iterator i = r.begin(); i != r.end(); ++i) {
2887                 earliest_position = min (earliest_position, (*i)->position());
2888         }
2889
2890         /* enable this so that we do not try to create xfades etc. as we add
2891          * regions
2892          */
2893
2894         pl->in_partition = true;
2895
2896         for (RegionList::const_iterator i = r.begin(); i != r.end(); ++i) {
2897
2898                 /* copy the region */
2899
2900                 boost::shared_ptr<Region> original_region = (*i);
2901                 boost::shared_ptr<Region> copied_region = RegionFactory::create (original_region, false);
2902
2903                 old_and_new_regions.push_back (TwoRegions (original_region,copied_region));
2904                 originals.push_back (original_region);
2905                 copies.push_back (copied_region);
2906
2907                 RegionFactory::add_compound_association (original_region, copied_region);
2908
2909                 /* make position relative to zero */
2910
2911                 pl->add_region (copied_region, original_region->position() - earliest_position);
2912
2913                 /* use the maximum number of channels for any region */
2914
2915                 channels = max (channels, original_region->n_channels());
2916
2917                 /* it will go above the layer of the highest existing region */
2918
2919                 layer = max (layer, original_region->layer());
2920         }
2921
2922         pl->in_partition = false;
2923
2924         pre_combine (copies);
2925
2926         /* add any dependent regions to the new playlist */
2927
2928         copy_dependents (old_and_new_regions, pl.get());
2929
2930         /* now create a new PlaylistSource for each channel in the new playlist */
2931
2932         SourceList sources;
2933         pair<framepos_t,framepos_t> extent = pl->get_extent();
2934
2935         for (uint32_t chn = 0; chn < channels; ++chn) {
2936                 sources.push_back (SourceFactory::createFromPlaylist (_type, _session, pl, id(), parent_name, chn, 0, extent.second, false, false));
2937
2938         }
2939
2940         /* now a new whole-file region using the list of sources */
2941
2942         plist.add (Properties::start, 0);
2943         plist.add (Properties::length, extent.second);
2944         plist.add (Properties::name, parent_name);
2945         plist.add (Properties::whole_file, true);
2946
2947         boost::shared_ptr<Region> parent_region = RegionFactory::create (sources, plist, true);
2948
2949         /* now the non-whole-file region that we will actually use in the
2950          * playlist
2951          */
2952
2953         plist.clear ();
2954         plist.add (Properties::start, 0);
2955         plist.add (Properties::length, extent.second);
2956         plist.add (Properties::name, child_name);
2957         plist.add (Properties::layer, layer+1);
2958
2959         boost::shared_ptr<Region> compound_region = RegionFactory::create (parent_region, plist, true);
2960
2961         /* remove all the selected regions from the current playlist
2962          */
2963
2964         freeze ();
2965
2966         for (RegionList::const_iterator i = r.begin(); i != r.end(); ++i) {
2967                 remove_region (*i);
2968         }
2969
2970         /* do type-specific stuff with the originals and the new compound
2971            region
2972         */
2973
2974         post_combine (originals, compound_region);
2975
2976         /* add the new region at the right location */
2977
2978         add_region (compound_region, earliest_position);
2979
2980         _combine_ops++;
2981
2982         thaw ();
2983
2984         return compound_region;
2985 }
2986
2987 void
2988 Playlist::uncombine (boost::shared_ptr<Region> target)
2989 {
2990         boost::shared_ptr<PlaylistSource> pls;
2991         boost::shared_ptr<const Playlist> pl;
2992         vector<boost::shared_ptr<Region> > originals;
2993         vector<TwoRegions> old_and_new_regions;
2994
2995         // (1) check that its really a compound region
2996
2997         if ((pls = boost::dynamic_pointer_cast<PlaylistSource>(target->source (0))) == 0) {
2998                 return;
2999         }
3000
3001         pl = pls->playlist();
3002
3003         framepos_t adjusted_start = 0; // gcc isn't smart enough
3004         framepos_t adjusted_end = 0;   // gcc isn't smart enough
3005
3006         /* the leftmost (earliest) edge of the compound region
3007            starts at zero in its source, or larger if it
3008            has been trimmed or content-scrolled.
3009
3010            the rightmost (latest) edge of the compound region
3011            relative to its source is the starting point plus
3012            the length of the region.
3013         */
3014
3015         // (2) get all the original regions
3016
3017         const RegionList& rl (pl->region_list().rlist());
3018         RegionFactory::CompoundAssociations& cassocs (RegionFactory::compound_associations());
3019         frameoffset_t move_offset = 0;
3020
3021         /* there are two possibilities here:
3022            1) the playlist that the playlist source was based on
3023            is us, so just add the originals (which belonged to
3024            us anyway) back in the right place.
3025
3026            2) the playlist that the playlist source was based on
3027            is NOT us, so we need to make copies of each of
3028            the original regions that we find, and add them
3029            instead.
3030         */
3031         bool same_playlist = (pls->original() == id());
3032
3033         for (RegionList::const_iterator i = rl.begin(); i != rl.end(); ++i) {
3034
3035                 boost::shared_ptr<Region> current (*i);
3036
3037                 RegionFactory::CompoundAssociations::iterator ca = cassocs.find (*i);
3038
3039                 if (ca == cassocs.end()) {
3040                         continue;
3041                 }
3042
3043                 boost::shared_ptr<Region> original (ca->second);
3044                 bool modified_region;
3045
3046                 if (i == rl.begin()) {
3047                         move_offset = (target->position() - original->position()) - target->start();
3048                         adjusted_start = original->position() + target->start();
3049                         adjusted_end = adjusted_start + target->length();
3050                 }
3051
3052                 if (!same_playlist) {
3053                         framepos_t pos = original->position();
3054                         /* make a copy, but don't announce it */
3055                         original = RegionFactory::create (original, false);
3056                         /* the pure copy constructor resets position() to zero,
3057                            so fix that up.
3058                         */
3059                         original->set_position (pos);
3060                 }
3061
3062                 /* check to see how the original region (in the
3063                  * playlist before compounding occured) overlaps
3064                  * with the new state of the compound region.
3065                  */
3066
3067                 original->clear_changes ();
3068                 modified_region = false;
3069
3070                 switch (original->coverage (adjusted_start, adjusted_end)) {
3071                 case OverlapNone:
3072                         /* original region does not cover any part
3073                            of the current state of the compound region
3074                         */
3075                         continue;
3076
3077                 case OverlapInternal:
3078                         /* overlap is just a small piece inside the
3079                          * original so trim both ends
3080                          */
3081                         original->trim_to (adjusted_start, adjusted_end - adjusted_start);
3082                         modified_region = true;
3083                         break;
3084
3085                 case OverlapExternal:
3086                         /* overlap fully covers original, so leave it
3087                            as is
3088                         */
3089                         break;
3090
3091                 case OverlapEnd:
3092                         /* overlap starts within but covers end,
3093                            so trim the front of the region
3094                         */
3095                         original->trim_front (adjusted_start);
3096                         modified_region = true;
3097                         break;
3098
3099                 case OverlapStart:
3100                         /* overlap covers start but ends within, so
3101                          * trim the end of the region.
3102                          */
3103                         original->trim_end (adjusted_end);
3104                         modified_region = true;
3105                         break;
3106                 }
3107
3108                 if (move_offset) {
3109                         /* fix the position to match any movement of the compound region.
3110                          */
3111                         original->set_position (original->position() + move_offset);
3112                         modified_region = true;
3113                 }
3114
3115                 if (modified_region) {
3116                         _session.add_command (new StatefulDiffCommand (original));
3117                 }
3118
3119                 /* and add to the list of regions waiting to be
3120                  * re-inserted
3121                  */
3122
3123                 originals.push_back (original);
3124                 old_and_new_regions.push_back (TwoRegions (*i, original));
3125         }
3126
3127         pre_uncombine (originals, target);
3128
3129         in_partition = true;
3130         freeze ();
3131
3132         // (3) remove the compound region
3133
3134         remove_region (target);
3135
3136         // (4) add the constituent regions
3137
3138         for (vector<boost::shared_ptr<Region> >::iterator i = originals.begin(); i != originals.end(); ++i) {
3139                 add_region ((*i), (*i)->position());
3140         }
3141
3142         /* now move dependent regions back from the compound to this playlist */
3143
3144         pl->copy_dependents (old_and_new_regions, this);
3145
3146         in_partition = false;
3147         thaw ();
3148 }
3149
3150 uint32_t
3151 Playlist::max_source_level () const
3152 {
3153         RegionLock rlock (const_cast<Playlist *> (this));
3154         uint32_t lvl = 0;
3155
3156         for (RegionList::const_iterator i = regions.begin(); i != regions.end(); ++i) {
3157                 lvl = max (lvl, (*i)->max_source_level());
3158         }
3159
3160         return lvl;
3161 }
3162
3163
3164 uint32_t
3165 Playlist::count_joined_regions () const
3166 {
3167         RegionLock rlock (const_cast<Playlist *> (this));
3168         uint32_t cnt = 0;
3169
3170         for (RegionList::const_iterator i = regions.begin(); i != regions.end(); ++i) {
3171                 if ((*i)->max_source_level() > 0) {
3172                         cnt++;
3173                 }
3174         }
3175
3176         return cnt;
3177 }
3178
3179 void
3180 Playlist::set_orig_track_id (const PBD::ID& id)
3181 {
3182         _orig_track_id = id;
3183 }
3184
3185 uint64_t
3186 Playlist::highest_layering_index () const
3187 {
3188         RegionLock rlock (const_cast<Playlist *> (this));
3189
3190         uint64_t h = 0;
3191         for (RegionList::const_iterator i = regions.begin(); i != regions.end(); ++i) {
3192                 h = max (h, (*i)->layering_index ());
3193         }
3194
3195         return h;
3196 }
3197