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