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