Aux-Send Latency compensation, part 2 & code-consolidation
authorRobin Gareus <robin@gareus.org>
Fri, 29 Sep 2017 02:17:16 +0000 (04:17 +0200)
committerRobin Gareus <robin@gareus.org>
Fri, 29 Sep 2017 03:03:48 +0000 (05:03 +0200)
libs/ardour/ardour/session.h
libs/ardour/route.cc
libs/ardour/session.cc

index 66ab5ce0824d07b1d5f90859b7c52d5f8b003ad2..a725fdc8028b5a3285802d3bf29db96b48c7a2db 100644 (file)
@@ -170,7 +170,7 @@ private:
 /** Ardour Session */
 class LIBARDOUR_API Session : public PBD::StatefulDestructible, public PBD::ScopedConnectionList, public SessionEventManager
 {
-  private:
+private:
        enum SubState {
                PendingDeclickIn      = 0x1,  ///< pending de-click fade-in for start
                PendingDeclickOut     = 0x2,  ///< pending de-click fade-out for stop
@@ -180,7 +180,7 @@ class LIBARDOUR_API Session : public PBD::StatefulDestructible, public PBD::Scop
                PendingLocate         = 0x20,
        };
 
-  public:
+public:
        enum RecordState {
                Disabled = 0,
                Enabled = 1,
@@ -473,12 +473,10 @@ 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_track_out_latency () const { return _worst_track_out_latency; }
-       samplecnt_t worst_playback_latency () const  { return std::max (_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_route_latency () const  { return _worst_route_latency; }
        samplecnt_t worst_latency_preroll () const;
 
        struct SaveAs {
@@ -1207,7 +1205,7 @@ class LIBARDOUR_API Session : public PBD::StatefulDestructible, public PBD::Scop
        void auto_connect_thread_wakeup ();
 
 
-  protected:
+protected:
        friend class AudioEngine;
        void set_block_size (pframes_t nframes);
        void set_sample_rate (samplecnt_t nframes);
@@ -1215,12 +1213,11 @@ class LIBARDOUR_API Session : public PBD::StatefulDestructible, public PBD::Scop
        void reconnect_existing_routes (bool withLock, bool reconnect_master = true, bool reconnect_inputs = true, bool reconnect_outputs = true);
 #endif
 
-  protected:
        friend class Route;
        void schedule_curve_reallocation ();
        void update_latency_compensation (bool force = false);
 
-  private:
+private:
        int  create (const std::string& mix_template, BusProfile*);
        void destroy ();
 
@@ -1262,14 +1259,13 @@ class LIBARDOUR_API Session : public PBD::StatefulDestructible, public PBD::Scop
        CubicInterpolation       interpolation;
 
        bool                     auto_play_legal;
-       samplepos_t              _last_slave_transport_sample;
-       samplecnt_t               maximum_output_latency;
-       samplepos_t              _requested_return_sample;
+       samplepos_t             _last_slave_transport_sample;
+       samplepos_t             _requested_return_sample;
        pframes_t                current_block_size;
-       samplecnt_t              _worst_output_latency;
-       samplecnt_t              _worst_input_latency;
-       samplecnt_t              _worst_track_latency;
-       samplecnt_t              _worst_track_out_latency;
+       samplecnt_t             _worst_output_latency;
+       samplecnt_t             _worst_input_latency;
+       samplecnt_t             _worst_route_latency;
+       uint32_t                _send_latency_changes;
        bool                    _have_captured;
        bool                    _non_soloed_outs_muted;
        bool                    _listening;
@@ -1288,16 +1284,15 @@ class LIBARDOUR_API Session : public PBD::StatefulDestructible, public PBD::Scop
        PBD::ScopedConnection ltc_status_connection;
 
        void initialize_latencies ();
+       void update_latency (bool playback);
+       bool update_route_latency (bool reverse, bool apply_to_delayline);
+
        void set_worst_io_latencies ();
-       void set_worst_playback_latency ();
-       void set_worst_capture_latency ();
-       void set_worst_io_latencies_x (IOChange, void *) {
-               set_worst_io_latencies ();
-       }
-       void post_capture_latency ();
-       void post_playback_latency ();
+       void set_worst_output_latency ();
+       void set_worst_input_latency ();
 
-       void update_latency_compensation_proxy (void* ignored);
+       void send_latency_compensation_change ();
+       void set_worst_io_latencies_x (IOChange, void *);
 
        void ensure_buffers (ChanCount howmany = ChanCount::ZERO);
 
@@ -1793,15 +1788,13 @@ class LIBARDOUR_API Session : public PBD::StatefulDestructible, public PBD::Scop
 
        mutable Glib::Threads::Mutex source_lock;
 
-  public:
+public:
        typedef std::map<PBD::ID,boost::shared_ptr<Source> > SourceMap;
 
-  private:
+private:
        void reset_write_sources (bool mark_write_complete, bool force = false);
        SourceMap sources;
 
-
-  private:
        int load_sources (const XMLNode& node);
        XMLNode& get_sources_as_xml ();
 
@@ -1903,8 +1896,6 @@ class LIBARDOUR_API Session : public PBD::StatefulDestructible, public PBD::Scop
 
        void process_rtop (SessionEvent*);
 
-       void  update_latency (bool playback);
-
        enum snapshot_t {
                NormalSave,
                SnapshotKeep,
index 6d1544fe4142b9c9f66418ad74a3bdf254b00490..85ed7a1fd89a454764fbcd86cffe05e1e70e3f7d 100644 (file)
@@ -4045,11 +4045,18 @@ Route::add_export_point()
 samplecnt_t
 Route::update_signal_latency (bool apply_to_delayline)
 {
+       // TODO: bail out if !active() and set/assume _signal_latency = 0,
+       // here or in Session::* ? -> also zero send latencies,
+       // and make sure that re-enabling a route updates things again...
+
        Glib::Threads::RWLock::ReaderLock lm (_processor_lock);
 
-       samplecnt_t l_in  = 0; // _input->latency ();
+       samplecnt_t l_in  = 0;
        samplecnt_t l_out = _output->user_latency();
        for (ProcessorList::reverse_iterator i = _processors.rbegin(); i != _processors.rend(); ++i) {
+               if (boost::shared_ptr<Send> snd = boost::dynamic_pointer_cast<Send> (*i)) {
+                       snd->set_delay_in (l_out + _output->latency());
+               }
                (*i)->set_output_latency (l_out);
                if ((*i)->active ()) {
                        l_out += (*i)->signal_latency ();
@@ -4069,7 +4076,6 @@ Route::update_signal_latency (bool apply_to_delayline)
                (*i)->set_capture_offset (_input->latency ());
        }
 
-
        lm.release ();
 
        if (apply_to_delayline) {
index df8ae9fc51f355969c1b459e394141115fc930d8..3d942c6f43e37c75bf5861ab0aacdc75efe906e9 100644 (file)
@@ -194,13 +194,12 @@ Session::Session (AudioEngine &eng,
        , _target_transport_speed (0.0)
        , auto_play_legal (false)
        , _last_slave_transport_sample (0)
-       , maximum_output_latency (0)
        , _requested_return_sample (-1)
        , current_block_size (0)
        , _worst_output_latency (0)
        , _worst_input_latency (0)
-       , _worst_track_latency (0)
-       , _worst_track_out_latency (0)
+       , _worst_route_latency (0)
+       , _send_latency_changes (0)
        , _have_captured (false)
        , _non_soloed_outs_muted (false)
        , _listening (false)
@@ -463,6 +462,8 @@ Session::Session (AudioEngine &eng,
        StartTimeChanged.connect_same_thread (*this, boost::bind (&Session::start_time_changed, this, _1));
        EndTimeChanged.connect_same_thread (*this, boost::bind (&Session::end_time_changed, this, _1));
 
+       Send::ChangedLatency.connect_same_thread (*this, boost::bind (&Session::send_latency_compensation_change, this));
+
        emit_thread_start ();
        auto_connect_thread_start ();
 
@@ -5717,13 +5718,9 @@ Session::graph_reordered ()
        resort_routes ();
 
        /* force all diskstreams to update their capture offset values to
-          reflect any changes in latencies within the graph.
-       */
-
-       boost::shared_ptr<RouteList> rl = routes.reader ();
-       for (RouteList::iterator i = rl->begin(); i != rl->end(); ++i) {
-               (*i)->update_signal_latency (true); // XXX
-       }
+        * reflect any changes in latencies within the graph.
+        */
+       update_route_latency (false, true);
 }
 
 /** @return Number of samples that there is disk space available to write,
@@ -6833,9 +6830,76 @@ Session::unknown_processors () const
 }
 
 void
-Session::update_latency (bool playback)
+Session::set_worst_io_latencies_x (IOChange, void *)
 {
+               set_worst_io_latencies ();
+}
 
+void
+Session::send_latency_compensation_change ()
+{
+       /* As a result of Send::set_output_latency()
+        * or InternalReturn::set_playback_offset ()
+        * the send's own latency can change (source track
+        * is aligned with target bus).
+        *
+        * This can only happen be triggered by
+        * Route::update_signal_latency ()
+        * when updating the processor latency.
+        *
+        * We need to walk the graph again to take those changes into account
+        * (we should probably recurse or process the graph in a 2 step process).
+        */
+       ++_send_latency_changes;
+}
+
+bool
+Session::update_route_latency (bool playback, bool apply_to_delayline)
+{
+       /* Note: RouteList is process-graph sorted */
+       boost::shared_ptr<RouteList> r = routes.reader ();
+
+       if (playback) {
+               /* reverse the list so that we work backwards from the last route to run to the first,
+                * this is not needed, but can help to reduce the iterations for aux-sends.
+                */
+               RouteList* rl = routes.reader().get();
+               r.reset (new RouteList (*rl));
+               reverse (r->begin(), r->end());
+       }
+
+       bool changed = false;
+       int bailout = 0;
+restart:
+       _send_latency_changes = 0;
+       _worst_route_latency = 0;
+
+       for (RouteList::iterator i = r->begin(); i != r->end(); ++i) {
+               // if (!(*i)->active()) { continue ; } // TODO
+               samplecnt_t l;
+               if ((*i)->signal_latency () != (l = (*i)->update_signal_latency (apply_to_delayline))) {
+                       changed = true;
+               }
+               _worst_route_latency = std::max (l, _worst_route_latency);
+       }
+
+       if (_send_latency_changes > 0) {
+               // only 1 extra iteration is needed (we allow only 1 level of aux-sends)
+               // BUT..  jack'n'sends'n'bugs
+               if (++bailout < 5) {
+                       cerr << "restarting Session::update_latency. # of send changes: " << _send_latency_changes << " iteration: " << bailout << endl;
+                       goto restart;
+               }
+       }
+
+       DEBUG_TRACE (DEBUG::Latency, string_compose ("worst signal processing latency: %1 (changed ? %2)\n", _worst_route_latency, (changed ? "yes" : "no")));
+
+       return changed;
+}
+
+void
+Session::update_latency (bool playback)
+{
        DEBUG_TRACE (DEBUG::Latency, string_compose ("JACK latency callback: %1\n", (playback ? "PLAYBACK" : "CAPTURE")));
 
        if ((_state_of_the_state & (InitialConnecting|Deletion)) || _adding_routes_in_progress || _route_deletion_in_progress) {
@@ -6861,47 +6925,16 @@ Session::update_latency (bool playback)
        }
 
        if (playback) {
-               post_playback_latency ();
+               set_worst_output_latency ();
+               update_route_latency (true, true);
        } else {
-               post_capture_latency ();
+               set_worst_input_latency ();
+               update_route_latency (false, false);
        }
 
        DEBUG_TRACE (DEBUG::Latency, "JACK latency callback: DONE\n");
 }
 
-void
-Session::post_playback_latency ()
-{
-       set_worst_playback_latency ();
-
-       boost::shared_ptr<RouteList> r = routes.reader ();
-
-       _worst_track_out_latency = 0; // XXX remove me
-
-       for (RouteList::iterator i = r->begin(); i != r->end(); ++i) {
-               assert (!(*i)->is_auditioner()); // XXX remove me
-               _worst_track_latency = max (_worst_track_latency, (*i)->update_signal_latency ());
-       }
-
-       for (RouteList::iterator i = r->begin(); i != r->end(); ++i) {
-               if (!(*i)->active()) { continue ; }
-               (*i)->apply_latency_compensation ();
-       }
-}
-
-void
-Session::post_capture_latency ()
-{
-       set_worst_capture_latency ();
-
-       /* reflect any changes in capture latencies into capture offsets */
-
-       boost::shared_ptr<RouteList> rl = routes.reader();
-       for (RouteList::iterator i = rl->begin(); i != rl->end(); ++i) {
-               (*i)->update_signal_latency ();
-       }
-}
-
 void
 Session::initialize_latencies ()
 {
@@ -6917,12 +6950,12 @@ Session::initialize_latencies ()
 void
 Session::set_worst_io_latencies ()
 {
-       set_worst_playback_latency ();
-       set_worst_capture_latency ();
+       set_worst_output_latency ();
+       set_worst_input_latency ();
 }
 
 void
-Session::set_worst_playback_latency ()
+Session::set_worst_output_latency ()
 {
        if (_state_of_the_state & (InitialConnecting|Deletion)) {
                return;
@@ -6946,7 +6979,7 @@ Session::set_worst_playback_latency ()
 }
 
 void
-Session::set_worst_capture_latency ()
+Session::set_worst_input_latency ()
 {
        if (_state_of_the_state & (InitialConnecting|Deletion)) {
                return;
@@ -6961,54 +6994,32 @@ Session::set_worst_capture_latency ()
        boost::shared_ptr<RouteList> r = routes.reader ();
 
        for (RouteList::iterator i = r->begin(); i != r->end(); ++i) {
-               boost::shared_ptr<Track> tr = boost::dynamic_pointer_cast<Track> (*i);
-               if (!tr) {
-                       continue;
-               }
                _worst_input_latency = max (_worst_input_latency, (*i)->input()->latency());
        }
 
-        DEBUG_TRACE (DEBUG::Latency, string_compose ("Worst input latency: %1\n", _worst_input_latency));
+       DEBUG_TRACE (DEBUG::Latency, string_compose ("Worst input latency: %1\n", _worst_input_latency));
 }
 
 void
 Session::update_latency_compensation (bool force_whole_graph)
 {
-       // TODO: consolidate
-       bool some_track_latency_changed = false;
-
        if (_state_of_the_state & (InitialConnecting|Deletion)) {
                return;
        }
 
-       DEBUG_TRACE(DEBUG::Latency, "---------------------------- update latency compensation\n\n");
-
-       _worst_track_latency = 0;
-
-       boost::shared_ptr<RouteList> r = routes.reader ();
-
-       for (RouteList::iterator i = r->begin(); i != r->end(); ++i) {
-               assert (!(*i)->is_auditioner()); // XXX remove me
-               if ((*i)->active()) {
-                       samplecnt_t tl;
-                       if ((*i)->signal_latency () != (tl = (*i)->update_signal_latency () /* - (*i)->output()->user_latency()*/)) {
-                               some_track_latency_changed = true;
-                       }
-                       _worst_track_latency = max (tl, _worst_track_latency);
-               }
-       }
-
-       DEBUG_TRACE (DEBUG::Latency, string_compose ("worst signal processing latency: %1 (changed ? %2)\n", _worst_track_latency,
-                                                    (some_track_latency_changed ? "yes" : "no")));
-
-       DEBUG_TRACE(DEBUG::Latency, "---------------------------- DONE update latency compensation\n\n");
+       bool some_track_latency_changed = update_route_latency (false, false);
 
        if (some_track_latency_changed || force_whole_graph)  {
                _engine.update_latencies ();
-       }
-
-       for (RouteList::iterator i = r->begin(); i != r->end(); ++i) {
-               (*i)->update_signal_latency (true);
+               /* above call will ask the backend up update its latencies, which
+                * eventually will trigger  AudioEngine::latency_callback () and
+                * call Session::update_latency ()
+                */
+       } else {
+               boost::shared_ptr<RouteList> r = routes.reader ();
+               for (RouteList::iterator i = r->begin(); i != r->end(); ++i) {
+                       (*i)->apply_latency_compensation ();
+               }
        }
 }