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