diff --git a/libs/ardour/ardour/session.h b/libs/ardour/ardour/session.h index 66ab5ce082..a725fdc802 100644 --- a/libs/ardour/ardour/session.h +++ b/libs/ardour/ardour/session.h @@ -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 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 update_latency (bool playback); + bool update_route_latency (bool reverse, bool apply_to_delayline); - void update_latency_compensation_proxy (void* ignored); + void set_worst_io_latencies (); + void set_worst_output_latency (); + void set_worst_input_latency (); + + 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 > 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, diff --git a/libs/ardour/route.cc b/libs/ardour/route.cc index 6d1544fe41..85ed7a1fd8 100644 --- a/libs/ardour/route.cc +++ b/libs/ardour/route.cc @@ -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 snd = boost::dynamic_pointer_cast (*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) { diff --git a/libs/ardour/session.cc b/libs/ardour/session.cc index df8ae9fc51..3d942c6f43 100644 --- a/libs/ardour/session.cc +++ b/libs/ardour/session.cc @@ -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 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, @@ -6832,10 +6829,77 @@ Session::unknown_processors () const return p; } +void +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 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 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 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 r = routes.reader (); for (RouteList::iterator i = r->begin(); i != r->end(); ++i) { - boost::shared_ptr tr = boost::dynamic_pointer_cast (*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 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 r = routes.reader (); + for (RouteList::iterator i = r->begin(); i != r->end(); ++i) { + (*i)->apply_latency_compensation (); + } } }