use filechooser widget in export dialog, selected files set format combos, hide progr...
[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 */
19
20 #include <algorithm>
21
22 #include <cstdlib>
23
24 #include <sigc++/bind.h>
25
26 #include <ardour/types.h>
27 #include <ardour/configuration.h>
28 #include <ardour/audioplaylist.h>
29 #include <ardour/audioregion.h>
30 #include <ardour/crossfade.h>
31 #include <ardour/crossfade_compare.h>
32 #include <ardour/session.h>
33 #include <pbd/enumwriter.h>
34
35 #include "i18n.h"
36
37 using namespace ARDOUR;
38 using namespace sigc;
39 using namespace std;
40 using namespace PBD;
41
42 AudioPlaylist::AudioPlaylist (Session& session, const XMLNode& node, bool hidden)
43         : Playlist (session, node, DataType::AUDIO, hidden)
44 {
45         const XMLProperty* prop = node.property("type");
46         assert(!prop || DataType(prop->value()) == DataType::AUDIO);
47
48         in_set_state++;
49         set_state (node);
50         in_set_state--;
51 }
52
53 AudioPlaylist::AudioPlaylist (Session& session, string name, bool hidden)
54         : Playlist (session, name, DataType::AUDIO, hidden)
55 {
56 }
57
58 AudioPlaylist::AudioPlaylist (boost::shared_ptr<const AudioPlaylist> other, string name, bool hidden)
59         : Playlist (other, name, hidden)
60 {
61         RegionList::const_iterator in_o  = other->regions.begin();
62         RegionList::iterator in_n = regions.begin();
63
64         while (in_o != other->regions.end()) {
65                 boost::shared_ptr<AudioRegion> ar = boost::dynamic_pointer_cast<AudioRegion>(*in_o);
66
67                 // We look only for crossfades which begin with the current region, so we don't get doubles
68                 for (Crossfades::const_iterator xfades = other->_crossfades.begin(); xfades != other->_crossfades.end(); ++xfades) {
69                         if ((*xfades)->in() == ar) {
70                                 // We found one! Now copy it!
71
72                                 RegionList::const_iterator out_o = other->regions.begin();
73                                 RegionList::const_iterator out_n = regions.begin();
74
75                                 while (out_o != other->regions.end()) {
76                                         
77                                         boost::shared_ptr<AudioRegion>ar2 = boost::dynamic_pointer_cast<AudioRegion>(*out_o);
78                                         
79                                         if ((*xfades)->out() == ar2) {
80                                                 boost::shared_ptr<AudioRegion>in  = boost::dynamic_pointer_cast<AudioRegion>(*in_n);
81                                                 boost::shared_ptr<AudioRegion>out = boost::dynamic_pointer_cast<AudioRegion>(*out_n);
82                                                 boost::shared_ptr<Crossfade> new_fade = boost::shared_ptr<Crossfade> (new Crossfade (*xfades, in, out));
83                                                 add_crossfade(new_fade);
84                                                 break;
85                                         }
86                                         
87                                         out_o++;
88                                         out_n++;
89                                 }
90 //                              cerr << "HUH!? second region in the crossfade not found!" << endl;
91                         }
92                 }
93
94                 in_o++;
95                 in_n++;
96         }
97 }
98
99 AudioPlaylist::AudioPlaylist (boost::shared_ptr<const AudioPlaylist> other, nframes_t start, nframes_t cnt, string name, bool hidden)
100         : Playlist (other, start, cnt, name, hidden)
101 {
102         /* this constructor does NOT notify others (session) */
103 }
104
105 AudioPlaylist::~AudioPlaylist ()
106 {
107         GoingAway (); /* EMIT SIGNAL */
108
109         /* drop connections to signals */
110
111         notify_callbacks ();
112         
113         _crossfades.clear ();
114 }
115
116 struct RegionSortByLayer {
117     bool operator() (boost::shared_ptr<Region> a, boost::shared_ptr<Region> b) {
118             return a->layer() < b->layer();
119     }
120 };
121
122 ARDOUR::nframes_t
123 AudioPlaylist::read (Sample *buf, Sample *mixdown_buffer, float *gain_buffer, nframes_t start,
124                      nframes_t cnt, unsigned chan_n)
125 {
126         nframes_t end;
127
128         /* optimizing this memset() away involves a lot of conditionals
129            that may well cause more of a hit due to cache misses 
130            and related stuff than just doing this here.
131            
132            it would be great if someone could measure this
133            at some point.
134
135            one way or another, parts of the requested area
136            that are not written to by Region::region_at()
137            for all Regions that cover the area need to be
138            zeroed.
139         */
140
141         memset (buf, 0, sizeof (Sample) * cnt);
142
143         /* this function is never called from a realtime thread, so 
144            its OK to block (for short intervals).
145         */
146
147         Glib::Mutex::Lock rm (region_lock);
148
149         end =  start + cnt - 1;
150
151         _read_data_count = 0;
152
153         map<uint32_t,vector<boost::shared_ptr<Region> > > relevant_regions;
154         map<uint32_t,vector<boost::shared_ptr<Crossfade> > > relevant_xfades;
155         vector<uint32_t> relevant_layers;
156
157         for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
158                 if ((*i)->coverage (start, end) != OverlapNone) {
159                         relevant_regions[(*i)->layer()].push_back (*i);
160                         relevant_layers.push_back ((*i)->layer());
161                 }
162         }
163
164         for (Crossfades::iterator i = _crossfades.begin(); i != _crossfades.end(); ++i) {
165                 if ((*i)->coverage (start, end) != OverlapNone) {
166                         relevant_xfades[(*i)->upper_layer()].push_back (*i);
167                 }
168         }
169
170 //      RegionSortByLayer layer_cmp;
171 //      relevant_regions.sort (layer_cmp);
172
173         /* XXX this whole per-layer approach is a hack that
174            should be removed once Crossfades become
175            CrossfadeRegions and we just grab a list of relevant
176            regions and call read_at() on all of them.
177         */
178
179         sort (relevant_layers.begin(), relevant_layers.end());
180
181         for (vector<uint32_t>::iterator l = relevant_layers.begin(); l != relevant_layers.end(); ++l) {
182
183                 vector<boost::shared_ptr<Region> > r (relevant_regions[*l]);
184                 vector<boost::shared_ptr<Crossfade> >& x (relevant_xfades[*l]);
185
186                 for (vector<boost::shared_ptr<Region> >::iterator i = r.begin(); i != r.end(); ++i) {
187                         boost::shared_ptr<AudioRegion> ar = boost::dynamic_pointer_cast<AudioRegion>(*i);
188                         assert(ar);
189                         ar->read_at (buf, mixdown_buffer, gain_buffer, start, cnt, chan_n);
190                         _read_data_count += ar->read_data_count();
191                 }
192                 
193                 for (vector<boost::shared_ptr<Crossfade> >::iterator i = x.begin(); i != x.end(); ++i) {
194                         (*i)->read_at (buf, mixdown_buffer, gain_buffer, start, cnt, chan_n);
195
196                         /* don't JACK up _read_data_count, since its the same data as we just
197                            read from the regions, and the OS should handle that for us.
198                         */
199                 }
200         }
201
202         return cnt;
203 }
204
205
206 void
207 AudioPlaylist::remove_dependents (boost::shared_ptr<Region> region)
208 {
209         boost::shared_ptr<AudioRegion> r = boost::dynamic_pointer_cast<AudioRegion> (region);
210
211         if (in_set_state) {
212                 return;
213         }
214         
215         if (r == 0) {
216                 fatal << _("programming error: non-audio Region passed to remove_overlap in audio playlist")
217                       << endmsg;
218                 return;
219         }
220
221         for (Crossfades::iterator i = _crossfades.begin(); i != _crossfades.end(); ) {
222                 
223                 if ((*i)->involves (r)) {
224                         i = _crossfades.erase (i);
225                 } else {
226                         ++i;
227                 }
228         }
229 }
230
231
232 void
233 AudioPlaylist::flush_notifications ()
234 {
235         Playlist::flush_notifications();
236
237         if (in_flush) {
238                 return;
239         }
240
241         in_flush = true;
242
243         Crossfades::iterator a;
244         for (a = _pending_xfade_adds.begin(); a != _pending_xfade_adds.end(); ++a) {
245                 NewCrossfade (*a); /* EMIT SIGNAL */
246         }
247
248         _pending_xfade_adds.clear ();
249         
250         in_flush = false;
251 }
252
253 void
254 AudioPlaylist::refresh_dependents (boost::shared_ptr<Region> r)
255 {
256         boost::shared_ptr<AudioRegion> ar = boost::dynamic_pointer_cast<AudioRegion>(r);
257         set<boost::shared_ptr<Crossfade> > updated;
258
259         if (ar == 0) {
260                 return;
261         }
262
263         for (Crossfades::iterator x = _crossfades.begin(); x != _crossfades.end();) {
264
265                 Crossfades::iterator tmp;
266                 
267                 tmp = x;
268                 ++tmp;
269
270                 /* only update them once */
271
272                 if ((*x)->involves (ar)) {
273
274                         pair<set<boost::shared_ptr<Crossfade> >::iterator, bool> const u = updated.insert (*x);
275                         
276                         if (u.second) {
277                                 /* x was successfully inserted into the set, so it has not already been updated */
278                                 try {
279                                         (*x)->refresh ();
280                                 }
281
282                                 catch (Crossfade::NoCrossfadeHere& err) {
283                                         // relax, Invalidated during refresh
284                                 }
285                         }
286                 }
287
288                 x = tmp;
289         }
290 }
291
292 void
293 AudioPlaylist::finalize_split_region (boost::shared_ptr<Region> o, boost::shared_ptr<Region> l, boost::shared_ptr<Region> r)
294 {
295         boost::shared_ptr<AudioRegion> orig  = boost::dynamic_pointer_cast<AudioRegion>(o);
296         boost::shared_ptr<AudioRegion> left  = boost::dynamic_pointer_cast<AudioRegion>(l);
297         boost::shared_ptr<AudioRegion> right = boost::dynamic_pointer_cast<AudioRegion>(r);
298
299         for (Crossfades::iterator x = _crossfades.begin(); x != _crossfades.end();) {
300                 Crossfades::iterator tmp;
301                 tmp = x;
302                 ++tmp;
303
304                 boost::shared_ptr<Crossfade> fade;
305                 
306                 if ((*x)->_in == orig) {
307                         if (! (*x)->covers(right->position())) {
308                                 fade = boost::shared_ptr<Crossfade> (new Crossfade (*x, left, (*x)->_out));
309                         } else {
310                                 // Overlap, the crossfade is copied on the left side of the right region instead
311                                 fade = boost::shared_ptr<Crossfade> (new Crossfade (*x, right, (*x)->_out));
312                         }
313                 }
314                 
315                 if ((*x)->_out == orig) {
316                         if (! (*x)->covers(right->position())) {
317                                 fade = boost::shared_ptr<Crossfade> (new Crossfade (*x, (*x)->_in, right));
318                         } else {
319                                 // Overlap, the crossfade is copied on the right side of the left region instead
320                                 fade = boost::shared_ptr<Crossfade> (new Crossfade (*x, (*x)->_in, left));
321                         }
322                 }
323                 
324                 if (fade) {
325                         _crossfades.remove (*x);
326                         add_crossfade (fade);
327                 }
328                 x = tmp;
329         }
330 }
331
332 void
333 AudioPlaylist::check_dependents (boost::shared_ptr<Region> r, bool norefresh)
334 {
335         boost::shared_ptr<AudioRegion> other;
336         boost::shared_ptr<AudioRegion> region;
337         boost::shared_ptr<AudioRegion> top;
338         boost::shared_ptr<AudioRegion> bottom;
339         boost::shared_ptr<Crossfade>   xfade;
340         RegionList*  touched_regions;
341
342         if (in_set_state || in_partition) {
343                 return;
344         }
345
346         if ((region = boost::dynamic_pointer_cast<AudioRegion> (r)) == 0) {
347                 fatal << _("programming error: non-audio Region tested for overlap in audio playlist")
348                       << endmsg;
349                 return;
350         }
351
352         if (!norefresh) {
353                 refresh_dependents (r);
354         }
355
356
357         if (!Config->get_auto_xfade()) {
358                 return;
359         }
360
361         for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
362
363                 nframes_t xfade_length;
364
365                 other = boost::dynamic_pointer_cast<AudioRegion> (*i);
366
367                 if (other == region) {
368                         continue;
369                 }
370
371                 if (other->muted() || region->muted()) {
372                         continue;
373                 }
374                 
375
376                 if (other->layer() < region->layer()) {
377                         top = region;
378                         bottom = other;
379                 } else {
380                         top = other;
381                         bottom = region;
382                 }
383
384                 if (!top->opaque()) {
385                         continue;
386                 }
387
388                 OverlapType c = top->coverage (bottom->position(), bottom->last_frame());
389
390                 try {
391                         switch (c) {
392                         case OverlapNone:
393                                 break;
394
395                         case OverlapInternal:
396                                  /* {=============== top  =============}
397                                   *     [ ----- bottom  ------- ]
398                                   */
399                                 break;
400
401                         case OverlapExternal:
402  
403                                 /*     [ -------- top ------- ]
404                                  * {=========== bottom =============}
405                                  */
406                                 
407                                 /* to avoid discontinuities at the region boundaries of an internal
408                                    overlap (this region is completely within another), we create
409                                    two hidden crossfades at each boundary. this is not dependent
410                                    on the auto-xfade option, because we require it as basic
411                                    audio engineering.
412                                 */
413                                 
414                                 xfade_length = min ((nframes_t) 720, top->length());
415
416                                 if (top_region_at (top->first_frame()) == top) {
417  
418                                         xfade = boost::shared_ptr<Crossfade> (new Crossfade (top, bottom, xfade_length, top->first_frame(), StartOfIn));
419                                         add_crossfade (xfade);
420                                 }
421
422                                 if (top_region_at (top->last_frame() - 1) == top) {
423
424                                         /* 
425                                            only add a fade out if there is no region on top of the end of 'top' (which 
426                                            would cover it).
427                                         */
428                                         
429                                         xfade = boost::shared_ptr<Crossfade> (new Crossfade (bottom, top, xfade_length, top->last_frame() - xfade_length, EndOfOut));
430                                         add_crossfade (xfade);
431                                 }
432                                 break;
433                         case OverlapStart:
434
435                                 /*                   { ==== top ============ } 
436                                  *   [---- bottom -------------------] 
437                                  */
438
439                                 if (Config->get_xfade_model() == FullCrossfade) {
440                                         touched_regions = regions_touched (top->first_frame(), bottom->last_frame());
441                                         if (touched_regions->size() <= 2) {
442                                                 xfade = boost::shared_ptr<Crossfade> (new Crossfade (region, other, Config->get_xfade_model(), Config->get_xfades_active()));
443                                                 add_crossfade (xfade);
444                                         }
445                                 } else {
446
447                                         touched_regions = regions_touched (top->first_frame(), 
448                                                                            top->first_frame() + min ((nframes_t)Config->get_short_xfade_seconds() * _session.frame_rate(), 
449                                                                                                      top->length()));
450                                         if (touched_regions->size() <= 2) {
451                                                 xfade = boost::shared_ptr<Crossfade> (new Crossfade (region, other, Config->get_xfade_model(), Config->get_xfades_active()));
452                                                 add_crossfade (xfade);
453                                         }
454                                 }
455                                 break;
456                         case OverlapEnd:
457
458
459                                 /* [---- top ------------------------] 
460                                  *                { ==== bottom ============ } 
461                                  */ 
462
463                                 if (Config->get_xfade_model() == FullCrossfade) {
464
465                                         touched_regions = regions_touched (bottom->first_frame(), top->last_frame());
466                                         if (touched_regions->size() <= 2) {
467                                                 xfade = boost::shared_ptr<Crossfade> (new Crossfade (region, other, 
468                                                                                                      Config->get_xfade_model(), Config->get_xfades_active()));
469                                                 add_crossfade (xfade);
470                                         }
471
472                                 } else {
473                                         touched_regions = regions_touched (bottom->first_frame(), 
474                                                                            bottom->first_frame() + min ((nframes_t)Config->get_short_xfade_seconds() * _session.frame_rate(), 
475                                                                                                         bottom->length()));
476                                         if (touched_regions->size() <= 2) {
477                                                 xfade = boost::shared_ptr<Crossfade> (new Crossfade (region, other, Config->get_xfade_model(), Config->get_xfades_active()));
478                                                 add_crossfade (xfade);
479                                         }
480                                 }
481                                 break;
482                         default:
483                                 xfade = boost::shared_ptr<Crossfade> (new Crossfade (region, other, 
484                                                                                      Config->get_xfade_model(), Config->get_xfades_active()));
485                                 add_crossfade (xfade);
486                         }
487                 }
488
489                 catch (failed_constructor& err) {
490                         continue;
491                 }
492                 
493                 catch (Crossfade::NoCrossfadeHere& err) {
494                         continue;
495                 }
496                 
497         }
498 }
499
500 void
501 AudioPlaylist::add_crossfade (boost::shared_ptr<Crossfade> xfade)
502 {
503         Crossfades::iterator ci;
504
505         for (ci = _crossfades.begin(); ci != _crossfades.end(); ++ci) {
506                 if (*(*ci) == *xfade) { // Crossfade::operator==()
507                         break;
508                 }
509         }
510         
511         if (ci != _crossfades.end()) {
512                 // it will just go away
513         } else {
514                 _crossfades.push_back (xfade);
515
516                 xfade->Invalidated.connect (mem_fun (*this, &AudioPlaylist::crossfade_invalidated));
517                 xfade->StateChanged.connect (mem_fun (*this, &AudioPlaylist::crossfade_changed));
518
519                 notify_crossfade_added (xfade);
520         }
521 }
522         
523 void AudioPlaylist::notify_crossfade_added (boost::shared_ptr<Crossfade> x)
524 {
525         if (g_atomic_int_get(&block_notifications)) {
526                 _pending_xfade_adds.insert (_pending_xfade_adds.end(), x);
527         } else {
528
529                 NewCrossfade (x); /* EMIT SIGNAL */
530         }
531 }
532
533 void
534 AudioPlaylist::crossfade_invalidated (boost::shared_ptr<Region> r)
535 {
536         Crossfades::iterator i;
537         boost::shared_ptr<Crossfade> xfade = boost::dynamic_pointer_cast<Crossfade> (r);
538
539         xfade->in()->resume_fade_in ();
540         xfade->out()->resume_fade_out ();
541
542         if ((i = find (_crossfades.begin(), _crossfades.end(), xfade)) != _crossfades.end()) {
543                 _crossfades.erase (i);
544         }
545 }
546
547 int
548 AudioPlaylist::set_state (const XMLNode& node)
549 {
550         XMLNode *child;
551         XMLNodeList nlist;
552         XMLNodeConstIterator niter;
553
554         in_set_state++;
555         freeze ();
556
557         Playlist::set_state (node);
558
559         nlist = node.children();
560
561         for (niter = nlist.begin(); niter != nlist.end(); ++niter) {
562
563                 child = *niter;
564
565                 if (child->name() != "Crossfade") {
566                         continue;
567                 }
568
569                 try {
570                         boost::shared_ptr<Crossfade> xfade = boost::shared_ptr<Crossfade> (new Crossfade (*((const Playlist *)this), *child));
571                         _crossfades.push_back (xfade);
572                         xfade->Invalidated.connect (mem_fun (*this, &AudioPlaylist::crossfade_invalidated));
573                         xfade->StateChanged.connect (mem_fun (*this, &AudioPlaylist::crossfade_changed));
574                         NewCrossfade(xfade);
575                 }
576                 
577                 catch (failed_constructor& err) {
578                         //      cout << string_compose (_("could not create crossfade object in playlist %1"),
579                         //        _name) 
580                         //    << endl;
581                         continue;
582                 }
583         }
584
585         thaw ();
586         in_set_state--;
587
588         return 0;
589 }
590
591 void
592 AudioPlaylist::clear (bool with_signals)
593 {
594         _crossfades.clear ();
595         Playlist::clear (with_signals);
596 }
597
598 XMLNode&
599 AudioPlaylist::state (bool full_state)
600 {
601         XMLNode& node = Playlist::state (full_state);
602
603         if (full_state) {
604                 for (Crossfades::iterator i = _crossfades.begin(); i != _crossfades.end(); ++i) {
605                         node.add_child_nocopy ((*i)->get_state());
606                 }
607         }
608         
609         return node;
610 }
611
612 void
613 AudioPlaylist::dump () const
614 {
615         boost::shared_ptr<Region>r;
616         boost::shared_ptr<Crossfade> x;
617
618         cerr << "Playlist \"" << _name << "\" " << endl
619              << regions.size() << " regions "
620              << _crossfades.size() << " crossfades"
621              << endl;
622
623         for (RegionList::const_iterator i = regions.begin(); i != regions.end(); ++i) {
624                 r = *i;
625                 cerr << "  " << r->name() << " @ " << r << " [" 
626                      << r->start() << "+" << r->length() 
627                      << "] at " 
628                      << r->position()
629                      << " on layer "
630                      << r->layer ()
631                      << endl;
632         }
633
634         for (Crossfades::const_iterator i = _crossfades.begin(); i != _crossfades.end(); ++i) {
635                 x = *i;
636                 cerr << "  xfade [" 
637                      << x->out()->name()
638                      << ','
639                      << x->in()->name()
640                      << " @ "
641                      << x->position()
642                      << " length = " 
643                      << x->length ()
644                      << " active ? "
645                      << (x->active() ? "yes" : "no")
646                      << endl;
647         }
648 }
649
650 bool
651 AudioPlaylist::destroy_region (boost::shared_ptr<Region> region)
652 {
653         boost::shared_ptr<AudioRegion> r = boost::dynamic_pointer_cast<AudioRegion> (region);
654         bool changed = false;
655         Crossfades::iterator c, ctmp;
656         set<boost::shared_ptr<Crossfade> > unique_xfades;
657
658         if (r == 0) {
659                 fatal << _("programming error: non-audio Region passed to remove_overlap in audio playlist")
660                       << endmsg;
661                 /*NOTREACHED*/
662                 return false;
663         }
664
665         { 
666                 RegionLock rlock (this);
667
668                 for (RegionList::iterator i = regions.begin(); i != regions.end(); ) {
669                         
670                         RegionList::iterator tmp = i;
671                         ++tmp;
672                         
673                         if ((*i) == region) {
674                                 regions.erase (i);
675                                 changed = true;
676                         }
677                         
678                         i = tmp;
679                 }
680
681                 for (set<boost::shared_ptr<Region> >::iterator x = all_regions.begin(); x != all_regions.end(); ) {
682
683                         set<boost::shared_ptr<Region> >::iterator xtmp = x;
684                         ++xtmp;
685                         
686                         if ((*x) == region) {
687                                 all_regions.erase (x);
688                                 changed = true;
689                         }
690                         
691                         x = xtmp;
692                 }
693
694                 region->set_playlist (boost::shared_ptr<Playlist>());
695         }
696
697         for (c = _crossfades.begin(); c != _crossfades.end(); ) {
698                 ctmp = c;
699                 ++ctmp;
700
701                 if ((*c)->involves (r)) {
702                         unique_xfades.insert (*c);
703                         _crossfades.erase (c);
704                 }
705                 
706                 c = ctmp;
707         }
708
709         if (changed) {
710                 /* overload this, it normally means "removed", not destroyed */
711                 notify_region_removed (region);
712         }
713
714         return changed;
715 }
716
717 void
718 AudioPlaylist::crossfade_changed (Change ignored)
719 {
720         if (in_flush || in_set_state) {
721                 return;
722         }
723
724         /* XXX is there a loop here? can an xfade change not happen
725            due to a playlist change? well, sure activation would
726            be an example. maybe we should check the type of change
727            that occured.
728         */
729
730         notify_modified ();
731 }
732
733 bool
734 AudioPlaylist::region_changed (Change what_changed, boost::shared_ptr<Region> region)
735 {
736         if (in_flush || in_set_state) {
737                 return false;
738         }
739
740         Change our_interests = Change (AudioRegion::FadeInChanged|
741                                        AudioRegion::FadeOutChanged|
742                                        AudioRegion::FadeInActiveChanged|
743                                        AudioRegion::FadeOutActiveChanged|
744                                        AudioRegion::EnvelopeActiveChanged|
745                                        AudioRegion::ScaleAmplitudeChanged|
746                                        AudioRegion::EnvelopeChanged);
747         bool parent_wants_notify;
748
749         parent_wants_notify = Playlist::region_changed (what_changed, region);
750
751         if ((parent_wants_notify || (what_changed & our_interests))) {
752                 notify_modified ();
753         }
754
755         return true; 
756 }
757
758 void
759 AudioPlaylist::crossfades_at (nframes_t frame, Crossfades& clist)
760 {
761         RegionLock rlock (this);
762
763         for (Crossfades::iterator i = _crossfades.begin(); i != _crossfades.end(); ++i) {
764                 nframes_t start, end;
765
766                 start = (*i)->position();
767                 end = start + (*i)->overlap_length(); // not length(), important difference
768
769                 if (frame >= start && frame <= end) {
770                         clist.push_back (*i);
771                 } 
772         }
773 }
774