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,11 +70,13 @@ 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);
@ -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)
{
@ -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;
@ -102,6 +103,17 @@ 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;
@ -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>
@ -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,10 +143,13 @@ 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 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

@ -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,9 +836,13 @@ 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
@ -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,16 +2478,23 @@ Route::set_meter_point (MeterPoint p, void *src)
nframes_t
Route::update_total_latency ()
{
nframes_t old = _own_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)->latency ();
_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
@ -2487,15 +2502,38 @@ Route::update_total_latency ()
have to take into account any 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)
{
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,6 +1248,7 @@ 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));
@ -1257,6 +1258,7 @@ Session::update_latency_compensation (bool with_stop, bool abort)
nframes_t track_latency = (*i)->update_total_latency ();
if (old_latency != track_latency) {
(*i)->update_port_total_latencies ();
update_jack = true;
}
@ -1265,16 +1267,14 @@ Session::update_latency_compensation (bool with_stop, bool abort)
}
}
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 ();
}
for (RouteList::iterator i = r->begin(); i != r->end(); ++i) {
(*i)->set_latency_delay (_worst_track_latency);
}
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 ()
{
nframes_t old = _own_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)->latency ();
_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__ */