#include <cassert>
#include <algorithm>
-#include <sigc++/bind.h>
#include "pbd/xml++.h"
#include "pbd/enumwriter.h"
#include "pbd/memento_command.h"
+#include "pbd/stacktrace.h"
#include "evoral/Curve.hpp"
using namespace PBD;
uint32_t Route::order_key_cnt = 0;
-sigc::signal<void, string const &> Route::SyncOrderKeys;
+PBD::Signal1<void,string const&> Route::SyncOrderKeys;
+PBD::Signal0<void> Route::RemoteControlIDChange;
Route::Route (Session& sess, string name, Flag flg, DataType default_type)
: SessionObject (sess, name)
, AutomatableControls (sess)
, _flags (flg)
, _solo_control (new SoloControllable (X_("solo"), *this))
+ , _mute_control (new MuteControllable (X_("mute"), *this))
, _mute_master (new MuteMaster (sess, name))
, _default_type (default_type)
/* add standard processors other than amp (added by ::init()) */
_meter.reset (new PeakMeter (_session));
- _meter->set_display_to_user (_meter_point == MeterCustom);
- add_processor (_meter, PreFader);
+ _meter->set_display_to_user (false);
+ add_processor (_meter, PostFader);
if (_flags & ControlOut) {
/* where we listen to tracks */
/* now that we have _meter, its safe to connect to this */
- _meter_connection = Metering::connect (mem_fun (*this, &Route::meter));
+ Metering::Meter.connect_same_thread (*this, (boost::bind (&Route::meter, this)));
}
Route::Route (Session& sess, const XMLNode& node, DataType default_type)
: SessionObject (sess, "toBeReset")
, AutomatableControls (sess)
, _solo_control (new SoloControllable (X_("solo"), *this))
+ , _mute_control (new MuteControllable (X_("mute"), *this))
, _mute_master (new MuteMaster (sess, "toBeReset"))
, _default_type (default_type)
{
/* now that we have _meter, its safe to connect to this */
- _meter_connection = Metering::connect (mem_fun (*this, &Route::meter));
+ Metering::Meter.connect_same_thread (*this, (boost::bind (&Route::meter, this)));
}
void
{
_self_solo = false;
_soloed_by_others = 0;
- _solo_isolated = false;
+ _solo_isolated = 0;
_solo_safe = false;
_active = true;
processor_max_streams.reset();
_in_configure_processors = false;
_mute_points = MuteMaster::AllPoints;
- _route_group = 0;
-
_phase_invert = 0;
_denormal_protection = false;
/* add standard controls */
+ _solo_control->set_flags (Controllable::Flag (_solo_control->flags() | Controllable::Toggle));
+ _mute_control->set_flags (Controllable::Flag (_solo_control->flags() | Controllable::Toggle));
+
add_control (_solo_control);
- add_control (_mute_master);
+ add_control (_mute_control);
/* input and output objects */
_input.reset (new IO (_session, _name, IO::Input, _default_type));
_output.reset (new IO (_session, _name, IO::Output, _default_type));
- _input->changed.connect (mem_fun (this, &Route::input_change_handler));
- _output->changed.connect (mem_fun (this, &Route::output_change_handler));
+ _input->changed.connect_same_thread (*this, boost::bind (&Route::input_change_handler, this, _1, _2));
+ _output->changed.connect_same_thread (*this, boost::bind (&Route::output_change_handler, this, _1, _2));
/* add amp processor */
Route::~Route ()
{
DEBUG_TRACE (DEBUG::Destruction, string_compose ("route %1 destructor\n", _name));
- Metering::disconnect (_meter_connection);
+
+ /* do this early so that we don't get incoming signals as we are going through destruction
+ */
+
+ drop_connections ();
/* don't use clear_processors here, as it depends on the session which may
- be half-destroyed by now */
+ be half-destroyed by now
+ */
Glib::RWLock::WriterLock lm (_processor_lock);
for (ProcessorList::iterator i = _processors.begin(); i != _processors.end(); ++i) {
}
void
-Route::set_remote_control_id (uint32_t id)
+Route::set_remote_control_id (uint32_t id, bool notify_class_listeners)
{
if (id != _remote_control_id) {
_remote_control_id = id;
RemoteControlIDChanged ();
+ if (notify_class_listeners) {
+ RemoteControlIDChange ();
+ }
}
}
assert (bufs.count() == (*i)->input_streams());
(*i)->run (bufs, start_frame, end_frame, nframes, *i != _processors.back());
- bufs.set_count (ChanCount::max(bufs.count(), (*i)->output_streams()));
- }
-
- if (!_processors.empty()) {
- bufs.set_count (ChanCount::max (bufs.count(), _processors.back()->output_streams()));
+ bufs.set_count ((*i)->output_streams());
}
}
}
void
Route::set_solo_isolated (bool yn, void *src)
{
+ if (is_master() || is_control() || is_hidden()) {
+ return;
+ }
+
if (_route_group && src != _route_group && _route_group->active_property (RouteGroup::Solo)) {
_route_group->apply (&Route::set_solo_isolated, yn, _route_group);
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) {
+ bool sends_only;
+ bool does_feed = feeds (*i, &sends_only);
+
+ if (does_feed && !sends_only) {
+ (*i)->set_solo_isolated (yn, (*i)->route_group());
+ }
+ }
- if (yn != _solo_isolated) {
- _solo_isolated = yn;
+ bool changed = false;
+
+ if (yn) {
+ if (_solo_isolated == 0) {
+ changed = true;
+ }
+ _solo_isolated++;
+ } else {
+ changed = (_solo_isolated == 1);
+ if (_solo_isolated > 0) {
+ _solo_isolated--;
+ }
+ }
+
+ if (changed) {
set_delivery_solo ();
solo_isolated_changed (src);
}
bool
Route::solo_isolated () const
{
- return _solo_isolated;
+ return _solo_isolated > 0;
}
void
// XXX: do we want to emit the signal here ? change call order.
processor->activate ();
}
- processor->ActiveChanged.connect (bind (mem_fun (_session, &Session::update_latency_compensation), false, false));
+
+ processor->ActiveChanged.connect_same_thread (*this, boost::bind (&Session::update_latency_compensation, &_session, false, false));
_output->set_user_latency (0);
}
- processors_changed (); /* EMIT SIGNAL */
+ processors_changed (RouteProcessorChange ()); /* EMIT SIGNAL */
return 0;
}
{
Glib::RWLock::WriterLock lm (_processor_lock);
- ProcessorList::iterator existing_end = _processors.end();
- --existing_end;
ChanCount potential_max_streams = ChanCount::max (_input->n_ports(), _output->n_ports());
}
}
- _processors.insert (iter, *i);
+ ProcessorList::iterator inserted = _processors.insert (iter, *i);
+
+ if ((*i)->active()) {
+ (*i)->activate ();
+ }
if (configure_processors_unlocked (err)) {
- ++existing_end;
- _processors.erase (existing_end, _processors.end());
+ _processors.erase (inserted);
configure_processors_unlocked (0); // it worked before we tried to add it ...
return -1;
}
- (*i)->ActiveChanged.connect (bind (mem_fun (_session, &Session::update_latency_compensation), false, false));
+ (*i)->ActiveChanged.connect_same_thread (*this, boost::bind (&Session::update_latency_compensation, &_session, false, false));
}
_output->set_user_latency (0);
}
- processors_changed (); /* EMIT SIGNAL */
+ processors_changed (RouteProcessorChange ()); /* EMIT SIGNAL */
return 0;
}
processor_max_streams.reset();
_have_internal_generator = false;
- processors_changed (); /* EMIT SIGNAL */
+ processors_changed (RouteProcessorChange ()); /* EMIT SIGNAL */
if (!already_deleting) {
_session.clear_deletion_in_progress();
}
processor->drop_references ();
- processors_changed (); /* EMIT SIGNAL */
+ processors_changed (RouteProcessorChange ()); /* EMIT SIGNAL */
return 0;
}
(*i)->drop_references ();
}
- processors_changed (); /* EMIT SIGNAL */
+ processors_changed (RouteProcessorChange ()); /* EMIT SIGNAL */
return 0;
}
}
}
- /* Take the process lock so that if we add a processor which increases the required
- number of scratch buffers, we create those scratch buffers before the process
- thread has a chance to ask for them.
- XXX: in an ideal world we'd perhaps use some RCU magic to avoid having to take
- the lock here.
- */
-
- Glib::Mutex::Lock pl (_session.engine().process_lock ());
-
// We can, so configure everything
list< pair<ChanCount,ChanCount> >::iterator c = configuration.begin();
for (ProcessorList::iterator p = _processors.begin(); p != _processors.end(); ++p, ++c) {
out = c->second;
}
+ if (_meter) {
+ _meter->reset_max_channels (processor_max_streams);
+ }
+
/* make sure we have sufficient scratch buffers to cope with the new processor
configuration */
_session.ensure_buffers (n_process_buffers ());
}
}
- processors_changed (); /* EMIT SIGNAL */
+ processors_changed (RouteProcessorChange ()); /* EMIT SIGNAL */
return 0;
}
}
}
- if ((prop = node.property (X_("route-group"))) != 0) {
- RouteGroup* route_group = _session.route_group_by_name(prop->value());
- if (route_group == 0) {
- error << string_compose(_("Route %1: unknown route group \"%2 in saved state (ignored)"), _name, prop->value()) << endmsg;
- } else {
- set_route_group (route_group, this);
- }
- }
-
if ((prop = node.property (X_("order-keys"))) != 0) {
long n;
_meter_point = MeterPoint (string_2_enum (prop->value (), _meter_point));
}
- /* XXX: if the route was in both a mix group and an edit group, it'll end up
- just in the edit group. */
-
- if ((prop = node.property (X_("mix-group"))) != 0) {
- RouteGroup* route_group = _session.route_group_by_name(prop->value());
- if (route_group == 0) {
- error << string_compose(_("Route %1: unknown route group \"%2 in saved state (ignored)"), _name, prop->value()) << endmsg;
- } else {
- set_route_group (route_group, this);
- }
- }
-
- if ((prop = node.property (X_("edit-group"))) != 0) {
- RouteGroup* route_group = _session.route_group_by_name(prop->value());
- if (route_group == 0) {
- error << string_compose(_("Route %1: unknown route group \"%2 in saved state (ignored)"), _name, prop->value()) << endmsg;
- } else {
- set_route_group (route_group, this);
- }
- }
+ /* 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) {
the XML state represents a working signal route.
*/
- processors_changed ();
+ processors_changed (RouteProcessorChange ());
}
void
}
}
-void
-Route::set_route_group (RouteGroup *rg, void *src)
-{
- if (rg == _route_group) {
- return;
- }
-
- if (_route_group) {
- _route_group->remove (this);
- }
-
- if ((_route_group = rg) != 0) {
- _route_group->add (this);
- }
-
- _session.set_dirty ();
- route_group_changed (src); /* EMIT SIGNAL */
-}
-
-void
-Route::drop_route_group (void *src)
-{
- _route_group = 0;
- _session.set_dirty ();
- route_group_changed (src); /* EMIT SIGNAL */
-}
-
void
Route::set_comment (string cmt, void *src)
{
void
Route::set_meter_point (MeterPoint p, void *src)
{
+ /* CAN BE CALLED FROM PROCESS CONTEXT */
+
if (_meter_point == p) {
return;
}
+ bool meter_was_visible_to_user = _meter->display_to_user ();
+
{
Glib::RWLock::WriterLock lm (_processor_lock);
- ProcessorList as_it_was (_processors);
-
+
if (p != MeterCustom) {
// Move meter in the processors list to reflect the new position
- ProcessorList::iterator loc = find(_processors.begin(), _processors.end(), _meter);
+ ProcessorList::iterator loc = find (_processors.begin(), _processors.end(), _meter);
_processors.erase(loc);
switch (p) {
case MeterInput:
loc = _processors.begin();
break;
case MeterPreFader:
- loc = find(_processors.begin(), _processors.end(), _amp);
+ loc = find (_processors.begin(), _processors.end(), _amp);
break;
case MeterPostFader:
loc = _processors.end();
break;
default:
- break;
+ break;
}
-
- _processors.insert(loc, _meter);
- if (configure_processors_unlocked (0)) {
- _processors = as_it_was;
- configure_processors_unlocked (0); // it worked before we tried to add it ...
- return;
+ ChanCount m_in;
+
+ if (loc == _processors.begin()) {
+ m_in = _input->n_ports();
+ } else {
+ ProcessorList::iterator before = loc;
+ --before;
+ m_in = (*before)->output_streams ();
}
-
+
+ _meter->reflect_inputs (m_in);
+
+ _processors.insert (loc, _meter);
+
+ /* we do not need to reconfigure the processors, because the meter
+ (a) is always ready to handle processor_max_streams
+ (b) is always an N-in/N-out processor, and thus moving
+ it doesn't require any changes to the other processors.
+ */
+
_meter->set_display_to_user (false);
-
+
} else {
-
+
// just make it visible and let the user move it
-
+
_meter->set_display_to_user (true);
}
-
}
-
+
_meter_point = p;
meter_change (src); /* EMIT SIGNAL */
- processors_changed (); /* EMIT SIGNAL */
- _session.set_dirty ();
+
+ bool const meter_visibly_changed = (_meter->display_to_user() != meter_was_visible_to_user);
+
+ processors_changed (RouteProcessorChange (RouteProcessorChange::MeterPointChange, meter_visibly_changed)); /* EMIT SIGNAL */
}
void
}
}
- processors_changed (); /* EMIT SIGNAL */
+ processors_changed (RouteProcessorChange ()); /* EMIT SIGNAL */
_session.set_dirty ();
}
Route::SoloControllable::set_value (float val)
{
bool bval = ((val >= 0.5f) ? true: false);
+# if 0
+ this is how it should be done
+
+ boost::shared_ptr<RouteList> rl (new RouteList);
+ rl->push_back (route);
+ if (Config->get_solo_control_is_listen_control()) {
+ _session.set_listen (rl, bval);
+ } else {
+ _session.set_solo (rl, bval);
+ }
+#else
route.set_solo (bval, this);
+#endif
}
float
Route::SoloControllable::get_value (void) const
{
- return route.self_soloed() ? 1.0f : 0.0f;
+ if (Config->get_solo_control_is_listen_control()) {
+ return route.listening() ? 1.0f : 0.0f;
+ } else {
+ return route.self_soloed() ? 1.0f : 0.0f;
+ }
+}
+
+Route::MuteControllable::MuteControllable (std::string name, Route& r)
+ : AutomationControl (r.session(), Evoral::Parameter (MuteAutomation),
+ boost::shared_ptr<AutomationList>(), name)
+ , route (r)
+{
+ boost::shared_ptr<AutomationList> gl(new AutomationList(Evoral::Parameter(MuteAutomation)));
+ set_list (gl);
+}
+
+void
+Route::MuteControllable::set_value (float val)
+{
+ bool bval = ((val >= 0.5f) ? true: false);
+# if 0
+ this is how it should be done
+
+ boost::shared_ptr<RouteList> rl (new RouteList);
+ rl->push_back (route);
+ _session.set_mute (rl, bval);
+#else
+ route.set_mute (bval, this);
+#endif
+}
+
+float
+Route::MuteControllable::get_value (void) const
+{
+ return route.muted() ? 1.0f : 0.0f;
}
void
return c;
}
+
+boost::shared_ptr<Processor>
+Route::nth_plugin (uint32_t n)
+{
+ Glib::RWLock::ReaderLock lm (_processor_lock);
+ ProcessorList::iterator i;
+
+ for (i = _processors.begin(); i != _processors.end(); ++i) {
+ if (boost::dynamic_pointer_cast<PluginInsert> (*i)) {
+ if (n-- == 0) {
+ return *i;
+ }
+ }
+ }
+
+ return boost::shared_ptr<Processor> ();
+}
+
+boost::shared_ptr<Processor>
+Route::nth_send (uint32_t n)
+{
+ Glib::RWLock::ReaderLock lm (_processor_lock);
+ ProcessorList::iterator i;
+
+ for (i = _processors.begin(); i != _processors.end(); ++i) {
+ cerr << "check " << (*i)->name() << endl;
+ if (boost::dynamic_pointer_cast<Send> (*i)) {
+ if (n-- == 0) {
+ return *i;
+ }
+ } else {
+ cerr << "\tnot a send\n";
+ }
+ }
+
+ return boost::shared_ptr<Processor> ();
+}