summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--gtk2_ardour/SConscript1
-rw-r--r--gtk2_ardour/latency_gui.cc167
-rw-r--r--gtk2_ardour/latency_gui.h61
-rw-r--r--gtk2_ardour/mixer_strip.cc5
-rw-r--r--gtk2_ardour/route_params_ui.cc113
-rw-r--r--gtk2_ardour/route_params_ui.h18
-rw-r--r--gtk2_ardour/route_ui.cc11
-rw-r--r--gtk2_ardour/route_ui.h2
-rw-r--r--libs/ardour/SConscript23
-rw-r--r--libs/ardour/ardour/audio_unit.h2
-rw-r--r--libs/ardour/ardour/audioengine.h1
-rw-r--r--libs/ardour/ardour/io.h9
-rw-r--r--libs/ardour/ardour/ladspa_plugin.h2
-rw-r--r--libs/ardour/ardour/latent.h26
-rw-r--r--libs/ardour/ardour/plugin.h4
-rw-r--r--libs/ardour/ardour/plugin_insert.h2
-rw-r--r--libs/ardour/ardour/port_insert.h2
-rw-r--r--libs/ardour/ardour/processor.h6
-rw-r--r--libs/ardour/ardour/route.h8
-rw-r--r--libs/ardour/ardour/vst_plugin.h2
-rw-r--r--libs/ardour/audio_unit.cc6
-rw-r--r--libs/ardour/audioengine.cc21
-rw-r--r--libs/ardour/io.cc14
-rw-r--r--libs/ardour/ladspa_plugin.cc7
-rw-r--r--libs/ardour/plugin_insert.cc11
-rw-r--r--libs/ardour/port_insert.cc2
-rw-r--r--libs/ardour/route.cc62
-rw-r--r--libs/ardour/session_transport.cc20
-rw-r--r--libs/ardour/track.cc19
-rw-r--r--libs/ardour/vst_plugin.cc6
-rw-r--r--libs/pbd/file_utils.cc4
-rw-r--r--libs/pbd/pbd/controllable.h15
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__ */