X-Git-Url: https://main.carlh.net/gitweb/?a=blobdiff_plain;f=libs%2Fardour%2Fpanner_shell.cc;h=d99e5068c155a2cce7ed036df31400236bf99d8c;hb=9bf40bde3aed831791108bfccc4b1e10b071afdc;hp=e60012d8b45e144eb4d5b1b25febde03c0c84472;hpb=11b0ac77f5c3b1e5bd2a96218d05edbe12a740db;p=ardour.git diff --git a/libs/ardour/panner_shell.cc b/libs/ardour/panner_shell.cc index e60012d8b4..d99e5068c1 100644 --- a/libs/ardour/panner_shell.cc +++ b/libs/ardour/panner_shell.cc @@ -21,7 +21,6 @@ #include #include -#include #include #include #include @@ -33,7 +32,6 @@ #include #include "pbd/cartesian.h" -#include "pbd/boost_debug.h" #include "pbd/convert.h" #include "pbd/error.h" #include "pbd/failed_constructor.h" @@ -43,19 +41,19 @@ #include "evoral/Curve.hpp" #include "ardour/audio_buffer.h" -#include "ardour/audio_buffer.h" -#include "ardour/automatable.h" +#include "ardour/audioengine.h" +#include "ardour/boost_debug.h" #include "ardour/buffer_set.h" #include "ardour/debug.h" #include "ardour/pannable.h" #include "ardour/panner.h" #include "ardour/panner_manager.h" #include "ardour/panner_shell.h" -#include "ardour/runtime_functions.h" +#include "ardour/profile.h" #include "ardour/session.h" -#include "ardour/utils.h" +#include "ardour/speakers.h" -#include "i18n.h" +#include "pbd/i18n.h" #include "pbd/mathfix.h" @@ -63,16 +61,31 @@ using namespace std; using namespace ARDOUR; using namespace PBD; -PannerShell::PannerShell (string name, Session& s, boost::shared_ptr p) +PannerShell::PannerShell (string name, Session& s, boost::shared_ptr p, bool is_send) : SessionObject (s, name) - , _pannable (p) + , _pannable_route (p) + , _is_send (is_send) + , _panlinked (true) + , _bypassed (false) + , _current_panner_uri("") + , _user_selected_panner_uri("") + , _panner_gui_uri("") + , _force_reselect (false) { + if (is_send) { + _pannable_internal.reset(new Pannable (s)); + if (Config->get_link_send_and_route_panner() && !ARDOUR::Profile->get_mixbus()) { + _panlinked = true; + } else { + _panlinked = false; + } + } set_name (name); } PannerShell::~PannerShell () { - DEBUG_TRACE(DEBUG::Destruction, string_compose ("panner shell for %1 destructor, pannable is %2\n", _name, _pannable)); + DEBUG_TRACE(DEBUG::Destruction, string_compose ("panner shell %3 for %1 destructor, panner is %4, pannable is %2\n", _name, _pannable_route, this, _panner)); } void @@ -85,25 +98,33 @@ PannerShell::configure_io (ChanCount in, ChanCount out) the config hasn't changed, we're done. */ - if (_panner && (_panner->in().n_audio() == nins) && (_panner->out().n_audio() == nouts)) { + if (!_force_reselect && _panner && (_panner->in().n_audio() == nins) && (_panner->out().n_audio() == nouts)) { return; } + _force_reselect = false; if (nouts < 2 || nins == 0) { /* no need for panning with less than 2 outputs or no inputs */ if (_panner) { _panner.reset (); + _current_panner_uri = ""; + _panner_gui_uri = ""; + if (!_is_send || !_panlinked) { + pannable()->set_panner(_panner); + } Changed (); /* EMIT SIGNAL */ } return; } - PannerInfo* pi = PannerManager::instance().select_panner (in, out); + PannerInfo* pi = PannerManager::instance().select_panner (in, out, _user_selected_panner_uri); if (!pi) { - cerr << "No panner found: check that panners are being discovered correctly during startup.\n"; - assert (pi); + fatal << _("No panner found: check that panners are being discovered correctly during startup.") << endmsg; + abort(); /*NOTREACHED*/ } + DEBUG_TRACE (DEBUG::Panning, string_compose (_("select panner: %1\n"), pi->descriptor.name.c_str())); + boost::shared_ptr speakers = _session.get_speakers (); if (nouts != speakers->size()) { @@ -115,27 +136,31 @@ PannerShell::configure_io (ChanCount in, ChanCount out) speakers.reset (s); } - Panner* p = pi->descriptor.factory (_pannable, speakers); - boost_debug_shared_ptr_mark_interesting (p, "Panner"); + /* TODO don't allow to link _is_send if internal & route panners are different types */ + Panner* p = pi->descriptor.factory (pannable(), speakers); + // boost_debug_shared_ptr_mark_interesting (p, "Panner"); _panner.reset (p); _panner->configure_io (in, out); + _current_panner_uri = pi->descriptor.panner_uri; + _panner_gui_uri = pi->descriptor.gui_uri; + if (!_is_send || !_panlinked) { + pannable()->set_panner(_panner); + } Changed (); /* EMIT SIGNAL */ } XMLNode& -PannerShell::get_state (void) -{ - return state (true); -} - -XMLNode& -PannerShell::state (bool full) +PannerShell::get_state () { XMLNode* node = new XMLNode ("PannerShell"); - if (_panner) { - node->add_child_nocopy (_panner->state (full)); + node->set_property (X_("bypassed"), _bypassed); + node->set_property (X_("user-panner"), _user_selected_panner_uri); + node->set_property (X_("linked-to-route"), _panlinked); + + if (_panner && _is_send) { + node->add_child_nocopy (_panner->get_state ()); } return *node; @@ -146,29 +171,76 @@ PannerShell::set_state (const XMLNode& node, int version) { XMLNodeList nlist = node.children (); XMLNodeConstIterator niter; - const XMLProperty *prop; - LocaleGuard lg (X_("POSIX")); + bool yn; + std::string str; + + if (node.get_property (X_("bypassed"), yn)) { + set_bypassed (yn); + } + + if (node.get_property (X_("linked-to-route"), yn)) { + if (!ARDOUR::Profile->get_mixbus()) { + _panlinked = yn; + } + } + + node.get_property (X_("user-panner"), _user_selected_panner_uri); _panner.reset (); for (niter = nlist.begin(); niter != nlist.end(); ++niter) { if ((*niter)->name() == X_("Panner")) { + if ((*niter)->get_property (X_("uri"), str)) { + PannerInfo* p = PannerManager::instance().get_by_uri(str); + if (p) { + _panner.reset (p->descriptor.factory ( + _is_send ? _pannable_internal : _pannable_route, _session.get_speakers ())); + _current_panner_uri = p->descriptor.panner_uri; + _panner_gui_uri = p->descriptor.gui_uri; + if (_is_send) { + if (!_panlinked) { + _pannable_internal->set_panner(_panner); + } else { + _force_reselect = true; + } + } else { + _pannable_route->set_panner(_panner); + } + if (_panner->set_state (**niter, version) == 0) { + return -1; + } + } + } - if ((prop = (*niter)->property (X_("type")))) { + else /* backwards compatibility */ + if ((*niter)->get_property (X_("type"), str)) { list::iterator p; PannerManager& pm (PannerManager::instance()); for (p = pm.panner_info.begin(); p != pm.panner_info.end(); ++p) { - if (prop->value() == (*p)->descriptor.name) { + if (str == (*p)->descriptor.name) { /* note that we assume that all the stream panners are of the same type. pretty good assumption, but it's still an assumption. */ - _panner.reset ((*p)->descriptor.factory (_pannable, _session.get_speakers ())); + _panner.reset ((*p)->descriptor.factory ( + _is_send ? _pannable_internal : _pannable_route, _session.get_speakers ())); + _current_panner_uri = (*p)->descriptor.panner_uri; + _panner_gui_uri = (*p)->descriptor.gui_uri; + + if (_is_send) { + if (!_panlinked) { + _pannable_internal->set_panner(_panner); + } else { + _force_reselect = true; + } + } else { + _pannable_route->set_panner(_panner); + } if (_panner->set_state (**niter, version) == 0) { return -1; @@ -180,7 +252,7 @@ PannerShell::set_state (const XMLNode& node, int version) if (p == pm.panner_info.end()) { error << string_compose (_("Unknown panner plugin \"%1\" found in pan state - ignored"), - prop->value()) + str) << endmsg; } @@ -211,13 +283,13 @@ PannerShell::distribute_no_automation (BufferSet& inbufs, BufferSet& outbufs, pf AudioBuffer& dst = outbufs.get_audio(0); - if (gain_coeff == 0.0f) { + if (gain_coeff == GAIN_COEFF_ZERO) { /* gain was zero, so make it silent */ dst.silence (nframes); - } else if (gain_coeff == 1.0f){ + } else if (gain_coeff == GAIN_COEFF_UNITY){ /* mix all input buffers into the output */ @@ -268,8 +340,16 @@ PannerShell::distribute_no_automation (BufferSet& inbufs, BufferSet& outbufs, pf } void -PannerShell::run (BufferSet& inbufs, BufferSet& outbufs, framepos_t start_frame, framepos_t end_frame, pframes_t nframes) +PannerShell::run (BufferSet& inbufs, BufferSet& outbufs, samplepos_t start_sample, samplepos_t end_sample, pframes_t nframes) { + if (inbufs.count().n_audio() == 0) { + /* Input has no audio buffers (e.g. Aux Send in a MIDI track at a + point with no audio because there is no preceding instrument) + */ + outbufs.silence(nframes, 0); + return; + } + if (outbufs.count().n_audio() == 0) { // Failing to deliver audio we were asked to deliver is a bug assert(inbufs.count().n_audio() == 0); @@ -302,16 +382,9 @@ PannerShell::run (BufferSet& inbufs, BufferSet& outbufs, framepos_t start_frame, // If we shouldn't play automation defer to distribute_no_automation - if (!(as & Play || ((as & Touch) && !_panner->touching()))) { - - // Speed quietning - gain_t gain_coeff = 1.0; - - if (fabsf(_session.transport_speed()) > 1.5f && Config->get_quieten_at_speed ()) { - gain_coeff = speed_quietning; - } + if (!((as & Play) || ((as & (Touch | Latch)) && !_panner->touching()))) { - distribute_no_automation (inbufs, outbufs, nframes, gain_coeff); + distribute_no_automation (inbufs, outbufs, nframes, 1.0); } else { @@ -322,7 +395,93 @@ PannerShell::run (BufferSet& inbufs, BufferSet& outbufs, framepos_t start_frame, i->silence(nframes); } - _panner->distribute_automated (inbufs, outbufs, start_frame, end_frame, nframes, _session.pan_automation_buffer()); + _panner->distribute_automated (inbufs, outbufs, start_sample, end_sample, nframes, _session.pan_automation_buffer()); } } +void +PannerShell::set_bypassed (bool yn) +{ + if (yn == _bypassed) { + return; + } + + _bypassed = yn; + _session.set_dirty (); + Changed (); /* EMIT SIGNAL */ +} + +bool +PannerShell::bypassed () const +{ + return _bypassed; +} + +/* set custom-panner config + * + * This function is intended to be only called from + * Route::set_custom_panner() + * which will trigger IO-reconfigutaion if this fn return true + */ +bool +PannerShell::set_user_selected_panner_uri (std::string const uri) +{ + if (uri == _user_selected_panner_uri) return false; + _user_selected_panner_uri = uri; + if (uri == _current_panner_uri) return false; + _force_reselect = true; + return true; +} + +bool +PannerShell::select_panner_by_uri (std::string const uri) +{ + if (uri == _user_selected_panner_uri) return false; + _user_selected_panner_uri = uri; + if (uri == _current_panner_uri) return false; + _force_reselect = true; + if (_panner) { + Glib::Threads::Mutex::Lock lx (AudioEngine::instance()->process_lock ()); + ChanCount in = _panner->in(); + ChanCount out = _panner->out(); + configure_io(in, out); + if (!_is_send || !_panlinked) { + pannable()->set_panner(_panner); + } + _session.set_dirty (); + } + return true; +} + +void +PannerShell::set_linked_to_route (bool onoff) +{ + assert(_is_send); + if (onoff == _panlinked) { + return; + } + + /* set _pannable-_has_state = true + * this way the panners will pick it up + * when it is re-created + */ + if (pannable()) { + XMLNode state = pannable()->get_state(); + pannable()->set_state(state, 3000); + } + + _panlinked = onoff; + + _force_reselect = true; + if (_panner) { + Glib::Threads::Mutex::Lock lx (AudioEngine::instance()->process_lock ()); + ChanCount in = _panner->in(); + ChanCount out = _panner->out(); + configure_io(in, out); + if (!_panlinked) { + pannable()->set_panner(_panner); + } + _session.set_dirty (); + } + PannableChanged(); +}