forward port automation handling changes from 2.x, upto and including about rev 6981...
authorPaul Davis <paul@linuxaudiosystems.com>
Thu, 19 Aug 2010 21:09:40 +0000 (21:09 +0000)
committerPaul Davis <paul@linuxaudiosystems.com>
Thu, 19 Aug 2010 21:09:40 +0000 (21:09 +0000)
git-svn-id: svn://localhost/ardour2/branches/3.0@7653 d708f5d6-7413-0410-9779-e7cbd77b26cf

37 files changed:
gtk2_ardour/automation_controller.cc
gtk2_ardour/gain_meter.cc
gtk2_ardour/generic_pluginui.cc
gtk2_ardour/panner_ui.cc
gtk2_ardour/panner_ui.h
libs/ardour/amp.cc
libs/ardour/ardour/amp.h
libs/ardour/ardour/automatable.h
libs/ardour/ardour/automation_control.h
libs/ardour/ardour/automation_list.h
libs/ardour/ardour/delivery.h
libs/ardour/ardour/internal_return.h
libs/ardour/ardour/internal_send.h
libs/ardour/ardour/meter.h
libs/ardour/ardour/monitor_processor.h
libs/ardour/ardour/panner.h
libs/ardour/ardour/plugin_insert.h
libs/ardour/ardour/port_insert.h
libs/ardour/ardour/processor.h
libs/ardour/ardour/return.h
libs/ardour/ardour/send.h
libs/ardour/automatable.cc
libs/ardour/automation_list.cc
libs/ardour/delivery.cc
libs/ardour/internal_return.cc
libs/ardour/internal_send.cc
libs/ardour/meter.cc
libs/ardour/midi_track.cc
libs/ardour/monitor_processor.cc
libs/ardour/panner.cc
libs/ardour/plugin_insert.cc
libs/ardour/port_insert.cc
libs/ardour/return.cc
libs/ardour/route.cc
libs/ardour/send.cc
libs/evoral/evoral/ControlList.hpp
libs/evoral/src/ControlList.cpp

index f63f2ed70c4fb48bc92c3ab38ce5c9d5f12b90ea..aa23cb2601e860c3707fb608b9610c19c26ff54c 100644 (file)
 */
 
 #include <iomanip>
+
 #include "pbd/error.h"
+
 #include "ardour/automation_list.h"
 #include "ardour/automation_control.h"
 #include "ardour/event_type_map.h"
 #include "ardour/automatable.h"
 #include "ardour/panner.h"
+#include "ardour/session.h"
+
 #include "ardour_ui.h"
 #include "utils.h"
 #include "automation_controller.h"
@@ -121,13 +125,24 @@ AutomationController::value_adjusted()
 void
 AutomationController::start_touch()
 {
-       _controllable->start_touch();
+       _controllable->start_touch (_controllable->session().transport_frame());
 }
 
 void
-AutomationController::end_touch()
+AutomationController::end_touch ()
 {
-       _controllable->stop_touch();
+        if (_controllable->automation_state() == Touch) {
+
+                bool mark = false;
+                double when = 0;
+                
+                if (_controllable->session().transport_rolling()) {
+                        mark = true;
+                        when = _controllable->session().transport_frame();
+                }
+
+                _controllable->stop_touch (mark, when);
+        }
 }
 
 void
index b8a6ad2df9c07d899b1c4b036a0d27f509687147..9ae59f63e6039e7938d5c1fca590f2498cb05a6f 100644 (file)
@@ -593,14 +593,14 @@ GainMeterBase::meter_point_clicked ()
 gint
 GainMeterBase::start_gain_touch (GdkEventButton*)
 {
-       _amp->gain_control()->start_touch ();
+       _amp->gain_control()->start_touch (_amp->session().transport_frame());
        return FALSE;
 }
 
 gint
 GainMeterBase::end_gain_touch (GdkEventButton*)
 {
-       _amp->gain_control()->stop_touch ();
+       _amp->gain_control()->stop_touch (false, _amp->session().transport_frame());
        return FALSE;
 }
 
index 40f8e16ef89d89e838daae1a51f095a54dfcdc1d..5e14ecf034d51e7dff81f1668f354940a5375a1f 100644 (file)
@@ -45,6 +45,7 @@
 #ifdef HAVE_SLV2
 #include "ardour/lv2_plugin.h"
 #endif
+#include "ardour/session.h"
 
 #include <lrdf.h>
 
@@ -603,13 +604,13 @@ GenericPluginUI::build_control_ui (guint32 port_index, boost::shared_ptr<Automat
 void
 GenericPluginUI::start_touch (GenericPluginUI::ControlUI* cui)
 {
-       cui->control->start_touch ();
+       cui->control->start_touch (cui->control->session().transport_frame());
 }
 
 void
 GenericPluginUI::stop_touch (GenericPluginUI::ControlUI* cui)
 {
-       cui->control->stop_touch ();
+       cui->control->stop_touch (false, cui->control->session().transport_frame());
 }
 
 void
index 69387ad3a20d4b569af39fd9987f14dfe3d56869..263a8ec51836d3f9c3f59c85ebcfbffd4bf43ced 100644 (file)
@@ -405,8 +405,10 @@ PannerUI::setup_pan ()
                        boost::shared_ptr<AutomationControl> ac = _panner->pan_control (asz);
 
                        if (asz) {
-                               bc->StartGesture.connect (sigc::mem_fun (*ac, &AutomationControl::start_touch));
-                               bc->StopGesture.connect (sigc::mem_fun (*ac, &AutomationControl::stop_touch));
+                               bc->StartGesture.connect (sigc::bind (sigc::mem_fun (*this, &PannerUI::start_touch), 
+                                                                      boost::weak_ptr<AutomationControl> (ac)));
+                               bc->StopGesture.connect (sigc::bind (sigc::mem_fun (*this, &PannerUI::stop_touch), 
+                                                                     boost::weak_ptr<AutomationControl>(ac)));
                        }
 
                        char buf[64];
@@ -459,6 +461,26 @@ PannerUI::setup_pan ()
        }
 }
 
+void
+PannerUI::start_touch (boost::weak_ptr<AutomationControl> wac)
+{
+        boost::shared_ptr<AutomationControl> ac = wac.lock();
+        if (!ac) {
+                return;
+        }
+        ac->start_touch (ac->session().transport_frame());
+}
+
+void
+PannerUI::stop_touch (boost::weak_ptr<AutomationControl> wac)
+{
+        boost::shared_ptr<AutomationControl> ac = wac.lock();
+        if (!ac) {
+                return;
+        }
+        ac->stop_touch (false, ac->session().transport_frame());
+}
+
 bool
 PannerUI::pan_button_event (GdkEventButton* ev, uint32_t which)
 {
index 4ed8767fb1258f30e7ba964b059a9461eb8d1c62..a1558981f68df4b756efed8408c78f2045e81213 100644 (file)
@@ -45,7 +45,9 @@ namespace ARDOUR {
        class Session;
        class Panner;
        class Delivery;
+        class AutomationControl;
 }
+
 namespace Gtkmm2ext {
        class FastMeter;
 }
@@ -167,6 +169,9 @@ class PannerUI : public Gtk::HBox, public ARDOUR::SessionHandlePtr
        std::string astyle_string (ARDOUR::AutoStyle);
        std::string short_astyle_string (ARDOUR::AutoStyle);
        std::string _astyle_string (ARDOUR::AutoStyle, bool);
+
+        void start_touch (boost::weak_ptr<ARDOUR::AutomationControl>);
+        void stop_touch (boost::weak_ptr<ARDOUR::AutomationControl>);
 };
 
 #endif /* __ardour_gtk_panner_ui_h__ */
index f2448c06aa41a01f0f0f37a0bd2cecbbcc83b2ca..6002a2549da6bfef85f7c281587b0281fef6faba 100644 (file)
@@ -70,7 +70,7 @@ Amp::configure_io (ChanCount in, ChanCount out)
 }
 
 void
-Amp::run (BufferSet& bufs, sframes_t /*start_frame*/, sframes_t /*end_frame*/, nframes_t nframes, bool)
+Amp::run (BufferSet& bufs, framepos_t /*start_frame*/, framepos_t /*end_frame*/, nframes_t nframes, bool)
 {
        if (!_active && !_pending_active) {
                return;
index 52aceaf85fe0a5294098abfe9e8d171a38aef71e..011fef13c0a7a03e530af9388d3832185c6a3139 100644 (file)
@@ -43,7 +43,7 @@ public:
        bool can_support_io_configuration (const ChanCount& in, ChanCount& out) const;
        bool configure_io (ChanCount in, ChanCount out);
 
-       void run (BufferSet& bufs, sframes_t start_frame, sframes_t end_frame, nframes_t nframes, bool);
+       void run (BufferSet& bufs, framepos_t start_frame, framepos_t end_frame, nframes_t nframes, bool);
 
        bool apply_gain() const  { return _apply_gain; }
        void apply_gain(bool yn) { _apply_gain = yn; }
index e0fe38e67ed377d22534988e06fb79efdf5fccf1..469025c69310e84d59ea95a81947200ffef1fefe 100644 (file)
@@ -61,7 +61,7 @@ public:
        void clear_controls ();
 
        virtual void automation_snapshot(nframes_t now, bool force);
-       virtual void transport_stopped (sframes_t now);
+       virtual void transport_stopped (framepos_t now);
 
        virtual std::string describe_parameter(Evoral::Parameter param);
 
index 2d22b9e661255d53a923b24fcc3223581fc3b636..006e74346f734d8a7fd4efd9ff82f41cedffb242 100644 (file)
@@ -64,14 +64,14 @@ public:
                return ((ARDOUR::AutomationList*)_list.get())->set_automation_state(as);
        }
 
-       inline void start_touch() {
+       inline void start_touch(double when) {
                set_touching (true);
-               return ((ARDOUR::AutomationList*)_list.get())->start_touch();
+               return ((ARDOUR::AutomationList*)_list.get())->start_touch(when);
        }
 
-       inline void stop_touch() {
+       inline void stop_touch(bool mark, double when) {
                set_touching (false);
-               return ((ARDOUR::AutomationList*)_list.get())->stop_touch();
+               return ((ARDOUR::AutomationList*)_list.get())->stop_touch(mark, when);
        }
 
        /** Set the value and do the right thing based on automation state
@@ -86,6 +86,8 @@ public:
         double lower() const { return parameter().min(); }
         double upper() const { return parameter().max(); }
 
+        const ARDOUR::Session& session() const { return _session; }
+
 protected:
        ARDOUR::Session& _session;
 };
index acb071cca7b012b3c45c5e113b0f40fe0a989329..9a5420e74bf8f43548a00b2bbdcfab50654a52b5 100644 (file)
@@ -61,19 +61,21 @@ class AutomationList : public PBD::StatefulDestructible, public Evoral::ControlL
        PBD::Signal0<void> automation_style_changed;
 
        bool automation_playback() const {
-               return (_state & Play) || ((_state & Touch) && !_touching);
+               return (_state & Play) || ((_state & Touch) && !touching());
        }
        bool automation_write () const {
-               return (_state & Write) || ((_state & Touch) && _touching);
-       }
+                return ((_state & Write) || ((_state & Touch) && touching()));
+        }
 
        PBD::Signal0<void> StateChanged;
 
        static PBD::Signal1<void,AutomationList*> AutomationListCreated;
 
-       void start_touch ();
-       void stop_touch ();
-       bool touching() const { return _touching; }
+       void start_touch (double when);
+       void stop_touch (bool mark, double when);
+       bool touching() const { return g_atomic_int_get (&_touching); }
+       bool writing() const { return _state == Write; }
+        bool touch_enabled() const { return _state == Touch; }
 
        XMLNode& get_state (); 
        int set_state (const XMLNode &, int version);
@@ -86,9 +88,9 @@ class AutomationList : public PBD::StatefulDestructible, public Evoral::ControlL
 
        void maybe_signal_changed ();
 
-       AutoState _state;
-       AutoStyle _style;
-       bool      _touching;
+       AutoState    _state;
+       AutoStyle    _style;
+       gint         _touching;
 };
 
 } // namespace
index ec808a9820b1285b5d2ceba07f7d36e8304e0fb7..f7cbc29af7d8e17fb169c24f181886c831a58353 100644 (file)
@@ -71,10 +71,7 @@ public:
        /* supplemental method used with MIDI */
 
        void flush_buffers (nframes_t nframes, nframes64_t time);
-       void transport_stopped ();
-
        void no_outs_cuz_we_no_monitor(bool);
-
        void cycle_start (nframes_t);
        void increment_output_offset (nframes_t);
        void transport_stopped (sframes_t frame);
@@ -100,8 +97,8 @@ public:
        void allow_pan_reset ();
 
        uint32_t pans_required() const { return _configured_input.n_audio(); }
-       void start_pan_touch (uint32_t which);
-       void end_pan_touch (uint32_t which);
+       void start_pan_touch (uint32_t which, double when);
+       void end_pan_touch (uint32_t which, bool mark, double when);
 
   protected:
        Role        _role;
index 2b8e6183373c7ad2cdfb58005011fbf9f9c296ef..75de9547522a37d032bf55e86206b380244a3ba9 100644 (file)
@@ -38,7 +38,7 @@ class InternalReturn : public Return
        XMLNode& get_state(void);
        int set_state(const XMLNode&, int version);
 
-       void run (BufferSet& bufs, sframes_t start_frame, sframes_t end_frame, nframes_t nframes, bool);
+       void run (BufferSet& bufs, framepos_t start_frame, framepos_t end_frame, nframes_t nframes, bool);
        bool configure_io (ChanCount in, ChanCount out);
        bool can_support_io_configuration (const ChanCount& in, ChanCount& out) const;
        int  set_block_size (nframes_t);
index 0764982a9302b5ed4dfd3580a5b3517e101679ac..d2a769982c8ef79d655539036b9c9cb88bdc45a1 100644 (file)
@@ -39,7 +39,7 @@ class InternalSend : public Send
        XMLNode& get_state(void);
        int set_state(const XMLNode& node, int version);
        
-       void run (BufferSet& bufs, sframes_t start_frame, sframes_t end_frame, nframes_t nframes, bool);
+       void run (BufferSet& bufs, framepos_t start_frame, framepos_t end_frame, nframes_t nframes, bool);
        bool feeds (boost::shared_ptr<Route> other) const;
        bool can_support_io_configuration (const ChanCount& in, ChanCount& out) const;
        bool configure_io (ChanCount in, ChanCount out);
index 80adcd41560bb3d862c2fd661d48b972daa18787..dad86d518572d0b2937e678eb8437034ad8857e2 100644 (file)
@@ -68,7 +68,7 @@ public:
        void reflect_inputs (const ChanCount& in);
 
        /** Compute peaks */
-       void run (BufferSet& bufs, sframes_t start_frame, sframes_t end_frame, nframes_t nframes, bool);
+       void run (BufferSet& bufs, framepos_t start_frame, framepos_t end_frame, nframes_t nframes, bool);
 
        ChanCount input_streams () const { return current_meters; }
        ChanCount output_streams () const { return current_meters; }
index e453551b0be5bd7d4eaca23229553d9a5ecbae26..0f23d7029fc91c1c5a1fd221f267369bfa6e149b 100644 (file)
@@ -110,7 +110,7 @@ class MonitorProcessor : public Processor
 
         bool display_to_user() const;
 
-       void run (BufferSet& /*bufs*/, sframes_t /*start_frame*/, sframes_t /*end_frame*/, nframes_t /*nframes*/, bool /*result_required*/);
+       void run (BufferSet& /*bufs*/, framepos_t /*start_frame*/, framepos_t /*end_frame*/, nframes_t /*nframes*/, bool /*result_required*/);
 
         XMLNode& state (bool full);
         int set_state (const XMLNode&, int /* version */);
index 62512f6181c1b0f8211525ff22944e43bde6e9c8..087471032d12acb2722ad3fe6a82718253c6950f 100644 (file)
@@ -228,7 +228,7 @@ public:
        bool can_support_io_configuration (const ChanCount& /*in*/, ChanCount& /*out*/) const { return true; };
 
        /// The fundamental Panner function
-       void run (BufferSet& src, BufferSet& dest, sframes_t start_frame, sframes_t end_frames, nframes_t nframes);
+       void run (BufferSet& src, BufferSet& dest, framepos_t start_frame, framepos_t end_frames, nframes_t nframes);
 
        bool bypassed() const { return _bypassed; }
        void set_bypassed (bool yn);
index e4487eb6c08a3d44eccfee0f4583924f62581b31..1fcce61f39ae0ae28eb7e38c2aa58d95dcd5babf 100644 (file)
@@ -52,7 +52,7 @@ class PluginInsert : public Processor
        XMLNode& get_state(void);
        int set_state(const XMLNode&, int version);
 
-       void run (BufferSet& in, sframes_t start_frame, sframes_t end_frame, nframes_t nframes, bool);
+       void run (BufferSet& in, framepos_t start_frame, framepos_t end_frame, nframes_t nframes, bool);
        void silence (nframes_t nframes);
 
        void activate ();
index d30edc71628de5e126a281f8246c66ae4a1e50fc..f4e20a8045f687fce39f84824ddbd2e02eb2aa05 100644 (file)
@@ -50,7 +50,7 @@ class PortInsert : public IOProcessor
        XMLNode& get_state(void);
        int set_state (const XMLNode&, int version);
 
-       void run (BufferSet& bufs, sframes_t start_frame, sframes_t end_frame, nframes_t nframes, bool);
+       void run (BufferSet& bufs, framepos_t start_frame, framepos_t end_frame, nframes_t nframes, bool);
 
        nframes_t signal_latency() const;
 
index 1e65bc58bd5f6225fe22a5375b57ddc17412fe79..55e0cfb2d6f24a1683687aea71393dd403c394c9 100644 (file)
@@ -63,15 +63,13 @@ class Processor : public SessionObject, public Automatable, public Latent
 
        virtual nframes_t signal_latency() const { return 0; }
 
-       virtual void transport_stopped (sframes_t /*frame*/) {}
-
        virtual int set_block_size (nframes_t /*nframes*/) { return 0; }
         virtual bool requires_fixed_sized_buffers() const { return false; }
 
        /** @param result_required true if, on return from this method, bufs is required to contain valid data;
         *  if false, the method need not bother writing to bufs if it doesn't want to.
         */  
-       virtual void run (BufferSet& /*bufs*/, sframes_t /*start_frame*/, sframes_t /*end_frame*/, nframes_t /*nframes*/, bool result_required) {}
+       virtual void run (BufferSet& /*bufs*/, framepos_t /*start_frame*/, framepos_t /*end_frame*/, nframes_t /*nframes*/, bool result_required) {}
        virtual void silence (nframes_t /*nframes*/) {}
 
        virtual void activate ()   { _pending_active = true; ActiveChanged(); }
index 63915e5d7865d8bf3a854455678219ba74d6193a..2dcb6eb270a905cf368e754a8ab339eb361f7742 100644 (file)
@@ -42,7 +42,7 @@ public:
 
        uint32_t bit_slot() const { return _bitslot; }
 
-       void run (BufferSet& bufs, sframes_t start_frame, sframes_t end_frame, nframes_t nframes, bool);
+       void run (BufferSet& bufs, framepos_t start_frame, framepos_t end_frame, nframes_t nframes, bool);
 
        boost::shared_ptr<Amp> amp() const { return _amp; }
        boost::shared_ptr<PeakMeter> meter() const { return _meter; }
index a060363a238a373a78913a2b9cd48fae49975d8e..af806b3c1f147e6368c0df3bee0408d31369724e 100644 (file)
@@ -55,7 +55,7 @@ class Send : public Delivery
 
        uint32_t pans_required() const { return _configured_input.n_audio(); }
 
-       void run (BufferSet& bufs, sframes_t start_frame, sframes_t end_frame, nframes_t nframes, bool);
+       void run (BufferSet& bufs, framepos_t start_frame, framepos_t end_frame, nframes_t nframes, bool);
 
        bool can_support_io_configuration (const ChanCount& in, ChanCount& out) const;
        bool configure_io (ChanCount in, ChanCount out);
index 5ae4e96b4e1f87436e53a0356ba9ed350e6d22b6..68c42ba7b57d21ffa636768bf65f809637c01b87 100644 (file)
@@ -27,6 +27,7 @@
 
 #include "pbd/error.h"
 #include "pbd/enumwriter.h"
+#include "pbd/stacktrace.h"
 
 #include "midi++/names.h"
 
@@ -393,7 +394,7 @@ Automatable::automation_snapshot (nframes_t now, bool force)
                for (Controls::iterator i = controls().begin(); i != controls().end(); ++i) {
                        boost::shared_ptr<AutomationControl> c
                                        = boost::dynamic_pointer_cast<AutomationControl>(i->second);
-                       if (c->automation_write()) {
+                       if (_a_session.transport_rolling() && c->automation_write()) {
                                c->list()->rt_add (now, i->second->user_double());
                        }
                }
@@ -403,20 +404,28 @@ Automatable::automation_snapshot (nframes_t now, bool force)
 }
 
 void
-Automatable::transport_stopped (sframes_t now)
+Automatable::transport_stopped (framepos_t now)
 {
        for (Controls::iterator li = controls().begin(); li != controls().end(); ++li) {
 
                boost::shared_ptr<AutomationControl> c
                                = boost::dynamic_pointer_cast<AutomationControl>(li->second);
-               boost::shared_ptr<AutomationList> l
+                if (c) {
+                        boost::shared_ptr<AutomationList> l
                                = boost::dynamic_pointer_cast<AutomationList>(c->list());
-
-               c->list()->reposition_for_rt_add (now);
-
-               if (c->automation_state() != Off) {
-                       c->set_value(c->list()->eval(now));
-               }
+                        
+                        if (l) {
+                                l->write_pass_finished (now);
+                                
+                                if (l->automation_playback()) {
+                                        c->set_value(c->list()->eval(now));
+                                }
+                                
+                                if (l->automation_state() == Write) {
+                                        l->set_automation_state (Touch);
+                                }
+                        }
+                }
        }
 }
 
index 2270b2eff72905c69fde4660a22a175acc2226ff..53fdb233c6b35f17887aece10c7860bee5e4fcf0 100644 (file)
@@ -53,7 +53,7 @@ AutomationList::AutomationList (Evoral::Parameter id)
 {
        _state = Off;
        _style = Absolute;
-       _touching = false;
+       g_atomic_int_set (&_touching, 0);
 
        create_curve_if_necessary();
 
@@ -67,7 +67,7 @@ AutomationList::AutomationList (const AutomationList& other)
 {
        _style = other._style;
        _state = other._state;
-       _touching = other._touching;
+       g_atomic_int_set (&_touching, other.touching());
 
        create_curve_if_necessary();
 
@@ -80,7 +80,7 @@ AutomationList::AutomationList (const AutomationList& other, double start, doubl
 {
        _style = other._style;
        _state = other._state;
-       _touching = other._touching;
+       g_atomic_int_set (&_touching, other.touching());
 
        create_curve_if_necessary();
 
@@ -94,7 +94,7 @@ AutomationList::AutomationList (const AutomationList& other, double start, doubl
 AutomationList::AutomationList (const XMLNode& node, Evoral::Parameter id)
        : ControlList(id)
 {
-       _touching = false;
+       g_atomic_int_set (&_touching, 0);
        _state = Off;
        _style = Absolute;
 
@@ -180,6 +180,12 @@ AutomationList::set_automation_state (AutoState s)
 {
        if (s != _state) {
                _state = s;
+
+                if (_state == Write) {
+                        Glib::Mutex::Lock lm (ControlList::_lock);
+                        nascent.push_back (new NascentInfo (false));
+                }
+
                automation_state_changed (s); /* EMIT SIGNAL */
        }
 }
@@ -194,17 +200,37 @@ AutomationList::set_automation_style (AutoStyle s)
 }
 
 void
-AutomationList::start_touch ()
+AutomationList::start_touch (double when)
 {
-       _touching = true;
-       _new_value = true;
+        if (_state == Touch) {
+                Glib::Mutex::Lock lm (ControlList::_lock);
+                nascent.push_back (new NascentInfo (true, when));
+        }
+
+       g_atomic_int_set (&_touching, 1);
 }
 
 void
-AutomationList::stop_touch ()
+AutomationList::stop_touch (bool mark, double when)
 {
-       _touching = false;
-       _new_value = false;
+       g_atomic_int_set (&_touching, 0);
+
+        if (_state == Touch) {
+                Glib::Mutex::Lock lm (ControlList::_lock);
+                
+                if (mark) {
+                        nascent.back()->end_time = when;
+                        
+                } else {
+                        
+                        /* nascent info created in start touch but never used. just get rid of it.
+                         */
+                        
+                        NascentInfo* ninfo = nascent.back ();
+                        nascent.erase (nascent.begin());
+                        delete ninfo;
+                }
+        }
 }
 
 void
@@ -247,7 +273,14 @@ AutomationList::state (bool full)
        root->add_property ("interpolation-style", enum_2_string (_interpolation));
 
        if (full) {
-               root->add_property ("state", auto_state_to_string (_state));
+                /* never serialize state with Write enabled - too dangerous
+                   for the user's data
+                */
+                if (_state != Write) {
+                        root->add_property ("state", auto_state_to_string (_state));
+                } else {
+                        root->add_property ("state", auto_state_to_string (Off));
+                }
        } else {
                /* never save anything but Off for automation state to a template */
                root->add_property ("state", auto_state_to_string (Off));
@@ -327,7 +360,6 @@ AutomationList::deserialize_events (const XMLNode& node)
                error << _("automation list: cannot load coordinates from XML, all points ignored") << endmsg;
        } else {
                mark_dirty ();
-               reposition_for_rt_add (0);
                maybe_signal_changed ();
        }
 
@@ -426,6 +458,9 @@ AutomationList::set_state (const XMLNode& node, int version)
 
        if ((prop = node.property (X_("state"))) != 0) {
                _state = string_to_auto_state (prop->value());
+                if (_state == Write) {
+                        _state = Off;
+                }
        } else {
                _state = Off;
        }
@@ -462,7 +497,6 @@ AutomationList::set_state (const XMLNode& node, int version)
                freeze ();
                clear ();
                mark_dirty ();
-               reposition_for_rt_add (0);
                maybe_signal_changed ();
                thaw ();
        }
index d5c982c0cc3248f60d3a5b5fc84cec2ff44646bb..50a87eb000e3d9289b591850b389ab6a531d949d 100644 (file)
@@ -221,7 +221,7 @@ Delivery::configure_io (ChanCount in, ChanCount out)
 }
 
 void
-Delivery::run (BufferSet& bufs, sframes_t start_frame, sframes_t end_frame, nframes_t nframes, bool result_required)
+Delivery::run (BufferSet& bufs, framepos_t start_frame, framepos_t end_frame, nframes_t nframes, bool result_required)
 {
        assert (_output);
 
@@ -418,27 +418,22 @@ Delivery::reset_panners ()
 
 
 void
-Delivery::start_pan_touch (uint32_t which)
+Delivery::start_pan_touch (uint32_t which, double when)
 {
        if (which < _panner->npanners()) {
-               _panner->pan_control(which)->start_touch();
+               _panner->pan_control(which)->start_touch(when);
        }
 }
 
 void
-Delivery::end_pan_touch (uint32_t which)
+Delivery::end_pan_touch (uint32_t which, bool mark, double when)
 {
        if (which < _panner->npanners()) {
-               _panner->pan_control(which)->stop_touch();
+               _panner->pan_control(which)->stop_touch(mark, when);
        }
 
 }
 
-void
-Delivery::transport_stopped (sframes_t frame)
-{
-       _panner->transport_stopped (frame);
-}
 
 void
 Delivery::flush_buffers (nframes_t nframes, nframes64_t time)
@@ -453,15 +448,19 @@ Delivery::flush_buffers (nframes_t nframes, nframes64_t time)
 }
 
 void
-Delivery::transport_stopped ()
+Delivery::transport_stopped (framepos_t now)
 {
-       /* turn off any notes that are on */
+        Processor::transport_stopped (now);
 
-       PortSet& ports (_output->ports());
+       _panner->transport_stopped (now);
 
-       for (PortSet::iterator i = ports.begin(); i != ports.end(); ++i) {
-               (*i).transport_stopped ();
-       }
+        if (_output) {
+                PortSet& ports (_output->ports());
+                
+                for (PortSet::iterator i = ports.begin(); i != ports.end(); ++i) {
+                        (*i).transport_stopped ();
+                }
+        }
 }
 
 gain_t
index e1f39b79a34a236887af470f54a16d5256b6cfcc..db3c68a3529e14bcfca7c99c65e945dc2254b789 100644 (file)
@@ -37,7 +37,7 @@ InternalReturn::InternalReturn (Session& s)
 }
 
 void
-InternalReturn::run (BufferSet& bufs, sframes_t /*start_frame*/, sframes_t /*end_frame*/, nframes_t nframes, bool)
+InternalReturn::run (BufferSet& bufs, framepos_t /*start_frame*/, framepos_t /*end_frame*/, nframes_t nframes, bool)
 {
        if (!_active && !_pending_active) {
                return;
index a1425e3059d998863da2418652e7085644532095..9c4238b134bfcbb852af4982149bb4b64932d42c 100644 (file)
@@ -80,7 +80,7 @@ InternalSend::send_to_going_away ()
 }
 
 void
-InternalSend::run (BufferSet& bufs, sframes_t start_frame, sframes_t end_frame, nframes_t nframes, bool)
+InternalSend::run (BufferSet& bufs, framepos_t start_frame, framepos_t end_frame, nframes_t nframes, bool)
 {
        if ((!_active && !_pending_active) || !target || !_send_to) {
                _meter->reset ();
index 3c53f56db45bcff4f66cd58c3fb891ef1e029735..443146cf135a4c94cfc5174a56b6d6b693fa7171 100644 (file)
@@ -39,7 +39,7 @@ PBD::Signal0<void> Metering::Meter;
  * be set to 0.
  */
 void
-PeakMeter::run (BufferSet& bufs, sframes_t /*start_frame*/, sframes_t /*end_frame*/, nframes_t nframes, bool)
+PeakMeter::run (BufferSet& bufs, framepos_t /*start_frame*/, framepos_t /*end_frame*/, nframes_t nframes, bool)
 {
        if (!_active && !_pending_active) {
                return;
index 25418ec96828bba951a1960d99e271c6df7f47c6..f79ed520c0a17551202f88dbfb16522544721d73 100644 (file)
@@ -396,12 +396,9 @@ MidiTrack::no_roll (nframes_t nframes, sframes_t start_frame, sframes_t end_fram
 void
 MidiTrack::handle_transport_stopped (bool abort, bool did_locate, bool flush_processors)
 {
-
-       _main_outs->transport_stopped ();
        Route::handle_transport_stopped (abort, did_locate, flush_processors);
 }
 
-
 void
 MidiTrack::push_midi_input_to_step_edit_ringbuffer (nframes_t nframes)
 {
index bcdc28a4ae8d507de05ae4df0fda3882fdb51c01..8ea5e70aab6088df661a337d058bd4eed97abefe 100644 (file)
@@ -245,7 +245,7 @@ MonitorProcessor::state (bool full)
 }
 
 void
-MonitorProcessor::run (BufferSet& bufs, sframes_t /*start_frame*/, sframes_t /*end_frame*/, nframes_t nframes, bool /*result_required*/)
+MonitorProcessor::run (BufferSet& bufs, framepos_t /*start_frame*/, framepos_t /*end_frame*/, nframes_t nframes, bool /*result_required*/)
 {
         uint32_t chn = 0;
         gain_t target_gain;
index c400199943c3bf9d89cb0b03f967b027576231f3..3f6b23672ab1b8e1da5cda40edb1162d82bb7fab 100644 (file)
@@ -1489,7 +1489,7 @@ Panner::distribute_no_automation (BufferSet& inbufs, BufferSet& outbufs, nframes
 }
 
 void
-Panner::run (BufferSet& inbufs, BufferSet& outbufs, sframes_t start_frame, sframes_t end_frame, nframes_t nframes)
+Panner::run (BufferSet& inbufs, BufferSet& outbufs, framepos_t start_frame, framepos_t end_frame, nframes_t nframes)
 {
        if (outbufs.count().n_audio() == 0) {
                // Failing to deliver audio we were asked to deliver is a bug
index 73655b24b1b4a1f505baa65f31edefe3a6b3bcd3..d6dfc014cfc713ead82575ad1c6b12db0c41c3ce 100644 (file)
@@ -367,7 +367,7 @@ PluginInsert::silence (nframes_t nframes)
 }
 
 void
-PluginInsert::run (BufferSet& bufs, sframes_t /*start_frame*/, sframes_t /*end_frame*/, nframes_t nframes, bool)
+PluginInsert::run (BufferSet& bufs, framepos_t /*start_frame*/, framepos_t /*end_frame*/, nframes_t nframes, bool)
 {
        if (_active || _pending_active) {
 
index 087d2577ad6c21b88cdb34eff76302225d726525..20c6b127d26f655ef9621a0fc2f89e805be8e536 100644 (file)
@@ -101,7 +101,7 @@ PortInsert::latency() const
 }
 
 void
-PortInsert::run (BufferSet& bufs, sframes_t start_frame, sframes_t end_frame, nframes_t nframes, bool)
+PortInsert::run (BufferSet& bufs, framepos_t start_frame, framepos_t end_frame, nframes_t nframes, bool)
 {
        if (_output->n_ports().n_total() == 0) {
                return;
index 10879d772206cb61172b1ae584f924b7e25cee33..d8c8dd22f8d40d375d02060bd98425c89e31ca1c 100644 (file)
@@ -103,7 +103,7 @@ Return::set_state (const XMLNode& node, int version)
 }
 
 void
-Return::run (BufferSet& bufs, sframes_t start_frame, sframes_t end_frame, nframes_t nframes, bool)
+Return::run (BufferSet& bufs, framepos_t start_frame, framepos_t end_frame, nframes_t nframes, bool)
 {
        if ((!_active && !_pending_active) || _input->n_ports() == ChanCount::ZERO) {
                return;
index 0f9e7ba24197a42df56fd3c07bc62ed7a1ed097b..290d9209d93c1bdda31e46f055396f69c9958c5d 100644 (file)
@@ -2621,7 +2621,7 @@ Route::direct_feeds (boost::shared_ptr<Route> other, bool* only_send)
 void
 Route::handle_transport_stopped (bool /*abort_ignored*/, bool did_locate, bool can_flush_processors)
 {
-       nframes_t now = _session.transport_frame();
+       framepos_t now = _session.transport_frame();
 
        {
                Glib::RWLock::ReaderLock lm (_processor_lock);
@@ -2630,6 +2630,8 @@ Route::handle_transport_stopped (bool /*abort_ignored*/, bool did_locate, bool c
                        automation_snapshot (now, true);
                }
 
+                Automatable::transport_stopped (now);
+
                for (ProcessorList::iterator i = _processors.begin(); i != _processors.end(); ++i) {
 
                        if (Config->get_plugins_stop_with_transport() && can_flush_processors) {
index 4624006ca9b7aac604a196f2eae84e94b0f08eec..9d5d2398c7224489d338c082f546c4510b028296 100644 (file)
@@ -71,7 +71,7 @@ Send::deactivate ()
 }
 
 void
-Send::run (BufferSet& bufs, sframes_t start_frame, sframes_t end_frame, nframes_t nframes, bool)
+Send::run (BufferSet& bufs, framepos_t start_frame, framepos_t end_frame, nframes_t nframes, bool)
 {
        if (_output->n_ports() == ChanCount::ZERO) {
                _meter->reset ();
index 28b9ba2fcd9df946e2399b04dd9cea3dfb1da63a..9d3ba09ef1196f2d36751cf18d70e30c8355c664 100644 (file)
@@ -114,10 +114,10 @@ public:
        bool extend_to (double);
        void slide (iterator before, double distance);
 
-       void reposition_for_rt_add (double when);
        void rt_add (double when, double value);
        void add (double when, double value);
        void fast_simple_add (double when, double value);
+        void merge_nascent (double when);
 
        void reset_range (double start, double end);
        void erase_range (double start, double end);
@@ -130,10 +130,6 @@ public:
        boost::shared_ptr<ControlList> copy (double, double);
        void clear (double, double);
 
-       boost::shared_ptr<ControlList> cut (iterator, iterator);
-       boost::shared_ptr<ControlList> copy (iterator, iterator);
-       void clear (iterator, iterator);
-
        bool paste (ControlList&, double position, float times);
 
        void set_yrange (double min, double max) {
@@ -236,6 +232,11 @@ public:
        InterpolationStyle interpolation() const { return _interpolation; }
        void set_interpolation (InterpolationStyle);
 
+        virtual bool touching() const { return false; }
+        virtual bool writing() const { return false; }
+        virtual bool touch_enabled() const { return false; }
+        void write_pass_finished (double when);
+
        /** Emitted when mark_dirty() is called on this object */
        mutable PBD::Signal0<void> Dirty;
        /** Emitted when our interpolation style changes */
@@ -257,25 +258,37 @@ protected:
 
        void _x_scale (double factor);
 
-       mutable LookupCache _lookup_cache;
-       mutable SearchCache _search_cache;
-
-       Parameter           _parameter;
-       InterpolationStyle  _interpolation;
-       EventList           _events;
-       mutable Glib::Mutex _lock;
-       int8_t              _frozen;
-       bool                _changed_when_thawed;
-       bool                _new_value;
-       double              _max_xval;
-       double              _min_yval;
-       double              _max_yval;
-       double              _default_value;
-       bool                _sort_pending;
-       iterator            _rt_insertion_point;
-       double              _rt_pos;
+       mutable LookupCache   _lookup_cache;
+       mutable SearchCache   _search_cache;
+
+       Parameter             _parameter;
+       InterpolationStyle    _interpolation;
+       EventList             _events;
+       mutable Glib::Mutex   _lock;
+       int8_t                _frozen;
+       bool                  _changed_when_thawed;
+       double                _max_xval;
+       double                _min_yval;
+       double                _max_yval;
+       double                _default_value;
+       bool                  _sort_pending;
 
        Curve* _curve;
+
+        struct NascentInfo {
+            EventList events;
+            bool   is_touch;
+            double start_time;
+            double end_time;
+            
+            NascentInfo (bool touching, double start = -1.0)
+                    : is_touch (touching)
+                    , start_time (start)
+                    , end_time (-1.0) 
+            {}
+        };
+    
+        std::list<NascentInfo*> nascent;
 };
 
 } // namespace Evoral
index 72b032e0cf774f7b53cf05f421bf04c3c44d7c6a..b49b294288e5e8473dd0024ecb86e09f1c409d7c 100644 (file)
@@ -45,7 +45,6 @@ ControlList::ControlList (const Parameter& id)
        _max_yval = id.max();
        _max_xval = 0; // means "no limit"
        _default_value = 0;
-       _rt_insertion_point = _events.end();
        _lookup_cache.left = -1;
        _lookup_cache.range.first = _events.end();
        _search_cache.left = -1;
@@ -64,7 +63,6 @@ ControlList::ControlList (const ControlList& other)
        _max_yval = other._max_yval;
        _max_xval = other._max_xval;
        _default_value = other._default_value;
-       _rt_insertion_point = _events.end();
        _lookup_cache.range.first = _events.end();
        _search_cache.first = _events.end();
        _sort_pending = false;
@@ -87,7 +85,6 @@ ControlList::ControlList (const ControlList& other, double start, double end)
        _max_yval = other._max_yval;
        _max_xval = other._max_xval;
        _default_value = other._default_value;
-       _rt_insertion_point = _events.end();
        _lookup_cache.range.first = _events.end();
        _search_cache.first = _events.end();
        _sort_pending = false;
@@ -110,6 +107,13 @@ ControlList::~ControlList()
        for (EventList::iterator x = _events.begin(); x != _events.end(); ++x) {
                delete (*x);
        }
+        
+       for (list<NascentInfo*>::iterator n = nascent.begin(); n != nascent.end(); ++n) {
+                for (EventList::iterator x = (*n)->events.begin(); x != (*n)->events.end(); ++x) {
+                        delete *x;
+                }
+               delete (*n);
+       }
 
        delete _curve;
 }
@@ -203,7 +207,8 @@ ControlList::extend_to (double when)
        return true;
 }
 
-void ControlList::_x_scale (double factor)
+void 
+ControlList::_x_scale (double factor)
 {
        for (iterator i = _events.begin(); i != _events.end(); ++i) {
                (*i)->when = floor ((*i)->when * factor);
@@ -213,90 +218,165 @@ void ControlList::_x_scale (double factor)
 }
 
 void
-ControlList::reposition_for_rt_add (double /*when*/)
+ControlList::write_pass_finished (double when)
 {
-       _rt_insertion_point = _events.end();
+        merge_nascent (when);
 }
 
 void
-ControlList::rt_add (double when, double value)
+ControlList::merge_nascent (double when)
 {
-       //cerr << "RT: alist " << this << " add " << value << " @ " << when << endl;
-
-       {
-               Glib::Mutex::Lock lm (_lock);
-
-               iterator where;
-               ControlEvent cp (when, 0.0);
-               bool done = false;
-
-               if ((_rt_insertion_point != _events.end()) && ((*_rt_insertion_point)->when < when) ) {
-
-                       /* we have a previous insertion point, so we should delete
-                          everything between it and the position where we are going
-                          to insert this point.
-                       */
-
-                       iterator after = _rt_insertion_point;
-
-                       if (++after != _events.end()) {
-                               iterator far = after;
-
-                               while (far != _events.end()) {
-                                       if ((*far)->when > when) {
-                                               break;
-                                       }
-                                       ++far;
-                               }
-
-                               if (_new_value) {
-                                       where = far;
-                                       _rt_insertion_point = where;
-
-                                       if ((*where)->when == when) {
-                                               (*where)->value = value;
-                                               done = true;
-                                       }
-                               } else {
-                                       where = _events.erase (after, far);
-                               }
-
-                       } else {
-
-                               where = after;
-
-                       }
-
-                       iterator previous = _rt_insertion_point;
-                       --previous;
-
-                       if (_rt_insertion_point != _events.begin() && (*_rt_insertion_point)->value == value && (*previous)->value == value) {
-                               (*_rt_insertion_point)->when = when;
-                               done = true;
-
-                       }
-
-               } else {
+        {
+                Glib::Mutex::Lock lm (_lock);
+
+                if (nascent.empty()) {
+                        return;
+                }
+
+                for (list<NascentInfo*>::iterator n = nascent.begin(); n != nascent.end(); ++n) {
+
+                        NascentInfo* ninfo = *n;
+                        EventList& nascent_events (ninfo->events);
+                        bool need_adjacent_start_clamp;
+                        bool need_adjacent_end_clamp;
+
+                        if (nascent_events.empty()) {
+                                delete ninfo;
+                                continue;
+                        }
+                        
+                        if (ninfo->start_time < 0.0) {
+                                ninfo->start_time = nascent_events.front()->when;
+                        }
+                        
+                        if (ninfo->end_time < 0.0) {
+                                ninfo->end_time = when;
+                        }
+
+                        bool preexisting = !_events.empty();
+
+                        if (!preexisting) {
+                                
+                                _events = nascent_events;
+                                
+                        } else if (ninfo->end_time < _events.front()->when) {
+                                
+                                /* all points in nascent are before the first existing point */
+
+                                _events.insert (_events.begin(), nascent_events.begin(), nascent_events.end());
+                                
+                        } else if (ninfo->start_time > _events.back()->when) {
+                                
+                                /* all points in nascent are after the last existing point */
+
+                                _events.insert (_events.end(), nascent_events.begin(), nascent_events.end());
+                                
+                        } else {
+                                
+                                /* find the range that overaps with nascent events,
+                                   and insert the contents of nascent events.
+                                */
+                                
+                                iterator i;
+                                iterator range_begin = _events.end();
+                                iterator range_end = _events.end();
+                                double end_value = unlocked_eval (ninfo->end_time);
+                                double start_value = unlocked_eval (ninfo->start_time - 1);
+
+                                need_adjacent_end_clamp = true;
+                                need_adjacent_start_clamp = true;
+
+                                for (i = _events.begin(); i != _events.end(); ++i) {
+
+                                        if ((*i)->when == ninfo->start_time) {
+                                                /* existing point at same time, remove it
+                                                   and the consider the next point instead.
+                                                */
+                                                i = _events.erase (i);
+
+                                                if (i == _events.end()) {
+                                                        break;
+                                                }
+
+                                                if (range_begin == _events.end()) {
+                                                        range_begin = i;
+                                                        need_adjacent_start_clamp = false;
+                                                } else {
+                                                        need_adjacent_end_clamp = false;
+                                                }
+                                                
+                                                if ((*i)->when > ninfo->end_time) {
+                                                        range_end = i;
+                                                        break;
+                                                }   
+
+                                        } else if ((*i)->when > ninfo->start_time) {
+                                                
+                                                if (range_begin == _events.end()) {
+                                                        range_begin = i;
+                                                }
+                                                
+                                                if ((*i)->when > ninfo->end_time) {
+                                                        range_end = i;
+                                                        break;
+                                                }
+                                        }
+                                }
+                                
+                                assert (range_begin != _events.end());
+                                
+                                if (range_begin != _events.begin()) {
+                                        /* clamp point before */
+                                        if (need_adjacent_start_clamp) {
+                                                _events.insert (range_begin, new ControlEvent (ninfo->start_time, start_value));
+                                        }
+                                }
+
+                                _events.insert (range_begin, nascent_events.begin(), nascent_events.end());
+
+                                if (range_end != _events.end()) {
+                                        /* clamp point after */
+                                        if (need_adjacent_end_clamp) {
+                                                _events.insert (range_begin, new ControlEvent (ninfo->end_time, end_value));
+                                        }
+                                }
+                                
+                                _events.erase (range_begin, range_end);
+                        }
+
+                        delete ninfo;
+                }
+
+                nascent.clear ();
+
+                if (writing()) {
+                        nascent.push_back (new NascentInfo (false));
+                }
+        }
 
-                       where = lower_bound (_events.begin(), _events.end(), &cp, time_comparator);
+        maybe_signal_changed ();
+}
 
-                       if (where != _events.end()) {
-                               if ((*where)->when == when) {
-                                       (*where)->value = value;
-                                       done = true;
-                               }
-                       }
-               }
+void
+ControlList::rt_add (double when, double value)
+{
+        // this is for automation recording 
+        
+        if (touch_enabled() && !touching()) {
+                return;
+        }
 
-               if (!done) {
-                       _rt_insertion_point = _events.insert (where, new ControlEvent (when, value));
-               }
+       //cerr << "RT: alist " << this << " add " << value << " @ " << when << endl;
 
-               _new_value = false;
-               mark_dirty ();
-       }
+        Glib::Mutex::Lock lm (_lock, Glib::TRY_LOCK);
 
-       maybe_signal_changed ();
+        if (lm.locked()) {
+                assert (!nascent.empty());
+                if (!nascent.back()->events.empty()) {
+                        assert (when > nascent.back()->events.back()->when);
+                }
+                nascent.back()->events.push_back (new ControlEvent (when, value));
+        }
 }
 
 void
@@ -336,7 +416,6 @@ ControlList::add (double when, double value)
                if (insert) {
 
                        _events.insert (insertion_point, new ControlEvent (when, value));
-                       reposition_for_rt_add (0);
 
                }
 
@@ -352,7 +431,6 @@ ControlList::erase (iterator i)
        {
                Glib::Mutex::Lock lm (_lock);
                _events.erase (i);
-               reposition_for_rt_add (0);
                mark_dirty ();
        }
        maybe_signal_changed ();
@@ -364,7 +442,6 @@ ControlList::erase (iterator start, iterator end)
        {
                Glib::Mutex::Lock lm (_lock);
                _events.erase (start, end);
-               reposition_for_rt_add (0);
                mark_dirty ();
        }
        maybe_signal_changed ();
@@ -411,7 +488,6 @@ ControlList::erase_range (double start, double endt)
                erased = erase_range_internal (start, endt, _events);
 
                if (erased) {
-                       reposition_for_rt_add (0);
                        mark_dirty ();
                }
 
@@ -662,7 +738,6 @@ ControlList::truncate_end (double last_coordinate)
                        _events.back()->value = last_val;
                }
 
-               reposition_for_rt_add (0);
                mark_dirty();
        }
 
@@ -762,8 +837,6 @@ ControlList::truncate_start (double overall_length)
                        _events.push_front (new ControlEvent (0, first_legal_value));
                }
 
-               reposition_for_rt_add (0);
-
                mark_dirty();
        }
 
@@ -1115,35 +1188,6 @@ ControlList::rt_safe_earliest_event_linear_unlocked (double start, double& x, do
        }
 }
 
-boost::shared_ptr<ControlList>
-ControlList::cut (iterator start, iterator end)
-{
-       boost::shared_ptr<ControlList> nal = create (_parameter);
-
-       {
-               Glib::Mutex::Lock lm (_lock);
-
-               for (iterator x = start; x != end; ) {
-                       iterator tmp;
-
-                       tmp = x;
-                       ++tmp;
-
-                       nal->_events.push_back (new ControlEvent (**x));
-                       _events.erase (x);
-
-                       reposition_for_rt_add (0);
-
-                       x = tmp;
-               }
-
-               mark_dirty ();
-       }
-
-       maybe_signal_changed ();
-
-       return nal;
-}
 
 /** @param start Start position in model coordinates.
  *  @param end End position in model coordinates.
@@ -1153,15 +1197,16 @@ boost::shared_ptr<ControlList>
 ControlList::cut_copy_clear (double start, double end, int op)
 {
        boost::shared_ptr<ControlList> nal = create (_parameter);
-       
        iterator s, e;
-       bool changed = false;
+        ControlEvent cp (start, 0.0);
 
        {
-               Glib::Mutex::Lock lm (_lock);
+               Glib::Mutex::Lock lm (_lock);
+
+                /* first, determine s & e, two iterators that define the range of points
+                   affected by this operation
+                */
 
-               /* find the first event in our list that is at or before `start' in time */
-               ControlEvent cp (start, 0.0);
                if ((s = lower_bound (_events.begin(), _events.end(), &cp, time_comparator)) == _events.end()) {
                        return nal;
                }
@@ -1170,70 +1215,79 @@ ControlList::cut_copy_clear (double start, double end, int op)
                cp.when = end;
                e = upper_bound (_events.begin(), _events.end(), &cp, time_comparator);
 
-               if (op != 2 && (*s)->when != start) {
-                       nal->_events.push_back (new ControlEvent (0, unlocked_eval (start)));
-               }
 
-               for (iterator x = s; x != e; ) {
-                       iterator tmp = x;
-                       ++tmp;
+                /* if "start" isn't the location of an existing point,
+                   evaluate the curve to get a value for the start. Add a point to
+                   both the existing event list, and if its not a "clear" operation,
+                   to the copy ("nal") as well. 
+
+                   Note that the time positions of the points in each list are different 
+                   because we want the copy ("nal") to have a zero time reference.
+                */
+
+                        
+                /* before we begin any cut/clear operations, get the value of the curve
+                   at "end".
+                */
 
-                       changed = true;
+                double end_value = unlocked_eval (end);
+
+                if ((*s)->when != start) {
+                        
+                        double val = unlocked_eval (start);
+
+                       if (op == 0) { // cut
+                               if (start > _events.front()->when) {
+                                       _events.insert (s, (new ControlEvent (start, val)));
+                               }
+                       }
+                        
+                        if (op != 2) { // ! clear
+                                nal->_events.push_back (new ControlEvent (0, val));
+                        }
+                }
+
+               for (iterator x = s; x != e; ) {
 
                        /* adjust new points to be relative to start, which
                           has been set to zero.
                        */
-
+                       
                        if (op != 2) {
                                nal->_events.push_back (new ControlEvent ((*x)->when - start, (*x)->value));
                        }
 
                        if (op != 1) {
-                               _events.erase (x);
-                       }
-
-                       x = tmp;
+                               x = _events.erase (x);
+                       } else {
+                                ++x;
+                        }
                }
+                
+                if (e == _events.end() || (*e)->when != end) {
 
-               if (op != 2 && nal->_events.back()->when != end - start) {
-                       nal->_events.push_back (new ControlEvent (end - start, unlocked_eval (end)));
-               }
+                        /* only add a boundary point if there is a point after "end"
+                         */
+
+                        if (op == 0 && (e != _events.end() && end < (*e)->when)) { // cut
+                                _events.insert (e, new ControlEvent (end, end_value));
+                        }
 
-               if (changed) {
-                       reposition_for_rt_add (0);
+                        if (op != 2 && (e != _events.end() && end < (*e)->when)) { // cut/copy
+                                nal->_events.push_back (new ControlEvent (end - start, end_value));
+                        }
                }
 
-               mark_dirty ();
+                mark_dirty ();
        }
 
-       maybe_signal_changed ();
+        if (op != 1) {
+                maybe_signal_changed ();
+        }
 
        return nal;
-
 }
 
-boost::shared_ptr<ControlList>
-ControlList::copy (iterator start, iterator end)
-{
-       boost::shared_ptr<ControlList> nal = create (_parameter);
-
-       {
-               Glib::Mutex::Lock lm (_lock);
-
-               for (iterator x = start; x != end; ) {
-                       iterator tmp;
-
-                       tmp = x;
-                       ++tmp;
-
-                       nal->_events.push_back (new ControlEvent (**x));
-
-                       x = tmp;
-               }
-       }
-
-       return nal;
-}
 
 boost::shared_ptr<ControlList>
 ControlList::cut (double start, double end)
@@ -1293,7 +1347,6 @@ ControlList::paste (ControlList& alist, double pos, float /*times*/)
                        }
                }
 
-               reposition_for_rt_add (0);
                mark_dirty ();
        }
 
@@ -1342,7 +1395,6 @@ ControlList::move_ranges (const list< RangeMove<double> >& movements)
                        _sort_pending = true;
                }
 
-               reposition_for_rt_add (0);
                mark_dirty ();
        }