fixes for destructive track offsets of various kinds; move from jack_nframes_t -...
[ardour.git] / libs / ardour / audio_playlist.cc
1 /*
2     Copyright (C) 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     $Id$
19 */
20
21 #include <algorithm>
22
23 #include <cstdlib>
24
25 #include <sigc++/bind.h>
26
27 #include <ardour/types.h>
28 #include <ardour/configuration.h>
29 #include <ardour/audioplaylist.h>
30 #include <ardour/audioregion.h>
31 #include <ardour/crossfade.h>
32 #include <ardour/crossfade_compare.h>
33 #include <ardour/session.h>
34
35 #include "i18n.h"
36
37 using namespace ARDOUR;
38 using namespace sigc;
39 using namespace std;
40 using namespace PBD;
41
42 AudioPlaylist::State::~State ()
43 {
44 }
45
46 AudioPlaylist::AudioPlaylist (Session& session, const XMLNode& node, bool hidden)
47         : Playlist (session, node, hidden)
48 {
49         in_set_state = true;
50         set_state (node);
51         in_set_state = false;
52
53         save_state (_("initial state"));
54
55         if (!hidden) {
56                 PlaylistCreated (this); /* EMIT SIGNAL */
57         }
58 }
59
60 AudioPlaylist::AudioPlaylist (Session& session, string name, bool hidden)
61         : Playlist (session, name, hidden)
62 {
63         save_state (_("initial state"));
64
65         if (!hidden) {
66                 PlaylistCreated (this); /* EMIT SIGNAL */
67         }
68
69 }
70
71 AudioPlaylist::AudioPlaylist (const AudioPlaylist& other, string name, bool hidden)
72         : Playlist (other, name, hidden)
73 {
74         save_state (_("initial state"));
75
76         RegionList::const_iterator in_o  = other.regions.begin();
77         RegionList::iterator in_n = regions.begin();
78
79         while (in_o != other.regions.end()) {
80                 boost::shared_ptr<AudioRegion> ar = boost::dynamic_pointer_cast<AudioRegion>(*in_o);
81
82                 // We look only for crossfades which begin with the current region, so we don't get doubles
83                 for (list<Crossfade *>::const_iterator xfades = other._crossfades.begin(); xfades != other._crossfades.end(); ++xfades) {
84                         if ((*xfades)->in() == ar) {
85                                 // We found one! Now copy it!
86
87                                 RegionList::const_iterator out_o = other.regions.begin();
88                                 RegionList::const_iterator out_n = regions.begin();
89
90                                 while (out_o != other.regions.end()) {
91                                         
92                                         boost::shared_ptr<AudioRegion>ar2 = boost::dynamic_pointer_cast<AudioRegion>(*out_o);
93                                         
94                                         if ((*xfades)->out() == ar2) {
95                                                 boost::shared_ptr<AudioRegion>in  = boost::dynamic_pointer_cast<AudioRegion>(*in_n);
96                                                 boost::shared_ptr<AudioRegion>out = boost::dynamic_pointer_cast<AudioRegion>(*out_n);
97                                                 Crossfade *new_fade = new Crossfade (*(*xfades), in, out);
98                                                 add_crossfade(*new_fade);
99                                                 break;
100                                         }
101                                         
102                                         out_o++;
103                                         out_n++;
104                                 }
105 //                              cerr << "HUH!? second region in the crossfade not found!" << endl;
106                         }
107                 }
108
109                 in_o++;
110                 in_n++;
111         }
112
113         if (!hidden) {
114                 PlaylistCreated (this); /* EMIT SIGNAL */
115         }
116 }
117
118 AudioPlaylist::AudioPlaylist (const AudioPlaylist& other, nframes_t start, nframes_t cnt, string name, bool hidden)
119         : Playlist (other, start, cnt, name, hidden)
120 {
121         save_state (_("initial state"));
122
123         /* this constructor does NOT notify others (session) */
124 }
125
126 AudioPlaylist::~AudioPlaylist ()
127 {
128         set<Crossfade*> all_xfades;
129         set<Region*> all_regions;
130
131         GoingAway (); /* EMIT SIGNAL */
132
133         /* drop connections to signals */
134
135         notify_callbacks ();
136
137         for (Crossfades::iterator x = _crossfades.begin(); x != _crossfades.end(); ++x) {
138                 all_xfades.insert (*x);
139         }
140
141         for (StateMap::iterator i = states.begin(); i != states.end(); ++i) {
142                 
143                 AudioPlaylist::State* apstate = dynamic_cast<AudioPlaylist::State*> (*i);
144
145         for (Crossfades::iterator xf = apstate->crossfades.begin(); xf != apstate->crossfades.end(); ++xf) {
146                         all_xfades.insert (*xf);
147                 }
148
149                 delete apstate;
150         }
151
152         /* delete every crossfade */
153
154         for (set<Crossfade *>::iterator axf = all_xfades.begin(); axf != all_xfades.end(); ++axf) {
155                 delete *axf;
156         }
157 }
158
159 struct RegionSortByLayer {
160     bool operator() (boost::shared_ptr<Region>a, boost::shared_ptr<Region>b) {
161             return a->layer() < b->layer();
162     }
163 };
164
165 nframes_t
166 AudioPlaylist::read (Sample *buf, Sample *mixdown_buffer, float *gain_buffer, nframes_t start,
167                      nframes_t cnt, unsigned chan_n)
168 {
169         nframes_t ret = cnt;
170         nframes_t end;
171         nframes_t read_frames;
172         nframes_t skip_frames;
173
174         /* optimizing this memset() away involves a lot of conditionals
175            that may well cause more of a hit due to cache misses 
176            and related stuff than just doing this here.
177            
178            it would be great if someone could measure this
179            at some point.
180
181            one way or another, parts of the requested area
182            that are not written to by Region::region_at()
183            for all Regions that cover the area need to be
184            zeroed.
185         */
186
187         memset (buf, 0, sizeof (Sample) * cnt);
188
189         /* this function is never called from a realtime thread, so 
190            its OK to block (for short intervals).
191         */
192
193         Glib::Mutex::Lock rm (region_lock);
194
195         end =  start + cnt - 1;
196
197         read_frames = 0;
198         skip_frames = 0;
199         _read_data_count = 0;
200
201         map<uint32_t,vector<boost::shared_ptr<Region> > > relevant_regions;
202         map<uint32_t,vector<Crossfade*> > relevant_xfades;
203         vector<uint32_t> relevant_layers;
204
205         for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
206                 if ((*i)->coverage (start, end) != OverlapNone) {
207                         relevant_regions[(*i)->layer()].push_back (*i);
208                         relevant_layers.push_back ((*i)->layer());
209                 }
210         }
211
212         for (Crossfades::iterator i = _crossfades.begin(); i != _crossfades.end(); ++i) {
213                 if ((*i)->coverage (start, end) != OverlapNone) {
214                         relevant_xfades[(*i)->upper_layer()].push_back (*i);
215                 }
216         }
217
218 //      RegionSortByLayer layer_cmp;
219 //      relevant_regions.sort (layer_cmp);
220
221         /* XXX this whole per-layer approach is a hack that
222            should be removed once Crossfades become
223            CrossfadeRegions and we just grab a list of relevant
224            regions and call read_at() on all of them.
225         */
226
227         sort (relevant_layers.begin(), relevant_layers.end());
228
229         for (vector<uint32_t>::iterator l = relevant_layers.begin(); l != relevant_layers.end(); ++l) {
230
231                 vector<boost::shared_ptr<Region> > r (relevant_regions[*l]);
232                 vector<Crossfade*>& x (relevant_xfades[*l]);
233
234                 for (vector<boost::shared_ptr<Region> >::iterator i = r.begin(); i != r.end(); ++i) {
235                         boost::shared_ptr<AudioRegion> ar = boost::dynamic_pointer_cast<AudioRegion>(*i);
236                         assert(ar);
237                         ar->read_at (buf, mixdown_buffer, gain_buffer, start, cnt, chan_n, read_frames, skip_frames);
238                         _read_data_count += ar->read_data_count();
239                 }
240                 
241                 for (vector<Crossfade*>::iterator i = x.begin(); i != x.end(); ++i) {
242                         (*i)->read_at (buf, mixdown_buffer, gain_buffer, start, cnt, chan_n);
243
244                         /* don't JACK up _read_data_count, since its the same data as we just
245                            read from the regions, and the OS should handle that for us.
246                         */
247                 }
248         }
249
250         return ret;
251 }
252
253
254 void
255 AudioPlaylist::remove_dependents (boost::shared_ptr<Region> region)
256 {
257         Crossfades::iterator i, tmp;
258         boost::shared_ptr<AudioRegion> r = boost::dynamic_pointer_cast<AudioRegion> (region);
259         
260         if (r == 0) {
261                 fatal << _("programming error: non-audio Region passed to remove_overlap in audio playlist")
262                       << endmsg;
263                 return;
264         }
265
266         for (i = _crossfades.begin(); i != _crossfades.end(); ) {
267                 tmp = i;
268                 tmp++;
269
270                 if ((*i)->involves (r)) {
271                         /* do not delete crossfades */
272                         _crossfades.erase (i);
273                 }
274                 
275                 i = tmp;
276         }
277 }
278
279
280 void
281 AudioPlaylist::flush_notifications ()
282 {
283         Playlist::flush_notifications();
284
285         if (in_flush) {
286                 return;
287         }
288
289         in_flush = true;
290
291         Crossfades::iterator a;
292         for (a = _pending_xfade_adds.begin(); a != _pending_xfade_adds.end(); ++a) {
293                 NewCrossfade (*a); /* EMIT SIGNAL */
294         }
295
296         _pending_xfade_adds.clear ();
297         
298         in_flush = false;
299 }
300
301 void
302 AudioPlaylist::refresh_dependents (boost::shared_ptr<Region> r)
303 {
304         boost::shared_ptr<AudioRegion> ar = boost::dynamic_pointer_cast<AudioRegion>(r);
305         set<Crossfade*> updated;
306
307         if (ar == 0) {
308                 return;
309         }
310
311         for (Crossfades::iterator x = _crossfades.begin(); x != _crossfades.end();) {
312
313                 Crossfades::iterator tmp;
314                 
315                 tmp = x;
316                 ++tmp;
317
318                 /* only update them once */
319
320                 if ((*x)->involves (ar)) {
321
322                         if (find (updated.begin(), updated.end(), *x) == updated.end()) {
323                                 if ((*x)->refresh ()) {
324                                         /* not invalidated by the refresh */
325                                         updated.insert (*x);
326                                 }
327                         }
328                 }
329
330                 x = tmp;
331         }
332 }
333
334 void
335 AudioPlaylist::finalize_split_region (boost::shared_ptr<Region> o, boost::shared_ptr<Region> l, boost::shared_ptr<Region> r)
336 {
337         boost::shared_ptr<AudioRegion> orig  = boost::dynamic_pointer_cast<AudioRegion>(o);
338         boost::shared_ptr<AudioRegion> left  = boost::dynamic_pointer_cast<AudioRegion>(l);
339         boost::shared_ptr<AudioRegion> right = boost::dynamic_pointer_cast<AudioRegion>(r);
340
341         for (Crossfades::iterator x = _crossfades.begin(); x != _crossfades.end();) {
342                 Crossfades::iterator tmp;
343                 tmp = x;
344                 ++tmp;
345
346                 Crossfade *fade = 0;
347                 
348                 if ((*x)->_in == orig) {
349                         if (! (*x)->covers(right->position())) {
350                                 fade = new Crossfade (**x, left, (*x)->_out);
351                         } else {
352                                 // Overlap, the crossfade is copied on the left side of the right region instead
353                                 fade = new Crossfade (**x, right, (*x)->_out);
354                         }
355                 }
356                 
357                 if ((*x)->_out == orig) {
358                         if (! (*x)->covers(right->position())) {
359                                 fade = new Crossfade (**x, (*x)->_in, right);
360                         } else {
361                                 // Overlap, the crossfade is copied on the right side of the left region instead
362                                 fade = new Crossfade (**x, (*x)->_in, left);
363                         }
364                 }
365                 
366                 if (fade) {
367                         _crossfades.remove (*x);
368                         add_crossfade (*fade);
369                 }
370                 x = tmp;
371         }
372 }
373
374 void
375 AudioPlaylist::check_dependents (boost::shared_ptr<Region> r, bool norefresh)
376 {
377         boost::shared_ptr<AudioRegion> other;
378         boost::shared_ptr<AudioRegion> region;
379         boost::shared_ptr<AudioRegion> top;
380         boost::shared_ptr<AudioRegion> bottom;
381         Crossfade*   xfade;
382
383         if (in_set_state || in_partition) {
384                 return;
385         }
386
387         if ((region = boost::dynamic_pointer_cast<AudioRegion> (r)) == 0) {
388                 fatal << _("programming error: non-audio Region tested for overlap in audio playlist")
389                       << endmsg;
390                 return;
391         }
392
393         if (!norefresh) {
394                 refresh_dependents (r);
395         }
396
397         if (!Config->get_auto_xfade()) {
398                 return;
399         }
400
401         for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
402
403                 other = boost::dynamic_pointer_cast<AudioRegion> (*i);
404
405                 if (other == region) {
406                         continue;
407                 }
408
409                 if (other->muted() || region->muted()) {
410                         continue;
411                 }
412
413                 if (other->layer() < region->layer()) {
414                         top = region;
415                         bottom = other;
416                 } else {
417                         top = other;
418                         bottom = region;
419                 }
420
421                 try {
422                                 
423                         if (top->coverage (bottom->position(), bottom->last_frame()) != OverlapNone) {
424                                 
425                                 /* check if the upper region is within the lower region */
426                                 
427                                 if (top->first_frame() > bottom->first_frame() &&
428                                     top->last_frame() < bottom->last_frame()) {
429                                         
430                                         
431                                         /*     [ -------- top ------- ]
432                                          * {=========== bottom =============}
433                                          */
434                                         
435                                         /* to avoid discontinuities at the region boundaries of an internal
436                                            overlap (this region is completely within another), we create
437                                            two hidden crossfades at each boundary. this is not dependent
438                                            on the auto-xfade option, because we require it as basic
439                                            audio engineering.
440                                         */
441                                         
442                                         nframes_t xfade_length = min ((nframes_t) 720, top->length());
443                                         
444                                                             /*  in,      out */
445                                         xfade = new Crossfade (top, bottom, xfade_length, top->first_frame(), StartOfIn);
446                                         add_crossfade (*xfade);
447                                         xfade = new Crossfade (bottom, top, xfade_length, top->last_frame() - xfade_length, EndOfOut);
448                                         add_crossfade (*xfade);
449                                         
450                                 } else {
451                 
452                                         xfade = new Crossfade (other, region, Config->get_xfade_model(), Config->get_crossfades_active());
453                                         add_crossfade (*xfade);
454                                 }
455                         } 
456                 }
457                 
458                 catch (failed_constructor& err) {
459                         continue;
460                 }
461                 
462                 catch (Crossfade::NoCrossfadeHere& err) {
463                         continue;
464                 }
465                 
466         }
467 }
468
469 void
470 AudioPlaylist::add_crossfade (Crossfade& xfade)
471 {
472         Crossfades::iterator ci;
473
474         for (ci = _crossfades.begin(); ci != _crossfades.end(); ++ci) {
475                 if (*(*ci) == xfade) { // Crossfade::operator==()
476                         break;
477                 }
478         }
479         
480         if (ci != _crossfades.end()) {
481                 delete &xfade;
482         } else {
483                 _crossfades.push_back (&xfade);
484
485                 xfade.Invalidated.connect (mem_fun (*this, &AudioPlaylist::crossfade_invalidated));
486                 xfade.StateChanged.connect (mem_fun (*this, &AudioPlaylist::crossfade_changed));
487
488                 notify_crossfade_added (&xfade);
489         }
490 }
491         
492 void AudioPlaylist::notify_crossfade_added (Crossfade *x)
493 {
494         if (g_atomic_int_get(&block_notifications)) {
495                 _pending_xfade_adds.insert (_pending_xfade_adds.end(), x);
496         } else {
497                 NewCrossfade (x); /* EMIT SIGNAL */
498         }
499 }
500
501 void
502 AudioPlaylist::crossfade_invalidated (Crossfade* xfade)
503 {
504         Crossfades::iterator i;
505
506         xfade->in()->resume_fade_in ();
507         xfade->out()->resume_fade_out ();
508
509         if ((i = find (_crossfades.begin(), _crossfades.end(), xfade)) != _crossfades.end()) {
510                 _crossfades.erase (i);
511         }
512 }
513
514 int
515 AudioPlaylist::set_state (const XMLNode& node)
516 {
517         XMLNode *child;
518         XMLNodeList nlist;
519         XMLNodeConstIterator niter;
520
521         if (!in_set_state) {
522                 Playlist::set_state (node);
523         }
524
525         nlist = node.children();
526
527         for (niter = nlist.begin(); niter != nlist.end(); ++niter) {
528
529                 child = *niter;
530
531                 if (child->name() == "Crossfade") {
532
533                         Crossfade *xfade;
534                         
535                         try {
536                                 xfade = new Crossfade (*((const Playlist *)this), *child);
537                         }
538
539                         catch (failed_constructor& err) {
540                           //    cout << string_compose (_("could not create crossfade object in playlist %1"),
541                           //      _name) 
542                           //    << endl;
543                                 continue;
544                         }
545
546                         Crossfades::iterator ci;
547
548                         for (ci = _crossfades.begin(); ci != _crossfades.end(); ++ci) {
549                                 if (*(*ci) == *xfade) {
550                                         break;
551                                 }
552                         }
553
554                         if (ci == _crossfades.end()) {
555                                 _crossfades.push_back (xfade);
556                                 xfade->Invalidated.connect (mem_fun (*this, &AudioPlaylist::crossfade_invalidated));
557                                 xfade->StateChanged.connect (mem_fun (*this, &AudioPlaylist::crossfade_changed));
558                                 NewCrossfade(xfade);
559                         } else {
560                                 delete xfade;
561                         }
562                 }
563
564         }
565
566         return 0;
567 }
568
569 void
570 AudioPlaylist::drop_all_states ()
571 {
572         set<Crossfade*> all_xfades;
573         set<boost::shared_ptr<Region> > all_regions;
574
575         /* find every region we've ever used, and add it to the set of 
576            all regions. same for xfades;
577         */
578
579         for (StateMap::iterator i = states.begin(); i != states.end(); ++i) {
580                 
581                 AudioPlaylist::State* apstate = dynamic_cast<AudioPlaylist::State*> (*i);
582
583                 for (RegionList::iterator r = apstate->regions.begin(); r != apstate->regions.end(); ++r) {
584                         all_regions.insert (*r);
585                 }
586
587                 for (Crossfades::iterator xf = apstate->crossfades.begin(); xf != apstate->crossfades.end(); ++xf) {
588                         all_xfades.insert (*xf);
589                 }
590         }
591
592         /* now remove from the "all" lists every region that is in the current list. */
593
594         for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
595                 set<boost::shared_ptr<Region> >::iterator x = all_regions.find (*i);
596                 if (x != all_regions.end()) {
597                         all_regions.erase (x);
598                 }
599         }
600
601         /* ditto for every crossfade */
602
603         for (list<Crossfade*>::iterator i = _crossfades.begin(); i != _crossfades.end(); ++i) {
604                 set<Crossfade*>::iterator x = all_xfades.find (*i);
605                 if (x != all_xfades.end()) {
606                         all_xfades.erase (x);
607                 }
608         }
609
610         /* delete every crossfade that is left (ditto as per regions) */
611
612         for (set<Crossfade *>::iterator axf = all_xfades.begin(); axf != all_xfades.end(); ++axf) {
613                 delete *axf;
614         }
615
616         /* Now do the generic thing ... */
617
618         StateManager::drop_all_states ();
619 }
620
621 StateManager::State*
622 AudioPlaylist::state_factory (std::string why) const
623 {
624         State* state = new State (why);
625
626         state->regions = regions;
627         state->region_states.clear ();
628         for (RegionList::const_iterator i = regions.begin(); i != regions.end(); ++i) {
629                 state->region_states.push_back ((*i)->get_memento());
630         }
631
632         state->crossfades = _crossfades;
633         state->crossfade_states.clear ();
634         for (Crossfades::const_iterator i = _crossfades.begin(); i != _crossfades.end(); ++i) {
635                 state->crossfade_states.push_back ((*i)->get_memento());
636         }
637         return state;
638 }
639
640 Change
641 AudioPlaylist::restore_state (StateManager::State& state)
642
643         { 
644                 RegionLock rlock (this);
645                 State* apstate = dynamic_cast<State*> (&state);
646
647                 in_set_state = true;
648
649                 regions = apstate->regions;
650
651                 for (list<UndoAction>::iterator s = apstate->region_states.begin(); s != apstate->region_states.end(); ++s) {
652                         (*s) ();
653                 }
654
655                 _crossfades = apstate->crossfades;
656                 
657                 for (list<UndoAction>::iterator s = apstate->crossfade_states.begin(); s != apstate->crossfade_states.end(); ++s) {
658                         (*s) ();
659                 }
660
661                 in_set_state = false;
662         }
663
664         notify_length_changed ();
665         return Change (~0);
666 }
667
668 UndoAction
669 AudioPlaylist::get_memento () const
670 {
671         return sigc::bind (mem_fun (*(const_cast<AudioPlaylist*> (this)), &StateManager::use_state), _current_state_id);
672 }
673
674 void
675 AudioPlaylist::clear (bool with_save)
676 {
677         _crossfades.clear ();
678         
679         Playlist::clear (with_save);
680 }
681
682 XMLNode&
683 AudioPlaylist::state (bool full_state)
684 {
685         XMLNode& node = Playlist::state (full_state);
686
687         if (full_state) {
688                 for (Crossfades::iterator i = _crossfades.begin(); i != _crossfades.end(); ++i) {
689                         node.add_child_nocopy ((*i)->get_state());
690                 }
691         }
692         
693         return node;
694 }
695
696 void
697 AudioPlaylist::dump () const
698 {
699         boost::shared_ptr<Region>r;
700         Crossfade *x;
701
702         cerr << "Playlist \"" << _name << "\" " << endl
703              << regions.size() << " regions "
704              << _crossfades.size() << " crossfades"
705              << endl;
706
707         for (RegionList::const_iterator i = regions.begin(); i != regions.end(); ++i) {
708                 r = *i;
709                 cerr << "  " << r->name() << " @ " << r << " [" 
710                      << r->start() << "+" << r->length() 
711                      << "] at " 
712                      << r->position()
713                      << " on layer "
714                      << r->layer ()
715                      << endl;
716         }
717
718         for (Crossfades::const_iterator i = _crossfades.begin(); i != _crossfades.end(); ++i) {
719                 x = *i;
720                 cerr << "  xfade [" 
721                      << x->out()->name()
722                      << ','
723                      << x->in()->name()
724                      << " @ "
725                      << x->position()
726                      << " length = " 
727                      << x->length ()
728                      << " active ? "
729                      << (x->active() ? "yes" : "no")
730                      << endl;
731         }
732 }
733
734 bool
735 AudioPlaylist::destroy_region (boost::shared_ptr<Region> region)
736 {
737         boost::shared_ptr<AudioRegion> r = boost::dynamic_pointer_cast<AudioRegion> (region);
738         bool changed = false;
739         Crossfades::iterator c, ctmp;
740         set<Crossfade*> unique_xfades;
741
742         if (r == 0) {
743                 fatal << _("programming error: non-audio Region passed to remove_overlap in audio playlist")
744                       << endmsg;
745                 /*NOTREACHED*/
746                 return false;
747         }
748
749         { 
750                 RegionLock rlock (this);
751                 RegionList::iterator i;
752                 RegionList::iterator tmp;
753
754                 for (i = regions.begin(); i != regions.end(); ) {
755                         
756                         tmp = i;
757                         ++tmp;
758                         
759                         if ((*i) == region) {
760                                 regions.erase (i);
761                                 changed = true;
762                         }
763                         
764                         i = tmp;
765                 }
766         }
767
768         for (c = _crossfades.begin(); c != _crossfades.end(); ) {
769                 ctmp = c;
770                 ++ctmp;
771
772                 if ((*c)->involves (r)) {
773                         unique_xfades.insert (*c);
774                         _crossfades.erase (c);
775                 }
776                 
777                 c = ctmp;
778         }
779
780         for (StateMap::iterator s = states.begin(); s != states.end(); ) {
781                 StateMap::iterator tmp;
782
783                 tmp = s;
784                 ++tmp;
785
786                 State* astate = dynamic_cast<State*> (*s);
787                 
788                 for (c = astate->crossfades.begin(); c != astate->crossfades.end(); ) {
789
790                         ctmp = c;
791                         ++ctmp;
792
793                         if ((*c)->involves (r)) {
794                                 unique_xfades.insert (*c);
795                                 _crossfades.erase (c);
796                         }
797
798                         c = ctmp;
799                 }
800
801                 list<UndoAction>::iterator rsi, rsitmp;
802                 RegionList::iterator ri, ritmp;
803
804                 for (ri = astate->regions.begin(), rsi = astate->region_states.begin(); 
805                      ri != astate->regions.end() && rsi != astate->region_states.end();) {
806
807
808                         ritmp = ri;
809                         ++ritmp;
810
811                         rsitmp = rsi; 
812                         ++rsitmp;
813
814                         if (region == (*ri)) {
815                                 astate->regions.erase (ri);
816                                 astate->region_states.erase (rsi);
817                         }
818
819                         ri = ritmp;
820                         rsi = rsitmp;
821                 }
822                 
823                 s = tmp;
824         }
825
826         for (set<Crossfade*>::iterator c = unique_xfades.begin(); c != unique_xfades.end(); ++c) {
827                 delete *c;
828         }
829
830         if (changed) {
831                 /* overload this, it normally means "removed", not destroyed */
832                 notify_region_removed (region);
833         }
834
835         return changed;
836 }
837
838 void
839 AudioPlaylist::crossfade_changed (Change ignored)
840 {
841         if (in_flush || in_set_state) {
842                 return;
843         }
844
845         /* XXX is there a loop here? can an xfade change not happen
846            due to a playlist change? well, sure activation would
847            be an example. maybe we should check the type of change
848            that occured.
849         */
850
851         maybe_save_state (_("xfade change"));
852
853         notify_modified ();
854 }
855
856 bool
857 AudioPlaylist::region_changed (Change what_changed, boost::shared_ptr<Region> region)
858 {
859         if (in_flush || in_set_state) {
860                 return false;
861         }
862
863         Change our_interests = Change (AudioRegion::FadeInChanged|
864                                        AudioRegion::FadeOutChanged|
865                                        AudioRegion::FadeInActiveChanged|
866                                        AudioRegion::FadeOutActiveChanged|
867                                        AudioRegion::EnvelopeActiveChanged|
868                                        AudioRegion::ScaleAmplitudeChanged|
869                                        AudioRegion::EnvelopeChanged);
870         bool parent_wants_notify;
871
872         parent_wants_notify = Playlist::region_changed (what_changed, region);
873
874         maybe_save_state (_("region modified"));
875
876         if ((parent_wants_notify || (what_changed & our_interests))) {
877                 notify_modified ();
878         }
879
880         return true; 
881 }
882
883 void
884 AudioPlaylist::crossfades_at (nframes_t frame, Crossfades& clist)
885 {
886         RegionLock rlock (this);
887
888         for (Crossfades::iterator i = _crossfades.begin(); i != _crossfades.end(); ++i) {
889                 nframes_t start, end;
890
891                 start = (*i)->position();
892                 end = start + (*i)->overlap_length(); // not length(), important difference
893
894                 if (frame >= start && frame <= end) {
895                         clist.push_back (*i);
896                 } 
897         }
898 }
899