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