#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/panner.h"
#include "ardour/panner_shell.h"
#include "ardour/parameter_descriptor.h"
+#include "ardour/phase_control.h"
#include "ardour/plugin_insert.h"
#include "ardour/port.h"
#include "ardour/port_insert.h"
#include "ardour/route_group.h"
#include "ardour/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)
- : Stripable (sess, name)
- , Automatable (sess)
+Route::Route (Session& sess, string name, PresentationInfo::Flag flag, DataType default_type)
+ : Stripable (sess, name, PresentationInfo (flag))
, GraphNode (sess._process_graph)
+ , Muteable (sess, name)
, _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)
, _meter_type (MeterPeak)
- , _self_solo (false)
- , _soloed_by_others_upstream (0)
- , _soloed_by_others_downstream (0)
- , _solo_isolated (false)
- , _solo_isolated_by_upstream (0)
, _denormal_protection (false)
, _recordable (true)
, _silent (false)
, _declickable (false)
- , _mute_master (new MuteMaster (sess, name))
, _have_internal_generator (false)
- , _solo_safe (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 standard controls */
- _solo_control.reset (new SoloControllable (X_("solo"), shared_from_this ()));
- _mute_control.reset (new MuteControllable (X_("mute"), shared_from_this ()));
- _phase_control.reset (new PhaseControllable (X_("phase"), shared_from_this ()));
-
- _solo_isolate_control.reset (new SoloIsolateControllable (X_("solo-iso"), shared_from_this ()));
- _solo_safe_control.reset (new SoloSafeControllable (X_("solo-safe"), shared_from_this ()));
+ _gain_control.reset (new GainControl (_session, GainAutomation));
+ add_control (_gain_control);
- _solo_control->set_flags (Controllable::Flag (_solo_control->flags() | Controllable::Toggle));
- _mute_control->set_flags (Controllable::Flag (_mute_control->flags() | Controllable::Toggle));
- _phase_control->set_flags (Controllable::Flag (_phase_control->flags() | Controllable::Toggle));
+ _trim_control.reset (new GainControl (_session, TrimAutomation));
+ add_control (_trim_control);
+ _solo_control.reset (new SoloControl (_session, X_("solo"), *this, *this));
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));
add_control (_mute_control);
+
+ _phase_control.reset (new PhaseControl (_session, X_("phase")));
add_control (_phase_control);
+ _solo_isolate_control.reset (new SoloIsolateControl (_session, X_("solo-iso"), *this, *this));
+ add_control (_solo_isolate_control);
+
+ _solo_safe_control.reset (new SoloSafeControl (_session, X_("solo-safe")));
+ add_control (_solo_safe_control);
+
/* panning */
- if (!(_flags & Route::MonitorOut)) {
+ if (!(_presentation_info.flags() & PresentationInfo::MonitorOut)) {
_pannable.reset (new Pannable (_session));
}
* it should be the first processor to be added on every route.
*/
- _gain_control = boost::shared_ptr<GainControllable> (new GainControllable (_session, GainAutomation, shared_from_this ()));
- add_control (_gain_control);
-
_amp.reset (new Amp (_session, X_("Fader"), _gain_control, true));
add_processor (_amp, PostFader);
/* and input trim */
- _trim_control = boost::shared_ptr<GainControllable> (new GainControllable (_session, TrimAutomation, shared_from_this ()));
- add_control (_trim_control);
-
_trim.reset (new Amp (_session, X_("Trim"), _trim_control, false));
_trim->set_display_to_user (false);
_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 newname;
}
-void
-Route::inc_gain (gain_t factor)
-{
- /* To be used ONLY when doing group-relative gain adjustment, from
- * ::set_gain()
- */
-
- float desired_gain = _gain_control->user_double();
-
- if (fabsf (desired_gain) < GAIN_COEFF_SMALL) {
- // really?! what's the idea here?
- _gain_control->route_set_value (0.000001f + (0.000001f * factor));
- } else {
- _gain_control->route_set_value (desired_gain + (desired_gain * factor));
- }
-}
-
-void
-Route::set_gain (gain_t val, Controllable::GroupControlDisposition group_override)
-{
- if (use_group (group_override, &RouteGroup::is_gain)) {
-
- if (_route_group->is_relative()) {
-
- gain_t usable_gain = _gain_control->get_value();
- if (usable_gain < 0.000001f) {
- usable_gain = 0.000001f;
- }
-
- gain_t delta = val;
- if (delta < 0.000001f) {
- delta = 0.000001f;
- }
-
- delta -= usable_gain;
-
- if (delta == 0.0f)
- return;
-
- gain_t factor = delta / usable_gain;
-
- if (factor > 0.0f) {
- factor = _route_group->get_max_factor(factor);
- if (factor == 0.0f) {
- _amp->gain_control()->Changed(); /* EMIT SIGNAL */
- return;
- }
- } else {
- factor = _route_group->get_min_factor(factor);
- if (factor == 0.0f) {
- _amp->gain_control()->Changed(); /* EMIT SIGNAL */
- return;
- }
- }
-
- _route_group->foreach_route (boost::bind (&Route::inc_gain, _1, factor));
-
- } else {
-
- _route_group->foreach_route (boost::bind (&Route::set_gain, _1, val, Controllable::NoGroup));
- }
-
- return;
- }
-
- if (val == _gain_control->get_value()) {
- return;
- }
-
- _gain_control->route_set_value (val);
-}
-
void
Route::set_trim (gain_t val, Controllable::GroupControlDisposition /* group override */)
{
// TODO route group, see set_gain()
- _trim_control->route_set_value (val);
+ // _trim_control->route_set_value (val);
}
void
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 ());
DENORMAL CONTROL/PHASE INVERT
----------------------------------------------------------------------------------------- */
- if (_phase_invert.any ()) {
+ if (!_phase_control->none()) {
int chn = 0;
for (BufferSet::audio_iterator i = bufs.audio_begin(); i != bufs.audio_end(); ++i, ++chn) {
Sample* const sp = i->data();
- if (_phase_invert[chn]) {
+ if (_phase_control->inverted (chn)) {
for (pframes_t nx = 0; nx < nframes; ++nx) {
sp[nx] = -sp[nx];
sp[nx] += 1.0e-27f;
for (BufferSet::audio_iterator i = bufs.audio_begin(); i != bufs.audio_end(); ++i, ++chn) {
Sample* const sp = i->data();
- if (_phase_invert[chn]) {
+ if (_phase_control->inverted (chn)) {
for (pframes_t nx = 0; nx < nframes; ++nx) {
sp[nx] = -sp[nx];
}
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 ();
}
}
void
-Route::set_listen (bool yn, Controllable::GroupControlDisposition group_override)
+Route::set_listen (bool yn)
{
- if (_solo_safe) {
- return;
- }
-
- if (use_group (group_override, &RouteGroup::is_solo)) {
- _route_group->foreach_route (boost::bind (&Route::set_listen, _1, yn, Controllable::ForGroup));
- return;
- }
-
if (_monitor_send) {
- if (yn != _monitor_send->active()) {
- if (yn) {
- _monitor_send->activate ();
- _mute_master->set_soloed_by_self (true);
- } else {
- _monitor_send->deactivate ();
- _mute_master->set_soloed_by_self (false);
- }
- _mute_master->set_soloed_by_others (false);
-
- listen_changed (group_override); /* EMIT SIGNAL */
- }
- }
-}
-
-bool
-Route::listening_via_monitor () const
-{
- if (_monitor_send) {
- return _monitor_send->active ();
- } else {
- return false;
- }
-}
-
-void
-Route::set_solo_safe (bool yn, Controllable::GroupControlDisposition /* group_override */)
-{
- if (_solo_safe != yn) {
- _solo_safe = yn;
- solo_safe_changed (); /* EMIT SIGNAL */
- _solo_safe_control->Changed(); /* EMIT SIGNAL */
- }
-}
-
-bool
-Route::solo_safe() const
-{
- return _solo_safe;
-}
-
-void
-Route::clear_all_solo_state ()
-{
- // ideally this function will never do anything, it only exists to forestall Murphy
- bool emit_changed = false;
-
-#ifndef NDEBUG
- // these are really debug messages, but of possible interest.
- if (_self_solo) {
- PBD::info << string_compose (_("Cleared Explicit solo: %1\n"), name());
- }
- if (_soloed_by_others_upstream || _soloed_by_others_downstream) {
- PBD::info << string_compose (_("Cleared Implicit solo: %1 up:%2 down:%3\n"),
- name(), _soloed_by_others_upstream, _soloed_by_others_downstream);
- }
-#endif
-
- if (!_self_solo && (_soloed_by_others_upstream || _soloed_by_others_downstream)) {
- // if self-soled, set_solo() will do signal emission
- emit_changed = true;
- }
-
- _soloed_by_others_upstream = 0;
- _soloed_by_others_downstream = 0;
-
- {
- PBD::Unwinder<bool> uw (_solo_safe, false);
- set_solo (false, Controllable::NoGroup);
- }
-
- if (emit_changed) {
- set_mute_master_solo ();
- solo_changed (false, Controllable::UseGroup); /* EMIT SIGNAL */
- }
-}
-
-void
-Route::set_solo (bool yn, Controllable::GroupControlDisposition group_override)
-{
- DEBUG_TRACE (DEBUG::Solo, string_compose ("%1: set solo => %2, grp ? %3 currently self-soloed ? %4\n",
- name(), yn, enum_2_string(group_override), self_soloed()));
-
- if (_solo_safe) {
- DEBUG_TRACE (DEBUG::Solo, string_compose ("%1 ignore solo change due to solo-safe\n", name()));
- return;
- }
-
- if (is_master() || is_monitor() || is_auditioner()) {
- DEBUG_TRACE (DEBUG::Solo, string_compose ("%1 ignore solo change (master, monitor or auditioner)\n", name()));
- return;
- }
-
- if (use_group (group_override, &RouteGroup::is_solo)) {
- _route_group->foreach_route (boost::bind (&Route::set_solo, _1, yn, Controllable::ForGroup));
- return;
- }
-
- if (self_soloed() != yn) {
- set_self_solo (yn);
- solo_changed (true, group_override); /* EMIT SIGNAL */
- _solo_control->Changed (); /* EMIT SIGNAL */
- }
-
- assert (Config->get_solo_control_is_listen_control() || !_monitor_send || !_monitor_send->active());
-
- /* XXX TRACKS DEVELOPERS: THIS LOGIC SUGGESTS THAT YOU ARE NOT AWARE OF
- Config->get_solo_mute_overrride().
- */
-
- if (yn && Profile->get_trx()) {
- set_mute (false, Controllable::UseGroup);
- }
-}
-
-void
-Route::set_self_solo (bool yn)
-{
- DEBUG_TRACE (DEBUG::Solo, string_compose ("%1: set SELF solo => %2\n", name(), yn));
- _self_solo = yn;
- set_mute_master_solo ();
-}
-
-void
-Route::mod_solo_by_others_upstream (int32_t delta)
-{
- DEBUG_TRACE (DEBUG::Solo, string_compose ("%1 mod solo-by-upstream by %2, current up = %3 down = %4\n",
- name(), delta, _soloed_by_others_upstream, _soloed_by_others_downstream));
-
- uint32_t old_sbu = _soloed_by_others_upstream;
-
- if (delta < 0) {
- if (_soloed_by_others_upstream >= (uint32_t) abs (delta)) {
- _soloed_by_others_upstream += delta;
- } else {
- _soloed_by_others_upstream = 0;
- }
- } else {
- _soloed_by_others_upstream += delta;
- }
-
- DEBUG_TRACE (DEBUG::Solo, string_compose (
- "%1 SbU delta %2 = %3 old = %4 sbd %5 ss %6 exclusive %7\n",
- name(), delta, _soloed_by_others_upstream, old_sbu,
- _soloed_by_others_downstream, _self_solo, Config->get_exclusive_solo()));
-
- /* push the inverse solo change to everything that feeds us.
-
- This is important for solo-within-group. When we solo 1 track out of N that
- feed a bus, that track will cause mod_solo_by_upstream (+1) to be called
- on the bus. The bus then needs to call mod_solo_by_downstream (-1) on all
- tracks that feed it. This will silence them if they were audible because
- of a bus solo, but the newly soloed track will still be audible (because
- it is self-soloed).
-
- but .. do this only when we are being told to solo-by-upstream (i.e delta = +1),
- not in reverse.
- */
-
- if ((_self_solo || _soloed_by_others_downstream) &&
- ((old_sbu == 0 && _soloed_by_others_upstream > 0) ||
- (old_sbu > 0 && _soloed_by_others_upstream == 0))) {
-
- if (delta > 0 || !Config->get_exclusive_solo()) {
- DEBUG_TRACE (DEBUG::Solo, string_compose("\t ... INVERT push from %1\n", _name));
- for (FedBy::iterator i = _fed_by.begin(); i != _fed_by.end(); ++i) {
- if (i->sends_only) {
- continue;
- }
- boost::shared_ptr<Route> sr = i->r.lock();
- if (sr) {
- sr->mod_solo_by_others_downstream (-delta);
- }
- }
+ if (_monitor_send->active() == yn) {
+ return;
}
- }
-
- set_mute_master_solo ();
- solo_changed (false, Controllable::UseGroup); /* EMIT SIGNAL */
-}
-
-void
-Route::mod_solo_by_others_downstream (int32_t delta)
-{
- DEBUG_TRACE (DEBUG::Solo, string_compose ("%1 mod solo-by-downstream by %2, current up = %3 down = %4\n",
- name(), delta, _soloed_by_others_upstream, _soloed_by_others_downstream));
-
- if (delta < 0) {
- if (_soloed_by_others_downstream >= (uint32_t) abs (delta)) {
- _soloed_by_others_downstream += delta;
+ if (yn) {
+ _monitor_send->activate ();
} else {
- _soloed_by_others_downstream = 0;
+ _monitor_send->deactivate ();
}
- } else {
- _soloed_by_others_downstream += delta;
}
-
- DEBUG_TRACE (DEBUG::Solo, string_compose ("%1 SbD delta %2 = %3\n", name(), delta, _soloed_by_others_downstream));
-
- set_mute_master_solo ();
- solo_changed (false, Controllable::UseGroup); /* EMIT SIGNAL */
-}
-
-void
-Route::set_mute_master_solo ()
-{
- _mute_master->set_soloed_by_self (self_soloed());
- _mute_master->set_soloed_by_others (soloed_by_others_downstream() || soloed_by_others_upstream());
}
void
-Route::mod_solo_isolated_by_upstream (bool yn)
+Route::solo_control_changed (bool, Controllable::GroupControlDisposition)
{
- bool old = solo_isolated ();
- DEBUG_TRACE (DEBUG::Solo, string_compose ("%1 mod_solo_isolated_by_upstream cur: %2 d: %3\n",
- name(), _solo_isolated_by_upstream, yn ? "+1" : "-1"));
-
- if (!yn) {
- if (_solo_isolated_by_upstream >= 1) {
- _solo_isolated_by_upstream--;
- } else {
- _solo_isolated_by_upstream = 0;
- }
- } else {
- _solo_isolated_by_upstream++;
- }
+ /* nothing to do if we're not using AFL/PFL. But if we are, we need
+ to alter the active state of the monitor send.
+ */
- if (solo_isolated() != old) {
- /* solo isolated status changed */
- _mute_master->set_solo_ignore (solo_isolated());
- solo_isolated_changed (); /* EMIT SIGNAL */
+ if (Config->get_solo_control_is_listen_control ()) {
+ set_listen (_solo_control->self_soloed() || _solo_control->get_masters_value());
}
}
void
-Route::set_solo_isolated (bool yn, Controllable::GroupControlDisposition group_override)
+Route::push_solo_isolate_upstream (int32_t delta)
{
- if (is_master() || is_monitor() || is_auditioner()) {
- return;
- }
-
- if (use_group (group_override, &RouteGroup::is_solo)) {
- _route_group->foreach_route (boost::bind (&Route::set_solo_isolated, _1, yn, Controllable::ForGroup));
- return;
- }
-
- bool changed = false;
-
- if (yn) {
- if (_solo_isolated == false) {
- _mute_master->set_solo_ignore (true);
- changed = true;
- }
- _solo_isolated = true;
- } else {
- if (_solo_isolated == true) {
- _solo_isolated = false;
- _mute_master->set_solo_ignore (false);
- changed = true;
- }
- }
-
-
- if (!changed) {
- return;
- }
-
/* forward propagate solo-isolate status to everything fed by this route, but not those via sends only */
boost::shared_ptr<RouteList> routes = _session.get_routes ();
for (RouteList::iterator i = routes->begin(); i != routes->end(); ++i) {
- if ((*i).get() == this || (*i)->is_master() || (*i)->is_monitor() || (*i)->is_auditioner()) {
+ if ((*i).get() == this || !(*i)->can_solo()) {
continue;
}
bool does_feed = feeds (*i, &sends_only);
if (does_feed && !sends_only) {
- (*i)->mod_solo_isolated_by_upstream (yn);
+ (*i)->solo_isolate_control()->mod_solo_isolated_by_upstream (delta);
}
}
-
- /* XXX should we back-propagate as well? (April 2010: myself and chris goddard think not) */
-
- solo_isolated_changed (); /* EMIT SIGNAL */
- _solo_isolate_control->Changed(); /* EMIT SIGNAL */
-}
-
-bool
-Route::solo_isolated () const
-{
- return (_solo_isolated == true) || (_solo_isolated_by_upstream > 0);
}
void
-Route::set_mute_points (MuteMaster::MutePoint mp)
+Route::push_solo_upstream (int delta)
{
- _mute_master->set_mute_points (mp);
- mute_points_changed (); /* EMIT SIGNAL */
-
- if (_mute_master->muted_by_self()) {
- mute_changed (); /* EMIT SIGNAL */
- _mute_control->Changed (); /* EMIT SIGNAL */
- }
-}
-
-void
-Route::set_mute (bool yn, Controllable::GroupControlDisposition group_override)
-{
- if (use_group (group_override, &RouteGroup::is_mute)) {
- _route_group->foreach_route (boost::bind (&Route::set_mute, _1, yn, Controllable::ForGroup));
- return;
- }
-
- if (muted() != yn) {
- _mute_master->set_muted_by_self (yn);
- /* allow any derived classes to respond to the mute change
- before anybody else knows about it.
- */
- act_on_mute ();
- /* tell everyone else */
- mute_changed (); /* EMIT SIGNAL */
- _mute_control->Changed (); /* EMIT SIGNAL */
+ DEBUG_TRACE (DEBUG::Solo, string_compose("\t ... INVERT push from %1\n", _name));
+ for (FedBy::iterator i = _fed_by.begin(); i != _fed_by.end(); ++i) {
+ if (i->sends_only) {
+ continue;
+ }
+ boost::shared_ptr<Route> sr (i->r.lock());
+ if (sr) {
+ sr->solo_control()->mod_solo_by_others_downstream (-delta);
+ }
}
}
-bool
-Route::muted () const
-{
- return _mute_master->muted_by_self();
-}
-
-bool
-Route::muted_by_others () const
-{
- // This method is only used by route_ui for display state.
- // The real thing is MuteMaster::muted_by_others_at()
-
- //master is never muted by others
- if (is_master())
- return false;
-
- //now check to see if something is soloed (and I am not)
- //see also MuteMaster::mute_gain_at()
- return (_session.soloing() && !soloed() && !solo_isolated());
-}
-
#if 0
static void
dump_processors(const string& name, const list<boost::shared_ptr<Processor> >& procs)
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;
- boost::to_string (_phase_invert, p);
- node->add_property("phase-invert", 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_property ("self-solo", (_self_solo ? "yes" : "no"));
- snprintf (buf, sizeof (buf), "%d", _soloed_by_others_upstream);
- node->add_property ("soloed-by-upstream", buf);
- snprintf (buf, sizeof (buf), "%d", _soloed_by_others_downstream);
- node->add_property ("soloed-by-downstream", buf);
- node->add_property ("solo-isolated", solo_isolated() ? "yes" : "no");
- node->add_property ("solo-safe", _solo_safe ? "yes" : "no");
+ 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 (_input->state (full_state));
node->add_child_nocopy (_output->state (full_state));
- node->add_child_nocopy (_solo_control->get_state ());
- node->add_child_nocopy (_mute_control->get_state ());
node->add_child_nocopy (_mute_master->get_state ());
+ node->add_child_nocopy (_mute_control->get_state ());
+ node->add_child_nocopy (_phase_control->get_state ());
+
if (full_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());
}
}
foreach_processor (sigc::bind (sigc::mem_fun (*this, &Route::set_plugin_state_dir), ""));
}
+ node->add_child_copy (Slavable::get_state());
+
return *node;
}
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 (is_master() || is_monitor() || is_auditioner()) {
- _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);
}
- }
- if (child->name() == X_("Processor")) {
+ } else if (child->name() == X_("Processor")) {
processor_state.add_child_copy (*child);
- }
-
- if (child->name() == X_("Pannable")) {
+ } else if (child->name() == X_("Pannable")) {
if (_pannable) {
_pannable->set_state (*child, version);
} else {
warning << string_compose (_("Pannable state found for route (%1) without a panner!"), name()) << endmsg;
}
+ } 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 ("self-solo")) != 0) {
- set_self_solo (string_is_affirmative (prop->value()));
- }
-
- if ((prop = node.property ("soloed-by-upstream")) != 0) {
- _soloed_by_others_upstream = 0; // needed for mod_.... () to work
- mod_solo_by_others_upstream (atoi (prop->value()));
- }
-
- if ((prop = node.property ("soloed-by-downstream")) != 0) {
- _soloed_by_others_downstream = 0; // needed for mod_.... () to work
- mod_solo_by_others_downstream (atoi (prop->value()));
- }
-
- if ((prop = node.property ("solo-isolated")) != 0) {
- set_solo_isolated (string_is_affirmative (prop->value()), Controllable::NoGroup);
+ bool denormal_protection;
+ if (node.get_property (X_("denormal-protection"), denormal_protection)) {
+ set_denormal_protection (denormal_protection);
}
- if ((prop = node.property ("solo-safe")) != 0) {
- set_solo_safe (string_is_affirmative (prop->value()), Controllable::NoGroup);
+ /* 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_("phase-invert"))) != 0) {
- set_phase_invert (boost::dynamic_bitset<> (prop->value ()));
- }
-
- 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);
- }
-
- if ((prop = node.property (X_("order-key"))) != 0) { // New order key (no separate mixer/editor ordering)
- set_order_key (atoi(prop->value()));
- }
-
- 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() == X_("MuteMaster")) {
+ } else if (child->name() == MuteMaster::xml_node_name) {
_mute_master->set_state (*child, version);
} else if (child->name() == Automatable::xml_node_name) {
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);
- }
-
- if ((prop = node.property (X_("phase-invert"))) != 0) {
- boost::dynamic_bitset<> p (_input->n_ports().n_audio ());
- if (string_is_affirmative (prop->value ())) {
- p.set ();
- }
- set_phase_invert (p);
- }
-
- if ((prop = node.property (X_("denormal-protection"))) != 0) {
- set_denormal_protection (string_is_affirmative (prop->value()));
- }
-
- if ((prop = node.property (X_("soloed"))) != 0) {
- bool yn = string_is_affirmative (prop->value());
-
- /* XXX force reset of solo status */
+ Stripable::set_state (node, version);
- set_solo (yn);
+ if ((prop = node.property (X_("denormal-protection"))) != 0) {
+ 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 */
contains ConfigurationChanged
*/
configure_processors (0);
- _phase_invert.resize (_input->n_ports().n_audio ());
+ _phase_control->resize (_input->n_ports().n_audio ());
io_changed (); /* EMIT SIGNAL */
}
- if (_soloed_by_others_upstream || _solo_isolated_by_upstream) {
+ if (_solo_control->soloed_by_others_upstream() || _solo_isolate_control->solo_isolated_by_upstream()) {
int sbou = 0;
int ibou = 0;
boost::shared_ptr<RouteList> routes = _session.get_routes ();
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;
}
- if ((*i)->solo_isolated()) {
+ if ((*i)->solo_isolate_control()->solo_isolated()) {
++ibou;
}
}
}
}
- int delta = sbou - _soloed_by_others_upstream;
- int idelta = ibou - _solo_isolated_by_upstream;
+ int delta = sbou - _solo_control->soloed_by_others_upstream();
+ int idelta = ibou - _solo_isolate_control->solo_isolated_by_upstream();
if (idelta < -1) {
PBD::warning << string_compose (
_("Invalid Solo-Isolate propagation: from:%1 new:%2 - old:%3 = delta:%4"),
- _name, ibou, _solo_isolated_by_upstream, idelta)
+ _name, ibou, _solo_isolate_control->solo_isolated_by_upstream(), idelta)
<< endmsg;
}
- if (_soloed_by_others_upstream) {
+ if (_solo_control->soloed_by_others_upstream()) {
// ignore new connections (they're not propagated)
if (delta <= 0) {
- mod_solo_by_others_upstream (delta);
+ _solo_control->mod_solo_by_others_upstream (delta);
}
}
- if (_solo_isolated_by_upstream) {
+ if (_solo_isolate_control->solo_isolated_by_upstream()) {
// solo-isolate currently only propagates downstream
if (idelta < 0) {
- mod_solo_isolated_by_upstream (false);
+ _solo_isolate_control->mod_solo_isolated_by_upstream (1);
}
- // TODO think: mod_solo_isolated_by_upstream() does not take delta arg,
- // but idelta can't be smaller than -1, can it?
//_solo_isolated_by_upstream = ibou;
}
bool sends_only;
bool does_feed = feeds (*i, &sends_only);
if (delta <= 0 && does_feed && !sends_only) {
- (*i)->mod_solo_by_others_upstream (delta);
+ (*i)->solo_control()->mod_solo_by_others_upstream (delta);
}
if (idelta < 0 && does_feed && !sends_only) {
- (*i)->mod_solo_isolated_by_upstream (false);
+ (*i)->solo_isolate_control()->mod_solo_isolated_by_upstream (-1);
}
}
}
io_changed (); /* EMIT SIGNAL */
}
- if (_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 - _soloed_by_others_downstream;
- if (delta <= 0) {
- // do not allow new connections to change implicit solo (no propagation)
- 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 || (*i)->is_master() || (*i)->is_monitor() || (*i)->is_auditioner()) {
- continue;
- }
- bool sends_only;
- bool does_feed = (*i)->feeds (shared_from_this(), &sends_only);
- if (delta != 0 && does_feed && !sends_only) {
- (*i)->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);
}
}
return boost::shared_ptr<Send>();
}
-/** @param c Audio channel index.
- * @param yn true to invert phase, otherwise false.
- */
-void
-Route::set_phase_invert (uint32_t c, bool yn)
-{
- if (_phase_invert[c] != yn) {
- _phase_invert[c] = yn;
- phase_invert_changed (); /* EMIT SIGNAL */
- _phase_control->Changed(); /* EMIT SIGNAL */
- _session.set_dirty ();
- }
-}
-
-void
-Route::set_phase_invert (boost::dynamic_bitset<> p)
-{
- if (_phase_invert != p) {
- _phase_invert = p;
- phase_invert_changed (); /* EMIT SIGNAL */
- _session.set_dirty ();
- }
-}
-
-bool
-Route::phase_invert (uint32_t c) const
-{
- return _phase_invert[c];
-}
-
-boost::dynamic_bitset<>
-Route::phase_invert () const
-{
- return _phase_invert;
-}
-
void
Route::set_denormal_protection (bool yn)
{
_active = yn;
_input->set_active (yn);
_output->set_active (yn);
+ flush_processors ();
active_changed (); // EMIT SIGNAL
_session.set_dirty ();
}
return _gain_control;
}
-boost::shared_ptr<AutomationControl>
+boost::shared_ptr<GainControl>
Route::trim_control() const
{
return _trim_control;
}
-boost::shared_ptr<AutomationControl>
+boost::shared_ptr<PhaseControl>
Route::phase_control() const
{
- if (phase_invert().size()) {
- return _phase_control;
- } else {
- return boost::shared_ptr<PhaseControllable>();
- }
+ return _phase_control;
}
boost::shared_ptr<AutomationControl>
return false;
}
-MuteMaster::MutePoint
-Route::mute_points () const
-{
- return _mute_master->mute_points ();
-}
-
void
Route::set_processor_positions ()
{
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 6;
+ }
+#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 2: port_number = 14; break;
+ case 3: port_number = 12; break;
+ case 4: port_number = 10; break;
+ case 5: 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 = 5; break; // HPF
+ case 1: port_number = 6; break; // LPF
+ case 2: port_number = 13; break; // lo
+ case 3: port_number = 11; break; // lo mid
+ case 4: port_number = 9; break; // hi mid
+ case 5: 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
#ifdef MIXBUS
boost::shared_ptr<PluginInsert> eq = ch_eq();
- if (!eq) {
+ if (is_master() || mixbus() || !eq) {
+ return boost::shared_ptr<AutomationControl>();
+ }
+#ifdef MIXBUS32C
+ return boost::dynamic_pointer_cast<ARDOUR::AutomationControl> (eq->control (Evoral::Parameter (ARDOUR::PluginAutomation, 0, 5)));
+#else
+ return boost::dynamic_pointer_cast<ARDOUR::AutomationControl> (eq->control (Evoral::Parameter (ARDOUR::PluginAutomation, 0, 2)));
+#endif
+
+#else
+ return boost::shared_ptr<AutomationControl>();
+#endif
+}
+
+boost::shared_ptr<AutomationControl>
+Route::eq_lpf_controllable () const
+{
+#ifdef MIXBUS32C
+ boost::shared_ptr<PluginInsert> eq = ch_eq();
+
+ if (is_master() || mixbus() || !eq) {
+ return boost::shared_ptr<AutomationControl>();
+ }
+
+ return boost::dynamic_pointer_cast<ARDOUR::AutomationControl> (eq->control (Evoral::Parameter (ARDOUR::PluginAutomation, 0, 4)));
+#else
+ return boost::shared_ptr<AutomationControl>();
+#endif
+}
+
+boost::shared_ptr<AutomationControl>
+Route::filter_enable_controllable () 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 _("HPF");
+ case 1: return _("LPF");
+ case 2: return _("lo");
+ case 3: return _("lo mid");
+ case 4: return _("hi mid");
+ case 5: 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>();
#endif
}
+bool
+Route::slaved () const
+{
+ if (!_gain_control) {
+ return false;
+ }
+ /* just test one particular control, not all of them */
+ return _gain_control->slaved ();
+}
+
bool
Route::slaved_to (boost::shared_ptr<VCA> vca) const
{
return false;
}
- return _gain_control->slaved_to (vca);
+ /* just test one particular control, not all of them */
+
+ return _gain_control->slaved_to (vca->gain_control());
+}
+
+bool
+Route::muted_by_others_soloing () const
+{
+ if (!can_be_muted_by_others ()) {
+ return false;
+ }
+
+ return _session.soloing() && !_solo_control->soloed() && !_solo_isolate_control->solo_isolated();
+}
+
+void
+Route::clear_all_solo_state ()
+{
+ _solo_control->clear_all_solo_state ();
+}
+
+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> ();
}