#include "ardour/buffer_set.h"
#include "ardour/configuration.h"
#include "ardour/cycle_timer.h"
+#include "ardour/delivery.h"
#include "ardour/dB.h"
+#include "ardour/internal_send.h"
+#include "ardour/internal_return.h"
#include "ardour/ladspa_plugin.h"
#include "ardour/meter.h"
#include "ardour/mix.h"
using namespace PBD;
uint32_t Route::order_key_cnt = 0;
-sigc::signal<void,const char*> Route::SyncOrderKeys;
+sigc::signal<void, string const &> Route::SyncOrderKeys;
Route::Route (Session& sess, string name, Flag flg, DataType default_type)
: SessionObject (sess, name)
{
init ();
+
+ /* add standard processors other than amp (added by ::init()) */
+
+ _meter.reset (new PeakMeter (_session));
+ add_processor (_meter, PreFader);
+
+ if (_flags & ControlOut) {
+ /* where we listen to tracks */
+ _intreturn.reset (new InternalReturn (_session));
+ add_processor (_intreturn, PreFader);
+ }
+
+ _main_outs.reset (new Delivery (_session, _output, _mute_master, _name, Delivery::Main));
+ add_processor (_main_outs, PostFader);
+
+ /* now that we have _meter, its safe to connect to this */
+
+ _meter_connection = Metering::connect (mem_fun (*this, &Route::meter));
}
Route::Route (Session& sess, const XMLNode& node, DataType default_type)
, _default_type (default_type)
{
init ();
+
_set_state (node, false);
+
+ /* now that we have _meter, its safe to connect to this */
+
+ _meter_connection = Metering::connect (mem_fun (*this, &Route::meter));
}
void
Route::init ()
{
+ _solo_level = 0;
+ _solo_isolated = false;
_active = true;
processor_max_streams.reset();
_solo_safe = false;
_recordable = true;
- order_keys[strdup (N_("signal"))] = order_key_cnt++;
+ order_keys[N_("signal")] = order_key_cnt++;
_silent = false;
_meter_point = MeterPostFader;
_initial_delay = 0;
_remote_control_id = 0;
_in_configure_processors = false;
- _edit_group = 0;
- _mix_group = 0;
+ _route_group = 0;
_phase_invert = 0;
_denormal_protection = false;
_input->changed.connect (mem_fun (this, &Route::input_change_handler));
_output->changed.connect (mem_fun (this, &Route::output_change_handler));
- /* add standard processors */
+ /* add amp processor */
_amp.reset (new Amp (_session, _mute_master));
add_processor (_amp, PostFader);
-
- _meter.reset (new PeakMeter (_session));
- add_processor (_meter, PreFader);
-
- _main_outs.reset (new Delivery (_session, _output, _mute_master, _name, Delivery::Main));
- add_processor (_main_outs, PostFader);
-
- /* now we can meter */
-
- _meter_connection = Metering::connect (mem_fun (*this, &Route::meter));
}
Route::~Route ()
clear_processors (PreFader);
clear_processors (PostFader);
-
- for (OrderKeys::iterator i = order_keys.begin(); i != order_keys.end(); ++i) {
- free ((void*)(i->first));
- }
}
void
}
long
-Route::order_key (const char* name) const
+Route::order_key (std::string const & name) const
{
- OrderKeys::const_iterator i;
-
- for (i = order_keys.begin(); i != order_keys.end(); ++i) {
- if (!strcmp (name, i->first)) {
- return i->second;
- }
+ OrderKeys::const_iterator i = order_keys.find (name);
+ if (i == order_keys.end()) {
+ return -1;
}
- return -1;
+ return i->second;
}
void
-Route::set_order_key (const char* name, long n)
+Route::set_order_key (std::string const & name, long n)
{
- order_keys[strdup(name)] = n;
+ order_keys[name] = n;
if (Config->get_sync_all_route_ordering()) {
for (OrderKeys::iterator x = order_keys.begin(); x != order_keys.end(); ++x) {
_session.set_dirty ();
}
+/** Set all order keys to be the same as that for `base', if such a key
+ * exists in this route.
+ * @param base Base key.
+ */
void
-Route::sync_order_keys (const char* base)
+Route::sync_order_keys (std::string const & base)
{
if (order_keys.empty()) {
return;
void
Route::set_gain (gain_t val, void *src)
{
- if (src != 0 && _mix_group && src != _mix_group && _mix_group->is_active()) {
+ if (src != 0 && _route_group && src != _route_group && _route_group->active_property (RouteGroup::Gain)) {
- if (_mix_group->is_relative()) {
-
+ if (_route_group->is_relative()) {
+
gain_t usable_gain = _amp->gain();
if (usable_gain < 0.000001f) {
usable_gain = 0.000001f;
gain_t factor = delta / usable_gain;
if (factor > 0.0f) {
- factor = _mix_group->get_max_factor(factor);
+ factor = _route_group->get_max_factor(factor);
if (factor == 0.0f) {
_amp->gain_control()->Changed(); /* EMIT SIGNAL */
return;
}
} else {
- factor = _mix_group->get_min_factor(factor);
+ factor = _route_group->get_min_factor(factor);
if (factor == 0.0f) {
_amp->gain_control()->Changed(); /* EMIT SIGNAL */
return;
}
}
- _mix_group->apply (&Route::inc_gain, factor, _mix_group);
+ _route_group->apply (&Route::inc_gain, factor, _route_group);
} else {
- _mix_group->apply (&Route::set_gain, val, _mix_group);
+ _route_group->apply (&Route::set_gain, val, _route_group);
}
return;
void
Route::process_output_buffers (BufferSet& bufs,
sframes_t start_frame, sframes_t end_frame, nframes_t nframes,
- bool with_processors, int declick)
+ bool /*with_processors*/, int declick)
{
bool monitor;
switch (Config->get_monitoring_model()) {
case HardwareMonitoring:
case ExternalMonitoring:
- monitor = record_enabled() && (_session.config.get_auto_input() || _session.actively_recording());
+ monitor = !record_enabled() || (_session.config.get_auto_input() && !_session.actively_recording());
break;
default:
monitor = true;
}
/* figure out if we're going to use gain automation */
-
_amp->setup_gain_automation (start_frame, end_frame, nframes);
- /* tell main outs what to do about monitoring */
+ /* tell main outs what to do about monitoring */
_main_outs->no_outs_cuz_we_no_monitor (!monitor);
+
/* -------------------------------------------------------------------------------------------
GLOBAL DECLICK (for transport changes etc.)
----------------------------------------------------------------------------------------- */
}
bufs.set_count (_input->n_ports());
-
- for (DataType::iterator t = DataType::begin(); t != DataType::end(); ++t) {
+
+ if (is_control() && _session.listening()) {
- BufferSet::iterator o = bufs.begin(*t);
- PortSet& ports (_input->ports());
+ /* control/monitor bus ignores input ports when something is
+ feeding the listen "stream". data will "arrive" into the
+ route from the intreturn processor element.
+ */
+
+ bufs.silence (nframes, 0);
- for (PortSet::iterator i = ports.begin(*t); i != ports.end(*t); ++i, ++o) {
- o->read_from (i->get_buffer(nframes), nframes);
+ } else {
+
+ for (DataType::iterator t = DataType::begin(); t != DataType::end(); ++t) {
+
+ BufferSet::iterator o = bufs.begin(*t);
+ PortSet& ports (_input->ports());
+
+ for (PortSet::iterator i = ports.begin(*t); i != ports.end(*t); ++i, ++o) {
+ o->read_from (i->get_buffer(nframes), nframes);
+ }
}
}
process_output_buffers (_session.get_silent_buffers (n_process_buffers()), start_frame, end_frame, nframes, true, declick);
}
+void
+Route::set_listen (bool yn, void* src)
+{
+ if (_control_outs) {
+ if (yn != _control_outs->active()) {
+ if (yn) {
+ _control_outs->activate ();
+ } else {
+ _control_outs->deactivate ();
+ }
+
+ listen_changed (src); /* EMIT SIGNAL */
+ }
+ }
+}
+
+bool
+Route::listening () const
+{
+ if (_control_outs) {
+ return _control_outs->active ();
+ } else {
+ return false;
+ }
+}
+
void
Route::set_solo (bool yn, void *src)
{
- if (_solo_safe) {
+ if (_solo_safe || _solo_isolated) {
return;
}
- if (_mix_group && src != _mix_group && _mix_group->is_active()) {
- _mix_group->apply (&Route::set_solo, yn, _mix_group);
+ if (_route_group && src != _route_group && _route_group->active_property (RouteGroup::Solo)) {
+ _route_group->apply (&Route::set_solo, yn, _route_group);
return;
}
- if (_main_outs->soloed() != yn) {
- _main_outs->mod_solo_level (yn ? 1 : -1);
+ if (soloed() != yn) {
+ mod_solo_level (yn ? 1 : -1);
solo_changed (src); /* EMIT SIGNAL */
_solo_control->Changed (); /* EMIT SIGNAL */
}
}
-bool
-Route::soloed() const
+void
+Route::mod_solo_level (int32_t delta)
{
- return _main_outs->soloed ();
+ if (delta < 0) {
+ if (_solo_level >= (uint32_t) delta) {
+ _solo_level += delta;
+ } else {
+ _solo_level = 0;
+ }
+ } else {
+ _solo_level += delta;
+ }
+
+ /* tell main outs what the solo situation is
+ */
+
+ _main_outs->set_solo_level (_solo_level);
+ _main_outs->set_solo_isolated (_solo_isolated);
}
void
Route::set_solo_isolated (bool yn, void *src)
{
- if (_mix_group && src != _mix_group && _mix_group->is_active()) {
- _mix_group->apply (&Route::set_solo_isolated, yn, _mix_group);
+ if (_route_group && src != _route_group && _route_group->active_property (RouteGroup::Solo)) {
+ _route_group->apply (&Route::set_solo_isolated, yn, _route_group);
return;
}
- _main_outs->set_solo_isolated (yn);
- solo_isolated_changed (src);
+ if (yn != _solo_isolated) {
+ _solo_isolated = yn;
+
+ /* tell main outs what the solo situation is
+ */
+
+ _main_outs->set_solo_level (_solo_level);
+ _main_outs->set_solo_isolated (_solo_isolated);
+
+ solo_isolated_changed (src);
+ }
}
bool
Route::solo_isolated () const
{
- return _main_outs->solo_isolated();
+ return _solo_isolated;
}
void
Route::set_mute (bool yn, void *src)
{
- if (_mix_group && src != _mix_group && _mix_group->is_active()) {
- _mix_group->apply (&Route::set_mute, yn, _mix_group);
+ if (_route_group && src != _route_group && _route_group->active_property (RouteGroup::Mute)) {
+ _route_group->apply (&Route::set_mute, yn, _route_group);
return;
}
return _mute_master->muted ();
}
-#if DEFINE_IF_YOU_NEED_THIS
+#if 0
static void
dump_processors(const string& name, const list<boost::shared_ptr<Processor> >& procs)
{
cerr << name << " {" << endl;
for (list<boost::shared_ptr<Processor> >::const_iterator p = procs.begin();
p != procs.end(); ++p) {
- cerr << "\t" << (*p)->name() << endl;
+ cerr << "\t" << (*p)->name() << " ID = " << (*p)->id() << endl;
}
cerr << "}" << endl;
}
#endif
-Route::ProcessorList::iterator
-Route::prefader_iterator()
-{
- Glib::RWLock::ReaderLock lm (_processor_lock);
- return find (_processors.begin(), _processors.end(), _amp);
-}
-
int
Route::add_processor (boost::shared_ptr<Processor> processor, Placement placement, ProcessorStreams* err)
{
if (placement == PreFader) {
/* generic pre-fader: insert immediately before the amp */
- loc = find(_processors.begin(), _processors.end(), _amp);
+ loc = find (_processors.begin(), _processors.end(), _amp);
} else {
- /* generic post-fader: insert at end */
- loc = _processors.end();
-
- if (processor->visible() && !_processors.empty()) {
- /* check for invisible processors stacked at the end and leave them there */
- ProcessorList::iterator p;
- p = _processors.end();
- --p;
- cerr << "Let's check " << (*p)->name() << " vis ? " << (*p)->visible() << endl;
- while (!(*p)->visible() && p != _processors.begin()) {
- --p;
- }
- ++p;
- loc = p;
- }
+ /* generic post-fader: insert right before the main outs */
+ loc = find (_processors.begin(), _processors.end(), _main_outs);
}
return add_processor (processor, loc, err);
--ploc;
_processors.erase(ploc);
configure_processors_unlocked (0); // it worked before we tried to add it ...
+ cerr << "configure failed\n";
return -1;
}
}
- if (_meter) {
- // Ensure peak vector sizes before the plugin is activated
- ChanCount potential_max_streams = ChanCount::max (processor->input_streams(), processor->output_streams());
- _meter->configure_io (potential_max_streams, potential_max_streams);
- }
-
// 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));
return 0;
}
-bool
-Route::add_processor_from_xml (const XMLNode& node, Placement placement)
-{
- ProcessorList::iterator loc;
- if (placement == PreFader) {
- /* generic pre-fader: insert immediately before the amp */
- loc = find(_processors.begin(), _processors.end(), _amp);
- } else {
- /* generic post-fader: insert at end */
- loc = _processors.end();
- }
-
- return add_processor_from_xml (node, loc);
-}
-
bool
Route::add_processor_from_xml (const XMLNode& node, ProcessorList::iterator iter)
{
const XMLProperty *prop;
- // legacy sessions use a different node name for sends
- if (node.name() == "Send") {
-
- try {
- boost::shared_ptr<Send> send (new Send (_session, _mute_master, node));
- add_processor (send, iter);
- return true;
- }
-
- catch (failed_constructor &err) {
- error << _("Send construction failed") << endmsg;
- return false;
- }
-
- } else if (node.name() == "Processor") {
+ if (node.name() != "Processor") {
+ return false;
+ }
- try {
- if ((prop = node.property ("type")) != 0) {
-
-
- cerr << _name << " : got processor type " << prop->value() << endl;
-
- boost::shared_ptr<Processor> processor;
- bool have_insert = false;
+ try {
+ if ((prop = node.property ("type")) != 0) {
+
+ boost::shared_ptr<Processor> processor;
- if (prop->value() == "ladspa" || prop->value() == "Ladspa" ||
- prop->value() == "lv2" ||
- prop->value() == "vst" ||
- prop->value() == "audiounit") {
+ if (prop->value() == "ladspa" || prop->value() == "Ladspa" ||
+ prop->value() == "lv2" ||
+ prop->value() == "vst" ||
+ prop->value() == "audiounit") {
- processor.reset (new PluginInsert(_session, node));
- have_insert = true;
+ processor.reset (new PluginInsert(_session, node));
- } else if (prop->value() == "port") {
+ } else if (prop->value() == "port") {
- processor.reset (new PortInsert (_session, _mute_master, node));
+ processor.reset (new PortInsert (_session, _mute_master, node));
- } else if (prop->value() == "send") {
+ } else if (prop->value() == "send") {
- processor.reset (new Send (_session, _mute_master, node));
- have_insert = true;
-
- } else if (prop->value() == "meter") {
+ processor.reset (new Send (_session, _mute_master, node));
+
+ } else if (prop->value() == "meter") {
+
+ if (_meter) {
+ if (_meter->set_state (node)) {
+ return false;
+ } else {
+ return true;
+ }
+ }
- processor = _meter;
- processor->set_state (node);
+ _meter.reset (new PeakMeter (_session, node));
+ processor = _meter;
- } else if (prop->value() == "amp") {
+ } else if (prop->value() == "amp") {
+
+ /* amp always exists */
- processor = _amp;
- processor->set_state (node);
+ processor = _amp;
+ if (processor->set_state (node)) {
+ return false;
+ } else {
+ /* never any reason to add it */
+ return true;
+ }
- } else if (prop->value() == "listen" || prop->value() == "deliver") {
+ } else if (prop->value() == "intsend") {
- /* XXX need to generalize */
+ processor.reset (new InternalSend (_session, _mute_master, node));
- processor = _control_outs;
- processor->set_state (node);
+ } else if (prop->value() == "intreturn") {
- } else if (prop->value() == "main-outs") {
+ if (_intreturn) {
+ if (_intreturn->set_state (node)) {
+ return false;
+ } else {
+ return true;
+ }
+ }
+ _intreturn.reset (new InternalReturn (_session, node));
+ processor = _intreturn;
+
+ } else if (prop->value() == "main-outs") {
- processor = _main_outs;
- processor->set_state (node);
+ if (_main_outs) {
+ if (_main_outs->set_state (node)) {
+ return false;
+ } else {
+ return true;
+ }
+ }
- } else {
+ _main_outs.reset (new Delivery (_session, _output, _mute_master, node));
+ processor = _main_outs;
- error << string_compose(_("unknown Processor type \"%1\"; ignored"), prop->value()) << endmsg;
- }
+ } else {
+ error << string_compose(_("unknown Processor type \"%1\"; ignored"), prop->value()) << endmsg;
+ return false;
+ }
- if (iter == _processors.end() && processor->visible() && !_processors.empty()) {
- /* check for invisible processors stacked at the end and leave them there */
- ProcessorList::iterator p;
- p = _processors.end();
+ if (iter == _processors.end() && processor->visible() && !_processors.empty()) {
+ /* check for invisible processors stacked at the end and leave them there */
+ ProcessorList::iterator p;
+ p = _processors.end();
+ --p;
+ while (!(*p)->visible() && p != _processors.begin()) {
--p;
- cerr << "Let's check " << (*p)->name() << " vis ? " << (*p)->visible() << endl;
- while (!(*p)->visible() && p != _processors.begin()) {
- --p;
- }
- ++p;
- iter = p;
}
-
- return (add_processor (processor, iter) == 0);
-
- } else {
- error << _("Processor XML node has no type property") << endmsg;
+ ++p;
+ iter = p;
}
- }
- catch (failed_constructor &err) {
- warning << _("processor could not be created. Ignored.") << endmsg;
+ return (add_processor (processor, iter) == 0);
+
+ } else {
+ error << _("Processor XML node has no type property") << endmsg;
return false;
}
}
- return false;
+
+ catch (failed_constructor &err) {
+ warning << _("processor could not be created. Ignored.") << endmsg;
+ return false;
+ }
}
int
-Route::add_processors (const ProcessorList& others, Placement placement, ProcessorStreams* err)
+Route::add_processors (const ProcessorList& others, boost::shared_ptr<Processor> before, ProcessorStreams* err)
{
ProcessorList::iterator loc;
- if (placement == PreFader) {
- /* generic pre-fader: insert immediately before the amp */
- loc = find(_processors.begin(), _processors.end(), _amp);
- } else {
- /* generic post-fader: insert at end */
- loc = _processors.end();
- if (!_processors.empty()) {
- /* check for invisible processors stacked at the end and leave them there */
- ProcessorList::iterator p;
- p = _processors.end();
- --p;
- cerr << "Let's check " << (*p)->name() << " vis ? " << (*p)->visible() << endl;
- while (!(*p)->visible() && p != _processors.begin()) {
- --p;
- }
- ++p;
- loc = p;
- }
+ if (before) {
+ loc = find(_processors.begin(), _processors.end(), before);
+ } else {
+ /* nothing specified - at end but before main outs */
+ loc = find (_processors.begin(), _processors.end(), _main_outs);
}
return add_processors (others, loc, err);
if ((pi = boost::dynamic_pointer_cast<PluginInsert>(*i)) != 0) {
pi->set_count (1);
- ChanCount m = max(pi->input_streams(), pi->output_streams());
- if (m > potential_max_streams)
+ ChanCount m = max (pi->input_streams(), pi->output_streams());
+
+ if (m > potential_max_streams) {
potential_max_streams = m;
+ }
}
- // Ensure peak vector sizes before the plugin is activated
- _meter->configure_io (potential_max_streams, potential_max_streams);
-
_processors.insert (iter, *i);
if (configure_processors_unlocked (err)) {
}
-/* Figure out the streams that will feed into PreFader */
-ChanCount
-Route::pre_fader_streams() const
-{
- boost::shared_ptr<Processor> processor;
-
- /* Find the last pre-fader redirect that isn't a send; sends don't affect the number
- * of streams. */
- for (ProcessorList::const_iterator i = _processors.begin(); i != _processors.end(); ++i) {
- if ((*i) == _amp) {
- break;
- }
- if (boost::dynamic_pointer_cast<Send> (*i) == 0) {
- processor = *i;
- }
- }
-
- if (processor) {
- return processor->output_streams();
- } else {
- return _input->n_ports ();
- }
-}
-
-
/** Remove processors with a given placement.
* @param p Placement of processors to remove.
*/
Glib::RWLock::WriterLock lm (_processor_lock);
ProcessorList new_list;
ProcessorStreams err;
+ bool seen_amp = false;
- ProcessorList::iterator amp_loc = find(_processors.begin(), _processors.end(), _amp);
- if (p == PreFader) {
- // Get rid of PreFader processors
- for (ProcessorList::iterator i = _processors.begin(); i != amp_loc; ++i) {
- (*i)->drop_references ();
- }
- // Keep the rest
- for (ProcessorList::iterator i = amp_loc; i != _processors.end(); ++i) {
- new_list.push_back (*i);
+ for (ProcessorList::iterator i = _processors.begin(); i != _processors.end(); ++i) {
+
+ if (*i == _amp) {
+ seen_amp = true;
}
- } else {
- // Keep PreFader processors
- for (ProcessorList::iterator i = _processors.begin(); i != amp_loc; ++i) {
+
+ if ((*i) == _amp || (*i) == _meter || (*i) == _main_outs) {
+
+ /* you can't remove these */
+
new_list.push_back (*i);
- }
- new_list.push_back (_amp);
- // Get rid of PostFader processors
- for (ProcessorList::iterator i = amp_loc; i != _processors.end(); ++i) {
- (*i)->drop_references ();
+
+ } else {
+ if (seen_amp) {
+
+ switch (p) {
+ case PreFader:
+ new_list.push_back (*i);
+ break;
+ case PostFader:
+ (*i)->drop_references ();
+ break;
+ }
+
+ } else {
+
+ switch (p) {
+ case PreFader:
+ (*i)->drop_references ();
+ break;
+ case PostFader:
+ new_list.push_back (*i);
+ break;
+ }
+ }
}
}
return 0;
}
+int
+Route::remove_processors (const ProcessorList& to_be_deleted, ProcessorStreams* err)
+{
+ ProcessorList deleted;
+ ProcessorList as_we_were;
+
+ if (!_session.engine().connected()) {
+ return 1;
+ }
+
+ processor_max_streams.reset();
+
+ {
+ Glib::RWLock::WriterLock lm (_processor_lock);
+ ProcessorList::iterator i;
+ boost::shared_ptr<Processor> processor;
+
+ as_we_were = _processors;
+
+ for (i = _processors.begin(); i != _processors.end(); ) {
+
+ processor = *i;
+
+ /* these can never be removed */
+
+ if (processor == _amp || processor == _meter || processor == _main_outs) {
+ ++i;
+ continue;
+ }
+
+ /* see if its in the list of processors to delete */
+
+ if (find (to_be_deleted.begin(), to_be_deleted.end(), processor) == to_be_deleted.end()) {
+ ++i;
+ continue;
+ }
+
+ /* stop IOProcessors that send to JACK ports
+ from causing noise as a result of no longer being
+ run.
+ */
+
+ boost::shared_ptr<IOProcessor> iop;
+
+ if ((iop = boost::dynamic_pointer_cast<IOProcessor> (processor)) != 0) {
+ iop->disconnect ();
+ }
+
+ deleted.push_back (processor);
+ i = _processors.erase (i);
+ }
+
+ if (deleted.empty()) {
+ /* none of those in the requested list were found */
+ return 0;
+ }
+
+ _output->set_user_latency (0);
+
+ if (configure_processors_unlocked (err)) {
+ /* get back to where we where */
+ _processors = as_we_were;
+ /* we know this will work, because it worked before :) */
+ configure_processors_unlocked (0);
+ return -1;
+ }
+
+ _have_internal_generator = false;
+
+ for (i = _processors.begin(); i != _processors.end(); ++i) {
+ boost::shared_ptr<PluginInsert> pi;
+
+ if ((pi = boost::dynamic_pointer_cast<PluginInsert>(*i)) != 0) {
+ if (pi->is_generator()) {
+ _have_internal_generator = true;
+ break;
+ }
+ }
+ }
+ }
+
+ /* now try to do what we need to so that those that were removed will be deleted */
+
+ for (ProcessorList::iterator i = deleted.begin(); i != deleted.end(); ++i) {
+ (*i)->drop_references ();
+ }
+
+ processors_changed (); /* EMIT SIGNAL */
+
+ return 0;
+}
+
+
int
Route::configure_processors (ProcessorStreams* err)
{
ChanCount out;
list< pair<ChanCount,ChanCount> > configuration;
uint32_t index = 0;
+
for (ProcessorList::iterator p = _processors.begin(); p != _processors.end(); ++p, ++index) {
if ((*p)->can_support_io_configuration(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) {
(*p)->configure_io(c->first, c->second);
- (*p)->activate();
processor_max_streams = ChanCount::max(processor_max_streams, c->first);
processor_max_streams = ChanCount::max(processor_max_streams, c->second);
out = c->second;
// Ensure route outputs match last processor's outputs
if (out != _output->n_ports ()) {
+ cerr << "For " << _name << " out/last mismatch - out = " << out << " vs. " << _output->n_ports() << endl;
_output->ensure_io (out, false, this);
}
_session.set_dirty ();
}
+bool
+Route::processor_is_prefader (boost::shared_ptr<Processor> p)
+{
+ bool pre_fader = true;
+ Glib::RWLock::ReaderLock lm (_processor_lock);
+
+ for (ProcessorList::iterator i = _processors.begin(); i != _processors.end(); ++i) {
+
+ /* semantic note: if p == amp, we want to return true, so test
+ for equality before checking if this is the amp
+ */
+
+ if ((*i) == p) {
+ break;
+ }
+
+ if ((*i) == _amp) {
+ pre_fader = false;
+ break;
+ }
+ }
+
+ return pre_fader;
+}
+
int
-Route::reorder_processors (const ProcessorList& new_order, Placement placement, ProcessorStreams* err)
+Route::reorder_processors (const ProcessorList& new_order, ProcessorStreams* err)
{
/* "new_order" is an ordered list of processors to be positioned according to "placement".
NOTE: all processors in "new_order" MUST be marked as visible. There maybe additional
ProcessorList::const_iterator niter;
ProcessorList as_it_was_before = _processors;
ProcessorList as_it_will_be;
- ProcessorList::iterator start, end;
-
- placement_range (placement, start, end);
- oiter = start;
+ oiter = _processors.begin();
niter = new_order.begin();
while (niter != new_order.end()) {
its been deleted. If its there, append it to the temp list.
*/
- if (oiter == end) {
+ if (oiter == _processors.end()) {
/* no more elements in the old list, so just stick the rest of
the new order onto the temp list.
as_it_will_be.insert (as_it_will_be.end(), niter, new_order.end());
while (niter != new_order.end()) {
- (*niter)->set_placement (placement);
++niter;
}
break;
if (!(*oiter)->visible()) {
as_it_will_be.push_back (*oiter);
- (*oiter)->set_placement (placement);
} else {
} else {
/* ignore this one, and add the next item from the new order instead */
as_it_will_be.push_back (*niter);
- (*niter)->set_placement (placement);
++niter;
}
}
node->add_property("denormal-protection", _denormal_protection?"yes":"no");
node->add_property("meter-point", enum_2_string (_meter_point));
- if (_edit_group) {
- node->add_property("edit-group", _edit_group->name());
- }
- if (_mix_group) {
- node->add_property("mix-group", _mix_group->name());
+ if (_route_group) {
+ node->add_property("route-group", _route_group->name());
}
string order_string;
}
int
-Route::_set_state (const XMLNode& node, bool call_base)
+Route::_set_state (const XMLNode& node, bool /*call_base*/)
{
XMLNodeList nlist;
} else {
_flags = Flag (0);
}
+
+ /* add all processors (except amp, which is always present) */
+
+ nlist = node.children();
+ XMLNode processor_state (X_("processor_state"));
+
+ for (niter = nlist.begin(); niter != nlist.end(); ++niter){
+
+ child = *niter;
+
+ if (child->name() == IO::state_node_name) {
+ if ((prop = child->property (X_("direction"))) == 0) {
+ continue;
+ }
+
+ if (prop->value() == "Input") {
+ _input->set_state (*child);
+ } else if (prop->value() == "Output") {
+ _output->set_state (*child);
+ }
+ }
+
+ if (child->name() == X_("Processor")) {
+ processor_state.add_child_copy (*child);
+ }
+ }
+
+ set_processor_state (processor_state);
+ if ((prop = node.property ("solo_level")) != 0) {
+ _solo_level = 0; // needed for mod_solo_level() to work
+ mod_solo_level (atoi (prop->value()));
+ }
+
+ if ((prop = node.property ("solo-isolated")) != 0) {
+ set_solo_isolated (prop->value() == "yes", this);
+ }
+
if ((prop = node.property (X_("phase-invert"))) != 0) {
set_phase_invert (prop->value()=="yes"?true:false);
}
_meter_point = MeterPoint (string_2_enum (prop->value (), _meter_point));
}
- if ((prop = node.property (X_("edit-group"))) != 0) {
- RouteGroup* edit_group = _session.edit_group_by_name(prop->value());
- if(edit_group == 0) {
- error << string_compose(_("Route %1: unknown edit group \"%2 in saved state (ignored)"), _name, prop->value()) << endmsg;
+ 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_edit_group(edit_group, this);
+ set_route_group (route_group, this);
}
}
error << string_compose (_("badly formed order key string in state file! [%1] ... ignored."), remaining)
<< endmsg;
} else {
- set_order_key (remaining.substr (0, equal).c_str(), n);
+ set_order_key (remaining.substr (0, equal), n);
}
}
}
}
- nlist = node.children();
- XMLNode processor_state (X_("processor_state"));
-
- for (niter = nlist.begin(); niter != nlist.end(); ++niter){
-
- child = *niter;
-
- if (child->name() == IO::state_node_name) {
- if ((prop = child->property (X_("direction"))) == 0) {
- continue;
- }
-
- if (prop->value() == "Input") {
- _input->set_state (*child);
- } else if (prop->value() == "Output") {
- _output->set_state (*child);
- }
- }
-
- if (child->name() == X_("Processor")) {
- processor_state.add_child_copy (*child);
- }
- }
-
- set_processor_state (processor_state);
-
for (niter = nlist.begin(); niter != nlist.end(); ++niter){
child = *niter;
}
}
- if ((prop = node.property (X_("mix-group"))) != 0) {
- RouteGroup* mix_group = _session.mix_group_by_name(prop->value());
- if (mix_group == 0) {
- error << string_compose(_("Route %1: unknown mix group \"%2 in saved state (ignored)"), _name, prop->value()) << endmsg;
- } else {
- set_mix_group(mix_group, this);
- }
- }
-
return 0;
}
{
const XMLNodeList &nlist = node.children();
XMLNodeConstIterator niter;
- bool has_meter_processor = false; // legacy sessions don't
ProcessorList::iterator i, o;
- cerr << _name << " _set_processor_states\n";
-
// Iterate through existing processors, remove those which are not in the state list
+
for (i = _processors.begin(); i != _processors.end(); ) {
+
+ /* leave amp alone, always */
+
+ if ((*i) == _amp) {
+ ++i;
+ continue;
+ }
+
ProcessorList::iterator tmp = i;
++tmp;
bool processorInStateList = false;
-
+
for (niter = nlist.begin(); niter != nlist.end(); ++niter) {
XMLProperty* id_prop = (*niter)->property(X_("id"));
+
if (id_prop && (*i)->id() == id_prop->value()) {
processorInStateList = true;
break;
// Iterate through state list and make sure all processors are on the track and in the correct order,
// set the state of existing processors according to the new state on the same go
+
i = _processors.begin();
for (niter = nlist.begin(); niter != nlist.end(); ++niter, ++i) {
XMLProperty* prop = (*niter)->property ("type");
- if (prop && prop->value() == "meter") {
- has_meter_processor = true;
- }
-
o = i;
- if (prop->value() != "meter" && prop->value() != "amp" && prop->value() != "main-outs") {
-
- // Check whether the next processor in the list
-
+ // Check whether the next processor in the list is the right one,
+ // except for "amp" which is always there and may not have the
+ // old ID since it is always created anew in every Route
+
+ if (prop->value() != "amp") {
while (o != _processors.end()) {
XMLProperty* id_prop = (*niter)->property(X_("id"));
if (id_prop && (*o)->id() == id_prop->value()) {
// If the processor (*niter) is not on the route,
// create it and move it to the correct location
+
if (o == _processors.end()) {
if (add_processor_from_xml (**niter, i)) {
cerr << "Error restoring route: unable to restore processor" << endl;
}
- // Otherwise, the processor already exists; just
- // ensure it is at the location provided in the XML state
} else {
+ // Otherwise, the processor already exists; just
+ // ensure it is at the location provided in the XML state
+
if (i != o) {
boost::shared_ptr<Processor> tmp = (*o);
_processors.erase (o); // remove the old copy
--i; // move iterator to the correct processor
}
+ // and make it (just) so
+
(*i)->set_state (**niter);
}
}
the XML state represents a working signal route.
*/
- if (!has_meter_processor) {
- set_meter_point (_meter_point, NULL);
- }
-
processors_changed ();
}
}
}
-boost::shared_ptr<Delivery>
-Route::add_listener (boost::shared_ptr<IO> io, const string& listen_name)
+void
+Route::add_internal_return ()
{
- string name = _name;
- name += '[';
- name += listen_name;
- name += ']';
-
- boost::shared_ptr<Delivery> listener (new Delivery (_session, _mute_master, name, Delivery::Listen));
-
- /* As an IO, our control outs need as many IO outputs as we have outputs
- * (we track the changes in ::output_change_handler()).
- * As a processor, the listener is an identity processor
- * (i.e. it does not modify its input buffers whatsoever)
- */
+ if (!_intreturn) {
+ _intreturn.reset (new InternalReturn (_session));
+ add_processor (_intreturn, PreFader);
+ }
+}
- if (listener->output()->ensure_io (n_outputs(), true, this)) {
- return boost::shared_ptr<Delivery>();
+BufferSet*
+Route::get_return_buffer () const
+{
+ Glib::RWLock::ReaderLock rm (_processor_lock);
+
+ for (ProcessorList::const_iterator x = _processors.begin(); x != _processors.end(); ++x) {
+ boost::shared_ptr<InternalReturn> d = boost::dynamic_pointer_cast<InternalReturn>(*x);
+
+ if (d) {
+ BufferSet* bs = d->get_buffers ();
+ return bs;
+ }
}
- add_processor (listener, PostFader);
+ return 0;
+}
- return listener;
+void
+Route::release_return_buffer () const
+{
+ Glib::RWLock::ReaderLock rm (_processor_lock);
+
+ for (ProcessorList::const_iterator x = _processors.begin(); x != _processors.end(); ++x) {
+ boost::shared_ptr<InternalReturn> d = boost::dynamic_pointer_cast<InternalReturn>(*x);
+
+ if (d) {
+ return d->release_buffers ();
+ }
+ }
}
int
-Route::listen_via (boost::shared_ptr<IO> io, const string& listen_name)
+Route::listen_via (boost::shared_ptr<Route> route, Placement placement, bool /*active*/, bool aux)
{
vector<string> ports;
vector<string>::const_iterator i;
{
Glib::RWLock::ReaderLock rm (_processor_lock);
- for (ProcessorList::const_iterator x = _processors.begin(); x != _processors.end(); ++x) {
- boost::shared_ptr<const Delivery> d = boost::dynamic_pointer_cast<const Delivery>(*x);
+ 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) {
+
+ /* if the target is the control outs, then make sure
+ we take note of which i-send is doing that.
+ */
+
+ if (route == _session.control_out()) {
+ _control_outs = boost::dynamic_pointer_cast<Delivery>(d);
+ }
- if (d && d->output() == io) {
/* already listening via the specified IO: do nothing */
+
return 0;
}
}
}
-
- uint32_t ni = io->n_ports().n_total();
-
- for (uint32_t n = 0; n < ni; ++n) {
- ports.push_back (io->nth (n)->name());
- }
-
- if (ports.empty()) {
- return 0;
- }
-
- boost::shared_ptr<Delivery> listen_point = add_listener (io, listen_name);
- /* XXX hack for now .... until we can generalize listen points */
+ boost::shared_ptr<InternalSend> listener;
- _control_outs = listen_point;
+ try {
+ listener.reset (new InternalSend (_session, _mute_master, route, (aux ? Delivery::Aux : Delivery::Listen)));
- /* now connect to the named ports */
-
- ni = listen_point->output()->n_ports().n_total();
- size_t psize = ports.size();
+ } catch (failed_constructor& err) {
+ return -1;
+ }
- for (size_t n = 0; n < ni; ++n) {
- if (listen_point->output()->connect (listen_point->output()->nth (n), ports[n % psize], this)) {
- error << string_compose (_("could not connect %1 to %2"),
- listen_point->output()->nth (n)->name(), ports[n % psize]) << endmsg;
- return -1;
- }
+ if (route == _session.control_out()) {
+ _control_outs = listener;
}
+ add_processor (listener, placement);
return 0;
}
void
-Route::drop_listen (boost::shared_ptr<IO> io)
+Route::drop_listen (boost::shared_ptr<Route> route)
{
ProcessorStreams err;
ProcessorList::iterator tmp;
- Glib::RWLock::ReaderLock rm (_processor_lock);
+ Glib::RWLock::ReaderLock rl(_processor_lock);
+ rl.acquire ();
+ again:
for (ProcessorList::iterator x = _processors.begin(); x != _processors.end(); ) {
- tmp = x;
- ++tmp;
-
- boost::shared_ptr<Delivery> d = boost::dynamic_pointer_cast<Delivery>(*x);
+ boost::shared_ptr<InternalSend> d = boost::dynamic_pointer_cast<InternalSend>(*x);
- if (d && d->output() == io) {
- /* already listening via the specified IO: do nothing */
+ if (d && d->target_route() == route) {
+ rl.release ();
remove_processor (*x, &err);
-
- }
-
- x = tmp;
- }
-}
+ rl.acquire ();
-void
-Route::set_edit_group (RouteGroup *eg, void *src)
+ /* list could have been demolished while we dropped the lock
+ so start over.
+ */
-{
- if (eg == _edit_group) {
- return;
+ goto again;
+ }
}
- if (_edit_group) {
- _edit_group->remove (this);
- }
+ rl.release ();
- if ((_edit_group = eg) != 0) {
- _edit_group->add (this);
+ if (route == _session.control_out()) {
+ _control_outs.reset ();
}
-
- _session.set_dirty ();
- edit_group_changed (src); /* EMIT SIGNAL */
-}
-
-void
-Route::drop_edit_group (void *src)
-{
- _edit_group = 0;
- _session.set_dirty ();
- edit_group_changed (src); /* EMIT SIGNAL */
}
void
-Route::set_mix_group (RouteGroup *mg, void *src)
-
+Route::set_route_group (RouteGroup *rg, void *src)
{
- if (mg == _mix_group) {
+ if (rg == _route_group) {
return;
}
- if (_mix_group) {
- _mix_group->remove (this);
+ if (_route_group) {
+ _route_group->remove (this);
}
- if ((_mix_group = mg) != 0) {
- _mix_group->add (this);
+ if ((_route_group = rg) != 0) {
+ _route_group->add (this);
}
_session.set_dirty ();
- mix_group_changed (src); /* EMIT SIGNAL */
+ route_group_changed (src); /* EMIT SIGNAL */
}
void
-Route::drop_mix_group (void *src)
+Route::drop_route_group (void *src)
{
- _mix_group = 0;
+ _route_group = 0;
_session.set_dirty ();
- mix_group_changed (src); /* EMIT SIGNAL */
+ route_group_changed (src); /* EMIT SIGNAL */
}
void
}
bool
-Route::feeds (boost::shared_ptr<IO> other)
+Route::feeds (boost::shared_ptr<Route> other)
{
- if (_output->connected_to (other)) {
+ // cerr << _name << endl;
+
+ if (_output->connected_to (other->input())) {
+ // cerr << "\tdirect FEEDS " << other->name() << endl;
return true;
}
- /* check IOProcessors which may also interconnect Routes */
-
for (ProcessorList::iterator r = _processors.begin(); r != _processors.end(); r++) {
-
+
boost::shared_ptr<IOProcessor> iop;
if ((iop = boost::dynamic_pointer_cast<IOProcessor>(*r)) != 0) {
- if (iop->output() && iop->output()->connected_to (other)) {
+ if (iop->feeds (other)) {
+ // cerr << "\tIOP " << iop->name() << " feeds " << other->name() << endl;
return true;
+ } else {
+ // cerr << "\tIOP " << iop->name() << " does NOT feeds " << other->name() << endl;
}
}
}
+ // cerr << "\tdoes NOT FEED " << other->name() << endl;
return false;
}
void
-Route::handle_transport_stopped (bool abort_ignored, bool did_locate, bool can_flush_processors)
+Route::handle_transport_stopped (bool /*abort_ignored*/, bool did_locate, bool can_flush_processors)
{
nframes_t now = _session.transport_frame();
}
void
-Route::input_change_handler (IOChange change, void *src)
+Route::input_change_handler (IOChange change, void * /*src*/)
{
if ((change & ConfigurationChanged)) {
configure_processors (0);
}
void
-Route::output_change_handler (IOChange change, void *src)
+Route::output_change_handler (IOChange change, void * /*src*/)
{
if ((change & ConfigurationChanged)) {
int
Route::no_roll (nframes_t nframes, sframes_t start_frame, sframes_t end_frame,
- bool session_state_changing, bool can_record, bool rec_monitors_input)
+ bool session_state_changing, bool /*can_record*/, bool /*rec_monitors_input*/)
{
if (n_outputs().n_total() == 0) {
return 0;
int
Route::roll (nframes_t nframes, sframes_t start_frame, sframes_t end_frame, int declick,
- bool can_record, bool rec_monitors_input)
+ bool /*can_record*/, bool /*rec_monitors_input*/)
{
{
// automation snapshot can also be called from the non-rt context
}
int
-Route::silent_roll (nframes_t nframes, sframes_t start_frame, sframes_t end_frame,
- bool can_record, bool rec_monitors_input)
+Route::silent_roll (nframes_t nframes, sframes_t /*start_frame*/, sframes_t /*end_frame*/,
+ bool /*can_record*/, bool /*rec_monitors_input*/)
{
silence (nframes);
return 0;
_session.set_dirty ();
}
}
+void
+Route::put_control_outs_at (Placement p)
+{
+ if (!_control_outs) {
+ return;
+ }
+
+ // Move meter in the processors list
+ ProcessorList::iterator loc = find(_processors.begin(), _processors.end(), _control_outs);
+ _processors.erase(loc);
+
+ switch (p) {
+ case PreFader:
+ loc = find(_processors.begin(), _processors.end(), _amp);
+ if (loc != _processors.begin()) {
+ --loc;
+ }
+ break;
+ case PostFader:
+ loc = find(_processors.begin(), _processors.end(), _amp);
+ assert (loc != _processors.end());
+ loc++;
+ break;
+ }
+
+ _processors.insert(loc, _control_outs);
+
+ processors_changed (); /* EMIT SIGNAL */
+ _session.set_dirty ();
+}
nframes_t
Route::update_total_latency ()
*/
void
-Route::shift (nframes64_t pos, nframes64_t frames)
+Route::shift (nframes64_t /*pos*/, nframes64_t /*frames*/)
{
#ifdef THIS_NEEDS_FIXING_FOR_V3
{
bool ret;
string ioproc_name;
+ string name;
+
+ name = Route::ensure_track_or_route_name (str, _session);
+ SessionObject::set_name (name);
- SessionObject::set_name (str);
-
- ret = (_input->set_name(str) && _output->set_name(str));
+ ret = (_input->set_name(name) && _output->set_name(name));
if (ret) {
for (ProcessorList::iterator i = _processors.begin(); i != _processors.end(); ++i) {
- /* rename all processors with outputs to reflect our new name */
+ /* rename all I/O processors that have inputs or outputs */
boost::shared_ptr<IOProcessor> iop = boost::dynamic_pointer_cast<IOProcessor> (*i);
- if (iop) {
- string iop_name = str;
- iop_name += '[';
- iop_name += "XXX FIX ME XXX";
- iop_name += ']';
-
- if (!iop->set_name (iop_name)) {
+ if (iop && (iop->output() || iop->input())) {
+ if (!iop->set_name (name)) {
ret = false;
}
}
}
boost::shared_ptr<Send>
-Route::send_for (boost::shared_ptr<const IO> target) const
+Route::internal_send_for (boost::shared_ptr<const Route> target) const
{
Glib::RWLock::ReaderLock lm (_processor_lock);
for (ProcessorList::const_iterator i = _processors.begin(); i != _processors.end(); ++i) {
- boost::shared_ptr<Send> send;
+ boost::shared_ptr<InternalSend> send;
- if ((send = boost::dynamic_pointer_cast<Send>(*i)) != 0) {
- if (send->output()->connected_to (target)) {
+ if ((send = boost::dynamic_pointer_cast<InternalSend>(*i)) != 0) {
+ if (send->target_route() == target) {
return send;
}
}
Route::meter ()
{
Glib::RWLock::ReaderLock rm (_processor_lock, Glib::TRY_LOCK);
+
+ assert (_meter);
+
_meter->meter ();
+
+ for (ProcessorList::iterator i = _processors.begin(); i != _processors.end(); ++i) {
+
+ boost::shared_ptr<Send> s;
+ boost::shared_ptr<Return> r;
+
+ if ((s = boost::dynamic_pointer_cast<Send> (*i)) != 0) {
+ s->meter()->meter();
+ } else if ((r = boost::dynamic_pointer_cast<Return> (*i)) != 0) {
+ r->meter()->meter ();
+ }
+ }
}
boost::shared_ptr<Panner>