From a3ea8641e4be48a51ad7baaeb533865624a1e895 Mon Sep 17 00:00:00 2001 From: Paul Davis Date: Wed, 14 Sep 2011 19:05:05 +0000 Subject: back-port Ardour3's capture alignment code to Ardour2, to get correct results with the new JACK latency API git-svn-id: svn://localhost/ardour2/branches/2.0-ongoing@10085 d708f5d6-7413-0410-9779-e7cbd77b26cf --- libs/ardour/ardour/audioengine.h | 11 ++- libs/ardour/ardour/buffer.h | 4 + libs/ardour/ardour/io.h | 4 +- libs/ardour/ardour/port.h | 18 +++- libs/ardour/ardour/route.h | 9 +- libs/ardour/ardour/session.h | 11 ++- libs/ardour/ardour/track.h | 3 +- libs/ardour/audio_diskstream.cc | 35 ++++++- libs/ardour/audio_track.cc | 6 +- libs/ardour/audioengine.cc | 84 ++++++++++++---- libs/ardour/io.cc | 14 +-- libs/ardour/port.cc | 168 ++++++++++++++++++++++++++++++-- libs/ardour/route.cc | 140 ++++++++++++++++++++++----- libs/ardour/session.cc | 202 ++++++++++++++++++++++++++++++++++----- libs/ardour/session_transport.cc | 70 ++------------ libs/ardour/track.cc | 26 +---- 16 files changed, 612 insertions(+), 193 deletions(-) (limited to 'libs') diff --git a/libs/ardour/ardour/audioengine.h b/libs/ardour/ardour/audioengine.h index 81fee6ded1..7325c13592 100644 --- a/libs/ardour/ardour/audioengine.h +++ b/libs/ardour/ardour/audioengine.h @@ -148,14 +148,12 @@ class AudioEngine : public sigc::trackable return get_nth_physical_audio (n, JackPortIsOutput); } - nframes_t get_port_total_latency (const Port&); - void update_total_latencies (); - /* the caller may not delete the object pointed to by - the return value + the return value for either of these functions. */ Port *get_port_by_name (const std::string& name, bool keep = true); + Port *get_ardour_port_by_name_unlocked (const string& portname); enum TransportState { TransportStopped = JackTransportStopped, @@ -205,6 +203,7 @@ _ the regular process() call to session->process() is not made. std::string make_port_name_relative (std::string); std::string make_port_name_non_relative (std::string); + bool port_is_mine (const std::string& portname) const; static AudioEngine* instance() { return _instance; } void died (); @@ -256,7 +255,9 @@ _ the regular process() call to session->process() is not made. static void _jack_timebase_callback (jack_transport_state_t, nframes_t, jack_position_t*, int, void*); static int _jack_sync_callback (jack_transport_state_t, jack_position_t*, void *arg); static void _freewheel_callback (int , void *arg); - + static void _latency_callback (jack_latency_callback_mode_t mode, void* arg); + + void jack_latency_callback (jack_latency_callback_mode_t mode); void jack_timebase_callback (jack_transport_state_t, nframes_t, jack_position_t*, int); int jack_sync_callback (jack_transport_state_t, jack_position_t*); int jack_bufsize_callback (nframes_t); diff --git a/libs/ardour/ardour/buffer.h b/libs/ardour/ardour/buffer.h index 88a803e0c4..2b45822492 100644 --- a/libs/ardour/ardour/buffer.h +++ b/libs/ardour/ardour/buffer.h @@ -19,6 +19,10 @@ #ifndef __ardour_buffer_h__ #define __ardour_buffer_h__ +#ifdef _XOPEN_SOURCE +#undef _XOPEN_SOURCE +#endif + #define _XOPEN_SOURCE 600 #include // for posix_memalign #include diff --git a/libs/ardour/ardour/io.h b/libs/ardour/ardour/io.h index 8f74abc00c..288eca630b 100644 --- a/libs/ardour/ardour/io.h +++ b/libs/ardour/ardour/io.h @@ -139,7 +139,6 @@ class IO : public PBD::StatefulDestructible nframes_t output_latency() const; nframes_t input_latency() const; - void set_port_latency (nframes_t); Port *output (uint32_t n) const { if (n < _noutputs) { @@ -157,6 +156,9 @@ class IO : public PBD::StatefulDestructible } } + std::vector& outputs() { return _outputs; } + std::vector& inputs() { return _inputs; } + uint32_t n_inputs () const { return _ninputs; } uint32_t n_outputs () const { return _noutputs; } diff --git a/libs/ardour/ardour/port.h b/libs/ardour/ardour/port.h index ef4d92b39f..251e6fb867 100644 --- a/libs/ardour/ardour/port.h +++ b/libs/ardour/ardour/port.h @@ -82,6 +82,8 @@ class Port : public sigc::trackable { return jack_port_get_connections (_port); } + int get_connections (std::vector & c) const; + void reset_overs () { _short_overs = 0; _long_overs = 0; @@ -168,12 +170,23 @@ class Port : public sigc::trackable { _silent = yn; } + void get_connected_latency_range (jack_latency_range_t& range, bool playback) const; + + void set_private_latency_range (jack_latency_range_t& range, bool playback); + const jack_latency_range_t& private_latency_range (bool playback) const; + + void set_public_latency_range (jack_latency_range_t& range, bool playback) const; + jack_latency_range_t public_latency_range (bool playback) const; + private: friend class AudioEngine; Port (jack_port_t *port); + void reset (); + static AudioEngine* _engine; ///< the AudioEngine + /* engine isn't supposed to use anything below here */ /* cache these 3 from JACK so that we can @@ -200,6 +213,9 @@ class Port : public sigc::trackable { static nframes_t _port_offset; static nframes_t _buffer_size; + jack_latency_range_t _private_playback_latency; + jack_latency_range_t _private_capture_latency; + private: friend class IO; // get_(input|output)_buffer friend class Session; // session_export.cc @@ -212,8 +228,6 @@ class Port : public sigc::trackable { return (Sample *) jack_port_get_buffer (_port, _buffer_size) + _port_offset; } - - }; } // namespace ARDOUR diff --git a/libs/ardour/ardour/route.h b/libs/ardour/ardour/route.h index c3883969aa..75dfdd8fb0 100644 --- a/libs/ardour/ardour/route.h +++ b/libs/ardour/ardour/route.h @@ -182,9 +182,12 @@ class Route : public IO void all_redirects_flip(); void all_redirects_active (Placement, bool state); - virtual nframes_t update_total_latency(); + nframes_t set_private_port_latencies (bool playback); + void set_public_port_latencies (nframes_t, bool playback); + nframes_t signal_latency() const { return _own_latency; } - virtual void set_latency_delay (nframes_t); + virtual void set_latency_compensation (nframes_t); + nframes_t update_own_latency (); sigc::signal solo_changed; sigc::signal solo_safe_changed; @@ -371,6 +374,8 @@ class Route : public IO void set_deferred_state (); void add_redirect_from_xml (const XMLNode&); void redirect_active_proxy (Redirect*, void*); + + nframes_t update_port_latencies (std::vector& ports, std::vector& feeders, bool playback, nframes_t) const; }; } // namespace ARDOUR diff --git a/libs/ardour/ardour/session.h b/libs/ardour/ardour/session.h index eb47b4d2bd..d467e99354 100644 --- a/libs/ardour/ardour/session.h +++ b/libs/ardour/ardour/session.h @@ -1001,7 +1001,7 @@ class Session : public PBD::StatefulDestructible protected: friend class Route; void schedule_curve_reallocation (); - void update_latency_compensation (bool, bool); + void update_latency_compensation (bool force_whole_graph = false); private: int create (bool& new_session, const string& mix_template, nframes_t initial_length); @@ -1066,10 +1066,15 @@ class Session : public PBD::StatefulDestructible bool _end_location_is_free; bool _was_seamless; + void initialize_latencies (); void set_worst_io_latencies (); - void set_worst_io_latencies_x (IOChange asifwecare, void *ignored) { + 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 route_redirects_changed (void* ignored); @@ -1687,6 +1692,8 @@ class Session : public PBD::StatefulDestructible void reset_jack_connection (jack_client_t* jack); void record_enable_change_all (bool yn); + void update_latency (bool playback); + XMLNode& state(bool); /* click track */ diff --git a/libs/ardour/ardour/track.h b/libs/ardour/ardour/track.h index 4699013369..69754eeb06 100644 --- a/libs/ardour/ardour/track.h +++ b/libs/ardour/ardour/track.h @@ -62,8 +62,7 @@ class Track : public Route virtual int use_diskstream (string name) = 0; virtual int use_diskstream (const PBD::ID& id) = 0; - nframes_t update_total_latency(); - void set_latency_delay (nframes_t); + void set_latency_compensation (nframes_t); enum FreezeState { NoFreeze, diff --git a/libs/ardour/audio_diskstream.cc b/libs/ardour/audio_diskstream.cc index 4a37c7038c..67d703621b 100644 --- a/libs/ardour/audio_diskstream.cc +++ b/libs/ardour/audio_diskstream.cc @@ -409,12 +409,27 @@ AudioDiskstream::check_record_status (nframes_t transport_frame, nframes_t nfram capture_start_frame = _session.transport_frame(); first_recordable_frame = capture_start_frame + _capture_offset; + // cerr << "SET FRF to " << first_recordable_frame << " from CSF + CO\n"; last_recordable_frame = max_frames; if (_alignment_style == ExistingMaterial) { first_recordable_frame += existing_material_offset; + // cerr << "RESET FRF to " << first_recordable_frame << " from EMO\n"; } +#if 0 + cerr << name() << " transitioned to recording, capture starts at " << capture_start_frame + << " align to " << (_alignment_style == ExistingMaterial ? "existing" : "capture-time") + << " first recordable = " << first_recordable_frame + << " roll_delay " << _roll_delay + << " capture offset " << _capture_offset + << " EMO " << existing_material_offset + << " worst input " << _session.worst_input_latency() + << " worst output " << _session.worst_output_latency() + << " worst track " << _session.worst_track_latency() + << endl; +#endif + if (recordable() && destructive()) { boost::shared_ptr c = channels.reader(); for (ChannelList::iterator chan = c->begin(); chan != c->end(); ++chan) { @@ -454,6 +469,8 @@ AudioDiskstream::check_record_status (nframes_t transport_frame, nframes_t nfram last_recordable_frame += existing_material_offset; } } + + // cerr << name() << " transitioned out of recording, last recordable = " << last_recordable_frame << endl; } } @@ -493,9 +510,11 @@ AudioDiskstream::process (nframes_t transport_frame, nframes_t nframes, bool can check_record_status (transport_frame, nframes, can_record); #if 0 - cerr << _name << " can record " << can_record << " re " << record_enabled() - << " FRF " << first_recordable_frame << " LRF " << last_recordable_frame - << endl; + if (record_enabled()) { + cerr << '@' << transport_frame << ' ' << _name << " can record " << can_record << " re " << record_enabled() + << " FRF " << first_recordable_frame << " LRF " << last_recordable_frame + << endl; + } #endif if (nframes == 0) { @@ -578,7 +597,13 @@ AudioDiskstream::process (nframes_t transport_frame, nframes_t nframes, bool can if (can_record && !_last_capture_regions.empty()) { _last_capture_regions.clear (); - } + } + +#if 0 + if (record_enabled()) { + cerr << "coverage = " << enum_2_string (ot) << " rec_nframes = " << rec_nframes << endl; + } +#endif if (rec_nframes) { @@ -1680,7 +1705,7 @@ AudioDiskstream::transport_stopped (struct tm& when, time_t twhen, bool abort_ca _session.region_name (region_name, whole_file_region_name, false); - // cerr << _name << ": based on ci of " << (*ci)->start << " for " << (*ci)->frames << " add region " << region_name << endl; + // cerr << _name << ": add region @ " << (*ci)->start << " internal buffer offset = " << buffer_position << " length " << (*ci)->frames << " name" << region_name << endl; try { boost::shared_ptr rx (RegionFactory::create (srcs, buffer_position, (*ci)->frames, region_name)); diff --git a/libs/ardour/audio_track.cc b/libs/ardour/audio_track.cc index 96405c8267..e442613aa1 100644 --- a/libs/ardour/audio_track.cc +++ b/libs/ardour/audio_track.cc @@ -612,7 +612,6 @@ AudioTrack::roll (nframes_t nframes, nframes_t start_frame, nframes_t end_frame, if ((dret = diskstream->process (transport_frame, nframes, can_record, rec_monitors_input)) != 0) { silence (nframes); - return dret; } @@ -668,7 +667,7 @@ AudioTrack::roll (nframes_t nframes, nframes_t start_frame, nframes_t end_frame, /* first time through just copy a channel into the output buffer. */ - + for (nframes_t xx = 0; xx < nframes; ++xx) { bufs[i][xx] = b[xx] * scaling; } @@ -686,7 +685,7 @@ AudioTrack::roll (nframes_t nframes, nframes_t start_frame, nframes_t end_frame, /* for all remaining channels, sum with existing data in the output buffers */ - + Session::mix_buffers_with_gain (bufs[i%blimit], b, nframes, scaling); if (n < diskstream->n_channels()) { @@ -727,6 +726,7 @@ AudioTrack::roll (nframes_t nframes, nframes_t start_frame, nframes_t end_frame, } else { /* problem with the diskstream; just be quiet for a bit */ silence (nframes); + } return 0; diff --git a/libs/ardour/audioengine.cc b/libs/ardour/audioengine.cc index 5501f76e79..7b519a9ddb 100644 --- a/libs/ardour/audioengine.cc +++ b/libs/ardour/audioengine.cc @@ -25,6 +25,7 @@ #include #include +#include #include #include #include @@ -96,6 +97,8 @@ AudioEngine::AudioEngine (string client_name) if (connect_to_jack (client_name)) { throw NoBackendAvailable (); } + + Port::_engine = this; } AudioEngine::~AudioEngine () @@ -176,6 +179,10 @@ AudioEngine::start () jack_set_timebase_callback (_priv_jack, 0, _jack_timebase_callback, this); } + if (jack_set_latency_callback) { + jack_set_latency_callback (_priv_jack, _latency_callback, this); + } + if (jack_activate (_priv_jack) == 0) { _running = true; _has_run = true; @@ -298,6 +305,20 @@ AudioEngine::_freewheel_callback (int onoff, void *arg) static_cast(arg)->_freewheeling = onoff; } +void +AudioEngine::_latency_callback (jack_latency_callback_mode_t mode, void* arg) +{ + return static_cast (arg)->jack_latency_callback (mode); +} + +void +AudioEngine::jack_latency_callback (jack_latency_callback_mode_t mode) +{ + if (session) { + session->update_latency (mode == JackPlaybackLatency); + } +} + int AudioEngine::process_callback (nframes_t nframes) { @@ -546,6 +567,8 @@ AudioEngine::port_registration_failure (const std::string& portname) reason = string_compose (_("No more JACK ports are available. You will need to stop %1 and restart JACK with ports if you need this many tracks."), PROGRAM_NAME); } + + cerr << " port reg failed: " << reason << endl; throw PortRegistrationFailure (string_compose (_("AudioEngine: cannot register port \"%1\": %2"), portname, reason).c_str()); } @@ -819,6 +842,35 @@ AudioEngine::get_port_by_name (const string& portname, bool keep) } } +Port * +AudioEngine::get_ardour_port_by_name_unlocked (const string& portname) +{ + if (!_running) { + if (!_has_run) { + fatal << _("get_port_by_name() called before engine was started") << endmsg; + /*NOTREACHED*/ + } else { + return 0; + } + } + + if (!port_is_mine (portname)) { + return 0; + } + + std::string const rel = make_port_name_relative (portname); + + boost::shared_ptr pr = ports.reader(); + + for (Ports::iterator i = pr->begin(); i != pr->end(); ++i) { + if (rel == (*i)->name()) { + return (*i); + } + } + + return 0; +} + const char ** AudioEngine::get_ports (const string& port_name_pattern, const string& type_name_pattern, uint32_t flags) { @@ -1006,14 +1058,6 @@ AudioEngine::get_nth_physical_audio (uint32_t n, int flag) return ret; } -nframes_t -AudioEngine::get_port_total_latency (const Port& port) -{ - GET_PRIVATE_JACK_POINTER_RET (_jack,0); - - return jack_port_get_total_latency (_priv_jack, port._port); -} - void AudioEngine::transport_stop () { @@ -1284,6 +1328,10 @@ AudioEngine::reconnect_to_jack () jack_set_timebase_callback (_priv_jack, 0, _jack_timebase_callback, this); } + if (jack_set_latency_callback) { + jack_set_latency_callback (_priv_jack, _latency_callback, this); + } + if (jack_activate (_priv_jack) == 0) { _running = true; _has_run = true; @@ -1330,15 +1378,6 @@ AudioEngine::request_buffer_size (nframes_t nframes) return jack_set_buffer_size (_priv_jack, nframes); } -void -AudioEngine::update_total_latencies () -{ -#ifdef HAVE_JACK_RECOMPUTE_LATENCIES - GET_PRIVATE_JACK_POINTER (_jack); - jack_recompute_total_latencies (_priv_jack); -#endif -} - string AudioEngine::make_port_name_relative (string portname) { @@ -1382,3 +1421,14 @@ AudioEngine::is_realtime () const GET_PRIVATE_JACK_POINTER_RET (_jack,false); return jack_is_realtime (_priv_jack); } + +bool +AudioEngine::port_is_mine (const string& portname) const +{ + if (portname.find_first_of (':') != string::npos) { + if (portname.substr (0, jack_client_name.length ()) != jack_client_name) { + return false; + } + } + return true; +} diff --git a/libs/ardour/io.cc b/libs/ardour/io.cc index 561fb93432..9026cfacf6 100644 --- a/libs/ardour/io.cc +++ b/libs/ardour/io.cc @@ -2210,16 +2210,6 @@ IO::set_output_maximum (int n) _output_maximum = n; } -void -IO::set_port_latency (nframes_t nframes) -{ - Glib::Mutex::Lock lm (io_lock); - - for (vector::iterator i = _outputs.begin(); i != _outputs.end(); ++i) { - (*i)->set_latency (nframes); - } -} - nframes_t IO::output_latency () const { @@ -2231,7 +2221,7 @@ IO::output_latency () const /* io lock not taken - must be protected by other means */ for (vector::const_iterator i = _outputs.begin(); i != _outputs.end(); ++i) { - if ((latency = _session.engine().get_port_total_latency (*(*i))) > max_latency) { + if ((latency = (*i)->private_latency_range (true).max) > max_latency) { max_latency = latency; } } @@ -2250,7 +2240,7 @@ IO::input_latency () const /* io lock not taken - must be protected by other means */ for (vector::const_iterator i = _inputs.begin(); i != _inputs.end(); ++i) { - if ((latency = _session.engine().get_port_total_latency (*(*i))) > max_latency) { + if ((latency = (*i)->private_latency_range (false).max) > max_latency) { max_latency = latency; } } diff --git a/libs/ardour/port.cc b/libs/ardour/port.cc index 00603c95b9..4a564ec93e 100644 --- a/libs/ardour/port.cc +++ b/libs/ardour/port.cc @@ -17,11 +17,16 @@ */ +#include #include "ardour/port.h" +#include "ardour/audioengine.h" + +#include "i18n.h" using namespace ARDOUR; using namespace std; +AudioEngine* Port::_engine; nframes_t Port::_short_over_length = 2; nframes_t Port::_long_over_length = 10; nframes_t Port::_port_offset = 0; @@ -34,6 +39,11 @@ Port::Port (jack_port_t *p) throw failed_constructor(); } + _private_playback_latency.min = 0; + _private_playback_latency.max = 0; + _private_capture_latency.min = 0; + _private_capture_latency.max = 0; + _flags = JackPortFlags (jack_port_flags (_port)); _type = jack_port_type (_port); _name = jack_port_name (_port); @@ -63,14 +73,160 @@ Port::set_name (string str) return ret; } -nframes_t -Port::latency () const +void +Port::set_public_latency_range (jack_latency_range_t& range, bool playback) const +{ + /* this sets the visible latency that the rest of JACK sees. because we do latency + compensation, all (most) of our visible port latency values are identical. + */ + + if (!jack_port_set_latency_range) { + return; + } + + jack_port_set_latency_range (_port, + (playback ? JackPlaybackLatency : JackCaptureLatency), + &range); +} + +void +Port::set_private_latency_range (jack_latency_range_t& range, bool playback) +{ + // cerr << name() << " set private " << (playback ? "playback" : "capture") << " latency to " << range.min << " - " + // << range.max << endl; + + if (playback) { + _private_playback_latency = range; + } else { + _private_capture_latency = range; + } + + /* push to public (JACK) location so that everyone else can see it */ + + set_public_latency_range (range, playback); +} + +const jack_latency_range_t& +Port::private_latency_range (bool playback) const { - return jack_port_get_latency (_port); + if (playback) { + return _private_playback_latency; + } else { + return _private_capture_latency; + } } -void -Port::set_latency (nframes_t nframes) +jack_latency_range_t +Port::public_latency_range (bool playback) const { - jack_port_set_latency (_port, nframes); + jack_latency_range_t r; + + jack_port_get_latency_range (_port, + sends_output() ? JackPlaybackLatency : JackCaptureLatency, + &r); + + return r; +} + +/** @param o Filled in with port full names of ports that we are connected to */ +int +Port::get_connections (std::vector & c) const +{ + int n = 0; + + if (_engine->connected()) { + const char** jc = jack_port_get_connections (_port); + if (jc) { + for (int i = 0; jc[i]; ++i) { + c.push_back (jc[i]); + ++n; + } + if (jack_free) { + jack_free (jc); + } else { + free (jc); + } + } + } + + return n; +} + +void +Port::get_connected_latency_range (jack_latency_range_t& range, bool playback) const +{ + if (!jack_port_get_latency_range) { + return; + } + + vector connections; + jack_client_t* jack = _engine->jack(); + + if (!jack) { + range.min = 0; + range.max = 0; + PBD::warning << _("get_connected_latency_range() called while disconnected from JACK") << endmsg; + return; + } + + get_connections (connections); + + if (!connections.empty()) { + + range.min = ~((jack_nframes_t) 0); + range.max = 0; + + // cerr << const_cast(this)->name() << " ========= " << connections.size() << " connections\n"; + + for (vector::const_iterator c = connections.begin(); + c != connections.end(); ++c) { + + jack_latency_range_t lr; + + // cerr << "\tConnected to " << (*c) << endl; + + if (!AudioEngine::instance()->port_is_mine (*c)) { + + /* port belongs to some other JACK client, use + * JACK to lookup its latency information. + */ + + jack_port_t* remote_port = jack_port_by_name (_engine->jack(), (*c).c_str()); + + if (remote_port) { + jack_port_get_latency_range ( + remote_port, + (playback ? JackPlaybackLatency : JackCaptureLatency), + &lr); + + range.min = min (range.min, lr.min); + range.max = max (range.max, lr.max); + + // cerr << "\t\tRemote port, range = " << range.min << " - " << range.max << endl; + } + + } else { + + /* port belongs to this instance of ardour, + so look up its latency information + internally, because our published/public + values already contain our plugin + latency compensation. + */ + + + Port* remote_port = AudioEngine::instance()->get_ardour_port_by_name_unlocked (*c); + if (remote_port) { + lr = remote_port->private_latency_range ((playback ? JackPlaybackLatency : JackCaptureLatency)); + range.min = min (range.min, lr.min); + range.max = max (range.max, lr.max); + // cerr << "\t\tArdour port, range = " << range.min << " - " << range.max << endl; + } + } + } + + } else { + range.min = 0; + range.max = 0; + } } diff --git a/libs/ardour/route.cc b/libs/ardour/route.cc index 454d29ab4d..5df739bc44 100644 --- a/libs/ardour/route.cc +++ b/libs/ardour/route.cc @@ -2491,50 +2491,141 @@ Route::set_meter_point (MeterPoint p, void *src) } nframes_t -Route::update_total_latency () +Route::update_port_latencies (vector& from, vector& to, bool playback, nframes_t our_latency) const { - _own_latency = 0; + /* we assume that all our input ports feed all our output ports. its not + universally true, but the alternative is way too corner-case to worry about. + */ - for (RedirectList::iterator i = _redirects.begin(); i != _redirects.end(); ++i) { + jack_latency_range_t all_connections; + + all_connections.min = ~((jack_nframes_t) 0); + all_connections.max = 0; + + /* iterate over all "from" ports and determine the latency range for all of their + connections to the "outside" (outside of this Route). + */ + + for (vector::const_iterator p = from.begin(); p != from.end(); ++p) { + + jack_latency_range_t range; + + (*p)->get_connected_latency_range (range, playback); + + // cerr << "***** for " << (*p)->name() << " CLR = " << range.min << " - " << range.max << endl; + + all_connections.min = min (all_connections.min, range.min); + all_connections.max = max (all_connections.max, range.max); + } + + /* set the "from" port latencies to the max/min range of all their connections */ + + for (vector::iterator p = from.begin(); p != from.end(); ++p) { + (*p)->set_private_latency_range (all_connections, playback); + } + + /* set the ports "in the direction of the flow" to the same value as above plus our own signal latency */ + + all_connections.min += our_latency; + all_connections.max += our_latency; + + for (vector::iterator p = to.begin(); p != to.end(); ++p) { + (*p)->set_private_latency_range (all_connections, playback); + } + + return all_connections.max; +} + +nframes_t +Route::set_private_port_latencies (bool playback) +{ + nframes_t own_latency = 0; + + /* Processor list not protected by lock: MUST BE CALLED FROM PROCESS THREAD + OR LATENCY CALLBACK. + + This is called (early) from the latency callback. It computes the REAL + latency associated with each port and stores the result as the "private" + latency of the port. A later call to Route::set_public_port_latencies() + sets all ports to the same value to reflect the fact that we do latency + compensation and so all signals are delayed by the same amount as they + flow through ardour. + */ + + for (RedirectList::const_iterator i = _redirects.begin(); i != _redirects.end(); ++i) { if ((*i)->active ()) { - _own_latency += (*i)->latency (); + own_latency += (*i)->latency (); } } -#undef DEBUG_LATENCY -#ifdef DEBUG_LATENCY - cerr << _name << ": internal redirect latency = " << _own_latency << endl; -#endif - - set_port_latency (_own_latency); + if (playback) { + /* playback: propagate latency from "outside the route" to outputs to inputs */ + return update_port_latencies (outputs (), inputs (), true, own_latency); + } else { + /* capture: propagate latency from "outside the route" to inputs to outputs */ + return update_port_latencies (inputs (), outputs (), false, own_latency); + } +} - /* this (virtual) function is used for pure Routes, - not derived classes like AudioTrack. this means - that the data processed here comes from an input - port, not prerecorded material, and therefore we - have to take into account any input latency. +void +Route::set_public_port_latencies (nframes_t value, bool playback) +{ + /* this is called to set the JACK-visible port latencies, which take + latency compensation into account. */ - _own_latency += input_latency (); + jack_latency_range_t range; -#ifdef DEBUG_LATENCY - cerr << _name << ": input latency = " << input_latency() << " total = " - << _own_latency << endl; -#endif + range.min = value; + range.max = value; - return _own_latency; + { + vector& ports (inputs()); + for (vector::iterator p = ports.begin(); p != ports.end(); ++p) { + (*p)->set_public_latency_range (range, playback); + } + } + + { + vector& ports (outputs()); + for (vector::iterator p = ports.begin(); p != ports.end(); ++p) { + (*p)->set_public_latency_range (range, playback); + } + } } void -Route::set_latency_delay (nframes_t longest_session_latency) +Route::set_latency_compensation (nframes_t longest_session_latency) { - _initial_delay = longest_session_latency - _own_latency; + if (_own_latency < longest_session_latency) { + _initial_delay = longest_session_latency - _own_latency; + } else { + _initial_delay = 0; + } if (_session.transport_stopped()) { _roll_delay = _initial_delay; } } +nframes_t +Route::update_own_latency () +{ + nframes_t l = 0; + + for (RedirectList::iterator i = _redirects.begin(); i != _redirects.end(); ++i) { + if ((*i)->active ()) { + l += (*i)->latency (); + } + } + + if (_own_latency != l) { + _own_latency = l; + } + + return _own_latency; +} + void Route::automation_snapshot (nframes_t now, bool force) { @@ -2602,7 +2693,7 @@ Route::set_block_size (nframes_t nframes) void Route::redirect_active_proxy (Redirect* ignored, void* ignored_src) { - _session.update_latency_compensation (false, false); + _session.update_latency_compensation (false); } void @@ -2717,7 +2808,6 @@ Route::set_name (string str, void* src) if (_control_outs) { string coutname = _name; coutname += _("[control]"); - cerr << _name << " reset control outs to " << coutname << endl; return _control_outs->set_name (coutname, src); } return 0; diff --git a/libs/ardour/session.cc b/libs/ardour/session.cc index e80a81e868..e2558be0bf 100644 --- a/libs/ardour/session.cc +++ b/libs/ardour/session.cc @@ -648,24 +648,6 @@ Session::destroy () delete mmc; } -void -Session::set_worst_io_latencies () -{ - _worst_output_latency = 0; - _worst_input_latency = 0; - - if (!_engine.connected()) { - return; - } - - boost::shared_ptr r = routes.reader (); - - for (RouteList::iterator i = r->begin(); i != r->end(); ++i) { - _worst_output_latency = max (_worst_output_latency, (*i)->output_latency()); - _worst_input_latency = max (_worst_input_latency, (*i)->input_latency()); - } -} - void Session::when_engine_running () { @@ -682,7 +664,7 @@ Session::when_engine_running () /* every time we reconnect, recompute worst case output latencies */ - _engine.Running.connect (mem_fun (*this, &Session::set_worst_io_latencies)); + _engine.Running.connect (mem_fun (*this, &Session::initialize_latencies)); if (synced_to_jack()) { _engine.transport_stop (); @@ -739,8 +721,6 @@ Session::when_engine_running () BootMessage (_("Compute I/O Latencies")); - set_worst_io_latencies (); - if (_clicking) { // XXX HOW TO ALERT UI TO THIS ? DO WE NEED TO? } @@ -895,6 +875,10 @@ Session::when_engine_running () BootMessage (_("Connect to engine")); + /* update latencies */ + + initialize_latencies (); + _engine.set_session (this); #ifdef HAVE_LIBLO @@ -974,6 +958,7 @@ Session::hookup_io () /* Tell all IO objects to connect themselves together */ + cerr << "Enable IO connections, state = " << _state_of_the_state << endl; IO::enable_connecting (); /* Now reset all panners */ @@ -986,7 +971,6 @@ Session::hookup_io () _state_of_the_state = StateOfTheState (_state_of_the_state & ~InitialConnecting); - /* now handle the whole enchilada as if it was one graph reorder event. */ @@ -2279,7 +2263,7 @@ Session::remove_route (shared_ptr route) route->disconnect_inputs (0); route->disconnect_outputs (0); - update_latency_compensation (false, false); + update_latency_compensation (); set_dirty(); /* get rid of it from the dead wood collection in the route list manager */ @@ -4427,3 +4411,175 @@ Session::sync_order_keys (const char* base) Route::SyncOrderKeys (base); // EMIT SIGNAL } + +void +Session::update_latency (bool playback) +{ + if (_state_of_the_state & (InitialConnecting|Deletion)) { + return; + } + + boost::shared_ptr r = routes.reader (); + nframes_t max_latency = 0; + + if (playback) { + /* reverse the list so that we work backwards from the last route to run to the first */ + reverse (r->begin(), r->end()); + } + + /* compute actual latency values for the given direction and store them all in per-port + structures. this will also publish the same values (to JACK) so that computation of latency + for routes can consistently use public latency values. + */ + + for (RouteList::iterator i = r->begin(); i != r->end(); ++i) { + max_latency = max (max_latency, (*i)->set_private_port_latencies (playback)); + } + + /* because we latency compensate playback, our published playback latencies should + be the same for all output ports - all material played back by ardour has + the same latency, whether its caused by plugins or by latency compensation. since + these may differ from the values computed above, reset all playback port latencies + to the same value. + */ + + for (RouteList::iterator i = r->begin(); i != r->end(); ++i) { + (*i)->set_public_port_latencies (max_latency, playback); + } + + if (playback) { + + post_playback_latency (); + + } else { + + post_capture_latency (); + } +} + +void +Session::post_playback_latency () +{ + set_worst_playback_latency (); + + boost::shared_ptr r = routes.reader (); + for (RouteList::iterator i = r->begin(); i != r->end(); ++i) { + if (!(*i)->hidden() && ((*i)->active())) { + _worst_track_latency = max (_worst_track_latency, (*i)->update_own_latency ()); + } + } + + for (RouteList::iterator i = r->begin(); i != r->end(); ++i) { + (*i)->set_latency_compensation (_worst_track_latency); + } +} + +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) { + boost::shared_ptr tr = boost::dynamic_pointer_cast (*i); + if (tr) { + boost::shared_ptr ds = tr->diskstream(); + if (ds) { + ds->set_capture_offset (); + } + } + } +} + +void +Session::initialize_latencies () +{ + { + Glib::Mutex::Lock lm (_engine.process_lock()); + update_latency (false); + update_latency (true); + } + + set_worst_io_latencies (); +} + +void +Session::set_worst_io_latencies () +{ + set_worst_playback_latency (); + set_worst_capture_latency (); +} + +void +Session::set_worst_playback_latency () +{ + if (_state_of_the_state & (InitialConnecting|Deletion)) { + return; + } + + _worst_output_latency = 0; + + if (!_engine.connected()) { + return; + } + + boost::shared_ptr r = routes.reader (); + + for (RouteList::iterator i = r->begin(); i != r->end(); ++i) { + _worst_output_latency = max (_worst_output_latency, (*i)->output_latency()); + } + + cerr << "Session: worst output latency = " << _worst_output_latency << endl; +} + +void +Session::set_worst_capture_latency () +{ + if (_state_of_the_state & (InitialConnecting|Deletion)) { + return; + } + + _worst_input_latency = 0; + + if (!_engine.connected()) { + return; + } + + boost::shared_ptr r = routes.reader (); + + for (RouteList::iterator i = r->begin(); i != r->end(); ++i) { + _worst_input_latency = max (_worst_input_latency, (*i)->input_latency()); + } + + cerr << "Session: worst input latency = " << _worst_input_latency << endl; +} + +void +Session::update_latency_compensation (bool force_whole_graph) +{ + bool some_track_latency_changed = false; + + if (_state_of_the_state & Deletion) { + return; + } + + _worst_track_latency = 0; + + boost::shared_ptr r = routes.reader (); + + for (RouteList::iterator i = r->begin(); i != r->end(); ++i) { + if (!(*i)->hidden() && ((*i)->active())) { + nframes_t tl; + nframes_t ol = (*i)->signal_latency(); + if (ol != (tl = (*i)->update_own_latency ())) { + some_track_latency_changed = true; + } + _worst_track_latency = max (tl, _worst_track_latency); + } + } +} + diff --git a/libs/ardour/session_transport.cc b/libs/ardour/session_transport.cc index 88c6de1933..c8fe685a8e 100644 --- a/libs/ardour/session_transport.cc +++ b/libs/ardour/session_transport.cc @@ -405,7 +405,11 @@ Session::non_realtime_stop (bool abort, int on_entry, bool& finished) } if (_engine.running()) { - update_latency_compensation (true, abort); + PostTransportWork ptw = post_transport_work; + for (RouteList::iterator i = r->begin(); i != r->end(); ++i) { + (*i)->handle_transport_stopped (abort, (ptw & PostTransportLocate), (!(ptw & PostTransportLocate) || pending_locate_flush)); + } + update_latency_compensation (); } if ((Config->get_slave_source() == None && Config->get_auto_return()) || @@ -1350,72 +1354,10 @@ Session::xrun_recovery () } } -void -Session::update_latency_compensation (bool with_stop, bool abort) -{ - bool update_jack = false; - - if (_state_of_the_state & Deletion) { - return; - } - - _worst_track_latency = 0; - -#undef DEBUG_LATENCY -#ifdef DEBUG_LATENCY - cerr << "\n---------------------------------\nUPDATE LATENCY\n"; -#endif - - boost::shared_ptr r = routes.reader (); - - for (RouteList::iterator i = r->begin(); i != r->end(); ++i) { - if (with_stop) { - (*i)->handle_transport_stopped (abort, (post_transport_work & PostTransportLocate), - (!(post_transport_work & PostTransportLocate) || pending_locate_flush)); - } - - nframes_t old_latency = (*i)->signal_latency (); - nframes_t track_latency = (*i)->update_total_latency (); - - if (old_latency != track_latency) { - update_jack = true; - } - - if (!(*i)->hidden() && ((*i)->active())) { - _worst_track_latency = max (_worst_track_latency, track_latency); - } - } - -#ifdef DEBUG_LATENCY - cerr << "\tworst was " << _worst_track_latency << endl; -#endif - - for (RouteList::iterator i = r->begin(); i != r->end(); ++i) { - (*i)->set_latency_delay (_worst_track_latency); - } - - /* tell JACK to play catch up */ - - if (update_jack) { - _engine.update_total_latencies (); - } - - set_worst_io_latencies (); - - /* reflect any changes in latencies into capture offsets - */ - - boost::shared_ptr dsl = diskstreams.reader(); - - for (DiskstreamList::iterator i = dsl->begin(); i != dsl->end(); ++i) { - (*i)->set_capture_offset (); - } -} - void Session::route_redirects_changed (void* ignored) { - update_latency_compensation (false, false); + update_latency_compensation (); resort_routes (); } diff --git a/libs/ardour/track.cc b/libs/ardour/track.cc index 52989edeff..679f9d2380 100644 --- a/libs/ardour/track.cc +++ b/libs/ardour/track.cc @@ -91,28 +91,6 @@ Track::toggle_monitor_input () } } -nframes_t -Track::update_total_latency () -{ - _own_latency = 0; - - for (RedirectList::iterator i = _redirects.begin(); i != _redirects.end(); ++i) { - if ((*i)->active ()) { - _own_latency += (*i)->latency (); - } - } - -#undef DEBUG_LATENCY -#ifdef DEBUG_LATENCY - cerr << _name << ": internal redirect (final) latency = " << _own_latency << endl; -#endif - - set_port_latency (_own_latency); - - return _own_latency; -} - - Track::FreezeRecord::~FreezeRecord () { for (vector::iterator i = insert_info.begin(); i != insert_info.end(); ++i) { @@ -218,9 +196,9 @@ Track::set_name (string str, void *src) } void -Track::set_latency_delay (nframes_t longest_session_latency) +Track::set_latency_compensation (nframes_t longest_session_latency) { - Route::set_latency_delay (longest_session_latency); + Route::set_latency_compensation (longest_session_latency); _diskstream->set_roll_delay (_roll_delay); } -- cgit v1.2.3