Various updates and fixes for Latency Compensation
authorRobin Gareus <robin@gareus.org>
Tue, 19 Sep 2017 00:10:38 +0000 (02:10 +0200)
committerRobin Gareus <robin@gareus.org>
Tue, 19 Sep 2017 00:11:22 +0000 (02:11 +0200)
* centralize signal_latency_at_***_position to processors
* update initial-delay/roll-delay when processor order changes
* consolidate signal-latency calculation: use the same method
  for processor-changes and session's post_playback_latency.
* include relative output-delay in roll-delay
* fix capture processor position & optimize stem-export latency

(roll-delay fixes pending Route:roll() update)

libs/ardour/ardour/processor.h
libs/ardour/ardour/route.h
libs/ardour/ardour/session.h
libs/ardour/luabindings.cc
libs/ardour/processor.cc
libs/ardour/route.cc
libs/ardour/session.cc
libs/ardour/session_export.cc

index fb77943a344de09c08b63d4dbd099c23e88d5b2b..8f617abc19d8d525de840d226d76471f2a93d852 100644 (file)
@@ -70,9 +70,12 @@ class LIBARDOUR_API Processor : public SessionObject, public Automatable, public
 
        virtual samplecnt_t signal_latency() const { return 0; }
 
-       virtual void set_input_latency (samplecnt_t);
+       virtual void set_input_latency (samplecnt_t cnt) { _input_latency = cnt; }
        samplecnt_t input_latency () const { return _input_latency; }
 
+       virtual void set_output_latency (samplecnt_t cnt) { _output_latency = cnt; }
+       samplecnt_t output_latency () const { return _output_latency; }
+
        virtual int set_block_size (pframes_t /*nframes*/) { return 0; }
        virtual bool requires_fixed_sized_buffers() const { return false; }
 
@@ -153,6 +156,7 @@ protected:
        PluginPinWindowProxy *_pinmgr_proxy;
        SessionObject* _owner;
        samplecnt_t _input_latency;
+       samplecnt_t _output_latency;
 };
 
 } // namespace ARDOUR
index 829ebd435b8541837f658874b980f95aee995154..1ecf36d445d9fbb7e5123c47e1a9d5eb222857e0 100644 (file)
@@ -339,11 +339,11 @@ public:
        samplecnt_t set_private_port_latencies (bool playback) const;
        void       set_public_port_latencies (samplecnt_t, bool playback) const;
 
-       samplecnt_t   update_signal_latency();
+       samplecnt_t   update_signal_latency (bool set_initial_delay = false);
        virtual void set_latency_compensation (samplecnt_t);
 
        void set_user_latency (samplecnt_t);
-       samplecnt_t initial_delay() const { return _initial_delay; }
+       samplecnt_t initial_delay() const  { return _initial_delay; }
        samplecnt_t signal_latency() const { return _signal_latency; }
 
        PBD::Signal0<void>       active_changed;
@@ -628,8 +628,6 @@ public:
 
        bool           _active;
        samplecnt_t     _signal_latency;
-       samplecnt_t     _signal_latency_at_amp_position;
-       samplecnt_t     _signal_latency_at_trim_position;
        samplecnt_t     _initial_delay;
        samplecnt_t     _roll_delay;
 
index b052ba5b6948f90d2924c2872272546ed5ececfb..8af452cc88edea4346b07cbb1e08a2604fc24176 100644 (file)
@@ -473,11 +473,13 @@ class LIBARDOUR_API Session : public PBD::StatefulDestructible, public PBD::Scop
        void set_end_is_free (bool);
        int location_name(std::string& result, std::string base = std::string(""));
 
-       pframes_t get_block_size()        const { return current_block_size; }
-       samplecnt_t worst_output_latency () const { return _worst_output_latency; }
-       samplecnt_t worst_input_latency ()  const { return _worst_input_latency; }
-       samplecnt_t worst_track_latency ()  const { return _worst_track_latency; }
-       samplecnt_t worst_playback_latency () const { return _worst_output_latency + _worst_track_latency; }
+       pframes_t get_block_size () const            { return current_block_size; }
+       samplecnt_t worst_output_latency ()const     { return _worst_output_latency; }
+       samplecnt_t worst_input_latency () const     { return _worst_input_latency; }
+       samplecnt_t worst_track_latency () const     { return _worst_track_latency; }
+       samplecnt_t worst_track_roll_delay () const  { return _worst_track_roll_delay; }
+       samplecnt_t worst_track_out_latency () const { return _worst_track_out_latency; }
+       samplecnt_t worst_playback_latency  () const { return _worst_output_latency + _worst_track_latency; }
 
        struct SaveAs {
                std::string new_parent_folder;  /* parent folder where new session folder will be created */
@@ -1264,6 +1266,8 @@ class LIBARDOUR_API Session : public PBD::StatefulDestructible, public PBD::Scop
        samplecnt_t              _worst_output_latency;
        samplecnt_t              _worst_input_latency;
        samplecnt_t              _worst_track_latency;
+       samplecnt_t              _worst_track_out_latency;
+       samplecnt_t              _worst_track_roll_delay;
        bool                    _have_captured;
        bool                    _non_soloed_outs_muted;
        bool                    _listening;
index 30beb085149114f4e7c2d498c7fe507fb0b07cac..c64445b5b9b7b1bf484899ff3d1399fecc08d2e7 100644 (file)
@@ -906,6 +906,7 @@ LuaBindings::common (lua_State* L)
                .addFunction ("midi", &IO::midi)
                .addFunction ("port_by_name", &IO::nth)
                .addFunction ("n_ports", &IO::n_ports)
+               .addFunction ("latency", &IO::latency)
                .endClass ()
 
                .deriveWSPtrClass <PannerShell, SessionObject> ("PannerShell")
@@ -1059,6 +1060,8 @@ LuaBindings::common (lua_State* L)
                .addFunction ("trim", &Route::trim)
                .addFunction ("peak_meter", (boost::shared_ptr<PeakMeter> (Route::*)())&Route::peak_meter)
                .addFunction ("set_meter_point", &Route::set_meter_point)
+               .addFunction ("initial_delay", &Route::initial_delay)
+               .addFunction ("signal_latency", &Route::signal_latency)
                .endClass ()
 
                .deriveWSPtrClass <Playlist, SessionObject> ("Playlist")
@@ -2166,6 +2169,8 @@ LuaBindings::common (lua_State* L)
                .addFunction ("worst_output_latency", &Session::worst_output_latency)
                .addFunction ("worst_input_latency", &Session::worst_input_latency)
                .addFunction ("worst_track_latency", &Session::worst_track_latency)
+               .addFunction ("worst_track_roll_delay", &Session::worst_track_roll_delay)
+               .addFunction ("worst_track_out_latency", &Session::worst_track_out_latency)
                .addFunction ("worst_playback_latency", &Session::worst_playback_latency)
                .addFunction ("cfg", &Session::cfg)
                .addFunction ("route_groups", &Session::route_groups)
index 507fa785549090ba676c68e0eba59ee7cc2694b4..06a7c9676f94435f1e2f9f3ce8a7ef09c49a752e 100644 (file)
@@ -69,6 +69,7 @@ Processor::Processor(Session& session, const string& name)
        , _pinmgr_proxy (0)
        , _owner (0)
        , _input_latency (0)
+       , _output_latency (0)
 {
 }
 
@@ -290,10 +291,3 @@ Processor::owner() const
 {
        return _owner;
 }
-
-void
-Processor::set_input_latency (samplecnt_t cnt)
-{
-       _input_latency = cnt;
-}
-
index 029fb99501362aaa048d9b1f6f09a1948f1b89fc..f0bd724e4467e52de4f085434050ee27d8cbb391 100644 (file)
@@ -94,8 +94,6 @@ Route::Route (Session& sess, string name, PresentationInfo::Flag flag, DataType
        , Muteable (sess, name)
        , _active (true)
        , _signal_latency (0)
-       , _signal_latency_at_amp_position (0)
-       , _signal_latency_at_trim_position (0)
        , _initial_delay (0)
        , _roll_delay (0)
        , _disk_io_point (DiskIOPreFader)
@@ -332,14 +330,14 @@ Route::process_output_buffers (BufferSet& bufs,
        if (gain_automation_ok) {
                _amp->set_gain_automation_buffer (_session.gain_automation_buffer ());
                _amp->setup_gain_automation (
-                               start_sample + _signal_latency_at_amp_position,
-                               end_sample + _signal_latency_at_amp_position,
+                               start_sample + _amp->output_latency (),
+                               end_sample + _amp->output_latency (),
                                nframes);
 
                _trim->set_gain_automation_buffer (_session.trim_automation_buffer ());
                _trim->setup_gain_automation (
-                               start_sample + _signal_latency_at_trim_position,
-                               end_sample + _signal_latency_at_trim_position,
+                               start_sample + _trim->output_latency (),
+                               end_sample + _trim->output_latency (),
                                nframes);
        } else {
                _amp->apply_gain_automation (false);
@@ -1809,8 +1807,6 @@ Route::configure_processors_unlocked (ProcessorStreams* err, Glib::Threads::RWLo
        // TODO check for a potential ReaderLock after ReaderLock ??
        Glib::Threads::RWLock::ReaderLock lr (_processor_lock);
 
-       samplecnt_t chain_latency = _input->latency ();
-
        list< pair<ChanCount,ChanCount> >::iterator c = configuration.begin();
        for (ProcessorList::iterator p = _processors.begin(); p != _processors.end(); ++p, ++c) {
 
@@ -1822,9 +1818,6 @@ Route::configure_processors_unlocked (ProcessorStreams* err, Glib::Threads::RWLo
                        return -1;
                }
 
-               (*p)->set_input_latency (chain_latency);
-               chain_latency += (*p)->signal_latency ();
-
                processor_max_streams = ChanCount::max(processor_max_streams, c->first);
                processor_max_streams = ChanCount::max(processor_max_streams, c->second);
 
@@ -2104,6 +2097,11 @@ Route::reorder_processors (const ProcessorList& new_order, ProcessorStreams* err
                g_atomic_int_set (&_pending_process_reorder, 1);
        }
 
+       /* update processor input/output latency
+        * (total signal_latency does not change)
+        */
+       update_signal_latency (true);
+
        return 0;
 }
 
@@ -3679,6 +3677,10 @@ Route::apply_processor_changes_rt ()
        }
        if (changed) {
                set_processor_positions ();
+               /* update processor input/output latency
+                * (total signal_latency does not change)
+                */
+               update_signal_latency (true);
        }
        if (emissions != 0) {
                g_atomic_int_set (&_pending_signals, emissions);
@@ -3851,8 +3853,9 @@ Route::add_export_point()
                Glib::Threads::RWLock::WriterLock lw (_processor_lock);
 
                // this aligns all tracks; but not tracks + busses
-               assert (_session.worst_track_latency () >= _initial_delay);
-               _capturing_processor.reset (new CapturingProcessor (_session, _session.worst_track_latency () - _initial_delay));
+               samplecnt_t latency = _session.worst_track_roll_delay ();
+               assert (latency >= _initial_delay);
+               _capturing_processor.reset (new CapturingProcessor (_session, latency - _initial_delay));
                _capturing_processor->activate ();
 
                configure_processors_unlocked (0, &lw);
@@ -3863,40 +3866,40 @@ Route::add_export_point()
 }
 
 samplecnt_t
-Route::update_signal_latency ()
+Route::update_signal_latency (bool set_initial_delay)
 {
-       samplecnt_t l = _output->user_latency();
-       samplecnt_t lamp = 0;
-       bool before_amp = true;
-       samplecnt_t ltrim = 0;
-       bool before_trim = true;
+       Glib::Threads::RWLock::ReaderLock lm (_processor_lock);
+
+       samplecnt_t l_in  = _input->latency ();
+       samplecnt_t l_out = 0;
 
        for (ProcessorList::iterator i = _processors.begin(); i != _processors.end(); ++i) {
                if ((*i)->active ()) {
-                       l += (*i)->signal_latency ();
-               }
-               if ((*i) == _amp) {
-                       before_amp = false;
-               }
-               if ((*i) == _trim) {
-                       before_amp = false;
-               }
-               if (before_amp) {
-                       lamp = l;
-               }
-               if (before_trim) {
-                       lamp = l;
+                       l_out += (*i)->signal_latency ();
+                       l_in += (*i)->signal_latency ();
                }
+               (*i)->set_input_latency (l_in);
+               (*i)->set_output_latency (l_out);
+       }
+
+       l_out += _output->user_latency();
+
+       for (ProcessorList::iterator i = _processors.begin(); i != _processors.end(); ++i) {
+               (*i)->set_output_latency (l_out - (*i)->output_latency ());
        }
 
-       DEBUG_TRACE (DEBUG::Latency, string_compose ("%1: internal signal latency = %2\n", _name, l));
+       DEBUG_TRACE (DEBUG::Latency, string_compose ("%1: internal signal latency = %2\n", _name, l_out));
 
-       // TODO: (lamp - _signal_latency) to sync to output (read-ahed),  currently _roll_delay shifts this around
-       _signal_latency_at_amp_position = lamp;
-       _signal_latency_at_trim_position = ltrim;
+       _signal_latency = l_out;
 
-       if (_signal_latency != l) {
-               _signal_latency = l;
+       lm.release ();
+
+       if (set_initial_delay) {
+               /* see also Session::post_playback_latency() */
+               set_latency_compensation (_session.worst_track_latency () + _session.worst_track_out_latency () - output ()->latency ());
+       }
+
+       if (_signal_latency != l_out) {
                signal_latency_changed (); /* EMIT SIGNAL */
        }
 
@@ -3914,9 +3917,10 @@ void
 Route::set_latency_compensation (samplecnt_t longest_session_latency)
 {
        samplecnt_t old = _initial_delay;
+       assert (!_disk_reader || _disk_reader->output_latency () <= _signal_latency);
 
-       if (_signal_latency < longest_session_latency) {
-               _initial_delay = longest_session_latency - _signal_latency;
+       if (_disk_reader &&  _signal_latency < longest_session_latency) {
+               _initial_delay = longest_session_latency - (_signal_latency - _disk_reader->input_latency ());
        } else {
                _initial_delay = 0;
        }
@@ -4651,7 +4655,7 @@ Route::setup_invisible_processors ()
 
        ProcessorList::iterator trim = new_processors.end();
 
-       if (_trim && _trim->active()) {
+       if (_trim->active()) {
                assert (!_trim->display_to_user ());
                new_processors.push_front (_trim);
                trim = new_processors.begin();
@@ -4670,11 +4674,6 @@ Route::setup_invisible_processors ()
 
        /* EXPORT PROCESSOR */
 
-       if (_capturing_processor) {
-               assert (!_capturing_processor->display_to_user ());
-               new_processors.push_front (_capturing_processor);
-       }
-
        /* DISK READER & WRITER (for Track objects) */
 
        if (_disk_reader || _disk_writer) {
@@ -4714,6 +4713,18 @@ Route::setup_invisible_processors ()
                }
        }
 
+       if (_capturing_processor) {
+               assert (!_capturing_processor->display_to_user ());
+               ProcessorList::iterator reader_pos = find (new_processors.begin(), new_processors.end(), _disk_reader);
+               if (reader_pos != new_processors.end()) {
+                       /* insert after disk-reader */
+                       new_processors.insert (++reader_pos, _capturing_processor);
+               } else {
+                       new_processors.push_front (_capturing_processor);
+               }
+       }
+
+
        _processors = new_processors;
 
        for (ProcessorList::iterator i = _processors.begin(); i != _processors.end(); ++i) {
index 33ed2c22d3b748d4d5d2b230d6f5d6e18f4beea3..af0f66a889db97ce73fd190cf72eed24e93a5f82 100644 (file)
@@ -199,6 +199,8 @@ Session::Session (AudioEngine &eng,
        , _worst_output_latency (0)
        , _worst_input_latency (0)
        , _worst_track_latency (0)
+       , _worst_track_out_latency (0)
+       , _worst_track_roll_delay (0)
        , _have_captured (false)
        , _non_soloed_outs_muted (false)
        , _listening (false)
@@ -6915,15 +6917,25 @@ Session::post_playback_latency ()
 
        boost::shared_ptr<RouteList> r = routes.reader ();
 
+       _worst_track_out_latency = 0;
+
        for (RouteList::iterator i = r->begin(); i != r->end(); ++i) {
                assert (!(*i)->is_auditioner()); // XXX remove me
-               if ((*i)->active()) {
-                       _worst_track_latency = max (_worst_track_latency, (*i)->update_signal_latency ());
+               if (!(*i)->active()) { continue ; }
+               _worst_track_latency = max (_worst_track_latency, (*i)->update_signal_latency ());
+               if (boost::dynamic_pointer_cast<Track> (*i)) {
+                       _worst_track_out_latency = max (_worst_track_out_latency, (*i)->output ()->latency ());
                }
        }
 
+       _worst_track_roll_delay = 0;
+
        for (RouteList::iterator i = r->begin(); i != r->end(); ++i) {
-               (*i)->set_latency_compensation (_worst_track_latency);
+               if (!(*i)->active()) { continue ; }
+               (*i)->set_latency_compensation (_worst_track_latency + _worst_track_out_latency - (*i)->output ()->latency ());
+               if (boost::dynamic_pointer_cast<Track> (*i)) {
+                       _worst_track_roll_delay = max (_worst_track_roll_delay, (*i)->initial_delay ());
+               }
        }
 }
 
index 4ef304eac7306ecdc76d47fac002fbf4ad9fb996..45e86073cdb5dee80bfc2fc047562f97fc98b5e6 100644 (file)
@@ -141,7 +141,7 @@ Session::start_audio_export (samplepos_t position, bool realtime, bool region_ex
         * (this is all still very [w]hacky. Individual Bus and Track outputs
         * are not aligned but one can select them in the PortExportChannelSelector)
         */
-       _export_latency = worst_track_latency ();
+       _export_latency = worst_track_roll_delay ();
 
        boost::shared_ptr<Route> master = master_out ();
        if (master && comensate_master_latency) {