Comment.
[ardour.git] / libs / ardour / audio_playlist.cc
index 1506d204f15f6400d49e6db7f5811780ce051455..9c3bce4196eb39b9560afa27e343c3a584fb45b3 100644 (file)
@@ -1,5 +1,5 @@
 /*
 /*
-    Copyright (C) 2003 Paul Davis 
+    Copyright (C) 2003 Paul Davis
 
     This program is free software; you can redistribute it and/or modify
     it under the terms of the GNU General Public License as published by
 
     This program is free software; you can redistribute it and/or modify
     it under the terms of the GNU General Public License as published by
 
 #include <cstdlib>
 
 
 #include <cstdlib>
 
-#include <sigc++/bind.h>
-
-#include <ardour/types.h>
-#include <ardour/configuration.h>
-#include <ardour/audioplaylist.h>
-#include <ardour/audioregion.h>
-#include <ardour/crossfade.h>
-#include <ardour/crossfade_compare.h>
-#include <ardour/session.h>
-#include <pbd/enumwriter.h>
+#include "ardour/types.h"
+#include "ardour/debug.h"
+#include "ardour/configuration.h"
+#include "ardour/audioplaylist.h"
+#include "ardour/audioregion.h"
+#include "ardour/crossfade.h"
+#include "ardour/crossfade_compare.h"
+#include "ardour/session.h"
+#include "pbd/enumwriter.h"
 
 #include "i18n.h"
 
 using namespace ARDOUR;
 
 #include "i18n.h"
 
 using namespace ARDOUR;
-using namespace sigc;
 using namespace std;
 using namespace PBD;
 
 using namespace std;
 using namespace PBD;
 
+namespace ARDOUR {
+       namespace Properties {
+               PBD::PropertyDescriptor<bool> crossfades;
+       }
+}
+
+void
+AudioPlaylist::make_property_quarks ()
+{
+        Properties::crossfades.property_id = g_quark_from_static_string (X_("crossfades"));
+        DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for crossfades = %1\n", Properties::crossfades.property_id));
+}
+
+CrossfadeListProperty::CrossfadeListProperty (AudioPlaylist& pl)
+        : SequenceProperty<std::list<boost::shared_ptr<Crossfade> > > (Properties::crossfades.property_id, boost::bind (&AudioPlaylist::update, &pl, _1))
+        , _playlist (pl)
+{
+       
+}
+
+CrossfadeListProperty::CrossfadeListProperty (CrossfadeListProperty const & p)
+       : PBD::SequenceProperty<std::list<boost::shared_ptr<Crossfade> > > (p)
+       , _playlist (p._playlist)
+{
+
+}
+
+
+CrossfadeListProperty *
+CrossfadeListProperty::create () const
+{
+       return new CrossfadeListProperty (_playlist);
+}
+
+CrossfadeListProperty *
+CrossfadeListProperty::clone () const
+{
+       return new CrossfadeListProperty (*this);
+}
+
+void
+CrossfadeListProperty::get_content_as_xml (boost::shared_ptr<Crossfade> xfade, XMLNode & node) const
+{
+       /* Crossfades are not written to any state when they are no
+          longer in use, so we must write their state here.
+       */
+
+       XMLNode& c = xfade->get_state ();
+       node.add_child_nocopy (c);
+}
+
+boost::shared_ptr<Crossfade>
+CrossfadeListProperty::get_content_from_xml (XMLNode const & node) const
+{
+       XMLNodeList const c = node.children ();
+       assert (c.size() == 1);
+       return boost::shared_ptr<Crossfade> (new Crossfade (_playlist, *c.front()));
+}
+
+
 AudioPlaylist::AudioPlaylist (Session& session, const XMLNode& node, bool hidden)
        : Playlist (session, node, DataType::AUDIO, hidden)
 AudioPlaylist::AudioPlaylist (Session& session, const XMLNode& node, bool hidden)
        : Playlist (session, node, DataType::AUDIO, hidden)
+       , _crossfades (*this)
 {
 {
+#ifndef NDEBUG
        const XMLProperty* prop = node.property("type");
        assert(!prop || DataType(prop->value()) == DataType::AUDIO);
        const XMLProperty* prop = node.property("type");
        assert(!prop || DataType(prop->value()) == DataType::AUDIO);
+#endif
+
+       add_property (_crossfades);
 
        in_set_state++;
 
        in_set_state++;
-       set_state (node);
+       set_state (node, Stateful::loading_state_version);
        in_set_state--;
 }
 
 AudioPlaylist::AudioPlaylist (Session& session, string name, bool hidden)
        : Playlist (session, name, DataType::AUDIO, hidden)
        in_set_state--;
 }
 
 AudioPlaylist::AudioPlaylist (Session& session, string name, bool hidden)
        : Playlist (session, name, DataType::AUDIO, hidden)
+       , _crossfades (*this)
 {
 {
+       add_property (_crossfades);
 }
 
 AudioPlaylist::AudioPlaylist (boost::shared_ptr<const AudioPlaylist> other, string name, bool hidden)
        : Playlist (other, name, hidden)
 }
 
 AudioPlaylist::AudioPlaylist (boost::shared_ptr<const AudioPlaylist> other, string name, bool hidden)
        : Playlist (other, name, hidden)
+       , _crossfades (*this)
 {
 {
+       add_property (_crossfades);
+       
        RegionList::const_iterator in_o  = other->regions.begin();
        RegionList::iterator in_n = regions.begin();
 
        RegionList::const_iterator in_o  = other->regions.begin();
        RegionList::iterator in_n = regions.begin();
 
@@ -73,9 +141,9 @@ AudioPlaylist::AudioPlaylist (boost::shared_ptr<const AudioPlaylist> other, stri
                                RegionList::const_iterator out_n = regions.begin();
 
                                while (out_o != other->regions.end()) {
                                RegionList::const_iterator out_n = regions.begin();
 
                                while (out_o != other->regions.end()) {
-                                       
+
                                        boost::shared_ptr<AudioRegion>ar2 = boost::dynamic_pointer_cast<AudioRegion>(*out_o);
                                        boost::shared_ptr<AudioRegion>ar2 = boost::dynamic_pointer_cast<AudioRegion>(*out_o);
-                                       
+
                                        if ((*xfades)->out() == ar2) {
                                                boost::shared_ptr<AudioRegion>in  = boost::dynamic_pointer_cast<AudioRegion>(*in_n);
                                                boost::shared_ptr<AudioRegion>out = boost::dynamic_pointer_cast<AudioRegion>(*out_n);
                                        if ((*xfades)->out() == ar2) {
                                                boost::shared_ptr<AudioRegion>in  = boost::dynamic_pointer_cast<AudioRegion>(*in_n);
                                                boost::shared_ptr<AudioRegion>out = boost::dynamic_pointer_cast<AudioRegion>(*out_n);
@@ -83,7 +151,7 @@ AudioPlaylist::AudioPlaylist (boost::shared_ptr<const AudioPlaylist> other, stri
                                                add_crossfade(new_fade);
                                                break;
                                        }
                                                add_crossfade(new_fade);
                                                break;
                                        }
-                                       
+
                                        out_o++;
                                        out_n++;
                                }
                                        out_o++;
                                        out_n++;
                                }
@@ -98,18 +166,15 @@ AudioPlaylist::AudioPlaylist (boost::shared_ptr<const AudioPlaylist> other, stri
 
 AudioPlaylist::AudioPlaylist (boost::shared_ptr<const AudioPlaylist> other, nframes_t start, nframes_t cnt, string name, bool hidden)
        : Playlist (other, start, cnt, name, hidden)
 
 AudioPlaylist::AudioPlaylist (boost::shared_ptr<const AudioPlaylist> other, nframes_t start, nframes_t cnt, string name, bool hidden)
        : Playlist (other, start, cnt, name, hidden)
+       , _crossfades (*this)
 {
 {
+       add_property (_crossfades);
+       
        /* this constructor does NOT notify others (session) */
 }
 
 AudioPlaylist::~AudioPlaylist ()
 {
        /* this constructor does NOT notify others (session) */
 }
 
 AudioPlaylist::~AudioPlaylist ()
 {
-       GoingAway (); /* EMIT SIGNAL */
-
-       /* drop connections to signals */
-
-       notify_callbacks ();
-       
        _crossfades.clear ();
 }
 
        _crossfades.clear ();
 }
 
@@ -129,9 +194,9 @@ AudioPlaylist::read (Sample *buf, Sample *mixdown_buffer, float *gain_buffer, nf
        nframes_t skip_frames;
 
        /* optimizing this memset() away involves a lot of conditionals
        nframes_t skip_frames;
 
        /* optimizing this memset() away involves a lot of conditionals
-          that may well cause more of a hit due to cache misses 
+          that may well cause more of a hit due to cache misses
           and related stuff than just doing this here.
           and related stuff than just doing this here.
-          
+
           it would be great if someone could measure this
           at some point.
 
           it would be great if someone could measure this
           at some point.
 
@@ -143,11 +208,11 @@ AudioPlaylist::read (Sample *buf, Sample *mixdown_buffer, float *gain_buffer, nf
 
        memset (buf, 0, sizeof (Sample) * cnt);
 
 
        memset (buf, 0, sizeof (Sample) * cnt);
 
-       /* this function is never called from a realtime thread, so 
+       /* this function is never called from a realtime thread, so
           its OK to block (for short intervals).
        */
 
           its OK to block (for short intervals).
        */
 
-       Glib::Mutex::Lock rm (region_lock);
+       Glib::RecMutex::Lock rm (region_lock);
 
        end =  start + cnt - 1;
        read_frames = 0;
 
        end =  start + cnt - 1;
        read_frames = 0;
@@ -171,7 +236,7 @@ AudioPlaylist::read (Sample *buf, Sample *mixdown_buffer, float *gain_buffer, nf
                if ((*i)->coverage (start, end) != OverlapNone) {
                        relevant_regions[(*i)->layer()].push_back (*i);
                        relevant_layers.push_back ((*i)->layer());
                if ((*i)->coverage (start, end) != OverlapNone) {
                        relevant_regions[(*i)->layer()].push_back (*i);
                        relevant_layers.push_back ((*i)->layer());
-               }
+                }
        }
 
        for (Crossfades::iterator i = _crossfades.begin(); i != _crossfades.end(); ++i) {
        }
 
        for (Crossfades::iterator i = _crossfades.begin(); i != _crossfades.end(); ++i) {
@@ -196,13 +261,15 @@ AudioPlaylist::read (Sample *buf, Sample *mixdown_buffer, float *gain_buffer, nf
                vector<boost::shared_ptr<Region> > r (relevant_regions[*l]);
                vector<boost::shared_ptr<Crossfade> >& x (relevant_xfades[*l]);
 
                vector<boost::shared_ptr<Region> > r (relevant_regions[*l]);
                vector<boost::shared_ptr<Crossfade> >& x (relevant_xfades[*l]);
 
+
                for (vector<boost::shared_ptr<Region> >::iterator i = r.begin(); i != r.end(); ++i) {
                        boost::shared_ptr<AudioRegion> ar = boost::dynamic_pointer_cast<AudioRegion>(*i);
                for (vector<boost::shared_ptr<Region> >::iterator i = r.begin(); i != r.end(); ++i) {
                        boost::shared_ptr<AudioRegion> ar = boost::dynamic_pointer_cast<AudioRegion>(*i);
+                        DEBUG_TRACE (DEBUG::AudioPlayback, string_compose ("read from region %1\n", ar->name()));
                        assert(ar);
                        ar->read_at (buf, mixdown_buffer, gain_buffer, start, cnt, chan_n, read_frames, skip_frames);
                        _read_data_count += ar->read_data_count();
                }
                        assert(ar);
                        ar->read_at (buf, mixdown_buffer, gain_buffer, start, cnt, chan_n, read_frames, skip_frames);
                        _read_data_count += ar->read_data_count();
                }
-               
+
                for (vector<boost::shared_ptr<Crossfade> >::iterator i = x.begin(); i != x.end(); ++i) {
                        (*i)->read_at (buf, mixdown_buffer, gain_buffer, start, cnt, chan_n);
 
                for (vector<boost::shared_ptr<Crossfade> >::iterator i = x.begin(); i != x.end(); ++i) {
                        (*i)->read_at (buf, mixdown_buffer, gain_buffer, start, cnt, chan_n);
 
@@ -225,7 +292,7 @@ AudioPlaylist::remove_dependents (boost::shared_ptr<Region> region)
        if (in_set_state) {
                return;
        }
        if (in_set_state) {
                return;
        }
-       
+
        if (r == 0) {
                fatal << _("programming error: non-audio Region passed to remove_overlap in audio playlist")
                      << endmsg;
        if (r == 0) {
                fatal << _("programming error: non-audio Region passed to remove_overlap in audio playlist")
                      << endmsg;
@@ -233,7 +300,7 @@ AudioPlaylist::remove_dependents (boost::shared_ptr<Region> region)
        }
 
        for (Crossfades::iterator i = _crossfades.begin(); i != _crossfades.end(); ) {
        }
 
        for (Crossfades::iterator i = _crossfades.begin(); i != _crossfades.end(); ) {
-               
+
                if ((*i)->involves (r)) {
                        i = _crossfades.erase (i);
                } else {
                if ((*i)->involves (r)) {
                        i = _crossfades.erase (i);
                } else {
@@ -244,9 +311,9 @@ AudioPlaylist::remove_dependents (boost::shared_ptr<Region> region)
 
 
 void
 
 
 void
-AudioPlaylist::flush_notifications ()
+AudioPlaylist::flush_notifications (bool from_undo)
 {
 {
-       Playlist::flush_notifications();
+       Playlist::flush_notifications (from_undo);
 
        if (in_flush) {
                return;
 
        if (in_flush) {
                return;
@@ -260,7 +327,7 @@ AudioPlaylist::flush_notifications ()
        }
 
        _pending_xfade_adds.clear ();
        }
 
        _pending_xfade_adds.clear ();
-       
+
        in_flush = false;
 }
 
        in_flush = false;
 }
 
@@ -277,7 +344,7 @@ AudioPlaylist::refresh_dependents (boost::shared_ptr<Region> r)
        for (Crossfades::iterator x = _crossfades.begin(); x != _crossfades.end();) {
 
                Crossfades::iterator tmp;
        for (Crossfades::iterator x = _crossfades.begin(); x != _crossfades.end();) {
 
                Crossfades::iterator tmp;
-               
+
                tmp = x;
                ++tmp;
 
                tmp = x;
                ++tmp;
 
@@ -286,7 +353,7 @@ AudioPlaylist::refresh_dependents (boost::shared_ptr<Region> r)
                if ((*x)->involves (ar)) {
 
                        pair<set<boost::shared_ptr<Crossfade> >::iterator, bool> const u = updated.insert (*x);
                if ((*x)->involves (ar)) {
 
                        pair<set<boost::shared_ptr<Crossfade> >::iterator, bool> const u = updated.insert (*x);
-                       
+
                        if (u.second) {
                                /* x was successfully inserted into the set, so it has not already been updated */
                                try {
                        if (u.second) {
                                /* x was successfully inserted into the set, so it has not already been updated */
                                try {
@@ -316,7 +383,7 @@ AudioPlaylist::finalize_split_region (boost::shared_ptr<Region> o, boost::shared
                ++tmp;
 
                boost::shared_ptr<Crossfade> fade;
                ++tmp;
 
                boost::shared_ptr<Crossfade> fade;
-               
+
                if ((*x)->_in == orig) {
                        if (! (*x)->covers(right->position())) {
                                fade = boost::shared_ptr<Crossfade> (new Crossfade (*x, left, (*x)->_out));
                if ((*x)->_in == orig) {
                        if (! (*x)->covers(right->position())) {
                                fade = boost::shared_ptr<Crossfade> (new Crossfade (*x, left, (*x)->_out));
@@ -325,7 +392,7 @@ AudioPlaylist::finalize_split_region (boost::shared_ptr<Region> o, boost::shared
                                fade = boost::shared_ptr<Crossfade> (new Crossfade (*x, right, (*x)->_out));
                        }
                }
                                fade = boost::shared_ptr<Crossfade> (new Crossfade (*x, right, (*x)->_out));
                        }
                }
-               
+
                if ((*x)->_out == orig) {
                        if (! (*x)->covers(right->position())) {
                                fade = boost::shared_ptr<Crossfade> (new Crossfade (*x, (*x)->_in, right));
                if ((*x)->_out == orig) {
                        if (! (*x)->covers(right->position())) {
                                fade = boost::shared_ptr<Crossfade> (new Crossfade (*x, (*x)->_in, right));
@@ -334,7 +401,7 @@ AudioPlaylist::finalize_split_region (boost::shared_ptr<Region> o, boost::shared
                                fade = boost::shared_ptr<Crossfade> (new Crossfade (*x, (*x)->_in, left));
                        }
                }
                                fade = boost::shared_ptr<Crossfade> (new Crossfade (*x, (*x)->_in, left));
                        }
                }
-               
+
                if (fade) {
                        _crossfades.remove (*x);
                        add_crossfade (fade);
                if (fade) {
                        _crossfades.remove (*x);
                        add_crossfade (fade);
@@ -351,7 +418,7 @@ AudioPlaylist::check_dependents (boost::shared_ptr<Region> r, bool norefresh)
        boost::shared_ptr<AudioRegion> top;
        boost::shared_ptr<AudioRegion> bottom;
        boost::shared_ptr<Crossfade>   xfade;
        boost::shared_ptr<AudioRegion> top;
        boost::shared_ptr<AudioRegion> bottom;
        boost::shared_ptr<Crossfade>   xfade;
-       RegionList*  touched_regions;
+       RegionList*  touched_regions = 0;
 
        if (in_set_state || in_partition) {
                return;
 
        if (in_set_state || in_partition) {
                return;
@@ -368,14 +435,11 @@ AudioPlaylist::check_dependents (boost::shared_ptr<Region> r, bool norefresh)
        }
 
 
        }
 
 
-       if (!Config->get_auto_xfade()) {
+       if (!_session.config.get_auto_xfade()) {
                return;
        }
 
        for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
                return;
        }
 
        for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
-
-               nframes_t xfade_length;
-
                other = boost::dynamic_pointer_cast<AudioRegion> (*i);
 
                if (other == region) {
                other = boost::dynamic_pointer_cast<AudioRegion> (*i);
 
                if (other == region) {
@@ -385,7 +449,7 @@ AudioPlaylist::check_dependents (boost::shared_ptr<Region> r, bool norefresh)
                if (other->muted() || region->muted()) {
                        continue;
                }
                if (other->muted() || region->muted()) {
                        continue;
                }
-               
+
 
                if (other->layer() < region->layer()) {
                        top = region;
 
                if (other->layer() < region->layer()) {
                        top = region;
@@ -401,7 +465,11 @@ AudioPlaylist::check_dependents (boost::shared_ptr<Region> r, bool norefresh)
 
                OverlapType c = top->coverage (bottom->position(), bottom->last_frame());
 
 
                OverlapType c = top->coverage (bottom->position(), bottom->last_frame());
 
+               delete touched_regions;
+               touched_regions = 0;
+
                try {
                try {
+                       framecnt_t xfade_length;
                        switch (c) {
                        case OverlapNone:
                                break;
                        switch (c) {
                        case OverlapNone:
                                break;
@@ -413,56 +481,56 @@ AudioPlaylist::check_dependents (boost::shared_ptr<Region> r, bool norefresh)
                                break;
 
                        case OverlapExternal:
                                break;
 
                        case OverlapExternal:
+
                                /*     [ -------- top ------- ]
                                 * {=========== bottom =============}
                                 */
                                /*     [ -------- top ------- ]
                                 * {=========== bottom =============}
                                 */
-                               
+
                                /* to avoid discontinuities at the region boundaries of an internal
                                   overlap (this region is completely within another), we create
                                   two hidden crossfades at each boundary. this is not dependent
                                   on the auto-xfade option, because we require it as basic
                                   audio engineering.
                                */
                                /* to avoid discontinuities at the region boundaries of an internal
                                   overlap (this region is completely within another), we create
                                   two hidden crossfades at each boundary. this is not dependent
                                   on the auto-xfade option, because we require it as basic
                                   audio engineering.
                                */
-                               
-                               xfade_length = min ((nframes_t) 720, top->length());
+
+                               xfade_length = min ((framecnt_t) 720, top->length());
 
                                if (top_region_at (top->first_frame()) == top) {
 
                                if (top_region_at (top->first_frame()) == top) {
+
                                        xfade = boost::shared_ptr<Crossfade> (new Crossfade (top, bottom, xfade_length, top->first_frame(), StartOfIn));
                                        add_crossfade (xfade);
                                }
 
                                if (top_region_at (top->last_frame() - 1) == top) {
 
                                        xfade = boost::shared_ptr<Crossfade> (new Crossfade (top, bottom, xfade_length, top->first_frame(), StartOfIn));
                                        add_crossfade (xfade);
                                }
 
                                if (top_region_at (top->last_frame() - 1) == top) {
 
-                                       /* 
-                                          only add a fade out if there is no region on top of the end of 'top' (which 
+                                       /*
+                                          only add a fade out if there is no region on top of the end of 'top' (which
                                           would cover it).
                                        */
                                           would cover it).
                                        */
-                                       
+
                                        xfade = boost::shared_ptr<Crossfade> (new Crossfade (bottom, top, xfade_length, top->last_frame() - xfade_length, EndOfOut));
                                        add_crossfade (xfade);
                                }
                                break;
                        case OverlapStart:
 
                                        xfade = boost::shared_ptr<Crossfade> (new Crossfade (bottom, top, xfade_length, top->last_frame() - xfade_length, EndOfOut));
                                        add_crossfade (xfade);
                                }
                                break;
                        case OverlapStart:
 
-                               /*                   { ==== top ============ } 
-                                *   [---- bottom -------------------] 
+                               /*                   { ==== top ============ }
+                                *   [---- bottom -------------------]
                                 */
 
                                 */
 
-                               if (Config->get_xfade_model() == FullCrossfade) {
+                               if (_session.config.get_xfade_model() == FullCrossfade) {
                                        touched_regions = regions_touched (top->first_frame(), bottom->last_frame());
                                        if (touched_regions->size() <= 2) {
                                        touched_regions = regions_touched (top->first_frame(), bottom->last_frame());
                                        if (touched_regions->size() <= 2) {
-                                               xfade = boost::shared_ptr<Crossfade> (new Crossfade (region, other, Config->get_xfade_model(), Config->get_xfades_active()));
+                                               xfade = boost::shared_ptr<Crossfade> (new Crossfade (region, other, _session.config.get_xfade_model(), _session.config.get_xfades_active()));
                                                add_crossfade (xfade);
                                        }
                                } else {
 
                                                add_crossfade (xfade);
                                        }
                                } else {
 
-                                       touched_regions = regions_touched (top->first_frame(), 
-                                                                          top->first_frame() + min ((nframes_t)Config->get_short_xfade_seconds() * _session.frame_rate(), 
+                                       touched_regions = regions_touched (top->first_frame(),
+                                                                          top->first_frame() + min ((framecnt_t) _session.config.get_short_xfade_seconds() * _session.frame_rate(),
                                                                                                     top->length()));
                                        if (touched_regions->size() <= 2) {
                                                                                                     top->length()));
                                        if (touched_regions->size() <= 2) {
-                                               xfade = boost::shared_ptr<Crossfade> (new Crossfade (region, other, Config->get_xfade_model(), Config->get_xfades_active()));
+                                               xfade = boost::shared_ptr<Crossfade> (new Crossfade (region, other, _session.config.get_xfade_model(), _session.config.get_xfades_active()));
                                                add_crossfade (xfade);
                                        }
                                }
                                                add_crossfade (xfade);
                                        }
                                }
@@ -470,32 +538,32 @@ AudioPlaylist::check_dependents (boost::shared_ptr<Region> r, bool norefresh)
                        case OverlapEnd:
 
 
                        case OverlapEnd:
 
 
-                               /* [---- top ------------------------] 
-                                *                { ==== bottom ============ } 
-                                */ 
+                               /* [---- top ------------------------]
+                                *                { ==== bottom ============ }
+                                */
 
 
-                               if (Config->get_xfade_model() == FullCrossfade) {
+                               if (_session.config.get_xfade_model() == FullCrossfade) {
 
                                        touched_regions = regions_touched (bottom->first_frame(), top->last_frame());
                                        if (touched_regions->size() <= 2) {
 
                                        touched_regions = regions_touched (bottom->first_frame(), top->last_frame());
                                        if (touched_regions->size() <= 2) {
-                                               xfade = boost::shared_ptr<Crossfade> (new Crossfade (region, other, 
-                                                                                                    Config->get_xfade_model(), Config->get_xfades_active()));
+                                               xfade = boost::shared_ptr<Crossfade> (new Crossfade (region, other,
+                                                                                                    _session.config.get_xfade_model(), _session.config.get_xfades_active()));
                                                add_crossfade (xfade);
                                        }
 
                                } else {
                                                add_crossfade (xfade);
                                        }
 
                                } else {
-                                       touched_regions = regions_touched (bottom->first_frame(), 
-                                                                          bottom->first_frame() + min ((nframes_t)Config->get_short_xfade_seconds() * _session.frame_rate(), 
+                                       touched_regions = regions_touched (bottom->first_frame(),
+                                                                          bottom->first_frame() + min ((framecnt_t)_session.config.get_short_xfade_seconds() * _session.frame_rate(),
                                                                                                        bottom->length()));
                                        if (touched_regions->size() <= 2) {
                                                                                                        bottom->length()));
                                        if (touched_regions->size() <= 2) {
-                                               xfade = boost::shared_ptr<Crossfade> (new Crossfade (region, other, Config->get_xfade_model(), Config->get_xfades_active()));
+                                               xfade = boost::shared_ptr<Crossfade> (new Crossfade (region, other, _session.config.get_xfade_model(), _session.config.get_xfades_active()));
                                                add_crossfade (xfade);
                                        }
                                }
                                break;
                        default:
                                                add_crossfade (xfade);
                                        }
                                }
                                break;
                        default:
-                               xfade = boost::shared_ptr<Crossfade> (new Crossfade (region, other, 
-                                                                                    Config->get_xfade_model(), Config->get_xfades_active()));
+                               xfade = boost::shared_ptr<Crossfade> (new Crossfade (region, other,
+                                                                                    _session.config.get_xfade_model(), _session.config.get_xfades_active()));
                                add_crossfade (xfade);
                        }
                }
                                add_crossfade (xfade);
                        }
                }
@@ -503,12 +571,14 @@ AudioPlaylist::check_dependents (boost::shared_ptr<Region> r, bool norefresh)
                catch (failed_constructor& err) {
                        continue;
                }
                catch (failed_constructor& err) {
                        continue;
                }
-               
+
                catch (Crossfade::NoCrossfadeHere& err) {
                        continue;
                }
                catch (Crossfade::NoCrossfadeHere& err) {
                        continue;
                }
-               
+
        }
        }
+
+       delete touched_regions;
 }
 
 void
 }
 
 void
@@ -521,25 +591,24 @@ AudioPlaylist::add_crossfade (boost::shared_ptr<Crossfade> xfade)
                        break;
                }
        }
                        break;
                }
        }
-       
+
        if (ci != _crossfades.end()) {
                // it will just go away
        } else {
                _crossfades.push_back (xfade);
 
        if (ci != _crossfades.end()) {
                // it will just go away
        } else {
                _crossfades.push_back (xfade);
 
-               xfade->Invalidated.connect (mem_fun (*this, &AudioPlaylist::crossfade_invalidated));
-               xfade->StateChanged.connect (mem_fun (*this, &AudioPlaylist::crossfade_changed));
+               xfade->Invalidated.connect_same_thread (*this, boost::bind (&AudioPlaylist::crossfade_invalidated, this, _1));
+               xfade->PropertyChanged.connect_same_thread (*this, boost::bind (&AudioPlaylist::crossfade_changed, this, _1));
 
                notify_crossfade_added (xfade);
        }
 }
 
                notify_crossfade_added (xfade);
        }
 }
-       
+
 void AudioPlaylist::notify_crossfade_added (boost::shared_ptr<Crossfade> x)
 {
        if (g_atomic_int_get(&block_notifications)) {
                _pending_xfade_adds.insert (_pending_xfade_adds.end(), x);
        } else {
 void AudioPlaylist::notify_crossfade_added (boost::shared_ptr<Crossfade> x)
 {
        if (g_atomic_int_get(&block_notifications)) {
                _pending_xfade_adds.insert (_pending_xfade_adds.end(), x);
        } else {
-
                NewCrossfade (x); /* EMIT SIGNAL */
        }
 }
                NewCrossfade (x); /* EMIT SIGNAL */
        }
 }
@@ -559,16 +628,17 @@ AudioPlaylist::crossfade_invalidated (boost::shared_ptr<Region> r)
 }
 
 int
 }
 
 int
-AudioPlaylist::set_state (const XMLNode& node)
+AudioPlaylist::set_state (const XMLNode& node, int version)
 {
        XMLNode *child;
        XMLNodeList nlist;
        XMLNodeConstIterator niter;
 
        in_set_state++;
 {
        XMLNode *child;
        XMLNodeList nlist;
        XMLNodeConstIterator niter;
 
        in_set_state++;
-       freeze ();
 
 
-       Playlist::set_state (node);
+       Playlist::set_state (node, version);
+
+       freeze ();
 
        nlist = node.children();
 
 
        nlist = node.children();
 
@@ -583,14 +653,14 @@ AudioPlaylist::set_state (const XMLNode& node)
                try {
                        boost::shared_ptr<Crossfade> xfade = boost::shared_ptr<Crossfade> (new Crossfade (*((const Playlist *)this), *child));
                        _crossfades.push_back (xfade);
                try {
                        boost::shared_ptr<Crossfade> xfade = boost::shared_ptr<Crossfade> (new Crossfade (*((const Playlist *)this), *child));
                        _crossfades.push_back (xfade);
-                       xfade->Invalidated.connect (mem_fun (*this, &AudioPlaylist::crossfade_invalidated));
-                       xfade->StateChanged.connect (mem_fun (*this, &AudioPlaylist::crossfade_changed));
+                       xfade->Invalidated.connect_same_thread (*this, boost::bind (&AudioPlaylist::crossfade_invalidated, this, _1));
+                       xfade->PropertyChanged.connect_same_thread (*this, boost::bind (&AudioPlaylist::crossfade_changed, this, _1));
                        NewCrossfade(xfade);
                }
                        NewCrossfade(xfade);
                }
-               
+
                catch (failed_constructor& err) {
                        //      cout << string_compose (_("could not create crossfade object in playlist %1"),
                catch (failed_constructor& err) {
                        //      cout << string_compose (_("could not create crossfade object in playlist %1"),
-                       //        _name) 
+                       //        _name)
                        //    << endl;
                        continue;
                }
                        //    << endl;
                        continue;
                }
@@ -619,7 +689,7 @@ AudioPlaylist::state (bool full_state)
                        node.add_child_nocopy ((*i)->get_state());
                }
        }
                        node.add_child_nocopy ((*i)->get_state());
                }
        }
-       
+
        return node;
 }
 
        return node;
 }
 
@@ -636,9 +706,9 @@ AudioPlaylist::dump () const
 
        for (RegionList::const_iterator i = regions.begin(); i != regions.end(); ++i) {
                r = *i;
 
        for (RegionList::const_iterator i = regions.begin(); i != regions.end(); ++i) {
                r = *i;
-               cerr << "  " << r->name() << " @ " << r << " [" 
-                    << r->start() << "+" << r->length() 
-                    << "] at " 
+               cerr << "  " << r->name() << " @ " << r << " ["
+                    << r->start() << "+" << r->length()
+                    << "] at "
                     << r->position()
                     << " on layer "
                     << r->layer ()
                     << r->position()
                     << " on layer "
                     << r->layer ()
@@ -647,13 +717,13 @@ AudioPlaylist::dump () const
 
        for (Crossfades::const_iterator i = _crossfades.begin(); i != _crossfades.end(); ++i) {
                x = *i;
 
        for (Crossfades::const_iterator i = _crossfades.begin(); i != _crossfades.end(); ++i) {
                x = *i;
-               cerr << "  xfade [" 
+               cerr << "  xfade ["
                     << x->out()->name()
                     << ','
                     << x->in()->name()
                     << " @ "
                     << x->position()
                     << x->out()->name()
                     << ','
                     << x->in()->name()
                     << " @ "
                     << x->position()
-                    << " length = " 
+                    << " length = "
                     << x->length ()
                     << " active ? "
                     << (x->active() ? "yes" : "no")
                     << x->length ()
                     << " active ? "
                     << (x->active() ? "yes" : "no")
@@ -665,30 +735,28 @@ bool
 AudioPlaylist::destroy_region (boost::shared_ptr<Region> region)
 {
        boost::shared_ptr<AudioRegion> r = boost::dynamic_pointer_cast<AudioRegion> (region);
 AudioPlaylist::destroy_region (boost::shared_ptr<Region> region)
 {
        boost::shared_ptr<AudioRegion> r = boost::dynamic_pointer_cast<AudioRegion> (region);
+
+        if (!r) {
+                return false;
+        }
+
        bool changed = false;
        Crossfades::iterator c, ctmp;
        set<boost::shared_ptr<Crossfade> > unique_xfades;
 
        bool changed = false;
        Crossfades::iterator c, ctmp;
        set<boost::shared_ptr<Crossfade> > unique_xfades;
 
-       if (r == 0) {
-               fatal << _("programming error: non-audio Region passed to remove_overlap in audio playlist")
-                     << endmsg;
-               /*NOTREACHED*/
-               return false;
-       }
-
-       { 
+       {
                RegionLock rlock (this);
 
                for (RegionList::iterator i = regions.begin(); i != regions.end(); ) {
                RegionLock rlock (this);
 
                for (RegionList::iterator i = regions.begin(); i != regions.end(); ) {
-                       
+
                        RegionList::iterator tmp = i;
                        ++tmp;
                        RegionList::iterator tmp = i;
                        ++tmp;
-                       
+
                        if ((*i) == region) {
                                regions.erase (i);
                                changed = true;
                        }
                        if ((*i) == region) {
                                regions.erase (i);
                                changed = true;
                        }
-                       
+
                        i = tmp;
                }
 
                        i = tmp;
                }
 
@@ -696,12 +764,12 @@ AudioPlaylist::destroy_region (boost::shared_ptr<Region> region)
 
                        set<boost::shared_ptr<Region> >::iterator xtmp = x;
                        ++xtmp;
 
                        set<boost::shared_ptr<Region> >::iterator xtmp = x;
                        ++xtmp;
-                       
+
                        if ((*x) == region) {
                                all_regions.erase (x);
                                changed = true;
                        }
                        if ((*x) == region) {
                                all_regions.erase (x);
                                changed = true;
                        }
-                       
+
                        x = xtmp;
                }
 
                        x = xtmp;
                }
 
@@ -716,7 +784,7 @@ AudioPlaylist::destroy_region (boost::shared_ptr<Region> region)
                        unique_xfades.insert (*c);
                        _crossfades.erase (c);
                }
                        unique_xfades.insert (*c);
                        _crossfades.erase (c);
                }
-               
+
                c = ctmp;
        }
 
                c = ctmp;
        }
 
@@ -729,7 +797,7 @@ AudioPlaylist::destroy_region (boost::shared_ptr<Region> region)
 }
 
 void
 }
 
 void
-AudioPlaylist::crossfade_changed (Change ignored)
+AudioPlaylist::crossfade_changed (const PropertyChange&)
 {
        if (in_flush || in_set_state) {
                return;
 {
        if (in_flush || in_set_state) {
                return;
@@ -741,32 +809,35 @@ AudioPlaylist::crossfade_changed (Change ignored)
           that occured.
        */
 
           that occured.
        */
 
-       notify_modified ();
+       notify_contents_changed ();
 }
 
 bool
 }
 
 bool
-AudioPlaylist::region_changed (Change what_changed, boost::shared_ptr<Region> region)
+AudioPlaylist::region_changed (const PropertyChange& what_changed, boost::shared_ptr<Region> region)
 {
        if (in_flush || in_set_state) {
                return false;
        }
 
 {
        if (in_flush || in_set_state) {
                return false;
        }
 
-       Change our_interests = Change (AudioRegion::FadeInChanged|
-                                      AudioRegion::FadeOutChanged|
-                                      AudioRegion::FadeInActiveChanged|
-                                      AudioRegion::FadeOutActiveChanged|
-                                      AudioRegion::EnvelopeActiveChanged|
-                                      AudioRegion::ScaleAmplitudeChanged|
-                                      AudioRegion::EnvelopeChanged);
+       PropertyChange our_interests;
+
+       our_interests.add (Properties::fade_in_active);
+       our_interests.add (Properties::fade_out_active);
+       our_interests.add (Properties::scale_amplitude);
+       our_interests.add (Properties::envelope_active);
+       our_interests.add (Properties::envelope);
+       our_interests.add (Properties::fade_in);
+       our_interests.add (Properties::fade_out);
+
        bool parent_wants_notify;
 
        parent_wants_notify = Playlist::region_changed (what_changed, region);
 
        bool parent_wants_notify;
 
        parent_wants_notify = Playlist::region_changed (what_changed, region);
 
-       if ((parent_wants_notify || (what_changed & our_interests))) {
-               notify_modified ();
+       if (parent_wants_notify || (what_changed.contains (our_interests))) {
+               notify_contents_changed ();
        }
 
        }
 
-       return true; 
+       return true;
 }
 
 void
 }
 
 void
@@ -782,7 +853,40 @@ AudioPlaylist::crossfades_at (nframes_t frame, Crossfades& clist)
 
                if (frame >= start && frame <= end) {
                        clist.push_back (*i);
 
                if (frame >= start && frame <= end) {
                        clist.push_back (*i);
-               } 
+               }
+       }
+}
+
+void
+AudioPlaylist::foreach_crossfade (boost::function<void (boost::shared_ptr<Crossfade>)> s)
+{
+       RegionLock rl (this, false);
+       for (Crossfades::iterator i = _crossfades.begin(); i != _crossfades.end(); ++i) {
+               s (*i);
+       }
+}
+
+void
+AudioPlaylist::update (const CrossfadeListProperty::ChangeRecord& change)
+{
+       for (CrossfadeListProperty::ChangeContainer::const_iterator i = change.added.begin(); i != change.added.end(); ++i) {
+               add_crossfade (*i);
        }
        }
+
+       /* don't remove crossfades here; they will be dealt with by the dependency code */
 }
 
 }
 
+boost::shared_ptr<Crossfade>
+AudioPlaylist::find_crossfade (const PBD::ID& id) const
+{
+       Crossfades::const_iterator i = _crossfades.begin ();
+       while (i != _crossfades.end() && (*i)->id() != id) {
+               ++i;
+       }
+
+       if (i == _crossfades.end()) {
+               return boost::shared_ptr<Crossfade> ();
+       }
+
+       return *i;
+}