From b5af3bb8e313e13166cc54c60a14e5492e674065 Mon Sep 17 00:00:00 2001 From: Paul Davis Date: Wed, 27 Jun 2007 22:06:35 +0000 Subject: allow user tweaking of everything that might have inherent latency; add GUI for track level adjustment and widget that can be (but is not yet) embedded in a plugin GUI git-svn-id: svn://localhost/ardour2/trunk@2075 d708f5d6-7413-0410-9779-e7cbd77b26cf --- libs/ardour/SConscript | 23 ++++++++++++++ libs/ardour/ardour/audio_unit.h | 2 +- libs/ardour/ardour/audioengine.h | 1 + libs/ardour/ardour/io.h | 9 ++++-- libs/ardour/ardour/ladspa_plugin.h | 2 +- libs/ardour/ardour/latent.h | 26 ++++++++++++++++ libs/ardour/ardour/plugin.h | 4 +-- libs/ardour/ardour/plugin_insert.h | 2 +- libs/ardour/ardour/port_insert.h | 2 +- libs/ardour/ardour/processor.h | 6 ++-- libs/ardour/ardour/route.h | 8 +++-- libs/ardour/ardour/vst_plugin.h | 2 +- libs/ardour/audio_unit.cc | 6 +++- libs/ardour/audioengine.cc | 21 +++++++++++++ libs/ardour/io.cc | 14 ++++++++- libs/ardour/ladspa_plugin.cc | 7 +++-- libs/ardour/plugin_insert.cc | 11 ++++--- libs/ardour/port_insert.cc | 2 +- libs/ardour/route.cc | 62 ++++++++++++++++++++++++++++++-------- libs/ardour/session_transport.cc | 20 ++++++------ libs/ardour/track.cc | 19 +++++++++--- libs/ardour/vst_plugin.cc | 6 +++- libs/pbd/file_utils.cc | 4 ++- libs/pbd/pbd/controllable.h | 15 +++++++++ 24 files changed, 221 insertions(+), 53 deletions(-) create mode 100644 libs/ardour/ardour/latent.h (limited to 'libs') diff --git a/libs/ardour/SConscript b/libs/ardour/SConscript index 6374047c1f..dd76aadcba 100644 --- a/libs/ardour/SConscript +++ b/libs/ardour/SConscript @@ -199,6 +199,25 @@ def CheckJackVideoFrameOffset(context): context.Result(result) return result + +# +# See if JACK supports jack_recompute_total_latency() (single port version) +# + +jack_port_latency_test = """ +#include +int main(int argc, char **argv) +{ + jack_recompute_total_latency ((jack_client_t*) 0, (jack_port_t*) 0); + return 0; +} +""" +def CheckJackRecomputeLatency(context): + context.Message('Checking for jack_recompute_total_latency()...') + result = context.TryLink(jack_port_latency_test, '.c') + context.Result(result) + return result + # # See if JACK supports jack_port_ensure_monitor_input() # @@ -223,6 +242,7 @@ def CheckJackEnsureMonitorInput(context): conf = Configure(ardour, custom_tests = { 'CheckJackClientOpen' : CheckJackClientOpen, 'CheckJackRecomputeLatencies' : CheckJackRecomputeLatencies, + 'CheckJackRecomputeLatency' : CheckJackRecomputeLatency, 'CheckJackVideoFrameOffset' : CheckJackVideoFrameOffset, 'CheckJackEnsureMonitorInput' : CheckJackEnsureMonitorInput }) @@ -233,6 +253,9 @@ if conf.CheckJackClientOpen(): if conf.CheckJackRecomputeLatencies(): ardour.Append(CXXFLAGS="-DHAVE_JACK_RECOMPUTE_LATENCIES") +if conf.CheckJackRecomputeLatency(): + ardour.Append(CXXFLAGS="-DHAVE_JACK_RECOMPUTE_LATENCY") + if conf.CheckJackVideoFrameOffset(): ardour.Append(CXXFLAGS="-DHAVE_JACK_VIDEO_SUPPORT") diff --git a/libs/ardour/ardour/audio_unit.h b/libs/ardour/ardour/audio_unit.h index 88591ab845..7b31b1937f 100644 --- a/libs/ardour/ardour/audio_unit.h +++ b/libs/ardour/ardour/audio_unit.h @@ -54,7 +54,7 @@ class AUPlugin : public ARDOUR::Plugin const char * maker () const; uint32_t parameter_count () const; float default_value (uint32_t port); - nframes_t latency () const; + nframes_t signal_latency () const; void set_parameter (uint32_t which, float val); float get_parameter (uint32_t which) const; diff --git a/libs/ardour/ardour/audioengine.h b/libs/ardour/ardour/audioengine.h index fca3c2df9a..cb5a6d72ce 100644 --- a/libs/ardour/ardour/audioengine.h +++ b/libs/ardour/ardour/audioengine.h @@ -139,6 +139,7 @@ class AudioEngine : public sigc::trackable nframes_t get_port_total_latency (const Port&); void update_total_latencies (); + void update_total_latency (const Port&); /** Caller may not delete the object pointed to by the return value */ diff --git a/libs/ardour/ardour/io.h b/libs/ardour/ardour/io.h index 1592ac7cac..fc49f0699f 100644 --- a/libs/ardour/ardour/io.h +++ b/libs/ardour/ardour/io.h @@ -41,6 +41,7 @@ #include #include #include +#include using std::string; using std::vector; @@ -64,7 +65,8 @@ class BufferSet; * An IO can contain ports of varying types, making routes/inserts/etc with * varied combinations of types (eg MIDI and audio) possible. */ -class IO : public Automatable + +class IO : public Automatable, public Latent { public: static const string state_node_name; @@ -141,9 +143,12 @@ class IO : public Automatable int disconnect_inputs (void *src); int disconnect_outputs (void *src); + nframes_t signal_latency() const { return _own_latency; } nframes_t output_latency() const; nframes_t input_latency() const; - void set_port_latency (nframes_t); + void set_port_latency (nframes_t); + + void update_port_total_latencies (); const PortSet& inputs() const { return _inputs; } const PortSet& outputs() const { return _outputs; } diff --git a/libs/ardour/ardour/ladspa_plugin.h b/libs/ardour/ardour/ladspa_plugin.h index 5c15632391..ba51f551c7 100644 --- a/libs/ardour/ardour/ladspa_plugin.h +++ b/libs/ardour/ardour/ladspa_plugin.h @@ -59,7 +59,7 @@ class LadspaPlugin : public ARDOUR::Plugin const char * maker() const { return descriptor->Maker; } uint32_t parameter_count() const { return descriptor->PortCount; } float default_value (uint32_t port); - nframes_t latency() const; + nframes_t signal_latency() const; void set_parameter (uint32_t port, float val); float get_parameter (uint32_t port) const; int get_parameter_descriptor (uint32_t which, ParameterDescriptor&) const; diff --git a/libs/ardour/ardour/latent.h b/libs/ardour/ardour/latent.h new file mode 100644 index 0000000000..11bdf11370 --- /dev/null +++ b/libs/ardour/ardour/latent.h @@ -0,0 +1,26 @@ +#ifndef __ardour_latent_h__ +#define __ardour_latent_h__ + +#include + +namespace ARDOUR { + +class Latent { + public: + Latent() : _own_latency (0), _user_latency (0) {} + virtual ~Latent() {} + + virtual nframes_t signal_latency() const = 0; + nframes_t user_latency () const { return _user_latency; } + + virtual void set_latency_delay (nframes_t val) { _own_latency = val; } + virtual void set_user_latency (nframes_t val) { _user_latency = val; } + + protected: + nframes_t _own_latency; + nframes_t _user_latency; +}; + +} + +#endif /* __ardour_latent_h__*/ diff --git a/libs/ardour/ardour/plugin.h b/libs/ardour/ardour/plugin.h index 22c0862202..00c31720a7 100644 --- a/libs/ardour/ardour/plugin.h +++ b/libs/ardour/ardour/plugin.h @@ -31,6 +31,7 @@ #include #include #include +#include #include #include @@ -79,7 +80,7 @@ class PluginInfo { typedef boost::shared_ptr PluginInfoPtr; typedef std::list PluginInfoList; -class Plugin : public PBD::StatefulDestructible +class Plugin : public PBD::StatefulDestructible, public Latent { public: Plugin (ARDOUR::AudioEngine&, ARDOUR::Session&); @@ -110,7 +111,6 @@ class Plugin : public PBD::StatefulDestructible virtual const char * maker() const = 0; virtual uint32_t parameter_count () const = 0; virtual float default_value (uint32_t port) = 0; - virtual nframes_t latency() const = 0; virtual void set_parameter (uint32_t which, float val) = 0; virtual float get_parameter(uint32_t which) const = 0; diff --git a/libs/ardour/ardour/plugin_insert.h b/libs/ardour/ardour/plugin_insert.h index ea8f78b62f..e3b1b62b19 100644 --- a/libs/ardour/ardour/plugin_insert.h +++ b/libs/ardour/ardour/plugin_insert.h @@ -92,7 +92,7 @@ class PluginInsert : public Processor string describe_parameter (ParamID param); - nframes_t latency(); + nframes_t signal_latency() const; void transport_stopped (nframes_t now); void automation_snapshot (nframes_t now); diff --git a/libs/ardour/ardour/port_insert.h b/libs/ardour/ardour/port_insert.h index 1f366ae259..619e2e5bd2 100644 --- a/libs/ardour/ardour/port_insert.h +++ b/libs/ardour/ardour/port_insert.h @@ -54,7 +54,7 @@ class PortInsert : public IOProcessor void run (BufferSet& bufs, nframes_t start_frame, nframes_t end_frame, nframes_t nframes, nframes_t offset); - nframes_t latency(); + nframes_t signal_latency() const; ChanCount output_streams() const; ChanCount input_streams() const; diff --git a/libs/ardour/ardour/processor.h b/libs/ardour/ardour/processor.h index 7d126e8e0c..3985306d01 100644 --- a/libs/ardour/ardour/processor.h +++ b/libs/ardour/ardour/processor.h @@ -33,7 +33,7 @@ #include #include #include - +#include class XMLNode; @@ -43,7 +43,7 @@ class Session; /* A mixer strip element - plugin, send, meter, etc. */ -class Processor : public Automatable +class Processor : public Automatable, public Latent { public: static const string state_node_name; @@ -66,7 +66,7 @@ class Processor : public Automatable bool get_next_ab_is_active () const { return _next_ab_is_active; } void set_next_ab_is_active (bool yn) { _next_ab_is_active = yn; } - virtual nframes_t latency() { return 0; } + virtual nframes_t signal_latency() const { return 0; } virtual void transport_stopped (nframes_t frame) {} diff --git a/libs/ardour/ardour/route.h b/libs/ardour/ardour/route.h index 008d8ffcbb..b8c9431e42 100644 --- a/libs/ardour/ardour/route.h +++ b/libs/ardour/ardour/route.h @@ -187,8 +187,9 @@ class Route : public IO void all_processors_active (Placement, bool state); virtual nframes_t update_total_latency(); - nframes_t signal_latency() const { return _own_latency; } - virtual void set_latency_delay (nframes_t); + void set_latency_delay (nframes_t); + void set_user_latency (nframes_t); + nframes_t initial_delay() const { return _initial_delay; } sigc::signal solo_changed; sigc::signal solo_safe_changed; @@ -204,6 +205,8 @@ class Route : public IO sigc::signal mix_group_changed; sigc::signal active_changed; sigc::signal meter_change; + sigc::signal signal_latency_changed; + sigc::signal initial_delay_changed; /* gui's call this for their own purposes. */ @@ -294,7 +297,6 @@ class Route : public IO nframes_t _initial_delay; nframes_t _roll_delay; - nframes_t _own_latency; ProcessorList _processors; Glib::RWLock _processor_lock; IO *_control_outs; diff --git a/libs/ardour/ardour/vst_plugin.h b/libs/ardour/ardour/vst_plugin.h index ffcb999183..1622df0c1a 100644 --- a/libs/ardour/ardour/vst_plugin.h +++ b/libs/ardour/ardour/vst_plugin.h @@ -62,7 +62,7 @@ class VSTPlugin : public ARDOUR::Plugin const char * maker() const; uint32_t parameter_count() const; float default_value (uint32_t port); - nframes_t latency() const; + nframes_t signal_latency() const; void set_parameter (uint32_t port, float val); float get_parameter (uint32_t port) const; int get_parameter_descriptor (uint32_t which, ParameterDescriptor&) const; diff --git a/libs/ardour/audio_unit.cc b/libs/ardour/audio_unit.cc index ac240828b5..dfcffb8cfe 100644 --- a/libs/ardour/audio_unit.cc +++ b/libs/ardour/audio_unit.cc @@ -113,8 +113,12 @@ AUPlugin::default_value (uint32_t port) } nframes_t -AUPlugin::latency () const +AUPlugin::signal_latency () const { + if (_user_latency) { + return _user_latency; + } + return unit->Latency (); } diff --git a/libs/ardour/audioengine.cc b/libs/ardour/audioengine.cc index cbdd1fda1c..a4ce5f291d 100644 --- a/libs/ardour/audioengine.cc +++ b/libs/ardour/audioengine.cc @@ -931,6 +931,27 @@ AudioEngine::get_port_total_latency (const Port& port) return jack_port_get_total_latency (_jack, port._port); } + +void +AudioEngine::update_total_latency (const Port& port) +{ + if (!_jack) { + fatal << _("update_total_latency() called with no JACK client connection") << endmsg; + /*NOTREACHED*/ + } + + if (!_running) { + if (!_has_run) { + fatal << _("update_total_latency() called before engine was started") << endmsg; + /*NOTREACHED*/ + } + } + +#ifdef HAVE_JACK_RECOMPUTE_LATENCY + jack_recompute_total_latency (_jack, port._port); +#endif +} + void AudioEngine::transport_stop () { diff --git a/libs/ardour/io.cc b/libs/ardour/io.cc index c60628b603..b61a654b25 100644 --- a/libs/ardour/io.cc +++ b/libs/ardour/io.cc @@ -1899,7 +1899,7 @@ IO::input_latency () const for (PortSet::const_iterator i = _inputs.begin(); i != _inputs.end(); ++i) { if ((latency = _session.engine().get_port_total_latency (*i)) > max_latency) { max_latency = latency; - } + } } return max_latency; @@ -2411,4 +2411,16 @@ IO::set_denormal_protection (bool yn, void *src) } } +void +IO::update_port_total_latencies () +{ + /* io_lock, not taken: function must be called from Session::process() calltree */ + for (PortSet::iterator i = _inputs.begin(); i != _inputs.end(); ++i) { + _session.engine().update_total_latency (*i); + } + + for (PortSet::iterator i = _outputs.begin(); i != _outputs.end(); ++i) { + _session.engine().update_total_latency (*i); + } +} diff --git a/libs/ardour/ladspa_plugin.cc b/libs/ardour/ladspa_plugin.cc index 98a65546a9..be744202e0 100644 --- a/libs/ardour/ladspa_plugin.cc +++ b/libs/ardour/ladspa_plugin.cc @@ -491,7 +491,6 @@ LadspaPlugin::get_parameter_descriptor (uint32_t which, ParameterDescriptor& des return 0; } - string LadspaPlugin::describe_parameter (ParamID which) { @@ -503,8 +502,12 @@ LadspaPlugin::describe_parameter (ParamID which) } ARDOUR::nframes_t -LadspaPlugin::latency () const +LadspaPlugin::signal_latency () const { + if (_user_latency) { + return _user_latency; + } + if (latency_control_port) { return (nframes_t) floor (*latency_control_port); } else { diff --git a/libs/ardour/plugin_insert.cc b/libs/ardour/plugin_insert.cc index 0ec5dde0db..a842277845 100644 --- a/libs/ardour/plugin_insert.cc +++ b/libs/ardour/plugin_insert.cc @@ -836,11 +836,15 @@ PluginInsert::describe_parameter (ParamID param) } ARDOUR::nframes_t -PluginInsert::latency() +PluginInsert::signal_latency() const { - return _plugins[0]->latency (); + if (_user_latency) { + return _user_latency; + } + + return _plugins[0]->signal_latency (); } - + ARDOUR::PluginType PluginInsert::type () { @@ -870,4 +874,3 @@ PluginInsert::type () } } - diff --git a/libs/ardour/port_insert.cc b/libs/ardour/port_insert.cc index 0c12c0ed2c..06cbcecc38 100644 --- a/libs/ardour/port_insert.cc +++ b/libs/ardour/port_insert.cc @@ -160,7 +160,7 @@ PortInsert::set_state(const XMLNode& node) } ARDOUR::nframes_t -PortInsert::latency() +PortInsert::signal_latency() const { /* because we deliver and collect within the same cycle, all I/O is necessarily delayed by at least frames_per_cycle(). diff --git a/libs/ardour/route.cc b/libs/ardour/route.cc index d8ecabb1c0..507d337619 100644 --- a/libs/ardour/route.cc +++ b/libs/ardour/route.cc @@ -89,6 +89,7 @@ Route::init () _initial_delay = 0; _roll_delay = 0; _own_latency = 0; + _user_latency = 0; _have_internal_generator = false; _declickable = false; _pending_declick = true; @@ -812,6 +813,8 @@ Route::add_processor (boost::shared_ptr processor, ProcessorStreams* processor->activate (); processor->ActiveChanged.connect (bind (mem_fun (_session, &Session::update_latency_compensation), false, false)); + + _user_latency = 0; } if (processor_max_outs != old_rmo || old_rmo == ChanCount::ZERO) { @@ -867,6 +870,8 @@ Route::add_processors (const ProcessorList& others, ProcessorStreams* err) (*i)->activate (); (*i)->ActiveChanged.connect (bind (mem_fun (_session, &Session::update_latency_compensation), false, false)); } + + _user_latency = 0; } if (processor_max_outs != old_rmo || old_rmo == ChanCount::ZERO) { @@ -1098,6 +1103,8 @@ Route::remove_processor (boost::shared_ptr processor, ProcessorStream removed = true; break; } + + _user_latency = 0; } if (!removed) { @@ -1337,6 +1344,7 @@ Route::copy_processors (const Route& other, Placement placement, ProcessorStream /* SUCCESSFUL COPY ATTEMPT: delete the processors we removed pre-copy */ to_be_deleted.clear (); + _user_latency = 0; } } @@ -2470,32 +2478,62 @@ Route::set_meter_point (MeterPoint p, void *src) nframes_t Route::update_total_latency () { - _own_latency = 0; + nframes_t old = _own_latency; - for (ProcessorList::iterator i = _processors.begin(); i != _processors.end(); ++i) { - if ((*i)->active ()) { - _own_latency += (*i)->latency (); + if (_user_latency) { + _own_latency = _user_latency; + } else { + _own_latency = 0; + + for (ProcessorList::iterator i = _processors.begin(); i != _processors.end(); ++i) { + if ((*i)->active ()) { + _own_latency += (*i)->signal_latency (); + } } } set_port_latency (_own_latency); + + if (!_user_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. + */ - /* 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_latency (); + _own_latency += input_latency (); + } + if (old != _own_latency) { + signal_latency_changed (); /* EMIT SIGNAL */ + } + return _own_latency; } +void +Route::set_user_latency (nframes_t nframes) +{ + Latent::set_user_latency (nframes); + _session.update_latency_compensation (false, false); +} + void Route::set_latency_delay (nframes_t longest_session_latency) { - _initial_delay = longest_session_latency - _own_latency; + nframes_t old = _initial_delay; + + if (_own_latency < longest_session_latency) { + _initial_delay = longest_session_latency - _own_latency; + } else { + _initial_delay = 0; + } + + if (_initial_delay != old) { + initial_delay_changed (); /* EMIT SIGNAL */ + } if (_session.transport_stopped()) { _roll_delay = _initial_delay; diff --git a/libs/ardour/session_transport.cc b/libs/ardour/session_transport.cc index de836b79da..52f6346789 100644 --- a/libs/ardour/session_transport.cc +++ b/libs/ardour/session_transport.cc @@ -1248,33 +1248,33 @@ Session::update_latency_compensation (bool with_stop, bool abort) boost::shared_ptr r = routes.reader (); for (RouteList::iterator i = r->begin(); i != r->end(); ++i) { + if (with_stop) { (*i)->handle_transport_stopped (abort, (post_transport_work & PostTransportLocate), (!(post_transport_work & PostTransportLocate) || pending_locate_flush)); } - + nframes_t old_latency = (*i)->signal_latency (); nframes_t track_latency = (*i)->update_total_latency (); - + if (old_latency != track_latency) { + (*i)->update_port_total_latencies (); update_jack = true; } - - if (!(*i)->hidden() && ((*i)->active())) { + + if (!(*i)->hidden() && ((*i)->active())) { _worst_track_latency = max (_worst_track_latency, track_latency); } + } + + if (update_jack) { + _engine.update_total_latencies (); } 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 diff --git a/libs/ardour/track.cc b/libs/ardour/track.cc index eac2ffd31a..dd6d7a2067 100644 --- a/libs/ardour/track.cc +++ b/libs/ardour/track.cc @@ -90,20 +90,29 @@ Track::toggle_monitor_input () ARDOUR::nframes_t Track::update_total_latency () { - _own_latency = 0; + nframes_t old = _own_latency; - for (ProcessorList::iterator i = _processors.begin(); i != _processors.end(); ++i) { - if ((*i)->active ()) { - _own_latency += (*i)->latency (); + if (_user_latency) { + _own_latency = _user_latency; + } else { + _own_latency = 0; + + for (ProcessorList::iterator i = _processors.begin(); i != _processors.end(); ++i) { + if ((*i)->active ()) { + _own_latency += (*i)->signal_latency (); + } } } set_port_latency (_own_latency); + if (old != _own_latency) { + signal_latency_changed (); /* EMIT SIGNAL */ + } + return _own_latency; } - Track::FreezeRecord::~FreezeRecord () { for (vector::iterator i = processor_info.begin(); i != processor_info.end(); ++i) { diff --git a/libs/ardour/vst_plugin.cc b/libs/ardour/vst_plugin.cc index 9adfacba8a..1ce1804697 100644 --- a/libs/ardour/vst_plugin.cc +++ b/libs/ardour/vst_plugin.cc @@ -351,8 +351,12 @@ VSTPlugin::describe_parameter (uint32_t param) } nframes_t -VSTPlugin::latency () const +VSTPlugin::signal_latency () const { + if (_user_latency) { + return _user_latency; + } + return _plugin->initialDelay; } diff --git a/libs/pbd/file_utils.cc b/libs/pbd/file_utils.cc index 30ecbc0586..af2c67b40c 100644 --- a/libs/pbd/file_utils.cc +++ b/libs/pbd/file_utils.cc @@ -41,7 +41,7 @@ get_files_in_directory (const sys::path& directory_path, vector& result) } catch (Glib::FileError& err) { - warning << err.what(); + warning << err.what() << endmsg; } } @@ -113,6 +113,7 @@ find_file_in_search_path(const SearchPath& search_path, return false; } +#if 0 if (tmp.size() != 1) { info << string_compose @@ -123,6 +124,7 @@ find_file_in_search_path(const SearchPath& search_path, ) << endmsg; } +#endif result = tmp.front(); diff --git a/libs/pbd/pbd/controllable.h b/libs/pbd/pbd/controllable.h index ed41af1be8..6dc421cfd9 100644 --- a/libs/pbd/pbd/controllable.h +++ b/libs/pbd/pbd/controllable.h @@ -70,6 +70,21 @@ class Controllable : public PBD::StatefulDestructible { static Controllables registry; }; +/* a utility class for the occasions when you need but do not have + a Controllable +*/ + +class IgnorableControllable : public Controllable +{ + public: + IgnorableControllable () : PBD::Controllable ("ignoreMe") {} + ~IgnorableControllable () {} + + void set_value (float v){} + float get_value () const { return 0.0; } + bool can_send_feedback () const { return false; } +}; + } #endif /* __pbd_controllable_h__ */ -- cgit v1.2.3