*/
+#ifdef WAF_BUILD
+#include "libardour-config.h"
+#endif
+
#include <cmath>
#include <fstream>
#include <cassert>
#include "pbd/memento_command.h"
#include "pbd/stacktrace.h"
#include "pbd/convert.h"
+#include "pbd/boost_debug.h"
#include "evoral/Curve.hpp"
#include "ardour/meter.h"
#include "ardour/mix.h"
#include "ardour/monitor_processor.h"
+#include "ardour/pannable.h"
#include "ardour/panner.h"
+#include "ardour/panner_shell.h"
#include "ardour/plugin_insert.h"
#include "ardour/port.h"
#include "ardour/port_insert.h"
#include "ardour/utils.h"
#include "ardour/graph.h"
#include "ardour/unknown_processor.h"
+#include "ardour/capturing_processor.h"
#include "i18n.h"
, _recordable (true)
, _silent (false)
, _declickable (false)
- , _solo_control (new SoloControllable (X_("solo"), *this))
- , _mute_control (new MuteControllable (X_("mute"), *this))
, _mute_master (new MuteMaster (sess, name))
, _have_internal_generator (false)
, _solo_safe (false)
int
Route::init ()
{
- /* add standard controls */
+ /* add standard controls */
+
+ _solo_control.reset (new SoloControllable (X_("solo"), shared_from_this ()));
+ _mute_control.reset (new MuteControllable (X_("mute"), shared_from_this ()));
_solo_control->set_flags (Controllable::Flag (_solo_control->flags() | Controllable::Toggle));
_mute_control->set_flags (Controllable::Flag (_mute_control->flags() | Controllable::Toggle));
add_control (_solo_control);
add_control (_mute_control);
+ /* panning */
+
+ Pannable* p = new Pannable (_session);
+#ifdef BOOST_SP_ENABLE_DEBUG_HOOKS
+ boost_debug_shared_ptr_mark_interesting (p, "Pannable");
+#endif
+ _pannable.reset (p);
+
/* input and output objects */
_input.reset (new IO (_session, _name, IO::Input, _default_type));
_amp.reset (new Amp (_session));
add_processor (_amp, PostFader);
- /* add standard processors: meter, main outs, monitor out */
+ /* create standard processors: meter, main outs, monitor out;
+ they will be added to _processors by setup_invisible_processors ()
+ */
_meter.reset (new PeakMeter (_session));
_meter->set_display_to_user (false);
+ _meter->activate ();
- add_processor (_meter, PostFader);
-
- _main_outs.reset (new Delivery (_session, _output, _mute_master, _name, Delivery::Main));
-
- add_processor (_main_outs, PostFader);
+ _main_outs.reset (new Delivery (_session, _output, _pannable, _mute_master, _name, Delivery::Main));
+ _main_outs->activate ();
if (is_monitor()) {
/* where we listen to tracks */
_intreturn.reset (new InternalReturn (_session));
- add_processor (_intreturn, PreFader);
-
- ProcessorList::iterator i;
-
- for (i = _processors.begin(); i != _processors.end(); ++i) {
- if (*i == _intreturn) {
- ++i;
- break;
- }
- }
+ _intreturn->activate ();
/* the thing that provides proper control over a control/monitor/listen bus
(such as per-channel cut, dim, solo, invert, etc).
- It always goes right after the internal return;
*/
_monitor_control.reset (new MonitorProcessor (_session));
- add_processor (_monitor_control, i);
+ _monitor_control->activate ();
/* no panning on the monitor main outs */
+#ifdef PANNER_HACKS
_main_outs->panner()->set_bypassed (true);
+#endif
}
if (is_master() || is_monitor() || is_hidden()) {
Metering::Meter.connect_same_thread (*this, (boost::bind (&Route::meter, this)));
+ {
+ /* run a configure so that the invisible processors get set up */
+ Glib::Mutex::Lock lm (AudioEngine::instance()->process_lock ());
+ configure_processors (0);
+ }
+
return 0;
}
if (boost::dynamic_pointer_cast<UnknownProcessor> (*i)) {
break;
}
-
- if (bufs.count() != (*i)->input_streams()) {
- cerr << _name << " bufs = " << bufs.count()
- << " input for " << (*i)->name() << " = " << (*i)->input_streams()
- << endl;
- }
- assert (bufs.count() == (*i)->input_streams());
-
+
+#ifndef NDEBUG
+ /* if it has any inputs, make sure they match */
+ if ((*i)->input_streams() != ChanCount::ZERO) {
+ if (bufs.count() != (*i)->input_streams()) {
+ cerr << _name << " bufs = " << bufs.count()
+ << " input for " << (*i)->name() << " = " << (*i)->input_streams()
+ << endl;
+ abort ();
+ }
+ }
+#endif
/* should we NOT run plugins here if the route is inactive?
do we catch route != active somewhere higher?
*/
}
bool
-Route::listening () const
+Route::listening_via_monitor () const
{
if (_monitor_send) {
return _monitor_send->active ();
/** Add a processor to the route.
- * @a iter must point to an iterator in _processors and the new
- * processor will be inserted immediately before this location. Otherwise,
- * @a position is used.
+ * @param iter an iterator in _processors; the new processor will be inserted immediately before this location.
*/
int
Route::add_processor (boost::shared_ptr<Processor> processor, ProcessorList::iterator iter, ProcessorStreams* err, bool activation_allowed)
{
+ assert (processor != _meter);
+ assert (processor != _main_outs);
+
+ DEBUG_TRACE (DEBUG::Processors, string_compose ("%1 adding processor %2\n", name(), processor->name()));
+
ChanCount old_pms = processor_max_streams;
if (!_session.engine().connected() || !processor) {
{
Glib::RWLock::WriterLock lm (_processor_lock);
+ ProcessorState pstate (this);
boost::shared_ptr<PluginInsert> pi;
boost::shared_ptr<PortInsert> porti;
ProcessorList::iterator loc = find(_processors.begin(), _processors.end(), processor);
- if (processor == _amp || processor == _meter || processor == _main_outs) {
- // Ensure only one of these are in the list at any time
+ if (processor == _amp) {
+ // Ensure only one amp is in the list at any time
if (loc != _processors.end()) {
if (iter == loc) { // Already in place, do nothing
return 0;
Glib::Mutex::Lock lm (AudioEngine::instance()->process_lock ());
if (configure_processors_unlocked (err)) {
- ProcessorList::iterator ploc = loc;
- --ploc;
- _processors.erase(ploc);
+ pstate.restore ();
configure_processors_unlocked (0); // it worked before we tried to add it ...
- cerr << "configure failed\n";
return -1;
}
}
}
- /* is this the monitor send ? if so, make sure we keep track of it */
-
- boost::shared_ptr<InternalSend> isend = boost::dynamic_pointer_cast<InternalSend> (processor);
-
- if (isend && _session.monitor_out() && (isend->target_id() == _session.monitor_out()->id())) {
- _monitor_send = isend;
- }
-
- if (activation_allowed && (processor != _monitor_send)) {
+ if (activation_allowed) {
processor->activate ();
}
} else {
- processor.reset (new PortInsert (_session, _mute_master));
+ processor.reset (new PortInsert (_session, _pannable, _mute_master));
}
}
} else if (node.name() == "Send") {
- processor.reset (new Send (_session, _mute_master));
+ processor.reset (new Send (_session, _pannable, _mute_master));
} else {
int
Route::add_processors (const ProcessorList& others, boost::shared_ptr<Processor> before, ProcessorStreams* err)
{
+ /* NOTE: this is intended to be used ONLY when copying
+ processors from another Route. Hence the subtle
+ differences between this and ::add_processor()
+ */
+
ProcessorList::iterator loc;
if (before) {
loc = find(_processors.begin(), _processors.end(), before);
} else {
- /* nothing specified - at end but before main outs */
- loc = find (_processors.begin(), _processors.end(), _main_outs);
+ /* nothing specified - at end */
+ loc = _processors.end ();
}
- return add_processors (others, loc, err);
-}
-
-int
-Route::add_processors (const ProcessorList& others, ProcessorList::iterator iter, ProcessorStreams* err)
-{
- /* NOTE: this is intended to be used ONLY when copying
- processors from another Route. Hence the subtle
- differences between this and ::add_processor()
- */
-
ChanCount old_pms = processor_max_streams;
if (!_session.engine().connected()) {
{
Glib::RWLock::WriterLock lm (_processor_lock);
-
- ChanCount potential_max_streams = ChanCount::max (_input->n_ports(), _output->n_ports());
+ ProcessorState pstate (this);
for (ProcessorList::const_iterator i = others.begin(); i != others.end(); ++i) {
- // Ensure meter only appears in the list once
if (*i == _meter) {
- ProcessorList::iterator m = find(_processors.begin(), _processors.end(), *i);
- if (m != _processors.end()) {
- _processors.erase(m);
- }
+ continue;
}
boost::shared_ptr<PluginInsert> pi;
if ((pi = boost::dynamic_pointer_cast<PluginInsert>(*i)) != 0) {
pi->set_count (1);
-
- ChanCount m = max (pi->input_streams(), pi->output_streams());
-
- if (m > potential_max_streams) {
- potential_max_streams = m;
- }
}
- ProcessorList::iterator inserted = _processors.insert (iter, *i);
+ ProcessorList::iterator inserted = _processors.insert (loc, *i);
if ((*i)->active()) {
(*i)->activate ();
{
Glib::Mutex::Lock lm (AudioEngine::instance()->process_lock ());
if (configure_processors_unlocked (err)) {
- _processors.erase (inserted);
+ pstate.restore ();
configure_processors_unlocked (0); // it worked before we tried to add it ...
return -1;
}
{
Glib::RWLock::WriterLock lm (_processor_lock);
+ ProcessorState pstate (this);
+
ProcessorList::iterator i;
bool removed = false;
Glib::Mutex::Lock lm (AudioEngine::instance()->process_lock ());
if (configure_processors_unlocked (err)) {
- /* get back to where we where */
- _processors.insert (i, processor);
+ pstate.restore ();
/* we know this will work, because it worked before :) */
configure_processors_unlocked (0);
return -1;
{
Glib::RWLock::WriterLock lm (_processor_lock);
+ ProcessorState pstate (this);
+
ProcessorList::iterator i;
boost::shared_ptr<Processor> processor;
- ProcessorList as_we_were = _processors;
-
for (i = _processors.begin(); i != _processors.end(); ) {
processor = *i;
Glib::Mutex::Lock lm (AudioEngine::instance()->process_lock ());
if (configure_processors_unlocked (err)) {
- /* get back to where we where */
- _processors = as_we_were;
+ pstate.restore ();
/* we know this will work, because it worked before :) */
configure_processors_unlocked (0);
return -1;
Glib::RWLock::WriterLock lm (_processor_lock);
return configure_processors_unlocked (err);
}
+
return 0;
}
return 0;
}
+ /* put invisible processors where they should be */
+ setup_invisible_processors ();
+
_in_configure_processors = true;
list<pair<ChanCount, ChanCount> > configuration = try_configure_processors_unlocked (input_streams (), err);
{
Glib::RWLock::WriterLock lm (_processor_lock);
- ChanCount old_pms = processor_max_streams;
+ ProcessorState pstate (this);
+
ProcessorList::iterator oiter;
ProcessorList::const_iterator niter;
- ProcessorList as_it_was_before = _processors;
ProcessorList as_it_will_be;
oiter = _processors.begin();
Glib::Mutex::Lock lm (AudioEngine::instance()->process_lock ());
if (configure_processors_unlocked (err)) {
- _processors = as_it_was_before;
- processor_max_streams = old_pms;
+ pstate.restore ();
return -1;
}
}
}
- if (true) {
- processors_changed (RouteProcessorChange ()); /* EMIT SIGNAL */
- set_processor_positions ();
- }
+ processors_changed (RouteProcessorChange ()); /* EMIT SIGNAL */
+ set_processor_positions ();
return 0;
}
cmt->add_content (_comment);
}
+ node->add_child_nocopy (_pannable->state (full_state));
+
for (i = _processors.begin(); i != _processors.end(); ++i) {
node->add_child_nocopy((*i)->state (full_state));
}
if (child->name() == X_("Processor")) {
processor_state.add_child_copy (*child);
}
+
+
+ if (child->name() == X_("Pannable")) {
+ _pannable->set_state (*child, version);
+ }
}
set_processor_state (processor_state);
io_child = *io_niter;
if (io_child->name() == X_("Panner")) {
- _main_outs->panner()->set_state(*io_child, version);
+ _main_outs->panner_shell()->set_state(*io_child, version);
} else if (io_child->name() == X_("Automation")) {
/* IO's automation is for the fader */
_amp->set_automation_xml_state (*io_child, Evoral::Parameter (GainAutomation));
new_order.push_back (_amp);
} else if (prop->value() == "meter") {
_meter->set_state (**niter, Stateful::current_state_version);
- new_order.push_back (_meter);
} else if (prop->value() == "main-outs") {
_main_outs->set_state (**niter, Stateful::current_state_version);
- new_order.push_back (_main_outs);
} else if (prop->value() == "intreturn") {
if (!_intreturn) {
_intreturn.reset (new InternalReturn (_session));
must_configure = true;
}
_intreturn->set_state (**niter, Stateful::current_state_version);
- new_order.push_back (_intreturn);
} else if (is_monitor() && prop->value() == "monitor") {
if (!_monitor_control) {
_monitor_control.reset (new MonitorProcessor (_session));
must_configure = true;
}
_monitor_control->set_state (**niter, Stateful::current_state_version);
- new_order.push_back (_monitor_control);
+ } else if (prop->value() == "capture") {
+ _capturing_processor.reset (new CapturingProcessor (_session));
} else {
ProcessorList::iterator o;
if (prop->value() == "intsend") {
- processor.reset (new InternalSend (_session, _mute_master, boost::shared_ptr<Route>(), Delivery::Role (0)));
-
+ processor.reset (new InternalSend (_session, _pannable, _mute_master, boost::shared_ptr<Route>(), Delivery::Role (0)));
} else if (prop->value() == "ladspa" || prop->value() == "Ladspa" ||
prop->value() == "lv2" ||
prop->value() == "vst" ||
} else if (prop->value() == "port") {
- processor.reset (new PortInsert (_session, _mute_master));
+ processor.reset (new PortInsert (_session, _pannable, _mute_master));
} else if (prop->value() == "send") {
- processor.reset (new Send (_session, _mute_master));
+ processor.reset (new Send (_session, _pannable, _mute_master));
} else {
error << string_compose(_("unknown Processor type \"%1\"; ignored"), prop->value()) << endmsg;
/* This processor could not be configured. Turn it into a UnknownProcessor */
processor.reset (new UnknownProcessor (_session, **niter));
}
-
+
+ /* we have to note the monitor send here, otherwise a new one will be created
+ and the state of this one will be lost.
+ */
+ boost::shared_ptr<InternalSend> isend = boost::dynamic_pointer_cast<InternalSend> (processor);
+ if (isend && isend->role() == Delivery::Listen) {
+ _monitor_send = isend;
+ }
+
+ /* it doesn't matter if invisible processors are added here, as they
+ will be sorted out by setup_invisible_processors () shortly.
+ */
+
new_order.push_back (processor);
must_configure = true;
}
}
}
-BufferSet*
-Route::get_return_buffer () const
+void
+Route::add_send_to_internal_return (InternalSend* send)
{
Glib::RWLock::ReaderLock rm (_processor_lock);
boost::shared_ptr<InternalReturn> d = boost::dynamic_pointer_cast<InternalReturn>(*x);
if (d) {
- BufferSet* bs = d->get_buffers ();
- return bs;
+ return d->add_send (send);
}
}
-
- return 0;
}
void
-Route::release_return_buffer () const
+Route::remove_send_from_internal_return (InternalSend* send)
{
Glib::RWLock::ReaderLock rm (_processor_lock);
boost::shared_ptr<InternalReturn> d = boost::dynamic_pointer_cast<InternalReturn>(*x);
if (d) {
- return d->release_buffers ();
+ return d->remove_send (send);
}
}
}
+/** Add a monitor send (if we don't already have one) but don't activate it */
int
-Route::listen_via (boost::shared_ptr<Route> route, Placement placement, bool /*active*/, bool aux)
+Route::listen_via_monitor ()
{
- vector<string> ports;
- vector<string>::const_iterator i;
+ /* master never sends to control outs */
+ assert (!is_master ());
+
+ /* make sure we have one */
+ if (!_monitor_send) {
+ _monitor_send.reset (new InternalSend (_session, _pannable, _mute_master, _session.monitor_out(), Delivery::Listen));
+ _monitor_send->set_display_to_user (false);
+ }
+
+ /* set it up */
+ Glib::Mutex::Lock lm (AudioEngine::instance()->process_lock ());
+ configure_processors (0);
+
+ return 0;
+}
+/** Add an internal send to a route.
+ * @param route route to send to.
+ * @param placement placement for the send.
+ */
+int
+Route::listen_via (boost::shared_ptr<Route> route, Placement placement)
+{
+ assert (route != _session.monitor_out ());
+
{
Glib::RWLock::ReaderLock rm (_processor_lock);
for (ProcessorList::iterator x = _processors.begin(); x != _processors.end(); ++x) {
- boost::shared_ptr<InternalSend> d = boost::dynamic_pointer_cast<InternalSend>(*x);
+ boost::shared_ptr<InternalSend> d = boost::dynamic_pointer_cast<InternalSend> (*x);
if (d && d->target_route() == route) {
-
- /* if the target is the control outs, then make sure
- we take note of which i-send is doing that.
- */
-
- if (route == _session.monitor_out()) {
- _monitor_send = boost::dynamic_pointer_cast<Delivery>(d);
- }
-
/* already listening via the specified IO: do nothing */
-
return 0;
}
}
}
- boost::shared_ptr<InternalSend> listener;
-
try {
-
- if (is_master()) {
-
- if (route == _session.monitor_out()) {
- /* master never sends to control outs */
- return 0;
- } else {
- listener.reset (new InternalSend (_session, _mute_master, route, (aux ? Delivery::Aux : Delivery::Listen)));
- }
-
- } else {
- listener.reset (new InternalSend (_session, _mute_master, route, (aux ? Delivery::Aux : Delivery::Listen)));
- }
+ boost::shared_ptr<InternalSend> listener (new InternalSend (_session, _pannable, _mute_master, route, Delivery::Aux));
+ add_processor (listener, placement);
} catch (failed_constructor& err) {
return -1;
}
- if (route == _session.monitor_out()) {
- _monitor_send = listener;
- }
-
-
- if (aux) {
-
- add_processor (listener, placement);
-
- } else {
-
- if (placement == PostFader) {
- /* put it *really* at the end, not just after the panner (main outs)
- */
- add_processor (listener, _processors.end());
- } else {
- add_processor (listener, PreFader);
- }
-
- }
-
return 0;
}
return;
}
+ _meter_point = p;
+
bool meter_was_visible_to_user = _meter->display_to_user ();
{
Glib::RWLock::WriterLock lm (_processor_lock);
- if (p != MeterCustom) {
- // Move meter in the processors list to reflect the new position
- ProcessorList::iterator loc = find (_processors.begin(), _processors.end(), _meter);
- _processors.erase(loc);
- switch (p) {
- case MeterInput:
- loc = _processors.begin();
- break;
- case MeterPreFader:
- loc = find (_processors.begin(), _processors.end(), _amp);
- break;
- case MeterPostFader:
- loc = _processors.end();
- break;
- default:
- break;
- }
+ if (_meter_point != MeterCustom) {
+
+ _meter->set_display_to_user (false);
+
+ setup_invisible_processors ();
+ ProcessorList::iterator loc = find (_processors.begin(), _processors.end(), _meter);
+
ChanCount m_in;
if (loc == _processors.begin()) {
_meter->reflect_inputs (m_in);
- _processors.insert (loc, _meter);
-
/* we do not need to reconfigure the processors, because the meter
(a) is always ready to handle processor_max_streams
(b) is always an N-in/N-out processor, and thus moving
it doesn't require any changes to the other processors.
*/
- _meter->set_display_to_user (false);
-
} else {
// just make it visible and let the user move it
}
}
- _meter_point = p;
meter_change (); /* EMIT SIGNAL */
bool const meter_visibly_changed = (_meter->display_to_user() != meter_was_visible_to_user);
}
void
-Route::put_monitor_send_at (Placement p)
+Route::listen_position_changed ()
{
- if (!_monitor_send) {
- return;
- }
-
{
Glib::RWLock::WriterLock lm (_processor_lock);
- ProcessorList as_it_was (_processors);
- ProcessorList::iterator loc = find(_processors.begin(), _processors.end(), _monitor_send);
- _processors.erase(loc);
-
- switch (p) {
- case PreFader:
- loc = find(_processors.begin(), _processors.end(), _amp);
- if (loc != _processors.begin()) {
- --loc;
- }
- break;
- case PostFader:
- loc = _processors.end();
- break;
- }
-
- _processors.insert (loc, _monitor_send);
+ ProcessorState pstate (this);
{
Glib::Mutex::Lock lm (AudioEngine::instance()->process_lock ());
if (configure_processors_unlocked (0)) {
- _processors = as_it_was;
+ pstate.restore ();
configure_processors_unlocked (0); // it worked before we tried to add it ...
return;
}
_session.set_dirty ();
}
+boost::shared_ptr<CapturingProcessor>
+Route::add_export_point()
+{
+ if (!_capturing_processor) {
+
+ _capturing_processor.reset (new CapturingProcessor (_session));
+ _capturing_processor->activate ();
+
+ {
+ Glib::Mutex::Lock lm (AudioEngine::instance()->process_lock ());
+ configure_processors (0);
+ }
+
+ }
+
+ return _capturing_processor;
+}
+
framecnt_t
Route::update_total_latency ()
{
void
Route::automation_snapshot (framepos_t now, bool force)
{
- panner()->automation_snapshot (now, force);
-
+ _pannable->automation_snapshot (now, force);
for (ProcessorList::iterator i = _processors.begin(); i != _processors.end(); ++i) {
(*i)->automation_snapshot (now, force);
}
}
-Route::SoloControllable::SoloControllable (std::string name, Route& r)
- : AutomationControl (r.session(), Evoral::Parameter (SoloAutomation),
+Route::SoloControllable::SoloControllable (std::string name, boost::shared_ptr<Route> r)
+ : AutomationControl (r->session(), Evoral::Parameter (SoloAutomation),
boost::shared_ptr<AutomationList>(), name)
- , route (r)
+ , _route (r)
{
boost::shared_ptr<AutomationList> gl(new AutomationList(Evoral::Parameter(SoloAutomation)));
set_list (gl);
Route::SoloControllable::set_value (double val)
{
bool bval = ((val >= 0.5f) ? true: false);
-# if 0
- this is how it should be done
boost::shared_ptr<RouteList> rl (new RouteList);
- rl->push_back (route);
+
+ boost::shared_ptr<Route> r = _route.lock ();
+ if (!r) {
+ return;
+ }
+
+ rl->push_back (r);
if (Config->get_solo_control_is_listen_control()) {
_session.set_listen (rl, bval);
} else {
_session.set_solo (rl, bval);
}
-#else
- route.set_solo (bval, this);
-#endif
}
double
-Route::SoloControllable::get_value (void) const
+Route::SoloControllable::get_value () const
{
+ boost::shared_ptr<Route> r = _route.lock ();
+ if (!r) {
+ return 0;
+ }
+
if (Config->get_solo_control_is_listen_control()) {
- return route.listening() ? 1.0f : 0.0f;
+ return r->listening_via_monitor() ? 1.0f : 0.0f;
} else {
- return route.self_soloed() ? 1.0f : 0.0f;
+ return r->self_soloed() ? 1.0f : 0.0f;
}
}
-Route::MuteControllable::MuteControllable (std::string name, Route& r)
- : AutomationControl (r.session(), Evoral::Parameter (MuteAutomation),
+Route::MuteControllable::MuteControllable (std::string name, boost::shared_ptr<Route> r)
+ : AutomationControl (r->session(), Evoral::Parameter (MuteAutomation),
boost::shared_ptr<AutomationList>(), name)
- , route (r)
+ , _route (r)
{
boost::shared_ptr<AutomationList> gl(new AutomationList(Evoral::Parameter(MuteAutomation)));
set_list (gl);
Route::MuteControllable::set_value (double val)
{
bool bval = ((val >= 0.5f) ? true: false);
-# if 0
- this is how it should be done
boost::shared_ptr<RouteList> rl (new RouteList);
- rl->push_back (route);
+
+ boost::shared_ptr<Route> r = _route.lock ();
+ if (!r) {
+ return;
+ }
+
+ rl->push_back (r);
_session.set_mute (rl, bval);
-#else
- route.set_mute (bval, this);
-#endif
}
double
-Route::MuteControllable::get_value (void) const
+Route::MuteControllable::get_value () const
{
- return route.muted() ? 1.0f : 0.0f;
+ boost::shared_ptr<Route> r = _route.lock ();
+ if (!r) {
+ return 0;
+ }
+
+ return r->muted() ? 1.0f : 0.0f;
}
void
/* pan automation */
{
- boost::shared_ptr<AutomationControl> pc;
- uint32_t npans = _main_outs->panner()->npanners();
-
- for (uint32_t p = 0; p < npans; ++p) {
- pc = _main_outs->panner()->pan_control (0, p);
+ ControlSet::Controls& c (_pannable->controls());
+
+ for (ControlSet::Controls::const_iterator ci = c.begin(); ci != c.end(); ++ci) {
+ boost::shared_ptr<AutomationControl> pc = boost::dynamic_pointer_cast<AutomationControl> (ci->second);
if (pc) {
boost::shared_ptr<AutomationList> al = pc->alist();
XMLNode& before = al->get_state ();
}
}
+boost::shared_ptr<Pannable>
+Route::pannable() const
+{
+ return _pannable;
+}
+
boost::shared_ptr<Panner>
Route::panner() const
{
- return _main_outs->panner();
+ /* may be null ! */
+ return _main_outs->panner_shell()->panner();
+}
+
+boost::shared_ptr<PannerShell>
+Route::panner_shell() const
+{
+ return _main_outs->panner_shell();
}
boost::shared_ptr<AutomationControl>
return p;
}
+
+void
+Route::set_latency_ranges (bool playback) const
+{
+ framecnt_t own_latency = 0;
+
+ /* Processor list not protected by lock: MUST BE CALLED FROM PROCESS THREAD OR
+ LATENCY CALLBACK
+ */
+
+ for (ProcessorList::const_iterator i = _processors.begin(); i != _processors.end(); ++i) {
+ if ((*i)->active ()) {
+ own_latency += (*i)->signal_latency ();
+ }
+ }
+
+ if (playback) {
+ update_port_latencies (_input->ports (), _output->ports (), true, own_latency);
+ } else {
+ update_port_latencies (_output->ports (), _input->ports (), false, own_latency);
+ }
+}
+
+void
+Route::update_port_latencies (const PortSet& operands, const PortSet& feeders, bool playback, framecnt_t our_latency) const
+{
+#ifdef HAVE_JACK_NEW_LATENCY
+
+ /* we assume that all our input ports feed all our output ports. its not
+ universally true, but the alternative is way too corner-case to worry about.
+ */
+
+ jack_latency_range_t all_connections;
+
+ all_connections.min = ~((jack_nframes_t) 0);
+ all_connections.max = 0;
+
+ /* iterate over all feeder ports and determine their relevant latency, taking
+ the maximum and minimum across all of them.
+ */
+
+ for (PortSet::const_iterator p = feeders.begin(); p != feeders.end(); ++p) {
+
+ jack_latency_range_t range;
+
+ p->get_connected_latency_range (range, playback);
+
+ all_connections.min = min (all_connections.min, range.min);
+ all_connections.max = max (all_connections.max, range.max);
+ }
+
+ all_connections.min += our_latency;
+ all_connections.max += our_latency;
+
+ for (PortSet::const_iterator p = operands.begin(); p != operands.end(); ++p) {
+
+ p->set_latency_range (all_connections, playback);
+
+ DEBUG_TRACE (DEBUG::Latency, string_compose ("Port %1 %5 latency range %2 .. %3 (including route latency of %4)\n",
+ p->name(),
+ all_connections.min,
+ all_connections.max,
+ our_latency,
+ (playback ? "PLAYBACK" : "CAPTURE")));
+ }
+#endif
+}
+
+
+/** Put the invisible processors in the right place in _processors.
+ * Must be called with a writer lock on _processor_lock held.
+ */
+void
+Route::setup_invisible_processors ()
+{
+#ifndef NDEBUG
+ Glib::RWLock::WriterLock lm (_processor_lock, Glib::TRY_LOCK);
+ assert (!lm.locked ());
+#endif
+
+ if (!_main_outs) {
+ /* too early to be doing this stuff */
+ return;
+ }
+
+ /* we'll build this new list here and then use it */
+
+ ProcessorList new_processors;
+
+ /* find visible processors */
+
+ for (ProcessorList::iterator i = _processors.begin(); i != _processors.end(); ++i) {
+ if ((*i)->display_to_user ()) {
+ new_processors.push_back (*i);
+ }
+ }
+
+ /* find the amp */
+
+ ProcessorList::iterator amp = new_processors.begin ();
+ while (amp != new_processors.end() && boost::dynamic_pointer_cast<Amp> (*amp) == 0) {
+ ++amp;
+ }
+
+ assert (amp != _processors.end ());
+
+ /* and the processor after the amp */
+
+ ProcessorList::iterator after_amp = amp;
+ ++after_amp;
+
+ /* METER */
+
+ if (_meter) {
+ switch (_meter_point) {
+ case MeterInput:
+ assert (!_meter->display_to_user ());
+ new_processors.push_front (_meter);
+ break;
+ case MeterPreFader:
+ assert (!_meter->display_to_user ());
+ new_processors.insert (amp, _meter);
+ break;
+ case MeterPostFader:
+ /* do nothing here */
+ break;
+ case MeterOutput:
+ /* do nothing here */
+ break;
+ case MeterCustom:
+ /* the meter is visible, so we don't touch it here */
+ break;
+ }
+ }
+
+ /* MAIN OUTS */
+
+ assert (_main_outs);
+ assert (!_main_outs->display_to_user ());
+ new_processors.push_back (_main_outs);
+
+ /* iterator for the main outs */
+
+ ProcessorList::iterator main = new_processors.end();
+ --main;
+
+ /* OUTPUT METERING */
+
+ if (_meter && (_meter_point == MeterOutput || _meter_point == MeterPostFader)) {
+ assert (!_meter->display_to_user ());
+
+ /* add the processor just before or just after the main outs */
+
+ ProcessorList::iterator meter_point = main;
+
+ if (_meter_point == MeterOutput) {
+ ++meter_point;
+ }
+ new_processors.insert (meter_point, _meter);
+ }
+
+ /* MONITOR SEND */
+
+ if (_monitor_send && !is_monitor ()) {
+ assert (!_monitor_send->display_to_user ());
+ if (Config->get_solo_control_is_listen_control()) {
+ switch (Config->get_listen_position ()) {
+ case PreFaderListen:
+ switch (Config->get_pfl_position ()) {
+ case PFLFromBeforeProcessors:
+ new_processors.push_front (_monitor_send);
+ break;
+ case PFLFromAfterProcessors:
+ new_processors.insert (amp, _monitor_send);
+ break;
+ }
+ break;
+ case AfterFaderListen:
+ switch (Config->get_afl_position ()) {
+ case AFLFromBeforeProcessors:
+ new_processors.insert (after_amp, _monitor_send);
+ break;
+ case AFLFromAfterProcessors:
+ new_processors.insert (new_processors.end(), _monitor_send);
+ break;
+ }
+ break;
+ }
+ } else {
+ new_processors.insert (new_processors.end(), _monitor_send);
+ }
+ }
+
+ /* MONITOR CONTROL */
+
+ if (_monitor_control && is_monitor ()) {
+ assert (!_monitor_control->display_to_user ());
+ new_processors.push_front (_monitor_control);
+ }
+
+ /* INTERNAL RETURN */
+
+ /* doing this here means that any monitor control will come just after
+ the return.
+ */
+
+ if (_intreturn) {
+ assert (!_intreturn->display_to_user ());
+ new_processors.push_front (_intreturn);
+ }
+
+ /* EXPORT PROCESSOR */
+
+ if (_capturing_processor) {
+ assert (!_capturing_processor->display_to_user ());
+ new_processors.push_front (_capturing_processor);
+ }
+
+ _processors = new_processors;
+
+ DEBUG_TRACE (DEBUG::Processors, string_compose ("%1: setup_invisible_processors\n", _name));
+ for (ProcessorList::iterator i = _processors.begin(); i != _processors.end(); ++i) {
+ DEBUG_TRACE (DEBUG::Processors, string_compose ("\t%1\n", (*i)->name ()));
+ }
+}