(Hopefully) clarify operator= and copy construction behaviour of the Property hierarc...
[ardour.git] / libs / ardour / audioregion.cc
index 477c479de8ad11bc38cd4a9e653bdc2bb19c6a81..7cc8e00998c89719c3dd9bf7d640b29f83028f7e 100644 (file)
@@ -1,5 +1,5 @@
 /*
-    Copyright (C) 2000-2006 Paul Davis 
+    Copyright (C) 2000-2006 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
 
 #include <set>
 
-#include <sigc++/bind.h>
-#include <sigc++/class_slot.h>
 
 #include <glibmm/thread.h>
 
-#include <pbd/basename.h>
-#include <pbd/xml++.h>
-#include <pbd/stacktrace.h>
-#include <pbd/enumwriter.h>
-#include <pbd/convert.h>
-
-#include <ardour/audioregion.h>
-#include <ardour/session.h>
-#include <ardour/gain.h>
-#include <ardour/dB.h>
-#include <ardour/playlist.h>
-#include <ardour/audiofilesource.h>
-#include <ardour/region_factory.h>
-#include <ardour/runtime_functions.h>
-#include <ardour/transient_detector.h>
+#include "pbd/basename.h"
+#include "pbd/xml++.h"
+#include "pbd/stacktrace.h"
+#include "pbd/enumwriter.h"
+#include "pbd/convert.h"
+
+#include "evoral/Curve.hpp"
+
+#include "ardour/audioregion.h"
+#include "ardour/debug.h"
+#include "ardour/session.h"
+#include "ardour/gain.h"
+#include "ardour/dB.h"
+#include "ardour/playlist.h"
+#include "ardour/audiofilesource.h"
+#include "ardour/region_factory.h"
+#include "ardour/runtime_functions.h"
+#include "ardour/transient_detector.h"
 
 #include "i18n.h"
 #include <locale.h>
@@ -52,226 +53,244 @@ using namespace std;
 using namespace ARDOUR;
 using namespace PBD;
 
-/* a Session will reset these to its chosen defaults by calling AudioRegion::set_default_fade() */
+namespace ARDOUR {
+       namespace Properties {
+               PBD::PropertyDescriptor<bool> envelope_active;
+               PBD::PropertyDescriptor<bool> default_fade_in;
+               PBD::PropertyDescriptor<bool> default_fade_out;
+               PBD::PropertyDescriptor<bool> fade_in_active;
+               PBD::PropertyDescriptor<bool> fade_out_active;
+               PBD::PropertyDescriptor<float> scale_amplitude;
+       }
+}
+
+void
+AudioRegion::make_property_quarks ()
+{
+       Properties::envelope_active.property_id = g_quark_from_static_string (X_("envelope-active"));
+        DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for envelope-active = %1\n",    Properties::envelope_active.property_id));
+       Properties::default_fade_in.property_id = g_quark_from_static_string (X_("default-fade-in"));
+        DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for default-fade-in = %1\n",    Properties::default_fade_in.property_id));
+       Properties::default_fade_out.property_id = g_quark_from_static_string (X_("default-fade-out"));
+        DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for default-fade-out = %1\n",   Properties::default_fade_out.property_id));
+       Properties::fade_in_active.property_id = g_quark_from_static_string (X_("fade-in-active"));
+        DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for fade-in-active = %1\n",     Properties::fade_in_active.property_id));
+       Properties::fade_out_active.property_id = g_quark_from_static_string (X_("fade-out-active"));
+        DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for fade-out-active = %1\n",    Properties::fade_out_active.property_id));
+       Properties::scale_amplitude.property_id = g_quark_from_static_string (X_("scale-amplitude"));
+        DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for scale-amplitude = %1\n",    Properties::scale_amplitude.property_id));
+}
+
+void
+AudioRegion::register_properties ()
+{
+       /* no need to register parent class properties */
+
+       add_property (_envelope_active);
+       add_property (_default_fade_in);
+       add_property (_default_fade_out);
+       add_property (_fade_in_active);
+       add_property (_fade_out_active);
+       add_property (_scale_amplitude);
+}
 
-Change AudioRegion::FadeInChanged         = ARDOUR::new_change();
-Change AudioRegion::FadeOutChanged        = ARDOUR::new_change();
-Change AudioRegion::FadeInActiveChanged   = ARDOUR::new_change();
-Change AudioRegion::FadeOutActiveChanged  = ARDOUR::new_change();
-Change AudioRegion::EnvelopeActiveChanged = ARDOUR::new_change();
-Change AudioRegion::ScaleAmplitudeChanged = ARDOUR::new_change();
-Change AudioRegion::EnvelopeChanged       = ARDOUR::new_change();
+#define AUDIOREGION_STATE_DEFAULT \
+       _envelope_active (Properties::envelope_active, false) \
+       , _default_fade_in (Properties::default_fade_in, true) \
+       , _default_fade_out (Properties::default_fade_out, true) \
+       , _fade_in_active (Properties::fade_in_active, true) \
+       , _fade_out_active (Properties::fade_out_active, true) \
+       , _scale_amplitude (Properties::scale_amplitude, 1.0)
+       
+#define AUDIOREGION_COPY_STATE(other) \
+       _envelope_active (Properties::envelope_active, other->_envelope_active) \
+       , _default_fade_in (Properties::default_fade_in, other->_default_fade_in) \
+       , _default_fade_out (Properties::default_fade_out, other->_default_fade_out) \
+       , _fade_in_active (Properties::fade_in_active, other->_fade_in_active) \
+       , _fade_out_active (Properties::fade_out_active, other->_fade_out_active) \
+       , _scale_amplitude (Properties::scale_amplitude, other->_scale_amplitude)
+/* a Session will reset these to its chosen defaults by calling AudioRegion::set_default_fade() */
 
 void
 AudioRegion::init ()
 {
-       _scale_amplitude = 1.0;
+       register_properties ();
 
        set_default_fades ();
        set_default_envelope ();
 
        listen_to_my_curves ();
-       listen_to_my_sources ();
+       connect_to_analysis_changed ();
+       connect_to_header_position_offset_changed ();
 }
 
-/* constructor for use by derived types only */
-AudioRegion::AudioRegion (Session& s, nframes_t start, nframes_t length, string name)
-       : Region (s, start, length, name, DataType::AUDIO)
-       , _automatable(s)
-       , _fade_in (new AutomationList(Parameter(FadeInAutomation)))
-       , _fade_out (new AutomationList(Parameter(FadeOutAutomation)))
-       , _envelope (new AutomationList(Parameter(EnvelopeAutomation)))
+/** Constructor for use by derived types only */
+AudioRegion::AudioRegion (Session& s, framepos_t start, framecnt_t len, std::string name)
+       : Region (s, start, len, name, DataType::AUDIO)
+       , AUDIOREGION_STATE_DEFAULT
+       , _automatable (s)
+       , _fade_in (new AutomationList(Evoral::Parameter(FadeInAutomation)))
+       , _fade_out (new AutomationList(Evoral::Parameter(FadeOutAutomation)))
+       , _envelope (new AutomationList(Evoral::Parameter(EnvelopeAutomation)))
+       , _fade_in_suspended (0)
+       , _fade_out_suspended (0)
 {
        init ();
+       assert (_sources.size() == _master_sources.size());
 }
 
-/** Basic AudioRegion constructor (one channel) */
-AudioRegion::AudioRegion (boost::shared_ptr<AudioSource> src, nframes_t start, nframes_t length)
-       : Region (src, start, length, PBD::basename_nosuffix(src->name()), DataType::AUDIO, 0,  Region::Flag(Region::DefaultFlags|Region::External))
-       , _automatable(src->session())
-       , _fade_in (new AutomationList(Parameter(FadeInAutomation)))
-       , _fade_out (new AutomationList(Parameter(FadeOutAutomation)))
-       , _envelope (new AutomationList(Parameter(EnvelopeAutomation)))
+/** Basic AudioRegion constructor */
+AudioRegion::AudioRegion (const SourceList& srcs)
+       : Region (srcs)
+       , AUDIOREGION_STATE_DEFAULT
+       , _automatable(srcs[0]->session())
+       , _fade_in (new AutomationList(Evoral::Parameter(FadeInAutomation)))
+       , _fade_out (new AutomationList(Evoral::Parameter(FadeOutAutomation)))
+       , _envelope (new AutomationList(Evoral::Parameter(EnvelopeAutomation)))
+       , _fade_in_suspended (0)
+       , _fade_out_suspended (0)
 {
-       boost::shared_ptr<AudioFileSource> afs = boost::dynamic_pointer_cast<AudioFileSource> (src);
-       if (afs) {
-               afs->HeaderPositionOffsetChanged.connect (mem_fun (*this, &AudioRegion::source_offset_changed));
-       }
-
        init ();
+       assert (_sources.size() == _master_sources.size());
 }
 
-/* Basic AudioRegion constructor (one channel) */
-AudioRegion::AudioRegion (boost::shared_ptr<AudioSource> src, nframes_t start, nframes_t length, const string& name, layer_t layer, Flag flags)
-       : Region (src, start, length, name, DataType::AUDIO, layer, flags)
-       , _automatable(src->session())
-       , _fade_in (new AutomationList(Parameter(FadeInAutomation)))
-       , _fade_out (new AutomationList(Parameter(FadeOutAutomation)))
-       , _envelope (new AutomationList(Parameter(EnvelopeAutomation)))
+AudioRegion::AudioRegion (boost::shared_ptr<const AudioRegion> other, nframes64_t offset, bool offset_relative)
+       : Region (other, offset, offset_relative)
+       , AUDIOREGION_COPY_STATE (other)
+       , _automatable (other->session())
+       , _fade_in (new AutomationList (*other->_fade_in))
+       , _fade_out (new AutomationList (*other->_fade_out))
+         /* As far as I can see, the _envelope's times are relative to region position, and have nothing
+            to do with sources (and hence _start).  So when we copy the envelope, we just use the supplied offset.
+         */
+       , _envelope (new AutomationList (*other->_envelope, offset, other->_length))
+       , _fade_in_suspended (0)
+       , _fade_out_suspended (0)
 {
-       boost::shared_ptr<AudioFileSource> afs = boost::dynamic_pointer_cast<AudioFileSource> (src);
-       if (afs) {
-               afs->HeaderPositionOffsetChanged.connect (mem_fun (*this, &AudioRegion::source_offset_changed));
-       }
+       /* don't use init here, because we got fade in/out from the other region
+       */
+       register_properties ();
+       listen_to_my_curves ();
+       connect_to_analysis_changed ();
+       connect_to_header_position_offset_changed ();
 
-       init ();
+       assert(_type == DataType::AUDIO);
+       assert (_sources.size() == _master_sources.size());
 }
 
-/* Basic AudioRegion constructor (many channels) */
-AudioRegion::AudioRegion (const SourceList& srcs, nframes_t start, nframes_t length, const string& name, layer_t layer, Flag flags)
-       : Region (srcs, start, length, name, DataType::AUDIO, layer, flags)
-       , _automatable(srcs[0]->session())
-       , _fade_in (new AutomationList(Parameter(FadeInAutomation)))
-       , _fade_out (new AutomationList(Parameter(FadeOutAutomation)))
-       , _envelope (new AutomationList(Parameter(EnvelopeAutomation)))
+AudioRegion::AudioRegion (boost::shared_ptr<const AudioRegion> other, const SourceList& srcs)
+       : Region (boost::static_pointer_cast<const Region>(other), srcs)
+       , AUDIOREGION_COPY_STATE (other)
+       , _automatable (other->session())
+       , _fade_in (new AutomationList (*other->_fade_in))
+       , _fade_out (new AutomationList (*other->_fade_out))
+       , _envelope (new AutomationList (*other->_envelope))
+       , _fade_in_suspended (0)
+       , _fade_out_suspended (0)
 {
-       init ();
-       listen_to_my_sources ();
+       /* make-a-sort-of-copy-with-different-sources constructor (used by audio filter) */
+
+       register_properties ();
+
+       listen_to_my_curves ();
+       connect_to_analysis_changed ();
+       connect_to_header_position_offset_changed ();
+
+       assert (_sources.size() == _master_sources.size());
 }
 
-/** Create a new AudioRegion, that is part of an existing one */
-AudioRegion::AudioRegion (boost::shared_ptr<const AudioRegion> other, nframes_t offset, nframes_t length, const string& name, layer_t layer, Flag flags)
-       : Region (other, offset, length, name, layer, flags)
-       , _automatable(other->session())
-       , _fade_in (new AutomationList(Parameter(FadeInAutomation)))
-       , _fade_out (new AutomationList(Parameter(FadeOutAutomation)))
-       , _envelope (new AutomationList(Parameter(EnvelopeAutomation)))
+AudioRegion::AudioRegion (SourceList& srcs)
+       : Region (srcs)
+       , AUDIOREGION_STATE_DEFAULT
+       , _automatable(srcs[0]->session())
+       , _fade_in (new AutomationList(Evoral::Parameter(FadeInAutomation)))
+       , _fade_out (new AutomationList(Evoral::Parameter(FadeOutAutomation)))
+       , _envelope (new AutomationList(Evoral::Parameter(EnvelopeAutomation)))
+       , _fade_in_suspended (0)
+       , _fade_out_suspended (0)
 {
-       set<boost::shared_ptr<Source> > unique_srcs;
+       init ();
 
-       for (SourceList::const_iterator i= other->_sources.begin(); i != other->_sources.end(); ++i) {
-               _sources.push_back (*i);
+       assert(_type == DataType::AUDIO);
+       assert (_sources.size() == _master_sources.size());
+}
 
-               pair<set<boost::shared_ptr<Source> >::iterator,bool> result;
+AudioRegion::~AudioRegion ()
+{
+}
 
-               result = unique_srcs.insert (*i);
-               
-               if (result.second) {
-                       boost::shared_ptr<AudioFileSource> afs = boost::dynamic_pointer_cast<AudioFileSource> (*i);
-                       if (afs) {
-                               afs->HeaderPositionOffsetChanged.connect (mem_fun (*this, &AudioRegion::source_offset_changed));
-                       }
-               }
+void
+AudioRegion::post_set ()
+{
+       if (!_sync_marked) {
+               _sync_position = _start;
        }
 
        /* return to default fades if the existing ones are too long */
-       init ();
 
-       if (_flags & LeftOfSplit) {
+       if (_left_of_split) {
                if (_fade_in->back()->when >= _length) {
                        set_default_fade_in ();
-               } else {
-                       _fade_in_disabled = other->_fade_in_disabled;
-               }
+               } 
                set_default_fade_out ();
-               _flags = Flag (_flags & ~Region::LeftOfSplit);
+               _left_of_split = false;
        }
 
-       if (_flags & RightOfSplit) {
+       if (_right_of_split) {
                if (_fade_out->back()->when >= _length) {
                        set_default_fade_out ();
-               } else {
-                       _fade_out_disabled = other->_fade_out_disabled;
-               }
+               } 
+
                set_default_fade_in ();
-               _flags = Flag (_flags & ~Region::RightOfSplit);
+               _right_of_split = false;
        }
 
-       _scale_amplitude = other->_scale_amplitude;
-
-       assert(_type == DataType::AUDIO);
-       listen_to_my_sources ();
-}
-
-AudioRegion::AudioRegion (boost::shared_ptr<const AudioRegion> other)
-       : Region (other)
-       , _automatable(other->session())
-       , _fade_in (new AutomationList(Parameter(FadeInAutomation)))
-       , _fade_out (new AutomationList(Parameter(FadeOutAutomation)))
-       , _envelope (new AutomationList(Parameter(EnvelopeAutomation)))
-{
-       assert(_type == DataType::AUDIO);
-       _scale_amplitude = other->_scale_amplitude;
-       _envelope = other->_envelope;
-
-       set_default_fades ();
-       
-       listen_to_my_curves ();
-       listen_to_my_sources ();
+       /* If _length changed, adjust our gain envelope accordingly */
+       _envelope->truncate_end (_length);
 }
 
-AudioRegion::AudioRegion (boost::shared_ptr<AudioSource> src, const XMLNode& node)
-       : Region (src, node)
-       , _automatable(src->session())
-       , _fade_in (new AutomationList(Parameter(FadeInAutomation)))
-       , _fade_out (new AutomationList(Parameter(FadeOutAutomation)))
-       , _envelope (new AutomationList(Parameter(EnvelopeAutomation)))
+void
+AudioRegion::connect_to_analysis_changed ()
 {
-       boost::shared_ptr<AudioFileSource> afs = boost::dynamic_pointer_cast<AudioFileSource> (src);
-       if (afs) {
-               afs->HeaderPositionOffsetChanged.connect (mem_fun (*this, &AudioRegion::source_offset_changed));
-       }
-
-       init ();
-
-       if (set_state (node)) {
-               throw failed_constructor();
+       for (SourceList::const_iterator i = _sources.begin(); i != _sources.end(); ++i) {
+               (*i)->AnalysisChanged.connect_same_thread (*this, boost::bind (&AudioRegion::invalidate_transients, this));
        }
-
-       assert(_type == DataType::AUDIO);
-       listen_to_my_sources ();
 }
 
-AudioRegion::AudioRegion (SourceList& srcs, const XMLNode& node)
-       : Region (srcs, node)
-       , _automatable(srcs[0]->session())
-       , _fade_in (new AutomationList(Parameter(FadeInAutomation)))
-       , _fade_out (new AutomationList(Parameter(FadeOutAutomation)))
-       , _envelope (new AutomationList(Parameter(EnvelopeAutomation)))
+void
+AudioRegion::connect_to_header_position_offset_changed ()
 {
-       init ();
-
-       if (set_state (node)) {
-               throw failed_constructor();
-       }
+       set<boost::shared_ptr<Source> > unique_srcs;
 
-       assert(_type == DataType::AUDIO);
-       listen_to_my_sources ();
-}
+       for (SourceList::const_iterator i = _sources.begin(); i != _sources.end(); ++i) {
 
-AudioRegion::~AudioRegion ()
-{
-}
+                /* connect only once to HeaderPositionOffsetChanged, even if sources are replicated
+                 */
 
-void
-AudioRegion::listen_to_my_sources ()
-{
-       for (SourceList::const_iterator i = _sources.begin(); i != _sources.end(); ++i) {
-               (*i)->AnalysisChanged.connect (mem_fun (*this, &AudioRegion::invalidate_transients));
+               if (unique_srcs.find (*i) == unique_srcs.end ()) {
+                       unique_srcs.insert (*i);
+                       boost::shared_ptr<AudioFileSource> afs = boost::dynamic_pointer_cast<AudioFileSource> (*i);
+                       if (afs) {
+                               afs->HeaderPositionOffsetChanged.connect_same_thread (*this, boost::bind (&AudioRegion::source_offset_changed, this));
+                       }
+               }
        }
 }
 
 void
 AudioRegion::listen_to_my_curves ()
 {
-       _envelope->StateChanged.connect (mem_fun (*this, &AudioRegion::envelope_changed));
-       _fade_in->StateChanged.connect (mem_fun (*this, &AudioRegion::fade_in_changed));
-       _fade_out->StateChanged.connect (mem_fun (*this, &AudioRegion::fade_out_changed));
+       _envelope->StateChanged.connect_same_thread (*this, boost::bind (&AudioRegion::envelope_changed, this));
+       _fade_in->StateChanged.connect_same_thread (*this, boost::bind (&AudioRegion::fade_in_changed, this));
+       _fade_out->StateChanged.connect_same_thread (*this, boost::bind (&AudioRegion::fade_out_changed, this));
 }
 
 void
 AudioRegion::set_envelope_active (bool yn)
 {
        if (envelope_active() != yn) {
-               char buf[64];
-               if (yn) {
-                       snprintf (buf, sizeof (buf), "envelope active");
-                       _flags = Flag (_flags|EnvelopeActive);
-               } else {
-                       snprintf (buf, sizeof (buf), "envelope off");
-                       _flags = Flag (_flags & ~EnvelopeActive);
-               }
-               send_change (EnvelopeActiveChanged);
+               _envelope_active = yn;
+               send_change (PropertyChange (Properties::envelope_active));
        }
 }
 
@@ -279,13 +298,13 @@ ARDOUR::nframes_t
 AudioRegion::read_peaks (PeakData *buf, nframes_t npeaks, nframes_t offset, nframes_t cnt, uint32_t chan_n, double samples_per_unit) const
 {
        if (chan_n >= _sources.size()) {
-               return 0; 
+               return 0;
        }
 
        if (audio_source(chan_n)->read_peaks (buf, npeaks, offset, cnt, samples_per_unit)) {
                return 0;
        } else {
-               if (_scale_amplitude != 1.0) {
+               if (_scale_amplitude != 1.0f) {
                        for (nframes_t n = 0; n < npeaks; ++n) {
                                buf[n].max *= _scale_amplitude;
                                buf[n].min *= _scale_amplitude;
@@ -295,41 +314,51 @@ AudioRegion::read_peaks (PeakData *buf, nframes_t npeaks, nframes_t offset, nfra
        }
 }
 
-nframes64_t
-AudioRegion::read (Sample* buf, nframes64_t position, nframes64_t cnt, int channel) const
+framecnt_t
+AudioRegion::read (Sample* buf, framepos_t timeline_position, framecnt_t cnt, int channel) const
 {
        /* raw read, no fades, no gain, nada */
-       return _read_at (_sources, _length, buf, 0, 0, _position + position, cnt, channel, 0, 0, true);
+       return _read_at (_sources, _length, buf, 0, 0, _position + timeline_position, cnt, channel, 0, 0, ReadOps (0));
 }
 
-nframes_t
-AudioRegion::read_at (Sample *buf, Sample *mixdown_buffer, float *gain_buffer, nframes_t position, 
-                     nframes_t cnt, 
-                     uint32_t chan_n, nframes_t read_frames, nframes_t skip_frames) const
+framecnt_t
+AudioRegion::read_at (Sample *buf, Sample *mixdown_buffer, float *gain_buffer,
+                     framepos_t file_position, framecnt_t cnt, uint32_t chan_n,
+                     framecnt_t read_frames, framecnt_t skip_frames) const
 {
        /* regular diskstream/butler read complete with fades etc */
-       return _read_at (_sources, _length, buf, mixdown_buffer, gain_buffer, position, cnt, chan_n, read_frames, skip_frames, false);
+       return _read_at (_sources, _length, buf, mixdown_buffer, gain_buffer,
+                       file_position, cnt, chan_n, read_frames, skip_frames, ReadOps (~0));
 }
 
-nframes_t
-AudioRegion::master_read_at (Sample *buf, Sample *mixdown_buffer, float *gain_buffer, nframes_t position, 
-                            nframes_t cnt, uint32_t chan_n) const
+framecnt_t
+AudioRegion::master_read_at (Sample *buf, Sample *mixdown_buffer, float *gain_buffer,
+                            framepos_t position, framecnt_t cnt, uint32_t chan_n) const
 {
-       return _read_at (_master_sources, _master_sources.front()->length(), buf, mixdown_buffer, gain_buffer, position, cnt, chan_n, 0, 0);
+       /* do not read gain/scaling/fades and do not count this disk i/o in statistics */
+
+       return _read_at (_master_sources, _master_sources.front()->length(_master_sources.front()->timeline_position()),
+                        buf, mixdown_buffer, gain_buffer, position, cnt, chan_n, 0, 0, ReadOps (0));
 }
 
-nframes_t
-AudioRegion::_read_at (const SourceList& srcs, nframes_t limit,
+framecnt_t
+AudioRegion::_read_at (const SourceList& /*srcs*/, framecnt_t limit,
                       Sample *buf, Sample *mixdown_buffer, float *gain_buffer,
-                      nframes_t position, nframes_t cnt, 
-                      uint32_t chan_n, 
-                      nframes_t read_frames, 
-                      nframes_t skip_frames,
-                      bool raw) const
+                      framepos_t position, 
+                      framecnt_t cnt,
+                      uint32_t chan_n,
+                      framecnt_t /*read_frames*/,
+                      framecnt_t /*skip_frames*/,
+                      ReadOps rops) const
 {
-       nframes_t internal_offset;
-       nframes_t buf_offset;
-       nframes_t to_read;
+       frameoffset_t internal_offset;
+       frameoffset_t buf_offset;
+       framecnt_t to_read;
+       bool raw = (rops == ReadOpsNone);
+
+        if (n_channels() == 0) {
+                return 0;
+        }
 
        if (muted() && !raw) {
                return 0; /* read nothing */
@@ -361,7 +390,7 @@ AudioRegion::_read_at (const SourceList& srcs, nframes_t limit,
                mixdown_buffer += buf_offset;
        }
 
-       if (!raw) {
+       if (rops & ReadOpsCount) {
                _read_data_count = 0;
        }
 
@@ -371,73 +400,83 @@ AudioRegion::_read_at (const SourceList& srcs, nframes_t limit,
                if (src->read (mixdown_buffer, _start + internal_offset, to_read) != to_read) {
                        return 0; /* "read nothing" */
                }
-               
-               if (!raw) {
+
+               if (rops & ReadOpsCount) {
                        _read_data_count += src->read_data_count();
                }
 
        } else {
-               
+
                /* track is N-channel, this region has less channels; silence the ones
                   we don't have.
                */
 
-               memset (mixdown_buffer, 0, sizeof (Sample) * cnt);
+                if (Config->get_replicate_missing_region_channels()) {
+                       /* track is N-channel, this region has less channels, so use a relevant channel
+                        */
+                        
+                        uint32_t channel = n_channels() % chan_n;
+                        boost::shared_ptr<AudioSource> src = audio_source (channel);
 
-               /* no fades required */
+                        if (src->read (mixdown_buffer, _start + internal_offset, to_read) != to_read) {
+                                return 0; /* "read nothing" */
+                        }
 
-               if (!raw) {
-                       goto merge;
-               }
+                        /* adjust read data count appropriately since this was a duplicate read */
+                        src->dec_read_data_count (to_read);
+                } else {
+                        memset (mixdown_buffer, 0, sizeof (Sample) * cnt);
+                }
        }
 
-       /* fade in */
+       if (rops & ReadOpsFades) {
+
+               /* fade in */
+
+               if (_fade_in_active && _session.config.get_use_region_fades()) {
 
-       if (!raw) {
-       
-               if (_flags & FadeIn) {
-                       
                        nframes_t fade_in_length = (nframes_t) _fade_in->back()->when;
-                       
+
                        /* see if this read is within the fade in */
-                       
+
                        if (internal_offset < fade_in_length) {
-                               
+
                                nframes_t fi_limit;
-                               
+
                                fi_limit = min (to_read, fade_in_length - internal_offset);
-                               
+
+
                                _fade_in->curve().get_vector (internal_offset, internal_offset+fi_limit, gain_buffer, fi_limit);
-                               
+
                                for (nframes_t n = 0; n < fi_limit; ++n) {
                                        mixdown_buffer[n] *= gain_buffer[n];
                                }
                        }
                }
-               
+
                /* fade out */
-               
-               if (_flags & FadeOut) {
+
+               if (_fade_out_active && _session.config.get_use_region_fades()) {
 
                        /* see if some part of this read is within the fade out */
-                       
+
                /* .................        >|            REGION
-                                           limit
-                                           
+                                            limit
+
                                  {           }            FADE
                                             fade_out_length
-                                 ^                                          
-                                limit - fade_out_length
+                                 ^
+                                 limit - fade_out_length
                         |--------------|
                         ^internal_offset
                                        ^internal_offset + to_read
-                                      
+
                                       we need the intersection of [internal_offset,internal_offset+to_read] with
                                       [limit - fade_out_length, limit]
-                                      
+
                */
 
-       
+
                        nframes_t fade_out_length = (nframes_t) _fade_out->back()->when;
                        nframes_t fade_interval_start = max(internal_offset, limit-fade_out_length);
                        nframes_t fade_interval_end   = min(internal_offset + to_read, limit);
@@ -448,219 +487,183 @@ AudioRegion::_read_at (const SourceList& srcs, nframes_t limit,
                                nframes_t fo_limit = fade_interval_end - fade_interval_start;
                                nframes_t curve_offset = fade_interval_start - (limit-fade_out_length);
                                nframes_t fade_offset = fade_interval_start - internal_offset;
-                               
+
                                _fade_out->curve().get_vector (curve_offset, curve_offset+fo_limit, gain_buffer, fo_limit);
 
                                for (nframes_t n = 0, m = fade_offset; n < fo_limit; ++n, ++m) {
                                        mixdown_buffer[m] *= gain_buffer[n];
                                }
-                       } 
-                       
-               }
-               
-               /* Regular gain curves */
-               
-               if (envelope_active())  {
-                       _envelope->curve().get_vector (internal_offset, internal_offset + to_read, gain_buffer, to_read);
-                       
-                       if (_scale_amplitude != 1.0f) {
-                               for (nframes_t n = 0; n < to_read; ++n) {
-                                       mixdown_buffer[n] *= gain_buffer[n] * _scale_amplitude;
-                               }
-                       } else {
-                               for (nframes_t n = 0; n < to_read; ++n) {
-                                       mixdown_buffer[n] *= gain_buffer[n];
-                               }
                        }
-               } else if (_scale_amplitude != 1.0f) {
-                       ARDOUR::apply_gain_to_buffer (mixdown_buffer, to_read, _scale_amplitude);
+
                }
-       
-         merge:
-               
-               if (!opaque()) {
-                       
-                       /* gack. the things we do for users.
-                        */
-                       
-                       buf += buf_offset;
-                       
+       }
+
+       /* Regular gain curves and scaling */
+
+       if ((rops & ReadOpsOwnAutomation) && envelope_active())  {
+               _envelope->curve().get_vector (internal_offset, internal_offset + to_read, gain_buffer, to_read);
+
+               if ((rops & ReadOpsOwnScaling) && _scale_amplitude != 1.0f) {
                        for (nframes_t n = 0; n < to_read; ++n) {
-                               buf[n] += mixdown_buffer[n];
+                               mixdown_buffer[n] *= gain_buffer[n] * _scale_amplitude;
                        }
-               } 
+               } else {
+                       for (nframes_t n = 0; n < to_read; ++n) {
+                               mixdown_buffer[n] *= gain_buffer[n];
+                       }
+               }
+       } else if ((rops & ReadOpsOwnScaling) && _scale_amplitude != 1.0f) {
+
+               // XXX this should be using what in 2.0 would have been:
+               // Session::apply_gain_to_buffer (mixdown_buffer, to_read, _scale_amplitude);
+
+               for (nframes_t n = 0; n < to_read; ++n) {
+                       mixdown_buffer[n] *= _scale_amplitude;
+               }
+       }
+
+       if (!opaque() && (buf != mixdown_buffer)) {
+
+               /* gack. the things we do for users.
+                */
+
+               buf += buf_offset;
+
+               for (nframes_t n = 0; n < to_read; ++n) {
+                       buf[n] += mixdown_buffer[n];
+               }
        }
 
        return to_read;
 }
-       
+
 XMLNode&
-AudioRegion::state (bool full)
+AudioRegion::state ()
 {
-       XMLNode& node (Region::state (full));
+       XMLNode& node (Region::state ());
        XMLNode *child;
        char buf[64];
-       char buf2[64];
        LocaleGuard lg (X_("POSIX"));
-       
-       node.add_property ("flags", enum_2_string (_flags));
-
-       snprintf (buf, sizeof(buf), "%.12g", _scale_amplitude);
-       node.add_property ("scale-gain", buf);
-
-       // XXX these should move into Region
-
-       for (uint32_t n=0; n < _sources.size(); ++n) {
-               snprintf (buf2, sizeof(buf2), "source-%d", n);
-               _sources[n]->id().print (buf, sizeof (buf));
-               node.add_property (buf2, buf);
-       }
-
-       for (uint32_t n=0; n < _master_sources.size(); ++n) {
-               snprintf (buf2, sizeof(buf2), "master-source-%d", n);
-               _master_sources[n]->id().print (buf, sizeof (buf));
-               node.add_property (buf2, buf);
-       }
 
        snprintf (buf, sizeof (buf), "%u", (uint32_t) _sources.size());
        node.add_property ("channels", buf);
 
-       if (full) {
-       
-               child = node.add_child (X_("FadeIn"));
-               
-               if ((_flags & DefaultFadeIn)) {
-                       child->add_property (X_("default"), X_("yes"));
-               } else {
-                       child->add_child_nocopy (_fade_in->get_state ());
-               }
+       Stateful::add_properties (node);
 
-               child->add_property (X_("active"), _fade_in_disabled ? X_("no") : X_("yes"));
-               
-               child = node.add_child (X_("FadeOut"));
-               
-               if ((_flags & DefaultFadeOut)) {
-                       child->add_property (X_("default"), X_("yes"));
-               } else {
-                       child->add_child_nocopy (_fade_out->get_state ());
+       child = node.add_child ("Envelope");
+
+       bool default_env = false;
+       
+       // If there are only two points, the points are in the start of the region and the end of the region
+       // so, if they are both at 1.0f, that means the default region.
+       
+       if (_envelope->size() == 2 &&
+           _envelope->front()->value == 1.0f &&
+           _envelope->back()->value==1.0f) {
+               if (_envelope->front()->when == 0 && _envelope->back()->when == _length) {
+                       default_env = true;
                }
-               
-               child->add_property (X_("active"), _fade_out_disabled ? X_("no") : X_("yes"));
        }
        
-       child = node.add_child ("Envelope");
+       if (default_env) {
+               child->add_property ("default", "yes");
+       } else {
+               child->add_child_nocopy (_envelope->get_state ());
+       }
 
-       if (full) {
-               bool default_env = false;
-               
-               // If there are only two points, the points are in the start of the region and the end of the region
-               // so, if they are both at 1.0f, that means the default region.
-
-               if (_envelope->size() == 2 &&
-                   _envelope->front()->value == 1.0f &&
-                   _envelope->back()->value==1.0f) {
-                       if (_envelope->front()->when == 0 && _envelope->back()->when == _length) {
-                               default_env = true;
-                       }
-               } 
+       child = node.add_child (X_("FadeIn"));
 
-               if (default_env) {
-                       child->add_property ("default", "yes");
-               } else {
-                       child->add_child_nocopy (_envelope->get_state ());
-               }
-
-       } else {
+       if (_default_fade_in) {
                child->add_property ("default", "yes");
+       } else {
+               child->add_child_nocopy (_fade_in->get_state ());
        }
 
-       if (full && _extra_xml) {
-               node.add_child_copy (*_extra_xml);
+       child = node.add_child (X_("FadeOut"));
+
+       if (_default_fade_out) {
+               child->add_property ("default", "yes");
+       } else {
+               child->add_child_nocopy (_fade_out->get_state ());
        }
 
        return node;
 }
 
 int
-AudioRegion::set_live_state (const XMLNode& node, Change& what_changed, bool send)
+AudioRegion::_set_state (const XMLNode& node, int version, PropertyChange& what_changed, bool send)
 {
        const XMLNodeList& nlist = node.children();
        const XMLProperty *prop;
        LocaleGuard lg (X_("POSIX"));
+       boost::shared_ptr<Playlist> the_playlist (_playlist.lock());    
 
-       Region::set_live_state (node, what_changed, false);
+       suspend_property_changes ();
 
-       uint32_t old_flags = _flags;
-
-       if ((prop = node.property ("flags")) != 0) {
-               _flags = Flag (string_2_enum (prop->value(), _flags));
+       if (the_playlist) {
+               the_playlist->freeze ();
+       }
 
-               //_flags = Flag (strtol (prop->value().c_str(), (char **) 0, 16));
 
-               _flags = Flag (_flags & ~Region::LeftOfSplit);
-               _flags = Flag (_flags & ~Region::RightOfSplit);
-       }
+       /* this will set all our State members and stuff controlled by the Region.
+          It should NOT send any changed signals - that is our responsibility.
+       */
 
-       if ((old_flags ^ _flags) & Muted) {
-               what_changed = Change (what_changed|MuteChanged);
-       }
-       if ((old_flags ^ _flags) & Opaque) {
-               what_changed = Change (what_changed|OpacityChanged);
-       }
-       if ((old_flags ^ _flags) & Locked) {
-               what_changed = Change (what_changed|LockChanged);
-       }
+       Region::_set_state (node, version, what_changed, false);
 
        if ((prop = node.property ("scale-gain")) != 0) {
-               _scale_amplitude = atof (prop->value().c_str());
-               what_changed = Change (what_changed|ScaleAmplitudeChanged);
-       } else {
-               _scale_amplitude = 1.0;
+               float a = atof (prop->value().c_str());
+               if (a != _scale_amplitude) {
+                       _scale_amplitude = a;
+                       what_changed.add (Properties::scale_amplitude);
+               }
        }
-       
-       /* Now find envelope description and other misc child items */
-                               
+
+       /* Now find envelope description and other related child items */
+
+        _envelope->freeze ();
+
        for (XMLNodeConstIterator niter = nlist.begin(); niter != nlist.end(); ++niter) {
-               
                XMLNode *child;
                XMLProperty *prop;
-               
+
                child = (*niter);
-               
+
                if (child->name() == "Envelope") {
 
                        _envelope->clear ();
 
-                       if ((prop = child->property ("default")) != 0 || _envelope->set_state (*child)) {
+                       if ((prop = child->property ("default")) != 0 || _envelope->set_state (*child, version)) {
                                set_default_envelope ();
                        }
 
                        _envelope->set_max_xval (_length);
                        _envelope->truncate_end (_length);
 
+
                } else if (child->name() == "FadeIn") {
-                       
+
                        _fade_in->clear ();
-                       
+
                        if ((prop = child->property ("default")) != 0 || (prop = child->property ("steepness")) != 0) {
                                set_default_fade_in ();
                        } else {
                                XMLNode* grandchild = child->child ("AutomationList");
                                if (grandchild) {
-                                       _fade_in->set_state (*grandchild);
+                                       _fade_in->set_state (*grandchild, version);
                                }
                        }
 
                        if ((prop = child->property ("active")) != 0) {
-                               if (prop->value() == "yes") {
+                               if (string_is_affirmative (prop->value())) {
                                        set_fade_in_active (true);
                                } else {
-                                       set_fade_in_active (true);
+                                       set_fade_in_active (false);
                                }
                        }
 
                } else if (child->name() == "FadeOut") {
-                       
+
                        _fade_out->clear ();
 
                        if ((prop = child->property ("default")) != 0 || (prop = child->property ("steepness")) != 0) {
@@ -668,37 +671,40 @@ AudioRegion::set_live_state (const XMLNode& node, Change& what_changed, bool sen
                        } else {
                                XMLNode* grandchild = child->child ("AutomationList");
                                if (grandchild) {
-                                       _fade_out->set_state (*grandchild);
-                               } 
+                                       _fade_out->set_state (*grandchild, version);
+                               }
                        }
 
                        if ((prop = child->property ("active")) != 0) {
-                               if (prop->value() == "yes") {
+                               if (string_is_affirmative (prop->value())) {
                                        set_fade_out_active (true);
                                } else {
                                        set_fade_out_active (false);
                                }
                        }
 
-               } 
+               }
        }
 
+        _envelope->thaw ();
+       resume_property_changes ();
+        
        if (send) {
                send_change (what_changed);
        }
 
+       if (the_playlist) {
+               the_playlist->thaw ();
+       }
+
        return 0;
 }
 
 int
-AudioRegion::set_state (const XMLNode& node)
+AudioRegion::set_state (const XMLNode& node, int version)
 {
-       /* Region::set_state() calls the virtual set_live_state(),
-          which will get us back to AudioRegion::set_live_state()
-          to handle the relevant stuff.
-       */
-
-       return Region::set_state (node);
+       PropertyChange what_changed;
+       return _set_state (node, version, what_changed, true);
 }
 
 void
@@ -714,9 +720,19 @@ AudioRegion::set_fade_out_shape (FadeShape shape)
 }
 
 void
-AudioRegion::set_fade_in (FadeShape shape, nframes_t len)
+AudioRegion::set_fade_in (boost::shared_ptr<AutomationList> f)
 {
-       _fade_in->freeze ();
+        _fade_in->freeze ();
+       *_fade_in = *f;
+        _fade_in->thaw ();
+       
+       send_change (PropertyChange (Properties::fade_in));
+}
+
+void
+AudioRegion::set_fade_in (FadeShape shape, framecnt_t len)
+{
+        _fade_in->freeze ();
        _fade_in->clear ();
 
        switch (shape) {
@@ -767,16 +783,23 @@ AudioRegion::set_fade_in (FadeShape shape, nframes_t len)
                break;
        }
 
-       _fade_in->thaw ();
-       _fade_in_shape = shape;
+        _fade_in->thaw ();
+}
 
-       send_change (FadeInChanged);
+void
+AudioRegion::set_fade_out (boost::shared_ptr<AutomationList> f)
+{
+        _fade_out->freeze ();
+       *_fade_out = *f;
+        _fade_out->thaw ();
+
+       send_change (PropertyChange (Properties::fade_in));
 }
 
 void
-AudioRegion::set_fade_out (FadeShape shape, nframes_t len)
+AudioRegion::set_fade_out (FadeShape shape, framecnt_t len)
 {
-       _fade_out->freeze ();
+        _fade_out->freeze ();
        _fade_out->clear ();
 
        switch (shape) {
@@ -825,14 +848,11 @@ AudioRegion::set_fade_out (FadeShape shape, nframes_t len)
                break;
        }
 
-       _fade_out->thaw ();
-       _fade_out_shape = shape;
-
-       send_change (FadeOutChanged);
+        _fade_out->thaw ();
 }
 
 void
-AudioRegion::set_fade_in_length (nframes_t len)
+AudioRegion::set_fade_in_length (framecnt_t len)
 {
        if (len > _length) {
                len = _length - 1;
@@ -841,13 +861,13 @@ AudioRegion::set_fade_in_length (nframes_t len)
        bool changed = _fade_in->extend_to (len);
 
        if (changed) {
-               _flags = Flag (_flags & ~DefaultFadeIn);
-               send_change (FadeInChanged);
+               _default_fade_in = false;
+               send_change (PropertyChange (Properties::fade_in));
        }
 }
 
 void
-AudioRegion::set_fade_out_length (nframes_t len)
+AudioRegion::set_fade_out_length (framecnt_t len)
 {
        if (len > _length) {
                len = _length - 1;
@@ -856,70 +876,61 @@ AudioRegion::set_fade_out_length (nframes_t len)
        bool changed =  _fade_out->extend_to (len);
 
        if (changed) {
-               _flags = Flag (_flags & ~DefaultFadeOut);
-               send_change (FadeOutChanged);
+               _default_fade_out = false;
+               send_change (PropertyChange (Properties::fade_out));
        }
 }
 
 void
 AudioRegion::set_fade_in_active (bool yn)
 {
-       if (yn == (_flags & FadeIn)) {
+       if (yn == _fade_in_active) {
                return;
        }
-       if (yn) {
-               _flags = Flag (_flags|FadeIn);
-       } else {
-               _flags = Flag (_flags & ~FadeIn);
-       }
 
-       send_change (FadeInActiveChanged);
+       _fade_in_active = yn;
+       send_change (PropertyChange (Properties::fade_in_active));
 }
 
 void
 AudioRegion::set_fade_out_active (bool yn)
 {
-       if (yn == (_flags & FadeOut)) {
+       if (yn == _fade_out_active) {
                return;
        }
-       if (yn) {
-               _flags = Flag (_flags | FadeOut);
-       } else {
-               _flags = Flag (_flags & ~FadeOut);
-       }
-
-       send_change (FadeOutActiveChanged);
+       _fade_out_active = yn;
+       send_change (PropertyChange (Properties::fade_out_active));
 }
 
 bool
 AudioRegion::fade_in_is_default () const
 {
-       return _fade_in_shape == Linear && _fade_in->back()->when == 64;
+       return _fade_in->size() == 2 && _fade_in->front()->when == 0 && _fade_in->back()->when == 64;
 }
 
 bool
 AudioRegion::fade_out_is_default () const
 {
-       return _fade_out_shape == Linear && _fade_out->back()->when == 64;
+       return _fade_out->size() == 2 && _fade_out->front()->when == 0 && _fade_out->back()->when == 64;
 }
 
 void
 AudioRegion::set_default_fade_in ()
 {
+       _fade_in_suspended = 0;
        set_fade_in (Linear, 64);
 }
 
 void
 AudioRegion::set_default_fade_out ()
 {
+       _fade_out_suspended = 0;
        set_fade_out (Linear, 64);
 }
 
 void
 AudioRegion::set_default_fades ()
 {
-       _fade_in_disabled = 0;
-       _fade_out_disabled = 0;
        set_default_fade_in ();
        set_default_fade_out ();
 }
@@ -927,35 +938,38 @@ AudioRegion::set_default_fades ()
 void
 AudioRegion::set_default_envelope ()
 {
-       _envelope->freeze ();
+        _envelope->freeze ();
        _envelope->clear ();
        _envelope->fast_simple_add (0, 1.0f);
        _envelope->fast_simple_add (_length, 1.0f);
-       _envelope->thaw ();
+        _envelope->thaw ();
 }
 
 void
 AudioRegion::recompute_at_end ()
 {
-       /* our length has changed. recompute a new final point by interpolating 
+       /* our length has changed. recompute a new final point by interpolating
           based on the the existing curve.
        */
-       
-       _envelope->freeze ();
+
+        _envelope->freeze ();
        _envelope->truncate_end (_length);
        _envelope->set_max_xval (_length);
-       _envelope->thaw ();
-
+        _envelope->thaw ();
+
+        if (_left_of_split) {
+                set_default_fade_out ();
+                _left_of_split = false;
+        } else if (_fade_out->back()->when > _length) {
+                _fade_out->extend_to (_length);
+                send_change (PropertyChange (Properties::fade_out));
+        }
+        
        if (_fade_in->back()->when > _length) {
                _fade_in->extend_to (_length);
-               send_change (FadeInChanged);
+               send_change (PropertyChange (Properties::fade_in));
        }
-
-       if (_fade_out->back()->when > _length) {
-               _fade_out->extend_to (_length);
-               send_change (FadeOutChanged);
-       }
-}      
+}
 
 void
 AudioRegion::recompute_at_start ()
@@ -964,32 +978,32 @@ AudioRegion::recompute_at_start ()
 
        _envelope->truncate_start (_length);
 
-       if (_fade_in->back()->when > _length) {
+        if (_right_of_split) {
+                set_default_fade_in ();
+                _right_of_split = false;
+        } else if (_fade_in->back()->when > _length) {
                _fade_in->extend_to (_length);
-               send_change (FadeInChanged);
+               send_change (PropertyChange (Properties::fade_in));
        }
 
        if (_fade_out->back()->when > _length) {
                _fade_out->extend_to (_length);
-               send_change (FadeOutChanged);
+               send_change (PropertyChange (Properties::fade_out));
        }
 }
 
 int
-AudioRegion::separate_by_channel (Session& session, vector<boost::shared_ptr<AudioRegion> >& v) const
+AudioRegion::separate_by_channel (Session& /*session*/, vector<boost::shared_ptr<Region> >& v) const
 {
        SourceList srcs;
        string new_name;
-       int n;
+       int n = 0;
 
        if (_sources.size() < 2) {
                return 0;
        }
 
-       n = 0;
-
        for (SourceList::const_iterator i = _sources.begin(); i != _sources.end(); ++i) {
-
                srcs.clear ();
                srcs.push_back (*i);
 
@@ -1006,92 +1020,95 @@ AudioRegion::separate_by_channel (Session& session, vector<boost::shared_ptr<Aud
                        new_name += ('0' + n + 1);
                }
 
-               /* create a copy with just one source. prevent if from being thought of as "whole file" even if 
-                  it covers the entire source file(s).
+               /* create a copy with just one source. prevent if from being thought of as
+                  "whole file" even if it covers the entire source file(s).
                 */
 
-               Flag f = Flag (_flags & ~WholeFile);
+               PropertyList plist;
+               
+               plist.add (Properties::start, _start.val());
+               plist.add (Properties::length, _length.val());
+               plist.add (Properties::name, new_name);
+               plist.add (Properties::layer, _layer.val());
 
-               boost::shared_ptr<Region> r = RegionFactory::create (srcs, _start, _length, new_name, _layer, f);
-               boost::shared_ptr<AudioRegion> ar = boost::dynamic_pointer_cast<AudioRegion> (r);
+               v.push_back(RegionFactory::create (srcs, plist));
+               v.back()->set_whole_file (false);
 
-               v.push_back (ar);
-               
                ++n;
        }
 
        return 0;
 }
 
-nframes_t
-AudioRegion::read_raw_internal (Sample* buf, nframes_t pos, nframes_t cnt) const
+framecnt_t
+AudioRegion::read_raw_internal (Sample* buf, framepos_t pos, framecnt_t cnt, int channel) const
 {
-       return audio_source()->read  (buf, pos, cnt);
+       return audio_source()->read (buf, pos, cnt, channel);
 }
 
 int
-AudioRegion::exportme (Session& session, ARDOUR::ExportSpecification& spec)
+AudioRegion::exportme (Session& /*session*/, ARDOUR::ExportSpecification& /*spec*/)
 {
        // TODO EXPORT
-//     const nframes_t blocksize = 4096;
-//     nframes_t to_read;
-//     int status = -1;
-// 
-//     spec.channels = _sources.size();
-// 
-//     if (spec.prepare (blocksize, session.frame_rate())) {
-//             goto out;
-//     }
-// 
-//     spec.pos = 0;
-//     spec.total_frames = _length;
-// 
-//     while (spec.pos < _length && !spec.stop) {
-//             
-//             
-//             /* step 1: interleave */
-//             
-//             to_read = min (_length - spec.pos, blocksize);
-//             
-//             if (spec.channels == 1) {
-// 
-//                     if (read_raw_internal (spec.dataF, _start + spec.pos, to_read) != to_read) {
-//                             goto out;
-//                     }
-// 
-//             } else {
-// 
-//                     Sample buf[blocksize];
-// 
-//                     for (uint32_t chan = 0; chan < spec.channels; ++chan) {
-//                             
-//                             if (audio_source(chan)->read (buf, _start + spec.pos, to_read) != to_read) {
-//                                     goto out;
-//                             }
-//                             
-//                             for (nframes_t x = 0; x < to_read; ++x) {
-//                                     spec.dataF[chan+(x*spec.channels)] = buf[x];
-//                             }
-//                     }
-//             }
-//             
-//             if (spec.process (to_read)) {
-//                     goto out;
-//             }
-//             
-//             spec.pos += to_read;
-//             spec.progress = (double) spec.pos /_length;
-//             
-//     }
-//     
-//     status = 0;
-// 
-//   out:      
-//     spec.running = false;
-//     spec.status = status;
-//     spec.clear();
-//     
-//     return status;
+//     const nframes_t blocksize = 4096;
+//     nframes_t to_read;
+//     int status = -1;
+//
+//     spec.channels = _sources.size();
+//
+//     if (spec.prepare (blocksize, session.frame_rate())) {
+//             goto out;
+//     }
+//
+//     spec.pos = 0;
+//     spec.total_frames = _length;
+//
+//     while (spec.pos < _length && !spec.stop) {
+//
+//
+//             /* step 1: interleave */
+//
+//             to_read = min (_length - spec.pos, blocksize);
+//
+//             if (spec.channels == 1) {
+//
+//                     if (read_raw_internal (spec.dataF, _start + spec.pos, to_read) != to_read) {
+//                             goto out;
+//                     }
+//
+//             } else {
+//
+//                     Sample buf[blocksize];
+//
+//                     for (uint32_t chan = 0; chan < spec.channels; ++chan) {
+//
+//                             if (audio_source(chan)->read (buf, _start + spec.pos, to_read) != to_read) {
+//                                     goto out;
+//                             }
+//
+//                             for (nframes_t x = 0; x < to_read; ++x) {
+//                                     spec.dataF[chan+(x*spec.channels)] = buf[x];
+//                             }
+//                     }
+//             }
+//
+//             if (spec.process (to_read)) {
+//                     goto out;
+//             }
+//
+//             spec.pos += to_read;
+//             spec.progress = (double) spec.pos /_length;
+//
+//     }
+//
+//     status = 0;
+//
+//  out:
+//     spec.running = false;
+//     spec.status = status;
+//     spec.clear();
+//
+//     return status;
        return 0;
 }
 
@@ -1103,108 +1120,100 @@ AudioRegion::set_scale_amplitude (gain_t g)
        _scale_amplitude = g;
 
        /* tell the diskstream we're in */
-       
+
        if (pl) {
-               pl->Modified();
+               pl->ContentsChanged();
        }
 
        /* tell everybody else */
 
-       send_change (ScaleAmplitudeChanged);
+       send_change (PropertyChange (Properties::scale_amplitude));
 }
 
-void
-AudioRegion::normalize_to (float target_dB)
+/** @return the maximum (linear) amplitude of the region */
+double
+AudioRegion::maximum_amplitude () const
 {
-       const nframes_t blocksize = 64 * 1024;
-       Sample buf[blocksize];
-       nframes_t fpos;
-       nframes_t fend;
-       nframes_t to_read;
+       framepos_t fpos = _start;
+       framepos_t const fend = _start + _length;
        double maxamp = 0;
-       gain_t target = dB_to_coefficient (target_dB);
-
-       if (target == 1.0f) {
-               /* do not normalize to precisely 1.0 (0 dBFS), to avoid making it appear
-                  that we may have clipped.
-               */
-               target -= FLT_EPSILON;
-       }
-
-       fpos = _start;
-       fend = _start + _length;
-
-       /* first pass: find max amplitude */
 
+       framecnt_t const blocksize = 64 * 1024;
+       Sample buf[blocksize];
+       
        while (fpos < fend) {
 
                uint32_t n;
 
-               to_read = min (fend - fpos, blocksize);
+               framecnt_t const to_read = min (fend - fpos, blocksize);
 
                for (n = 0; n < n_channels(); ++n) {
 
                        /* read it in */
 
-                       if (read_raw_internal (buf, fpos, to_read) != to_read) {
-                               return;
+                       if (read_raw_internal (buf, fpos, to_read, 0) != to_read) {
+                               return 0;
                        }
-                       
+
                        maxamp = compute_peak (buf, to_read, maxamp);
                }
 
                fpos += to_read;
-       };
+       }
+
+       return maxamp;
+}
+
+/** Normalize using a given maximum amplitude and target, so that region
+ *  _scale_amplitude becomes target / max_amplitude.
+ */
+void
+AudioRegion::normalize (float max_amplitude, float target_dB)
+{
+       gain_t target = dB_to_coefficient (target_dB);
+
+       if (target == 1.0f) {
+               /* do not normalize to precisely 1.0 (0 dBFS), to avoid making it appear
+                  that we may have clipped.
+               */
+               target -= FLT_EPSILON;
+       }
 
-       if (maxamp == 0.0f) {
+       if (max_amplitude == 0.0f) {
                /* don't even try */
                return;
        }
 
-       if (maxamp == target) {
+       if (max_amplitude == target) {
                /* we can't do anything useful */
                return;
        }
 
-       /* compute scale factor */
-
-       _scale_amplitude = target/maxamp;
-
-       /* tell the diskstream we're in */
-
-       boost::shared_ptr<Playlist> pl (playlist());
-
-       if (pl) {
-               pl->Modified();
-       }
-
-       /* tell everybody else */
-
-       send_change (ScaleAmplitudeChanged);
+       set_scale_amplitude (target / max_amplitude);
 }
 
 void
 AudioRegion::fade_in_changed ()
 {
-       send_change (FadeInChanged);
+       send_change (PropertyChange (Properties::fade_in));
 }
 
 void
 AudioRegion::fade_out_changed ()
 {
-       send_change (FadeOutChanged);
+       send_change (PropertyChange (Properties::fade_out));
 }
 
 void
 AudioRegion::envelope_changed ()
 {
-       send_change (EnvelopeChanged);
+       send_change (PropertyChange (Properties::envelope));
 }
 
 void
 AudioRegion::suspend_fade_in ()
 {
-       if (++_fade_in_disabled == 1) {
+       if (++_fade_in_suspended == 1) {
                if (fade_in_is_default()) {
                        set_fade_in_active (false);
                }
@@ -1214,7 +1223,7 @@ AudioRegion::suspend_fade_in ()
 void
 AudioRegion::resume_fade_in ()
 {
-       if (--_fade_in_disabled == 0 && _fade_in_disabled) {
+       if (--_fade_in_suspended == 0 && _fade_in_suspended) {
                set_fade_in_active (true);
        }
 }
@@ -1222,7 +1231,7 @@ AudioRegion::resume_fade_in ()
 void
 AudioRegion::suspend_fade_out ()
 {
-       if (++_fade_out_disabled == 1) {
+       if (++_fade_out_suspended == 1) {
                if (fade_out_is_default()) {
                        set_fade_out_active (false);
                }
@@ -1232,7 +1241,7 @@ AudioRegion::suspend_fade_out ()
 void
 AudioRegion::resume_fade_out ()
 {
-       if (--_fade_out_disabled == 0 &&_fade_out_disabled) {
+       if (--_fade_out_suspended == 0 &&_fade_out_suspended) {
                set_fade_out_active (true);
        }
 }
@@ -1267,7 +1276,7 @@ AudioRegion::source_offset_changed ()
        if (afs && afs->destructive()) {
                // set_start (source()->natural_position(), this);
                set_position (source()->natural_position(), this);
-       } 
+       }
 }
 
 boost::shared_ptr<AudioSource>
@@ -1277,6 +1286,63 @@ AudioRegion::audio_source (uint32_t n) const
        return boost::dynamic_pointer_cast<AudioSource>(source(n));
 }
 
+int 
+AudioRegion::adjust_transients (nframes64_t delta)
+{
+       for (AnalysisFeatureList::iterator x = _transients.begin(); x != _transients.end(); ++x) {
+               (*x) = (*x) + delta;
+       }
+       
+       send_change (PropertyChange (Properties::valid_transients));
+       
+       return 0;  
+} 
+
+int
+AudioRegion::update_transient (nframes64_t old_position, nframes64_t new_position)
+{
+       for (AnalysisFeatureList::iterator x = _transients.begin(); x != _transients.end(); ++x) {
+               if ((*x) == old_position) {
+                       (*x) = new_position;
+                       send_change (PropertyChange (Properties::valid_transients));
+                       
+                       break;
+               }
+       }
+       
+       return 0;
+}
+
+void
+AudioRegion::add_transient (nframes64_t where)
+{
+       _transients.push_back(where);
+       _valid_transients = true;
+       
+       send_change (PropertyChange (Properties::valid_transients));
+}
+
+void
+AudioRegion::remove_transient (nframes64_t where)
+{
+       _transients.remove(where);
+       _valid_transients = true;
+       
+       send_change (PropertyChange (Properties::valid_transients));
+}
+
+int
+AudioRegion::set_transients (AnalysisFeatureList& results)
+{
+       _transients.clear();
+       _transients = results;
+       _valid_transients = true;
+       
+       send_change (PropertyChange (Properties::valid_transients));
+       
+       return 0;
+}
+
 int
 AudioRegion::get_transients (AnalysisFeatureList& results, bool force_new)
 {
@@ -1292,14 +1358,14 @@ AudioRegion::get_transients (AnalysisFeatureList& results, bool force_new)
        }
 
        SourceList::iterator s;
-       
+
        for (s = _sources.begin() ; s != _sources.end(); ++s) {
                if (!(*s)->has_been_analysed()) {
                        cerr << "For " << name() << " source " << (*s)->name() << " has not been analyzed\n";
                        break;
                }
        }
-       
+
        if (s == _sources.end()) {
                /* all sources are analyzed, merge data from each one */
 
@@ -1314,14 +1380,14 @@ AudioRegion::get_transients (AnalysisFeatureList& results, bool force_new)
                        AnalysisFeatureList::iterator high = upper_bound ((*s)->transients.begin(),
                                                                          (*s)->transients.end(),
                                                                          _start + _length);
-                                                                        
+
                        /* and add them */
 
                        results.insert (results.end(), low, high);
                }
 
                TransientDetector::cleanup_transients (results, pl->session().frame_rate(), 3.0);
-               
+
                /* translate all transients to current position */
 
                for (AnalysisFeatureList::iterator x = results.begin(); x != results.end(); ++x) {
@@ -1337,14 +1403,22 @@ AudioRegion::get_transients (AnalysisFeatureList& results, bool force_new)
 
        /* no existing/complete transient info */
 
+        static bool analyse_dialog_shown = false; /* global per instance of Ardour */
+
        if (!Config->get_auto_analyse_audio()) {
-               pl->session().Dialog (_("\
-You have requested an operation that requires audio analysis.\n\n\
+                if (!analyse_dialog_shown) {
+                        pl->session().Dialog (_("\
+You have requested an operation that requires audio analysis.\n\n       \
 You currently have \"auto-analyse-audio\" disabled, which means\n\
 that transient data must be generated every time it is required.\n\n\
 If you are doing work that will require transient data on a\n\
 regular basis, you should probably enable \"auto-analyse-audio\"\n\
-then quit ardour and restart."));
++then quit ardour and restart.\n\n\
++This dialog will not display again.  But you may notice a slight delay\n\
++in this and future transient-detection operations.\n\
++"));
+                        analyse_dialog_shown = true;
+                }
        }
 
        TransientDetector t (pl->session().frame_rate());
@@ -1364,19 +1438,19 @@ then quit ardour and restart."));
                }
 
                /* translate all transients to give absolute position */
-               
+
                for (AnalysisFeatureList::iterator i = these_results.begin(); i != these_results.end(); ++i) {
                        (*i) += _position;
                }
 
                /* merge */
-               
+
                _transients.insert (_transients.end(), these_results.begin(), these_results.end());
        }
-       
+
        if (!results.empty()) {
                if (existing_results) {
-                       
+
                        /* merge our transients into the existing ones, then clean up
                           those.
                        */
@@ -1400,11 +1474,78 @@ then quit ardour and restart."));
        return 0;
 }
 
+/** Find areas of `silence' within a region.
+ *
+ *  @param threshold Threshold below which signal is considered silence (as a sample value)
+ *  @param min_length Minimum length of silent period to be reported.
+ *  @return Silent periods; first of pair is the offset within the region, second is the length of the period
+ */
+
+std::list<std::pair<frameoffset_t, framecnt_t> >
+AudioRegion::find_silence (Sample threshold, framecnt_t min_length, InterThreadInfo& itt) const
+{
+       framecnt_t const block_size = 64 * 1024;
+       Sample loudest[block_size];
+       Sample buf[block_size];
+
+       framepos_t pos = _start;
+       framepos_t const end = _start + _length - 1;
+
+       std::list<std::pair<frameoffset_t, framecnt_t> > silent_periods;
+
+       bool in_silence = false;
+       frameoffset_t silence_start = 0;
+       bool silence;
+
+       while (pos < end && !itt.cancel) {
+
+               /* fill `loudest' with the loudest absolute sample at each instant, across all channels */
+               memset (loudest, 0, sizeof (Sample) * block_size);
+               for (uint32_t n = 0; n < n_channels(); ++n) {
+
+                       read_raw_internal (buf, pos, block_size, n);
+                       for (framecnt_t i = 0; i < block_size; ++i) {
+                               loudest[i] = max (loudest[i], abs (buf[i]));
+                       }
+               }
+
+               /* now look for silence */
+               for (framecnt_t i = 0; i < block_size; ++i) {
+                       silence = abs (loudest[i]) < threshold;
+                       if (silence && !in_silence) {
+                               /* non-silence to silence */
+                               in_silence = true;
+                               silence_start = pos + i;
+                       } else if (!silence && in_silence) {
+                               /* silence to non-silence */
+                               in_silence = false;
+                               if (pos + i - 1 - silence_start >= min_length) {
+                                       silent_periods.push_back (std::make_pair (silence_start, pos + i - 1));
+                               }
+                       }
+               }
+
+               pos += block_size;
+                itt.progress = (end-pos)/(double)_length;
+       }
+
+       if (in_silence && end - 1 - silence_start >= min_length) {
+               /* last block was silent, so finish off the last period */
+               silent_periods.push_back (std::make_pair (silence_start, end));
+       }
+
+        itt.done = true;
+
+       return silent_periods;
+}
+
+
+
 extern "C" {
 
-       int region_read_peaks_from_c (void *arg, uint32_t npeaks, uint32_t start, uint32_t cnt, intptr_t data, uint32_t n_chan, double samples_per_unit) 
+       int region_read_peaks_from_c (void *arg, uint32_t npeaks, uint32_t start, uint32_t cnt, intptr_t data, uint32_t n_chan, double samples_per_unit)
 {
-       return ((AudioRegion *) arg)->read_peaks ((PeakData *) data, (nframes_t) npeaks, (nframes_t) start, (nframes_t) cnt, n_chan,samples_per_unit);
+       return ((AudioRegion *) arg)->read_peaks ((PeakData *) data, (framecnt_t) npeaks, (framepos_t) start, (framecnt_t) cnt, n_chan,samples_per_unit);
 }
 
 uint32_t region_length_from_c (void *arg)