#endif
#include <cmath>
-#include <fstream>
#include <cassert>
#include <algorithm>
, _initial_delay (0)
, _roll_delay (0)
, _pending_process_reorder (0)
+ , _pending_signals (0)
, _flags (flg)
, _pending_declick (true)
, _meter_point (MeterPostFader)
void
Route::set_remote_control_id_internal (uint32_t id, bool notify_class_listeners)
{
- /* force IDs for master/monitor busses and prevent
+ /* force IDs for master/monitor busses and prevent
any other route from accidentally getting these IDs
(i.e. legacy sessions)
*/
/* don't allow it to collide */
- if (!is_master () && !is_monitor() &&
+ if (!is_master () && !is_monitor() &&
(id == MasterBusRemoteControlID || id == MonitorBusRemoteControlID)) {
id += MonitorBusRemoteControlID;
}
{
if (is_master()) {
return MasterBusRemoteControlID;
- }
+ }
if (is_monitor()) {
return MonitorBusRemoteControlID;
string newname = name;
while (!session.io_name_is_legal (newname)) {
- newname = bump_name_once (newname, '.');
+ newname = bump_name_once (newname, ' ');
}
return newname;
/* Tell main outs what to do about monitoring. We do this so that
on a transition between monitoring states we get a de-clicking gain
- change in the _main_outs delivery.
- */
- bool silence = monitoring_state () == MonitoringSilence;
+ change in the _main_outs delivery, if config.get_use_monitor_fades()
+ is true.
- //but we override this in the case where we have an internal generator
- if ( _have_internal_generator )
- silence = false;
+ We override this in the case where we have an internal generator.
+ */
+ bool silence = _have_internal_generator ? false : (monitoring_state () == MonitoringSilence);
_main_outs->no_outs_cuz_we_no_monitor (silence);
if (yn != _monitor_send->active()) {
if (yn) {
_monitor_send->activate ();
- _mute_master->set_soloed (true);
+ _mute_master->set_soloed_by_self (true);
} else {
_monitor_send->deactivate ();
- _mute_master->set_soloed (false);
+ _mute_master->set_soloed_by_self (false);
}
+ _mute_master->set_soloed_by_others (false);
listen_changed (src); /* EMIT SIGNAL */
}
return;
}
- DEBUG_TRACE (DEBUG::Solo, string_compose ("%1: set solo => %2, src: %3 grp ? %4 currently self-soloed ? %5\n",
+ DEBUG_TRACE (DEBUG::Solo, string_compose ("%1: set solo => %2, src: %3 grp ? %4 currently self-soloed ? %5\n",
name(), yn, src, (src == _route_group), self_soloed()));
if (self_soloed() != yn) {
void
Route::mod_solo_by_others_upstream (int32_t delta)
{
- if (_solo_safe) {
- DEBUG_TRACE (DEBUG::Solo, string_compose ("%1 ignore solo-by-upstream due to solo-safe\n", name()));
- return;
- }
-
- DEBUG_TRACE (DEBUG::Solo, string_compose ("%1 mod solo-by-upstream by %2, current up = %3 down = %4\n",
+ 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;
(old_sbu > 0 && _soloed_by_others_upstream == 0))) {
if (delta > 0 || !Config->get_exclusive_solo()) {
- DEBUG_TRACE (DEBUG::Solo, "\t ... INVERT push\n");
+ 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);
void
Route::mod_solo_by_others_downstream (int32_t delta)
{
- if (_solo_safe) {
- DEBUG_TRACE (DEBUG::Solo, string_compose ("%1 ignore solo-by-downstream due to solo safe\n", name()));
- return;
- }
-
- DEBUG_TRACE (DEBUG::Solo, string_compose ("%1 mod solo-by-downstream by %2, current up = %3 down = %4\n",
+ 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) {
void
Route::set_mute_master_solo ()
{
- _mute_master->set_soloed (self_soloed() || soloed_by_others_downstream() || soloed_by_others_upstream());
+ _mute_master->set_soloed_by_self (self_soloed());
+ _mute_master->set_soloed_by_others (soloed_by_others_downstream() || soloed_by_others_upstream());
}
void
}
}
-
+
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()) {
continue;
}
-
+
bool sends_only;
bool does_feed = feeds (*i, &sends_only);
-
+
if (does_feed && !sends_only) {
(*i)->mod_solo_isolated_by_upstream (yn, src);
}
}
-
+
/* XXX should we back-propagate as well? (April 2010: myself and chris goddard think not) */
solo_isolated_changed (src);
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)
- return (_session.soloing() && !self_soloed() && !solo_isolated());
+ //see also MuteMaster::mute_gain_at()
+ return (_session.soloing() && !soloed() && !solo_isolated());
}
#if 0
Glib::Threads::RWLock::ReaderLock lm (_processor_lock);
ProcessorList::iterator loc;
-
+
if (p == PreFader) {
/* generic pre-fader: insert immediately before the amp */
loc = find (_processors.begin(), _processors.end(), _amp);
}
Glib::Threads::RWLock::ReaderLock lm (_processor_lock);
-
+
ProcessorList::iterator i = _processors.begin ();
int j = 0;
while (i != _processors.end() && j < index) {
if ((*i)->display_to_user()) {
++j;
}
-
+
++i;
}
}
- if (activation_allowed && (!_session.get_disable_all_loaded_plugins () || !processor->display_to_user ())) {
+ if (activation_allowed && (!_session.get_bypass_all_loaded_plugins () || !processor->display_to_user ())) {
processor->activate ();
}
prop->value() == "lxvst" ||
prop->value() == "audiounit") {
- processor.reset (new PluginInsert (_session));
+ if (_session.get_disable_all_loaded_plugins ()) {
+ processor.reset (new UnknownProcessor (_session, node));
+ } else {
+ processor.reset (new PluginInsert (_session));
+ }
} else {
if (processor->set_state (node, version)) {
return false;
}
-
+
//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_disable_all_loaded_plugins () || !processor->display_to_user () ) )
+ if ( string_is_affirmative (prop->value()) && (!_session.get_bypass_all_loaded_plugins () || !processor->display_to_user () ) )
processor->activate();
else
processor->deactivate();
if (!removed) {
/* what? */
return 1;
- }
+ }
if (configure_processors_unlocked (err)) {
pstate.restore ();
for (ProcessorList::iterator p = _processors.begin(); p != _processors.end(); ++p, ++index) {
- if (boost::dynamic_pointer_cast<UnknownProcessor> (*p)) {
- DEBUG_TRACE (DEBUG::Processors, "--- CONFIGURE ABORTED due to unknown processor.\n");
- DEBUG_TRACE (DEBUG::Processors, "}\n");
- return list<pair<ChanCount, ChanCount> > ();
- }
-
if ((*p)->can_support_io_configuration(in, out)) {
DEBUG_TRACE (DEBUG::Processors, string_compose ("\t%1 ID=%2 in=%3 out=%4\n",(*p)->name(), (*p)->id(), in, out));
configuration.push_back(make_pair(in, out));
list< pair<ChanCount,ChanCount> >::iterator c = configuration.begin();
for (ProcessorList::iterator p = _processors.begin(); p != _processors.end(); ++p, ++c) {
- if (boost::dynamic_pointer_cast<UnknownProcessor> (*p)) {
- break;
+ if (!(*p)->configure_io(c->first, c->second)) {
+ DEBUG_TRACE (DEBUG::Processors, string_compose ("%1: configuration failed\n", _name));
}
-
- (*p)->configure_io(c->first, c->second);
processor_max_streams = ChanCount::max(processor_max_streams, c->first);
processor_max_streams = ChanCount::max(processor_max_streams, c->second);
}
/* make sure we have sufficient scratch buffers to cope with the new processor
- configuration
+ configuration
*/
_session.ensure_buffers (n_process_buffers ());
if (_processors.empty()) {
return;
}
-
+
for (ProcessorList::iterator i = _processors.begin(); i != _processors.end(); ++i) {
if (!(*i)->display_to_user() || boost::dynamic_pointer_cast<Amp> (*i)) {
continue;
}
-
+
if (state) {
(*i)->activate ();
} else {
for (i = _processors.begin(); i != _processors.end(); ++i) {
if (!full_state) {
- /* template save: do not include internal sends functioning as
+ /* template save: do not include internal sends functioning as
aux sends because the chance of the target ID
in the session where this template is used
is not very likely.
prop->value() == "lxvst" ||
prop->value() == "audiounit") {
- processor.reset (new PluginInsert(_session));
-
+ if (_session.get_disable_all_loaded_plugins ()) {
+ processor.reset (new UnknownProcessor (_session, **niter));
+ } else {
+ processor.reset (new PluginInsert (_session));
+ }
} else if (prop->value() == "port") {
processor.reset (new PortInsert (_session, _pannable, _mute_master));
again:
for (ProcessorList::iterator x = _processors.begin(); x != _processors.end(); ++x) {
-
+
boost::shared_ptr<InternalSend> d = boost::dynamic_pointer_cast<InternalSend>(*x);
-
+
if (d && d->target_route() == route) {
rl.release ();
- remove_processor (*x, &err, false);
+ if (remove_processor (*x, &err, false) > 0) {
+ rl.acquire ();
+ continue;
+ }
rl.acquire ();
/* list could have been demolished while we dropped the lock
so start over.
*/
-
- goto again;
+ if (_session.engine().connected()) {
+ /* i/o processors cannot be removed if the engine is not running
+ * so don't live-loop in case the engine is N/A or dies
+ */
+ goto again;
+ }
}
}
}
bool need_to_queue_solo_change = true;
if ((change.type & IOChange::ConfigurationChanged)) {
- /* This is called with the process lock held if change
- contains ConfigurationChanged
+ /* This is called with the process lock held if change
+ contains ConfigurationChanged
*/
need_to_queue_solo_change = false;
configure_processors (0);
} else {
cancel_solo_after_disconnect (true);
}
+#if 1
+ } else if (_soloed_by_others_upstream) {
+ bool cancel_solo = true;
+ 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()) {
+ continue;
+ }
+ bool sends_only;
+ bool does_feed = (*i)->direct_feeds_according_to_reality (shared_from_this(), &sends_only);
+ if (does_feed && !sends_only) {
+ if ((*i)->soloed()) {
+ cancel_solo = false;
+ break;
+ }
+ }
+ }
+ if (cancel_solo) {
+ cancel_solo_after_disconnect (true);
+ }
+#else
+ } else if (self_soloed()) {
+#endif
+ // TODO propagate upstream
+ // see commment in output_change_handler() below
}
}
}
if ((change.type & IOChange::ConfigurationChanged)) {
- /* This is called with the process lock held if change
- contains ConfigurationChanged
+ /* This is called with the process lock held if change
+ contains ConfigurationChanged
*/
need_to_queue_solo_change = false;
configure_processors (0);
} else {
cancel_solo_after_disconnect (false);
}
+#if 1
+ } else if (_soloed_by_others_downstream) {
+ bool cancel_solo = true;
+ /* 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 ();
+ 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()) {
+ cancel_solo = false;
+ break;
+ }
+ }
+ }
+ if (cancel_solo) {
+ cancel_solo_after_disconnect (false);
+ }
+#else
+ } else if (self_soloed()) {
+ // TODO propagate change downstream to the disconnected routes
+ // Q: how to get the routes that were just disconnected. ?
+ // A: /maybe/ by diff feeds() aka fed_by() vs direct_feeds_according_to_reality() ?!?
+#endif
}
}
/* Set up the meter for its new position */
ProcessorList::iterator loc = find (_processors.begin(), _processors.end(), _meter);
-
+
ChanCount m_in;
-
+
if (loc == _processors.begin()) {
m_in = _input->n_ports();
} else {
XMLNodeList children = node.children();
for (XMLNodeIterator i = children.begin(); i != children.end(); ++i) {
-
+
if ((*i)->name() == X_("IO")) {
IO::set_name_in_state (**i, name);
if (role && role->value() == X_("Main")) {
(*i)->add_property (X_("name"), name);
}
-
+
} else if ((*i)->name() == X_("Diskstream")) {
(*i)->add_property (X_("playlist"), string_compose ("%1.1", name).c_str());
(*i)->add_property (X_("name"), name);
-
+
}
}
}
{
list<string> p;
+ if (_session.get_disable_all_loaded_plugins ()) {
+ // Do not list "missing plugins" if they are explicitly disabled
+ return p;
+ }
+
Glib::Threads::RWLock::ReaderLock lm (_processor_lock);
for (ProcessorList::const_iterator i = _processors.begin(); i != _processors.end(); ++i) {
if (boost::dynamic_pointer_cast<UnknownProcessor const> (*i)) {
} else {
all_connections.min = ~((pframes_t) 0);
all_connections.max = 0;
-
+
/* iterate over all "from" ports and determine the latency range for all of their
connections to the "outside" (outside of this Route).
*/
-
+
for (PortSet::iterator p = from.begin(); p != from.end(); ++p) {
-
+
LatencyRange range;
-
+
p->get_connected_latency_range (range, playback);
-
+
all_connections.min = min (all_connections.min, range.min);
all_connections.max = max (all_connections.max, range.max);
}
/* we'll build this new list here and then use it
*
- * TODO put the ProcessorList is on the stack for RT-safety.
+ * TODO put the ProcessorList is on the stack for RT-safety.
*/
ProcessorList new_processors;
_processors = new_processors;
for (ProcessorList::iterator i = _processors.begin(); i != _processors.end(); ++i) {
- if (!(*i)->display_to_user () && !(*i)->active ()) {
+ if (!(*i)->display_to_user () && !(*i)->active () && (*i) != _monitor_send) {
(*i)->activate ();
}
}
if (_meter_point != MeterCustom) {
return;
}
-
+
_custom_meter_position_noted = true;
/* custom meter points range from after trim to before panner/main_outs
* this is a limitation by the current processor UI
/* ignore inactive processors and obviously ignore the main
* outs since everything has them and we don't care.
*/
-
+
if ((*i)->active() && (*i) != _main_outs && (*i)->does_routing()) {
return true;;
}
{
//Glib::Threads::Mutex::Lock lx (AudioEngine::instance()->process_lock ());
Glib::Threads::RWLock::ReaderLock lm (_processor_lock);
-
+
for (ProcessorList::iterator i = _processors.begin(); i != _processors.end(); ++i) {
(*i)->transport_located (pos);
}
}
+ _roll_delay = _initial_delay;
}
void
{
size_t n_buffers;
size_t i;
-
- /* MIDI
- *
+
+ /* MIDI
+ *
* We don't currently mix MIDI input together, so we don't need the
* complex logic of the audio case.
*/
boost::shared_ptr<MidiPort> source_port = io->midi (i);
MidiBuffer& buf (bufs.get_midi (i));
-
+
if (source_port) {
buf.copy (source_port->get_midi_buffer(nframes));
} else {
if (n_ports > n_buffers) {
scaling = ((float) n_buffers) / n_ports;
}
-
+
for (i = 0; i < n_ports; ++i) {
-
+
/* if there are more ports than buffers, map them onto buffers
* in a round-robin fashion
*/
boost::shared_ptr<AudioPort> source_port = io->audio (i);
AudioBuffer& buf (bufs.get_audio (i%n_buffers));
-
+
if (i < n_buffers) {
-
+
/* first time through just copy a channel into
the output buffer.
*/
if (scaling != 1.0f) {
buf.apply_gain (scaling, nframes);
}
-
+
} else {
-
+
/* on subsequent times around, merge data from
- * the port with what is already there
+ * the port with what is already there
*/
if (scaling != 1.0f) {