From ec6ab8a04811acd1b548a23ab8f95c27797267fe Mon Sep 17 00:00:00 2001 From: David Robillard Date: Fri, 3 Oct 2008 03:16:19 +0000 Subject: [PATCH] Apply panners/automation patch from torbenh (Panner is-a Processor). git-svn-id: svn://localhost/ardour2/branches/3.0@3848 d708f5d6-7413-0410-9779-e7cbd77b26cf --- gtk2_ardour/audio_time_axis.cc | 11 +- gtk2_ardour/editor_actions.cc | 2 +- gtk2_ardour/generic_pluginui.cc | 4 +- gtk2_ardour/mixer_strip.cc | 10 +- gtk2_ardour/panner2d.cc | 4 +- gtk2_ardour/panner_ui.cc | 33 +-- libs/ardour/ardour/io.h | 7 +- libs/ardour/ardour/panner.h | 93 +++++---- libs/ardour/automatable.cc | 19 +- libs/ardour/io.cc | 49 +++-- libs/ardour/ladspa_plugin.cc | 4 +- libs/ardour/lv2_plugin.cc | 2 +- libs/ardour/panner.cc | 190 +++++++++--------- libs/ardour/plugin_insert.cc | 13 +- libs/ardour/processor.cc | 2 +- libs/ardour/route.cc | 7 +- .../mackie/mackie_control_protocol.cc | 12 +- libs/surfaces/mackie/route_signal.cc | 4 +- 18 files changed, 261 insertions(+), 205 deletions(-) diff --git a/gtk2_ardour/audio_time_axis.cc b/gtk2_ardour/audio_time_axis.cc index 14a94e7195..20a5be2dcf 100644 --- a/gtk2_ardour/audio_time_axis.cc +++ b/gtk2_ardour/audio_time_axis.cc @@ -352,11 +352,14 @@ AudioTimeAxisView::create_automation_child (const Evoral::Parameter& param, bool void AudioTimeAxisView::update_pans (bool show) { - Panner::iterator p; + const set& params = _route->panner().what_can_be_automated(); + set::iterator p; uint32_t i = 0; - for (p = _route->panner().begin(); p != _route->panner().end(); ++p) { - boost::shared_ptr pan_control = (*p)->pan_control(); + for (p = params.begin(); p != params.end(); ++p) { + boost::shared_ptr pan_control + = boost::dynamic_pointer_cast( + _route->panner().data().control(*p)); if (pan_control->parameter().type() == NullAutomation) { error << "Pan control has NULL automation type!" << endmsg; @@ -370,7 +373,7 @@ AudioTimeAxisView::update_pans (bool show) false, parent_canvas, _route->describe_parameter(pan_control->parameter()))); - add_automation_child(Evoral::Parameter(PanAutomation, i), pan_track, show); + add_automation_child(*p, pan_track, show); ++i; } } diff --git a/gtk2_ardour/editor_actions.cc b/gtk2_ardour/editor_actions.cc index fdad190c47..06e4b2c91f 100644 --- a/gtk2_ardour/editor_actions.cc +++ b/gtk2_ardour/editor_actions.cc @@ -755,7 +755,7 @@ Editor::register_actions () act = ActionManager::register_action (editor_actions, X_("addExternalAudioToRegionList"), _("Import to Region List"), bind (mem_fun(*this, &Editor::add_external_audio_action), ImportAsRegion)); ActionManager::session_sensitive_actions.push_back (act); -+ ActionManager::register_action (editor_actions, X_("importFromSession"), _("Import From Session"), mem_fun(*this, &Editor::session_import_dialog)); + ActionManager::register_action (editor_actions, X_("importFromSession"), _("Import From Session"), mem_fun(*this, &Editor::session_import_dialog)); act = ActionManager::register_toggle_action (editor_actions, X_("toggle-waveform-visible"), _("Show Waveforms"), mem_fun (*this, &Editor::toggle_waveform_visibility)); ActionManager::track_selection_sensitive_actions.push_back (act); diff --git a/gtk2_ardour/generic_pluginui.cc b/gtk2_ardour/generic_pluginui.cc index 1c414d6785..56fa8483b7 100644 --- a/gtk2_ardour/generic_pluginui.cc +++ b/gtk2_ardour/generic_pluginui.cc @@ -185,7 +185,7 @@ GenericPluginUI::build () /* Don't show latency control ports */ - if (plugin->describe_parameter (Evoral::Parameter(PluginAutomation, i)) == X_("latency")) { + if (plugin->describe_parameter (Evoral::Parameter(PluginAutomation, 0, i)) == X_("latency")) { continue; } @@ -211,7 +211,7 @@ GenericPluginUI::build () boost::shared_ptr c = boost::dynamic_pointer_cast( - insert->data().control(Evoral::Parameter(PluginAutomation, i))); + insert->data().control(Evoral::Parameter(PluginAutomation, 0, i))); if ((cui = build_control_ui (i, c)) == 0) { error << string_compose(_("Plugin Editor: could not build control element for port %1"), i) << endmsg; diff --git a/gtk2_ardour/mixer_strip.cc b/gtk2_ardour/mixer_strip.cc index 2459ac57a8..f98469eb7b 100644 --- a/gtk2_ardour/mixer_strip.cc +++ b/gtk2_ardour/mixer_strip.cc @@ -745,11 +745,13 @@ MixerStrip::connect_to_pan () panstate_connection.disconnect (); panstyle_connection.disconnect (); - if (!_route->panner().empty()) { - StreamPanner* sp = _route->panner().front(); + boost::shared_ptr pan_control + = boost::dynamic_pointer_cast( + _route->panner().data().control(Evoral::Parameter( PanAutomation ) )); - panstate_connection = sp->pan_control()->alist()->automation_state_changed.connect (mem_fun(panners, &PannerUI::pan_automation_state_changed)); - panstyle_connection = sp->pan_control()->alist()->automation_style_changed.connect (mem_fun(panners, &PannerUI::pan_automation_style_changed)); + if (pan_control) { + panstate_connection = pan_control->alist()->automation_state_changed.connect (mem_fun(panners, &PannerUI::pan_automation_state_changed)); + panstyle_connection = pan_control->alist()->automation_style_changed.connect (mem_fun(panners, &PannerUI::pan_automation_style_changed)); } panners.pan_changed (this); diff --git a/gtk2_ardour/panner2d.cc b/gtk2_ardour/panner2d.cc index 6fa64be8e6..34b223ecef 100644 --- a/gtk2_ardour/panner2d.cc +++ b/gtk2_ardour/panner2d.cc @@ -411,7 +411,9 @@ Panner2d::handle_motion (gint evx, gint evy, GdkModifierType state) if (drag_is_puck) { - panner[drag_index]->set_position (drag_target->x, drag_target->y); + //panner.streampanner(drag_index).set_position (drag_target->x, drag_target->y); + panner.pan_control( drag_index )->set_value( drag_target->x ); + //panner.control( Evoral::Parameter( PanAutomation, 1, drag_index ) )->set_value( drag_target->y ); } else { diff --git a/gtk2_ardour/panner_ui.cc b/gtk2_ardour/panner_ui.cc index fec4cbf464..2be7be73ed 100644 --- a/gtk2_ardour/panner_ui.cc +++ b/gtk2_ardour/panner_ui.cc @@ -304,7 +304,7 @@ PannerUI::setup_pan () } else if (nouts == 2) { vector::size_type asz; - uint32_t npans = _io->panner().size(); + uint32_t npans = _io->panner().npanners(); while (!pan_adjustments.empty()) { delete pan_bars.back(); @@ -321,7 +321,7 @@ PannerUI::setup_pan () /* initialize adjustment with 0.0 (L) or 1.0 (R) for the first and second panners, which serves as a default, otherwise use current value */ - _io->panner()[asz]->get_position (rx); + rx = _io->panner().pan_control( asz)->get_value(); if (npans == 1) { x = 0.5; @@ -334,13 +334,14 @@ PannerUI::setup_pan () } pan_adjustments.push_back (new Adjustment (x, 0, 1.0, 0.05, 0.1)); - bc = new PannerBar (*pan_adjustments[asz], _io->panner()[asz]->pan_control()); + bc = new PannerBar (*pan_adjustments[asz], + boost::static_pointer_cast( _io->panner().pan_control( asz )) ); /* now set adjustment with current value of panner, then connect the signals */ pan_adjustments.back()->set_value(rx); pan_adjustments.back()->signal_value_changed().connect (bind (mem_fun(*this, &PannerUI::pan_adjustment_changed), (uint32_t) asz)); - _io->panner()[asz]->Changed.connect (bind (mem_fun(*this, &PannerUI::pan_value_changed), (uint32_t) asz)); + _io->panner().pan_control( asz )->Changed.connect (bind (mem_fun(*this, &PannerUI::pan_value_changed), (uint32_t) asz)); bc->set_name ("PanSlider"); @@ -425,7 +426,7 @@ PannerUI::build_pan_menu (uint32_t which) /* set state first, connect second */ - (dynamic_cast (&items.back()))->set_active (_io->panner()[which]->muted()); + (dynamic_cast (&items.back()))->set_active (_io->panner().streampanner(which).muted()); (dynamic_cast (&items.back()))->signal_toggled().connect (bind (mem_fun(*this, &PannerUI::pan_mute), which)); @@ -445,8 +446,8 @@ PannerUI::build_pan_menu (uint32_t which) void PannerUI::pan_mute (uint32_t which) { - StreamPanner* sp = _io->panner()[which]; - sp->set_muted (!sp->muted()); + StreamPanner& sp = _io->panner().streampanner(which); + sp.set_muted (!sp.muted()); } void @@ -492,7 +493,7 @@ PannerUI::pan_changed (void *src) return; } - switch (_io->panner().size()) { + switch (_io->panner().npanners()) { case 0: panning_link_direction_button.set_sensitive (false); panning_link_button.set_sensitive (false); @@ -527,11 +528,11 @@ PannerUI::pan_changed (void *src) void PannerUI::pan_adjustment_changed (uint32_t which) { - if (!in_pan_update && which < _io->panner().size()) { + if (!in_pan_update && which < _io->panner().npanners()) { float xpos; float val = pan_adjustments[which]->get_value (); - _io->panner()[which]->get_position (xpos); + xpos = _io->panner().pan_control( which )->get_value(); /* add a kinda-sorta detent for the middle */ @@ -548,7 +549,7 @@ PannerUI::pan_adjustment_changed (uint32_t which) if (!Panner::equivalent (val, xpos)) { - _io->panner()[which]->set_position (val); + _io->panner().streampanner(which).set_position (val); /* XXX the panner objects have no access to the session, so do this here. ick. @@ -563,11 +564,11 @@ PannerUI::pan_value_changed (uint32_t which) { ENSURE_GUI_THREAD (bind (mem_fun(*this, &PannerUI::pan_value_changed), which)); - if (_io->n_outputs().n_audio() > 1 && which < _io->panner().size()) { + if (_io->n_outputs().n_audio() > 1 && which < _io->panner().npanners()) { float xpos; float val = pan_adjustments[which]->get_value (); - _io->panner()[which]->get_position (xpos); + _io->panner().streampanner(which).get_position (xpos); if (!Panner::equivalent (val, xpos)) { in_pan_update = true; @@ -593,14 +594,14 @@ PannerUI::update_pan_bars (bool only_if_aplay) float xpos, val; if (only_if_aplay) { - boost::shared_ptr alist (_io->panner()[n]->pan_control()->alist()); + boost::shared_ptr alist (_io->panner().streampanner(n).pan_control()->alist()); if (!alist->automation_playback()) { continue; } } - _io->panner()[n]->get_effective_position (xpos); + _io->panner().streampanner(n).get_effective_position (xpos); val = (*i)->get_value (); if (!Panner::equivalent (val, xpos)) { @@ -727,7 +728,7 @@ PannerUI::pan_automation_state_changed () return; } - x = (_io->panner().front()->pan_control()->alist()->automation_state() != Off); + x = (_io->panner().streampanner(0).pan_control()->alist()->automation_state() != Off); if (pan_automation_state_button.get_active() != x) { ignore_toggle = true; diff --git a/libs/ardour/ardour/io.h b/libs/ardour/ardour/io.h index 34ffad94ce..af26319e82 100644 --- a/libs/ardour/ardour/io.h +++ b/libs/ardour/ardour/io.h @@ -228,15 +228,16 @@ class IO : public SessionObject, public AutomatableControls, public Latent /* automation */ struct GainControl : public AutomationControl { - GainControl (std::string name, IO& i, boost::shared_ptr al) - : AutomationControl (i._session, al->parameter(), al, name) + GainControl (std::string name, IO* i, const Evoral::Parameter ¶m, + boost::shared_ptr al = boost::shared_ptr() ) + : AutomationControl (i->_session, param, al, name ) , _io (i) {} void set_value (float val); float get_value (void) const; - IO& _io; + IO* _io; }; boost::shared_ptr gain_control() { diff --git a/libs/ardour/ardour/panner.h b/libs/ardour/ardour/panner.h index 7b4f2d1039..7019f5e5ac 100644 --- a/libs/ardour/ardour/panner.h +++ b/libs/ardour/ardour/panner.h @@ -32,12 +32,15 @@ #include #include +#include +#include using std::istream; using std::ostream; namespace ARDOUR { +class Route; class Session; class Panner; class BufferSet; @@ -102,21 +105,7 @@ class StreamPanner : public sigc::trackable, public PBD::Stateful bool _muted; - struct PanControllable : public AutomationControl { - PanControllable (Session& s, std::string name, StreamPanner& p, Evoral::Parameter param) - : AutomationControl (s, param, - boost::shared_ptr(new AutomationList(param)), name) - , panner (p) - { assert(param.type() != NullAutomation); } - - AutomationList* alist() { return (AutomationList*)_list.get(); } - StreamPanner& panner; - - void set_value (float); - float get_value (void) const; - }; - - boost::shared_ptr _control; + boost::shared_ptr _control; void add_state (XMLNode&); virtual void update () = 0; @@ -197,7 +186,8 @@ class Multi2dPanner : public StreamPanner void update (); }; -class Panner : public std::vector, public PBD::Stateful, public sigc::trackable + +class Panner : public Processor { public: struct Output { @@ -211,37 +201,43 @@ class Panner : public std::vector, public PBD::Stateful, public s }; + //Panner (std::string name, Session&, int _num_bufs); Panner (string name, Session&); virtual ~Panner (); + void clear_panners (); + + /// The fundamental Panner function - void distribute(BufferSet& src, BufferSet& dest, nframes_t start_frame, nframes_t end_frames, nframes_t nframes, nframes_t offset); - - bool bypassed() const { return _bypassed; } - void set_bypassed (bool yn); - - StreamPanner* add (); - void remove (uint32_t which); - void clear (); - void reset (uint32_t noutputs, uint32_t npans); - - void snapshot (nframes_t now); - void transport_stopped (nframes_t frame); - - void clear_automation (); - void set_automation_state (AutoState); AutoState automation_state() const; void set_automation_style (AutoStyle); AutoStyle automation_style() const; bool touching() const; + bool is_in_place () const { return false; } + bool is_out_of_place () const { return true; } + bool can_support_io_configuration (const ChanCount& in, ChanCount& out) const { return true; }; + + void run_out_of_place(BufferSet& src, BufferSet& dest, nframes_t start_frame, nframes_t end_frames, nframes_t nframes, nframes_t offset); + + //void* get_inline_gui() const = 0; + //void* get_full_gui() const = 0; + + bool bypassed() const { return _bypassed; } + void set_bypassed (bool yn); + + StreamPanner* add (); + void remove (uint32_t which); + void reset (uint32_t noutputs, uint32_t npans); + + + + XMLNode& get_state (void); XMLNode& state (bool full); int set_state (const XMLNode&); - sigc::signal Changed; - static bool equivalent (pan_t a, pan_t b) { return fabsf (a - b) < 0.002; // about 1 degree of arc for a stereo panner } @@ -251,7 +247,6 @@ class Panner : public std::vector, public PBD::Stateful, public s Output& output (uint32_t n) { return outputs[n]; } std::vector outputs; - Session& session() const { return _session; } enum LinkDirection { SameDirection, @@ -264,6 +259,10 @@ class Panner : public std::vector, public PBD::Stateful, public s bool linked() const { return _linked; } void set_linked (bool yn); + StreamPanner &streampanner( uint32_t n ) const { assert( n < _streampanners.size() ); return *_streampanners[n]; } + uint32_t npanners() const { return _streampanners.size(); } + + sigc::signal Changed; sigc::signal LinkStateChanged; sigc::signal StateChanged; /* for bypass */ @@ -277,10 +276,31 @@ class Panner : public std::vector, public PBD::Stateful, public s int load (); + struct PanControllable : public AutomationControl { + PanControllable (Session& s, std::string name, Panner& p, Evoral::Parameter param) + : AutomationControl (s, param, + boost::shared_ptr(new AutomationList(param)), name) + , panner (p) + { assert(param.type() != NullAutomation); } + + AutomationList* alist() { return (AutomationList*)_list.get(); } + Panner& panner; + + void set_value (float); + float get_value (void) const; + }; + + boost::shared_ptr pan_control ( int id, int chan=0 ) { + return boost::dynamic_pointer_cast( control( Evoral::Parameter (PanAutomation, chan, id) )); + } + + boost::shared_ptr pan_control ( int id, int chan=0 ) const { + return boost::dynamic_pointer_cast( control( Evoral::Parameter (PanAutomation, chan, id) )); + } + private: void distribute_no_automation(BufferSet& src, BufferSet& dest, nframes_t nframes, nframes_t offset, gain_t gain_coeff); - - Session& _session; + std::vector _streampanners; uint32_t current_outs; bool _linked; bool _bypassed; @@ -291,7 +311,6 @@ class Panner : public std::vector, public PBD::Stateful, public s /* old school automation handling */ std::string automation_path; - void set_name (std::string); }; } // namespace ARDOUR diff --git a/libs/ardour/automatable.cc b/libs/ardour/automatable.cc index a4d0de0958..c145c3c501 100644 --- a/libs/ardour/automatable.cc +++ b/libs/ardour/automatable.cc @@ -29,6 +29,7 @@ #include #include #include +#include #include "i18n.h" @@ -67,7 +68,7 @@ Automatable::old_set_automation_state (const XMLNode& node) if (sstr.fail()) { break; } - mark_automation_visible (Evoral::Parameter(PluginAutomation, what), true); + mark_automation_visible (Evoral::Parameter(PluginAutomation, 0, what), true); } } @@ -110,7 +111,7 @@ Automatable::load_automation (const string& path) in >> when; if (!in) goto bad; in >> value; if (!in) goto bad; - Evoral::Parameter param(PluginAutomation, port); + Evoral::Parameter param(PluginAutomation, 0, port); /* FIXME: this is legacy and only used for plugin inserts? I think? */ boost::shared_ptr c = control (param, true); c->list()->add (when, value); @@ -234,10 +235,14 @@ Automatable::set_automation_state (const XMLNode& node, Evoral::Parameter legacy } boost::shared_ptr existing = control(param); - if (existing) + if (existing) { existing->set_list(al); - else - add_control(control_factory(param)); + } else { + boost::shared_ptr newcontrol = control_factory(param); + add_control(newcontrol); + newcontrol->set_list(al); + warning << "Control did not exist"; + } } else { error << "Expected AutomationList node, got '" << (*niter)->name() << endmsg; @@ -401,6 +406,10 @@ Automatable::control_factory(const Evoral::Parameter& param) control = new MidiTrack::MidiControl((MidiTrack*)this, param); } else if (param.type() == PluginAutomation) { control = new PluginInsert::PluginControl((PluginInsert*)this, param); + } else if (param.type() == GainAutomation) { + control = new IO::GainControl( X_("gaincontrol"), (IO*)this, param); + } else if (param.type() == PanAutomation) { + control = new Panner::PanControllable( ((Panner *)this)->session(), X_("panner"), *(Panner *)this, param); } else { control = new AutomationControl(_a_session, param); } diff --git a/libs/ardour/io.cc b/libs/ardour/io.cc index 0343545936..b994b17358 100644 --- a/libs/ardour/io.cc +++ b/libs/ardour/io.cc @@ -140,8 +140,7 @@ IO::IO (Session& s, const string& name, boost::shared_ptr gl( new AutomationList(Evoral::Parameter(GainAutomation))); - _gain_control = boost::shared_ptr( - new GainControl(X_("gaincontrol"), *this, gl)); + _gain_control = boost::shared_ptr( new GainControl( X_("gaincontrol"), this, Evoral::Parameter(GainAutomation), gl )); add_control(_gain_control); @@ -183,7 +182,7 @@ IO::IO (Session& s, const XMLNode& node, DataType dt) new AutomationList(Evoral::Parameter(GainAutomation))); _gain_control = boost::shared_ptr( - new GainControl(X_("gaincontrol"), *this, gl)); + new GainControl( X_("gaincontrol"), this, Evoral::Parameter(GainAutomation), gl)); add_control(_gain_control); @@ -265,10 +264,10 @@ IO::deliver_output (BufferSet& bufs, nframes_t start_frame, nframes_t end_frame, if (dg != _gain || dg != 1.0) Amp::run_in_place(bufs, nframes, _gain, dg, _phase_invert); } - + // Use the panner to distribute audio to output port buffers - if (_panner && !_panner->empty() && !_panner->bypassed()) { - _panner->distribute (bufs, output_buffers(), start_frame, end_frame, nframes, offset); + if( _panner && _panner->npanners() && !_panner->bypassed()) { + _panner->run_out_of_place(bufs, output_buffers(), start_frame, end_frame, nframes, offset); } else { const DataType type = DataType::AUDIO; @@ -1402,6 +1401,7 @@ IO::set_state (const XMLNode& node) for (iter = node.children().begin(); iter != node.children().end(); ++iter) { + // Old school Panner. if ((*iter)->name() == "Panner") { if (_panner == 0) { _panner = new Panner (_name, _session); @@ -1409,6 +1409,15 @@ IO::set_state (const XMLNode& node) _panner->set_state (**iter); } + if ((*iter)->name() == "Processor") { + if ((*iter)->property ("type") && ((*iter)->property ("type")->value() == "panner" ) ) { + if (_panner == 0) { + _panner = new Panner (_name, _session); + } + _panner->set_state (**iter); + } + } + if ((*iter)->name() == X_("Automation")) { set_automation_state (*(*iter), Evoral::Parameter(GainAutomation)); @@ -1432,6 +1441,8 @@ IO::set_state (const XMLNode& node) port_legal_c = PortsLegal.connect (mem_fun (*this, &IO::ports_became_legal)); } + if( !_panner ) + _panner = new Panner( _name, _session ); if (panners_legal) { reset_panner (); } else { @@ -2223,10 +2234,9 @@ IO::GainControl::set_value (float val) if (val > 1.99526231f) val = 1.99526231f; - _user_value = val; - _io.set_gain (val, this); + _io->set_gain (val, this); - Changed(); /* EMIT SIGNAL */ + AutomationControl::set_value(val); } float @@ -2270,7 +2280,7 @@ void IO::clear_automation () { data().clear (); // clears gain automation - _panner->clear_automation (); + _panner->data().clear(); } void @@ -2341,10 +2351,12 @@ IO::set_gain (gain_t val, void *src) _gain = val; } + /* if (_session.transport_stopped() && src != 0 && src != this && _gain_control->automation_write()) { _gain_control->list()->add (_session.transport_frame(), val); } + */ _session.set_dirty(); } @@ -2352,16 +2364,16 @@ IO::set_gain (gain_t val, void *src) void IO::start_pan_touch (uint32_t which) { - if (which < _panner->size()) { - (*_panner)[which]->pan_control()->start_touch(); + if (which < _panner->npanners()) { + (*_panner).pan_control(which)->start_touch(); } } void IO::end_pan_touch (uint32_t which) { - if (which < _panner->size()) { - (*_panner)[which]->pan_control()->stop_touch(); + if (which < _panner->npanners()) { + (*_panner).pan_control(which)->stop_touch(); } } @@ -2370,12 +2382,17 @@ void IO::automation_snapshot (nframes_t now, bool force) { AutomatableControls::automation_snapshot (now, force); + // XXX: This seems to be wrong. + // drobilla: shouldnt automation_snapshot for panner be called + // "automagically" because its an Automatable now ? + // + // we could dump this whole method then. <3 if (_last_automation_snapshot > now || (now - _last_automation_snapshot) > _automation_interval) { - _panner->snapshot (now); + _panner->automation_snapshot (now, force); } - _panner->snapshot (now); + _panner->automation_snapshot (now, force); _last_automation_snapshot = now; } diff --git a/libs/ardour/ladspa_plugin.cc b/libs/ardour/ladspa_plugin.cc index d756bbfcbe..66c6be871b 100644 --- a/libs/ardour/ladspa_plugin.cc +++ b/libs/ardour/ladspa_plugin.cc @@ -297,7 +297,7 @@ LadspaPlugin::set_parameter (uint32_t which, float val) if (which < _descriptor->PortCount) { _shadow_data[which] = (LADSPA_Data) val; #if 0 - ParameterChanged (Parameter(PluginAutomation, which), val); /* EMIT SIGNAL */ + ParameterChanged (Parameter(PluginAutomation, 0, which), val); /* EMIT SIGNAL */ if (which < parameter_count() && controls[which]) { controls[which]->Changed (); @@ -503,7 +503,7 @@ LadspaPlugin::automatable () const if (LADSPA_IS_PORT_INPUT(port_descriptor (i)) && LADSPA_IS_PORT_CONTROL(port_descriptor (i))){ - ret.insert (ret.end(), Evoral::Parameter(PluginAutomation, i)); + ret.insert (ret.end(), Evoral::Parameter(PluginAutomation, 0, i)); } } diff --git a/libs/ardour/lv2_plugin.cc b/libs/ardour/lv2_plugin.cc index 231d267468..0260e02560 100644 --- a/libs/ardour/lv2_plugin.cc +++ b/libs/ardour/lv2_plugin.cc @@ -390,7 +390,7 @@ LV2Plugin::automatable () const for (uint32_t i = 0; i < parameter_count(); ++i){ if (parameter_is_input(i) && parameter_is_control(i)) { - ret.insert (ret.end(), Evoral::Parameter(PluginAutomation, i)); + ret.insert (ret.end(), Evoral::Parameter(PluginAutomation, 0, i)); } } diff --git a/libs/ardour/panner.cc b/libs/ardour/panner.cc index 3ff4caf4f3..c616a6fe00 100644 --- a/libs/ardour/panner.cc +++ b/libs/ardour/panner.cc @@ -65,19 +65,19 @@ static pan_t direct_control_to_pan (double fract) { return fract; } -static double direct_pan_to_control (pan_t val) { - return val; -} + +//static double direct_pan_to_control (pan_t val) { +// return val; +//} StreamPanner::StreamPanner (Panner& p, Evoral::Parameter param) : parent (p) - , _control (new PanControllable(p.session(), X_("panner"), *this, param)) { assert(param.type() != NullAutomation); _muted = false; - parent.session().add_controllable (_control); + _control = boost::dynamic_pointer_cast( parent.control( param, true ) ); x = 0.5; y = 0.5; @@ -89,17 +89,16 @@ StreamPanner::~StreamPanner () } void -StreamPanner::PanControllable::set_value (float val) +Panner::PanControllable::set_value (float val) { - panner.set_position (direct_control_to_pan (val)); + panner.streampanner(parameter().id()).set_position (direct_control_to_pan (val)); + AutomationControl::set_value(val); } float -StreamPanner::PanControllable::get_value (void) const +Panner::PanControllable::get_value (void) const { - float xpos; - panner.get_effective_position (xpos); - return direct_pan_to_control (xpos); + return AutomationControl::get_value(); } void @@ -376,7 +375,7 @@ EqualPowerStereoPanner::update () desired_right = panR * (scale * panR + 1.0f - scale); effective_x = x; - _control->set_value(x); + //_control->set_value(x); } void @@ -404,7 +403,6 @@ EqualPowerStereoPanner::distribute_automated (AudioBuffer& srcbuf, BufferSet& ob if (nframes > 0) { effective_x = buffers[0][nframes-1]; - _control->set_value(effective_x); // signal, update UI } if (_muted) { @@ -473,9 +471,7 @@ EqualPowerStereoPanner::state (bool full_state) root->add_property (X_("x"), buf); root->add_property (X_("type"), EqualPowerStereoPanner::name); - XMLNode* autonode = new XMLNode (X_("Automation")); - autonode->add_child_nocopy (((AutomationList*)_control->list().get())->state (full_state)); - root->add_child_nocopy (*autonode); + // XXX: dont save automation here... its part of the automatable panner now. StreamPanner::add_state (*root); @@ -560,7 +556,6 @@ Multi2dPanner::update () } effective_x = x; - _control->set_value(x); } void @@ -704,8 +699,9 @@ Multi2dPanner::set_state (const XMLNode& node) /*---------------------------------------------------------------------- */ Panner::Panner (string name, Session& s) - : _session (s) + : Processor(s, name, PostFader) { + //set_name_old_auto (name); set_name (name); _linked = false; @@ -737,6 +733,7 @@ Panner::set_link_direction (LinkDirection ld) } } + void Panner::set_bypassed (bool yn) { @@ -753,12 +750,14 @@ Panner::reset (uint32_t nouts, uint32_t npans) uint32_t n; bool changed = false; - if (nouts < 2 || (nouts == outputs.size() && npans == size())) { + //configure_io( ChanCount( DataType::AUDIO, nout ), ChanCount( DataType::AUDIO, nin ) ) + + if (nouts < 2 || (nouts == outputs.size() && npans == _streampanners.size())) { return; } - n = size(); - clear (); + n = _streampanners.size(); + clear_panners (); if (n != npans) { changed = true; @@ -788,7 +787,7 @@ Panner::reset (uint32_t nouts, uint32_t npans) outputs.push_back (Output (1.0, 0)); for (n = 0; n < npans; ++n) { - push_back (new EqualPowerStereoPanner (*this, Evoral::Parameter(PanAutomation, n))); + _streampanners.push_back (new EqualPowerStereoPanner (*this, Evoral::Parameter(PanAutomation, 0, n))); } break; @@ -798,7 +797,7 @@ Panner::reset (uint32_t nouts, uint32_t npans) outputs.push_back (Output (1.0, 1.0)); for (n = 0; n < npans; ++n) { - push_back (new Multi2dPanner (*this, Evoral::Parameter(PanAutomation, n))); + _streampanners.push_back (new Multi2dPanner (*this, Evoral::Parameter(PanAutomation, 0, n))); } break; @@ -810,7 +809,7 @@ Panner::reset (uint32_t nouts, uint32_t npans) outputs.push_back (Output (0, 1.0)); for (n = 0; n < npans; ++n) { - push_back (new Multi2dPanner (*this, Evoral::Parameter(PanAutomation, n))); + _streampanners.push_back (new Multi2dPanner (*this, Evoral::Parameter(PanAutomation, 0, n))); } break; @@ -823,7 +822,7 @@ Panner::reset (uint32_t nouts, uint32_t npans) outputs.push_back (Output (0.5, 0.75)); for (n = 0; n < npans; ++n) { - push_back (new Multi2dPanner (*this, Evoral::Parameter(PanAutomation, n))); + _streampanners.push_back (new Multi2dPanner (*this, Evoral::Parameter(PanAutomation, 0, n))); } break; @@ -835,13 +834,13 @@ Panner::reset (uint32_t nouts, uint32_t npans) } for (n = 0; n < npans; ++n) { - push_back (new Multi2dPanner (*this, Evoral::Parameter(PanAutomation, n))); + _streampanners.push_back (new Multi2dPanner (*this, Evoral::Parameter(PanAutomation, 0, n))); } break; } - for (iterator x = begin(); x != end(); ++x) { + for (std::vector::iterator x = _streampanners.begin(); x != _streampanners.end(); ++x) { (*x)->update (); } @@ -857,16 +856,16 @@ Panner::reset (uint32_t nouts, uint32_t npans) float left; float right; - front()->get_position (left); - back()->get_position (right); + _streampanners.front()->get_position (left); + _streampanners.back()->get_position (right); if (changed || ((left == 0.5) && (right == 0.5))) { - front()->set_position (0.0); - front()->pan_control()->list()->reset_default (0.0); + _streampanners.front()->set_position (0.0); + _streampanners.front()->pan_control()->list()->reset_default (0.0); - back()->set_position (1.0); - back()->pan_control()->list()->reset_default (1.0); + _streampanners.back()->set_position (1.0); + _streampanners.back()->pan_control()->list()->reset_default (1.0); changed = true; } @@ -883,28 +882,28 @@ void Panner::remove (uint32_t which) { vector::iterator i; - for (i = begin(); i != end() && which; ++i, --which); + for (i = _streampanners.begin(); i != _streampanners.end() && which; ++i, --which); - if (i != end()) { + if (i != _streampanners.end()) { delete *i; - erase (i); + _streampanners.erase (i); } } void -Panner::clear () +Panner::clear_panners () { - for (vector::iterator i = begin(); i != end(); ++i) { + for (vector::iterator i = _streampanners.begin(); i != _streampanners.end(); ++i) { delete *i; } - vector::clear (); + _streampanners.clear (); } void Panner::set_automation_style (AutoStyle style) { - for (vector::iterator i = begin(); i != end(); ++i) { + for (vector::iterator i = _streampanners.begin(); i != _streampanners.end(); ++i) { ((AutomationList*)(*i)->pan_control()->list().get())->set_automation_style (style); } _session.set_dirty (); @@ -913,7 +912,7 @@ Panner::set_automation_style (AutoStyle style) void Panner::set_automation_state (AutoState state) { - for (vector::iterator i = begin(); i != end(); ++i) { + for (vector::iterator i = _streampanners.begin(); i != _streampanners.end(); ++i) { ((AutomationList*)(*i)->pan_control()->list().get())->set_automation_state (state); } _session.set_dirty (); @@ -923,7 +922,7 @@ AutoState Panner::automation_state () const { if (!empty()) { - return ((AutomationList*)front()->pan_control()->list().get())->automation_state (); + return ((AutomationList*)_streampanners.front()->pan_control()->list().get())->automation_state (); } else { return Off; } @@ -933,38 +932,12 @@ AutoStyle Panner::automation_style () const { if (!empty()) { - return ((AutomationList*)front()->pan_control()->list().get())->automation_style (); + return ((AutomationList*)_streampanners.front()->pan_control()->list().get())->automation_style (); } else { return Absolute; } } -void -Panner::transport_stopped (nframes_t frame) -{ - for (vector::iterator i = begin(); i != end(); ++i) { - ((AutomationList*)(*i)->pan_control()->list().get())->reposition_for_rt_add (frame); - } -} - -void -Panner::snapshot (nframes_t now) -{ - for (vector::iterator i = begin(); i != end(); ++i) { - AutomationList* list = ((AutomationList*)(*i)->pan_control()->list().get()); - if (list->automation_write()) - list->rt_add(now, (*i)->pan_control()->get_value()); - } -} - -void -Panner::clear_automation () -{ - for (vector::iterator i = begin(); i != end(); ++i) { - (*i)->pan_control()->list()->clear (); - } - _session.set_dirty (); -} struct PanPlugins { string name; @@ -987,13 +960,20 @@ Panner::get_state (void) XMLNode& Panner::state (bool full) { - XMLNode* root = new XMLNode (X_("Panner")); + XMLNode& node = Processor::state(full); + + node.add_property ("type", "panner"); + char buf[32]; - root->add_property (X_("linked"), (_linked ? "yes" : "no")); - root->add_property (X_("link_direction"), enum_2_string (_link_direction)); - root->add_property (X_("bypassed"), (bypassed() ? "yes" : "no")); + node.add_property (X_("linked"), (_linked ? "yes" : "no")); + node.add_property (X_("link_direction"), enum_2_string (_link_direction)); + node.add_property (X_("bypassed"), (bypassed() ? "yes" : "no")); + snprintf (buf, sizeof (buf), "%d", _streampanners.size()); + node.add_property (X_("ins"), buf); + snprintf (buf, sizeof (buf), "%d", outputs.size()); + node.add_property (X_("outs"), buf); /* add each output */ for (vector::iterator o = outputs.begin(); o != outputs.end(); ++o) { @@ -1002,14 +982,15 @@ Panner::state (bool full) onode->add_property (X_("x"), buf); snprintf (buf, sizeof (buf), "%.12g", (*o).y); onode->add_property (X_("y"), buf); - root->add_child_nocopy (*onode); + node.add_child_nocopy (*onode); } - for (vector::const_iterator i = begin(); i != end(); ++i) { - root->add_child_nocopy ((*i)->state (full)); + for (vector::const_iterator i = _streampanners.begin(); i != _streampanners.end(); ++i) { + node.add_child_nocopy ((*i)->state (full)); } - return *root; + + return node; } int @@ -1022,7 +1003,14 @@ Panner::set_state (const XMLNode& node) StreamPanner* sp; LocaleGuard lg (X_("POSIX")); - clear (); + clear_panners (); + + Processor::set_state(node); + + ChanCount ins = ChanCount::ZERO; + ChanCount outs = ChanCount::ZERO; + + // XXX: this might not be necessary anymore outputs.clear (); if ((prop = node.property (X_("linked"))) != 0) { @@ -1033,6 +1021,15 @@ Panner::set_state (const XMLNode& node) if ((prop = node.property (X_("bypassed"))) != 0) { set_bypassed (prop->value() == "yes"); } + + if ((prop = node.property (X_("ins"))) != 0) { + ins.set_audio(atoi(prop->value().c_str())); + } + + if ((prop = node.property (X_("outs"))) != 0) { + outs.set_audio(atoi(prop->value().c_str())); + } + if ((prop = node.property (X_("link_direction"))) != 0) { LinkDirection ld; /* here to provide type information */ @@ -1071,10 +1068,10 @@ Panner::set_state (const XMLNode& node) assumption, but its still an assumption. */ - sp = pan_plugins[i].factory (*this, Evoral::Parameter(PanAutomation, 0)); + sp = pan_plugins[i].factory (*this, Evoral::Parameter(PanAutomation, 0, i)); if (sp->set_state (**niter) == 0) { - push_back (sp); + _streampanners.push_back (sp); } break; @@ -1097,6 +1094,7 @@ Panner::set_state (const XMLNode& node) } } + reset(ins.n_audio(), outs.n_audio()); /* don't try to do old-school automation loading if it wasn't marked as existing */ if ((prop = node.property (X_("automation")))) { @@ -1109,12 +1107,10 @@ Panner::set_state (const XMLNode& node) return 0; } - - bool Panner::touching () const { - for (vector::const_iterator i = begin(); i != end(); ++i) { + for (vector::const_iterator i = _streampanners.begin(); i != _streampanners.end(); ++i) { if (((AutomationList*)(*i)->pan_control()->list().get())->touching ()) { return true; } @@ -1135,7 +1131,7 @@ Panner::set_position (float xpos, StreamPanner& orig) if (_link_direction == SameDirection) { - for (vector::iterator i = begin(); i != end(); ++i) { + for (vector::iterator i = _streampanners.begin(); i != _streampanners.end(); ++i) { if (*i == &orig) { (*i)->set_position (xpos, true); } else { @@ -1148,7 +1144,7 @@ Panner::set_position (float xpos, StreamPanner& orig) } else { - for (vector::iterator i = begin(); i != end(); ++i) { + for (vector::iterator i = _streampanners.begin(); i != _streampanners.end(); ++i) { if (*i == &orig) { (*i)->set_position (xpos, true); } else { @@ -1174,7 +1170,7 @@ Panner::set_position (float xpos, float ypos, StreamPanner& orig) if (_link_direction == SameDirection) { - for (vector::iterator i = begin(); i != end(); ++i) { + for (vector::iterator i = _streampanners.begin(); i != _streampanners.end(); ++i) { if (*i == &orig) { (*i)->set_position (xpos, ypos, true); } else { @@ -1192,7 +1188,7 @@ Panner::set_position (float xpos, float ypos, StreamPanner& orig) } else { - for (vector::iterator i = begin(); i != end(); ++i) { + for (vector::iterator i = _streampanners.begin(); i != _streampanners.end(); ++i) { if (*i == &orig) { (*i)->set_position (xpos, ypos, true); } else { @@ -1224,7 +1220,7 @@ Panner::set_position (float xpos, float ypos, float zpos, StreamPanner& orig) if (_link_direction == SameDirection) { - for (vector::iterator i = begin(); i != end(); ++i) { + for (vector::iterator i = _streampanners.begin(); i != _streampanners.end(); ++i) { if (*i == &orig) { (*i)->set_position (xpos, ypos, zpos, true); } else { @@ -1245,7 +1241,7 @@ Panner::set_position (float xpos, float ypos, float zpos, StreamPanner& orig) } else { - for (vector::iterator i = begin(); i != end(); ++i) { + for (vector::iterator i = _streampanners.begin(); i != _streampanners.end(); ++i) { if (*i == &orig) { (*i)->set_position (xpos, ypos, true); } else { @@ -1332,13 +1328,13 @@ Panner::distribute_no_automation (BufferSet& inbufs, BufferSet& outbufs, nframes BufferSet::audio_iterator i = inbufs.audio_begin(); - for (iterator pan = begin(); pan != end() && i != inbufs.audio_end(); ++pan, ++i) { + for (vector::iterator pan = _streampanners.begin(); pan != _streampanners.end() && i != inbufs.audio_end(); ++pan, ++i) { (*pan)->distribute (*i, outbufs, gain_coeff, nframes); } } void -Panner::distribute (BufferSet& inbufs, BufferSet& outbufs, nframes_t start_frame, nframes_t end_frame, nframes_t nframes, nframes_t offset) +Panner::run_out_of_place (BufferSet& inbufs, BufferSet& outbufs, nframes_t start_frame, nframes_t end_frame, nframes_t nframes, nframes_t offset) { if (outbufs.count().n_audio() == 0) { // Failing to deliver audio we were asked to deliver is a bug @@ -1385,7 +1381,7 @@ Panner::distribute (BufferSet& inbufs, BufferSet& outbufs, nframes_t start_frame } // More than 1 output, we should have 1 panner for each input - assert(size() == inbufs.count().n_audio()); + //assert(_streampanners.size() == inbufs.count().n_audio()); /* the terrible silence ... */ for (BufferSet::audio_iterator i = outbufs.audio_begin(); i != outbufs.audio_end(); ++i) { @@ -1393,19 +1389,21 @@ Panner::distribute (BufferSet& inbufs, BufferSet& outbufs, nframes_t start_frame } BufferSet::audio_iterator i = inbufs.audio_begin(); - for (iterator pan = begin(); pan != end(); ++pan, ++i) { + for (vector::iterator pan = _streampanners.begin(); pan != _streampanners.end(); ++pan, ++i) { (*pan)->distribute_automated (*i, outbufs, start_frame, end_frame, nframes, _session.pan_automation_buffer()); } } /* old school automation handling */ +/* void Panner::set_name (string str) { automation_path = Glib::build_filename(_session.automation_dir(), _session.snap_name() + "-pan-" + legalize_for_path (str) + ".automation"); } +*/ int Panner::load () @@ -1413,7 +1411,7 @@ Panner::load () char line[128]; uint32_t linecnt = 0; float version; - iterator sp; + vector::iterator sp; LocaleGuard lg (X_("POSIX")); if (automation_path.length() == 0) { @@ -1433,7 +1431,7 @@ Panner::load () return -1; } - sp = begin(); + sp = _streampanners.begin(); while (in.getline (line, sizeof(line), '\n')) { @@ -1458,7 +1456,7 @@ Panner::load () if (strcmp (line, "begin") == 0) { - if (sp == end()) { + if (sp == _streampanners.end()) { error << string_compose (_("too many panner states found in pan automation file %1"), automation_path) << endmsg; diff --git a/libs/ardour/plugin_insert.cc b/libs/ardour/plugin_insert.cc index 97ce2d7c01..eede6b3388 100644 --- a/libs/ardour/plugin_insert.cc +++ b/libs/ardour/plugin_insert.cc @@ -80,7 +80,10 @@ PluginInsert::PluginInsert (Session& s, const XMLNode& node) throw failed_constructor(); } - set_automatable (); + // XXX: This would dump all automation, which has already been loaded by + // Processor. But this could also have been related to the Parameter change.. + // will look into this later. + //set_automatable (); { Glib::Mutex::Lock em (_session.engine().process_lock()); @@ -630,7 +633,7 @@ PluginInsert::state (bool full) node.add_child_nocopy (_plugins[0]->get_state()); /* add port automation state */ - XMLNode *autonode = new XMLNode(port_automation_node_name); + //XMLNode *autonode = new XMLNode(port_automation_node_name); set automatable = _plugins[0]->automatable(); for (set::iterator x = automatable.begin(); x != automatable.end(); ++x) { @@ -642,10 +645,10 @@ PluginInsert::state (bool full) child->add_child_nocopy (automation_list (*x).state (full)); autonode->add_child_nocopy (*child); */ - autonode->add_child_nocopy (((AutomationList*)data().control(*x)->list().get())->state (full)); + //autonode->add_child_nocopy (((AutomationList*)data().control(*x)->list().get())->state (full)); } - node.add_child_nocopy (*autonode); + //node.add_child_nocopy (*autonode); return node; } @@ -766,7 +769,7 @@ PluginInsert::set_state(const XMLNode& node) } boost::shared_ptr c = boost::dynamic_pointer_cast( - data().control(Evoral::Parameter(PluginAutomation, port_id), true)); + data().control(Evoral::Parameter(PluginAutomation, 0, port_id), true)); if (!child->children().empty()) { c->alist()->set_state (*child->children().front()); diff --git a/libs/ardour/processor.cc b/libs/ardour/processor.cc index 5fbb30810d..a541ae0423 100644 --- a/libs/ardour/processor.cc +++ b/libs/ardour/processor.cc @@ -214,7 +214,7 @@ Processor::set_state (const XMLNode& node) break; } // FIXME: other automation types? - mark_automation_visible (Evoral::Parameter(PluginAutomation, what), true); + mark_automation_visible (Evoral::Parameter(PluginAutomation, 0, what), true); } } diff --git a/libs/ardour/route.cc b/libs/ardour/route.cc index 740e5949b4..78d4a85e61 100644 --- a/libs/ardour/route.cc +++ b/libs/ardour/route.cc @@ -1046,7 +1046,9 @@ Route::passthru (nframes_t start_frame, nframes_t end_frame, nframes_t nframes, if (meter_first) { _meter->run_in_place(bufs, start_frame, end_frame, nframes, offset); meter_first = false; - } + } else { + meter_first = true; + } process_output_buffers (bufs, start_frame, end_frame, nframes, offset, true, declick, meter_first); } @@ -1163,6 +1165,7 @@ Route::add_processor (boost::shared_ptr processor, ProcessorStreams* _meter->configure_io (potential_max_streams, potential_max_streams); + // XXX: do we want to emit the signal here ? change call order. processor->activate (); processor->ActiveChanged.connect (bind (mem_fun (_session, &Session::update_latency_compensation), false, false)); @@ -2830,7 +2833,7 @@ Route::roll (nframes_t nframes, nframes_t start_frame, nframes_t end_frame, nfra if (am.locked() && _session.transport_rolling()) { - if (_gain_control->alist()->automation_playback()) { + if (_gain_control->automation_playback()) { apply_gain_automation = _gain_control->list()->curve().rt_safe_get_vector ( start_frame, end_frame, _session.gain_automation_buffer(), nframes); } diff --git a/libs/surfaces/mackie/mackie_control_protocol.cc b/libs/surfaces/mackie/mackie_control_protocol.cc index 4558dc641c..502cea581f 100644 --- a/libs/surfaces/mackie/mackie_control_protocol.cc +++ b/libs/surfaces/mackie/mackie_control_protocol.cc @@ -862,11 +862,10 @@ void MackieControlProtocol::handle_control_event( SurfacePort & port, Control & { if ( route != 0 ) { - if ( route->panner().size() == 1 ) + if ( route->panner().npanners() == 1 ) { // assume pan for now - float xpos; - route->panner()[0]->get_effective_position (xpos); + float xpos = route->panner().pan_control(0)->get_value (); // calculate new value, and trim xpos += state.delta; @@ -875,7 +874,7 @@ void MackieControlProtocol::handle_control_event( SurfacePort & port, Control & else if ( xpos < 0.0 ) xpos = 0.0; - route->panner()[0]->set_position( xpos ); + route->panner().pan_control(0)->set_value( xpos ); } } else @@ -999,10 +998,9 @@ void MackieControlProtocol::notify_panner_changed( RouteSignal * route_signal ) { Pot & pot = route_signal->strip().vpot(); - if ( route_signal->route().panner().size() == 1 ) + if ( route_signal->route().panner().npanners() == 1 ) { - float pos; - route_signal->route().panner()[0]->get_effective_position( pos); + float pos = route_signal->route().panner().pan_control(0)->get_value(); route_signal->port().write( builder.build_led_ring( pot, ControlState( on, pos ) ) ); } else diff --git a/libs/surfaces/mackie/route_signal.cc b/libs/surfaces/mackie/route_signal.cc index adaeadd805..c21d326fef 100644 --- a/libs/surfaces/mackie/route_signal.cc +++ b/libs/surfaces/mackie/route_signal.cc @@ -41,9 +41,9 @@ void RouteSignal::connect() _name_changed_connection = _route.NameChanged.connect( sigc::bind ( mem_fun ( _mcp, &MackieControlProtocol::notify_name_changed ), this ) ); - if ( _route.panner().size() == 1 ) + if ( _route.panner().npanners() == 1 ) { - _panner_changed_connection = _route.panner()[0]->Changed.connect( sigc::bind ( mem_fun ( _mcp, &MackieControlProtocol::notify_panner_changed ), this ) ); + _panner_changed_connection = _route.panner().pan_control(0)->Changed.connect( sigc::bind ( mem_fun ( _mcp, &MackieControlProtocol::notify_panner_changed ), this ) ); } try