Add mising file.
[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 <set>
21 #include <fstream>
22 #include <algorithm>
23 #include <unistd.h>
24 #include <cerrno>
25 #include <string>
26 #include <climits>
27
28 #include <sigc++/bind.h>
29
30 #include "pbd/failed_constructor.h"
31 #include "pbd/stl_delete.h"
32 #include "pbd/xml++.h"
33 #include "pbd/stacktrace.h"
34
35 #include "ardour/playlist.h"
36 #include "ardour/session.h"
37 #include "ardour/region.h"
38 #include "ardour/region_factory.h"
39 #include "ardour/playlist_factory.h"
40 #include "ardour/transient_detector.h"
41
42 #include "i18n.h"
43
44 using namespace std;
45 using namespace ARDOUR;
46 using namespace PBD;
47
48 struct ShowMeTheList {
49     ShowMeTheList (boost::shared_ptr<Playlist> pl, const string& n) : playlist (pl), name (n) {}
50     ~ShowMeTheList () { 
51             cerr << ">>>>" << name << endl; playlist->dump(); cerr << "<<<<" << name << endl << endl; 
52     };
53     boost::shared_ptr<Playlist> playlist;
54     string name;
55 };
56
57 struct RegionSortByLayer {
58     bool operator() (boost::shared_ptr<Region> a, boost::shared_ptr<Region> b) {
59             return a->layer() < b->layer();
60     }
61 };
62
63 struct RegionSortByPosition {
64     bool operator() (boost::shared_ptr<Region> a, boost::shared_ptr<Region> b) {
65             return a->position() < b->position();
66     }
67 };
68
69 struct RegionSortByLastLayerOp {
70     bool operator() (boost::shared_ptr<Region> a, boost::shared_ptr<Region> b) {
71             return a->last_layer_op() < b->last_layer_op();
72     }
73 };
74
75
76 Playlist::Playlist (Session& sess, string nom, DataType type, bool hide)
77         : SessionObject(sess, nom)
78         , _type(type)
79 {
80         init (hide);
81         first_set_state = false;
82         _name = nom;
83         
84 }
85
86 Playlist::Playlist (Session& sess, const XMLNode& node, DataType type, bool hide)
87         : SessionObject(sess, "unnamed playlist")
88         , _type(type)
89 {
90         const XMLProperty* prop = node.property("type");
91         assert(!prop || DataType(prop->value()) == _type);
92
93         init (hide);
94         _name = "unnamed"; /* reset by set_state */
95
96         /* set state called by derived class */
97 }
98
99 Playlist::Playlist (boost::shared_ptr<const Playlist> other, string namestr, bool hide)
100         : SessionObject(other->_session, namestr), _type(other->_type), _orig_diskstream_id(other->_orig_diskstream_id)
101 {
102         init (hide);
103
104         RegionList tmp;
105         other->copy_regions (tmp);
106         
107         in_set_state++;
108
109         for (list<boost::shared_ptr<Region> >::iterator x = tmp.begin(); x != tmp.end(); ++x) {
110                 add_region_internal( (*x), (*x)->position());
111         }
112
113         in_set_state--;
114
115         _splicing  = other->_splicing;
116         _nudging   = other->_nudging;
117         _edit_mode = other->_edit_mode;
118
119         in_set_state = 0;
120         first_set_state = false;
121         in_flush = false;
122         in_partition = false;
123         subcnt = 0;
124         _read_data_count = 0;
125         _frozen = other->_frozen;
126         
127         layer_op_counter = other->layer_op_counter;
128         freeze_length = other->freeze_length;
129 }
130
131 Playlist::Playlist (boost::shared_ptr<const Playlist> other, nframes_t start, nframes_t cnt, string str, bool hide)
132         : SessionObject(other->_session, str), _type(other->_type), _orig_diskstream_id(other->_orig_diskstream_id)
133 {
134         RegionLock rlock2 (const_cast<Playlist*> (other.get()));
135
136         nframes_t end = start + cnt - 1;
137
138         init (hide);
139
140         in_set_state++;
141
142         for (RegionList::const_iterator i = other->regions.begin(); i != other->regions.end(); i++) {
143
144                 boost::shared_ptr<Region> region;
145                 boost::shared_ptr<Region> new_region;
146                 nframes_t offset = 0;
147                 nframes_t position = 0;
148                 nframes_t len = 0;
149                 string    new_name;
150                 OverlapType overlap;
151
152                 region = *i;
153
154                 overlap = region->coverage (start, end);
155
156                 switch (overlap) {
157                 case OverlapNone:
158                         continue;
159
160                 case OverlapInternal:
161                         offset = start - region->position();
162                         position = 0;
163                         len = cnt;
164                         break;
165
166                 case OverlapStart:
167                         offset = 0;
168                         position = region->position() - start;
169                         len = end - region->position();
170                         break;
171
172                 case OverlapEnd:
173                         offset = start - region->position();
174                         position = 0;
175                         len = region->length() - offset;
176                         break;
177
178                 case OverlapExternal:
179                         offset = 0;
180                         position = region->position() - start;
181                         len = region->length();
182                         break;
183                 }
184
185                 _session.region_name (new_name, region->name(), false);
186
187                 new_region = RegionFactory::RegionFactory::create (region, offset, len, new_name, region->layer(), region->flags());
188
189                 add_region_internal (new_region, position);
190         }
191         
192         in_set_state--;
193         first_set_state = false;
194
195         /* this constructor does NOT notify others (session) */
196 }
197
198 void
199 Playlist::use ()
200 {
201         ++_refcnt;
202         InUse (true); /* EMIT SIGNAL */
203 }
204
205 void
206 Playlist::release ()
207 {
208         if (_refcnt > 0) {
209                 _refcnt--; 
210         }
211
212         if (_refcnt == 0) {
213                 InUse (false); /* EMIT SIGNAL */
214         }
215 }
216
217 void
218 Playlist::copy_regions (RegionList& newlist) const
219 {
220         RegionLock rlock (const_cast<Playlist *> (this));
221
222         for (RegionList::const_iterator i = regions.begin(); i != regions.end(); ++i) {
223                 newlist.push_back (RegionFactory::RegionFactory::create (*i));
224         }
225 }
226
227 void
228 Playlist::init (bool hide)
229 {
230         g_atomic_int_set (&block_notifications, 0);
231         g_atomic_int_set (&ignore_state_changes, 0);
232         pending_modified = false;
233         pending_length = false;
234         first_set_state = true;
235         _refcnt = 0;
236         _hidden = hide;
237         _splicing = false;
238         _shuffling = false;
239         _nudging = false;
240         in_set_state = 0;
241         _edit_mode = Config->get_edit_mode();
242         in_flush = false;
243         in_partition = false;
244         subcnt = 0;
245         _read_data_count = 0;
246         _frozen = false;
247         layer_op_counter = 0;
248         freeze_length = 0;
249
250         Modified.connect (mem_fun (*this, &Playlist::mark_session_dirty));
251 }
252
253 Playlist::~Playlist ()
254 {
255         {
256                 RegionLock rl (this);
257
258                 for (set<boost::shared_ptr<Region> >::iterator i = all_regions.begin(); i != all_regions.end(); ++i) {
259                         (*i)->set_playlist (boost::shared_ptr<Playlist>());
260                 }
261         }
262
263         /* GoingAway must be emitted by derived classes */
264 }
265
266 bool
267 Playlist::set_name (const string& str)
268 {
269         /* in a typical situation, a playlist is being used
270            by one diskstream and also is referenced by the
271            Session. if there are more references than that,
272            then don't change the name.
273         */
274
275         if (_refcnt > 2) {
276                 return false;
277         } else {
278                 return SessionObject::set_name(str);
279         }
280 }
281
282 /***********************************************************************
283  CHANGE NOTIFICATION HANDLING
284  
285  Notifications must be delayed till the region_lock is released. This
286  is necessary because handlers for the signals may need to acquire
287  the lock (e.g. to read from the playlist).
288  ***********************************************************************/
289
290 void
291 Playlist::freeze ()
292 {
293         delay_notifications ();
294         g_atomic_int_inc (&ignore_state_changes);
295 }
296
297 void
298 Playlist::thaw ()
299 {
300         g_atomic_int_dec_and_test (&ignore_state_changes);
301         release_notifications ();
302 }
303
304
305 void
306 Playlist::delay_notifications ()
307 {
308         g_atomic_int_inc (&block_notifications);
309         freeze_length = _get_maximum_extent();
310 }
311
312 void
313 Playlist::release_notifications ()
314 {
315         if (g_atomic_int_dec_and_test (&block_notifications)) { 
316                 flush_notifications ();
317         } 
318 }
319
320 void
321 Playlist::notify_modified ()
322 {
323         if (holding_state ()) {
324                 pending_modified = true;
325         } else {
326                 pending_modified = false;
327                 Modified(); /* EMIT SIGNAL */
328         }
329 }
330
331 void
332 Playlist::notify_region_removed (boost::shared_ptr<Region> r)
333 {
334         if (holding_state ()) {
335                 pending_removes.insert (r);
336                 pending_modified = true;
337                 pending_length = true;
338         } else {
339                 /* this might not be true, but we have to act
340                    as though it could be.
341                 */
342                 pending_length = false;
343                 LengthChanged (); /* EMIT SIGNAL */
344                 pending_modified = false;
345                 Modified (); /* EMIT SIGNAL */
346         }
347 }
348
349 void
350 Playlist::notify_region_moved (boost::shared_ptr<Region> r)
351 {
352         Evoral::RangeMove<nframes_t> const move (r->last_position (), r->length (), r->position ());
353                         
354         if (holding_state ()) {
355
356                 pending_range_moves.push_back (move);
357                 
358         } else {
359
360                 list< Evoral::RangeMove<nframes_t> > m;
361                 m.push_back (move);
362                 RangesMoved (m);
363
364         }
365
366 }
367
368 void
369 Playlist::notify_region_added (boost::shared_ptr<Region> r)
370 {
371         /* the length change might not be true, but we have to act
372            as though it could be.
373         */
374
375         if (holding_state()) {
376                 pending_adds.insert (r);
377                 pending_modified = true;
378                 pending_length = true;
379         } else {
380                 pending_length = false;
381                 LengthChanged (); /* EMIT SIGNAL */
382                 pending_modified = false;
383                 Modified (); /* EMIT SIGNAL */
384         }
385 }
386
387 void
388 Playlist::notify_length_changed ()
389 {
390         if (holding_state ()) {
391                 pending_length = true;
392         } else {
393                 pending_length = false;
394                 LengthChanged(); /* EMIT SIGNAL */
395                 pending_modified = false;
396                 Modified (); /* EMIT SIGNAL */
397         }
398 }
399
400 void
401 Playlist::flush_notifications ()
402 {
403         set<boost::shared_ptr<Region> > dependent_checks_needed;
404         set<boost::shared_ptr<Region> >::iterator s;
405         uint32_t n = 0;
406
407         if (in_flush) {
408                 return;
409         }
410
411         in_flush = true;
412
413         /* we have no idea what order the regions ended up in pending
414            bounds (it could be based on selection order, for example).
415            so, to preserve layering in the "most recently moved is higher" 
416            model, sort them by existing layer, then timestamp them.
417         */
418
419         // RegionSortByLayer cmp;
420         // pending_bounds.sort (cmp);
421
422         for (RegionList::iterator r = pending_bounds.begin(); r != pending_bounds.end(); ++r) {
423                 if (Config->get_layer_model() == MoveAddHigher) {
424                         timestamp_layer_op (*r);
425                 }
426                 pending_length = true;
427                 dependent_checks_needed.insert (*r);
428                 n++;
429         }
430
431         for (s = pending_adds.begin(); s != pending_adds.end(); ++s) {
432                 dependent_checks_needed.insert (*s);
433                 n++;
434         }
435
436         for (s = pending_removes.begin(); s != pending_removes.end(); ++s) {
437                 remove_dependents (*s);
438                 n++;
439         }
440
441         if ((freeze_length != _get_maximum_extent()) || pending_length) {
442                 pending_length = 0;
443                 LengthChanged(); /* EMIT SIGNAL */
444                 n++;
445         }
446
447         if (n || pending_modified) {
448                 if (!in_set_state) {
449                         relayer ();
450                 }
451                 pending_modified = false;
452                 Modified (); /* EMIT SIGNAL */
453                 
454         }
455
456         for (s = dependent_checks_needed.begin(); s != dependent_checks_needed.end(); ++s) {
457                 check_dependents (*s, false);
458         }
459
460         if (!pending_range_moves.empty ()) {
461                 RangesMoved (pending_range_moves);
462         }
463
464         pending_adds.clear ();
465         pending_removes.clear ();
466         pending_bounds.clear ();
467         pending_range_moves.clear ();
468
469         in_flush = false;
470 }
471
472 /*************************************************************
473   PLAYLIST OPERATIONS
474  *************************************************************/
475
476 void
477 Playlist::add_region (boost::shared_ptr<Region> region, nframes_t position, float times, bool auto_partition) 
478 {
479         RegionLock rlock (this);
480         times = fabs (times);
481         
482         int itimes = (int) floor (times);
483
484         nframes_t pos = position;
485
486         if(times == 1 && auto_partition){
487                 partition(pos, (nframes_t) (pos + region->length()), true);
488         }
489         
490         if (itimes >= 1) {
491                 add_region_internal (region, pos);
492                 pos += region->length();
493                 --itimes;
494         }
495         
496         
497         /* note that itimes can be zero if we being asked to just
498            insert a single fraction of the region.
499         */
500
501         for (int i = 0; i < itimes; ++i) {
502                 boost::shared_ptr<Region> copy = RegionFactory::create (region);
503                 add_region_internal (copy, pos);
504                 pos += region->length();
505         }
506         
507         nframes_t length = 0;
508
509         if (floor (times) != times) {
510                 length = (nframes_t) floor (region->length() * (times - floor (times)));
511                 string name;
512                 _session.region_name (name, region->name(), false);
513                 boost::shared_ptr<Region> sub = RegionFactory::create (region, 0, length, name, region->layer(), region->flags());
514                 add_region_internal (sub, pos);
515         }
516
517         possibly_splice_unlocked (position, (pos + length) - position, boost::shared_ptr<Region>());
518 }
519
520 void
521 Playlist::set_region_ownership ()
522 {
523         RegionLock rl (this);
524         RegionList::iterator i;
525         boost::weak_ptr<Playlist> pl (shared_from_this());
526
527         for (i = regions.begin(); i != regions.end(); ++i) {
528                 (*i)->set_playlist (pl);
529         }
530 }
531
532 bool
533 Playlist::add_region_internal (boost::shared_ptr<Region> region, nframes_t position)
534 {
535         if (region->data_type() != _type){
536                 return false;
537         }
538
539         RegionSortByPosition cmp;
540         nframes_t old_length = 0;
541
542         if (!holding_state()) {
543                  old_length = _get_maximum_extent();
544         }
545
546         if (!first_set_state) {
547                 boost::shared_ptr<Playlist> foo (shared_from_this());
548                 region->set_playlist (boost::weak_ptr<Playlist>(foo));
549         } 
550
551         region->set_position (position, this);
552
553         timestamp_layer_op (region);
554
555         regions.insert (upper_bound (regions.begin(), regions.end(), region, cmp), region);
556         all_regions.insert (region);
557
558         possibly_splice_unlocked (position, region->length(), region);
559
560         if (!holding_state () && !in_set_state) {
561                 /* layers get assigned from XML state */
562                 relayer ();
563         }
564
565         /* we need to notify the existence of new region before checking dependents. Ick. */
566
567         notify_region_added (region);
568         
569         if (!holding_state ()) {
570                 check_dependents (region, false);
571                 if (old_length != _get_maximum_extent()) {
572                         notify_length_changed ();
573                 }
574         }
575
576         region_state_changed_connections.push_back (
577                 region->StateChanged.connect (sigc::bind (mem_fun (this, &Playlist::region_changed_proxy), 
578                                                           boost::weak_ptr<Region> (region)))
579                 );
580
581         return true;
582 }
583
584 void
585 Playlist::replace_region (boost::shared_ptr<Region> old, boost::shared_ptr<Region> newr, nframes_t pos)
586 {
587         RegionLock rlock (this);
588
589         bool old_sp = _splicing;
590         _splicing = true;
591
592         remove_region_internal (old);
593         add_region_internal (newr, pos);
594
595         _splicing = old_sp;
596
597         possibly_splice_unlocked (pos, (nframes64_t) old->length() - (nframes64_t) newr->length());
598 }
599
600 void
601 Playlist::remove_region (boost::shared_ptr<Region> region)
602 {
603         RegionLock rlock (this);
604         remove_region_internal (region);
605 }
606
607 int
608 Playlist::remove_region_internal (boost::shared_ptr<Region> region)
609 {
610         RegionList::iterator i;
611         nframes_t old_length = 0;
612
613         if (!holding_state()) {
614                 old_length = _get_maximum_extent();
615         }
616
617         if (!in_set_state) {
618                 /* unset playlist */
619                 region->set_playlist (boost::weak_ptr<Playlist>());
620         }
621
622         for (i = regions.begin(); i != regions.end(); ++i) {
623                 if (*i == region) {
624
625                         nframes_t pos = (*i)->position();
626                         nframes64_t distance = (*i)->length();
627
628                         regions.erase (i);
629
630                         possibly_splice_unlocked (pos, -distance);
631
632                         if (!holding_state ()) {
633                                 relayer ();
634                                 remove_dependents (region);
635                                 
636                                 if (old_length != _get_maximum_extent()) {
637                                         notify_length_changed ();
638                                 }
639                         }
640
641                         notify_region_removed (region);
642                         return 0;
643                 }
644         }
645
646
647
648         return -1;
649 }
650
651 void
652 Playlist::get_equivalent_regions (boost::shared_ptr<Region> other, vector<boost::shared_ptr<Region> >& results)
653 {
654         if (Config->get_use_overlap_equivalency()) {
655                 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
656                         if ((*i)->overlap_equivalent (other)) {
657                                 results.push_back ((*i));
658                         }
659                 }
660         } else {
661                 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
662                         if ((*i)->equivalent (other)) {
663                                 results.push_back ((*i));
664                         }
665                 }
666         }
667 }
668
669 void
670 Playlist::get_region_list_equivalent_regions (boost::shared_ptr<Region> other, vector<boost::shared_ptr<Region> >& results)
671 {
672         for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
673
674                 if ((*i) && (*i)->region_list_equivalent (other)) {
675                         results.push_back (*i);
676                 }
677         }
678 }
679
680 void
681 Playlist::partition (nframes_t start, nframes_t end, bool cut)
682 {
683         RegionList thawlist;
684
685         partition_internal (start, end, cut, thawlist);
686
687         for (RegionList::iterator i = thawlist.begin(); i != thawlist.end(); ++i) {
688                 (*i)->thaw ("separation");
689         }
690 }
691
692 void
693 Playlist::partition_internal (nframes_t start, nframes_t end, bool cutting, RegionList& thawlist)
694 {
695         RegionList new_regions;
696
697         {
698                 RegionLock rlock (this);
699
700                 boost::shared_ptr<Region> region;
701                 boost::shared_ptr<Region> current;
702                 string new_name;
703                 RegionList::iterator tmp;
704                 OverlapType overlap;
705                 nframes_t pos1, pos2, pos3, pos4;
706                 
707                 in_partition = true;
708                 
709                 /* need to work from a copy, because otherwise the regions we add during the process
710                    get operated on as well.
711                 */
712                 
713                 RegionList copy = regions;
714                 
715                 for (RegionList::iterator i = copy.begin(); i != copy.end(); i = tmp) {
716
717                         tmp = i;
718                         ++tmp;
719                         
720                         current = *i;
721
722                         if (current->first_frame() >= start && current->last_frame() < end) {
723
724                                 if (cutting) {
725                                         remove_region_internal (current);
726                                 }
727
728                                 continue;
729                         }
730                         
731                         /* coverage will return OverlapStart if the start coincides
732                            with the end point. we do not partition such a region,
733                            so catch this special case.
734                         */
735
736                         if (current->first_frame() >= end) {
737                                 continue;
738                         }
739
740                         if ((overlap = current->coverage (start, end)) == OverlapNone) {
741                                 continue;
742                         }
743
744                         pos1 = current->position();
745                         pos2 = start;
746                         pos3 = end;
747                         pos4 = current->last_frame();
748                         
749                         if (overlap == OverlapInternal) {
750                                 /* split: we need 3 new regions, the front, middle and end.
751                                    cut:   we need 2 regions, the front and end.
752                                 */
753                                 
754                                 /*
755                                          start                 end
756                           ---------------*************************------------
757                                          P1  P2              P3  P4
758                           SPLIT:
759                           ---------------*****++++++++++++++++====------------
760                           CUT
761                           ---------------*****----------------====------------
762                           
763                                 */
764
765                                 if (!cutting) {
766                                         /* "middle" ++++++ */
767                                         
768                                         _session.region_name (new_name, current->name(), false);
769                                         region = RegionFactory::create (current, pos2 - pos1, pos3 - pos2, new_name,
770                                                                         regions.size(), Region::Flag(current->flags()|Region::Automatic|Region::LeftOfSplit|Region::RightOfSplit));
771                                         add_region_internal (region, start);
772                                         new_regions.push_back (region);
773                                 }
774                                 
775                                 /* "end" ====== */
776                         
777                                 _session.region_name (new_name, current->name(), false);
778                                 region = RegionFactory::create (current, pos3 - pos1, pos4 - pos3, new_name, 
779                                                                 regions.size(), Region::Flag(current->flags()|Region::Automatic|Region::RightOfSplit));
780
781                                 add_region_internal (region, end);
782                                 new_regions.push_back (region);
783                                 
784                                 /* "front" ***** */
785                                 
786                                 current->freeze ();
787                                 thawlist.push_back (current);
788                                 current->trim_end (pos2, this);
789
790                         } else if (overlap == OverlapEnd) {
791
792                                 /*
793                                                               start           end
794                                     ---------------*************************------------
795                                                    P1           P2         P4   P3
796                                     SPLIT:                                                 
797                                     ---------------**************+++++++++++------------
798                                     CUT:                                                   
799                                     ---------------**************-----------------------
800                                 */
801                                 
802                                 if (!cutting) {
803
804                                         /* end +++++ */
805                                         
806                                         _session.region_name (new_name, current->name(), false);
807                                         region = RegionFactory::create (current, pos2 - pos1, pos4 - pos2, new_name, (layer_t) regions.size(),
808                                                                         Region::Flag(current->flags()|Region::Automatic|Region::LeftOfSplit));
809
810                                         add_region_internal (region, start);
811                                         new_regions.push_back (region);
812                                 }
813                                 
814                                 /* front ****** */
815                                 
816                                 current->freeze ();
817                                 thawlist.push_back (current);
818                                 current->trim_end (pos2, this);
819
820                         } else if (overlap == OverlapStart) {
821
822                                 /* split: we need 2 regions: the front and the end.
823                                    cut: just trim current to skip the cut area
824                                 */
825                                 
826                                 /*
827                                                         start           end
828                                     ---------------*************************------------
829                                        P2          P1 P3                   P4          
830
831                                     SPLIT:
832                                     ---------------****+++++++++++++++++++++------------
833                                     CUT:
834                                     -------------------*********************------------
835                                     
836                                 */
837
838                                 if (!cutting) {
839                                         /* front **** */
840                                         _session.region_name (new_name, current->name(), false);
841                                         region = RegionFactory::create (current, 0, pos3 - pos1, new_name,
842                                                                         regions.size(), Region::Flag(current->flags()|Region::Automatic|Region::RightOfSplit));
843
844                                         add_region_internal (region, pos1);
845                                         new_regions.push_back (region);
846                                 } 
847                                 
848                                 /* end */
849                                 
850                                 current->freeze ();
851                                 thawlist.push_back (current);
852                                 current->trim_front (pos3, this);
853                         } else if (overlap == OverlapExternal) {
854
855                                 /* split: no split required.
856                                    cut: remove the region.
857                                 */
858                                 
859                                 /*
860                                        start                                      end
861                                     ---------------*************************------------
862                                        P2          P1 P3                   P4          
863
864                                     SPLIT:
865                                     ---------------*************************------------
866                                     CUT:
867                                     ----------------------------------------------------
868                                     
869                                 */
870                                 
871                                 if (cutting) {
872                                         remove_region_internal (current);
873                                 }
874
875                                 new_regions.push_back (current);
876                         }
877                 }
878
879                 in_partition = false;
880         }
881
882         for (RegionList::iterator i = new_regions.begin(); i != new_regions.end(); ++i) {
883                 check_dependents (*i, false);
884         }
885 }
886
887 boost::shared_ptr<Playlist>
888 Playlist::cut_copy (boost::shared_ptr<Playlist> (Playlist::*pmf)(nframes_t, nframes_t,bool), list<AudioRange>& ranges, bool result_is_hidden)
889 {
890         boost::shared_ptr<Playlist> ret;
891         boost::shared_ptr<Playlist> pl;
892         nframes_t start;
893
894         if (ranges.empty()) {
895                 return boost::shared_ptr<Playlist>();
896         }
897
898         start = ranges.front().start;
899
900         for (list<AudioRange>::iterator i = ranges.begin(); i != ranges.end(); ++i) {
901
902                 pl = (this->*pmf)((*i).start, (*i).length(), result_is_hidden);
903                 
904                 if (i == ranges.begin()) {
905                         ret = pl;
906                 } else {
907                         
908                         /* paste the next section into the nascent playlist,
909                            offset to reflect the start of the first range we
910                            chopped.
911                         */
912
913                         ret->paste (pl, (*i).start - start, 1.0f);
914                 }
915         }
916
917         return ret;
918 }
919
920 boost::shared_ptr<Playlist>
921 Playlist::cut (list<AudioRange>& ranges, bool result_is_hidden)
922 {
923         boost::shared_ptr<Playlist> (Playlist::*pmf)(nframes_t,nframes_t,bool) = &Playlist::cut;
924         return cut_copy (pmf, ranges, result_is_hidden);
925 }
926
927 boost::shared_ptr<Playlist>
928 Playlist::copy (list<AudioRange>& ranges, bool result_is_hidden)
929 {
930         boost::shared_ptr<Playlist> (Playlist::*pmf)(nframes_t,nframes_t,bool) = &Playlist::copy;
931         return cut_copy (pmf, ranges, result_is_hidden);
932 }
933
934 boost::shared_ptr<Playlist>
935 Playlist::cut (nframes_t start, nframes_t cnt, bool result_is_hidden)
936 {
937         boost::shared_ptr<Playlist> the_copy;
938         RegionList thawlist;
939         char buf[32];
940
941         snprintf (buf, sizeof (buf), "%" PRIu32, ++subcnt);
942         string new_name = _name;
943         new_name += '.';
944         new_name += buf;
945
946         if ((the_copy = PlaylistFactory::create (shared_from_this(), start, cnt, new_name, result_is_hidden)) == 0) {
947                 return boost::shared_ptr<Playlist>();
948         }
949
950         partition_internal (start, start+cnt-1, true, thawlist);
951
952         for (RegionList::iterator i = thawlist.begin(); i != thawlist.end(); ++i) {
953                 (*i)->thaw ("playlist cut");
954         }
955
956         return the_copy;
957 }
958
959 boost::shared_ptr<Playlist>
960 Playlist::copy (nframes_t start, nframes_t cnt, bool result_is_hidden)
961 {
962         char buf[32];
963         
964         snprintf (buf, sizeof (buf), "%" PRIu32, ++subcnt);
965         string new_name = _name;
966         new_name += '.';
967         new_name += buf;
968
969         cnt = min (_get_maximum_extent() - start, cnt);
970         return PlaylistFactory::create (shared_from_this(), start, cnt, new_name, result_is_hidden);
971 }
972
973 int
974 Playlist::paste (boost::shared_ptr<Playlist> other, nframes_t position, float times)
975 {
976         times = fabs (times);
977         nframes_t old_length;
978
979         {
980                 RegionLock rl1 (this);
981                 RegionLock rl2 (other.get());
982
983                 old_length = _get_maximum_extent();
984         
985                 int itimes = (int) floor (times);
986                 nframes_t pos = position;
987                 nframes_t shift = other->_get_maximum_extent();
988                 layer_t top_layer = regions.size();
989
990                 while (itimes--) {
991                         for (RegionList::iterator i = other->regions.begin(); i != other->regions.end(); ++i) {
992                                 boost::shared_ptr<Region> copy_of_region = RegionFactory::create (*i);
993
994                                 /* put these new regions on top of all existing ones, but preserve
995                                    the ordering they had in the original playlist.
996                                 */
997                                 
998                                 copy_of_region->set_layer (copy_of_region->layer() + top_layer);
999                                 add_region_internal (copy_of_region, copy_of_region->position() + pos);
1000                         }
1001                         pos += shift;
1002                 }
1003
1004
1005                 /* XXX shall we handle fractional cases at some point? */
1006
1007                 if (old_length != _get_maximum_extent()) {
1008                         notify_length_changed ();
1009                 }
1010
1011                 
1012         }
1013
1014         return 0;
1015 }
1016
1017
1018 void
1019 Playlist::duplicate (boost::shared_ptr<Region> region, nframes_t position, float times)
1020 {
1021         times = fabs (times);
1022
1023         RegionLock rl (this);
1024         int itimes = (int) floor (times);
1025         nframes_t pos = position;
1026
1027         while (itimes--) {
1028                 boost::shared_ptr<Region> copy = RegionFactory::create (region);
1029                 add_region_internal (copy, pos);
1030                 pos += region->length();
1031         }
1032
1033         if (floor (times) != times) {
1034                 nframes_t length = (nframes_t) floor (region->length() * (times - floor (times)));
1035                 string name;
1036                 _session.region_name (name, region->name(), false);
1037                 boost::shared_ptr<Region> sub = RegionFactory::create (region, 0, length, name, region->layer(), region->flags());
1038                 add_region_internal (sub, pos);
1039         }
1040 }
1041
1042 void
1043 Playlist::shift (nframes64_t at, nframes64_t distance, bool move_intersected, bool ignore_music_glue)
1044 {
1045         RegionLock rlock (this);
1046         RegionList copy (regions);
1047         RegionList fixup;
1048
1049         for (RegionList::iterator r = copy.begin(); r != copy.end(); ++r) {
1050
1051                 if ((*r)->last_frame() < at) {
1052                         /* too early */
1053                         continue;
1054                 }
1055                 
1056                 if (at > (*r)->first_frame() && at < (*r)->last_frame()) {
1057                         /* intersected region */
1058                         if (!move_intersected) {
1059                                 continue;
1060                         }
1061                 }
1062                 
1063                 /* do not move regions glued to music time - that
1064                    has to be done separately.
1065                 */
1066
1067                 if (!ignore_music_glue && (*r)->positional_lock_style() != Region::AudioTime) {
1068                         fixup.push_back (*r);
1069                         continue;
1070                 }
1071
1072                 (*r)->set_position ((*r)->position() + distance, this);
1073         }
1074
1075         for (RegionList::iterator r = fixup.begin(); r != fixup.end(); ++r) {
1076                 (*r)->recompute_position_from_lock_style ();
1077         }
1078 }
1079
1080 void
1081 Playlist::split (nframes64_t at)
1082 {
1083         RegionLock rlock (this);
1084         RegionList copy (regions);
1085
1086         /* use a copy since this operation can modify the region list
1087          */
1088
1089         for (RegionList::iterator r = copy.begin(); r != copy.end(); ++r) {
1090                 _split_region (*r, at);
1091         }
1092 }
1093
1094 void
1095 Playlist::split_region (boost::shared_ptr<Region> region, nframes_t playlist_position)
1096 {
1097         RegionLock rl (this);
1098         _split_region (region, playlist_position);
1099 }
1100
1101 void
1102 Playlist::_split_region (boost::shared_ptr<Region> region, nframes_t playlist_position)
1103 {
1104         if (!region->covers (playlist_position)) {
1105                 return;
1106         }
1107
1108         if (region->position() == playlist_position ||
1109             region->last_frame() == playlist_position) {
1110                 return;
1111         }
1112
1113         boost::shared_ptr<Region> left;
1114         boost::shared_ptr<Region> right;
1115         nframes_t before;
1116         nframes_t after;
1117         string before_name;
1118         string after_name;
1119
1120         /* split doesn't change anything about length, so don't try to splice */
1121         
1122         bool old_sp = _splicing;
1123         _splicing = true;
1124
1125         before = playlist_position - region->position();
1126         after = region->length() - before;
1127         
1128         _session.region_name (before_name, region->name(), false);
1129         left = RegionFactory::create (region, 0, before, before_name, region->layer(), Region::Flag (region->flags()|Region::LeftOfSplit));
1130
1131         _session.region_name (after_name, region->name(), false);
1132         right = RegionFactory::create (region, before, after, after_name, region->layer(), Region::Flag (region->flags()|Region::RightOfSplit));
1133
1134         add_region_internal (left, region->position());
1135         add_region_internal (right, region->position() + before);
1136
1137         uint64_t orig_layer_op = region->last_layer_op();
1138         for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
1139                 if ((*i)->last_layer_op() > orig_layer_op) {
1140                         (*i)->set_last_layer_op( (*i)->last_layer_op() + 1 );
1141                 }
1142         }
1143         
1144         left->set_last_layer_op ( orig_layer_op );
1145         right->set_last_layer_op ( orig_layer_op + 1);
1146
1147         layer_op_counter++;
1148
1149         finalize_split_region (region, left, right);
1150         
1151         remove_region_internal (region);
1152
1153         _splicing = old_sp;
1154 }
1155
1156 void
1157 Playlist::possibly_splice (nframes_t at, nframes64_t distance, boost::shared_ptr<Region> exclude)
1158 {
1159         if (_splicing || in_set_state) {
1160                 /* don't respond to splicing moves or state setting */
1161                 return;
1162         }
1163
1164         if (_edit_mode == Splice) {
1165                 splice_locked (at, distance, exclude);
1166         }
1167 }
1168
1169 void
1170 Playlist::possibly_splice_unlocked (nframes_t at, nframes64_t distance, boost::shared_ptr<Region> exclude)
1171 {
1172         if (_splicing || in_set_state) {
1173                 /* don't respond to splicing moves or state setting */
1174                 return;
1175         }
1176
1177         if (_edit_mode == Splice) {
1178                 splice_unlocked (at, distance, exclude);
1179         }
1180 }
1181
1182 void
1183 Playlist::splice_locked (nframes_t at, nframes64_t distance, boost::shared_ptr<Region> exclude)
1184 {
1185         {
1186                 RegionLock rl (this);
1187                 core_splice (at, distance, exclude);
1188         }
1189 }
1190
1191 void
1192 Playlist::splice_unlocked (nframes_t at, nframes64_t distance, boost::shared_ptr<Region> exclude)
1193 {
1194         core_splice (at, distance, exclude);
1195 }
1196
1197 void
1198 Playlist::core_splice (nframes_t at, nframes64_t distance, boost::shared_ptr<Region> exclude)
1199 {
1200         _splicing = true;
1201
1202         for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
1203
1204                 if (exclude && (*i) == exclude) {
1205                         continue;
1206                 }
1207
1208                 if ((*i)->position() >= at) {
1209                         nframes64_t new_pos = (*i)->position() + distance;
1210                         if (new_pos < 0) {
1211                                 new_pos = 0;
1212                         } else if (new_pos >= max_frames - (*i)->length()) {
1213                                 new_pos = max_frames - (*i)->length();
1214                         } 
1215                                 
1216                         (*i)->set_position (new_pos, this);
1217                 }
1218         }
1219
1220         _splicing = false;
1221
1222         notify_length_changed ();
1223 }
1224
1225 void
1226 Playlist::region_bounds_changed (Change what_changed, boost::shared_ptr<Region> region)
1227 {
1228         if (in_set_state || _splicing || _nudging || _shuffling) {
1229                 return;
1230         }
1231
1232         if (what_changed & ARDOUR::PositionChanged) {
1233
1234                 /* remove it from the list then add it back in
1235                    the right place again.
1236                 */
1237                 
1238                 RegionSortByPosition cmp;
1239
1240                 RegionList::iterator i = find (regions.begin(), regions.end(), region);
1241                 
1242                 if (i == regions.end()) {
1243                         warning << string_compose (_("%1: bounds changed received for region (%2)not in playlist"),
1244                                             _name, region->name())
1245                                 << endmsg;
1246                         return;
1247                 }
1248
1249                 regions.erase (i);
1250                 regions.insert (upper_bound (regions.begin(), regions.end(), region, cmp), region);
1251         }
1252
1253         if (what_changed & Change (ARDOUR::PositionChanged|ARDOUR::LengthChanged)) {
1254                 
1255                 nframes64_t delta = 0;
1256                 
1257                 if (what_changed & ARDOUR::PositionChanged) {
1258                         delta = (nframes64_t) region->position() - (nframes64_t) region->last_position();
1259                 } 
1260                 
1261                 if (what_changed & ARDOUR::LengthChanged) {
1262                         delta += (nframes64_t) region->length() - (nframes64_t) region->last_length();
1263                 } 
1264
1265                 if (delta) {
1266                         possibly_splice (region->last_position() + region->last_length(), delta, region);
1267                 }
1268
1269                 if (holding_state ()) {
1270                         pending_bounds.push_back (region);
1271                 } else {
1272                         if (Config->get_layer_model() == MoveAddHigher) {
1273                                 /* it moved or changed length, so change the timestamp */
1274                                 timestamp_layer_op (region);
1275                         }
1276                         
1277                         notify_length_changed ();
1278                         relayer ();
1279                         check_dependents (region, false);
1280                 }
1281         }
1282 }
1283
1284 void
1285 Playlist::region_changed_proxy (Change what_changed, boost::weak_ptr<Region> weak_region)
1286 {
1287         boost::shared_ptr<Region> region (weak_region.lock());
1288
1289         if (!region) {
1290                 return;
1291         }
1292
1293
1294         /* this makes a virtual call to the right kind of playlist ... */
1295
1296         region_changed (what_changed, region);
1297 }
1298
1299 bool
1300 Playlist::region_changed (Change what_changed, boost::shared_ptr<Region> region)
1301 {
1302         Change our_interests = Change (Region::MuteChanged|Region::LayerChanged|Region::OpacityChanged);
1303         bool save = false;
1304
1305         if (in_set_state || in_flush) {
1306                 return false;
1307         }
1308
1309         if (what_changed & BoundsChanged) {
1310                 region_bounds_changed (what_changed, region);
1311                 save = !(_splicing || _nudging);
1312         }
1313                 
1314         if ((what_changed & our_interests) && 
1315             !(what_changed & Change (ARDOUR::PositionChanged|ARDOUR::LengthChanged))) {
1316                 check_dependents (region, false);
1317         }
1318
1319         if (what_changed & Change (ARDOUR::PositionChanged)) {
1320                 notify_region_moved (region);
1321         }
1322                 
1323         if (what_changed & our_interests) {
1324                 save = true;
1325         }
1326
1327         return save;
1328 }
1329
1330 void
1331 Playlist::drop_regions ()
1332 {
1333         RegionLock rl (this);
1334         regions.clear ();
1335         all_regions.clear ();
1336 }
1337
1338 void
1339 Playlist::clear (bool with_signals)
1340 {
1341         {
1342                 RegionLock rl (this);
1343
1344                 for (
1345                         std::list<sigc::connection>::iterator i = region_state_changed_connections.begin ();
1346                         i != region_state_changed_connections.end ();
1347                         ++i
1348                         ) {
1349
1350                         i->disconnect ();
1351                         
1352                 }
1353                      
1354                 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
1355                         pending_removes.insert (*i);
1356                 }
1357                 regions.clear ();
1358         }
1359
1360         if (with_signals) {
1361                 pending_length = false;
1362                 LengthChanged ();
1363                 pending_modified = false;
1364                 Modified ();
1365         }
1366
1367 }
1368
1369 /***********************************************************************
1370  FINDING THINGS
1371  **********************************************************************/
1372
1373 Playlist::RegionList *
1374 Playlist::regions_at (nframes_t frame)
1375
1376 {
1377         RegionLock rlock (this);
1378         return find_regions_at (frame);
1379 }       
1380
1381 boost::shared_ptr<Region>
1382 Playlist::top_region_at (nframes_t frame)
1383
1384 {
1385         RegionLock rlock (this);
1386         RegionList *rlist = find_regions_at (frame);
1387         boost::shared_ptr<Region> region;
1388         
1389         if (rlist->size()) {
1390                 RegionSortByLayer cmp;
1391                 rlist->sort (cmp);
1392                 region = rlist->back();
1393         } 
1394
1395         delete rlist;
1396         return region;
1397 }       
1398
1399 Playlist::RegionList*
1400 Playlist::regions_to_read (nframes_t start, nframes_t end)
1401 {
1402         /* Caller must hold lock */
1403
1404         RegionList covering;
1405         set<nframes_t> to_check;
1406         set<boost::shared_ptr<Region> > unique;
1407         RegionList here;
1408
1409         to_check.insert (start);
1410         to_check.insert (end);
1411
1412         for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
1413
1414                 /* find all/any regions that span start+end */
1415
1416                 switch ((*i)->coverage (start, end)) {
1417                 case OverlapNone:
1418                         break;
1419
1420                 case OverlapInternal:
1421                         covering.push_back (*i);
1422                         break;
1423
1424                 case OverlapStart:
1425                         to_check.insert ((*i)->position());
1426                         covering.push_back (*i);
1427                         break;
1428
1429                 case OverlapEnd:
1430                         to_check.insert ((*i)->last_frame());
1431                         covering.push_back (*i);
1432                         break;
1433
1434                 case OverlapExternal:
1435                         covering.push_back (*i);
1436                         to_check.insert ((*i)->position());
1437                         to_check.insert ((*i)->last_frame());
1438                         break;
1439                 }
1440
1441                 /* don't go too far */
1442
1443                 if ((*i)->position() > end) {
1444                         break;
1445                 }
1446         }
1447
1448         RegionList* rlist = new RegionList;
1449
1450         /* find all the regions that cover each position .... */
1451
1452         if (covering.size() == 1) {
1453
1454                 rlist->push_back (covering.front());
1455                 
1456         } else {
1457         
1458                 for (set<nframes_t>::iterator t = to_check.begin(); t != to_check.end(); ++t) {
1459                         
1460                         here.clear ();
1461                         
1462                         for (RegionList::iterator x = covering.begin(); x != covering.end(); ++x) {
1463                         
1464                                 if ((*x)->covers (*t)) {
1465                                         here.push_back (*x);
1466                                 }
1467                         }
1468                         
1469                         RegionSortByLayer cmp;
1470                         here.sort (cmp);
1471                         
1472                         /* ... and get the top/transparent regions at "here" */
1473                         
1474                         for (RegionList::reverse_iterator c = here.rbegin(); c != here.rend(); ++c) {
1475                                 
1476                                 unique.insert (*c);
1477                                 
1478                                 if ((*c)->opaque()) {
1479                                         
1480                                         /* the other regions at this position are hidden by this one */
1481                                         
1482                                         break;
1483                                 }
1484                         }
1485                 }
1486                 
1487                 for (set<boost::shared_ptr<Region> >::iterator s = unique.begin(); s != unique.end(); ++s) {
1488                         rlist->push_back (*s);
1489                 }
1490
1491                 if (rlist->size() > 1) {
1492                         /* now sort by time order */
1493                         
1494                         RegionSortByPosition cmp;
1495                         rlist->sort (cmp);
1496                 }
1497         }
1498
1499         return rlist;
1500 }
1501
1502 Playlist::RegionList *
1503 Playlist::find_regions_at (nframes_t frame)
1504 {
1505         /* Caller must hold lock */
1506
1507         RegionList *rlist = new RegionList;
1508
1509         for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
1510                 if ((*i)->covers (frame)) {
1511                         rlist->push_back (*i);
1512                 }
1513         }
1514
1515         return rlist;
1516 }
1517
1518 Playlist::RegionList *
1519 Playlist::regions_touched (nframes_t start, nframes_t end)
1520 {
1521         RegionLock rlock (this);
1522         RegionList *rlist = new RegionList;
1523
1524         for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
1525                 if ((*i)->coverage (start, end) != OverlapNone) {
1526                         rlist->push_back (*i);
1527                 }
1528         }
1529
1530         return rlist;
1531 }
1532
1533 nframes64_t
1534 Playlist::find_next_transient (nframes64_t from, int dir)
1535 {
1536         RegionLock rlock (this);
1537         AnalysisFeatureList points;
1538         AnalysisFeatureList these_points;
1539
1540         for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
1541                 if (dir > 0) {
1542                         if ((*i)->last_frame() < from) {
1543                                 continue;
1544                         }
1545                 } else {
1546                         if ((*i)->first_frame() > from) {
1547                                 continue;
1548                         }
1549                 }
1550
1551                 (*i)->get_transients (these_points);
1552
1553                 /* add first frame, just, err, because */
1554                 
1555                 these_points.push_back ((*i)->first_frame());
1556                 
1557                 points.insert (points.end(), these_points.begin(), these_points.end());
1558                 these_points.clear ();
1559         }
1560         
1561         if (points.empty()) {
1562                 return -1;
1563         }
1564
1565         TransientDetector::cleanup_transients (points, _session.frame_rate(), 3.0);
1566         bool reached = false;
1567         
1568         if (dir > 0) {
1569                 for (AnalysisFeatureList::iterator x = points.begin(); x != points.end(); ++x) {
1570                         if ((*x) >= from) {
1571                                 reached = true;
1572                         }
1573                         
1574                         if (reached && (*x) > from) {
1575                                 return *x;
1576                         }
1577                 }
1578         } else {
1579                 for (AnalysisFeatureList::reverse_iterator x = points.rbegin(); x != points.rend(); ++x) {
1580                         if ((*x) <= from) {
1581                                 reached = true;
1582                         }
1583                         
1584                         if (reached && (*x) < from) {
1585                                 return *x;
1586                         }
1587                 }
1588         }
1589
1590         return -1;
1591 }
1592
1593 boost::shared_ptr<Region>
1594 Playlist::find_next_region (nframes_t frame, RegionPoint point, int dir)
1595 {
1596         RegionLock rlock (this);
1597         boost::shared_ptr<Region> ret;
1598         nframes_t closest = max_frames;
1599
1600         bool end_iter = false;
1601
1602         for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
1603
1604                 if(end_iter) break;
1605
1606                 nframes_t distance;
1607                 boost::shared_ptr<Region> r = (*i);
1608                 nframes_t pos = 0;
1609
1610                 switch (point) {
1611                 case Start:
1612                         pos = r->first_frame ();
1613                         break;
1614                 case End:
1615                         pos = r->last_frame ();
1616                         break;
1617                 case SyncPoint:
1618                         pos = r->sync_position ();
1619                         // r->adjust_to_sync (r->first_frame());
1620                         break;
1621                 }
1622
1623                 switch (dir) {
1624                 case 1: /* forwards */
1625
1626                         if (pos > frame) {
1627                                 if ((distance = pos - frame) < closest) {
1628                                         closest = distance;
1629                                         ret = r;
1630                                         end_iter = true;
1631                                 }
1632                         }
1633
1634                         break;
1635
1636                 default: /* backwards */
1637                         
1638                         if (pos < frame) {
1639                                 if ((distance = frame - pos) < closest) {
1640                                         closest = distance;
1641                                         ret = r;
1642                                 }
1643                         }
1644                         else {
1645                                 end_iter = true;
1646                         }
1647
1648                         break;
1649                 }
1650         }
1651
1652         return ret;
1653 }
1654
1655 nframes64_t
1656 Playlist::find_next_region_boundary (nframes64_t frame, int dir)
1657 {
1658         RegionLock rlock (this);
1659
1660         nframes64_t closest = max_frames;
1661         nframes64_t ret = -1;
1662
1663         if (dir > 0) {
1664
1665                 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
1666
1667                         boost::shared_ptr<Region> r = (*i);
1668                         nframes64_t distance;
1669                         
1670                         if (r->first_frame() > frame) {
1671
1672                                 distance = r->first_frame() - frame;
1673                                 
1674                                 if (distance < closest) {
1675                                         ret = r->first_frame();
1676                                         closest = distance;
1677                                 }
1678                         }
1679
1680                         if (r->last_frame () > frame) {
1681                                 
1682                                 distance = r->last_frame () - frame;
1683                                 
1684                                 if (distance < closest) {
1685                                         ret = r->last_frame ();
1686                                         closest = distance;
1687                                 }
1688                         }
1689                 }
1690
1691         } else {
1692
1693                 for (RegionList::reverse_iterator i = regions.rbegin(); i != regions.rend(); ++i) {
1694                         
1695                         boost::shared_ptr<Region> r = (*i);
1696                         nframes64_t distance;
1697
1698                         if (r->last_frame() < frame) {
1699
1700                                 distance = frame - r->last_frame();
1701                                 
1702                                 if (distance < closest) {
1703                                         ret = r->last_frame();
1704                                         closest = distance;
1705                                 }
1706                         }
1707
1708                         if (r->first_frame() < frame) {
1709                                 
1710                                 distance = frame - r->first_frame();
1711
1712                                 if (distance < closest) {
1713                                         ret = r->first_frame();
1714                                         closest = distance;
1715                                 }
1716                         }
1717                 }
1718         }
1719
1720         return ret;
1721 }
1722
1723 /***********************************************************************/
1724
1725
1726
1727
1728 void
1729 Playlist::mark_session_dirty ()
1730 {
1731         if (!in_set_state && !holding_state ()) {
1732                 _session.set_dirty();
1733         }
1734 }
1735
1736 int
1737 Playlist::set_state (const XMLNode& node)
1738 {
1739         XMLNode *child;
1740         XMLNodeList nlist;
1741         XMLNodeConstIterator niter;
1742         XMLPropertyList plist;
1743         XMLPropertyConstIterator piter;
1744         XMLProperty *prop;
1745         boost::shared_ptr<Region> region;
1746         string region_name;
1747
1748         in_set_state++;
1749
1750         if (node.name() != "Playlist") {
1751                 in_set_state--;
1752                 return -1;
1753         }
1754
1755         freeze ();
1756
1757         plist = node.properties();
1758
1759         for (piter = plist.begin(); piter != plist.end(); ++piter) {
1760
1761                 prop = *piter;
1762                 
1763                 if (prop->name() == X_("name")) {
1764                         _name = prop->value();
1765                 } else if (prop->name() == X_("orig_diskstream_id")) {
1766                         _orig_diskstream_id = prop->value ();
1767                 } else if (prop->name() == X_("frozen")) {
1768                         _frozen = (prop->value() == X_("yes"));
1769                 }
1770         }
1771
1772         clear (false);
1773         
1774         nlist = node.children();
1775
1776         for (niter = nlist.begin(); niter != nlist.end(); ++niter) {
1777
1778                 child = *niter;
1779                 
1780                 if (child->name() == "Region") {
1781
1782                         if ((prop = child->property ("id")) == 0) {
1783                                 error << _("region state node has no ID, ignored") << endmsg;
1784                                 continue;
1785                         }
1786                         
1787                         ID id = prop->value ();
1788                         
1789                         if ((region = region_by_id (id))) {
1790
1791                                 Change what_changed = Change (0);
1792
1793                                 if (region->set_live_state (*child, what_changed, true)) {
1794                                         error << _("Playlist: cannot reset region state from XML") << endmsg;
1795                                         continue;
1796                                 }
1797
1798                         } else if ((region = RegionFactory::create (_session, *child, true)) == 0) {
1799                                 error << _("Playlist: cannot create region from XML") << endmsg;
1800                                 continue;
1801                         }
1802
1803                         add_region (region, region->position(), 1.0);
1804
1805                         // So that layer_op ordering doesn't get screwed up
1806                         region->set_last_layer_op( region->layer());
1807
1808                 }                       
1809         }
1810         
1811         notify_modified ();
1812
1813         thaw ();
1814
1815         /* update dependents, which was not done during add_region_internal 
1816            due to in_set_state being true 
1817         */
1818
1819         for (RegionList::iterator r = regions.begin(); r != regions.end(); ++r) {
1820                 check_dependents (*r, false);
1821         }
1822
1823         in_set_state--;
1824         first_set_state = false;
1825         return 0;
1826 }
1827
1828 XMLNode&
1829 Playlist::get_state()
1830 {
1831         return state(true);
1832 }
1833
1834 XMLNode&
1835 Playlist::get_template()
1836 {
1837         return state(false);
1838 }
1839
1840 XMLNode&
1841 Playlist::state (bool full_state)
1842 {
1843         XMLNode *node = new XMLNode (X_("Playlist"));
1844         char buf[64];
1845         
1846         node->add_property (X_("name"), _name);
1847         node->add_property (X_("type"), _type.to_string());
1848
1849         _orig_diskstream_id.print (buf, sizeof (buf));
1850         node->add_property (X_("orig_diskstream_id"), buf);
1851         node->add_property (X_("frozen"), _frozen ? "yes" : "no");
1852
1853         if (full_state) {
1854                 RegionLock rlock (this, false);
1855                 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
1856                         node->add_child_nocopy ((*i)->get_state());
1857                 }
1858         }
1859
1860         if (_extra_xml) {
1861                 node->add_child_copy (*_extra_xml);
1862         }
1863
1864         return *node;
1865 }
1866
1867 bool
1868 Playlist::empty() const
1869 {
1870         RegionLock rlock (const_cast<Playlist *>(this), false);
1871         return regions.empty();
1872 }
1873
1874 uint32_t
1875 Playlist::n_regions() const
1876 {
1877         RegionLock rlock (const_cast<Playlist *>(this), false);
1878         return regions.size();
1879 }
1880
1881 nframes_t
1882 Playlist::get_maximum_extent () const
1883 {
1884         RegionLock rlock (const_cast<Playlist *>(this), false);
1885         return _get_maximum_extent ();
1886 }
1887
1888 ARDOUR::nframes_t
1889 Playlist::_get_maximum_extent () const
1890 {
1891         RegionList::const_iterator i;
1892         nframes_t max_extent = 0;
1893         nframes_t end = 0;
1894
1895         for (i = regions.begin(); i != regions.end(); ++i) {
1896                 if ((end = (*i)->position() + (*i)->length()) > max_extent) {
1897                         max_extent = end;
1898                 }
1899         }
1900
1901         return max_extent;
1902 }
1903
1904 string 
1905 Playlist::bump_name (string name, Session &session)
1906 {
1907         string newname = name;
1908
1909         do {
1910                 newname = bump_name_once (newname);
1911         } while (session.playlist_by_name (newname)!=NULL);
1912
1913         return newname;
1914 }
1915
1916
1917 layer_t
1918 Playlist::top_layer() const
1919 {
1920         RegionLock rlock (const_cast<Playlist *> (this));       
1921         layer_t top = 0;
1922
1923         for (RegionList::const_iterator i = regions.begin(); i != regions.end(); ++i) {
1924                 top = max (top, (*i)->layer());
1925         }
1926         return top;
1927 }
1928
1929 void
1930 Playlist::set_edit_mode (EditMode mode)
1931 {
1932         _edit_mode = mode;
1933 }
1934
1935 /********************
1936  * Region Layering
1937  ********************/
1938
1939 void
1940 Playlist::relayer ()
1941 {
1942         /* don't send multiple Modified notifications
1943            when multiple regions are relayered.
1944         */
1945  
1946         freeze ();
1947
1948         /* build up a new list of regions on each layer */
1949
1950         std::vector<RegionList> layers;
1951
1952         /* we want to go through regions from desired lowest to desired highest layer,
1953            which depends on the layer model
1954         */
1955
1956         RegionList copy = regions;
1957
1958         /* sort according to the model */
1959
1960         if (Config->get_layer_model() == MoveAddHigher || Config->get_layer_model() == AddHigher) {
1961                 RegionSortByLastLayerOp cmp;
1962                 copy.sort (cmp);
1963         }
1964         
1965         for (RegionList::iterator i = copy.begin(); i != copy.end(); ++i) {
1966
1967                 /* find the lowest layer that this region can go on */
1968                 size_t j = layers.size();
1969                 while (j > 0) {
1970                         /* try layer j - 1; it can go on if it overlaps no other region
1971                            that is already on that layer
1972                         */
1973                         RegionList::iterator k = layers[j - 1].begin();
1974                         while (k != layers[j - 1].end()) {
1975                                 if ((*k)->overlap_equivalent (*i)) {
1976                                         break;
1977                                 }
1978                                 k++;
1979                         }
1980
1981                         if (k != layers[j - 1].end()) {
1982                                 /* no overlap, so we can use this layer */
1983                                 break;
1984                         }
1985                                         
1986                         j--;
1987                 }
1988
1989                 if (j == layers.size()) {
1990                         /* we need a new layer for this region */
1991                         layers.push_back (RegionList ());
1992                 }
1993
1994                 layers[j].push_back (*i);
1995         }
1996
1997         /* first pass: set up the layer numbers in the regions */
1998         for (size_t j = 0; j < layers.size(); ++j) {
1999                 for (RegionList::iterator i = layers[j].begin(); i != layers[j].end(); ++i) {
2000                         (*i)->set_layer (j);
2001                 }
2002         }
2003
2004         /* sending Modified means that various kinds of layering
2005            models operate correctly at the GUI
2006            level. slightly inefficient, but only slightly.
2007
2008            We force a Modified signal here in case no layers actually
2009            changed.
2010         */
2011
2012         notify_modified ();
2013
2014         thaw ();
2015 }
2016
2017 /* XXX these layer functions are all deprecated */
2018
2019 void
2020 Playlist::raise_region (boost::shared_ptr<Region> region)
2021 {
2022         uint32_t rsz = regions.size();
2023         layer_t target = region->layer() + 1U;
2024
2025         if (target >= rsz) {
2026                 /* its already at the effective top */
2027                 return;
2028         }
2029
2030         move_region_to_layer (target, region, 1);
2031 }
2032
2033 void
2034 Playlist::lower_region (boost::shared_ptr<Region> region)
2035 {
2036         if (region->layer() == 0) {
2037                 /* its already at the bottom */
2038                 return;
2039         }
2040
2041         layer_t target = region->layer() - 1U;
2042
2043         move_region_to_layer (target, region, -1);
2044 }
2045
2046 void
2047 Playlist::raise_region_to_top (boost::shared_ptr<Region> region)
2048 {
2049         /* does nothing useful if layering mode is later=higher */
2050         if ((Config->get_layer_model() == MoveAddHigher) ||
2051             (Config->get_layer_model() == AddHigher)) {
2052                 timestamp_layer_op (region);
2053                 relayer ();
2054         }
2055 }
2056
2057 void
2058 Playlist::lower_region_to_bottom (boost::shared_ptr<Region> region)
2059 {
2060         /* does nothing useful if layering mode is later=higher */
2061         if ((Config->get_layer_model() == MoveAddHigher) ||
2062             (Config->get_layer_model() == AddHigher)) {
2063                 region->set_last_layer_op (0);
2064                 relayer ();
2065         }
2066 }
2067
2068 int
2069 Playlist::move_region_to_layer (layer_t target_layer, boost::shared_ptr<Region> region, int dir)
2070 {
2071         RegionList::iterator i;
2072         typedef pair<boost::shared_ptr<Region>,layer_t> LayerInfo;
2073         list<LayerInfo> layerinfo;
2074         layer_t dest;
2075
2076         {
2077                 RegionLock rlock (const_cast<Playlist *> (this));
2078                 
2079                 for (i = regions.begin(); i != regions.end(); ++i) {
2080                         
2081                         if (region == *i) {
2082                                 continue;
2083                         }
2084
2085                         if (dir > 0) {
2086
2087                                 /* region is moving up, move all regions on intermediate layers
2088                                    down 1
2089                                 */
2090                                 
2091                                 if ((*i)->layer() > region->layer() && (*i)->layer() <= target_layer) {
2092                                         dest = (*i)->layer() - 1;
2093                                 } else {
2094                                         /* not affected */
2095                                         continue;
2096                                 }
2097                         } else {
2098
2099                                 /* region is moving down, move all regions on intermediate layers
2100                                    up 1
2101                                 */
2102
2103                                 if ((*i)->layer() < region->layer() && (*i)->layer() >= target_layer) {
2104                                         dest = (*i)->layer() + 1;
2105                                 } else {
2106                                         /* not affected */
2107                                         continue;
2108                                 }
2109                         }
2110
2111                         LayerInfo newpair;
2112                         
2113                         newpair.first = *i;
2114                         newpair.second = dest;
2115                         
2116                         layerinfo.push_back (newpair);
2117                 } 
2118         }
2119
2120         /* now reset the layers without holding the region lock */
2121
2122         for (list<LayerInfo>::iterator x = layerinfo.begin(); x != layerinfo.end(); ++x) {
2123                 x->first->set_layer (x->second);
2124         }
2125
2126         region->set_layer (target_layer);
2127
2128 #if 0
2129         /* now check all dependents */
2130
2131         for (list<LayerInfo>::iterator x = layerinfo.begin(); x != layerinfo.end(); ++x) {
2132                 check_dependents (x->first, false);
2133         }
2134         
2135         check_dependents (region, false);
2136 #endif
2137         
2138         return 0;
2139 }
2140
2141 void
2142 Playlist::nudge_after (nframes_t start, nframes_t distance, bool forwards)
2143 {
2144         RegionList::iterator i;
2145         nframes_t new_pos;
2146         bool moved = false;
2147
2148         _nudging = true;
2149
2150         {
2151                 RegionLock rlock (const_cast<Playlist *> (this));
2152                 
2153                 for (i = regions.begin(); i != regions.end(); ++i) {
2154
2155                         if ((*i)->position() >= start) {
2156
2157                                 if (forwards) {
2158
2159                                         if ((*i)->last_frame() > max_frames - distance) {
2160                                                 new_pos = max_frames - (*i)->length();
2161                                         } else {
2162                                                 new_pos = (*i)->position() + distance;
2163                                         }
2164                                         
2165                                 } else {
2166                                         
2167                                         if ((*i)->position() > distance) {
2168                                                 new_pos = (*i)->position() - distance;
2169                                         } else {
2170                                                 new_pos = 0;
2171                                         }
2172                                 }
2173
2174                                 (*i)->set_position (new_pos, this);
2175                                 moved = true;
2176                         }
2177                 }
2178         }
2179
2180         if (moved) {
2181                 _nudging = false;
2182                 notify_length_changed ();
2183         }
2184
2185 }
2186
2187 boost::shared_ptr<Region>
2188 Playlist::find_region (const ID& id) const
2189 {
2190         RegionLock rlock (const_cast<Playlist*> (this));
2191
2192         /* searches all regions currently in use by the playlist */
2193
2194         for (RegionList::const_iterator i = regions.begin(); i != regions.end(); ++i) {
2195                 if ((*i)->id() == id) {
2196                         return *i;
2197                 }
2198         }
2199
2200         return boost::shared_ptr<Region> ();
2201 }
2202
2203 boost::shared_ptr<Region>
2204 Playlist::region_by_id (ID id)
2205 {
2206         /* searches all regions ever added to this playlist */
2207
2208         for (set<boost::shared_ptr<Region> >::iterator i = all_regions.begin(); i != all_regions.end(); ++i) {
2209                 if ((*i)->id() == id) {
2210                         return *i;
2211                 }
2212         }
2213         return boost::shared_ptr<Region> ();
2214 }
2215         
2216 void
2217 Playlist::dump () const
2218 {
2219         boost::shared_ptr<Region> r;
2220
2221         cerr << "Playlist \"" << _name << "\" " << endl
2222              << regions.size() << " regions "
2223              << endl;
2224
2225         for (RegionList::const_iterator i = regions.begin(); i != regions.end(); ++i) {
2226                 r = *i;
2227                 cerr << "  " << r->name() << " [" 
2228                      << r->start() << "+" << r->length() 
2229                      << "] at " 
2230                      << r->position()
2231                      << " on layer "
2232                      << r->layer ()
2233                      << endl;
2234         }
2235 }
2236
2237 void
2238 Playlist::set_frozen (bool yn)
2239 {
2240         _frozen = yn;
2241 }
2242
2243 void
2244 Playlist::timestamp_layer_op (boost::shared_ptr<Region> region)
2245 {
2246 //      struct timeval tv;
2247 //      gettimeofday (&tv, 0);
2248         region->set_last_layer_op (++layer_op_counter);
2249 }
2250
2251
2252 void
2253 Playlist::shuffle (boost::shared_ptr<Region> region, int dir)
2254 {
2255         bool moved = false;
2256         nframes_t new_pos;
2257
2258         if (region->locked()) {
2259                 return;
2260         }
2261
2262         _shuffling = true;
2263
2264         {
2265                 RegionLock rlock (const_cast<Playlist*> (this));
2266                 
2267                 
2268                 if (dir > 0) {
2269                         
2270                         RegionList::iterator next;
2271
2272                         for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {       
2273                                 if ((*i) == region) {
2274                                         next = i;
2275                                         ++next;
2276
2277                                         if (next != regions.end()) {
2278
2279                                                 if ((*next)->locked()) {
2280                                                         break;
2281                                                 }
2282
2283                                                 if ((*next)->position() != region->last_frame() + 1) {
2284                                                         /* they didn't used to touch, so after shuffle,
2285                                                            just have them swap positions.
2286                                                         */
2287                                                         new_pos = (*next)->position();
2288                                                 } else {
2289                                                         /* they used to touch, so after shuffle,
2290                                                            make sure they still do. put the earlier
2291                                                            region where the later one will end after
2292                                                            it is moved.
2293                                                         */
2294                                                         new_pos = region->position() + (*next)->length();
2295                                                 }
2296
2297                                                 (*next)->set_position (region->position(), this);
2298                                                 region->set_position (new_pos, this);
2299
2300                                                 /* avoid a full sort */
2301
2302                                                 regions.erase (i); // removes the region from the list */
2303                                                 next++;
2304                                                 regions.insert (next, region); // adds it back after next
2305
2306                                                 moved = true;
2307                                         }
2308                                         break;
2309                                 }
2310                         }
2311                 } else {
2312                         
2313                         RegionList::iterator prev = regions.end();
2314                         
2315                         for (RegionList::iterator i = regions.begin(); i != regions.end(); prev = i, ++i) {     
2316                                 if ((*i) == region) {
2317
2318                                         if (prev != regions.end()) {
2319
2320                                                 if ((*prev)->locked()) {
2321                                                         break;
2322                                                 }
2323
2324                                                 if (region->position() != (*prev)->last_frame() + 1) {
2325                                                         /* they didn't used to touch, so after shuffle,
2326                                                            just have them swap positions.
2327                                                         */
2328                                                         new_pos = region->position();
2329                                                 } else {
2330                                                         /* they used to touch, so after shuffle,
2331                                                            make sure they still do. put the earlier
2332                                                            one where the later one will end after
2333                                                         */
2334                                                         new_pos = (*prev)->position() + region->length();
2335                                                 }
2336
2337                                                 region->set_position ((*prev)->position(), this);
2338                                                 (*prev)->set_position (new_pos, this);
2339                                                 
2340                                                 /* avoid a full sort */
2341
2342                                                 regions.erase (i); // remove region
2343                                                 regions.insert (prev, region); // insert region before prev
2344
2345                                                 moved = true;
2346                                         }
2347
2348                                         break;
2349                                 }
2350                         }
2351                 }
2352         }
2353
2354         _shuffling = false;
2355
2356         if (moved) {
2357
2358                 relayer ();
2359                 check_dependents (region, false);
2360                 
2361                 notify_modified();
2362         }
2363
2364 }
2365
2366 bool
2367 Playlist::region_is_shuffle_constrained (boost::shared_ptr<Region>) 
2368 {
2369         RegionLock rlock (const_cast<Playlist*> (this));
2370         
2371         if (regions.size() > 1) {
2372                 return true;
2373         }
2374
2375         return false;
2376 }
2377
2378 void
2379 Playlist::update_after_tempo_map_change ()
2380 {
2381         RegionLock rlock (const_cast<Playlist*> (this));
2382         RegionList copy (regions);
2383
2384         freeze ();
2385         
2386         for (RegionList::iterator i = copy.begin(); i != copy.end(); ++i) {     
2387                 (*i)->update_position_after_tempo_map_change ();
2388         }
2389
2390         thaw ();
2391 }