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