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