#include "pbd/enumwriter.h"
#include "pbd/memento_command.h"
#include "pbd/stacktrace.h"
-#include "pbd/convert.h"
+#include "pbd/types_convert.h"
#include "pbd/unwind.h"
#include "ardour/amp.h"
#include "ardour/capturing_processor.h"
#include "ardour/debug.h"
#include "ardour/delivery.h"
+#include "ardour/event_type_map.h"
#include "ardour/gain_control.h"
#include "ardour/internal_return.h"
#include "ardour/internal_send.h"
#include "ardour/session.h"
#include "ardour/solo_control.h"
#include "ardour/solo_isolate_control.h"
+#include "ardour/types_convert.h"
#include "ardour/unknown_processor.h"
#include "ardour/utils.h"
#include "ardour/vca.h"
-#include "i18n.h"
+#include "pbd/i18n.h"
using namespace std;
using namespace ARDOUR;
using namespace PBD;
-PBD::Signal0<void> Route::SyncOrderKeys;
-PBD::Signal0<void> Route::RemoteControlIDChange;
PBD::Signal3<int,boost::shared_ptr<Route>, boost::shared_ptr<PluginInsert>, Route::PluginSetupOptions > Route::PluginSetup;
/** Base class for all routable/mixable objects (tracks and busses) */
-Route::Route (Session& sess, string name, Flag flg, DataType default_type)
- : GraphNode (sess._process_graph)
- , Stripable (sess, name)
+Route::Route (Session& sess, string name, PresentationInfo::Flag flag, DataType default_type)
+ : Stripable (sess, name, PresentationInfo (flag))
+ , GraphNode (sess._process_graph)
, Muteable (sess, name)
- , Automatable (sess)
, _active (true)
, _signal_latency (0)
, _signal_latency_at_amp_position (0)
, _roll_delay (0)
, _pending_process_reorder (0)
, _pending_signals (0)
- , _flags (flg)
, _pending_declick (true)
, _meter_point (MeterPostFader)
, _pending_meter_point (MeterPostFader)
, _declickable (false)
, _have_internal_generator (false)
, _default_type (default_type)
- , _order_key (0)
- , _has_order_key (false)
- , _remote_control_id (0)
, _track_number (0)
, _in_configure_processors (false)
, _initial_io_setup (false)
processor_max_streams.reset();
}
+boost::weak_ptr<Route>
+Route::weakroute () {
+ return boost::weak_ptr<Route> (boost::dynamic_pointer_cast<Route> (shared_from_this ()));
+}
+
int
Route::init ()
{
add_control (_trim_control);
_solo_control.reset (new SoloControl (_session, X_("solo"), *this, *this));
- _solo_control->set_flags (Controllable::Flag (_solo_control->flags() | Controllable::Toggle));
add_control (_solo_control);
_solo_control->Changed.connect_same_thread (*this, boost::bind (&Route::solo_control_changed, this, _1, _2));
_mute_control.reset (new MuteControl (_session, X_("mute"), *this));
- _mute_control->set_flags (Controllable::Flag (_mute_control->flags() | Controllable::Toggle));
add_control (_mute_control);
_phase_control.reset (new PhaseControl (_session, X_("phase")));
/* panning */
- if (!(_flags & Route::MonitorOut)) {
+ if (!(_presentation_info.flags() & PresentationInfo::MonitorOut)) {
_pannable.reset (new Pannable (_session));
}
_monitor_control->activate ();
}
- if (is_master() || is_monitor() || is_auditioner()) {
- _mute_master->set_solo_ignore (true);
- }
-
/* now that we have _meter, its safe to connect to this */
{
_processors.clear ();
}
-void
-Route::set_remote_control_id (uint32_t id, bool notify_class_listeners)
-{
- if (Config->get_remote_model() != UserOrdered) {
- return;
- }
-
- set_remote_control_id_internal (id, notify_class_listeners);
-}
-
-void
-Route::set_remote_control_id_internal (uint32_t id, bool notify_class_listeners)
-{
- /* force IDs for master/monitor busses and prevent
- any other route from accidentally getting these IDs
- (i.e. legacy sessions)
- */
-
- if (is_master() && id != MasterBusRemoteControlID) {
- id = MasterBusRemoteControlID;
- }
-
- if (is_monitor() && id != MonitorBusRemoteControlID) {
- id = MonitorBusRemoteControlID;
- }
-
- if (id < 1) {
- return;
- }
-
- /* don't allow it to collide */
-
- if (!is_master () && !is_monitor() &&
- (id == MasterBusRemoteControlID || id == MonitorBusRemoteControlID)) {
- id += MonitorBusRemoteControlID;
- }
-
- if (id != remote_control_id()) {
- _remote_control_id = id;
- RemoteControlIDChanged ();
-
- if (notify_class_listeners) {
- RemoteControlIDChange ();
- }
- }
-}
-
-uint32_t
-Route::remote_control_id() const
-{
- if (is_master()) {
- return MasterBusRemoteControlID;
- }
-
- if (is_monitor()) {
- return MonitorBusRemoteControlID;
- }
-
- return _remote_control_id;
-}
-
-bool
-Route::has_order_key () const
-{
- return _has_order_key;
-}
-
-uint32_t
-Route::order_key () const
-{
- return _order_key;
-}
-
-void
-Route::set_remote_control_id_explicit (uint32_t rid)
-{
- if (is_master() || is_monitor() || is_auditioner()) {
- /* hard-coded remote IDs, or no remote ID */
- return;
- }
-
- if (_remote_control_id != rid) {
- DEBUG_TRACE (DEBUG::OrderKeys, string_compose ("%1: set edit-based RID to %2\n", name(), rid));
- _remote_control_id = rid;
- RemoteControlIDChanged (); /* EMIT SIGNAL (per-route) */
- }
-
- /* don't emit the class-level RID signal RemoteControlIDChange here,
- leave that to the entity that changed the order key, so that we
- don't get lots of emissions for no good reasons (e.g. when changing
- all route order keys).
-
- See Session::sync_remote_id_from_order_keys() for the (primary|only)
- spot where that is emitted.
- */
-}
-
-void
-Route::set_order_key (uint32_t n)
-{
- _has_order_key = true;
-
- if (_order_key == n) {
- return;
- }
-
- _order_key = n;
-
- DEBUG_TRACE (DEBUG::OrderKeys, string_compose ("%1 order key set to %2\n",
- name(), order_key ()));
-
- _session.set_dirty ();
-}
-
string
Route::ensure_track_or_route_name(string name, Session &session)
{
return;
}
+ _mute_control->automation_run (start_frame, nframes);
+
/* figure out if we're going to use gain automation */
if (gain_automation_ok) {
_amp->set_gain_automation_buffer (_session.gain_automation_buffer ());
bool const meter_already_run = metering_state() == MeteringInput;
framecnt_t latency = 0;
+ const double speed = _session.transport_speed ();
for (ProcessorList::const_iterator i = _processors.begin(); i != _processors.end(); ++i) {
if (boost::dynamic_pointer_cast<Send>(*i) != 0) {
boost::dynamic_pointer_cast<Send>(*i)->set_delay_in(_signal_latency - latency);
}
+ if (boost::dynamic_pointer_cast<PluginInsert>(*i) != 0) {
+ const framecnt_t longest_session_latency = _initial_delay + _signal_latency;
+ boost::dynamic_pointer_cast<PluginInsert>(*i)->set_sidechain_latency (
+ _initial_delay + latency, longest_session_latency - latency);
+ }
- (*i)->run (bufs, start_frame - latency, end_frame - latency, nframes, *i != _processors.back());
+ (*i)->run (bufs, start_frame - latency, end_frame - latency, speed, nframes, *i != _processors.back());
bufs.set_count ((*i)->output_streams());
if ((*i)->active ()) {
_trim->setup_gain_automation (start, start + nframes, nframes);
latency = 0;
+ const double speed = _session.transport_speed ();
for (ProcessorList::iterator i = _processors.begin(); i != _processors.end(); ++i) {
if (!include_endpoint && (*i) == endpoint) {
*/
if ((*i) == _main_outs) {
assert ((*i)->does_routing());
- (*i)->run (buffers, start - latency, start - latency + nframes, nframes, true);
+ (*i)->run (buffers, start - latency, start - latency + nframes, speed, nframes, true);
buffers.set_count ((*i)->output_streams());
}
* Also don't bother with metering.
*/
if (!(*i)->does_routing() && !boost::dynamic_pointer_cast<PeakMeter>(*i)) {
- (*i)->run (buffers, start - latency, start - latency + nframes, nframes, true);
+ (*i)->run (buffers, start - latency, start - latency + nframes, 1.0, nframes, true);
buffers.set_count ((*i)->output_streams());
latency += (*i)->signal_latency ();
}
Route::set_listen (bool yn)
{
if (_monitor_send) {
+ if (_monitor_send->active() == yn) {
+ return;
+ }
if (yn) {
_monitor_send->activate ();
} else {
if (prop->value() == "ladspa" || prop->value() == "Ladspa" ||
prop->value() == "lv2" ||
prop->value() == "windows-vst" ||
+ prop->value() == "mac-vst" ||
prop->value() == "lxvst" ||
prop->value() == "audiounit") {
//A2 uses the "active" flag in the toplevel redirect node, not in the child plugin/IO
if (i != children.end()) {
if ((prop = (*i)->property (X_("active"))) != 0) {
- if ( string_is_affirmative (prop->value()) && (!_session.get_bypass_all_loaded_plugins () || !processor->display_to_user () ) )
+ if ( string_to<bool> (prop->value()) && (!_session.get_bypass_all_loaded_plugins () || !processor->display_to_user () ) )
processor->activate();
else
processor->deactivate();
Route::add_processors (const ProcessorList& others, boost::shared_ptr<Processor> before, ProcessorStreams* err)
{
ProcessorList::iterator loc;
+ boost::shared_ptr <PluginInsert> fanout;
+
+ if (g_atomic_int_get (&_pending_process_reorder)) {
+ /* we need to flush any pending re-order changes */
+ Glib::Threads::Mutex::Lock lx (AudioEngine::instance()->process_lock ());
+ apply_processor_changes_rt ();
+ }
if (before) {
loc = find(_processors.begin(), _processors.end(), before);
flags &= mask;
if (flags != None) {
- boost::optional<int> rv = PluginSetup (shared_from_this (), pi, flags); /* EMIT SIGNAL */
- switch (rv.get_value_or (0)) {
+ boost::optional<int> rv = PluginSetup (boost::dynamic_pointer_cast<Route>(shared_from_this ()), pi, flags); /* EMIT SIGNAL */
+ int mode = rv.get_value_or (0);
+ switch (mode & 3) {
case 1:
to_skip.push_back (*i); // don't add this one;
break;
default:
break;
}
+ if ((mode & 5) == 4) {
+ fanout = pi;
+ }
}
}
}
}
+ if (pi && pi->has_sidechain ()) {
+ pi->sidechain_input ()->changed.connect_same_thread (*this, boost::bind (&Route::sidechain_change_handler, this, _1, _2));
+ }
+
if ((*i)->active()) {
+ // emit ActiveChanged() and latency_changed() if needed
(*i)->activate ();
}
processors_changed (RouteProcessorChange ()); /* EMIT SIGNAL */
set_processor_positions ();
+ if (fanout && fanout->configured ()
+ && fanout->output_streams().n_audio() > 2
+ && boost::dynamic_pointer_cast<PluginInsert> (the_instrument ()) == fanout) {
+ fan_out (); /* EMIT SIGNAL */
+ }
return 0;
}
placement_range(p, start, end);
for (ProcessorList::iterator i = start; i != end; ++i) {
- (*i)->deactivate ();
+ (*i)->enable (false);
}
_session.set_dirty ();
Glib::Threads::RWLock::ReaderLock lm (_processor_lock);
for (ProcessorList::iterator i = _processors.begin(); i != _processors.end(); ++i) {
- (*i)->deactivate ();
+ (*i)->enable (false);
}
_session.set_dirty ();
for (ProcessorList::iterator i = start; i != end; ++i) {
if (boost::dynamic_pointer_cast<PluginInsert> (*i)) {
- (*i)->deactivate ();
+ (*i)->enable (false);
}
}
for (ProcessorList::iterator i = _processors.begin(); i != _processors.end(); ++i) {
if (boost::dynamic_pointer_cast<PluginInsert> (*i)) {
- (*i)->deactivate ();
+ (*i)->enable (false);
}
}
if (!boost::dynamic_pointer_cast<PluginInsert> (*i)) {
continue;
}
+ if (!(*i)->display_to_user ()) {
+ continue;
+ }
+#ifdef MIXBUS
+ if (boost::dynamic_pointer_cast<PluginInsert> (*i)->is_channelstrip()) {
+ continue;
+ }
+#endif
- if ((*i)->active()) {
- (*i)->deactivate ();
+ if ((*i)->enabled ()) {
+ (*i)->enable (false);
(*i)->set_next_ab_is_active (true);
} else {
(*i)->set_next_ab_is_active (false);
/* backward = if the redirect was marked to go active on the next ab, do so */
for (ProcessorList::iterator i = _processors.begin(); i != _processors.end(); ++i) {
-
if (!boost::dynamic_pointer_cast<PluginInsert> (*i)) {
continue;
}
-
- if ((*i)->get_next_ab_is_active()) {
- (*i)->activate ();
- } else {
- (*i)->deactivate ();
+ if (!(*i)->display_to_user ()) {
+ continue;
}
+#ifdef MIXBUS
+ if (boost::dynamic_pointer_cast<PluginInsert> (*i)->is_channelstrip()) {
+ continue;
+ }
+#endif
+
+ (*i)->enable ((*i)->get_next_ab_is_active ());
}
}
ProcessorList::iterator i;
bool replaced = false;
- bool enable = old->active ();
+ bool enable = old->enabled ();
for (i = _processors.begin(); i != _processors.end(); ) {
if (*i == old) {
}
if (enable) {
- sub->activate ();
+ sub->enable (true);
}
sub->ActiveChanged.connect_same_thread (*this, boost::bind (&Session::update_latency_compensation, &_session, false));
if (boost::dynamic_pointer_cast<Delivery> (*p)
&& boost::dynamic_pointer_cast<Delivery> (*p)->role() == Delivery::Main
- && !(is_monitor() || is_auditioner())
- && ( _strict_io || Profile->get_mixbus ())) {
+ && !is_auditioner()
+ && (is_monitor() || _strict_io || Profile->get_mixbus ())) {
/* with strict I/O the panner + output are forced to
* follow the last processor's output.
*
* Delivery::configure_io() will do the actual removal
* by calling _output->ensure_io()
*/
- if (!is_master() && _session.master_out ()) {
- /* ..but at least as many as there are master-inputs */
+ if (!is_master() && _session.master_out () && in.n_audio() > 0) {
+ /* ..but at least as many as there are master-inputs, if
+ * the delivery is dealing with audio */
// XXX this may need special-casing for mixbus (master-outputs)
// and should maybe be a preference anyway ?!
out = ChanCount::max (in, _session.master_out ()->n_inputs ());
if (is_monitor()) {
// restriction for Monitor Section Processors
if (in.n_audio() != out.n_audio() || out.n_midi() > 0) {
- /* do not allow to add/remove channels (for now)
- * The Monitor follows the master-bus and has no panner (unpan)
- * but do allow processors with midi-in to be added (e.g VSTs with control that
- * will remain unconnected)
+ /* Note: The Monitor follows the master-bus and has no panner.
+ *
+ * The general idea is to only allow plugins that retain the channel-count
+ * and plugins with MIDI in (e.g VSTs with control that will remain unconnected).
+ * Then again 5.1 in, monitor stereo is a valid use-case.
+ *
+ * and worse: we only refuse adding plugins *here*.
+ *
+ * 1) stereo-master, stereo-mon, add a stereo-plugin, OK
+ * 2) change master-bus, add a channel
+ * 2a) monitor-secion follows
+ * 3) monitor processors fail to re-reconfigure (stereo plugin)
+ * 4) re-load session, monitor-processor remains unconfigured, crash.
*/
- DEBUG_TRACE (DEBUG::Processors, "Monitor: Channel configuration not allowed.\n");
- return list<pair<ChanCount, ChanCount> > ();
+ DEBUG_TRACE (DEBUG::Processors, "Monitor: Channel configuration change.\n");
}
if (boost::dynamic_pointer_cast<InternalSend> (*p)) {
// internal sends make no sense, only feedback
if (!(*i)->display_to_user() || boost::dynamic_pointer_cast<Amp> (*i)) {
continue;
}
-
- if (state) {
- (*i)->activate ();
- } else {
- (*i)->deactivate ();
+#ifdef MIXBUS
+ boost::shared_ptr<PluginInsert> pi;
+ if (0 != (pi = boost::dynamic_pointer_cast<PluginInsert>(*i))) {
+ if (pi->is_channelstrip ()) {
+ continue;
+ }
}
+#endif
+ (*i)->enable (state);
}
_session.set_dirty ();
maybe_note_meter_position ();
}
+void
+Route::move_instrument_down (bool postfader)
+{
+ Glib::Threads::RWLock::ReaderLock lm (_processor_lock);
+ ProcessorList new_order;
+ boost::shared_ptr<Processor> instrument;
+ for (ProcessorList::const_iterator i = _processors.begin(); i != _processors.end(); ++i) {
+ boost::shared_ptr<PluginInsert> pi = boost::dynamic_pointer_cast<PluginInsert>(*i);
+ if (pi && pi->plugin ()->get_info ()->is_instrument ()) {
+ instrument = *i;
+ } else if (instrument && *i == _amp) {
+ if (postfader) {
+ new_order.push_back (*i);
+ new_order.push_back (instrument);
+ } else {
+ new_order.push_back (instrument);
+ new_order.push_back (*i);
+ }
+ } else {
+ new_order.push_back (*i);
+ }
+ }
+ if (!instrument) {
+ return;
+ }
+ lm.release ();
+ reorder_processors (new_order, 0);
+}
+
int
Route::reorder_processors (const ProcessorList& new_order, ProcessorStreams* err)
{
{
LocaleGuard lg;
if (!_session._template_state_dir.empty()) {
- assert (!full_state); // only for templates
foreach_processor (sigc::bind (sigc::mem_fun (*this, &Route::set_plugin_state_dir), _session._template_state_dir));
}
XMLNode *node = new XMLNode("Route");
ProcessorList::iterator i;
- char buf[32];
- id().print (buf, sizeof (buf));
- node->add_property("id", buf);
- node->add_property ("name", _name);
- node->add_property("default-type", _default_type.to_string());
- node->add_property ("strict-io", _strict_io);
+ node->set_property ("id", id ());
+ node->set_property ("name", name());
+ node->set_property ("default-type", _default_type);
+ node->set_property ("strict-io", _strict_io);
- if (_flags) {
- node->add_property("flags", enum_2_string (_flags));
- }
+ node->add_child_nocopy (_presentation_info.get_state());
- node->add_property("active", _active?"yes":"no");
- string p;
- node->add_property("denormal-protection", _denormal_protection?"yes":"no");
- node->add_property("meter-point", enum_2_string (_meter_point));
+ node->set_property ("active", _active);
+ node->set_property ("denormal-protection", _denormal_protection);
+ node->set_property ("meter-point", _meter_point);
- node->add_property("meter-type", enum_2_string (_meter_type));
+ node->set_property ("meter-type", _meter_type);
if (_route_group) {
- node->add_property("route-group", _route_group->name());
+ node->set_property ("route-group", _route_group->name());
}
- snprintf (buf, sizeof (buf), "%d", _order_key);
- node->add_property ("order-key", buf);
-
node->add_child_nocopy (_solo_control->get_state ());
node->add_child_nocopy (_solo_isolate_control->get_state ());
node->add_child_nocopy (_solo_safe_control->get_state ());
node->add_child_nocopy (Automatable::get_automation_xml_state ());
}
- XMLNode* remote_control_node = new XMLNode (X_("RemoteControl"));
- snprintf (buf, sizeof (buf), "%d", _remote_control_id);
- remote_control_node->add_property (X_("id"), buf);
- node->add_child_nocopy (*remote_control_node);
-
if (_comment.length()) {
XMLNode *cmt = node->add_child ("Comment");
cmt->add_content (_comment);
if (_custom_meter_position_noted) {
boost::shared_ptr<Processor> after = _processor_after_last_custom_meter.lock ();
if (after) {
- after->id().print (buf, sizeof (buf));
- node->add_property (X_("processor-after-last-custom-meter"), buf);
+ node->set_property (X_("processor-after-last-custom-meter"), after->id());
}
}
XMLNodeList nlist;
XMLNodeConstIterator niter;
XMLNode *child;
- XMLProperty const * prop;
if (node.name() != "Route"){
error << string_compose(_("Bad node sent to Route::set_state() [%1]"), node.name()) << endmsg;
return -1;
}
- if ((prop = node.property (X_("name"))) != 0) {
- Route::set_name (prop->value());
+ std::string route_name;
+ if (node.get_property (X_("name"), route_name)) {
+ Route::set_name (route_name);
}
set_id (node);
_initial_io_setup = true;
- if ((prop = node.property (X_("flags"))) != 0) {
- _flags = Flag (string_2_enum (prop->value(), _flags));
- } else {
- _flags = Flag (0);
- }
-
- if ((prop = node.property (X_("strict-io"))) != 0) {
- _strict_io = string_is_affirmative (prop->value());
- }
+ Stripable::set_state (node, version);
- if (!can_solo()) {
- _mute_master->set_solo_ignore (true);
- }
+ node.get_property (X_("strict-io"), _strict_io);
if (is_monitor()) {
/* monitor bus does not get a panner, but if (re)created
child = *niter;
if (child->name() == IO::state_node_name) {
- if ((prop = child->property (X_("direction"))) == 0) {
+ std::string direction;
+ if (!child->get_property (X_("direction"), direction)) {
continue;
}
- if (prop->value() == "Input") {
+ if (direction == "Input") {
_input->set_state (*child, version);
- } else if (prop->value() == "Output") {
+ } else if (direction == "Output") {
_output->set_state (*child, version);
}
} else {
warning << string_compose (_("Pannable state found for route (%1) without a panner!"), name()) << endmsg;
}
- } else if (child->name() == Controllable::xml_node_name) {
- if ((prop = child->property (X_("name"))) == 0) {
- continue;
- }
-
- if (prop->value() == _gain_control->name()) {
- _gain_control->set_state (*child, version);
- } else if (prop->value() == _solo_control->name()) {
- _solo_control->set_state (*child, version);
- } else if (prop->value() == _solo_safe_control->name()) {
- _solo_safe_control->set_state (*child, version);
- } else if (prop->value() == _solo_isolate_control->name()) {
- _solo_isolate_control->set_state (*child, version);
- } else if (prop->value() == _solo_control->name()) {
- _mute_control->set_state (*child, version);
- }
} else if (child->name() == Slavable::xml_node_name) {
Slavable::set_state (*child, version);
}
}
- if ((prop = node.property (X_("meter-point"))) != 0) {
- MeterPoint mp = MeterPoint (string_2_enum (prop->value (), _meter_point));
+ MeterPoint mp;
+ if (node.get_property (X_("meter-point"), mp)) {
set_meter_point (mp, true);
if (_meter) {
_meter->set_display_to_user (_meter_point == MeterCustom);
}
}
- if ((prop = node.property (X_("meter-type"))) != 0) {
- _meter_type = MeterType (string_2_enum (prop->value (), _meter_type));
- }
+ node.get_property (X_("meter-type"), _meter_type);
_initial_io_setup = false;
// this looks up the internal instrument in processors
reset_instrument_info();
- if ((prop = node.property (X_("denormal-protection"))) != 0) {
- set_denormal_protection (string_is_affirmative (prop->value()));
- }
-
- if ((prop = node.property (X_("active"))) != 0) {
- bool yn = string_is_affirmative (prop->value());
- set_active (yn, this);
+ bool denormal_protection;
+ if (node.get_property (X_("denormal-protection"), denormal_protection)) {
+ set_denormal_protection (denormal_protection);
}
- if ((prop = node.property (X_("order-key"))) != 0) { // New order key (no separate mixer/editor ordering)
- set_order_key (atoi(prop->value()));
+ /* convert old 3001 state */
+ std::string phase_invert_str;
+ if (node.get_property (X_("phase-invert"), phase_invert_str)) {
+ _phase_control->set_phase_invert (boost::dynamic_bitset<> (phase_invert_str));
}
- if ((prop = node.property (X_("order-keys"))) != 0) { // Deprecated order keys
-
- int32_t n;
-
- string::size_type colon, equal;
- string remaining = prop->value();
-
- while (remaining.length()) {
-
- if ((equal = remaining.find_first_of ('=')) == string::npos || equal == remaining.length()) {
- error << string_compose (_("badly formed order key string in state file! [%1] ... ignored."), remaining)
- << endmsg;
- } else {
- if (sscanf (remaining.substr (equal+1).c_str(), "%d", &n) != 1) {
- error << string_compose (_("badly formed order key string in state file! [%1] ... ignored."), remaining)
- << endmsg;
- } else {
- string keyname = remaining.substr (0, equal);
-
- if ((keyname == "EditorSort") || (keyname == "editor")) {
- cerr << "Setting " << name() << " order key to " << n << " using saved Editor order." << endl;
- set_order_key (n);
- }
- }
- }
-
- colon = remaining.find_first_of (':');
-
- if (colon != string::npos) {
- remaining = remaining.substr (colon+1);
- } else {
- break;
- }
- }
+ bool is_active;
+ if (node.get_property (X_("active"), is_active)) {
+ set_active (is_active, this);
}
- if ((prop = node.property (X_("processor-after-last-custom-meter"))) != 0) {
- PBD::ID id (prop->value ());
+ std::string id_string;
+ if (node.get_property (X_("processor-after-last-custom-meter"), id_string)) {
+ PBD::ID id (id_string);
Glib::Threads::RWLock::ReaderLock lm (_processor_lock);
ProcessorList::const_iterator i = _processors.begin ();
while (i != _processors.end() && (*i)->id() != id) {
XMLNode *cmt = *(child->children().begin());
_comment = cmt->content();
- } else if (child->name() == Controllable::xml_node_name && (prop = child->property("name")) != 0) {
- if (prop->value() == "solo") {
- _solo_control->set_state (*child, version);
- } else if (prop->value() == "mute") {
- _mute_control->set_state (*child, version);
+ } else if (child->name() == Controllable::xml_node_name) {
+ std::string control_name;
+ if (!child->get_property (X_("name"), control_name)) {
+ continue;
}
- } else if (child->name() == X_("RemoteControl")) {
- if ((prop = child->property (X_("id"))) != 0) {
- int32_t x;
- sscanf (prop->value().c_str(), "%d", &x);
- set_remote_control_id_internal (x);
+ if (control_name == _gain_control->name()) {
+ _gain_control->set_state (*child, version);
+ } else if (control_name == _solo_control->name()) {
+ _solo_control->set_state (*child, version);
+ } else if (control_name == _solo_safe_control->name()) {
+ _solo_safe_control->set_state (*child, version);
+ } else if (control_name == _solo_isolate_control->name()) {
+ _solo_isolate_control->set_state (*child, version);
+ } else if (control_name == _mute_control->name()) {
+ _mute_control->set_state (*child, version);
+ } else if (control_name == _phase_control->name()) {
+ _phase_control->set_state (*child, version);
+ } else {
+ Evoral::Parameter p = EventTypeMap::instance().from_symbol (control_name);
+ if (p.type () >= MidiCCAutomation && p.type () < MidiSystemExclusiveAutomation) {
+ boost::shared_ptr<AutomationControl> ac = automation_control (p, true);
+ if (ac) {
+ ac->set_state (*child, version);
+ }
+ }
}
-
} else if (child->name() == MuteMaster::xml_node_name) {
_mute_master->set_state (*child, version);
return -1;
}
- if ((prop = node.property (X_("flags"))) != 0) {
- string f = prop->value ();
- boost::replace_all (f, "ControlOut", "MonitorOut");
- _flags = Flag (string_2_enum (f, _flags));
- } else {
- _flags = Flag (0);
- }
-
- if (is_master() || is_monitor() || is_auditioner()) {
- _mute_master->set_solo_ignore (true);
- }
+ Stripable::set_state (node, version);
if ((prop = node.property (X_("denormal-protection"))) != 0) {
- set_denormal_protection (string_is_affirmative (prop->value()));
+ set_denormal_protection (string_to<bool> (prop->value()));
}
if ((prop = node.property (X_("muted"))) != 0) {
bool first = true;
- bool muted = string_is_affirmative (prop->value());
+ bool muted = string_to<bool> (prop->value());
if (muted) {
if ((prop = node.property (X_("mute-affects-pre-fader"))) != 0) {
- if (string_is_affirmative (prop->value())){
+ if (string_to<bool> (prop->value())){
mute_point = mute_point + "PreFader";
first = false;
}
if ((prop = node.property (X_("mute-affects-post-fader"))) != 0) {
- if (string_is_affirmative (prop->value())){
+ if (string_to<bool> (prop->value())){
if (!first) {
mute_point = mute_point + ",";
if ((prop = node.property (X_("mute-affects-control-outs"))) != 0) {
- if (string_is_affirmative (prop->value())){
+ if (string_to<bool> (prop->value())){
if (!first) {
mute_point = mute_point + ",";
if ((prop = node.property (X_("mute-affects-main-outs"))) != 0) {
- if (string_is_affirmative (prop->value())){
+ if (string_to<bool> (prop->value())){
if (!first) {
mute_point = mute_point + ",";
_meter_point = MeterPoint (string_2_enum (prop->value (), _meter_point));
}
- /* do not carry over edit/mix groups from 2.X because (a) its hard (b) they
- don't mean the same thing.
- */
-
- if ((prop = node.property (X_("order-keys"))) != 0) {
-
- int32_t n;
-
- string::size_type colon, equal;
- string remaining = prop->value();
-
- while (remaining.length()) {
-
- if ((equal = remaining.find_first_of ('=')) == string::npos || equal == remaining.length()) {
- error << string_compose (_("badly formed order key string in state file! [%1] ... ignored."), remaining)
- << endmsg;
- } else {
- if (sscanf (remaining.substr (equal+1).c_str(), "%d", &n) != 1) {
- error << string_compose (_("badly formed order key string in state file! [%1] ... ignored."), remaining)
- << endmsg;
- } else {
- string keyname = remaining.substr (0, equal);
-
- if (keyname == "EditorSort" || keyname == "editor") {
- info << string_compose(_("Converting deprecated order key for %1 using Editor order %2"), name (), n) << endmsg;
- set_order_key (n);
- }
- }
- }
-
- colon = remaining.find_first_of (':');
-
- if (colon != string::npos) {
- remaining = remaining.substr (colon+1);
- } else {
- break;
- }
- }
- }
-
/* IOs */
nlist = node.children ();
set_id (*child);
if ((prop = child->property (X_("active"))) != 0) {
- bool yn = string_is_affirmative (prop->value());
+ bool yn = string_to<bool> (prop->value());
_active = !yn; // force switch
set_active (yn, this);
}
_mute_control->set_state (*child, version);
}
- } else if (child->name() == X_("RemoteControl")) {
- if ((prop = child->property (X_("id"))) != 0) {
- int32_t x;
- sscanf (prop->value().c_str(), "%d", &x);
- set_remote_control_id_internal (x);
- }
-
}
}
} else if (prop->value() == "ladspa" || prop->value() == "Ladspa" ||
prop->value() == "lv2" ||
prop->value() == "windows-vst" ||
+ prop->value() == "mac-vst" ||
prop->value() == "lxvst" ||
prop->value() == "luaproc" ||
prop->value() == "audiounit") {
{
Glib::Threads::Mutex::Lock lx (AudioEngine::instance()->process_lock ());
Glib::Threads::RWLock::WriterLock lm (_processor_lock);
+ /* re-assign _processors w/o process-lock.
+ * if there's an IO-processor present in _processors but
+ * not in new_order, it will be deleted and ~IO takes
+ * a process lock.
+ */
_processors = new_order;
if (must_configure) {
{
/* Must be called with the processor lock held */
+ const framepos_t now = _session.transport_frame ();
+
if (!_silent) {
_output->silence (nframes);
continue;
}
- (*i)->silence (nframes);
+ (*i)->silence (nframes, now);
}
if (nframes == _session.get_block_size()) {
{
Glib::Threads::Mutex::Lock lm (AudioEngine::instance()->process_lock ());
- boost::shared_ptr<Pannable> sendpan (new Pannable (_session));
- listener.reset (new InternalSend (_session, sendpan, _mute_master, boost::dynamic_pointer_cast<ARDOUR::Route>(shared_from_this()), route, Delivery::Aux));
+ listener.reset (new InternalSend (_session, _pannable, _mute_master, boost::dynamic_pointer_cast<ARDOUR::Route>(shared_from_this()), route, Delivery::Aux));
}
add_processor (listener, before);
if (iop != 0) {
boost::shared_ptr<const IO> iop_out = iop->output();
+ if (other.get() == this && iop_out && iop->input() && iop_out->connected_to (iop->input())) {
+ // TODO this needs a delaylines in the Insert to align connections (!)
+ DEBUG_TRACE (DEBUG::Graph, string_compose ("\tIOP %1 does feed its own return (%2)\n", iop->name(), other->name()));
+ continue;
+ }
if ((iop_out && other->all_inputs().fed_by (iop_out)) || iop->feeds (other)) {
DEBUG_TRACE (DEBUG::Graph, string_compose ("\tIOP %1 does feed %2\n", iop->name(), other->name()));
if (via_send_only) {
bool
Route::direct_feeds_according_to_graph (boost::shared_ptr<Route> other, bool* via_send_only)
{
- return _session._current_route_graph.has (shared_from_this (), other, via_send_only);
+ return _session._current_route_graph.has (boost::dynamic_pointer_cast<Route> (shared_from_this ()), other, via_send_only);
}
bool
Route::feeds_according_to_graph (boost::shared_ptr<Route> other)
{
- return _session._current_route_graph.feeds (shared_from_this (), other);
+ return _session._current_route_graph.feeds (boost::dynamic_pointer_cast<Route> (shared_from_this ()), other);
}
/** Called from the (non-realtime) butler thread when the transport is stopped */
continue;
}
bool sends_only;
- bool does_feed = (*i)->direct_feeds_according_to_reality (shared_from_this(), &sends_only);
+ bool does_feed = (*i)->direct_feeds_according_to_reality (boost::dynamic_pointer_cast<Route> (shared_from_this()), &sends_only);
if (does_feed && !sends_only) {
if ((*i)->soloed()) {
++sbou;
io_changed (); /* EMIT SIGNAL */
}
- if (_solo_control->soloed_by_others_downstream()) {
- int sbod = 0;
- /* checking all all downstream routes for
- * explicit of implict solo is a rather drastic measure,
- * ideally the input_change_handler() of the other route
- * would propagate the change to us.
+ if ((change.type & IOChange::ConnectionsChanged)) {
+
+ /* do this ONLY if connections have changed. Configuration
+ * changes do not, by themselves alter solo upstream or
+ * downstream status.
*/
- boost::shared_ptr<RouteList> routes = _session.get_routes ();
- if (_output->connected()) {
- for (RouteList::iterator i = routes->begin(); i != routes->end(); ++i) {
- if ((*i).get() == this || (*i)->is_master() || (*i)->is_monitor() || (*i)->is_auditioner()) {
- continue;
- }
- bool sends_only;
- bool does_feed = direct_feeds_according_to_reality (*i, &sends_only);
- if (does_feed && !sends_only) {
- if ((*i)->soloed()) {
- ++sbod;
- break;
+
+ if (_solo_control->soloed_by_others_downstream()) {
+ int sbod = 0;
+ /* checking all all downstream routes for
+ * explicit of implict solo is a rather drastic measure,
+ * ideally the input_change_handler() of the other route
+ * would propagate the change to us.
+ */
+ boost::shared_ptr<RouteList> routes = _session.get_routes ();
+ if (_output->connected()) {
+ for (RouteList::iterator i = routes->begin(); i != routes->end(); ++i) {
+ if ((*i).get() == this || (*i)->is_master() || (*i)->is_monitor() || (*i)->is_auditioner()) {
+ continue;
+ }
+ bool sends_only;
+ bool does_feed = direct_feeds_according_to_reality (*i, &sends_only);
+ if (does_feed && !sends_only) {
+ if ((*i)->soloed()) {
+ ++sbod;
+ break;
+ }
}
}
}
- }
- int delta = sbod - _solo_control->soloed_by_others_downstream();
- if (delta <= 0) {
- // do not allow new connections to change implicit solo (no propagation)
- _solo_control->mod_solo_by_others_downstream (delta);
- // Session::route_solo_changed() does not propagate indirect solo-changes
- // propagate upstream to tracks
- for (RouteList::iterator i = routes->begin(); i != routes->end(); ++i) {
- if ((*i).get() == this || !can_solo()) {
- continue;
- }
- bool sends_only;
- bool does_feed = (*i)->feeds (shared_from_this(), &sends_only);
- if (delta != 0 && does_feed && !sends_only) {
- (*i)->solo_control()->mod_solo_by_others_downstream (delta);
+
+ int delta = sbod - _solo_control->soloed_by_others_downstream();
+ if (delta <= 0) {
+ // do not allow new connections to change implicit solo (no propagation)
+ _solo_control->mod_solo_by_others_downstream (delta);
+ // Session::route_solo_changed() does not propagate indirect solo-changes
+ // propagate upstream to tracks
+ boost::shared_ptr<Route> shared_this = boost::dynamic_pointer_cast<Route> (shared_from_this());
+ for (RouteList::iterator i = routes->begin(); i != routes->end(); ++i) {
+ if ((*i).get() == this || !can_solo()) {
+ continue;
+ }
+ bool sends_only;
+ bool does_feed = (*i)->feeds (shared_this, &sends_only);
+ if (delta != 0 && does_feed && !sends_only) {
+ (*i)->solo_control()->mod_solo_by_others_downstream (delta);
+ }
}
- }
+ }
}
}
}
return max (n_inputs ().n_audio(), processor_max_streams.n_audio());
}
+void
+Route::flush_processor_buffers_locked (framecnt_t nframes)
+{
+ for (ProcessorList::iterator i = _processors.begin(); i != _processors.end(); ++i) {
+ boost::shared_ptr<Delivery> d = boost::dynamic_pointer_cast<Delivery> (*i);
+ if (d) {
+ d->flush_buffers (nframes);
+ } else {
+ boost::shared_ptr<PortInsert> p = boost::dynamic_pointer_cast<PortInsert> (*i);
+ if (p) {
+ p->flush_buffers (nframes);
+ }
+ }
+ }
+}
+
int
Route::no_roll (pframes_t nframes, framepos_t start_frame, framepos_t end_frame, bool session_state_changing)
{
return 0;
}
- if (n_outputs().n_total() == 0) {
- return 0;
- }
-
- if (!_active || n_inputs() == ChanCount::ZERO) {
+ if (!_active) {
silence_unlocked (nframes);
return 0;
}
fill_buffers_with_input (bufs, _input, nframes);
if (_meter_point == MeterInput) {
- _meter->run (bufs, start_frame, end_frame, nframes, true);
+ _meter->run (bufs, start_frame, end_frame, 0.0, nframes, true);
}
_amp->apply_gain_automation (false);
_trim->apply_gain_automation (false);
passthru (bufs, start_frame, end_frame, nframes, 0);
+ flush_processor_buffers_locked (nframes);
+
return 0;
}
return 0;
}
- if (n_outputs().n_total() == 0) {
- return 0;
- }
-
- if (!_active || n_inputs().n_total() == 0) {
+ if (!_active) {
silence_unlocked (nframes);
return 0;
}
fill_buffers_with_input (bufs, _input, nframes);
if (_meter_point == MeterInput) {
- _meter->run (bufs, start_frame, end_frame, nframes, true);
+ _meter->run (bufs, start_frame, end_frame, 1.0, nframes, true);
}
passthru (bufs, start_frame, end_frame, nframes, declick);
+ flush_processor_buffers_locked (nframes);
+
return 0;
}
Route::silent_roll (pframes_t nframes, framepos_t /*start_frame*/, framepos_t /*end_frame*/, bool& /* need_butler */)
{
silence (nframes);
+ flush_processor_buffers_locked (nframes);
return 0;
}
void
Route::flush_processors ()
{
- /* XXX shouldn't really try to take this lock, since
- this is called from the RT audio thread.
- */
-
Glib::Threads::RWLock::ReaderLock lm (_processor_lock);
for (ProcessorList::iterator i = _processors.begin(); i != _processors.end(); ++i) {
Glib::Threads::Mutex::Lock lx (AudioEngine::instance()->process_lock ());
Glib::Threads::RWLock::WriterLock lw (_processor_lock);
- _capturing_processor.reset (new CapturingProcessor (_session));
+ // this aligns all tracks; but not tracks + busses
+ assert (_session.worst_track_latency () >= _initial_delay);
+ _capturing_processor.reset (new CapturingProcessor (_session, _session.worst_track_latency () - _initial_delay));
_capturing_processor->activate ();
configure_processors_unlocked (0, &lw);
bool
Route::set_name (const string& str)
{
+ if (str.empty ()) {
+ return false;
+ }
+
if (str == name()) {
return true;
}
void
Route::set_name_in_state (XMLNode& node, string const & name, bool rename_playlist)
{
- node.add_property (X_("name"), name);
+ node.set_property (X_("name"), name);
XMLNodeList children = node.children();
for (XMLNodeIterator i = children.begin(); i != children.end(); ++i) {
} else if ((*i)->name() == X_("Processor")) {
- XMLProperty const * role = (*i)->property (X_("role"));
- if (role && role->value() == X_("Main")) {
- (*i)->add_property (X_("name"), name);
+ std::string str;
+ if ((*i)->get_property (X_("role"), str) && str == X_("Main")) {
+ (*i)->set_property (X_("name"), name);
}
} else if ((*i)->name() == X_("Diskstream")) {
if (rename_playlist) {
- (*i)->add_property (X_("playlist"), string_compose ("%1.1", name).c_str());
+ (*i)->set_property (X_("playlist"), name + ".1");
}
- (*i)->add_property (X_("name"), name);
+ (*i)->set_property (X_("name"), name);
}
}
_active = yn;
_input->set_active (yn);
_output->set_active (yn);
+ flush_processors ();
active_changed (); // EMIT SIGNAL
_session.set_dirty ();
}
new_processors.insert (amp, _monitor_control);
}
+ /* TRIMÂ CONTROL */
+
+ if (_trim && _trim->active()) {
+ assert (!_trim->display_to_user ());
+ new_processors.push_front (_trim);
+ }
+
/* INTERNAL RETURN */
- /* doing this here means that any monitor control will come just after
- the return.
+ /* doing this here means that any monitor control will come after
+ the return and trim.
*/
if (_intreturn) {
new_processors.push_front (_intreturn);
}
- if (_trim && _trim->active()) {
- assert (!_trim->display_to_user ());
- new_processors.push_front (_trim);
- }
/* EXPORT PROCESSOR */
if (_capturing_processor) {
_processors = new_processors;
for (ProcessorList::iterator i = _processors.begin(); i != _processors.end(); ++i) {
- if (!(*i)->display_to_user () && !(*i)->active () && (*i) != _monitor_send) {
- (*i)->activate ();
+ if (!(*i)->display_to_user () && !(*i)->enabled () && (*i) != _monitor_send) {
+ (*i)->enable (true);
}
}
Route::pan_azimuth_control() const
{
#ifdef MIXBUS
+# undef MIXBUS_PORTS_H
+# include "../../gtk2_ardour/mixbus_ports.h"
boost::shared_ptr<ARDOUR::PluginInsert> plug = ch_post();
if (!plug) {
return boost::shared_ptr<AutomationControl>();
}
- const uint32_t port_channel_post_pan = 2; // gtk2_ardour/mixbus_ports.h
return boost::dynamic_pointer_cast<ARDOUR::AutomationControl> (plug->control (Evoral::Parameter (ARDOUR::PluginAutomation, 0, port_channel_post_pan)));
#else
if (!_pannable || !panner()) {
boost::shared_ptr<AutomationControl>
Route::pan_width_control() const
{
+#ifdef MIXBUS
+ if (mixbus() && _ch_pre) {
+ //mono blend
+ return boost::dynamic_pointer_cast<ARDOUR::AutomationControl>(_ch_pre->control(Evoral::Parameter(PluginAutomation, 0, 5)));
+ }
+#endif
if (Profile->get_mixbus() || !_pannable || !panner()) {
return boost::shared_ptr<AutomationControl>();
}
Route::eq_band_cnt () const
{
if (Profile->get_mixbus()) {
+#ifdef MIXBUS32C
+ if (is_master() || mixbus()) {
+ return 3;
+ } else {
+ return 4;
+ }
+#else
return 3;
+#endif
} else {
/* Ardour has no well-known EQ object */
return 0;
}
uint32_t port_number;
- switch (band) {
- case 0:
- if (is_master() || mixbus()) {
- port_number = 4;
- } else {
- port_number = 8;
+ if (is_master() || mixbus()) {
+ switch (band) {
+ case 0: port_number = 4; break;
+ case 1: port_number = 3; break;
+ case 2: port_number = 2; break;
+ default:
+ return boost::shared_ptr<AutomationControl>();
}
- break;
- case 1:
- if (is_master() || mixbus()) {
- port_number = 3;
- } else {
- port_number = 6;
+ } else {
+#ifdef MIXBUS32C
+ switch (band) {
+ case 0: port_number = 14; break;
+ case 1: port_number = 12; break;
+ case 2: port_number = 10; break;
+ case 3: port_number = 8; break;
+ default:
+ return boost::shared_ptr<AutomationControl>();
}
- break;
- case 2:
- if (is_master() || mixbus()) {
- port_number = 2;
- } else {
- port_number = 4;
+#else
+ switch (band) {
+ case 0: port_number = 8; break;
+ case 1: port_number = 6; break;
+ case 2: port_number = 4; break;
+ default:
+ return boost::shared_ptr<AutomationControl>();
}
- break;
- default:
- return boost::shared_ptr<AutomationControl>();
+#endif
}
return boost::dynamic_pointer_cast<ARDOUR::AutomationControl> (eq->control (Evoral::Parameter (ARDOUR::PluginAutomation, 0, port_number)));
Route::eq_freq_controllable (uint32_t band) const
{
#ifdef MIXBUS
-
if (mixbus() || is_master()) {
/* no frequency controls for mixbusses or master */
return boost::shared_ptr<AutomationControl>();
}
uint32_t port_number;
+#ifdef MIXBUS32C
switch (band) {
- case 0:
- port_number = 7;
- break;
- case 1:
- port_number = 5;
- break;
- case 2:
- port_number = 3;
- break;
- default:
- return boost::shared_ptr<AutomationControl>();
+ case 0: port_number = 13; break; // lo
+ case 1: port_number = 11; break; // lo mid
+ case 2: port_number = 9; break; // hi mid
+ case 3: port_number = 7; break; // hi
+ default:
+ return boost::shared_ptr<AutomationControl>();
+ }
+#else
+ switch (band) {
+ case 0: port_number = 7; break;
+ case 1: port_number = 5; break;
+ case 2: port_number = 3; break;
+ default:
+ return boost::shared_ptr<AutomationControl>();
}
+#endif
return boost::dynamic_pointer_cast<ARDOUR::AutomationControl> (eq->control (Evoral::Parameter (ARDOUR::PluginAutomation, 0, port_number)));
#else
boost::shared_ptr<AutomationControl>
Route::eq_shape_controllable (uint32_t band) const
{
+#ifdef MIXBUS32C
+ boost::shared_ptr<PluginInsert> eq = ch_eq();
+ if (is_master() || mixbus() || !eq) {
+ return boost::shared_ptr<AutomationControl>();
+ }
+ switch (band) {
+ case 0:
+ return boost::dynamic_pointer_cast<ARDOUR::AutomationControl> (eq->control (Evoral::Parameter (ARDOUR::PluginAutomation, 0, 4))); // lo bell
+ break;
+ case 3:
+ return boost::dynamic_pointer_cast<ARDOUR::AutomationControl> (eq->control (Evoral::Parameter (ARDOUR::PluginAutomation, 0, 3))); // hi bell
+ break;
+ default:
+ break;
+ }
+#endif
return boost::shared_ptr<AutomationControl>();
}
}
boost::shared_ptr<AutomationControl>
-Route::eq_hpf_controllable () const
+Route::filter_freq_controllable (bool hpf) const
{
#ifdef MIXBUS
boost::shared_ptr<PluginInsert> eq = ch_eq();
- if (!eq) {
+ if (is_master() || mixbus() || !eq) {
+ return boost::shared_ptr<AutomationControl>();
+ }
+ if (hpf) {
+#ifdef MIXBUS32C
+ return boost::dynamic_pointer_cast<ARDOUR::AutomationControl> (eq->control (Evoral::Parameter (ARDOUR::PluginAutomation, 0, 5))); // HPF freq
+#else
+ return boost::dynamic_pointer_cast<ARDOUR::AutomationControl> (eq->control (Evoral::Parameter (ARDOUR::PluginAutomation, 0, 2)));
+#endif
+ } else {
+#ifdef MIXBUS32C
+ return boost::dynamic_pointer_cast<ARDOUR::AutomationControl> (eq->control (Evoral::Parameter (ARDOUR::PluginAutomation, 0, 6))); // LPF freq
+#else
+ return boost::shared_ptr<AutomationControl>();
+#endif
+ }
+
+#else
+ return boost::shared_ptr<AutomationControl>();
+#endif
+}
+
+boost::shared_ptr<AutomationControl>
+Route::filter_slope_controllable (bool) const
+{
+ return boost::shared_ptr<AutomationControl>();
+}
+
+boost::shared_ptr<AutomationControl>
+Route::filter_enable_controllable (bool) const
+{
+#ifdef MIXBUS32C
+ boost::shared_ptr<PluginInsert> eq = ch_eq();
+
+ if (is_master() || mixbus() || !eq) {
return boost::shared_ptr<AutomationControl>();
}
string
Route::eq_band_name (uint32_t band) const
{
+#ifdef MIXBUS32C
+ if (is_master() || mixbus()) {
+#endif
if (Profile->get_mixbus()) {
switch (band) {
- case 0:
- return _("lo");
- case 1:
- return _("mid");
- case 2:
- return _("hi");
- default:
- return string();
+ case 0: return _("lo");
+ case 1: return _("mid");
+ case 2: return _("hi");
+ default: return string();
}
} else {
return string ();
}
+#ifdef MIXBUS32C
+ } else {
+ switch (band) {
+ case 0: return _("lo");
+ case 1: return _("lo mid");
+ case 2: return _("hi mid");
+ case 3: return _("hi");
+ default: return string();
+ }
+ }
+#endif
}
boost::shared_ptr<AutomationControl>
return boost::shared_ptr<AutomationControl>();
#endif
}
-boost::shared_ptr<AutomationControl>
+boost::shared_ptr<ReadOnlyControl>
Route::comp_redux_controllable () const
{
#ifdef MIXBUS
boost::shared_ptr<PluginInsert> comp = ch_comp();
if (!comp) {
- return boost::shared_ptr<AutomationControl>();
+ return boost::shared_ptr<ReadOnlyControl>();
+ }
+ if (is_master()) {
+ return comp->control_output (2);
+ } else {
+ return comp->control_output (6);
}
- return boost::dynamic_pointer_cast<ARDOUR::AutomationControl> (comp->control (Evoral::Parameter (ARDOUR::PluginAutomation, 0, 6)));
#else
- return boost::shared_ptr<AutomationControl>();
+ return boost::shared_ptr<ReadOnlyControl>();
#endif
}
Route::send_level_controllable (uint32_t n) const
{
#ifdef MIXBUS
+# undef MIXBUS_PORTS_H
+# include "../../gtk2_ardour/mixbus_ports.h"
boost::shared_ptr<ARDOUR::PluginInsert> plug = ch_post();
- if (!plug) {
- return boost::shared_ptr<AutomationControl>();
- }
+ if (plug) {
+ uint32_t port_id = 0;
+ switch (n) {
+ case 0: port_id = port_channel_post_aux1_level; break;
+ case 1: port_id = port_channel_post_aux2_level; break;
+ case 2: port_id = port_channel_post_aux3_level; break;
+ case 3: port_id = port_channel_post_aux4_level; break;
+ case 4: port_id = port_channel_post_aux5_level; break;
+ case 5: port_id = port_channel_post_aux6_level; break;
+ case 6: port_id = port_channel_post_aux7_level; break;
+ case 7: port_id = port_channel_post_aux8_level; break;
+# ifdef MIXBUS32C
+ case 8: port_id = port_channel_post_aux9_level; break;
+ case 9: port_id = port_channel_post_aux10_level; break;
+ case 10: port_id = port_channel_post_aux11_level; break;
+ case 11: port_id = port_channel_post_aux12_level; break;
+# endif
+ default:
+ break;
+ }
- if (n >= 8) {
- /* no such bus */
- return boost::shared_ptr<AutomationControl>();
+ if (port_id > 0) {
+ return boost::dynamic_pointer_cast<ARDOUR::AutomationControl> (plug->control (Evoral::Parameter (ARDOUR::PluginAutomation, 0, port_id)));
+ }
+# ifdef MIXBUS32C
+ assert (n > 11);
+ n -= 12;
+# else
+ assert (n > 7);
+ n -= 8;
+# endif
}
-
- const uint32_t port_id = port_channel_post_aux1_level + (2*n); // gtk2_ardour/mixbus_ports.h
- return boost::dynamic_pointer_cast<ARDOUR::AutomationControl> (plug->control (Evoral::Parameter (ARDOUR::PluginAutomation, 0, port_id)));
-#else
+#endif
boost::shared_ptr<Send> s = boost::dynamic_pointer_cast<Send>(nth_send (n));
if (!s) {
return boost::shared_ptr<AutomationControl>();
}
return s->gain_control ();
-#endif
}
boost::shared_ptr<AutomationControl>
Route::send_enable_controllable (uint32_t n) const
{
#ifdef MIXBUS
+# undef MIXBUS_PORTS_H
+# include "../../gtk2_ardour/mixbus_ports.h"
boost::shared_ptr<ARDOUR::PluginInsert> plug = ch_post();
- if (!plug) {
- return boost::shared_ptr<AutomationControl>();
- }
+ if (plug) {
+ uint32_t port_id = 0;
+ switch (n) {
+ case 0: port_id = port_channel_post_aux1_asgn; break;
+ case 1: port_id = port_channel_post_aux2_asgn; break;
+ case 2: port_id = port_channel_post_aux3_asgn; break;
+ case 3: port_id = port_channel_post_aux4_asgn; break;
+ case 4: port_id = port_channel_post_aux5_asgn; break;
+ case 5: port_id = port_channel_post_aux6_asgn; break;
+ case 6: port_id = port_channel_post_aux7_asgn; break;
+ case 7: port_id = port_channel_post_aux8_asgn; break;
+# ifdef MIXBUS32C
+ case 8: port_id = port_channel_post_aux9_asgn; break;
+ case 9: port_id = port_channel_post_aux10_asgn; break;
+ case 10: port_id = port_channel_post_aux11_asgn; break;
+ case 11: port_id = port_channel_post_aux12_asgn; break;
+# endif
+ default:
+ break;
+ }
- if (n >= 8) {
- /* no such bus */
- return boost::shared_ptr<AutomationControl>();
+ if (port_id > 0) {
+ return boost::dynamic_pointer_cast<ARDOUR::AutomationControl> (plug->control (Evoral::Parameter (ARDOUR::PluginAutomation, 0, port_id)));
+ }
+# ifdef MIXBUS32C
+ assert (n > 11);
+ n -= 12;
+# else
+ assert (n > 7);
+ n -= 8;
+# endif
}
-
- const uint32_t port_id = port_channel_post_aux1_asgn + (2*n); // gtk2_ardour/mixbus_ports.h
- return boost::dynamic_pointer_cast<ARDOUR::AutomationControl> (plug->control (Evoral::Parameter (ARDOUR::PluginAutomation, 0, port_id)));
-#else
+#endif
/* although Ardour sends have enable/disable as part of the Processor
- API, it is not exposed as a controllable.
-
- XXX: we should fix this.
- */
+ * API, it is not exposed as a controllable.
+ *
+ * XXX: we should fix this (make it click-free, automatable enable-control)
+ */
return boost::shared_ptr<AutomationControl>();
-#endif
}
string
Route::send_name (uint32_t n) const
{
#ifdef MIXBUS
- if (n >= 8) {
- return string();
+# ifdef MIXBUS32C
+ if (n < 12) {
+ return _session.get_mixbus (n)->name();
}
- boost::shared_ptr<Route> r = _session.get_mixbus (n);
- assert (r);
- return r->name();
+ n -= 12;
#else
+ if (n < 8) {
+ return _session.get_mixbus (n)->name();
+ }
+ n -= 8;
+# endif
+#endif
boost::shared_ptr<Processor> p = nth_send (n);
if (p) {
return p->name();
} else {
return string();
}
-#endif
}
boost::shared_ptr<AutomationControl>
if (!plug) {
return boost::shared_ptr<AutomationControl>();
}
+# undef MIXBUS_PORTS_H
+# include "../../gtk2_ardour/mixbus_ports.h"
return boost::dynamic_pointer_cast<ARDOUR::AutomationControl> (plug->control (Evoral::Parameter (ARDOUR::PluginAutomation, 0, port_channel_post_mstr_assign)));
#else
return boost::shared_ptr<AutomationControl>();
}
bool
-Route::slaved_to (boost::shared_ptr<VCA> vca) const
+Route::slaved () const
{
- if (!vca || !_gain_control) {
+ if (!_gain_control) {
return false;
}
-
/* just test one particular control, not all of them */
-
- return _gain_control->slaved_to (vca->gain_control());
-}
-
-int
-Route::assign_controls (boost::shared_ptr<VCA> vca)
-{
- _gain_control->add_master (vca->gain_control());
- _solo_control->add_master (vca->solo_control());
- _mute_control->add_master (vca->mute_control());
-
- return 0;
+ return _gain_control->slaved ();
}
-int
-Route::unassign_controls (boost::shared_ptr<VCA> vca)
+bool
+Route::slaved_to (boost::shared_ptr<VCA> vca) const
{
- if (!vca) {
- /* unassign from all */
- _gain_control->clear_masters ();
- _solo_control->clear_masters ();
- _mute_control->clear_masters ();
- } else {
- _gain_control->remove_master (vca->gain_control());
- _solo_control->remove_master (vca->solo_control());
- _mute_control->remove_master (vca->mute_control());
+ if (!vca || !_gain_control) {
+ return false;
}
- return 0;
+ /* just test one particular control, not all of them */
+
+ return _gain_control->slaved_to (vca->gain_control());
}
bool
return false;
}
- return _session.soloing() && !_solo_control->soloed() && !_solo_isolate_control->solo_isolated();
+ return _session.soloing() && !_solo_control->soloed() && !_solo_isolate_control->solo_isolated();
}
void
Route::clear_all_solo_state ()
{
- double v = _solo_safe_control->get_value ();
-
_solo_control->clear_all_solo_state ();
+}
- if (v != 0.0) {
- _solo_safe_control->set_value (v, Controllable::NoGroup);
+boost::shared_ptr<AutomationControl>
+Route::automation_control_recurse (PBD::ID const & id) const
+{
+ boost::shared_ptr<AutomationControl> ac = Automatable::automation_control (id);
+
+ if (ac) {
+ return ac;
}
+
+ Glib::Threads::RWLock::ReaderLock lm (_processor_lock);
+
+ for (ProcessorList::const_iterator i = _processors.begin(); i != _processors.end(); ++i) {
+ if ((ac = (*i)->automation_control (id))) {
+ return ac;
+ }
+ }
+
+ return boost::shared_ptr<AutomationControl> ();
}