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