summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorPaul Davis <paul@linuxaudiosystems.com>2011-09-14 19:05:05 +0000
committerPaul Davis <paul@linuxaudiosystems.com>2011-09-14 19:05:05 +0000
commita3ea8641e4be48a51ad7baaeb533865624a1e895 (patch)
tree0332e0492059457a9a055f3454c223b6ac617547
parent711d16a651a56d969a8cb8c0f0f9e30be418a912 (diff)
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
-rw-r--r--libs/ardour/ardour/audioengine.h11
-rw-r--r--libs/ardour/ardour/buffer.h4
-rw-r--r--libs/ardour/ardour/io.h4
-rw-r--r--libs/ardour/ardour/port.h18
-rw-r--r--libs/ardour/ardour/route.h9
-rw-r--r--libs/ardour/ardour/session.h11
-rw-r--r--libs/ardour/ardour/track.h3
-rw-r--r--libs/ardour/audio_diskstream.cc35
-rw-r--r--libs/ardour/audio_track.cc6
-rw-r--r--libs/ardour/audioengine.cc84
-rw-r--r--libs/ardour/io.cc14
-rw-r--r--libs/ardour/port.cc168
-rw-r--r--libs/ardour/route.cc140
-rw-r--r--libs/ardour/session.cc202
-rw-r--r--libs/ardour/session_transport.cc70
-rw-r--r--libs/ardour/track.cc26
16 files changed, 612 insertions, 193 deletions
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 <cstdlib> // for posix_memalign
#include <cassert>
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<Port*>& outputs() { return _outputs; }
+ std::vector<Port*>& 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<std::string> & 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<void,void*> solo_changed;
sigc::signal<void,void*> 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<Port*>& ports, std::vector<Port*>& 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<ChannelList> 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<Region> 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 <boost/scoped_ptr.hpp>
#include <glibmm/timer.h>
+#include <jack/weakjack.h>
#include <pbd/pthread_utils.h>
#include <pbd/stacktrace.h>
#include <pbd/epa.h>
@@ -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<AudioEngine*>(arg)->_freewheeling = onoff;
}
+void
+AudioEngine::_latency_callback (jack_latency_callback_mode_t mode, void* arg)
+{
+ return static_cast<AudioEngine *> (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<Ports> 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<Port *>::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<Port *>::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<Port *>::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 <jack/weakjack.h>
#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<std::string> & 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<string> 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<Port*>(this)->name() << " ========= " << connections.size() << " connections\n";
+
+ for (vector<string>::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<Port*>& from, vector<Port*>& 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<Port*>::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<Port*>::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<Port*>::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<Port*>& ports (inputs());
+ for (vector<Port*>::iterator p = ports.begin(); p != ports.end(); ++p) {
+ (*p)->set_public_latency_range (range, playback);
+ }
+ }
+
+ {
+ vector<Port*>& ports (outputs());
+ for (vector<Port*>::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
@@ -649,24 +649,6 @@ Session::destroy ()
}
void
-Session::set_worst_io_latencies ()
-{
- _worst_output_latency = 0;
- _worst_input_latency = 0;
-
- if (!_engine.connected()) {
- return;
- }
-
- boost::shared_ptr<RouteList> 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 ()
{
/* we don't want to run execute this again */
@@ -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)
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<RouteList> 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<RouteList> 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<RouteList> rl = routes.reader();
+
+ for (RouteList::iterator i = rl->begin(); i != rl->end(); ++i) {
+ boost::shared_ptr<Track> tr = boost::dynamic_pointer_cast<Track> (*i);
+ if (tr) {
+ boost::shared_ptr<Diskstream> 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<RouteList> 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<RouteList> 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<RouteList> 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()) ||
@@ -1351,71 +1355,9 @@ 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<RouteList> 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<DiskstreamList> 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<FreezeRecordInsertInfo*>::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);
}