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
This commit is contained in:
Paul Davis 2007-06-27 22:06:35 +00:00
parent 34be8c2119
commit b5af3bb8e3
32 changed files with 553 additions and 99 deletions

View File

@ -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

167
gtk2_ardour/latency_gui.cc Normal file
View File

@ -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;
}
}
}

61
gtk2_ardour/latency_gui.h Normal file
View File

@ -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;
};

View File

@ -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());

View File

@ -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));
@ -252,6 +262,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()
{
@ -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 ()
{

View File

@ -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 ();

View File

@ -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());
}

View File

@ -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__ */

View File

@ -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")

View File

@ -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;

View File

@ -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
*/

View File

@ -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; }

View File

@ -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;

View File

@ -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__*/

View File

@ -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;

View File

@ -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);

View File

@ -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;

View File

@ -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) {}

View File

@ -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;

View File

@ -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;

View File

@ -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 ();
}

View File

@ -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 ()
{

View File

@ -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);
}
}

View File

@ -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 {

View File

@ -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 ()
}
}

View File

@ -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().

View File

@ -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;

View File

@ -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

View File

@ -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) {

View File

@ -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;
}

View File

@ -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();

View File

@ -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__ */