Consistent Automation evaluation:
authorRobin Gareus <robin@gareus.org>
Sat, 15 Jul 2017 18:45:49 +0000 (20:45 +0200)
committerRobin Gareus <robin@gareus.org>
Sun, 16 Jul 2017 14:53:39 +0000 (16:53 +0200)
Rule #89: The *owner* of each automation-control is responsible to
evaluate automation of automated automation-controls (and emit Changed()
signals to notify the GUI and slaved controls).

This can happen during run(), when the Processor evaluates automation
(eg. PluginInsert does that), but needs to regardless, every cycle.
Emit Changed signal for GainControl

This follow the same concept as PluginInsert: The Changed signal
is called on demand when evaluating automation.

libs/ardour/amp.cc
libs/ardour/ardour/automatable.h
libs/ardour/ardour/processor.h
libs/ardour/automatable.cc
libs/ardour/automation_control.cc
libs/ardour/plugin_insert.cc
libs/ardour/route.cc
libs/ardour/session_process.cc

index 8760c2d42663991993ca073bbb1140eb678a00b6..705e79af73bb0f0b9ebf3ee503ff8e787ec7b496 100644 (file)
@@ -90,6 +90,9 @@ Amp::run (BufferSet& bufs, framepos_t /*start_frame*/, framepos_t /*end_frame*/,
                        gain_t* gab = _gain_automation_buffer;
                        assert (gab);
 
+                       /* see note in PluginInsert::connect_and_run -- emit Changed signal */
+                       _gain_control->set_value_unchecked (gab[0]);
+
                        if (_midi_amp) {
                                for (BufferSet::midi_iterator i = bufs.midi_begin(); i != bufs.midi_end(); ++i) {
                                        MidiBuffer& mb (*i);
@@ -130,6 +133,12 @@ Amp::run (BufferSet& bufs, framepos_t /*start_frame*/, framepos_t /*end_frame*/,
 
                                _current_gain = Amp::apply_gain (bufs, _session.nominal_frame_rate(), nframes, _current_gain, dg, _midi_amp);
 
+                               /* see note in PluginInsert::connect_and_run ()
+                                * set_value_unchecked() won't emit a signal since the value is effectively unchanged
+                                */
+
+                               _gain_control->Changed (false, PBD::Controllable::NoGroup);
+
                        } else if (_current_gain != GAIN_COEFF_UNITY) {
 
                                /* gain has not changed, but its non-unity */
index 1acb8a60dcd11ca6cee52d8c61f49e092aadaf32..bdc0edf4da328675fec32c7c65666aa23d331bab 100644 (file)
@@ -50,7 +50,7 @@ public:
        Automatable(Session&);
        Automatable (const Automatable& other);
 
-        virtual ~Automatable();
+       virtual ~Automatable();
 
        boost::shared_ptr<Evoral::Control> control_factory(const Evoral::Parameter& id);
 
@@ -83,9 +83,11 @@ public:
        virtual bool find_next_event (double start, double end, Evoral::ControlEvent& ev, bool only_active = true) const;
        void clear_controls ();
 
-        virtual void transport_located (framepos_t now);
+       virtual void transport_located (framepos_t now);
        virtual void transport_stopped (framepos_t now);
 
+       virtual void automation_run (framepos_t, pframes_t);
+
        virtual std::string describe_parameter(Evoral::Parameter param);
 
        AutoState get_parameter_automation_state (Evoral::Parameter param);
@@ -103,7 +105,7 @@ public:
 
        PBD::Signal0<void> AutomationStateChanged;
 
-  protected:
+protected:
        Session& _a_session;
 
        void can_automate(Evoral::Parameter);
index 97dda32c2994296768db581069191cff0218d395..9f252433271c539f3bf41100ce19dbeb72d97356 100644 (file)
@@ -77,7 +77,7 @@ class LIBARDOUR_API Processor : public SessionObject, public Automatable, public
         *  if false, the method need not bother writing to @a bufs if it doesn't want to.
         */
        virtual void run (BufferSet& /*bufs*/, framepos_t /*start_frame*/, framepos_t /*end_frame*/, double speed, pframes_t /*nframes*/, bool /*result_required*/) {}
-       virtual void silence (framecnt_t /*nframes*/, framepos_t /*start_frame*/) {}
+       virtual void silence (framecnt_t nframes, framepos_t start_frame) { automation_run (start_frame, nframes); }
 
        virtual void activate ()   { _pending_active = true; ActiveChanged(); }
        virtual void deactivate () { _pending_active = false; ActiveChanged(); }
index 3045e8573c20553c32e660be08c9663dc89d8670..908ca181372f601c1d6ef851c8c9395d241e09d4 100644 (file)
@@ -345,7 +345,7 @@ Automatable::transport_located (framepos_t now)
                boost::shared_ptr<AutomationControl> c
                                = boost::dynamic_pointer_cast<AutomationControl>(li->second);
                if (c) {
-                        boost::shared_ptr<AutomationList> l
+                       boost::shared_ptr<AutomationList> l
                                = boost::dynamic_pointer_cast<AutomationList>(c->list());
 
                        if (l) {
@@ -395,6 +395,19 @@ Automatable::transport_stopped (framepos_t now)
        }
 }
 
+void
+Automatable::automation_run (framepos_t start, pframes_t nframes)
+{
+       for (Controls::iterator li = controls().begin(); li != controls().end(); ++li) {
+               boost::shared_ptr<AutomationControl> c =
+                       boost::dynamic_pointer_cast<AutomationControl>(li->second);
+               if (!c) {
+                       continue;
+               }
+               c->automation_run (start, nframes);
+       }
+}
+
 boost::shared_ptr<Evoral::Control>
 Automatable::control_factory(const Evoral::Parameter& param)
 {
index dbff577ead9db0a259a0edcffd8dfba6f6a3f763..c3ad2481d6ecfe881628c152161a4b8d94565a6a 100644 (file)
@@ -260,6 +260,7 @@ AutomationControl::set_automation_state (AutoState as)
                        }
                } else {
                        AutomationWatch::instance().remove_automation_watch (shared_from_this());
+                       Changed (false, Controllable::NoGroup);
                }
        }
 }
index a29381e6747374ad2f29b6c7e9241912b204edb2..6b733ee1d139a49158a7c8946d212db88bab57cc 100644 (file)
@@ -1170,6 +1170,7 @@ PluginInsert::run (BufferSet& bufs, framepos_t start_frame, framepos_t end_frame
 
        } else {
                bypass (bufs, nframes);
+               automation_run (start_frame, nframes);
                _delaybuffers.flush ();
        }
 
index 34c2b2f8239ef456edaf2501c6ff5e8aa050c9fa..9bd5c49cea04fa10eeec6fd8c328296aea0f4bb6 100644 (file)
@@ -322,7 +322,7 @@ Route::process_output_buffers (BufferSet& bufs,
                return;
        }
 
-       _mute_control->automation_run (start_frame, nframes);
+       automation_run (start_frame, nframes);
 
        /* figure out if we're going to use gain automation */
        if (gain_automation_ok) {
@@ -2980,11 +2980,16 @@ Route::silence_unlocked (framecnt_t nframes)
 
                _output->silence (nframes);
 
+               // update owned automated controllables
+               automation_run (now, nframes);
+
                for (ProcessorList::iterator i = _processors.begin(); i != _processors.end(); ++i) {
                        boost::shared_ptr<PluginInsert> pi;
 
                        if (!_active && (pi = boost::dynamic_pointer_cast<PluginInsert> (*i)) != 0) {
-                               // skip plugins, they don't need anything when we're not active
+                               /* evaluate automated automation controls */
+                               pi->automation_run (now, nframes);
+                               /* skip plugins, they don't need anything when we're not active */
                                continue;
                        }
 
index fabf4d7d017c9b732800bc57f94c9290b64cda5b..f74bfd2d73d5b298d84e05647e1bd63210757d30 100644 (file)
@@ -40,6 +40,8 @@
 #include "ardour/slave.h"
 #include "ardour/ticker.h"
 #include "ardour/types.h"
+#include "ardour/vca.h"
+#include "ardour/vca_manager.h"
 
 #include "midi++/mmc.h"
 
@@ -142,6 +144,11 @@ Session::no_roll (pframes_t nframes)
 
        ltc_tx_send_time_code_for_cycle (_transport_frame, end_frame, _target_transport_speed, _transport_speed, nframes);
 
+       VCAList v = _vca_manager->vcas ();
+       for (VCAList::const_iterator i = v.begin(); i != v.end(); ++i) {
+               (*i)->automation_run (_transport_frame, nframes);
+       }
+
        if (_process_graph) {
                DEBUG_TRACE(DEBUG::ProcessThreads,"calling graph/no-roll\n");
                _process_graph->routes_no_roll( nframes, _transport_frame, end_frame, non_realtime_work_pending(), declick);
@@ -180,6 +187,11 @@ Session::process_routes (pframes_t nframes, bool& need_butler)
        const framepos_t start_frame = _transport_frame;
        const framepos_t end_frame = _transport_frame + floor (nframes * _transport_speed);
 
+       VCAList v = _vca_manager->vcas ();
+       for (VCAList::const_iterator i = v.begin(); i != v.end(); ++i) {
+               (*i)->automation_run (start_frame, nframes);
+       }
+
        if (_process_graph) {
                DEBUG_TRACE(DEBUG::ProcessThreads,"calling graph/process-routes\n");
                if (_process_graph->process_routes (nframes, start_frame, end_frame, declick, need_butler) < 0) {
@@ -225,6 +237,11 @@ Session::silent_process_routes (pframes_t nframes, bool& need_butler)
        const framepos_t start_frame = _transport_frame;
        const framepos_t end_frame = _transport_frame + lrintf(nframes * _transport_speed);
 
+       VCAList v = _vca_manager->vcas ();
+       for (VCAList::const_iterator i = v.begin(); i != v.end(); ++i) {
+               (*i)->automation_run (start_frame, nframes);
+       }
+
        if (_process_graph) {
                _process_graph->silent_process_routes (nframes, start_frame, end_frame, need_butler);
        } else {