sorta-kinda working latency compensation, latency reporting and capture alignment ... working except that we report the wrong information to JACK and i've noticed a couple of odd circumstances where turning on a latent plugin caused punch recording to fail

git-svn-id: svn://localhost/ardour2/branches/3.0@9121 d708f5d6-7413-0410-9779-e7cbd77b26cf
This commit is contained in:
Paul Davis 2011-03-11 02:55:52 +00:00
parent e806084402
commit d155f32039
30 changed files with 352 additions and 362 deletions

View file

@ -166,7 +166,7 @@ public:
return _data + offset; return _data + offset;
} }
void prepare () { _written = false; } void prepare () { _written = false; _silent = false; }
bool written() const { return _written; } bool written() const { return _written; }
private: private:

View file

@ -41,11 +41,11 @@ class AudioPort : public Port
size_t raw_buffer_size (pframes_t nframes) const; size_t raw_buffer_size (pframes_t nframes) const;
Buffer& get_buffer (framecnt_t nframes) { Buffer& get_buffer (pframes_t nframes) {
return get_audio_buffer (nframes); return get_audio_buffer (nframes);
} }
AudioBuffer& get_audio_buffer (framecnt_t nframes); AudioBuffer& get_audio_buffer (pframes_t nframes);
protected: protected:
friend class AudioEngine; friend class AudioEngine;

View file

@ -187,7 +187,6 @@ class AudioEngine : public SessionHandlePtr
void get_physical_inputs (DataType type, std::vector<std::string>&); void get_physical_inputs (DataType type, std::vector<std::string>&);
void update_total_latencies (); void update_total_latencies ();
void update_total_latency (const Port&);
Port *get_port_by_name (const std::string &); Port *get_port_by_name (const std::string &);
@ -320,10 +319,8 @@ _ the regular process() call to session->process() is not made.
void set_jack_callbacks (); void set_jack_callbacks ();
#ifdef HAVE_JACK_NEW_LATENCY
static void _latency_callback (jack_latency_callback_mode_t, void*); static void _latency_callback (jack_latency_callback_mode_t, void*);
void jack_latency_callback (jack_latency_callback_mode_t); void jack_latency_callback (jack_latency_callback_mode_t);
#endif
int connect_to_jack (std::string client_name, std::string session_uuid); int connect_to_jack (std::string client_name, std::string session_uuid);

View file

@ -75,7 +75,6 @@ public:
void flush_buffers (framecnt_t nframes, framepos_t time); void flush_buffers (framecnt_t nframes, framepos_t time);
void no_outs_cuz_we_no_monitor(bool); void no_outs_cuz_we_no_monitor(bool);
void cycle_start (pframes_t); void cycle_start (pframes_t);
void increment_output_offset (framecnt_t);
void transport_stopped (framepos_t frame); void transport_stopped (framepos_t frame);
BufferSet& output_buffers() { return *_output_buffers; } BufferSet& output_buffers() { return *_output_buffers; }
@ -105,7 +104,6 @@ public:
Role _role; Role _role;
BufferSet* _output_buffers; BufferSet* _output_buffers;
gain_t _current_gain; gain_t _current_gain;
framecnt_t _output_offset;
bool _no_outs_cuz_we_no_monitor; bool _no_outs_cuz_we_no_monitor;
boost::shared_ptr<MuteMaster> _mute_master; boost::shared_ptr<MuteMaster> _mute_master;
bool no_panner_reset; bool no_panner_reset;

View file

@ -90,6 +90,7 @@ class IO : public SessionObject, public Latent
bool set_name (const std::string& str); bool set_name (const std::string& str);
virtual void silence (framecnt_t); virtual void silence (framecnt_t);
void increment_port_buffer_offset (pframes_t offset);
int ensure_io (ChanCount cnt, bool clear, void *src); int ensure_io (ChanCount cnt, bool clear, void *src);
@ -111,9 +112,6 @@ class IO : public SessionObject, public Latent
framecnt_t signal_latency () const { return _own_latency; } framecnt_t signal_latency () const { return _own_latency; }
framecnt_t latency () const; framecnt_t latency () const;
void set_port_latency (framecnt_t);
void update_port_total_latencies ();
PortSet& ports() { return _ports; } PortSet& ports() { return _ports; }
const PortSet& ports() const { return _ports; } const PortSet& ports() const { return _ports; }

View file

@ -65,6 +65,8 @@ class IOProcessor : public Processor
void silence (framecnt_t nframes); void silence (framecnt_t nframes);
void disconnect (); void disconnect ();
void increment_port_buffer_offset (pframes_t);
virtual bool feeds (boost::shared_ptr<Route> other) const; virtual bool feeds (boost::shared_ptr<Route> other) const;
PBD::Signal2<void,IOProcessor*,bool> AutomationPlaybackChanged; PBD::Signal2<void,IOProcessor*,bool> AutomationPlaybackChanged;

View file

@ -46,11 +46,11 @@ class MidiPort : public Port {
size_t raw_buffer_size (pframes_t nframes) const; size_t raw_buffer_size (pframes_t nframes) const;
Buffer& get_buffer (framecnt_t nframes) { Buffer& get_buffer (pframes_t nframes) {
return get_midi_buffer (nframes); return get_midi_buffer (nframes);
} }
MidiBuffer& get_midi_buffer (framecnt_t nframes); MidiBuffer& get_midi_buffer (pframes_t nframes);
protected: protected:
friend class AudioEngine; friend class AudioEngine;

View file

@ -52,8 +52,6 @@ public:
return DataType::MIDI; return DataType::MIDI;
} }
void set_latency_delay (framecnt_t);
int export_stuff (BufferSet& bufs, framecnt_t nframes, framepos_t end_frame); int export_stuff (BufferSet& bufs, framecnt_t nframes, framepos_t end_frame);
void freeze_me (InterThreadInfo&); void freeze_me (InterThreadInfo&);

View file

@ -20,6 +20,8 @@
#ifndef __ardour_port_h__ #ifndef __ardour_port_h__
#define __ardour_port_h__ #define __ardour_port_h__
#include "libardour-config.h"
#include <set> #include <set>
#include <string> #include <string>
#include <vector> #include <vector>
@ -45,9 +47,6 @@ public:
virtual ~Port (); virtual ~Port ();
static void set_buffer_size (pframes_t sz) {
_buffer_size = sz;
}
static void set_connecting_blocked( bool yn ) { static void set_connecting_blocked( bool yn ) {
_connecting_blocked = yn; _connecting_blocked = yn;
} }
@ -93,16 +92,22 @@ public:
void ensure_monitor_input (bool); void ensure_monitor_input (bool);
bool monitoring_input () const; bool monitoring_input () const;
framecnt_t total_latency () const;
int reestablish (); int reestablish ();
int reconnect (); int reconnect ();
void request_monitor_input (bool); void request_monitor_input (bool);
void set_latency (framecnt_t);
#ifdef HAVE_JACK_NEW_LATENCY bool last_monitor() const { return _last_monitor; }
void set_last_monitor (bool yn) { _last_monitor = yn; }
jack_port_t* jack_port() const { return _jack_port; }
void get_connected_latency_range (jack_latency_range_t& range, bool playback) const; void get_connected_latency_range (jack_latency_range_t& range, bool playback) const;
void set_latency_range (jack_latency_range_t& range, bool playback) const;
#endif 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;
virtual void reset (); virtual void reset ();
@ -110,10 +115,10 @@ public:
virtual size_t raw_buffer_size (pframes_t nframes) const = 0; virtual size_t raw_buffer_size (pframes_t nframes) const = 0;
virtual DataType type () const = 0; virtual DataType type () const = 0;
virtual void cycle_start (pframes_t) = 0; virtual void cycle_start (pframes_t);
virtual void cycle_end (pframes_t) = 0; virtual void cycle_end (pframes_t) = 0;
virtual void cycle_split () = 0; virtual void cycle_split () = 0;
virtual Buffer& get_buffer (framecnt_t nframes) = 0; virtual Buffer& get_buffer (pframes_t nframes) = 0;
virtual void flush_buffers (pframes_t nframes, framepos_t /*time*/) {} virtual void flush_buffers (pframes_t nframes, framepos_t /*time*/) {}
virtual void transport_stopped () {} virtual void transport_stopped () {}
@ -124,15 +129,18 @@ public:
PBD::Signal1<void,bool> MonitorInputChanged; PBD::Signal1<void,bool> MonitorInputChanged;
static framecnt_t port_offset() { return _port_offset; } static void set_cycle_framecnt (pframes_t n) {
_cycle_nframes = n;
static void set_port_offset (framecnt_t off) { }
_port_offset = off; static framecnt_t port_offset() { return _global_port_buffer_offset; }
static void set_global_port_buffer_offset (pframes_t off) {
_global_port_buffer_offset = off;
}
static void increment_global_port_buffer_offset (pframes_t n) {
_global_port_buffer_offset += n;
} }
static void increment_port_offset (framecnt_t n) { virtual void increment_port_buffer_offset (pframes_t n);
_port_offset += n;
}
protected: protected:
@ -140,25 +148,25 @@ protected:
jack_port_t* _jack_port; ///< JACK port jack_port_t* _jack_port; ///< JACK port
static pframes_t _buffer_size;
static bool _connecting_blocked; static bool _connecting_blocked;
static framecnt_t _port_offset; static pframes_t _global_port_buffer_offset; /* access only from process() tree */
static pframes_t _cycle_nframes; /* access only from process() tree */
framecnt_t _port_buffer_offset; /* access only from process() tree */
jack_latency_range_t _private_playback_latency;
jack_latency_range_t _private_capture_latency;
static AudioEngine* _engine; ///< the AudioEngine static AudioEngine* _engine; ///< the AudioEngine
private: private:
friend class AudioEngine;
void recompute_total_latency () const;
/* XXX */
bool _last_monitor;
std::string _name; ///< port short name std::string _name; ///< port short name
Flags _flags; ///< flags Flags _flags; ///< flags
bool _last_monitor;
/** ports that we are connected to, kept so that we can /** ports that we are connected to, kept so that we can
reconnect to JACK when required */ reconnect to JACK when required
*/
std::set<std::string> _connections; std::set<std::string> _connections;
}; };

View file

@ -250,11 +250,15 @@ class Route : public SessionObject, public Automatable, public RouteGroupMember,
void all_processors_flip(); void all_processors_flip();
void all_processors_active (Placement, bool state); void all_processors_active (Placement, bool state);
void set_latency_ranges (bool playback) const; framecnt_t set_private_port_latencies (bool playback) const;
virtual framecnt_t update_total_latency(); void set_public_port_latencies (framecnt_t, bool playback) const;
void set_latency_delay (framecnt_t);
framecnt_t update_signal_latency();
virtual void set_latency_compensation (framecnt_t);
void set_user_latency (framecnt_t); void set_user_latency (framecnt_t);
framecnt_t initial_delay() const { return _initial_delay; } framecnt_t initial_delay() const { return _initial_delay; }
framecnt_t signal_latency() const { return _signal_latency; }
PBD::Signal0<void> active_changed; PBD::Signal0<void> active_changed;
PBD::Signal0<void> phase_invert_changed; PBD::Signal0<void> phase_invert_changed;
@ -426,6 +430,7 @@ class Route : public SessionObject, public Automatable, public RouteGroupMember,
boost::shared_ptr<IO> _output; boost::shared_ptr<IO> _output;
bool _active; bool _active;
framecnt_t _signal_latency;
framecnt_t _initial_delay; framecnt_t _initial_delay;
framecnt_t _roll_delay; framecnt_t _roll_delay;
@ -517,7 +522,7 @@ class Route : public SessionObject, public Automatable, public RouteGroupMember,
void set_mute_master_solo (); void set_mute_master_solo ();
void set_processor_positions (); void set_processor_positions ();
void update_port_latencies (const PortSet& ports, const PortSet& feeders, bool playback, framecnt_t) const; framecnt_t update_port_latencies (const PortSet& ports, const PortSet& feeders, bool playback, framecnt_t) const;
void setup_invisible_processors (); void setup_invisible_processors ();

View file

@ -20,6 +20,8 @@
#ifndef __ardour_session_h__ #ifndef __ardour_session_h__
#define __ardour_session_h__ #define __ardour_session_h__
#include "libardour-config.h"
#include <list> #include <list>
#include <map> #include <map>
#include <set> #include <set>
@ -818,7 +820,7 @@ class Session : public PBD::StatefulDestructible, public PBD::ScopedConnectionLi
protected: protected:
friend class Route; friend class Route;
void schedule_curve_reallocation (); void schedule_curve_reallocation ();
void update_latency_compensation (bool, bool); void update_latency_compensation (bool, bool, bool force=false);
private: private:
int create (const std::string& mix_template, BusProfile*); int create (const std::string& mix_template, BusProfile*);
@ -1364,9 +1366,7 @@ class Session : public PBD::StatefulDestructible, public PBD::ScopedConnectionLi
void reset_jack_connection (jack_client_t* jack); void reset_jack_connection (jack_client_t* jack);
void process_rtop (SessionEvent*); void process_rtop (SessionEvent*);
#ifdef HAVE_JACK_NEW_LATENCY
void update_latency (bool playback); void update_latency (bool playback);
#endif
XMLNode& state(bool); XMLNode& state(bool);

View file

@ -67,8 +67,7 @@ class Track : public Route, public PublicDiskstream
virtual void use_new_diskstream () = 0; virtual void use_new_diskstream () = 0;
virtual void set_diskstream (boost::shared_ptr<Diskstream>); virtual void set_diskstream (boost::shared_ptr<Diskstream>);
framecnt_t update_total_latency(); void set_latency_compensation (framecnt_t);
void set_latency_delay (framecnt_t);
enum FreezeState { enum FreezeState {
NoFreeze, NoFreeze,

View file

@ -390,12 +390,12 @@ AudioDiskstream::prepare_record_status(framepos_t capture_start_frame)
boost::shared_ptr<ChannelList> c = channels.reader(); boost::shared_ptr<ChannelList> c = channels.reader();
for (ChannelList::iterator chan = c->begin(); chan != c->end(); ++chan) { for (ChannelList::iterator chan = c->begin(); chan != c->end(); ++chan) {
RingBufferNPT<CaptureTransition>::rw_vector transvec; RingBufferNPT<CaptureTransition>::rw_vector transitions;
(*chan)->capture_transition_buf->get_write_vector(&transvec); (*chan)->capture_transition_buf->get_write_vector (&transitions);
if (transvec.len[0] > 0) { if (transitions.len[0] > 0) {
transvec.buf[0]->type = CaptureStart; transitions.buf[0]->type = CaptureStart;
transvec.buf[0]->capture_val = capture_start_frame; transitions.buf[0]->capture_val = capture_start_frame;
(*chan)->capture_transition_buf->increment_write_ptr(1); (*chan)->capture_transition_buf->increment_write_ptr(1);
} else { } else {
// bad! // bad!
@ -1481,7 +1481,7 @@ AudioDiskstream::transport_stopped_wallclock (struct tm& when, time_t twhen, boo
RegionFactory::region_name (region_name, whole_file_region_name, false); RegionFactory::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 << ": based on ci of " << (*ci)->start << " for " << (*ci)->frames << " add region " << region_name << endl;
try { try {
@ -1548,7 +1548,7 @@ AudioDiskstream::transport_looped (framepos_t transport_frame)
capture_captured += _capture_offset; capture_captured += _capture_offset;
if (_alignment_style == ExistingMaterial) { if (_alignment_style == ExistingMaterial) {
capture_captured += _session.worst_playback_latency(); capture_captured += _session.worst_output_latency();
} else { } else {
capture_captured += _roll_delay; capture_captured += _roll_delay;
} }

View file

@ -45,6 +45,8 @@ AudioPort::cycle_start (pframes_t nframes)
{ {
/* caller must hold process lock */ /* caller must hold process lock */
Port::cycle_start (nframes);
if (sends_output()) { if (sends_output()) {
_buffer->prepare (); _buffer->prepare ();
} }
@ -67,10 +69,11 @@ AudioPort::cycle_split ()
} }
AudioBuffer& AudioBuffer&
AudioPort::get_audio_buffer (framecnt_t nframes) AudioPort::get_audio_buffer (pframes_t nframes)
{ {
/* caller must hold process lock */ /* caller must hold process lock */
_buffer->set_data ((Sample *) jack_port_get_buffer (_jack_port, nframes) + _port_offset, nframes); _buffer->set_data ((Sample *) jack_port_get_buffer (_jack_port, _cycle_nframes) +
_global_port_buffer_offset + _port_buffer_offset, nframes);
return *_buffer; return *_buffer;
} }

View file

@ -387,6 +387,7 @@ AudioTrack::roll (pframes_t nframes, framepos_t start_frame, framepos_t end_fram
playback distance to zero, thus causing diskstream::commit playback distance to zero, thus causing diskstream::commit
to do nothing. to do nothing.
*/ */
cerr << name() << " Can't operate at " << transport_frame << " since roll delay is only " << _roll_delay << endl;
return diskstream->process (transport_frame, 0, can_record, rec_monitors_input, need_butler); return diskstream->process (transport_frame, 0, can_record, rec_monitors_input, need_butler);
} }

View file

@ -190,11 +190,10 @@ AudioEngine::set_jack_callbacks ()
if( jack_set_session_callback) if( jack_set_session_callback)
jack_set_session_callback (_priv_jack, _session_callback, this); jack_set_session_callback (_priv_jack, _session_callback, this);
#endif #endif
#if HAVE_JACK_NEW_LATENCY
if (jack_set_latency_callback) { if (jack_set_latency_callback) {
jack_set_latency_callback (_priv_jack, _latency_callback, this); jack_set_latency_callback (_priv_jack, _latency_callback, this);
} }
#endif
jack_set_error_function (ardour_jack_error); jack_set_error_function (ardour_jack_error);
} }
@ -389,13 +388,11 @@ AudioEngine::_registration_callback (jack_port_id_t /*id*/, int /*reg*/, void* a
ae->PortRegisteredOrUnregistered (); /* EMIT SIGNAL */ ae->PortRegisteredOrUnregistered (); /* EMIT SIGNAL */
} }
#ifdef HAVE_JACK_NEW_LATENCY
void void
AudioEngine::_latency_callback (jack_latency_callback_mode_t mode, void* arg) AudioEngine::_latency_callback (jack_latency_callback_mode_t mode, void* arg)
{ {
return static_cast<AudioEngine *> (arg)->jack_latency_callback (mode); return static_cast<AudioEngine *> (arg)->jack_latency_callback (mode);
} }
#endif
void void
AudioEngine::_connect_callback (jack_port_id_t id_a, jack_port_id_t id_b, int conn, void* arg) AudioEngine::_connect_callback (jack_port_id_t id_a, jack_port_id_t id_b, int conn, void* arg)
@ -413,9 +410,9 @@ AudioEngine::_connect_callback (jack_port_id_t id_a, jack_port_id_t id_b, int co
boost::shared_ptr<Ports> pr = ae->ports.reader (); boost::shared_ptr<Ports> pr = ae->ports.reader ();
Ports::iterator i = pr->begin (); Ports::iterator i = pr->begin ();
while (i != pr->end() && (port_a == 0 || port_b == 0)) { while (i != pr->end() && (port_a == 0 || port_b == 0)) {
if (jack_port_a == (*i)->_jack_port) { if (jack_port_a == (*i)->jack_port()) {
port_a = *i; port_a = *i;
} else if (jack_port_b == (*i)->_jack_port) { } else if (jack_port_b == (*i)->jack_port()) {
port_b = *i; port_b = *i;
} }
++i; ++i;
@ -429,7 +426,7 @@ AudioEngine::split_cycle (pframes_t offset)
{ {
/* caller must hold process lock */ /* caller must hold process lock */
AudioPort::increment_port_offset (offset); Port::increment_global_port_buffer_offset (offset);
/* tell all Ports that we're going to start a new (split) cycle */ /* tell all Ports that we're going to start a new (split) cycle */
@ -512,7 +509,8 @@ AudioEngine::process_callback (pframes_t nframes)
/* tell all relevant objects that we're starting a new cycle */ /* tell all relevant objects that we're starting a new cycle */
Delivery::CycleStart (nframes); Delivery::CycleStart (nframes);
AudioPort::set_port_offset (0); Port::set_global_port_buffer_offset (0);
Port::set_cycle_framecnt (nframes);
InternalReturn::CycleStart (nframes); InternalReturn::CycleStart (nframes);
/* tell all Ports that we're starting a new cycle */ /* tell all Ports that we're starting a new cycle */
@ -560,8 +558,8 @@ AudioEngine::process_callback (pframes_t nframes)
Port *port = (*i); Port *port = (*i);
bool x; bool x;
if (port->_last_monitor != (x = port->monitoring_input ())) { if (port->last_monitor() != (x = port->monitoring_input ())) {
port->_last_monitor = x; port->set_last_monitor (x);
/* XXX I think this is dangerous, due to /* XXX I think this is dangerous, due to
a likely mutex in the signal handlers ... a likely mutex in the signal handlers ...
*/ */
@ -621,7 +619,6 @@ AudioEngine::jack_sample_rate_callback (pframes_t nframes)
return 0; return 0;
} }
#ifdef HAVE_JACK_NEW_LATENCY
void void
AudioEngine::jack_latency_callback (jack_latency_callback_mode_t mode) AudioEngine::jack_latency_callback (jack_latency_callback_mode_t mode)
{ {
@ -629,7 +626,6 @@ AudioEngine::jack_latency_callback (jack_latency_callback_mode_t mode)
_session->update_latency (mode == JackPlaybackLatency); _session->update_latency (mode == JackPlaybackLatency);
} }
} }
#endif
int int
AudioEngine::_bufsize_callback (pframes_t nframes, void *arg) AudioEngine::_bufsize_callback (pframes_t nframes, void *arg)
@ -1188,12 +1184,6 @@ AudioEngine::get_physical_outputs (DataType type, vector<string>& outs)
get_physical (type, JackPortIsInput, outs); get_physical (type, JackPortIsInput, outs);
} }
void
AudioEngine::update_total_latency (const Port& port)
{
port.recompute_total_latency ();
}
void void
AudioEngine::transport_stop () AudioEngine::transport_stop ()
{ {

View file

@ -59,7 +59,6 @@ Delivery::Delivery (Session& s, boost::shared_ptr<IO> io, boost::shared_ptr<Pann
, _role (r) , _role (r)
, _output_buffers (new BufferSet()) , _output_buffers (new BufferSet())
, _current_gain (1.0) , _current_gain (1.0)
, _output_offset (0)
, _no_outs_cuz_we_no_monitor (false) , _no_outs_cuz_we_no_monitor (false)
, _mute_master (mm) , _mute_master (mm)
, no_panner_reset (false) , no_panner_reset (false)
@ -81,7 +80,6 @@ Delivery::Delivery (Session& s, boost::shared_ptr<Pannable> pannable, boost::sha
, _role (r) , _role (r)
, _output_buffers (new BufferSet()) , _output_buffers (new BufferSet())
, _current_gain (1.0) , _current_gain (1.0)
, _output_offset (0)
, _no_outs_cuz_we_no_monitor (false) , _no_outs_cuz_we_no_monitor (false)
, _mute_master (mm) , _mute_master (mm)
, no_panner_reset (false) , no_panner_reset (false)
@ -123,16 +121,9 @@ Delivery::display_name () const
void void
Delivery::cycle_start (pframes_t /*nframes*/) Delivery::cycle_start (pframes_t /*nframes*/)
{ {
_output_offset = 0;
_no_outs_cuz_we_no_monitor = false; _no_outs_cuz_we_no_monitor = false;
} }
void
Delivery::increment_output_offset (framecnt_t n)
{
_output_offset += n;
}
bool bool
Delivery::can_support_io_configuration (const ChanCount& in, ChanCount& out) const Delivery::can_support_io_configuration (const ChanCount& in, ChanCount& out) const
{ {

View file

@ -640,6 +640,9 @@ Diskstream::check_record_status (framepos_t transport_frame, bool can_record)
if (possibly_recording == last_possibly_recording) { if (possibly_recording == last_possibly_recording) {
return; return;
} }
framecnt_t existing_material_offset = _session.worst_playback_latency();
if (possibly_recording == fully_rec_enabled) { if (possibly_recording == fully_rec_enabled) {
if (last_possibly_recording == fully_rec_enabled) { if (last_possibly_recording == fully_rec_enabled) {
@ -650,52 +653,30 @@ Diskstream::check_record_status (framepos_t transport_frame, bool can_record)
first_recordable_frame = transport_frame + _capture_offset; first_recordable_frame = transport_frame + _capture_offset;
last_recordable_frame = max_framepos; last_recordable_frame = max_framepos;
capture_start_frame = transport_frame; capture_start_frame = _session.transport_frame();
DEBUG_TRACE (DEBUG::CaptureAlignment, string_compose ("%1: @ %7 basic FRF = %2 LRF = %3 CSF = %4 CO = %5, WPL = %6\n", /* in theory, we should be offsetting by _session.worst_playback_latency() when we adjust
for ExistingMaterial alignment. But that number includes the worst processor latency
across all routes, and each track will already be roll-delay adjusted to handle that.
so don't use worst_playback_latency(), just worst_output_latency() which covers
only downstream latency from IO ports.
*/
DEBUG_TRACE (DEBUG::CaptureAlignment, string_compose ("%1: @ %7 basic FRF = %2 CSF = %4 CO = %5, EMO = %6 RD = %8\n",
name(), first_recordable_frame, last_recordable_frame, capture_start_frame, name(), first_recordable_frame, last_recordable_frame, capture_start_frame,
_capture_offset, _capture_offset,
_session.worst_playback_latency(), existing_material_offset,
transport_frame)); transport_frame,
_roll_delay));
if (change & transport_rolling) {
/* transport-change (started rolling) */
if (_alignment_style == ExistingMaterial) {
/* audio played by ardour will take (up to) _session.worst_playback_latency() ("WOL") to
appear at the speakers; audio played at the time when it does appear at
the speakers will take _capture_offset to arrive back here. we've
already added _capture_offset, so now add WOL.
*/
first_recordable_frame += _session.worst_playback_latency();
DEBUG_TRACE (DEBUG::CaptureAlignment, string_compose ("\tROLL: shift FRF by delta between WOL %1\n",
first_recordable_frame));
} else {
first_recordable_frame += _roll_delay;
DEBUG_TRACE (DEBUG::CaptureAlignment, string_compose ("\tROLL: shift FRF by roll delay of %1 to %2\n",
_roll_delay, first_recordable_frame));
}
if (_alignment_style == ExistingMaterial) {
first_recordable_frame += existing_material_offset;
DEBUG_TRACE (DEBUG::CaptureAlignment, string_compose ("\tshift FRF by EMO %1\n",
first_recordable_frame));
} else { } else {
capture_start_frame += _roll_delay;
/* punch in */ DEBUG_TRACE (DEBUG::CaptureAlignment, string_compose ("\tshift CFS by roll delay of %1 to %2\n",
_roll_delay, capture_start_frame));
if (_alignment_style == ExistingMaterial) {
/* see comment in ExistingMaterial block above */
first_recordable_frame += _session.worst_playback_latency();
DEBUG_TRACE (DEBUG::CaptureAlignment, string_compose ("\tMANUAL PUNCH: shift FRF by delta between WOL and CO to %1\n",
first_recordable_frame));
} else {
capture_start_frame -= _roll_delay;
DEBUG_TRACE (DEBUG::CaptureAlignment, string_compose ("\tPUNCH: shift CSF by roll delay of %1 to %2\n",
_roll_delay, capture_start_frame));
}
} }
prepare_record_status (capture_start_frame); prepare_record_status (capture_start_frame);
@ -707,7 +688,11 @@ Diskstream::check_record_status (framepos_t transport_frame, bool can_record)
/* we were recording last time */ /* we were recording last time */
if (change & transport_rolling) { if (change & transport_rolling) {
/* transport-change (stopped rolling): last_recordable_frame was set in ::prepare_to_stop() */
/* transport-change (stopped rolling): last_recordable_frame was set in ::prepare_to_stop(). We
had to set it there because we likely rolled past the stopping point to declick out,
and then backed up.
*/
} else { } else {
/* punch out */ /* punch out */
@ -715,7 +700,7 @@ Diskstream::check_record_status (framepos_t transport_frame, bool can_record)
last_recordable_frame = transport_frame + _capture_offset; last_recordable_frame = transport_frame + _capture_offset;
if (_alignment_style == ExistingMaterial) { if (_alignment_style == ExistingMaterial) {
last_recordable_frame += _session.worst_input_latency(); last_recordable_frame += existing_material_offset;
} else { } else {
last_recordable_frame += _roll_delay; last_recordable_frame += _roll_delay;
} }

View file

@ -96,6 +96,18 @@ IO::~IO ()
} }
} }
void
IO::increment_port_buffer_offset (pframes_t offset)
{
/* io_lock, not taken: function must be called from Session::process() calltree */
if (_direction == Output) {
for (PortSet::iterator i = _ports.begin(); i != _ports.end(); ++i) {
i->increment_port_buffer_offset (offset);
}
}
}
void void
IO::silence (framecnt_t nframes) IO::silence (framecnt_t nframes)
{ {
@ -1133,16 +1145,6 @@ IO::set_name (const string& requested_name)
return r; return r;
} }
void
IO::set_port_latency (framecnt_t nframes)
{
Glib::Mutex::Lock lm (io_lock);
for (PortSet::iterator i = _ports.begin(); i != _ports.end(); ++i) {
i->set_latency (nframes);
}
}
framecnt_t framecnt_t
IO::latency () const IO::latency () const
{ {
@ -1154,26 +1156,17 @@ IO::latency () const
/* io lock not taken - must be protected by other means */ /* io lock not taken - must be protected by other means */
for (PortSet::const_iterator i = _ports.begin(); i != _ports.end(); ++i) { for (PortSet::const_iterator i = _ports.begin(); i != _ports.end(); ++i) {
if ((latency = i->total_latency ()) > max_latency) { if ((latency = i->public_latency_range (_direction == Output).max) > max_latency) {
max_latency = latency; max_latency = latency;
} }
} }
DEBUG_TRACE (DEBUG::Latency, string_compose ("%1: max latency from %2 ports = %3\n", DEBUG_TRACE (DEBUG::Latency, string_compose ("%1: max %4 latency from %2 ports = %3\n",
name(), _ports.num_ports(), max_latency)); name(), _ports.num_ports(), max_latency,
((_direction == Output) ? "PLAYBACK" : "CAPTURE")));
return max_latency; return max_latency;
} }
void
IO::update_port_total_latencies ()
{
/* io_lock, not taken: function must be called from Session::process() calltree */
for (PortSet::iterator i = _ports.begin(); i != _ports.end(); ++i) {
_session.engine().update_total_latency (*i);
}
}
int int
IO::connect_ports_to_bundle (boost::shared_ptr<Bundle> c, void* src) IO::connect_ports_to_bundle (boost::shared_ptr<Bundle> c, void* src)
{ {

View file

@ -249,6 +249,14 @@ IOProcessor::silence (framecnt_t nframes)
} }
} }
void
IOProcessor::increment_port_buffer_offset (pframes_t offset)
{
if (_own_output && _output) {
_output->increment_port_buffer_offset (offset);
}
}
ChanCount ChanCount
IOProcessor::natural_output_streams() const IOProcessor::natural_output_streams() const
{ {

View file

@ -1104,7 +1104,7 @@ MidiDiskstream::transport_looped (framepos_t transport_frame)
capture_captured += _capture_offset; capture_captured += _capture_offset;
if (_alignment_style == ExistingMaterial) { if (_alignment_style == ExistingMaterial) {
capture_captured += _session.worst_playback_latency(); capture_captured += _session.worst_output_latency();
} else { } else {
capture_captured += _roll_delay; capture_captured += _roll_delay;
} }

View file

@ -41,7 +41,10 @@ MidiPort::~MidiPort()
void void
MidiPort::cycle_start (pframes_t nframes) MidiPort::cycle_start (pframes_t nframes)
{ {
Port::cycle_start (nframes);
_buffer->clear (); _buffer->clear ();
assert (_buffer->size () == 0); assert (_buffer->size () == 0);
if (sends_output ()) { if (sends_output ()) {
@ -50,7 +53,7 @@ MidiPort::cycle_start (pframes_t nframes)
} }
MidiBuffer & MidiBuffer &
MidiPort::get_midi_buffer (framecnt_t nframes) MidiPort::get_midi_buffer (pframes_t nframes)
{ {
if (_has_been_mixed_down) { if (_has_been_mixed_down) {
return *_buffer; return *_buffer;
@ -59,7 +62,7 @@ MidiPort::get_midi_buffer (framecnt_t nframes)
if (receives_input ()) { if (receives_input ()) {
void* jack_buffer = jack_port_get_buffer (_jack_port, nframes); void* jack_buffer = jack_port_get_buffer (_jack_port, nframes);
const pframes_t event_count = jack_midi_get_event_count(jack_buffer); const pframes_t event_count = jack_midi_get_event_count (jack_buffer);
assert (event_count < _buffer->capacity()); assert (event_count < _buffer->capacity());
@ -78,17 +81,18 @@ MidiPort::get_midi_buffer (framecnt_t nframes)
continue; continue;
} }
if (ev.time >= _port_offset && ev.time < (_port_offset + nframes)) { /* check that the event is in the acceptable time range */
if ((ev.time >= (_global_port_buffer_offset + _port_buffer_offset)) &&
(ev.time < (_global_port_buffer_offset + _port_buffer_offset + nframes))) {
_buffer->push_back (ev); _buffer->push_back (ev);
} else { } else {
cerr << "Dropping incoming MIDI at time " << ev.time << "; offset=" << _port_offset << " limit=" << (_port_offset + nframes) << "\n"; cerr << "Dropping incoming MIDI at time " << ev.time << "; offset="
<< _global_port_buffer_offset << " limit="
<< (_global_port_buffer_offset + _port_buffer_offset + nframes) << "\n";
} }
} }
if (nframes) {
_has_been_mixed_down = true;
}
} else { } else {
_buffer->silence (nframes); _buffer->silence (nframes);
} }
@ -137,14 +141,16 @@ MidiPort::flush_buffers (pframes_t nframes, framepos_t time)
// event times are in frames, relative to cycle start // event times are in frames, relative to cycle start
assert (ev.time() < (nframes + _port_offset)); assert (ev.time() < (nframes + _global_port_buffer_offset + _port_buffer_offset));
if (ev.time() >= _port_offset) { if (ev.time() >= _global_port_buffer_offset + _port_buffer_offset) {
if (jack_midi_event_write (jack_buffer, (jack_nframes_t) ev.time(), ev.buffer(), ev.size()) != 0) { if (jack_midi_event_write (jack_buffer, (jack_nframes_t) ev.time(), ev.buffer(), ev.size()) != 0) {
cerr << "write failed, drop flushed note off on the floor, time " << ev.time() << " > " << _port_offset << endl; cerr << "write failed, drop flushed note off on the floor, time "
<< ev.time() << " > " << _global_port_buffer_offset + _port_buffer_offset << endl;
} }
} else { } else {
cerr << "drop flushed event on the floor, time " << ev.time() << " < " << _port_offset << endl; cerr << "drop flushed event on the floor, time " << ev.time()
<< " < " << _global_port_buffer_offset + _port_buffer_offset << endl;
} }
} }
} }

View file

@ -464,13 +464,6 @@ MidiTrack::export_stuff (BufferSet& /*bufs*/, framecnt_t /*nframes*/, framepos_t
return -1; return -1;
} }
void
MidiTrack::set_latency_delay (framecnt_t longest_session_latency)
{
Route::set_latency_delay (longest_session_latency);
_diskstream->set_roll_delay (_roll_delay);
}
boost::shared_ptr<Region> boost::shared_ptr<Region>
MidiTrack::bounce (InterThreadInfo& /*itt*/) MidiTrack::bounce (InterThreadInfo& /*itt*/)
{ {

View file

@ -40,15 +40,16 @@ using namespace ARDOUR;
using namespace PBD; using namespace PBD;
AudioEngine* Port::_engine = 0; AudioEngine* Port::_engine = 0;
pframes_t Port::_buffer_size = 0;
bool Port::_connecting_blocked = false; bool Port::_connecting_blocked = false;
framecnt_t Port::_port_offset = 0; pframes_t Port::_global_port_buffer_offset = 0;
pframes_t Port::_cycle_nframes = 0;
/** @param n Port short name */ /** @param n Port short name */
Port::Port (std::string const & n, DataType t, Flags f) Port::Port (std::string const & n, DataType t, Flags f)
: _last_monitor (false) : _port_buffer_offset (0)
, _name (n) , _name (n)
, _flags (f) , _flags (f)
, _last_monitor (false)
{ {
/* Unfortunately we have to pass the DataType into this constructor so that we can /* Unfortunately we have to pass the DataType into this constructor so that we can
@ -217,41 +218,79 @@ void
Port::reset () Port::reset ()
{ {
_last_monitor = false; _last_monitor = false;
// XXX
// _metering = 0;
// reset_meters ();
} }
void void
Port::recompute_total_latency () const Port::cycle_start (pframes_t nframes)
{ {
#ifndef HAVE_JACK_NEW_LATENCY _port_buffer_offset = 0;
#ifdef HAVE_JACK_RECOMPUTE_LATENCY
jack_client_t* jack = _engine->jack();
if (!jack) {
return;
}
jack_recompute_total_latency (jack, _jack_port);
#endif
#endif
} }
#ifdef HAVE_JACK_NEW_LATENCY
void void
Port::set_latency_range (jack_latency_range_t& range, bool playback) const Port::increment_port_buffer_offset (pframes_t nframes)
{ {
_port_buffer_offset += nframes;
}
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) { if (!jack_port_set_latency_range) {
return; return;
} }
DEBUG_TRACE (DEBUG::Latency, string_compose ("PORT %1 %4 latency now [%2 - %3]\n", name(),
range.min,
range.max,
(playback ? "PLAYBACK" : "CAPTURE")));;
jack_port_set_latency_range (_jack_port, (playback ? JackPlaybackLatency : JackCaptureLatency), &range); jack_port_set_latency_range (_jack_port, (playback ? JackPlaybackLatency : JackCaptureLatency), &range);
} }
#endif
#ifdef HAVE_JACK_NEW_LATENCY void
Port::set_private_latency_range (jack_latency_range_t& range, bool playback)
{
if (playback) {
_private_playback_latency = range;
DEBUG_TRACE (DEBUG::Latency, string_compose ("PORT %1 playback latency now [%2 - %3]\n", name(),
_private_playback_latency.min,
_private_playback_latency.max));
} else {
_private_capture_latency = range;
DEBUG_TRACE (DEBUG::Latency, string_compose ("PORT %1 capture latency now [%2 - %3]\n", name(),
_private_playback_latency.min,
_private_playback_latency.max));
}
}
const jack_latency_range_t&
Port::private_latency_range (bool playback) const
{
if (playback) {
return _private_playback_latency;
} else {
return _private_capture_latency;
}
}
jack_latency_range_t
Port::public_latency_range (bool playback) const
{
jack_latency_range_t r;
jack_port_get_latency_range (_jack_port,
sends_output() ? JackPlaybackLatency : JackCaptureLatency,
&r);
DEBUG_TRACE (DEBUG::Latency, string_compose ("PORT %1: %4 public latency range %2 .. %3\n",
name(), r.min, r.max,
sends_output() ? "PLAYBACK" : "CAPTURE"));
return r;
}
void void
Port::get_connected_latency_range (jack_latency_range_t& range, bool playback) const Port::get_connected_latency_range (jack_latency_range_t& range, bool playback) const
{ {
@ -280,11 +319,9 @@ Port::get_connected_latency_range (jack_latency_range_t& range, bool playback) c
jack_port_t* remote_port = jack_port_by_name (_engine->jack(), (*c).c_str()); jack_port_t* remote_port = jack_port_by_name (_engine->jack(), (*c).c_str());
jack_latency_range_t lr; jack_latency_range_t lr;
DEBUG_TRACE (DEBUG::Latency, string_compose ("\t%1 connected to %2\n", name(), *c));
if (remote_port) { if (remote_port) {
jack_port_get_latency_range (remote_port, (playback ? JackPlaybackLatency : JackCaptureLatency), &lr); jack_port_get_latency_range (remote_port, (playback ? JackPlaybackLatency : JackCaptureLatency), &lr);
DEBUG_TRACE (DEBUG::Latency, string_compose ("\t\tremote has latency range %1 .. %2\n", lr.min, lr.max)); DEBUG_TRACE (DEBUG::Latency, string_compose ("\t\%1 has latency range %2 .. %3\n", *c, lr.min, lr.max));
range.min = min (range.min, lr.min); range.min = min (range.min, lr.min);
range.max = max (range.max, lr.max); range.max = max (range.max, lr.max);
} }
@ -296,29 +333,6 @@ Port::get_connected_latency_range (jack_latency_range_t& range, bool playback) c
range.max = 0; range.max = 0;
} }
} }
#endif /* HAVE_JACK_NEW_LATENCY */
framecnt_t
Port::total_latency () const
{
#ifndef HAVE_JACK_NEW_LATENCY
jack_client_t* jack = _engine->jack();
if (!jack) {
return 0;
}
return jack_port_get_total_latency (jack, _jack_port);
#else
jack_latency_range_t r;
jack_port_get_latency_range (_jack_port,
sends_output() ? JackPlaybackLatency : JackCaptureLatency,
&r);
DEBUG_TRACE (DEBUG::Latency, string_compose ("PORT %1: latency range %2 .. %3\n",
name(), r.min, r.max));
return r.max;
#endif
}
int int
Port::reestablish () Port::reestablish ()
@ -329,7 +343,6 @@ Port::reestablish ()
return -1; return -1;
} }
cerr << "RE-REGISTER: " << _name.c_str() << endl;
_jack_port = jack_port_register (jack, _name.c_str(), type().to_jack_type(), _flags, 0); _jack_port = jack_port_register (jack, _name.c_str(), type().to_jack_type(), _flags, 0);
if (_jack_port == 0) { if (_jack_port == 0) {
@ -380,14 +393,6 @@ Port::request_monitor_input (bool yn)
jack_port_request_monitor (_jack_port, yn); jack_port_request_monitor (_jack_port, yn);
} }
void
Port::set_latency (framecnt_t n)
{
#ifndef HAVE_JACK_NEW_LATENCY
jack_port_set_latency (_jack_port, n);
#endif
}
bool bool
Port::physically_connected () const Port::physically_connected () const
{ {

View file

@ -84,6 +84,7 @@ Route::Route (Session& sess, string name, Flag flg, DataType default_type)
, Automatable (sess) , Automatable (sess)
, GraphNode( sess.route_graph ) , GraphNode( sess.route_graph )
, _active (true) , _active (true)
, _signal_latency (0)
, _initial_delay (0) , _initial_delay (0)
, _roll_delay (0) , _roll_delay (0)
, _flags (flg) , _flags (flg)
@ -919,7 +920,7 @@ Route::add_processor (boost::shared_ptr<Processor> processor, ProcessorList::ite
processor->activate (); processor->activate ();
} }
processor->ActiveChanged.connect_same_thread (*this, boost::bind (&Session::update_latency_compensation, &_session, false, false)); processor->ActiveChanged.connect_same_thread (*this, boost::bind (&Session::update_latency_compensation, &_session, false, false, false));
_output->set_user_latency (0); _output->set_user_latency (0);
} }
@ -1056,7 +1057,7 @@ Route::add_processors (const ProcessorList& others, boost::shared_ptr<Processor>
} }
} }
(*i)->ActiveChanged.connect_same_thread (*this, boost::bind (&Session::update_latency_compensation, &_session, false, false)); (*i)->ActiveChanged.connect_same_thread (*this, boost::bind (&Session::update_latency_compensation, &_session, false, false, false));
} }
for (ProcessorList::const_iterator i = _processors.begin(); i != _processors.end(); ++i) { for (ProcessorList::const_iterator i = _processors.begin(); i != _processors.end(); ++i) {
@ -2420,6 +2421,9 @@ Route::set_processor_state (const XMLNode& node)
} }
for (ProcessorList::const_iterator i = _processors.begin(); i != _processors.end(); ++i) { for (ProcessorList::const_iterator i = _processors.begin(); i != _processors.end(); ++i) {
(*i)->ActiveChanged.connect_same_thread (*this, boost::bind (&Session::update_latency_compensation, &_session, false, false, false));
boost::shared_ptr<PluginInsert> pi; boost::shared_ptr<PluginInsert> pi;
if ((pi = boost::dynamic_pointer_cast<PluginInsert>(*i)) != 0) { if ((pi = boost::dynamic_pointer_cast<PluginInsert>(*i)) != 0) {
@ -2837,15 +2841,23 @@ Route::check_initial_delay (framecnt_t nframes, framecnt_t& transport_frame)
nframes -= _roll_delay; nframes -= _roll_delay;
silence_unlocked (_roll_delay); silence_unlocked (_roll_delay);
/* we've written _roll_delay of samples into the
output ports, so make a note of that for
future reference.
*/
_main_outs->increment_output_offset (_roll_delay);
transport_frame += _roll_delay; transport_frame += _roll_delay;
/* shuffle all the port buffers for things that lead "out" of this Route
to reflect that we just wrote _roll_delay frames of silence.
*/
Glib::RWLock::ReaderLock lm (_processor_lock);
for (ProcessorList::iterator i = _processors.begin(); i != _processors.end(); ++i) {
boost::shared_ptr<IOProcessor> iop = boost::dynamic_pointer_cast<IOProcessor> (*i);
if (iop) {
iop->increment_port_buffer_offset (_roll_delay);
}
}
_output->increment_port_buffer_offset (_roll_delay);
_roll_delay = 0; _roll_delay = 0;
} }
return nframes; return nframes;
@ -3040,41 +3052,24 @@ Route::add_export_point()
} }
framecnt_t framecnt_t
Route::update_total_latency () Route::update_signal_latency ()
{ {
framecnt_t old = _output->effective_latency(); framecnt_t l = _output->user_latency();
framecnt_t own_latency = _output->user_latency();
for (ProcessorList::iterator i = _processors.begin(); i != _processors.end(); ++i) { for (ProcessorList::iterator i = _processors.begin(); i != _processors.end(); ++i) {
if ((*i)->active ()) { if ((*i)->active ()) {
own_latency += (*i)->signal_latency (); l += (*i)->signal_latency ();
} }
} }
DEBUG_TRACE (DEBUG::Latency, string_compose ("%1: bus: internal redirect latency = %2\n", _name, own_latency)); DEBUG_TRACE (DEBUG::Latency, string_compose ("%1: internal signal latency = %2\n", _name, l));
_output->set_port_latency (own_latency); if (_signal_latency != l) {
_signal_latency = l;
if (_output->user_latency() == 0) {
/* 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.
*/
own_latency += _input->signal_latency ();
}
if (old != own_latency) {
_output->set_latency_delay (own_latency);
signal_latency_changed (); /* EMIT SIGNAL */ signal_latency_changed (); /* EMIT SIGNAL */
} }
DEBUG_TRACE (DEBUG::Latency, string_compose ("%1: input latency = %2 total = %3\n", _name, _input->signal_latency(), own_latency)); return _signal_latency;
return _output->effective_latency ();
} }
void void
@ -3085,16 +3080,19 @@ Route::set_user_latency (framecnt_t nframes)
} }
void void
Route::set_latency_delay (framecnt_t longest_session_latency) Route::set_latency_compensation (framecnt_t longest_session_latency)
{ {
framecnt_t old = _initial_delay; framecnt_t old = _initial_delay;
if (_output->effective_latency() < longest_session_latency) { if (_signal_latency < longest_session_latency) {
_initial_delay = longest_session_latency - _output->effective_latency(); _initial_delay = longest_session_latency - _signal_latency;
} else { } else {
_initial_delay = 0; _initial_delay = 0;
} }
DEBUG_TRACE (DEBUG::Latency, string_compose ("%1: compensate for maximum latency of %2, given own latency of %3, using initial delay of %4\n",
name(), longest_session_latency, _signal_latency, _initial_delay));
if (_initial_delay != old) { if (_initial_delay != old) {
initial_delay_changed (); /* EMIT SIGNAL */ initial_delay_changed (); /* EMIT SIGNAL */
} }
@ -3595,33 +3593,10 @@ Route::unknown_processors () const
return p; return p;
} }
void
Route::set_latency_ranges (bool playback) const framecnt_t
Route::update_port_latencies (const PortSet& from, const PortSet& to, bool playback, framecnt_t our_latency) const
{ {
framecnt_t own_latency = 0;
/* Processor list not protected by lock: MUST BE CALLED FROM PROCESS THREAD OR
LATENCY CALLBACK
*/
for (ProcessorList::const_iterator i = _processors.begin(); i != _processors.end(); ++i) {
if ((*i)->active ()) {
own_latency += (*i)->signal_latency ();
}
}
if (playback) {
update_port_latencies (_input->ports (), _output->ports (), true, own_latency);
} else {
update_port_latencies (_output->ports (), _input->ports (), false, own_latency);
}
}
void
Route::update_port_latencies (const PortSet& operands, const PortSet& feeders, bool playback, framecnt_t our_latency) const
{
#ifdef HAVE_JACK_NEW_LATENCY
/* we assume that all our input ports feed all our output ports. its not /* 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. universally true, but the alternative is way too corner-case to worry about.
*/ */
@ -3631,11 +3606,11 @@ Route::update_port_latencies (const PortSet& operands, const PortSet& feeders, b
all_connections.min = ~((jack_nframes_t) 0); all_connections.min = ~((jack_nframes_t) 0);
all_connections.max = 0; all_connections.max = 0;
/* iterate over all feeder ports and determine their relevant latency, taking /* iterate over all "from" ports and determine the latency range for all of their
the maximum and minimum across all of them. connections to the "outside" (outside of this Route).
*/ */
for (PortSet::const_iterator p = feeders.begin(); p != feeders.end(); ++p) { for (PortSet::const_iterator p = from.begin(); p != from.end(); ++p) {
jack_latency_range_t range; jack_latency_range_t range;
@ -3645,23 +3620,80 @@ Route::update_port_latencies (const PortSet& operands, const PortSet& feeders, b
all_connections.max = max (all_connections.max, range.max); all_connections.max = max (all_connections.max, range.max);
} }
/* set the "from" port latencies to the max/min range of all their connections */
for (PortSet::const_iterator p = from.begin(); p != from.end(); ++p) {
p->set_public_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.min += our_latency;
all_connections.max += our_latency; all_connections.max += our_latency;
for (PortSet::const_iterator p = operands.begin(); p != operands.end(); ++p) { for (PortSet::const_iterator p = to.begin(); p != to.end(); ++p) {
p->set_public_latency_range (all_connections, playback);
p->set_latency_range (all_connections, playback);
DEBUG_TRACE (DEBUG::Latency, string_compose ("Port %1 %5 latency range %2 .. %3 (including route latency of %4)\n",
p->name(),
all_connections.min,
all_connections.max,
our_latency,
(playback ? "PLAYBACK" : "CAPTURE")));
} }
#endif
return all_connections.max;
} }
framecnt_t
Route::set_private_port_latencies (bool playback) const
{
framecnt_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 (ProcessorList::const_iterator i = _processors.begin(); i != _processors.end(); ++i) {
if ((*i)->active ()) {
own_latency += (*i)->signal_latency ();
}
}
if (playback) {
/* playback: propagate latency from "outside the route" to outputs to inputs */
return update_port_latencies (_output->ports (), _input->ports (), true, own_latency);
} else {
/* capture: propagate latency from "outside the route" to inputs to outputs */
return update_port_latencies (_input->ports (), _output->ports (), false, own_latency);
}
}
void
Route::set_public_port_latencies (framecnt_t value, bool playback) const
{
/* this is called to set the JACK-visible port latencies, which take latency compensation
into account.
*/
jack_latency_range_t range;
range.min = value;
range.max = value;
{
const PortSet& ports (_input->ports());
for (PortSet::const_iterator p = ports.begin(); p != ports.end(); ++p) {
p->set_public_latency_range (range, playback);
}
}
{
const PortSet& ports (_output->ports());
for (PortSet::const_iterator p = ports.begin(); p != ports.end(); ++p) {
p->set_public_latency_range (range, playback);
}
}
}
/** Put the invisible processors in the right place in _processors. /** Put the invisible processors in the right place in _processors.
* Must be called with a writer lock on _processor_lock held. * Must be called with a writer lock on _processor_lock held.

View file

@ -649,16 +649,14 @@ Session::when_engine_running ()
} }
} }
set_worst_io_latencies ();
_state_of_the_state = StateOfTheState (_state_of_the_state & ~(CannotSave|Dirty)); _state_of_the_state = StateOfTheState (_state_of_the_state & ~(CannotSave|Dirty));
/* hook us up to the engine */ /* hook us up to the engine */
BootMessage (_("Connect to engine")); BootMessage (_("Connect to engine"));
_engine.set_session (this); _engine.set_session (this);
_engine.update_total_latencies ();
update_latency_compensation (false, false, true);
} }
void void
@ -4153,13 +4151,13 @@ Session::unknown_processors () const
return p; return p;
} }
#ifdef HAVE_JACK_NEW_LATENCY
void void
Session::update_latency (bool playback) Session::update_latency (bool playback)
{ {
DEBUG_TRACE (DEBUG::Latency, "JACK latency callback\n"); DEBUG_TRACE (DEBUG::Latency, string_compose ("\n\nJACK latency callback: %1\n", (playback ? "PLAYBACK" : "CAPTURE")));
boost::shared_ptr<RouteList> r = routes.reader (); boost::shared_ptr<RouteList> r = routes.reader ();
framecnt_t max_latency = 0;
if (playback) { if (playback) {
/* reverse the list so that we work backwards from the last route to run to the first */ /* reverse the list so that we work backwards from the last route to run to the first */
@ -4167,9 +4165,14 @@ Session::update_latency (bool playback)
} }
for (RouteList::iterator i = r->begin(); i != r->end(); ++i) { for (RouteList::iterator i = r->begin(); i != r->end(); ++i) {
DEBUG_TRACE (DEBUG::Latency, string_compose ("------------- Working on latency for %1\n", (*i)->name())); max_latency = max (max_latency, (*i)->set_private_port_latencies (playback));
(*i)->set_latency_ranges (playback); }
DEBUG_TRACE (DEBUG::Latency, string_compose ("------------- Done working on latency for %1\n\n", (*i)->name()));
#if 0
DEBUG_TRACE (DEBUG::Latency, string_compose ("Set public port latencies to %1\n", max_latency));
for (RouteList::iterator i = r->begin(); i != r->end(); ++i) {
(*i)->set_public_port_latencies (max_latency, playback);
} }
}
#endif #endif
}

View file

@ -932,7 +932,7 @@ Session::maybe_sync_start (pframes_t & nframes)
no_roll (sync_offset); no_roll (sync_offset);
nframes -= sync_offset; nframes -= sync_offset;
AudioPort::increment_port_offset (sync_offset); Port::increment_global_port_buffer_offset (sync_offset);
waiting_for_sync_offset = false; waiting_for_sync_offset = false;
if (nframes == 0) { if (nframes == 0) {

View file

@ -1466,7 +1466,7 @@ Session::route_processors_changed (RouteProcessorChange c)
} }
void void
Session::update_latency_compensation (bool with_stop, bool abort) Session::update_latency_compensation (bool with_stop, bool abort, bool force_whole_graph)
{ {
bool update_jack = false; bool update_jack = false;
PostTransportWork ptw; PostTransportWork ptw;
@ -1478,7 +1478,7 @@ Session::update_latency_compensation (bool with_stop, bool abort)
_worst_track_latency = 0; _worst_track_latency = 0;
ptw = post_transport_work(); ptw = post_transport_work();
DEBUG_TRACE(DEBUG::Latency, "---------------------------- update latency\n\n") DEBUG_TRACE(DEBUG::Latency, "---------------------------- update latency compensation\n\n")
boost::shared_ptr<RouteList> r = routes.reader (); boost::shared_ptr<RouteList> r = routes.reader ();
@ -1488,30 +1488,28 @@ Session::update_latency_compensation (bool with_stop, bool abort)
(*i)->nonrealtime_handle_transport_stopped (abort, (ptw & PostTransportLocate), (!(ptw & PostTransportLocate) || pending_locate_flush)); (*i)->nonrealtime_handle_transport_stopped (abort, (ptw & PostTransportLocate), (!(ptw & PostTransportLocate) || pending_locate_flush));
} }
framecnt_t old_latency = (*i)->output()->signal_latency (); framecnt_t old_latency = (*i)->signal_latency ();
framecnt_t track_latency = (*i)->update_total_latency (); framecnt_t new_latency = (*i)->update_signal_latency ();
if (old_latency != track_latency) { if (old_latency != new_latency) {
#ifndef HAVE_JACK_NEW_LATENCY
(*i)->input()->update_port_total_latencies ();
(*i)->output()->update_port_total_latencies ();
#endif
update_jack = true; update_jack = true;
} }
if (!(*i)->is_hidden() && ((*i)->active())) { if (!(*i)->is_hidden() && ((*i)->active())) {
_worst_track_latency = max (_worst_track_latency, track_latency); _worst_track_latency = max (_worst_track_latency, new_latency);
} }
} }
if (update_jack) { if (force_whole_graph || update_jack) {
/* trigger a full recompute of latency numbers for the graph
*/
_engine.update_total_latencies (); _engine.update_total_latencies ();
} }
DEBUG_TRACE(DEBUG::Latency, string_compose("worst case route internal latency was %1\n", _worst_track_latency)); DEBUG_TRACE(DEBUG::Latency, string_compose("worst case route internal latency was %1\n", _worst_track_latency));
for (RouteList::iterator i = r->begin(); i != r->end(); ++i) { for (RouteList::iterator i = r->begin(); i != r->end(); ++i) {
(*i)->set_latency_delay (_worst_track_latency); (*i)->set_latency_compensation (_worst_track_latency);
} }
set_worst_io_latencies (); set_worst_io_latencies ();
@ -1526,6 +1524,7 @@ Session::update_latency_compensation (bool with_stop, bool abort)
tr->set_capture_offset (); tr->set_capture_offset ();
} }
} }
DEBUG_TRACE(DEBUG::Latency, "---------------------------- DONE update latency compensation\n\n")
} }
void void

View file

@ -85,30 +85,6 @@ Track::toggle_monitor_input ()
} }
} }
ARDOUR::framecnt_t
Track::update_total_latency ()
{
framecnt_t old = _output->effective_latency();
framecnt_t own_latency = _output->user_latency();
for (ProcessorList::iterator i = _processors.begin(); i != _processors.end(); ++i) {
if ((*i)->active ()) {
own_latency += (*i)->signal_latency ();
}
}
DEBUG_TRACE (DEBUG::Latency, string_compose ("%1: track: internal redirect latency = %2\n", _name, own_latency));
_output->set_port_latency (own_latency);
if (old != own_latency) {
_output->set_latency_delay (own_latency);
signal_latency_changed (); /* EMIT SIGNAL */
}
return _output->effective_latency();
}
Track::FreezeRecord::~FreezeRecord () Track::FreezeRecord::~FreezeRecord ()
{ {
for (vector<FreezeRecordProcessorInfo*>::iterator i = processor_info.begin(); i != processor_info.end(); ++i) { for (vector<FreezeRecordProcessorInfo*>::iterator i = processor_info.begin(); i != processor_info.end(); ++i) {
@ -218,9 +194,9 @@ Track::set_name (const string& str)
} }
void void
Track::set_latency_delay (framecnt_t longest_session_latency) Track::set_latency_compensation (framecnt_t longest_session_latency)
{ {
Route::set_latency_delay (longest_session_latency); Route::set_latency_compensation (longest_session_latency);
_diskstream->set_roll_delay (_roll_delay); _diskstream->set_roll_delay (_roll_delay);
} }

View file

@ -273,9 +273,9 @@ def configure(conf):
conf.check_cc(fragment = "#include <jack/jack.h>\nint main(int argc, char **argv) { jack_port_t* p; jack_latency_range_t r; jack_port_set_latency_range (p, JackCaptureLatency, &r); return 0; }\n", conf.check_cc(fragment = "#include <jack/jack.h>\nint main(int argc, char **argv) { jack_port_t* p; jack_latency_range_t r; jack_port_set_latency_range (p, JackCaptureLatency, &r); return 0; }\n",
linkflags = ['-ljack'], linkflags = ['-ljack'],
msg = 'Checking for new JACK latency API', msg = 'Checking for new JACK latency API',
define_name = 'HAVE_JACK_NEW_LATENCY', okmsg = 'present',
uselib_store = "JACK_NEW_LATENCY", mandatory = True,
okmsg = 'present') errmsg = 'missing - a version of JACK that supports jack_port_set_latency_range() is required to compile Ardour3')
conf.check_cc(fragment = '#include <jack/jack.h>\nint main(int argc, char **argv) { jack_port_type_get_buffer_size ((jack_client_t*)0, ""); }\n', conf.check_cc(fragment = '#include <jack/jack.h>\nint main(int argc, char **argv) { jack_port_type_get_buffer_size ((jack_client_t*)0, ""); }\n',
linkflags = ['-ljack'], linkflags = ['-ljack'],