use shared_ptr<> for all region handling
[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, jack_nframes_t start, jack_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 jack_nframes_t
166 AudioPlaylist::read (Sample *buf, Sample *mixdown_buffer, float *gain_buffer, jack_nframes_t start,
167                      jack_nframes_t cnt, unsigned chan_n)
168 {
169         jack_nframes_t ret = cnt;
170         jack_nframes_t end;
171         jack_nframes_t read_frames;
172         jack_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                         
208                         relevant_regions[(*i)->layer()].push_back (*i);
209                         relevant_layers.push_back ((*i)->layer());
210                 }
211         }
212
213         for (Crossfades::iterator i = _crossfades.begin(); i != _crossfades.end(); ++i) {
214                 if ((*i)->coverage (start, end) != OverlapNone) {
215                         relevant_xfades[(*i)->upper_layer()].push_back (*i);
216                 }
217         }
218
219 //      RegionSortByLayer layer_cmp;
220 //      relevant_regions.sort (layer_cmp);
221
222         /* XXX this whole per-layer approach is a hack that
223            should be removed once Crossfades become
224            CrossfadeRegions and we just grab a list of relevant
225            regions and call read_at() on all of them.
226         */
227
228         sort (relevant_layers.begin(), relevant_layers.end());
229
230         for (vector<uint32_t>::iterator l = relevant_layers.begin(); l != relevant_layers.end(); ++l) {
231
232                 vector<boost::shared_ptr<Region> > r (relevant_regions[*l]);
233                 vector<Crossfade*>& x (relevant_xfades[*l]);
234
235                 for (vector<boost::shared_ptr<Region> >::iterator i = r.begin(); i != r.end(); ++i) {
236                         boost::shared_ptr<AudioRegion> ar = boost::dynamic_pointer_cast<AudioRegion>(*i);
237                         assert(ar);
238                         ar->read_at (buf, mixdown_buffer, gain_buffer, start, cnt, chan_n, read_frames, skip_frames);
239                         _read_data_count += ar->read_data_count();
240                 }
241                 
242                 for (vector<Crossfade*>::iterator i = x.begin(); i != x.end(); ++i) {
243                         (*i)->read_at (buf, mixdown_buffer, gain_buffer, start, cnt, chan_n);
244
245                         /* don't JACK up _read_data_count, since its the same data as we just
246                            read from the regions, and the OS should handle that for us.
247                         */
248                 }
249         }
250
251         return ret;
252 }
253
254
255 void
256 AudioPlaylist::remove_dependents (boost::shared_ptr<Region> region)
257 {
258         Crossfades::iterator i, tmp;
259         boost::shared_ptr<AudioRegion> r = boost::dynamic_pointer_cast<AudioRegion> (region);
260         
261         if (r == 0) {
262                 fatal << _("programming error: non-audio Region passed to remove_overlap in audio playlist")
263                       << endmsg;
264                 return;
265         }
266
267         for (i = _crossfades.begin(); i != _crossfades.end(); ) {
268                 tmp = i;
269                 tmp++;
270
271                 if ((*i)->involves (r)) {
272                         /* do not delete crossfades */
273                         _crossfades.erase (i);
274                 }
275                 
276                 i = tmp;
277         }
278 }
279
280
281 void
282 AudioPlaylist::flush_notifications ()
283 {
284         Playlist::flush_notifications();
285
286         if (in_flush) {
287                 return;
288         }
289
290         in_flush = true;
291
292         Crossfades::iterator a;
293         for (a = _pending_xfade_adds.begin(); a != _pending_xfade_adds.end(); ++a) {
294                 NewCrossfade (*a); /* EMIT SIGNAL */
295         }
296
297         _pending_xfade_adds.clear ();
298         
299         in_flush = false;
300 }
301
302 void
303 AudioPlaylist::refresh_dependents (boost::shared_ptr<Region> r)
304 {
305         boost::shared_ptr<AudioRegion> ar = boost::dynamic_pointer_cast<AudioRegion>(r);
306         set<Crossfade*> updated;
307
308         if (ar == 0) {
309                 return;
310         }
311
312         for (Crossfades::iterator x = _crossfades.begin(); x != _crossfades.end();) {
313
314                 Crossfades::iterator tmp;
315                 
316                 tmp = x;
317                 ++tmp;
318
319                 /* only update them once */
320
321                 if ((*x)->involves (ar)) {
322
323                         if (find (updated.begin(), updated.end(), *x) == updated.end()) {
324                                 if ((*x)->refresh ()) {
325                                         /* not invalidated by the refresh */
326                                         updated.insert (*x);
327                                 }
328                         }
329                 }
330
331                 x = tmp;
332         }
333 }
334
335 void
336 AudioPlaylist::finalize_split_region (boost::shared_ptr<Region> o, boost::shared_ptr<Region> l, boost::shared_ptr<Region> r)
337 {
338         boost::shared_ptr<AudioRegion> orig  = boost::dynamic_pointer_cast<AudioRegion>(o);
339         boost::shared_ptr<AudioRegion> left  = boost::dynamic_pointer_cast<AudioRegion>(l);
340         boost::shared_ptr<AudioRegion> right = boost::dynamic_pointer_cast<AudioRegion>(r);
341
342         for (Crossfades::iterator x = _crossfades.begin(); x != _crossfades.end();) {
343                 Crossfades::iterator tmp;
344                 tmp = x;
345                 ++tmp;
346
347                 Crossfade *fade = 0;
348                 
349                 if ((*x)->_in == orig) {
350                         if (! (*x)->covers(right->position())) {
351                                 fade = new Crossfade (**x, left, (*x)->_out);
352                         } else {
353                                 // Overlap, the crossfade is copied on the left side of the right region instead
354                                 fade = new Crossfade (**x, right, (*x)->_out);
355                         }
356                 }
357                 
358                 if ((*x)->_out == orig) {
359                         if (! (*x)->covers(right->position())) {
360                                 fade = new Crossfade (**x, (*x)->_in, right);
361                         } else {
362                                 // Overlap, the crossfade is copied on the right side of the left region instead
363                                 fade = new Crossfade (**x, (*x)->_in, left);
364                         }
365                 }
366                 
367                 if (fade) {
368                         _crossfades.remove (*x);
369                         add_crossfade (*fade);
370                 }
371                 x = tmp;
372         }
373 }
374
375 void
376 AudioPlaylist::check_dependents (boost::shared_ptr<Region> r, bool norefresh)
377 {
378         boost::shared_ptr<AudioRegion> other;
379         boost::shared_ptr<AudioRegion> region;
380         boost::shared_ptr<AudioRegion> top;
381         boost::shared_ptr<AudioRegion> bottom;
382         Crossfade*   xfade;
383
384         if (in_set_state || in_partition) {
385                 return;
386         }
387
388         if ((region = boost::dynamic_pointer_cast<AudioRegion> (r)) == 0) {
389                 fatal << _("programming error: non-audio Region tested for overlap in audio playlist")
390                       << endmsg;
391                 return;
392         }
393
394         if (!norefresh) {
395                 refresh_dependents (r);
396         }
397
398         if (!Config->get_auto_xfade()) {
399                 return;
400         }
401
402         for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
403
404                 other = boost::dynamic_pointer_cast<AudioRegion> (*i);
405
406                 if (other == region) {
407                         continue;
408                 }
409
410                 if (other->muted() || region->muted()) {
411                         continue;
412                 }
413
414                 if (other->layer() < region->layer()) {
415                         top = region;
416                         bottom = other;
417                 } else {
418                         top = other;
419                         bottom = region;
420                 }
421
422                 try {
423                                 
424                         if (top->coverage (bottom->position(), bottom->last_frame()) != OverlapNone) {
425                                 
426                                 /* check if the upper region is within the lower region */
427                                 
428                                 if (top->first_frame() > bottom->first_frame() &&
429                                     top->last_frame() < bottom->last_frame()) {
430                                         
431                                         
432                                         /*     [ -------- top ------- ]
433                                          * {=========== bottom =============}
434                                          */
435                                         
436                                         /* to avoid discontinuities at the region boundaries of an internal
437                                            overlap (this region is completely within another), we create
438                                            two hidden crossfades at each boundary. this is not dependent
439                                            on the auto-xfade option, because we require it as basic
440                                            audio engineering.
441                                         */
442                                         
443                                         jack_nframes_t xfade_length = min ((jack_nframes_t) 720, top->length());
444                                         
445                                                             /*  in,      out */
446                                         xfade = new Crossfade (top, bottom, xfade_length, top->first_frame(), StartOfIn);
447                                         add_crossfade (*xfade);
448                                         xfade = new Crossfade (bottom, top, xfade_length, top->last_frame() - xfade_length, EndOfOut);
449                                         add_crossfade (*xfade);
450                                         
451                                 } else {
452                 
453                                         xfade = new Crossfade (other, region, _session.get_xfade_model(), _session.get_crossfades_active());
454                                         add_crossfade (*xfade);
455                                 }
456                         } 
457                 }
458                 
459                 catch (failed_constructor& err) {
460                         continue;
461                 }
462                 
463                 catch (Crossfade::NoCrossfadeHere& err) {
464                         continue;
465                 }
466                 
467         }
468 }
469
470 void
471 AudioPlaylist::add_crossfade (Crossfade& xfade)
472 {
473         Crossfades::iterator ci;
474
475         for (ci = _crossfades.begin(); ci != _crossfades.end(); ++ci) {
476                 if (*(*ci) == xfade) { // Crossfade::operator==()
477                         break;
478                 }
479         }
480         
481         if (ci != _crossfades.end()) {
482                 delete &xfade;
483         } else {
484                 _crossfades.push_back (&xfade);
485
486                 xfade.Invalidated.connect (mem_fun (*this, &AudioPlaylist::crossfade_invalidated));
487                 xfade.StateChanged.connect (mem_fun (*this, &AudioPlaylist::crossfade_changed));
488
489                 notify_crossfade_added (&xfade);
490         }
491 }
492         
493 void AudioPlaylist::notify_crossfade_added (Crossfade *x)
494 {
495         if (g_atomic_int_get(&block_notifications)) {
496                 _pending_xfade_adds.insert (_pending_xfade_adds.end(), x);
497         } else {
498                 NewCrossfade (x); /* EMIT SIGNAL */
499         }
500 }
501
502 void
503 AudioPlaylist::crossfade_invalidated (Crossfade* xfade)
504 {
505         Crossfades::iterator i;
506
507         xfade->in()->resume_fade_in ();
508         xfade->out()->resume_fade_out ();
509
510         if ((i = find (_crossfades.begin(), _crossfades.end(), xfade)) != _crossfades.end()) {
511                 _crossfades.erase (i);
512         }
513 }
514
515 int
516 AudioPlaylist::set_state (const XMLNode& node)
517 {
518         XMLNode *child;
519         XMLNodeList nlist;
520         XMLNodeConstIterator niter;
521
522         if (!in_set_state) {
523                 Playlist::set_state (node);
524         }
525
526         nlist = node.children();
527
528         for (niter = nlist.begin(); niter != nlist.end(); ++niter) {
529
530                 child = *niter;
531
532                 if (child->name() == "Crossfade") {
533
534                         Crossfade *xfade;
535                         
536                         try {
537                                 xfade = new Crossfade (*((const Playlist *)this), *child);
538                         }
539
540                         catch (failed_constructor& err) {
541                           //    cout << string_compose (_("could not create crossfade object in playlist %1"),
542                           //      _name) 
543                           //    << endl;
544                                 continue;
545                         }
546
547                         Crossfades::iterator ci;
548
549                         for (ci = _crossfades.begin(); ci != _crossfades.end(); ++ci) {
550                                 if (*(*ci) == *xfade) {
551                                         break;
552                                 }
553                         }
554
555                         if (ci == _crossfades.end()) {
556                                 _crossfades.push_back (xfade);
557                                 xfade->Invalidated.connect (mem_fun (*this, &AudioPlaylist::crossfade_invalidated));
558                                 xfade->StateChanged.connect (mem_fun (*this, &AudioPlaylist::crossfade_changed));
559                                 /* no need to notify here */
560                         } else {
561                                 delete xfade;
562                         }
563                 }
564
565         }
566
567         return 0;
568 }
569
570 void
571 AudioPlaylist::drop_all_states ()
572 {
573         set<Crossfade*> all_xfades;
574         set<boost::shared_ptr<Region> > all_regions;
575
576         /* find every region we've ever used, and add it to the set of 
577            all regions. same for xfades;
578         */
579
580         for (StateMap::iterator i = states.begin(); i != states.end(); ++i) {
581                 
582                 AudioPlaylist::State* apstate = dynamic_cast<AudioPlaylist::State*> (*i);
583
584                 for (RegionList::iterator r = apstate->regions.begin(); r != apstate->regions.end(); ++r) {
585                         all_regions.insert (*r);
586                 }
587
588                 for (Crossfades::iterator xf = apstate->crossfades.begin(); xf != apstate->crossfades.end(); ++xf) {
589                         all_xfades.insert (*xf);
590                 }
591         }
592
593         /* now remove from the "all" lists every region that is in the current list. */
594
595         for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
596                 set<boost::shared_ptr<Region> >::iterator x = all_regions.find (*i);
597                 if (x != all_regions.end()) {
598                         all_regions.erase (x);
599                 }
600         }
601
602         /* ditto for every crossfade */
603
604         for (list<Crossfade*>::iterator i = _crossfades.begin(); i != _crossfades.end(); ++i) {
605                 set<Crossfade*>::iterator x = all_xfades.find (*i);
606                 if (x != all_xfades.end()) {
607                         all_xfades.erase (x);
608                 }
609         }
610
611         /* delete every region that is left - these are all things that are part of our "history" */
612
613         for (set<boost::shared_ptr<Region> >::iterator ar = all_regions.begin(); ar != all_regions.end(); ++ar) {
614                 (*ar)->unlock_sources ();
615         }
616
617         /* delete every crossfade that is left (ditto as per regions) */
618
619         for (set<Crossfade *>::iterator axf = all_xfades.begin(); axf != all_xfades.end(); ++axf) {
620                 delete *axf;
621         }
622
623         /* Now do the generic thing ... */
624
625         StateManager::drop_all_states ();
626 }
627
628 StateManager::State*
629 AudioPlaylist::state_factory (std::string why) const
630 {
631         State* state = new State (why);
632
633         state->regions = regions;
634         state->region_states.clear ();
635         for (RegionList::const_iterator i = regions.begin(); i != regions.end(); ++i) {
636                 state->region_states.push_back ((*i)->get_memento());
637         }
638
639         state->crossfades = _crossfades;
640         state->crossfade_states.clear ();
641         for (Crossfades::const_iterator i = _crossfades.begin(); i != _crossfades.end(); ++i) {
642                 state->crossfade_states.push_back ((*i)->get_memento());
643         }
644         return state;
645 }
646
647 Change
648 AudioPlaylist::restore_state (StateManager::State& state)
649
650         { 
651                 RegionLock rlock (this);
652                 State* apstate = dynamic_cast<State*> (&state);
653
654                 in_set_state = true;
655
656                 regions = apstate->regions;
657
658                 for (list<UndoAction>::iterator s = apstate->region_states.begin(); s != apstate->region_states.end(); ++s) {
659                         (*s) ();
660                 }
661
662                 _crossfades = apstate->crossfades;
663                 
664                 for (list<UndoAction>::iterator s = apstate->crossfade_states.begin(); s != apstate->crossfade_states.end(); ++s) {
665                         (*s) ();
666                 }
667
668                 in_set_state = false;
669         }
670
671         notify_length_changed ();
672         return Change (~0);
673 }
674
675 UndoAction
676 AudioPlaylist::get_memento () const
677 {
678         return sigc::bind (mem_fun (*(const_cast<AudioPlaylist*> (this)), &StateManager::use_state), _current_state_id);
679 }
680
681 void
682 AudioPlaylist::clear (bool with_save)
683 {
684         _crossfades.clear ();
685         
686         Playlist::clear (with_save);
687 }
688
689 XMLNode&
690 AudioPlaylist::state (bool full_state)
691 {
692         XMLNode& node = Playlist::state (full_state);
693
694         if (full_state) {
695                 for (Crossfades::iterator i = _crossfades.begin(); i != _crossfades.end(); ++i) {
696                         node.add_child_nocopy ((*i)->get_state());
697                 }
698         }
699         
700         return node;
701 }
702
703 void
704 AudioPlaylist::dump () const
705 {
706         boost::shared_ptr<Region>r;
707         Crossfade *x;
708
709         cerr << "Playlist \"" << _name << "\" " << endl
710              << regions.size() << " regions "
711              << _crossfades.size() << " crossfades"
712              << endl;
713
714         for (RegionList::const_iterator i = regions.begin(); i != regions.end(); ++i) {
715                 r = *i;
716                 cerr << "  " << r->name() << " @ " << r << " [" 
717                      << r->start() << "+" << r->length() 
718                      << "] at " 
719                      << r->position()
720                      << " on layer "
721                      << r->layer ()
722                      << endl;
723         }
724
725         for (Crossfades::const_iterator i = _crossfades.begin(); i != _crossfades.end(); ++i) {
726                 x = *i;
727                 cerr << "  xfade [" 
728                      << x->out()->name()
729                      << ','
730                      << x->in()->name()
731                      << " @ "
732                      << x->position()
733                      << " length = " 
734                      << x->length ()
735                      << " active ? "
736                      << (x->active() ? "yes" : "no")
737                      << endl;
738         }
739 }
740
741 bool
742 AudioPlaylist::destroy_region (boost::shared_ptr<Region> region)
743 {
744         boost::shared_ptr<AudioRegion> r = boost::dynamic_pointer_cast<AudioRegion> (region);
745         bool changed = false;
746         Crossfades::iterator c, ctmp;
747         set<Crossfade*> unique_xfades;
748
749         if (r == 0) {
750                 fatal << _("programming error: non-audio Region passed to remove_overlap in audio playlist")
751                       << endmsg;
752                 /*NOTREACHED*/
753                 return false;
754         }
755
756         { 
757                 RegionLock rlock (this);
758                 RegionList::iterator i;
759                 RegionList::iterator tmp;
760
761                 for (i = regions.begin(); i != regions.end(); ) {
762                         
763                         tmp = i;
764                         ++tmp;
765                         
766                         if ((*i) == region) {
767                                 (*i)->unlock_sources ();
768                                 regions.erase (i);
769                                 changed = true;
770                         }
771                         
772                         i = tmp;
773                 }
774         }
775
776         for (c = _crossfades.begin(); c != _crossfades.end(); ) {
777                 ctmp = c;
778                 ++ctmp;
779
780                 if ((*c)->involves (r)) {
781                         unique_xfades.insert (*c);
782                         _crossfades.erase (c);
783                 }
784                 
785                 c = ctmp;
786         }
787
788         for (StateMap::iterator s = states.begin(); s != states.end(); ) {
789                 StateMap::iterator tmp;
790
791                 tmp = s;
792                 ++tmp;
793
794                 State* astate = dynamic_cast<State*> (*s);
795                 
796                 for (c = astate->crossfades.begin(); c != astate->crossfades.end(); ) {
797
798                         ctmp = c;
799                         ++ctmp;
800
801                         if ((*c)->involves (r)) {
802                                 unique_xfades.insert (*c);
803                                 _crossfades.erase (c);
804                         }
805
806                         c = ctmp;
807                 }
808
809                 list<UndoAction>::iterator rsi, rsitmp;
810                 RegionList::iterator ri, ritmp;
811
812                 for (ri = astate->regions.begin(), rsi = astate->region_states.begin(); 
813                      ri != astate->regions.end() && rsi != astate->region_states.end();) {
814
815
816                         ritmp = ri;
817                         ++ritmp;
818
819                         rsitmp = rsi; 
820                         ++rsitmp;
821
822                         if (region == (*ri)) {
823                                 astate->regions.erase (ri);
824                                 astate->region_states.erase (rsi);
825                         }
826
827                         ri = ritmp;
828                         rsi = rsitmp;
829                 }
830                 
831                 s = tmp;
832         }
833
834         for (set<Crossfade*>::iterator c = unique_xfades.begin(); c != unique_xfades.end(); ++c) {
835                 delete *c;
836         }
837
838         if (changed) {
839                 /* overload this, it normally means "removed", not destroyed */
840                 notify_region_removed (region);
841         }
842
843         return changed;
844 }
845
846 void
847 AudioPlaylist::crossfade_changed (Change ignored)
848 {
849         if (in_flush || in_set_state) {
850                 return;
851         }
852
853         /* XXX is there a loop here? can an xfade change not happen
854            due to a playlist change? well, sure activation would
855            be an example. maybe we should check the type of change
856            that occured.
857         */
858
859         maybe_save_state (_("xfade change"));
860
861         notify_modified ();
862 }
863
864 bool
865 AudioPlaylist::region_changed (Change what_changed, boost::shared_ptr<Region> region)
866 {
867         if (in_flush || in_set_state) {
868                 return false;
869         }
870
871         Change our_interests = Change (AudioRegion::FadeInChanged|
872                                        AudioRegion::FadeOutChanged|
873                                        AudioRegion::FadeInActiveChanged|
874                                        AudioRegion::FadeOutActiveChanged|
875                                        AudioRegion::EnvelopeActiveChanged|
876                                        AudioRegion::ScaleAmplitudeChanged|
877                                        AudioRegion::EnvelopeChanged);
878         bool parent_wants_notify;
879
880         parent_wants_notify = Playlist::region_changed (what_changed, region);
881
882         maybe_save_state (_("region modified"));
883
884         if ((parent_wants_notify || (what_changed & our_interests))) {
885                 notify_modified ();
886         }
887
888         return true; 
889 }
890
891 void
892 AudioPlaylist::crossfades_at (jack_nframes_t frame, Crossfades& clist)
893 {
894         RegionLock rlock (this);
895
896         for (Crossfades::iterator i = _crossfades.begin(); i != _crossfades.end(); ++i) {
897                 jack_nframes_t start, end;
898
899                 start = (*i)->position();
900                 end = start + (*i)->overlap_length(); // not length(), important difference
901
902                 if (frame >= start && frame <= end) {
903                         clist.push_back (*i);
904                 } 
905         }
906 }