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