59425fcf1d7797a2107a39865eecb396db9c1a7c
[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                                 NewCrossfade(xfade);
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 crossfade that is left (ditto as per regions) */
612
613         for (set<Crossfade *>::iterator axf = all_xfades.begin(); axf != all_xfades.end(); ++axf) {
614                 delete *axf;
615         }
616
617         /* Now do the generic thing ... */
618
619         StateManager::drop_all_states ();
620 }
621
622 StateManager::State*
623 AudioPlaylist::state_factory (std::string why) const
624 {
625         State* state = new State (why);
626
627         state->regions = regions;
628         state->region_states.clear ();
629         for (RegionList::const_iterator i = regions.begin(); i != regions.end(); ++i) {
630                 state->region_states.push_back ((*i)->get_memento());
631         }
632
633         state->crossfades = _crossfades;
634         state->crossfade_states.clear ();
635         for (Crossfades::const_iterator i = _crossfades.begin(); i != _crossfades.end(); ++i) {
636                 state->crossfade_states.push_back ((*i)->get_memento());
637         }
638         return state;
639 }
640
641 Change
642 AudioPlaylist::restore_state (StateManager::State& state)
643
644         { 
645                 RegionLock rlock (this);
646                 State* apstate = dynamic_cast<State*> (&state);
647
648                 in_set_state = true;
649
650                 regions = apstate->regions;
651
652                 for (list<UndoAction>::iterator s = apstate->region_states.begin(); s != apstate->region_states.end(); ++s) {
653                         (*s) ();
654                 }
655
656                 _crossfades = apstate->crossfades;
657                 
658                 for (list<UndoAction>::iterator s = apstate->crossfade_states.begin(); s != apstate->crossfade_states.end(); ++s) {
659                         (*s) ();
660                 }
661
662                 in_set_state = false;
663         }
664
665         notify_length_changed ();
666         return Change (~0);
667 }
668
669 UndoAction
670 AudioPlaylist::get_memento () const
671 {
672         return sigc::bind (mem_fun (*(const_cast<AudioPlaylist*> (this)), &StateManager::use_state), _current_state_id);
673 }
674
675 void
676 AudioPlaylist::clear (bool with_save)
677 {
678         _crossfades.clear ();
679         
680         Playlist::clear (with_save);
681 }
682
683 XMLNode&
684 AudioPlaylist::state (bool full_state)
685 {
686         XMLNode& node = Playlist::state (full_state);
687
688         if (full_state) {
689                 for (Crossfades::iterator i = _crossfades.begin(); i != _crossfades.end(); ++i) {
690                         node.add_child_nocopy ((*i)->get_state());
691                 }
692         }
693         
694         return node;
695 }
696
697 void
698 AudioPlaylist::dump () const
699 {
700         boost::shared_ptr<Region>r;
701         Crossfade *x;
702
703         cerr << "Playlist \"" << _name << "\" " << endl
704              << regions.size() << " regions "
705              << _crossfades.size() << " crossfades"
706              << endl;
707
708         for (RegionList::const_iterator i = regions.begin(); i != regions.end(); ++i) {
709                 r = *i;
710                 cerr << "  " << r->name() << " @ " << r << " [" 
711                      << r->start() << "+" << r->length() 
712                      << "] at " 
713                      << r->position()
714                      << " on layer "
715                      << r->layer ()
716                      << endl;
717         }
718
719         for (Crossfades::const_iterator i = _crossfades.begin(); i != _crossfades.end(); ++i) {
720                 x = *i;
721                 cerr << "  xfade [" 
722                      << x->out()->name()
723                      << ','
724                      << x->in()->name()
725                      << " @ "
726                      << x->position()
727                      << " length = " 
728                      << x->length ()
729                      << " active ? "
730                      << (x->active() ? "yes" : "no")
731                      << endl;
732         }
733 }
734
735 bool
736 AudioPlaylist::destroy_region (boost::shared_ptr<Region> region)
737 {
738         boost::shared_ptr<AudioRegion> r = boost::dynamic_pointer_cast<AudioRegion> (region);
739         bool changed = false;
740         Crossfades::iterator c, ctmp;
741         set<Crossfade*> unique_xfades;
742
743         if (r == 0) {
744                 fatal << _("programming error: non-audio Region passed to remove_overlap in audio playlist")
745                       << endmsg;
746                 /*NOTREACHED*/
747                 return false;
748         }
749
750         { 
751                 RegionLock rlock (this);
752                 RegionList::iterator i;
753                 RegionList::iterator tmp;
754
755                 for (i = regions.begin(); i != regions.end(); ) {
756                         
757                         tmp = i;
758                         ++tmp;
759                         
760                         if ((*i) == region) {
761                                 regions.erase (i);
762                                 changed = true;
763                         }
764                         
765                         i = tmp;
766                 }
767         }
768
769         for (c = _crossfades.begin(); c != _crossfades.end(); ) {
770                 ctmp = c;
771                 ++ctmp;
772
773                 if ((*c)->involves (r)) {
774                         unique_xfades.insert (*c);
775                         _crossfades.erase (c);
776                 }
777                 
778                 c = ctmp;
779         }
780
781         for (StateMap::iterator s = states.begin(); s != states.end(); ) {
782                 StateMap::iterator tmp;
783
784                 tmp = s;
785                 ++tmp;
786
787                 State* astate = dynamic_cast<State*> (*s);
788                 
789                 for (c = astate->crossfades.begin(); c != astate->crossfades.end(); ) {
790
791                         ctmp = c;
792                         ++ctmp;
793
794                         if ((*c)->involves (r)) {
795                                 unique_xfades.insert (*c);
796                                 _crossfades.erase (c);
797                         }
798
799                         c = ctmp;
800                 }
801
802                 list<UndoAction>::iterator rsi, rsitmp;
803                 RegionList::iterator ri, ritmp;
804
805                 for (ri = astate->regions.begin(), rsi = astate->region_states.begin(); 
806                      ri != astate->regions.end() && rsi != astate->region_states.end();) {
807
808
809                         ritmp = ri;
810                         ++ritmp;
811
812                         rsitmp = rsi; 
813                         ++rsitmp;
814
815                         if (region == (*ri)) {
816                                 astate->regions.erase (ri);
817                                 astate->region_states.erase (rsi);
818                         }
819
820                         ri = ritmp;
821                         rsi = rsitmp;
822                 }
823                 
824                 s = tmp;
825         }
826
827         for (set<Crossfade*>::iterator c = unique_xfades.begin(); c != unique_xfades.end(); ++c) {
828                 delete *c;
829         }
830
831         if (changed) {
832                 /* overload this, it normally means "removed", not destroyed */
833                 notify_region_removed (region);
834         }
835
836         return changed;
837 }
838
839 void
840 AudioPlaylist::crossfade_changed (Change ignored)
841 {
842         if (in_flush || in_set_state) {
843                 return;
844         }
845
846         /* XXX is there a loop here? can an xfade change not happen
847            due to a playlist change? well, sure activation would
848            be an example. maybe we should check the type of change
849            that occured.
850         */
851
852         maybe_save_state (_("xfade change"));
853
854         notify_modified ();
855 }
856
857 bool
858 AudioPlaylist::region_changed (Change what_changed, boost::shared_ptr<Region> region)
859 {
860         if (in_flush || in_set_state) {
861                 return false;
862         }
863
864         Change our_interests = Change (AudioRegion::FadeInChanged|
865                                        AudioRegion::FadeOutChanged|
866                                        AudioRegion::FadeInActiveChanged|
867                                        AudioRegion::FadeOutActiveChanged|
868                                        AudioRegion::EnvelopeActiveChanged|
869                                        AudioRegion::ScaleAmplitudeChanged|
870                                        AudioRegion::EnvelopeChanged);
871         bool parent_wants_notify;
872
873         parent_wants_notify = Playlist::region_changed (what_changed, region);
874
875         maybe_save_state (_("region modified"));
876
877         if ((parent_wants_notify || (what_changed & our_interests))) {
878                 notify_modified ();
879         }
880
881         return true; 
882 }
883
884 void
885 AudioPlaylist::crossfades_at (jack_nframes_t frame, Crossfades& clist)
886 {
887         RegionLock rlock (this);
888
889         for (Crossfades::iterator i = _crossfades.begin(); i != _crossfades.end(); ++i) {
890                 jack_nframes_t start, end;
891
892                 start = (*i)->position();
893                 end = start + (*i)->overlap_length(); // not length(), important difference
894
895                 if (frame >= start && frame <= end) {
896                         clist.push_back (*i);
897                 } 
898         }
899 }
900