diff options
32 files changed, 553 insertions, 99 deletions
diff --git a/gtk2_ardour/SConscript b/gtk2_ardour/SConscript index 3643d3ad32..384e312e62 100644 --- a/gtk2_ardour/SConscript +++ b/gtk2_ardour/SConscript @@ -152,6 +152,7 @@ gtk-custom-ruler.c io_selector.cc keyboard.cc ladspa_pluginui.cc +latency_gui.cc location_ui.cc main.cc marker.cc diff --git a/gtk2_ardour/latency_gui.cc b/gtk2_ardour/latency_gui.cc new file mode 100644 index 0000000000..0146bfdaa8 --- /dev/null +++ b/gtk2_ardour/latency_gui.cc @@ -0,0 +1,167 @@ +#define __STDC_FORMAT_MACROS 1 +#include <inttypes.h> + +#include <ardour/latent.h> +#include <gtkmm2ext/utils.h> + +#include "latency_gui.h" + +#include "i18n.h" + +using namespace PBD; +using namespace Gtk; +using namespace Gtkmm2ext; +using namespace sigc; +using namespace ARDOUR; + + +static const gchar *_unit_strings[] = { + N_("sample"), + N_("msec"), + N_("period"), + 0 +}; + +std::vector<std::string> LatencyGUI::unit_strings; + +void +LatencyGUI::latency_printer (char *buf, unsigned int bufsize) +{ + double nframes = adjustment.get_value(); + + if (nframes < (sample_rate / 1000.0)) { + snprintf (buf, bufsize, "%" PRId64 " samples", (nframes64_t) rint (nframes)); + } else { + snprintf (buf, bufsize, "%.2g msecs" , nframes / (sample_rate / 1000.0)); + } +} + +LatencyGUI::LatencyGUI (Latent& l, nframes64_t sr, nframes64_t psz) + : _latent (l), + initial_value (_latent.signal_latency()), + sample_rate (sr), + period_size (psz), + /* max 1 second, step by frames, page by msecs */ + adjustment (initial_value, 0.0, sample_rate, 1.0, sample_rate / 1000.0f), + bc (adjustment, ignored, sigc::mem_fun (*this, &LatencyGUI::latency_printer)), + reset_button (_("Automatic")) +{ + Widget* w; + + if (unit_strings.empty()) { + unit_strings = I18N (_unit_strings); + } + + set_popdown_strings (units_combo, unit_strings); + units_combo.set_active_text (unit_strings.front()); + + w = manage (new Image (Stock::ADD, ICON_SIZE_BUTTON)); + w->show (); + plus_button.add (*w); + w = manage (new Image (Stock::REMOVE, ICON_SIZE_BUTTON)); + w->show (); + minus_button.add (*w); + + hbox1.pack_start (bc, true, true); + + hbox2.set_homogeneous (false); + hbox2.set_spacing (12); + hbox2.pack_start (reset_button); + hbox2.pack_start (minus_button); + hbox2.pack_start (plus_button); + hbox2.pack_start (units_combo, true, true); + + minus_button.signal_clicked().connect (bind (mem_fun (*this, &LatencyGUI::change_latency_from_button), -1)); + plus_button.signal_clicked().connect (bind (mem_fun (*this, &LatencyGUI::change_latency_from_button), 1)); + reset_button.signal_clicked().connect (mem_fun (*this, &LatencyGUI::reset)); + + adjustment.signal_value_changed().connect (mem_fun (*this, &LatencyGUI::finish)); + + bc.set_size_request (-1, 25); + bc.set_style (BarController::LeftToRight); + bc.set_use_parent (true); + bc.set_name (X_("PluginSlider")); + + set_spacing (12); + pack_start (hbox1, true, true); + pack_start (hbox2, true, true); +} + +void +LatencyGUI::finish () +{ + nframes64_t new_value = (nframes64_t) adjustment.get_value(); + if (new_value != initial_value) { + _latent.set_user_latency (new_value); + } +} + +void +LatencyGUI::reset () +{ + _latent.set_user_latency (0); + adjustment.set_value (initial_value); +} + +void +LatencyGUI::refresh () +{ + initial_value = _latent.signal_latency(); + adjustment.set_value (initial_value); +} + +void +LatencyGUI::change_latency_from_button (int dir) +{ + Glib::ustring unitstr = units_combo.get_active_text(); + double shift; + + if (unitstr == unit_strings[0]) { + shift = 1; + } else if (unitstr == unit_strings[1]) { + shift = (sample_rate / 1000.0); + } else if (unitstr == unit_strings[2]) { + shift = period_size; + } else { + fatal << string_compose (_("programming error: %1 (%2)"), X_("illegal string in latency GUI units combo"), unitstr) + << endmsg; + /*NOTREACHED*/ + } + + if (dir > 0) { + adjustment.set_value (adjustment.get_value() + shift); + } else { + adjustment.set_value (adjustment.get_value() - shift); + } +} + +LatencyDialog::LatencyDialog (const Glib::ustring& title, Latent& l, nframes64_t sr, nframes64_t psz) + : ArdourDialog (title, false, true), + lwidget (l, sr, psz) +{ + + get_vbox()->pack_start (lwidget); + add_button (Stock::CANCEL, RESPONSE_CANCEL); + add_button (Stock::APPLY, RESPONSE_REJECT); + add_button (Stock::OK, RESPONSE_ACCEPT); + + show_all (); + + while (true) { + int ret = run (); + + switch (ret) { + case RESPONSE_ACCEPT: + return; + break; + + case RESPONSE_REJECT: + lwidget.finish (); + break; + default: + return; + } + } +} + + diff --git a/gtk2_ardour/latency_gui.h b/gtk2_ardour/latency_gui.h new file mode 100644 index 0000000000..49f22fa266 --- /dev/null +++ b/gtk2_ardour/latency_gui.h @@ -0,0 +1,61 @@ +#include <vector> +#include <string> + +#include <gtkmm/dialog.h> +#include <gtkmm/box.h> +#include <gtkmm/button.h> +#include <gtkmm/adjustment.h> + +#include <gtkmm2ext/barcontroller.h> +#include <pbd/controllable.h> + +#include <ardour/types.h> + +#include "ardour_dialog.h" + +namespace ARDOUR { + class Latent; +} + +class LatencyGUI : public Gtk::VBox +{ + public: + LatencyGUI (ARDOUR::Latent&, nframes64_t sample_rate, nframes64_t period_size); + ~LatencyGUI() { } + + void finish (); + void reset (); + void refresh (); + + private: + ARDOUR::Latent& _latent; + nframes64_t initial_value; + nframes64_t sample_rate; + nframes64_t period_size; + PBD::IgnorableControllable ignored; + + Gtk::Adjustment adjustment; + Gtkmm2ext::BarController bc; + Gtk::HBox hbox1; + Gtk::HBox hbox2; + Gtk::HButtonBox hbbox; + Gtk::Button minus_button; + Gtk::Button plus_button; + Gtk::Button reset_button; + Gtk::ComboBoxText units_combo; + + void change_latency_from_button (int dir); + void latency_printer (char* buf, unsigned int bufsize); + + static std::vector<std::string> unit_strings; +}; + +class LatencyDialog : public ArdourDialog +{ + public: + LatencyDialog (const Glib::ustring& title, ARDOUR::Latent&, nframes64_t sample_rate, nframes64_t period_size); + ~LatencyDialog() {} + + private: + LatencyGUI lwidget; +}; diff --git a/gtk2_ardour/mixer_strip.cc b/gtk2_ardour/mixer_strip.cc index cb57f9809c..40571d3e91 100644 --- a/gtk2_ardour/mixer_strip.cc +++ b/gtk2_ardour/mixer_strip.cc @@ -984,6 +984,11 @@ MixerStrip::build_route_ops_menu () items.push_back (CheckMenuElem (_("Active"), mem_fun (*this, &RouteUI::toggle_route_active))); route_active_menu_item = dynamic_cast<CheckMenuItem *> (&items.back()); route_active_menu_item->set_active (_route->active()); + + items.push_back (SeparatorElem()); + + items.push_back (MenuElem (_("Adjust latency"), mem_fun (*this, &RouteUI::adjust_latency))); + items.push_back (SeparatorElem()); items.push_back (CheckMenuElem (_("Invert Polarity"), mem_fun (*this, &RouteUI::toggle_polarity))); polarity_menu_item = dynamic_cast<CheckMenuItem *> (&items.back()); diff --git a/gtk2_ardour/route_params_ui.cc b/gtk2_ardour/route_params_ui.cc index 51b1c9e26b..7807cd4a84 100644 --- a/gtk2_ardour/route_params_ui.cc +++ b/gtk2_ardour/route_params_ui.cc @@ -18,6 +18,8 @@ */ #include <algorithm> +#define __STDC_FORMAT_MACROS +#include <inttypes.h> #include <glibmm/thread.h> #include <gtkmm2ext/utils.h> @@ -58,7 +60,9 @@ using namespace sigc; RouteParams_UI::RouteParams_UI () : ArdourDialog ("track/bus inspector"), + latency_apply_button (Stock::APPLY), track_menu(0) + { pre_insert_box = 0; post_insert_box = 0; @@ -66,12 +70,14 @@ RouteParams_UI::RouteParams_UI () _output_iosel = 0; _active_pre_view = 0; _active_post_view = 0; - + latency_widget = 0; + using namespace Notebook_Helpers; input_frame.set_shadow_type(Gtk::SHADOW_NONE); output_frame.set_shadow_type(Gtk::SHADOW_NONE); - + latency_frame.set_shadow_type (Gtk::SHADOW_NONE); + notebook.set_show_tabs (true); notebook.set_show_border (true); notebook.set_name ("RouteParamNotebook"); @@ -92,7 +98,6 @@ RouteParams_UI::RouteParams_UI () route_select_scroller.add(route_display); route_select_scroller.set_policy(Gtk::POLICY_NEVER, Gtk::POLICY_AUTOMATIC); - route_select_frame.set_name("RouteSelectBaseFrame"); route_select_frame.set_shadow_type (Gtk::SHADOW_IN); route_select_frame.add(route_select_scroller); @@ -103,12 +108,17 @@ RouteParams_UI::RouteParams_UI () notebook.pages().push_back (TabElem (output_frame, _("Outputs"))); notebook.pages().push_back (TabElem (pre_redir_hpane, _("Pre-fader Redirects"))); notebook.pages().push_back (TabElem (post_redir_hpane, _("Post-fader Redirects"))); + notebook.pages().push_back (TabElem (latency_frame, _("Latency"))); notebook.set_name ("InspectorNotebook"); title_label.set_name ("RouteParamsTitleLabel"); update_title(); + latency_packer.set_spacing (18); + latency_button_box.pack_start (latency_apply_button); + delay_label.set_alignment (0, 0.5); + // changeable area route_param_frame.set_name("RouteParamsBaseFrame"); route_param_frame.set_shadow_type (Gtk::SHADOW_IN); @@ -116,7 +126,6 @@ RouteParams_UI::RouteParams_UI () route_hpacker.pack_start (notebook, true, true); - route_vpacker.pack_start (title_label, false, false); route_vpacker.pack_start (route_hpacker, true, true); @@ -142,6 +151,7 @@ RouteParams_UI::RouteParams_UI () title += _("Track/Bus Inspector"); set_title (title.get_string()); + // events route_display.get_selection()->signal_changed().connect(mem_fun(*this, &RouteParams_UI::route_selected)); route_display.get_column(0)->signal_clicked().connect(mem_fun(*this, &RouteParams_UI::show_track_menu)); @@ -253,6 +263,55 @@ RouteParams_UI::cleanup_processor_boxes() } void +RouteParams_UI::refresh_latency () +{ + if (latency_widget) { + latency_widget->refresh(); + + char buf[128]; + snprintf (buf, sizeof (buf), _("Playback delay: %u samples"), _route->initial_delay()); + delay_label.set_text (buf); + } +} + +void +RouteParams_UI::cleanup_latency_frame () +{ + if (latency_widget) { + latency_frame.remove (); + latency_packer.remove (*latency_widget); + latency_packer.remove (latency_button_box); + latency_packer.remove (delay_label); + delete latency_widget; + latency_widget = 0; + latency_conn.disconnect (); + delay_conn.disconnect (); + latency_apply_conn.disconnect (); + } +} + +void +RouteParams_UI::setup_latency_frame () +{ + latency_widget = new LatencyGUI (*(_route.get()), session->frame_rate(), session->engine().frames_per_cycle()); + + char buf[128]; + snprintf (buf, sizeof (buf), _("Playback delay: %u samples"), _route->initial_delay()); + delay_label.set_text (buf); + + latency_packer.pack_start (*latency_widget, false, false); + latency_packer.pack_start (latency_button_box, false, false); + latency_packer.pack_start (delay_label); + + latency_apply_conn = latency_apply_button.signal_clicked().connect (mem_fun (*latency_widget, &LatencyGUI::finish)); + latency_conn = _route->signal_latency_changed.connect (mem_fun (*this, &RouteParams_UI::refresh_latency)); + delay_conn = _route->initial_delay_changed.connect (mem_fun (*this, &RouteParams_UI::refresh_latency)); + + latency_frame.add (latency_packer); + latency_frame.show_all (); +} + +void RouteParams_UI::setup_io_frames() { cleanup_io_frames(); @@ -387,6 +446,7 @@ RouteParams_UI::session_gone () cleanup_pre_view(); cleanup_post_view(); cleanup_processor_boxes(); + cleanup_latency_frame (); _route.reset ((Route*) 0); _pre_processor.reset ((Processor*) 0); @@ -402,6 +462,7 @@ RouteParams_UI::route_selected() { Glib::RefPtr<TreeSelection> selection = route_display.get_selection(); TreeModel::iterator iter = selection->get_selected(); // only used with Gtk::SELECTION_SINGLE + if(iter) { //If anything is selected boost::shared_ptr<Route> route = (*iter)[route_display_columns.route] ; @@ -419,6 +480,7 @@ RouteParams_UI::route_selected() cleanup_pre_view(); cleanup_post_view(); cleanup_io_frames(); + cleanup_latency_frame (); } // update the other panes with the correct info @@ -427,6 +489,7 @@ RouteParams_UI::route_selected() setup_io_frames(); setup_processor_boxes(); + setup_latency_frame (); // bind to redirects changed event for this route _route_conn = route->processors_changed.connect (mem_fun(*this, &RouteParams_UI::processors_changed)); @@ -434,6 +497,7 @@ RouteParams_UI::route_selected() track_input_label.set_text (_route->name()); update_title(); + } else { // no selection if (_route) { @@ -444,6 +508,7 @@ RouteParams_UI::route_selected() cleanup_pre_view(); cleanup_post_view(); cleanup_processor_boxes(); + cleanup_latency_frame (); _route.reset ((Route*) 0); _pre_processor.reset ((Processor*) 0); @@ -454,52 +519,19 @@ RouteParams_UI::route_selected() } } -//void -//RouteParams_UI::route_unselected (gint row, gint col, GdkEvent *ev) -//{ -// if (_route) { -// _route_conn.disconnect(); - - // remove from view -// cleanup_io_frames(); -// cleanup_pre_view(); -// cleanup_post_view(); -// cleanup_processor_boxes(); - -// _route.reset ((Route*)0); -// _pre_processor = 0; -// _post_processor = 0; -// track_input_label.set_text(_("NO TRACK")); -// update_title(); -// } -//} - void RouteParams_UI::processors_changed () - { ENSURE_GUI_THREAD(mem_fun(*this, &RouteParams_UI::processors_changed)); - -// pre_insert_list.freeze (); -// pre_insert_list.clear (); -// post_insert_list.freeze (); -// post_insert_list.clear (); -// if (_route) { -// _route->foreach_redirect (this, &RouteParams_UI::add_redirect_to_display); -// } -// pre_insert_list.thaw (); -// post_insert_list.thaw (); - cleanup_pre_view(); cleanup_post_view(); _pre_processor.reset ((Processor*) 0); _post_processor.reset ((Processor*) 0); + //update_title(); } - - void RouteParams_UI::show_track_menu() { @@ -515,8 +547,6 @@ RouteParams_UI::show_track_menu() track_menu->popup (1, gtk_get_current_event_time()); } - - void RouteParams_UI::redirect_selected (boost::shared_ptr<ARDOUR::Processor> insert, ARDOUR::Placement place) { @@ -658,7 +688,7 @@ RouteParams_UI::update_title () // } title_label.set_text(_route->name()); - + title += _route->name(); set_title(title.get_string()); @@ -670,7 +700,6 @@ RouteParams_UI::update_title () } } - void RouteParams_UI::start_updating () { diff --git a/gtk2_ardour/route_params_ui.h b/gtk2_ardour/route_params_ui.h index 39305ae467..e9fca333ab 100644 --- a/gtk2_ardour/route_params_ui.h +++ b/gtk2_ardour/route_params_ui.h @@ -42,6 +42,7 @@ #include "ardour_dialog.h" #include "processor_box.h" #include "route_processor_selection.h" +#include "latency_gui.h" namespace ARDOUR { class Route; @@ -83,7 +84,7 @@ class RouteParams_UI : public ArdourDialog Gtk::Frame output_frame; Gtk::HPaned pre_redir_hpane; Gtk::HPaned post_redir_hpane; - + Gtk::Frame route_select_frame; Gtk::HBox route_hpacker; @@ -102,7 +103,18 @@ class RouteParams_UI : public ArdourDialog Gtk::VBox choice_vpacker; + Gtk::Frame latency_frame; + Gtk::VBox latency_packer; + Gtk::HButtonBox latency_button_box; + Gtk::Button latency_apply_button; + LatencyGUI* latency_widget; + Gtk::Label delay_label; + sigc::connection latency_conn; + sigc::connection delay_conn; + sigc::connection latency_apply_conn; + void refresh_latency (); + Gtk::ToggleButton input_button; Gtk::ToggleButton output_button; Gtk::Label track_input_label; @@ -168,8 +180,8 @@ class RouteParams_UI : public ArdourDialog void cleanup_io_frames(); void cleanup_pre_view(bool stopupdate = true); void cleanup_post_view(bool stopupdate = true); - - + void cleanup_latency_frame (); + void setup_latency_frame (); void processors_changed (); diff --git a/gtk2_ardour/route_ui.cc b/gtk2_ardour/route_ui.cc index eb2eaeb115..95042af6ea 100644 --- a/gtk2_ardour/route_ui.cc +++ b/gtk2_ardour/route_ui.cc @@ -22,17 +22,21 @@ #include <gtkmm2ext/choice.h> #include <gtkmm2ext/doi.h> #include <gtkmm2ext/bindable_button.h> +#include <gtkmm2ext/barcontroller.h> #include <ardour/route_group.h> #include <pbd/memento_command.h> #include <pbd/stacktrace.h> #include <pbd/shiva.h> +#include <pbd/controllable.h> #include "route_ui.h" #include "keyboard.h" #include "utils.h" #include "prompter.h" #include "gui_thread.h" +#include "ardour_dialog.h" +#include "latency_gui.h" #include <ardour/route.h> #include <ardour/session.h> @@ -451,7 +455,7 @@ RouteUI::update_rec_display () rec_enable_button->set_active (model); ignore_toggle = false; } - + /* now make sure its color state is correct */ if (model) { @@ -1040,3 +1044,8 @@ RouteUI::map_frozen () } } +void +RouteUI::adjust_latency () +{ + LatencyDialog dialog (_route->name() + _("latency"), *(_route.get()), _session.frame_rate(), _session.engine().frames_per_cycle()); +} diff --git a/gtk2_ardour/route_ui.h b/gtk2_ardour/route_ui.h index 4a4fed5b75..730330372a 100644 --- a/gtk2_ardour/route_ui.h +++ b/gtk2_ardour/route_ui.h @@ -163,6 +163,8 @@ class RouteUI : public virtual AxisView void reversibly_apply_route_boolean (string name, void (ARDOUR::Route::*func)(bool, void*), bool, void *); void reversibly_apply_track_boolean (string name, void (ARDOUR::Track::*func)(bool, void*), bool, void *); + + void adjust_latency (); }; #endif /* __ardour_route_ui__ */ 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 <jack/jack.h> +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 <ardour/data_type.h> #include <ardour/port_set.h> #include <ardour/chan_count.h> +#include <ardour/latent.h> 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 <ardour/types.h> + +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 <ardour/chan_count.h> #include <ardour/plugin_state.h> #include <ardour/cycles.h> +#include <ardour/latent.h> #include <ardour/param_id.h> #include <vector> @@ -79,7 +80,7 @@ class PluginInfo { typedef boost::shared_ptr<PluginInfo> PluginInfoPtr; typedef std::list<PluginInfoPtr> 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 <ardour/plugin_state.h> #include <ardour/buffer_set.h> #include <ardour/automatable.h> - +#include <ardour/latent.h> 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<void,void*> solo_changed; sigc::signal<void,void*> solo_safe_changed; @@ -204,6 +205,8 @@ class Route : public IO sigc::signal<void,void*> mix_group_changed; sigc::signal<void> active_changed; sigc::signal<void,void*> meter_change; + sigc::signal<void> signal_latency_changed; + sigc::signal<void> 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> 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> 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<RouteList> r = routes.reader (); for (RouteList::iterator i = r->begin(); i != r->end(); ++i) { + if (with_stop) { (*i)->handle_transport_stopped (abort, (post_transport_work & PostTransportLocate), (!(post_transport_work & PostTransportLocate) || pending_locate_flush)); } - + nframes_t old_latency = (*i)->signal_latency (); nframes_t track_latency = (*i)->update_total_latency (); - + if (old_latency != track_latency) { + (*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<FreezeRecordProcessorInfo*>::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<string>& 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__ */ |