X-Git-Url: https://main.carlh.net/gitweb/?a=blobdiff_plain;f=libs%2Fardour%2Fpanner_shell.cc;h=afc34c358a29410d5d30ede10c1a3b86841ff0af;hb=a232673454fa3583da22fdd55eea16200f90c438;hp=ecf7adf990cf8c0f896f84df425f75268fb29541;hpb=01471c8879869dc87a6a17d88c945728a3193325;p=ardour.git diff --git a/libs/ardour/panner_shell.cc b/libs/ardour/panner_shell.cc index ecf7adf990..afc34c358a 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,17 +41,17 @@ #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" @@ -63,68 +61,106 @@ 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 PannerShell::configure_io (ChanCount in, ChanCount out) { - uint32_t nouts = out.n_audio(); - uint32_t nins = in.n_audio(); + uint32_t nouts = out.n_audio(); + uint32_t nins = in.n_audio(); /* if new and old config don't need panning, or if the config hasn't changed, we're done. */ - if (_panner && _panner->in().n_audio() == nins && _panner->out().n_audio() == nouts) { - return; + 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 (); + _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) { + fatal << _("No panner found: check that panners are being discovered correctly during startup.") << endmsg; + abort(); /*NOTREACHED*/ + } - if (pi == 0) { - abort (); - } + DEBUG_TRACE (DEBUG::Panning, string_compose (_("select panner: %1\n"), pi->descriptor.name.c_str())); - Panner* p = pi->descriptor.factory (_pannable, _session.get_speakers()); - boost_debug_shared_ptr_mark_interesting (p, "Panner"); - _panner.reset (p); - _panner->configure_io (in, out); + boost::shared_ptr speakers = _session.get_speakers (); - Changed (); /* EMIT SIGNAL */ -} + if (nouts != speakers->size()) { + /* hmm, output count doesn't match session speaker count so + create a new speaker set. + */ + Speakers* s = new Speakers (); + s->setup_default_speakers (nouts); + speakers.reset (s); + } -XMLNode& -PannerShell::get_state (void) -{ - return state (true); + /* 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::state (bool full) +PannerShell::get_state () { XMLNode* node = new XMLNode ("PannerShell"); - if (_panner) { - node->add_child_nocopy (_panner->state (full)); + node->add_property (X_("bypassed"), _bypassed ? X_("yes") : X_("no")); + node->add_property (X_("user-panner"), _user_selected_panner_uri); + node->add_property (X_("linked-to-route"), _panlinked ? X_("yes") : X_("no")); + + if (_panner && _is_send) { + node->add_child_nocopy (_panner->get_state ()); } return *node; @@ -135,19 +171,56 @@ PannerShell::set_state (const XMLNode& node, int version) { XMLNodeList nlist = node.children (); XMLNodeConstIterator niter; - const XMLProperty *prop; - LocaleGuard lg (X_("POSIX")); + XMLProperty const * prop; + LocaleGuard lg; + + if ((prop = node.property (X_("bypassed"))) != 0) { + set_bypassed (string_is_affirmative (prop->value ())); + } - _panner.reset (); + if ((prop = node.property (X_("linked-to-route"))) != 0) { + if (!ARDOUR::Profile->get_mixbus()) { + _panlinked = string_is_affirmative (prop->value ()); + } + } + + if ((prop = node.property (X_("user-panner"))) != 0) { + _user_selected_panner_uri = prop->value (); + } + + _panner.reset (); for (niter = nlist.begin(); niter != nlist.end(); ++niter) { if ((*niter)->name() == X_("Panner")) { + if ((prop = (*niter)->property (X_("uri")))) { + PannerInfo* p = PannerManager::instance().get_by_uri(prop->value()); + 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; + } + } + } + + else /* backwards compatibility */ if ((prop = (*niter)->property (X_("type")))) { - list::iterator p; - PannerManager& pm (PannerManager::instance()); + 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) { @@ -156,12 +229,25 @@ PannerShell::set_state (const XMLNode& node, int version) 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; - } + return -1; + } break; } @@ -169,7 +255,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()) + prop->value()) << endmsg; } @@ -196,17 +282,17 @@ PannerShell::distribute_no_automation (BufferSet& inbufs, BufferSet& outbufs, pf if (outbufs.count().n_audio() == 1) { - /* just one output: no real panning going on */ + /* just one output: no real panning going on */ 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 */ @@ -241,24 +327,32 @@ PannerShell::distribute_no_automation (BufferSet& inbufs, BufferSet& outbufs, pf return; } - /* multiple outputs ... we must have a panner */ + /* multiple outputs ... we must have a panner */ - assert (_panner); + assert (_panner); /* setup silent buffers so that we can mix into the outbuffers (slightly suboptimal - - better to copy the first set of data then mix after that, but hey, its 2011) - */ + better to copy the first set of data then mix after that, but hey, its 2011) + */ for (BufferSet::audio_iterator b = outbufs.audio_begin(); b != outbufs.audio_end(); ++b) { (*b).silence (nframes); } - _panner->distribute (inbufs, outbufs, gain_coeff, nframes); + _panner->distribute (inbufs, outbufs, gain_coeff, nframes); } void PannerShell::run (BufferSet& inbufs, BufferSet& outbufs, framepos_t start_frame, framepos_t end_frame, 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); @@ -267,7 +361,7 @@ PannerShell::run (BufferSet& inbufs, BufferSet& outbufs, framepos_t start_frame, if (outbufs.count().n_audio() == 1) { - /* one output only: no panner */ + /* one output only: no panner */ AudioBuffer& dst = outbufs.get_audio(0); @@ -287,31 +381,110 @@ PannerShell::run (BufferSet& inbufs, BufferSet& outbufs, framepos_t start_frame, // More than 1 output - AutoState as = _panner->automation_state (); + AutoState as = _panner->automation_state (); // 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; + distribute_no_automation (inbufs, outbufs, nframes, 1.0); + + } else { + + /* setup the terrible silence so that we can mix into the outbuffers (slightly suboptimal - + better to copy the first set of data then mix after that, but hey, its 2011) + */ + for (BufferSet::audio_iterator i = outbufs.audio_begin(); i != outbufs.audio_end(); ++i) { + i->silence(nframes); } - distribute_no_automation (inbufs, outbufs, nframes, gain_coeff); + _panner->distribute_automated (inbufs, outbufs, start_frame, end_frame, nframes, _session.pan_automation_buffer()); + } +} - } else { +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; +} - /* setup the terrible silence so that we can mix into the outbuffers (slightly suboptimal - - better to copy the first set of data then mix after that, but hey, its 2011) - */ - for (BufferSet::audio_iterator i = outbufs.audio_begin(); i != outbufs.audio_end(); ++i) { - i->silence(nframes); - } - - _panner->distribute_automated (inbufs, outbufs, start_frame, end_frame, nframes, _session.pan_automation_buffer()); - } +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(); +}