Various updates and fixes for Latency Compensation
* centralize signal_latency_at_***_position to processors * update initial-delay/roll-delay when processor order changes * consolidate signal-latency calculation: use the same method for processor-changes and session's post_playback_latency. * include relative output-delay in roll-delay * fix capture processor position & optimize stem-export latency (roll-delay fixes pending Route:roll() update)
This commit is contained in:
parent
013088ca5f
commit
10b76ae631
@ -70,9 +70,12 @@ class LIBARDOUR_API Processor : public SessionObject, public Automatable, public
|
||||
|
||||
virtual samplecnt_t signal_latency() const { return 0; }
|
||||
|
||||
virtual void set_input_latency (samplecnt_t);
|
||||
virtual void set_input_latency (samplecnt_t cnt) { _input_latency = cnt; }
|
||||
samplecnt_t input_latency () const { return _input_latency; }
|
||||
|
||||
virtual void set_output_latency (samplecnt_t cnt) { _output_latency = cnt; }
|
||||
samplecnt_t output_latency () const { return _output_latency; }
|
||||
|
||||
virtual int set_block_size (pframes_t /*nframes*/) { return 0; }
|
||||
virtual bool requires_fixed_sized_buffers() const { return false; }
|
||||
|
||||
@ -153,6 +156,7 @@ protected:
|
||||
PluginPinWindowProxy *_pinmgr_proxy;
|
||||
SessionObject* _owner;
|
||||
samplecnt_t _input_latency;
|
||||
samplecnt_t _output_latency;
|
||||
};
|
||||
|
||||
} // namespace ARDOUR
|
||||
|
@ -339,11 +339,11 @@ public:
|
||||
samplecnt_t set_private_port_latencies (bool playback) const;
|
||||
void set_public_port_latencies (samplecnt_t, bool playback) const;
|
||||
|
||||
samplecnt_t update_signal_latency();
|
||||
samplecnt_t update_signal_latency (bool set_initial_delay = false);
|
||||
virtual void set_latency_compensation (samplecnt_t);
|
||||
|
||||
void set_user_latency (samplecnt_t);
|
||||
samplecnt_t initial_delay() const { return _initial_delay; }
|
||||
samplecnt_t initial_delay() const { return _initial_delay; }
|
||||
samplecnt_t signal_latency() const { return _signal_latency; }
|
||||
|
||||
PBD::Signal0<void> active_changed;
|
||||
@ -628,8 +628,6 @@ public:
|
||||
|
||||
bool _active;
|
||||
samplecnt_t _signal_latency;
|
||||
samplecnt_t _signal_latency_at_amp_position;
|
||||
samplecnt_t _signal_latency_at_trim_position;
|
||||
samplecnt_t _initial_delay;
|
||||
samplecnt_t _roll_delay;
|
||||
|
||||
|
@ -473,11 +473,13 @@ class LIBARDOUR_API Session : public PBD::StatefulDestructible, public PBD::Scop
|
||||
void set_end_is_free (bool);
|
||||
int location_name(std::string& result, std::string base = std::string(""));
|
||||
|
||||
pframes_t get_block_size() const { return current_block_size; }
|
||||
samplecnt_t worst_output_latency () const { return _worst_output_latency; }
|
||||
samplecnt_t worst_input_latency () const { return _worst_input_latency; }
|
||||
samplecnt_t worst_track_latency () const { return _worst_track_latency; }
|
||||
samplecnt_t worst_playback_latency () const { return _worst_output_latency + _worst_track_latency; }
|
||||
pframes_t get_block_size () const { return current_block_size; }
|
||||
samplecnt_t worst_output_latency ()const { return _worst_output_latency; }
|
||||
samplecnt_t worst_input_latency () const { return _worst_input_latency; }
|
||||
samplecnt_t worst_track_latency () const { return _worst_track_latency; }
|
||||
samplecnt_t worst_track_roll_delay () const { return _worst_track_roll_delay; }
|
||||
samplecnt_t worst_track_out_latency () const { return _worst_track_out_latency; }
|
||||
samplecnt_t worst_playback_latency () const { return _worst_output_latency + _worst_track_latency; }
|
||||
|
||||
struct SaveAs {
|
||||
std::string new_parent_folder; /* parent folder where new session folder will be created */
|
||||
@ -1264,6 +1266,8 @@ class LIBARDOUR_API Session : public PBD::StatefulDestructible, public PBD::Scop
|
||||
samplecnt_t _worst_output_latency;
|
||||
samplecnt_t _worst_input_latency;
|
||||
samplecnt_t _worst_track_latency;
|
||||
samplecnt_t _worst_track_out_latency;
|
||||
samplecnt_t _worst_track_roll_delay;
|
||||
bool _have_captured;
|
||||
bool _non_soloed_outs_muted;
|
||||
bool _listening;
|
||||
|
@ -906,6 +906,7 @@ LuaBindings::common (lua_State* L)
|
||||
.addFunction ("midi", &IO::midi)
|
||||
.addFunction ("port_by_name", &IO::nth)
|
||||
.addFunction ("n_ports", &IO::n_ports)
|
||||
.addFunction ("latency", &IO::latency)
|
||||
.endClass ()
|
||||
|
||||
.deriveWSPtrClass <PannerShell, SessionObject> ("PannerShell")
|
||||
@ -1059,6 +1060,8 @@ LuaBindings::common (lua_State* L)
|
||||
.addFunction ("trim", &Route::trim)
|
||||
.addFunction ("peak_meter", (boost::shared_ptr<PeakMeter> (Route::*)())&Route::peak_meter)
|
||||
.addFunction ("set_meter_point", &Route::set_meter_point)
|
||||
.addFunction ("initial_delay", &Route::initial_delay)
|
||||
.addFunction ("signal_latency", &Route::signal_latency)
|
||||
.endClass ()
|
||||
|
||||
.deriveWSPtrClass <Playlist, SessionObject> ("Playlist")
|
||||
@ -2166,6 +2169,8 @@ LuaBindings::common (lua_State* L)
|
||||
.addFunction ("worst_output_latency", &Session::worst_output_latency)
|
||||
.addFunction ("worst_input_latency", &Session::worst_input_latency)
|
||||
.addFunction ("worst_track_latency", &Session::worst_track_latency)
|
||||
.addFunction ("worst_track_roll_delay", &Session::worst_track_roll_delay)
|
||||
.addFunction ("worst_track_out_latency", &Session::worst_track_out_latency)
|
||||
.addFunction ("worst_playback_latency", &Session::worst_playback_latency)
|
||||
.addFunction ("cfg", &Session::cfg)
|
||||
.addFunction ("route_groups", &Session::route_groups)
|
||||
|
@ -69,6 +69,7 @@ Processor::Processor(Session& session, const string& name)
|
||||
, _pinmgr_proxy (0)
|
||||
, _owner (0)
|
||||
, _input_latency (0)
|
||||
, _output_latency (0)
|
||||
{
|
||||
}
|
||||
|
||||
@ -290,10 +291,3 @@ Processor::owner() const
|
||||
{
|
||||
return _owner;
|
||||
}
|
||||
|
||||
void
|
||||
Processor::set_input_latency (samplecnt_t cnt)
|
||||
{
|
||||
_input_latency = cnt;
|
||||
}
|
||||
|
||||
|
@ -94,8 +94,6 @@ Route::Route (Session& sess, string name, PresentationInfo::Flag flag, DataType
|
||||
, Muteable (sess, name)
|
||||
, _active (true)
|
||||
, _signal_latency (0)
|
||||
, _signal_latency_at_amp_position (0)
|
||||
, _signal_latency_at_trim_position (0)
|
||||
, _initial_delay (0)
|
||||
, _roll_delay (0)
|
||||
, _disk_io_point (DiskIOPreFader)
|
||||
@ -332,14 +330,14 @@ Route::process_output_buffers (BufferSet& bufs,
|
||||
if (gain_automation_ok) {
|
||||
_amp->set_gain_automation_buffer (_session.gain_automation_buffer ());
|
||||
_amp->setup_gain_automation (
|
||||
start_sample + _signal_latency_at_amp_position,
|
||||
end_sample + _signal_latency_at_amp_position,
|
||||
start_sample + _amp->output_latency (),
|
||||
end_sample + _amp->output_latency (),
|
||||
nframes);
|
||||
|
||||
_trim->set_gain_automation_buffer (_session.trim_automation_buffer ());
|
||||
_trim->setup_gain_automation (
|
||||
start_sample + _signal_latency_at_trim_position,
|
||||
end_sample + _signal_latency_at_trim_position,
|
||||
start_sample + _trim->output_latency (),
|
||||
end_sample + _trim->output_latency (),
|
||||
nframes);
|
||||
} else {
|
||||
_amp->apply_gain_automation (false);
|
||||
@ -1809,8 +1807,6 @@ Route::configure_processors_unlocked (ProcessorStreams* err, Glib::Threads::RWLo
|
||||
// TODO check for a potential ReaderLock after ReaderLock ??
|
||||
Glib::Threads::RWLock::ReaderLock lr (_processor_lock);
|
||||
|
||||
samplecnt_t chain_latency = _input->latency ();
|
||||
|
||||
list< pair<ChanCount,ChanCount> >::iterator c = configuration.begin();
|
||||
for (ProcessorList::iterator p = _processors.begin(); p != _processors.end(); ++p, ++c) {
|
||||
|
||||
@ -1822,9 +1818,6 @@ Route::configure_processors_unlocked (ProcessorStreams* err, Glib::Threads::RWLo
|
||||
return -1;
|
||||
}
|
||||
|
||||
(*p)->set_input_latency (chain_latency);
|
||||
chain_latency += (*p)->signal_latency ();
|
||||
|
||||
processor_max_streams = ChanCount::max(processor_max_streams, c->first);
|
||||
processor_max_streams = ChanCount::max(processor_max_streams, c->second);
|
||||
|
||||
@ -2104,6 +2097,11 @@ Route::reorder_processors (const ProcessorList& new_order, ProcessorStreams* err
|
||||
g_atomic_int_set (&_pending_process_reorder, 1);
|
||||
}
|
||||
|
||||
/* update processor input/output latency
|
||||
* (total signal_latency does not change)
|
||||
*/
|
||||
update_signal_latency (true);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -3679,6 +3677,10 @@ Route::apply_processor_changes_rt ()
|
||||
}
|
||||
if (changed) {
|
||||
set_processor_positions ();
|
||||
/* update processor input/output latency
|
||||
* (total signal_latency does not change)
|
||||
*/
|
||||
update_signal_latency (true);
|
||||
}
|
||||
if (emissions != 0) {
|
||||
g_atomic_int_set (&_pending_signals, emissions);
|
||||
@ -3851,8 +3853,9 @@ Route::add_export_point()
|
||||
Glib::Threads::RWLock::WriterLock lw (_processor_lock);
|
||||
|
||||
// this aligns all tracks; but not tracks + busses
|
||||
assert (_session.worst_track_latency () >= _initial_delay);
|
||||
_capturing_processor.reset (new CapturingProcessor (_session, _session.worst_track_latency () - _initial_delay));
|
||||
samplecnt_t latency = _session.worst_track_roll_delay ();
|
||||
assert (latency >= _initial_delay);
|
||||
_capturing_processor.reset (new CapturingProcessor (_session, latency - _initial_delay));
|
||||
_capturing_processor->activate ();
|
||||
|
||||
configure_processors_unlocked (0, &lw);
|
||||
@ -3863,40 +3866,40 @@ Route::add_export_point()
|
||||
}
|
||||
|
||||
samplecnt_t
|
||||
Route::update_signal_latency ()
|
||||
Route::update_signal_latency (bool set_initial_delay)
|
||||
{
|
||||
samplecnt_t l = _output->user_latency();
|
||||
samplecnt_t lamp = 0;
|
||||
bool before_amp = true;
|
||||
samplecnt_t ltrim = 0;
|
||||
bool before_trim = true;
|
||||
Glib::Threads::RWLock::ReaderLock lm (_processor_lock);
|
||||
|
||||
samplecnt_t l_in = _input->latency ();
|
||||
samplecnt_t l_out = 0;
|
||||
|
||||
for (ProcessorList::iterator i = _processors.begin(); i != _processors.end(); ++i) {
|
||||
if ((*i)->active ()) {
|
||||
l += (*i)->signal_latency ();
|
||||
}
|
||||
if ((*i) == _amp) {
|
||||
before_amp = false;
|
||||
}
|
||||
if ((*i) == _trim) {
|
||||
before_amp = false;
|
||||
}
|
||||
if (before_amp) {
|
||||
lamp = l;
|
||||
}
|
||||
if (before_trim) {
|
||||
lamp = l;
|
||||
l_out += (*i)->signal_latency ();
|
||||
l_in += (*i)->signal_latency ();
|
||||
}
|
||||
(*i)->set_input_latency (l_in);
|
||||
(*i)->set_output_latency (l_out);
|
||||
}
|
||||
|
||||
DEBUG_TRACE (DEBUG::Latency, string_compose ("%1: internal signal latency = %2\n", _name, l));
|
||||
l_out += _output->user_latency();
|
||||
|
||||
// TODO: (lamp - _signal_latency) to sync to output (read-ahed), currently _roll_delay shifts this around
|
||||
_signal_latency_at_amp_position = lamp;
|
||||
_signal_latency_at_trim_position = ltrim;
|
||||
for (ProcessorList::iterator i = _processors.begin(); i != _processors.end(); ++i) {
|
||||
(*i)->set_output_latency (l_out - (*i)->output_latency ());
|
||||
}
|
||||
|
||||
if (_signal_latency != l) {
|
||||
_signal_latency = l;
|
||||
DEBUG_TRACE (DEBUG::Latency, string_compose ("%1: internal signal latency = %2\n", _name, l_out));
|
||||
|
||||
_signal_latency = l_out;
|
||||
|
||||
lm.release ();
|
||||
|
||||
if (set_initial_delay) {
|
||||
/* see also Session::post_playback_latency() */
|
||||
set_latency_compensation (_session.worst_track_latency () + _session.worst_track_out_latency () - output ()->latency ());
|
||||
}
|
||||
|
||||
if (_signal_latency != l_out) {
|
||||
signal_latency_changed (); /* EMIT SIGNAL */
|
||||
}
|
||||
|
||||
@ -3914,9 +3917,10 @@ void
|
||||
Route::set_latency_compensation (samplecnt_t longest_session_latency)
|
||||
{
|
||||
samplecnt_t old = _initial_delay;
|
||||
assert (!_disk_reader || _disk_reader->output_latency () <= _signal_latency);
|
||||
|
||||
if (_signal_latency < longest_session_latency) {
|
||||
_initial_delay = longest_session_latency - _signal_latency;
|
||||
if (_disk_reader && _signal_latency < longest_session_latency) {
|
||||
_initial_delay = longest_session_latency - (_signal_latency - _disk_reader->input_latency ());
|
||||
} else {
|
||||
_initial_delay = 0;
|
||||
}
|
||||
@ -4651,7 +4655,7 @@ Route::setup_invisible_processors ()
|
||||
|
||||
ProcessorList::iterator trim = new_processors.end();
|
||||
|
||||
if (_trim && _trim->active()) {
|
||||
if (_trim->active()) {
|
||||
assert (!_trim->display_to_user ());
|
||||
new_processors.push_front (_trim);
|
||||
trim = new_processors.begin();
|
||||
@ -4670,11 +4674,6 @@ Route::setup_invisible_processors ()
|
||||
|
||||
/* EXPORT PROCESSOR */
|
||||
|
||||
if (_capturing_processor) {
|
||||
assert (!_capturing_processor->display_to_user ());
|
||||
new_processors.push_front (_capturing_processor);
|
||||
}
|
||||
|
||||
/* DISK READER & WRITER (for Track objects) */
|
||||
|
||||
if (_disk_reader || _disk_writer) {
|
||||
@ -4714,6 +4713,18 @@ Route::setup_invisible_processors ()
|
||||
}
|
||||
}
|
||||
|
||||
if (_capturing_processor) {
|
||||
assert (!_capturing_processor->display_to_user ());
|
||||
ProcessorList::iterator reader_pos = find (new_processors.begin(), new_processors.end(), _disk_reader);
|
||||
if (reader_pos != new_processors.end()) {
|
||||
/* insert after disk-reader */
|
||||
new_processors.insert (++reader_pos, _capturing_processor);
|
||||
} else {
|
||||
new_processors.push_front (_capturing_processor);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
_processors = new_processors;
|
||||
|
||||
for (ProcessorList::iterator i = _processors.begin(); i != _processors.end(); ++i) {
|
||||
|
@ -199,6 +199,8 @@ Session::Session (AudioEngine &eng,
|
||||
, _worst_output_latency (0)
|
||||
, _worst_input_latency (0)
|
||||
, _worst_track_latency (0)
|
||||
, _worst_track_out_latency (0)
|
||||
, _worst_track_roll_delay (0)
|
||||
, _have_captured (false)
|
||||
, _non_soloed_outs_muted (false)
|
||||
, _listening (false)
|
||||
@ -6915,15 +6917,25 @@ Session::post_playback_latency ()
|
||||
|
||||
boost::shared_ptr<RouteList> r = routes.reader ();
|
||||
|
||||
_worst_track_out_latency = 0;
|
||||
|
||||
for (RouteList::iterator i = r->begin(); i != r->end(); ++i) {
|
||||
assert (!(*i)->is_auditioner()); // XXX remove me
|
||||
if ((*i)->active()) {
|
||||
_worst_track_latency = max (_worst_track_latency, (*i)->update_signal_latency ());
|
||||
if (!(*i)->active()) { continue ; }
|
||||
_worst_track_latency = max (_worst_track_latency, (*i)->update_signal_latency ());
|
||||
if (boost::dynamic_pointer_cast<Track> (*i)) {
|
||||
_worst_track_out_latency = max (_worst_track_out_latency, (*i)->output ()->latency ());
|
||||
}
|
||||
}
|
||||
|
||||
_worst_track_roll_delay = 0;
|
||||
|
||||
for (RouteList::iterator i = r->begin(); i != r->end(); ++i) {
|
||||
(*i)->set_latency_compensation (_worst_track_latency);
|
||||
if (!(*i)->active()) { continue ; }
|
||||
(*i)->set_latency_compensation (_worst_track_latency + _worst_track_out_latency - (*i)->output ()->latency ());
|
||||
if (boost::dynamic_pointer_cast<Track> (*i)) {
|
||||
_worst_track_roll_delay = max (_worst_track_roll_delay, (*i)->initial_delay ());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -141,7 +141,7 @@ Session::start_audio_export (samplepos_t position, bool realtime, bool region_ex
|
||||
* (this is all still very [w]hacky. Individual Bus and Track outputs
|
||||
* are not aligned but one can select them in the PortExportChannelSelector)
|
||||
*/
|
||||
_export_latency = worst_track_latency ();
|
||||
_export_latency = worst_track_roll_delay ();
|
||||
|
||||
boost::shared_ptr<Route> master = master_out ();
|
||||
if (master && comensate_master_latency) {
|
||||
|
Loading…
Reference in New Issue
Block a user