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