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