radically rethink export/bounce/freeze code design. probably not 100% done by freeze...
[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 boost::shared_ptr<RegionList>
1638 Playlist::regions_at (framepos_t frame)
1639 {
1640         RegionLock rlock (this);
1641         return find_regions_at (frame);
1642 }
1643
1644  uint32_t
1645  Playlist::count_regions_at (framepos_t frame) const
1646  {
1647          RegionLock rlock (const_cast<Playlist*>(this));
1648          uint32_t cnt = 0;
1649
1650          for (RegionList::const_iterator i = regions.begin(); i != regions.end(); ++i) {
1651                  if ((*i)->covers (frame)) {
1652                          cnt++;
1653                  }
1654          }
1655
1656          return cnt;
1657  }
1658
1659  boost::shared_ptr<Region>
1660  Playlist::top_region_at (framepos_t frame)
1661
1662  {
1663          RegionLock rlock (this);
1664          boost::shared_ptr<RegionList> rlist = find_regions_at (frame);
1665          boost::shared_ptr<Region> region;
1666
1667          if (rlist->size()) {
1668                  RegionSortByLayer cmp;
1669                  rlist->sort (cmp);
1670                  region = rlist->back();
1671          }
1672
1673          return region;
1674  }
1675
1676  boost::shared_ptr<Region>
1677  Playlist::top_unmuted_region_at (framepos_t frame)
1678
1679  {
1680          RegionLock rlock (this);
1681          boost::shared_ptr<RegionList> rlist = find_regions_at (frame);
1682
1683          for (RegionList::iterator i = rlist->begin(); i != rlist->end(); ) {
1684
1685                  RegionList::iterator tmp = i;
1686                  ++tmp;
1687
1688                  if ((*i)->muted()) {
1689                          rlist->erase (i);
1690                  }
1691
1692                  i = tmp;
1693          }
1694
1695          boost::shared_ptr<Region> region;
1696
1697          if (rlist->size()) {
1698                  RegionSortByLayer cmp;
1699                  rlist->sort (cmp);
1700                  region = rlist->back();
1701          }
1702
1703          return region;
1704  }
1705
1706 boost::shared_ptr<RegionList>
1707 Playlist::regions_to_read (framepos_t start, framepos_t end)
1708 {
1709          /* Caller must hold lock */
1710
1711          RegionList covering;
1712          set<framepos_t> to_check;
1713          set<boost::shared_ptr<Region> > unique;
1714
1715          to_check.insert (start);
1716          to_check.insert (end);
1717
1718          DEBUG_TRACE (DEBUG::AudioPlayback, ">>>>> REGIONS TO READ\n");
1719
1720          for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
1721
1722                  /* find all/any regions that span start+end */
1723
1724                  switch ((*i)->coverage (start, end)) {
1725                  case OverlapNone:
1726                          break;
1727
1728                  case OverlapInternal:
1729                          covering.push_back (*i);
1730                          DEBUG_TRACE (DEBUG::AudioPlayback, string_compose ("toread: will cover %1 (OInternal)\n", (*i)->name()));
1731                          break;
1732
1733                  case OverlapStart:
1734                          to_check.insert ((*i)->position());
1735                          if ((*i)->position() != 0) {
1736                                  to_check.insert ((*i)->position()-1);
1737                          }
1738                          DEBUG_TRACE (DEBUG::AudioPlayback, string_compose ("toread: will check %1 for %2\n", (*i)->position(), (*i)->name()));
1739                          covering.push_back (*i);
1740                          break;
1741
1742                  case OverlapEnd:
1743                          to_check.insert ((*i)->last_frame());
1744                          to_check.insert ((*i)->last_frame()+1);
1745                          DEBUG_TRACE (DEBUG::AudioPlayback, string_compose ("toread: will cover %1 (OEnd)\n", (*i)->name()));
1746                          DEBUG_TRACE (DEBUG::AudioPlayback, string_compose ("\ttoread: will check %1 for %2\n", (*i)->last_frame(), (*i)->name()));
1747                          DEBUG_TRACE (DEBUG::AudioPlayback, string_compose ("\ttoread: will check %1 for %2\n", (*i)->last_frame(), (*i)->name()));
1748                          covering.push_back (*i);
1749                          break;
1750
1751                  case OverlapExternal:
1752                          covering.push_back (*i);
1753                          to_check.insert ((*i)->position());
1754                          if ((*i)->position() != 0) {
1755                                  to_check.insert ((*i)->position()-1);
1756                          }
1757                          to_check.insert ((*i)->last_frame());
1758                          to_check.insert ((*i)->last_frame()+1);
1759                          DEBUG_TRACE (DEBUG::AudioPlayback, string_compose ("toread: will cover %1 (OExt)\n", (*i)->name()));
1760                          DEBUG_TRACE (DEBUG::AudioPlayback, string_compose ("\ttoread: will check %1 for %2\n", (*i)->position(), (*i)->name()));
1761                          DEBUG_TRACE (DEBUG::AudioPlayback, string_compose ("\ttoread: will check %1 for %2\n", (*i)->last_frame(), (*i)->name()));
1762                          break;
1763                  }
1764
1765                  /* don't go too far */
1766
1767                  if ((*i)->position() > end) {
1768                          break;
1769                  }
1770          }
1771
1772          boost::shared_ptr<RegionList> rlist (new RegionList);
1773
1774          /* find all the regions that cover each position .... */
1775
1776          if (covering.size() == 1) {
1777
1778                  rlist->push_back (covering.front());
1779                  DEBUG_TRACE (DEBUG::AudioPlayback, string_compose ("Just one covering region (%1)\n", covering.front()->name()));
1780
1781          } else {
1782
1783                  RegionList here;
1784                  for (set<framepos_t>::iterator t = to_check.begin(); t != to_check.end(); ++t) {
1785
1786                          here.clear ();
1787
1788                          DEBUG_TRACE (DEBUG::AudioPlayback, string_compose ("++++ Considering %1\n", *t));
1789
1790                          for (RegionList::iterator x = covering.begin(); x != covering.end(); ++x) {
1791
1792                                  if ((*x)->covers (*t)) {
1793                                          here.push_back (*x);
1794                                          DEBUG_TRACE (DEBUG::AudioPlayback, string_compose ("region %1 covers %2\n",
1795                                                                                             (*x)->name(),
1796                                                                                             (*t)));
1797                                  } else {
1798                                          DEBUG_TRACE (DEBUG::AudioPlayback, string_compose ("region %1 does NOT covers %2\n",
1799                                                                                             (*x)->name(),
1800                                                                                             (*t)));
1801                                  }
1802
1803                          }
1804
1805                          RegionSortByLayer cmp;
1806                          here.sort (cmp);
1807
1808                          /* ... and get the top/transparent regions at "here" */
1809
1810                          for (RegionList::reverse_iterator c = here.rbegin(); c != here.rend(); ++c) {
1811
1812                                  unique.insert (*c);
1813
1814                                  if ((*c)->opaque()) {
1815
1816                                          /* the other regions at this position are hidden by this one */
1817                                          DEBUG_TRACE (DEBUG::AudioPlayback, string_compose ("%1 is opaque, ignore all others\n",
1818                                                                                             (*c)->name()));
1819                                          break;
1820                                  }
1821                          }
1822                  }
1823
1824                  for (set<boost::shared_ptr<Region> >::iterator s = unique.begin(); s != unique.end(); ++s) {
1825                          rlist->push_back (*s);
1826                  }
1827
1828                  if (rlist->size() > 1) {
1829                          /* now sort by time order */
1830
1831                          RegionSortByPosition cmp;
1832                          rlist->sort (cmp);
1833                  }
1834          }
1835
1836          DEBUG_TRACE (DEBUG::AudioPlayback, string_compose ("<<<<< REGIONS TO READ returns %1\n", rlist->size()));
1837
1838          return rlist;
1839  }
1840
1841 boost::shared_ptr<RegionList>
1842 Playlist::find_regions_at (framepos_t frame)
1843 {
1844         /* Caller must hold lock */
1845         
1846         boost::shared_ptr<RegionList> rlist (new RegionList);
1847
1848         for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
1849                 if ((*i)->covers (frame)) {
1850                         rlist->push_back (*i);
1851                 }
1852         }
1853         
1854         return rlist;
1855 }
1856
1857 boost::shared_ptr<RegionList>
1858 Playlist::regions_touched (framepos_t start, framepos_t end)
1859 {
1860         RegionLock rlock (this);
1861         boost::shared_ptr<RegionList> rlist (new RegionList);
1862         
1863         for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
1864                 if ((*i)->coverage (start, end) != OverlapNone) {
1865                         rlist->push_back (*i);
1866                 }
1867         }
1868         
1869          return rlist;
1870 }
1871
1872  framepos_t
1873  Playlist::find_next_transient (framepos_t from, int dir)
1874  {
1875          RegionLock rlock (this);
1876          AnalysisFeatureList points;
1877          AnalysisFeatureList these_points;
1878
1879          for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
1880                  if (dir > 0) {
1881                          if ((*i)->last_frame() < from) {
1882                                  continue;
1883                          }
1884                  } else {
1885                          if ((*i)->first_frame() > from) {
1886                                  continue;
1887                          }
1888                  }
1889
1890                  (*i)->get_transients (these_points);
1891
1892                  /* add first frame, just, err, because */
1893
1894                  these_points.push_back ((*i)->first_frame());
1895
1896                  points.insert (points.end(), these_points.begin(), these_points.end());
1897                  these_points.clear ();
1898          }
1899
1900          if (points.empty()) {
1901                  return -1;
1902          }
1903
1904          TransientDetector::cleanup_transients (points, _session.frame_rate(), 3.0);
1905          bool reached = false;
1906
1907          if (dir > 0) {
1908                  for (AnalysisFeatureList::iterator x = points.begin(); x != points.end(); ++x) {
1909                          if ((*x) >= from) {
1910                                  reached = true;
1911                          }
1912
1913                          if (reached && (*x) > from) {
1914                                  return *x;
1915                          }
1916                  }
1917          } else {
1918                  for (AnalysisFeatureList::reverse_iterator x = points.rbegin(); x != points.rend(); ++x) {
1919                          if ((*x) <= from) {
1920                                  reached = true;
1921                          }
1922
1923                          if (reached && (*x) < from) {
1924                                  return *x;
1925                          }
1926                  }
1927          }
1928
1929          return -1;
1930  }
1931
1932  boost::shared_ptr<Region>
1933  Playlist::find_next_region (framepos_t frame, RegionPoint point, int dir)
1934  {
1935          RegionLock rlock (this);
1936          boost::shared_ptr<Region> ret;
1937          framepos_t closest = max_framepos;
1938
1939          bool end_iter = false;
1940
1941          for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
1942
1943                  if(end_iter) break;
1944
1945                  frameoffset_t distance;
1946                  boost::shared_ptr<Region> r = (*i);
1947                  framepos_t pos = 0;
1948
1949                  switch (point) {
1950                  case Start:
1951                          pos = r->first_frame ();
1952                          break;
1953                  case End:
1954                          pos = r->last_frame ();
1955                          break;
1956                  case SyncPoint:
1957                          pos = r->sync_position ();
1958                          break;
1959                  }
1960
1961                  switch (dir) {
1962                  case 1: /* forwards */
1963
1964                          if (pos > frame) {
1965                                  if ((distance = pos - frame) < closest) {
1966                                          closest = distance;
1967                                          ret = r;
1968                                          end_iter = true;
1969                                  }
1970                          }
1971
1972                          break;
1973
1974                  default: /* backwards */
1975
1976                          if (pos < frame) {
1977                                  if ((distance = frame - pos) < closest) {
1978                                          closest = distance;
1979                                          ret = r;
1980                                  }
1981                          }
1982                          else {
1983                                  end_iter = true;
1984                          }
1985
1986                          break;
1987                  }
1988          }
1989
1990          return ret;
1991  }
1992
1993  framepos_t
1994  Playlist::find_next_region_boundary (framepos_t frame, int dir)
1995  {
1996          RegionLock rlock (this);
1997
1998          framepos_t closest = max_framepos;
1999          framepos_t ret = -1;
2000
2001          if (dir > 0) {
2002
2003                  for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
2004
2005                          boost::shared_ptr<Region> r = (*i);
2006                          frameoffset_t distance;
2007
2008                          if (r->first_frame() > frame) {
2009
2010                                  distance = r->first_frame() - frame;
2011
2012                                  if (distance < closest) {
2013                                          ret = r->first_frame();
2014                                          closest = distance;
2015                                  }
2016                          }
2017
2018                          if (r->last_frame () > frame) {
2019
2020                                  distance = r->last_frame () - frame;
2021
2022                                  if (distance < closest) {
2023                                          ret = r->last_frame ();
2024                                          closest = distance;
2025                                  }
2026                          }
2027                  }
2028
2029          } else {
2030
2031                  for (RegionList::reverse_iterator i = regions.rbegin(); i != regions.rend(); ++i) {
2032
2033                          boost::shared_ptr<Region> r = (*i);
2034                          frameoffset_t distance;
2035
2036                          if (r->last_frame() < frame) {
2037
2038                                  distance = frame - r->last_frame();
2039
2040                                  if (distance < closest) {
2041                                          ret = r->last_frame();
2042                                          closest = distance;
2043                                  }
2044                          }
2045
2046                          if (r->first_frame() < frame) {
2047
2048                                  distance = frame - r->first_frame();
2049
2050                                  if (distance < closest) {
2051                                          ret = r->first_frame();
2052                                          closest = distance;
2053                                  }
2054                          }
2055                  }
2056          }
2057
2058          return ret;
2059  }
2060
2061
2062  /***********************************************************************/
2063
2064
2065
2066
2067  void
2068  Playlist::mark_session_dirty ()
2069  {
2070          if (!in_set_state && !holding_state ()) {
2071                  _session.set_dirty();
2072          }
2073  }
2074
2075  void
2076  Playlist::rdiff (vector<Command*>& cmds) const
2077  {
2078          RegionLock rlock (const_cast<Playlist *> (this));
2079          Stateful::rdiff (cmds);
2080  }
2081
2082  void
2083  Playlist::clear_owned_changes ()
2084  {
2085          RegionLock rlock (this);
2086          Stateful::clear_owned_changes ();
2087  }
2088
2089  void
2090  Playlist::update (const RegionListProperty::ChangeRecord& change)
2091  {
2092          DEBUG_TRACE (DEBUG::Properties, string_compose ("Playlist %1 updates from a change record with %2 adds %3 removes\n",
2093                                                          name(), change.added.size(), change.removed.size()));
2094
2095          freeze ();
2096          /* add the added regions */
2097          for (RegionListProperty::ChangeContainer::iterator i = change.added.begin(); i != change.added.end(); ++i) {
2098                  add_region_internal ((*i), (*i)->position());
2099          }
2100          /* remove the removed regions */
2101          for (RegionListProperty::ChangeContainer::iterator i = change.removed.begin(); i != change.removed.end(); ++i) {
2102                  remove_region (*i);
2103          }
2104
2105          thaw ();
2106  }
2107
2108  int
2109  Playlist::set_state (const XMLNode& node, int version)
2110  {
2111          XMLNode *child;
2112          XMLNodeList nlist;
2113          XMLNodeConstIterator niter;
2114          XMLPropertyList plist;
2115          XMLPropertyConstIterator piter;
2116          XMLProperty *prop;
2117          boost::shared_ptr<Region> region;
2118          string region_name;
2119          bool seen_region_nodes = false;
2120          int ret = 0;
2121
2122          in_set_state++;
2123
2124          if (node.name() != "Playlist") {
2125                  in_set_state--;
2126                  return -1;
2127          }
2128
2129          freeze ();
2130
2131          plist = node.properties();
2132
2133          set_id (node);
2134
2135          for (piter = plist.begin(); piter != plist.end(); ++piter) {
2136
2137                  prop = *piter;
2138
2139                  if (prop->name() == X_("name")) {
2140                          _name = prop->value();
2141                          _set_sort_id ();
2142                  } else if (prop->name() == X_("orig-diskstream-id")) {
2143                          /* XXX legacy session: fix up later */
2144                          _orig_track_id = prop->value ();
2145                  } else if (prop->name() == X_("orig-track-id")) {
2146                          _orig_track_id = prop->value ();
2147                  } else if (prop->name() == X_("frozen")) {
2148                          _frozen = string_is_affirmative (prop->value());
2149                  } else if (prop->name() == X_("combine-ops")) {
2150                          _combine_ops = atoi (prop->value());
2151                  }
2152          }
2153
2154          clear (true);
2155
2156          nlist = node.children();
2157
2158          for (niter = nlist.begin(); niter != nlist.end(); ++niter) {
2159
2160                  child = *niter;
2161
2162                  if (child->name() == "Region") {
2163
2164                          seen_region_nodes = true;
2165
2166                          if ((prop = child->property ("id")) == 0) {
2167                                  error << _("region state node has no ID, ignored") << endmsg;
2168                                  continue;
2169                          }
2170
2171                          ID id = prop->value ();
2172
2173                          if ((region = region_by_id (id))) {
2174
2175                                  region->suspend_property_changes ();
2176
2177                                  if (region->set_state (*child, version)) {
2178                                          region->resume_property_changes ();
2179                                          continue;
2180                                  }
2181
2182                          } else if ((region = RegionFactory::create (_session, *child, true)) != 0) {
2183                                  region->suspend_property_changes ();
2184                          } else {
2185                                  error << _("Playlist: cannot create region from XML") << endmsg;
2186                                 return -1;
2187                         }
2188
2189                          {
2190                                  RegionLock rlock (this);
2191                                  add_region_internal (region, region->position());
2192                          }
2193                         
2194                         region->resume_property_changes ();
2195
2196                 }
2197         }
2198
2199         if (seen_region_nodes && regions.empty()) {
2200                 ret = -1;
2201         } else {
2202
2203                 /* update dependents, which was not done during add_region_internal
2204                    due to in_set_state being true
2205                 */
2206                 
2207                 for (RegionList::iterator r = regions.begin(); r != regions.end(); ++r) {
2208                         check_dependents (*r, false);
2209                 }
2210         }
2211                 
2212         thaw ();
2213         notify_contents_changed ();
2214
2215         in_set_state--;
2216         first_set_state = false;
2217
2218         return ret;
2219 }
2220
2221 XMLNode&
2222 Playlist::get_state()
2223 {
2224         return state (true);
2225 }
2226
2227 XMLNode&
2228 Playlist::get_template()
2229 {
2230         return state (false);
2231 }
2232
2233 /** @param full_state true to include regions in the returned state, otherwise false.
2234  */
2235 XMLNode&
2236 Playlist::state (bool full_state)
2237 {
2238         XMLNode *node = new XMLNode (X_("Playlist"));
2239         char buf[64];
2240
2241         node->add_property (X_("id"), id().to_s());
2242         node->add_property (X_("name"), _name);
2243         node->add_property (X_("type"), _type.to_string());
2244
2245         _orig_track_id.print (buf, sizeof (buf));
2246         node->add_property (X_("orig-track-id"), buf);
2247         node->add_property (X_("frozen"), _frozen ? "yes" : "no");
2248
2249         if (full_state) {
2250                 RegionLock rlock (this, false);
2251
2252                 snprintf (buf, sizeof (buf), "%u", _combine_ops);
2253                 node->add_property ("combine-ops", buf);
2254
2255                 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
2256                         node->add_child_nocopy ((*i)->get_state());
2257                 }
2258         }
2259
2260         if (_extra_xml) {
2261                 node->add_child_copy (*_extra_xml);
2262         }
2263
2264         return *node;
2265 }
2266
2267 bool
2268 Playlist::empty() const
2269 {
2270         RegionLock rlock (const_cast<Playlist *>(this), false);
2271         return regions.empty();
2272 }
2273
2274 uint32_t
2275 Playlist::n_regions() const
2276 {
2277         RegionLock rlock (const_cast<Playlist *>(this), false);
2278         return regions.size();
2279 }
2280
2281 pair<framepos_t, framepos_t>
2282 Playlist::get_extent () const
2283 {
2284         RegionLock rlock (const_cast<Playlist *>(this), false);
2285         return _get_extent ();
2286 }
2287
2288 pair<framepos_t, framepos_t>
2289 Playlist::_get_extent () const
2290 {
2291         pair<framepos_t, framepos_t> ext (max_framepos, 0);
2292
2293         if (regions.empty()) {
2294                 ext.first = 0;
2295                 return ext;
2296         }
2297
2298         for (RegionList::const_iterator i = regions.begin(); i != regions.end(); ++i) {
2299                 pair<framepos_t, framepos_t> const e ((*i)->position(), (*i)->position() + (*i)->length());
2300                 if (e.first < ext.first) {
2301                         ext.first = e.first;
2302                 }
2303                 if (e.second > ext.second) {
2304                         ext.second = e.second;
2305                 }
2306         }
2307
2308         return ext;
2309 }
2310
2311 string
2312 Playlist::bump_name (string name, Session &session)
2313 {
2314         string newname = name;
2315
2316         do {
2317                 newname = bump_name_once (newname, '.');
2318         } while (session.playlists->by_name (newname)!=NULL);
2319
2320         return newname;
2321 }
2322
2323
2324 layer_t
2325 Playlist::top_layer() const
2326 {
2327         RegionLock rlock (const_cast<Playlist *> (this));
2328         layer_t top = 0;
2329
2330         for (RegionList::const_iterator i = regions.begin(); i != regions.end(); ++i) {
2331                 top = max (top, (*i)->layer());
2332         }
2333         return top;
2334 }
2335
2336 void
2337 Playlist::set_edit_mode (EditMode mode)
2338 {
2339         _edit_mode = mode;
2340 }
2341
2342 struct RelayerSort {
2343         bool operator () (boost::shared_ptr<Region> a, boost::shared_ptr<Region> b) {
2344                 return a->layering_index() < b->layering_index();
2345         }
2346 };
2347
2348 /** Set a new layer for a region.  This adjusts the layering indices of all
2349  *  regions in the playlist to put the specified region in the appropriate
2350  *  place.  The actual layering will be fixed up when relayer() happens.
2351  */
2352
2353 void
2354 Playlist::set_layer (boost::shared_ptr<Region> region, double new_layer)
2355 {
2356         /* Remove the layer we are setting from our region list, and sort it */
2357         RegionList copy = regions.rlist();
2358         copy.remove (region);
2359         copy.sort (RelayerSort ());
2360
2361         /* Put region back in the right place */
2362         RegionList::iterator i = copy.begin();
2363         while (i != copy.end ()) {
2364                 if ((*i)->layer() > new_layer) {
2365                         break;
2366                 }
2367                 ++i;
2368         }
2369         
2370         copy.insert (i, region);
2371
2372         setup_layering_indices (copy);
2373 }
2374
2375 void
2376 Playlist::setup_layering_indices (RegionList const & regions) const
2377 {
2378         uint64_t j = 0;
2379         for (RegionList::const_iterator k = regions.begin(); k != regions.end(); ++k) {
2380                 (*k)->set_layering_index (j++);
2381         }
2382 }
2383
2384
2385 /** Take the layering indices of each of our regions, compute the layers
2386  *  that they should be on, and write the layers back to the regions.
2387  */
2388 void
2389 Playlist::relayer ()
2390 {
2391         /* never compute layers when setting from XML */
2392
2393         if (in_set_state) {
2394                 return;
2395         }
2396
2397         /* Build up a new list of regions on each layer, stored in a set of lists
2398            each of which represent some period of time on some layer.  The idea
2399            is to avoid having to search the entire region list to establish whether
2400            each region overlaps another */
2401
2402         /* how many pieces to divide this playlist's time up into */
2403         int const divisions = 512;
2404
2405         /* find the start and end positions of the regions on this playlist */
2406         framepos_t start = INT64_MAX;
2407         framepos_t end = 0;
2408         for (RegionList::const_iterator i = regions.begin(); i != regions.end(); ++i) {
2409                 start = min (start, (*i)->position());
2410                 end = max (end, (*i)->position() + (*i)->length());
2411         }
2412
2413         /* hence the size of each time division */
2414         double const division_size = (end - start) / double (divisions);
2415
2416         vector<vector<RegionList> > layers;
2417         layers.push_back (vector<RegionList> (divisions));
2418
2419         /* Sort our regions into layering index order */
2420         RegionList copy = regions.rlist();
2421         copy.sort (RelayerSort ());
2422
2423         DEBUG_TRACE (DEBUG::Layering, "relayer() using:\n");
2424         for (RegionList::iterator i = copy.begin(); i != copy.end(); ++i) {
2425                 DEBUG_TRACE (DEBUG::Layering, string_compose ("\t%1 %2\n", (*i)->name(), (*i)->layering_index()));
2426         }
2427
2428         for (RegionList::iterator i = copy.begin(); i != copy.end(); ++i) {
2429
2430                 /* find the time divisions that this region covers; if there are no regions on the list,
2431                    division_size will equal 0 and in this case we'll just say that
2432                    start_division = end_division = 0.
2433                 */
2434                 int start_division = 0;
2435                 int end_division = 0;
2436
2437                 if (division_size > 0) {
2438                         start_division = floor ( ((*i)->position() - start) / division_size);
2439                         end_division = floor ( ((*i)->position() + (*i)->length() - start) / division_size );
2440                         if (end_division == divisions) {
2441                                 end_division--;
2442                         }
2443                 }
2444
2445                 assert (divisions == 0 || end_division < divisions);
2446
2447                 /* find the lowest layer that this region can go on */
2448                 size_t j = layers.size();
2449                 while (j > 0) {
2450                         /* try layer j - 1; it can go on if it overlaps no other region
2451                            that is already on that layer
2452                         */
2453
2454                         bool overlap = false;
2455                         for (int k = start_division; k <= end_division; ++k) {
2456                                 RegionList::iterator l = layers[j-1][k].begin ();
2457                                 while (l != layers[j-1][k].end()) {
2458                                         if ((*l)->overlap_equivalent (*i)) {
2459                                                 overlap = true;
2460                                                 break;
2461                                         }
2462                                         l++;
2463                                 }
2464
2465                                 if (overlap) {
2466                                         break;
2467                                 }
2468                         }
2469
2470                         if (overlap) {
2471                                 /* overlap, so we must use layer j */
2472                                 break;
2473                         }
2474
2475                         --j;
2476                 }
2477
2478                 if (j == layers.size()) {
2479                         /* we need a new layer for this region */
2480                         layers.push_back (vector<RegionList> (divisions));
2481                 }
2482
2483                 /* put a reference to this region in each of the divisions that it exists in */
2484                 for (int k = start_division; k <= end_division; ++k) {
2485                         layers[j][k].push_back (*i);
2486                 }
2487
2488                 (*i)->set_layer (j);
2489         }
2490
2491         /* It's a little tricky to know when we could avoid calling this; e.g. if we are
2492            relayering because we just removed the only region on the top layer, nothing will
2493            appear to have changed, but the StreamView must still sort itself out.  We could
2494            probably keep a note of the top layer last time we relayered, and check that,
2495            but premature optimisation &c...
2496         */
2497         notify_layering_changed ();
2498
2499         /* This relayer() may have been called as a result of a region removal, in which
2500            case we need to setup layering indices so account for the one that has just
2501            gone away.
2502         */
2503         setup_layering_indices (copy);
2504 }
2505
2506 void
2507 Playlist::raise_region (boost::shared_ptr<Region> region)
2508 {
2509         set_layer (region, region->layer() + 1.5);
2510         relayer ();
2511 }
2512
2513 void
2514 Playlist::lower_region (boost::shared_ptr<Region> region)
2515 {
2516         set_layer (region, region->layer() - 1.5);
2517         relayer ();
2518 }
2519
2520 void
2521 Playlist::raise_region_to_top (boost::shared_ptr<Region> region)
2522 {
2523         set_layer (region, DBL_MAX);
2524         relayer ();
2525 }
2526
2527 void
2528 Playlist::lower_region_to_bottom (boost::shared_ptr<Region> region)
2529 {
2530         set_layer (region, -0.5);
2531         relayer ();
2532 }
2533
2534 void
2535 Playlist::nudge_after (framepos_t start, framecnt_t distance, bool forwards)
2536 {
2537         RegionList::iterator i;
2538         bool moved = false;
2539
2540         _nudging = true;
2541
2542         {
2543                 RegionLock rlock (const_cast<Playlist *> (this));
2544
2545                 for (i = regions.begin(); i != regions.end(); ++i) {
2546
2547                         if ((*i)->position() >= start) {
2548
2549                                 framepos_t new_pos;
2550
2551                                 if (forwards) {
2552
2553                                         if ((*i)->last_frame() > max_framepos - distance) {
2554                                                 new_pos = max_framepos - (*i)->length();
2555                                         } else {
2556                                                 new_pos = (*i)->position() + distance;
2557                                         }
2558
2559                                 } else {
2560
2561                                         if ((*i)->position() > distance) {
2562                                                 new_pos = (*i)->position() - distance;
2563                                         } else {
2564                                                 new_pos = 0;
2565                                         }
2566                                 }
2567
2568                                 (*i)->set_position (new_pos);
2569                                 moved = true;
2570                         }
2571                 }
2572         }
2573
2574         if (moved) {
2575                 _nudging = false;
2576                 notify_contents_changed ();
2577         }
2578
2579 }
2580
2581 bool
2582 Playlist::uses_source (boost::shared_ptr<const Source> src) const
2583 {
2584         RegionLock rlock (const_cast<Playlist*> (this));
2585
2586         for (set<boost::shared_ptr<Region> >::iterator r = all_regions.begin(); r != all_regions.end(); ++r) {
2587                 if ((*r)->uses_source (src)) {
2588                         return true;
2589                 }
2590         }
2591
2592         return false;
2593 }
2594
2595 boost::shared_ptr<Region>
2596 Playlist::find_region (const ID& id) const
2597 {
2598         RegionLock rlock (const_cast<Playlist*> (this));
2599
2600         /* searches all regions currently in use by the playlist */
2601
2602         for (RegionList::const_iterator i = regions.begin(); i != regions.end(); ++i) {
2603                 if ((*i)->id() == id) {
2604                         return *i;
2605                 }
2606         }
2607
2608         return boost::shared_ptr<Region> ();
2609 }
2610
2611 uint32_t
2612 Playlist::region_use_count (boost::shared_ptr<Region> r) const
2613 {
2614         RegionLock rlock (const_cast<Playlist*> (this));
2615         uint32_t cnt = 0;
2616
2617         for (RegionList::const_iterator i = regions.begin(); i != regions.end(); ++i) {
2618                 if ((*i) == r) {
2619                         cnt++;
2620                 }
2621         }
2622
2623         return cnt;
2624 }
2625
2626 boost::shared_ptr<Region>
2627 Playlist::region_by_id (const ID& id) const
2628 {
2629         /* searches all regions ever added to this playlist */
2630
2631         for (set<boost::shared_ptr<Region> >::iterator i = all_regions.begin(); i != all_regions.end(); ++i) {
2632                 if ((*i)->id() == id) {
2633                         return *i;
2634                 }
2635         }
2636         return boost::shared_ptr<Region> ();
2637 }
2638
2639 void
2640 Playlist::dump () const
2641 {
2642         boost::shared_ptr<Region> r;
2643
2644         cerr << "Playlist \"" << _name << "\" " << endl
2645              << regions.size() << " regions "
2646              << endl;
2647
2648         for (RegionList::const_iterator i = regions.begin(); i != regions.end(); ++i) {
2649                 r = *i;
2650                 cerr << "  " << r->name() << " ["
2651                      << r->start() << "+" << r->length()
2652                      << "] at "
2653                      << r->position()
2654                      << " on layer "
2655                      << r->layer ()
2656                      << endl;
2657         }
2658 }
2659
2660 void
2661 Playlist::set_frozen (bool yn)
2662 {
2663         _frozen = yn;
2664 }
2665
2666 void
2667 Playlist::shuffle (boost::shared_ptr<Region> region, int dir)
2668 {
2669         bool moved = false;
2670
2671         if (region->locked()) {
2672                 return;
2673         }
2674
2675         _shuffling = true;
2676
2677         {
2678                 RegionLock rlock (const_cast<Playlist*> (this));
2679
2680
2681                 if (dir > 0) {
2682
2683                         RegionList::iterator next;
2684
2685                         for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
2686                                 if ((*i) == region) {
2687                                         next = i;
2688                                         ++next;
2689
2690                                         if (next != regions.end()) {
2691
2692                                                 if ((*next)->locked()) {
2693                                                         break;
2694                                                 }
2695
2696                                                 framepos_t new_pos;
2697
2698                                                 if ((*next)->position() != region->last_frame() + 1) {
2699                                                         /* they didn't used to touch, so after shuffle,
2700                                                            just have them swap positions.
2701                                                         */
2702                                                         new_pos = (*next)->position();
2703                                                 } else {
2704                                                         /* they used to touch, so after shuffle,
2705                                                            make sure they still do. put the earlier
2706                                                            region where the later one will end after
2707                                                            it is moved.
2708                                                         */
2709                                                         new_pos = region->position() + (*next)->length();
2710                                                 }
2711
2712                                                 (*next)->set_position (region->position());
2713                                                 region->set_position (new_pos);
2714
2715                                                 /* avoid a full sort */
2716
2717                                                 regions.erase (i); // removes the region from the list */
2718                                                 next++;
2719                                                 regions.insert (next, region); // adds it back after next
2720
2721                                                 moved = true;
2722                                         }
2723                                         break;
2724                                 }
2725                         }
2726                 } else {
2727
2728                         RegionList::iterator prev = regions.end();
2729
2730                         for (RegionList::iterator i = regions.begin(); i != regions.end(); prev = i, ++i) {
2731                                 if ((*i) == region) {
2732
2733                                         if (prev != regions.end()) {
2734
2735                                                 if ((*prev)->locked()) {
2736                                                         break;
2737                                                 }
2738
2739                                                 framepos_t new_pos;
2740                                                 if (region->position() != (*prev)->last_frame() + 1) {
2741                                                         /* they didn't used to touch, so after shuffle,
2742                                                            just have them swap positions.
2743                                                         */
2744                                                         new_pos = region->position();
2745                                                 } else {
2746                                                         /* they used to touch, so after shuffle,
2747                                                            make sure they still do. put the earlier
2748                                                            one where the later one will end after
2749                                                         */
2750                                                         new_pos = (*prev)->position() + region->length();
2751                                                 }
2752
2753                                                 region->set_position ((*prev)->position());
2754                                                 (*prev)->set_position (new_pos);
2755
2756                                                 /* avoid a full sort */
2757
2758                                                 regions.erase (i); // remove region
2759                                                 regions.insert (prev, region); // insert region before prev
2760
2761                                                 moved = true;
2762                                         }
2763
2764                                         break;
2765                                 }
2766                         }
2767                 }
2768         }
2769
2770         _shuffling = false;
2771
2772         if (moved) {
2773
2774                 relayer ();
2775                 check_dependents (region, false);
2776
2777                 notify_contents_changed();
2778         }
2779
2780 }
2781
2782 bool
2783 Playlist::region_is_shuffle_constrained (boost::shared_ptr<Region>)
2784 {
2785         RegionLock rlock (const_cast<Playlist*> (this));
2786
2787         if (regions.size() > 1) {
2788                 return true;
2789         }
2790
2791         return false;
2792 }
2793
2794 void
2795 Playlist::update_after_tempo_map_change ()
2796 {
2797         RegionLock rlock (const_cast<Playlist*> (this));
2798         RegionList copy (regions.rlist());
2799
2800         freeze ();
2801
2802         for (RegionList::iterator i = copy.begin(); i != copy.end(); ++i) {
2803                 (*i)->update_after_tempo_map_change ();
2804         }
2805
2806         thaw ();
2807 }
2808
2809 void
2810 Playlist::foreach_region (boost::function<void(boost::shared_ptr<Region>)> s)
2811 {
2812         RegionLock rl (this, false);
2813         for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
2814                 s (*i);
2815         }
2816 }
2817
2818 bool
2819 Playlist::has_region_at (framepos_t const p) const
2820 {
2821         RegionLock (const_cast<Playlist *> (this));
2822
2823         RegionList::const_iterator i = regions.begin ();
2824         while (i != regions.end() && !(*i)->covers (p)) {
2825                 ++i;
2826         }
2827
2828         return (i != regions.end());
2829 }
2830
2831 /** Remove any region that uses a given source */
2832 void
2833 Playlist::remove_region_by_source (boost::shared_ptr<Source> s)
2834 {
2835         RegionLock rl (this);
2836
2837         RegionList::iterator i = regions.begin();
2838         while (i != regions.end()) {
2839                 RegionList::iterator j = i;
2840                 ++j;
2841
2842                 if ((*i)->uses_source (s)) {
2843                         remove_region_internal (*i);
2844                 }
2845
2846                 i = j;
2847         }
2848 }
2849
2850 /** Look from a session frame time and find the start time of the next region
2851  *  which is on the top layer of this playlist.
2852  *  @param t Time to look from.
2853  *  @return Position of next top-layered region, or max_framepos if there isn't one.
2854  */
2855 framepos_t
2856 Playlist::find_next_top_layer_position (framepos_t t) const
2857 {
2858         RegionLock rlock (const_cast<Playlist *> (this));
2859
2860         layer_t const top = top_layer ();
2861
2862         RegionList copy = regions.rlist ();
2863         copy.sort (RegionSortByPosition ());
2864
2865         for (RegionList::const_iterator i = copy.begin(); i != copy.end(); ++i) {
2866                 if ((*i)->position() >= t && (*i)->layer() == top) {
2867                         return (*i)->position();
2868                 }
2869         }
2870
2871         return max_framepos;
2872 }
2873
2874 boost::shared_ptr<Region>
2875 Playlist::combine (const RegionList& r)
2876 {
2877         PropertyList plist;
2878         uint32_t channels = 0;
2879         uint32_t layer = 0;
2880         framepos_t earliest_position = max_framepos;
2881         vector<TwoRegions> old_and_new_regions;
2882         vector<boost::shared_ptr<Region> > originals;
2883         vector<boost::shared_ptr<Region> > copies;
2884         string parent_name;
2885         string child_name;
2886         uint32_t max_level = 0;
2887
2888         /* find the maximum depth of all the regions we're combining */
2889
2890         for (RegionList::const_iterator i = r.begin(); i != r.end(); ++i) {
2891                 max_level = max (max_level, (*i)->max_source_level());
2892         }
2893
2894         parent_name = RegionFactory::compound_region_name (name(), combine_ops(), max_level, true);
2895         child_name = RegionFactory::compound_region_name (name(), combine_ops(), max_level, false);
2896
2897         boost::shared_ptr<Playlist> pl = PlaylistFactory::create (_type, _session, parent_name, true);
2898
2899         for (RegionList::const_iterator i = r.begin(); i != r.end(); ++i) {
2900                 earliest_position = min (earliest_position, (*i)->position());
2901         }
2902
2903         /* enable this so that we do not try to create xfades etc. as we add
2904          * regions
2905          */
2906
2907         pl->in_partition = true;
2908
2909         for (RegionList::const_iterator i = r.begin(); i != r.end(); ++i) {
2910
2911                 /* copy the region */
2912
2913                 boost::shared_ptr<Region> original_region = (*i);
2914                 boost::shared_ptr<Region> copied_region = RegionFactory::create (original_region, false);
2915
2916                 old_and_new_regions.push_back (TwoRegions (original_region,copied_region));
2917                 originals.push_back (original_region);
2918                 copies.push_back (copied_region);
2919
2920                 RegionFactory::add_compound_association (original_region, copied_region);
2921
2922                 /* make position relative to zero */
2923
2924                 pl->add_region (copied_region, original_region->position() - earliest_position);
2925
2926                 /* use the maximum number of channels for any region */
2927
2928                 channels = max (channels, original_region->n_channels());
2929
2930                 /* it will go above the layer of the highest existing region */
2931
2932                 layer = max (layer, original_region->layer());
2933         }
2934
2935         pl->in_partition = false;
2936
2937         pre_combine (copies);
2938
2939         /* add any dependent regions to the new playlist */
2940
2941         copy_dependents (old_and_new_regions, pl.get());
2942
2943         /* now create a new PlaylistSource for each channel in the new playlist */
2944
2945         SourceList sources;
2946         pair<framepos_t,framepos_t> extent = pl->get_extent();
2947
2948         for (uint32_t chn = 0; chn < channels; ++chn) {
2949                 sources.push_back (SourceFactory::createFromPlaylist (_type, _session, pl, id(), parent_name, chn, 0, extent.second, false, false));
2950
2951         }
2952
2953         /* now a new whole-file region using the list of sources */
2954
2955         plist.add (Properties::start, 0);
2956         plist.add (Properties::length, extent.second);
2957         plist.add (Properties::name, parent_name);
2958         plist.add (Properties::whole_file, true);
2959
2960         boost::shared_ptr<Region> parent_region = RegionFactory::create (sources, plist, true);
2961
2962         /* now the non-whole-file region that we will actually use in the
2963          * playlist
2964          */
2965
2966         plist.clear ();
2967         plist.add (Properties::start, 0);
2968         plist.add (Properties::length, extent.second);
2969         plist.add (Properties::name, child_name);
2970         plist.add (Properties::layer, layer+1);
2971
2972         boost::shared_ptr<Region> compound_region = RegionFactory::create (parent_region, plist, true);
2973
2974         /* remove all the selected regions from the current playlist
2975          */
2976
2977         freeze ();
2978
2979         for (RegionList::const_iterator i = r.begin(); i != r.end(); ++i) {
2980                 remove_region (*i);
2981         }
2982
2983         /* do type-specific stuff with the originals and the new compound
2984            region
2985         */
2986
2987         post_combine (originals, compound_region);
2988
2989         /* add the new region at the right location */
2990
2991         add_region (compound_region, earliest_position);
2992
2993         _combine_ops++;
2994
2995         thaw ();
2996
2997         return compound_region;
2998 }
2999
3000 void
3001 Playlist::uncombine (boost::shared_ptr<Region> target)
3002 {
3003         boost::shared_ptr<PlaylistSource> pls;
3004         boost::shared_ptr<const Playlist> pl;
3005         vector<boost::shared_ptr<Region> > originals;
3006         vector<TwoRegions> old_and_new_regions;
3007
3008         // (1) check that its really a compound region
3009
3010         if ((pls = boost::dynamic_pointer_cast<PlaylistSource>(target->source (0))) == 0) {
3011                 return;
3012         }
3013
3014         pl = pls->playlist();
3015
3016         framepos_t adjusted_start = 0; // gcc isn't smart enough
3017         framepos_t adjusted_end = 0;   // gcc isn't smart enough
3018
3019         /* the leftmost (earliest) edge of the compound region
3020            starts at zero in its source, or larger if it
3021            has been trimmed or content-scrolled.
3022
3023            the rightmost (latest) edge of the compound region
3024            relative to its source is the starting point plus
3025            the length of the region.
3026         */
3027
3028         // (2) get all the original regions
3029
3030         const RegionList& rl (pl->region_list().rlist());
3031         RegionFactory::CompoundAssociations& cassocs (RegionFactory::compound_associations());
3032         frameoffset_t move_offset = 0;
3033
3034         /* there are two possibilities here:
3035            1) the playlist that the playlist source was based on
3036            is us, so just add the originals (which belonged to
3037            us anyway) back in the right place.
3038
3039            2) the playlist that the playlist source was based on
3040            is NOT us, so we need to make copies of each of
3041            the original regions that we find, and add them
3042            instead.
3043         */
3044         bool same_playlist = (pls->original() == id());
3045
3046         for (RegionList::const_iterator i = rl.begin(); i != rl.end(); ++i) {
3047
3048                 boost::shared_ptr<Region> current (*i);
3049
3050                 RegionFactory::CompoundAssociations::iterator ca = cassocs.find (*i);
3051
3052                 if (ca == cassocs.end()) {
3053                         continue;
3054                 }
3055
3056                 boost::shared_ptr<Region> original (ca->second);
3057                 bool modified_region;
3058
3059                 if (i == rl.begin()) {
3060                         move_offset = (target->position() - original->position()) - target->start();
3061                         adjusted_start = original->position() + target->start();
3062                         adjusted_end = adjusted_start + target->length();
3063                 }
3064
3065                 if (!same_playlist) {
3066                         framepos_t pos = original->position();
3067                         /* make a copy, but don't announce it */
3068                         original = RegionFactory::create (original, false);
3069                         /* the pure copy constructor resets position() to zero,
3070                            so fix that up.
3071                         */
3072                         original->set_position (pos);
3073                 }
3074
3075                 /* check to see how the original region (in the
3076                  * playlist before compounding occured) overlaps
3077                  * with the new state of the compound region.
3078                  */
3079
3080                 original->clear_changes ();
3081                 modified_region = false;
3082
3083                 switch (original->coverage (adjusted_start, adjusted_end)) {
3084                 case OverlapNone:
3085                         /* original region does not cover any part
3086                            of the current state of the compound region
3087                         */
3088                         continue;
3089
3090                 case OverlapInternal:
3091                         /* overlap is just a small piece inside the
3092                          * original so trim both ends
3093                          */
3094                         original->trim_to (adjusted_start, adjusted_end - adjusted_start);
3095                         modified_region = true;
3096                         break;
3097
3098                 case OverlapExternal:
3099                         /* overlap fully covers original, so leave it
3100                            as is
3101                         */
3102                         break;
3103
3104                 case OverlapEnd:
3105                         /* overlap starts within but covers end,
3106                            so trim the front of the region
3107                         */
3108                         original->trim_front (adjusted_start);
3109                         modified_region = true;
3110                         break;
3111
3112                 case OverlapStart:
3113                         /* overlap covers start but ends within, so
3114                          * trim the end of the region.
3115                          */
3116                         original->trim_end (adjusted_end);
3117                         modified_region = true;
3118                         break;
3119                 }
3120
3121                 if (move_offset) {
3122                         /* fix the position to match any movement of the compound region.
3123                          */
3124                         original->set_position (original->position() + move_offset);
3125                         modified_region = true;
3126                 }
3127
3128                 if (modified_region) {
3129                         _session.add_command (new StatefulDiffCommand (original));
3130                 }
3131
3132                 /* and add to the list of regions waiting to be
3133                  * re-inserted
3134                  */
3135
3136                 originals.push_back (original);
3137                 old_and_new_regions.push_back (TwoRegions (*i, original));
3138         }
3139
3140         pre_uncombine (originals, target);
3141
3142         in_partition = true;
3143         freeze ();
3144
3145         // (3) remove the compound region
3146
3147         remove_region (target);
3148
3149         // (4) add the constituent regions
3150
3151         for (vector<boost::shared_ptr<Region> >::iterator i = originals.begin(); i != originals.end(); ++i) {
3152                 add_region ((*i), (*i)->position());
3153         }
3154
3155         /* now move dependent regions back from the compound to this playlist */
3156
3157         pl->copy_dependents (old_and_new_regions, this);
3158
3159         in_partition = false;
3160         thaw ();
3161 }
3162
3163 uint32_t
3164 Playlist::max_source_level () const
3165 {
3166         RegionLock rlock (const_cast<Playlist *> (this));
3167         uint32_t lvl = 0;
3168
3169         for (RegionList::const_iterator i = regions.begin(); i != regions.end(); ++i) {
3170                 lvl = max (lvl, (*i)->max_source_level());
3171         }
3172
3173         return lvl;
3174 }
3175
3176
3177 uint32_t
3178 Playlist::count_joined_regions () const
3179 {
3180         RegionLock rlock (const_cast<Playlist *> (this));
3181         uint32_t cnt = 0;
3182
3183         for (RegionList::const_iterator i = regions.begin(); i != regions.end(); ++i) {
3184                 if ((*i)->max_source_level() > 0) {
3185                         cnt++;
3186                 }
3187         }
3188
3189         return cnt;
3190 }
3191
3192 void
3193 Playlist::set_orig_track_id (const PBD::ID& id)
3194 {
3195         _orig_track_id = id;
3196 }
3197
3198 uint64_t
3199 Playlist::highest_layering_index () const
3200 {
3201         RegionLock rlock (const_cast<Playlist *> (this));
3202
3203         uint64_t h = 0;
3204         for (RegionList::const_iterator i = regions.begin(); i != regions.end(); ++i) {
3205                 h = max (h, (*i)->layering_index ());
3206         }
3207
3208         return h;
3209 }
3210