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