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