/*
- Copyright (C) 2000 Paul Davis
+ Copyright (C) 2000 Paul Davis
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
#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)
, _solo_control (new SoloControllable (X_("solo"), *this))
, _mute_master (new MuteMaster (sess, name))
, _default_type (default_type)
-
+
{
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);
+
+ _set_state (node, Stateful::loading_state_version, 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;
_pending_declick = true;
_remote_control_id = 0;
_in_configure_processors = false;
-
- _edit_group = 0;
- _mix_group = 0;
+
+ _route_group = 0;
_phase_invert = 0;
_denormal_protection = false;
add_control (_solo_control);
add_control (_mute_master);
-
+
/* input and output objects */
_input.reset (new IO (_session, _name, IO::Input, _default_type));
_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) {
x->second = n;
}
- }
+ }
_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 (_mix_group->is_relative()) {
-
+ if (src != 0 && _route_group && src != _route_group && _route_group->active_property (RouteGroup::Gain)) {
+
+ if (_route_group->is_relative()) {
+
gain_t usable_gain = _amp->gain();
if (usable_gain < 0.000001f) {
usable_gain = 0.000001f;
}
-
+
gain_t delta = val;
if (delta < 0.000001f) {
delta = 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;
- }
+ }
if (val == _amp->gain()) {
return;
/** Process this route for one (sub) cycle (process thread)
*
* @param bufs Scratch buffers to use for the signal path
- * @param start_frame Initial transport frame
+ * @param start_frame Initial transport frame
* @param end_frame Final transport frame
* @param nframes Number of frames to output (to ports)
*
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.)
----------------------------------------------------------------------------------------- */
Amp::apply_gain (bufs, nframes, 0.0, 1.0);
} else if (declick < 0) {
Amp::apply_gain (bufs, nframes, 1.0, 0.0);
- }
+ }
_pending_declick = 0;
-
+
/* -------------------------------------------------------------------------------------------
DENORMAL CONTROL/PHASE INVERT
----------------------------------------------------------------------------------------- */
if (_phase_invert) {
-
+
int chn = 0;
if (_denormal_protection || Config->get_denormal_protection()) {
-
+
for (BufferSet::audio_iterator i = bufs.audio_begin(); i != bufs.audio_end(); ++i, ++chn) {
Sample* const sp = i->data();
for (BufferSet::audio_iterator i = bufs.audio_begin(); i != bufs.audio_end(); ++i, ++chn) {
Sample* const sp = i->data();
-
+
if (_phase_invert & chn) {
for (nframes_t nx = 0; nx < nframes; ++nx) {
sp[nx] = -sp[nx];
}
- }
+ }
+ }
+ }
+
+ } else {
+
+ if (_denormal_protection || Config->get_denormal_protection()) {
+
+ for (BufferSet::audio_iterator i = bufs.audio_begin(); i != bufs.audio_end(); ++i) {
+ Sample* const sp = i->data();
+ for (nframes_t nx = 0; nx < nframes; ++nx) {
+ sp[nx] += 1.0e-27f;
+ }
}
+
}
}
if (rm.locked()) {
for (ProcessorList::iterator i = _processors.begin(); i != _processors.end(); ++i) {
- bufs.set_count (ChanCount::max(bufs.count(), (*i)->input_streams()));
- (*i)->run_in_place (bufs, start_frame, end_frame, nframes);
+ if (bufs.count() != (*i)->input_streams()) {
+ cerr << _name << " bufs = " << bufs.count()
+ << " input for " << (*i)->name() << " = " << (*i)->input_streams()
+ << endl;
+ }
+ assert (bufs.count() == (*i)->input_streams());
+ (*i)->run (bufs, start_frame, end_frame, nframes);
bufs.set_count (ChanCount::max(bufs.count(), (*i)->output_streams()));
}
_silent = false;
assert (bufs.available() >= _input->n_ports());
-
+
if (_input->n_ports() == ChanCount::ZERO) {
silence (nframes);
}
-
+
bufs.set_count (_input->n_ports());
-
- 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);
+ if (is_control() && _session.listening()) {
+
+ /* 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);
+
+ } 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);
+ }
}
}
+ write_out_of_band_data (bufs, start_frame, end_frame, nframes);
process_output_buffers (bufs, start_frame, end_frame, nframes, true, declick);
}
void
Route::passthru_silence (sframes_t start_frame, sframes_t end_frame, nframes_t nframes, int declick)
{
- process_output_buffers (_session.get_silent_buffers (n_process_buffers()), start_frame, end_frame, nframes, true, declick);
+ BufferSet& bufs (_session.get_silent_buffers (n_process_buffers()));
+ bufs.set_count (_input->n_ports());
+ write_out_of_band_data (bufs, start_frame, end_frame, nframes);
+ process_output_buffers (bufs, 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
+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;
}
_mute_master->mute (yn);
mute_changed (src);
}
-}
+}
bool
-Route::muted() const
+Route::muted() const
{
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);
// Set up processor list channels. This will set processor->[input|output]_streams(),
// configure redirect ports properly, etc.
-
if (configure_processors_unlocked (err)) {
ProcessorList::iterator ploc = loc;
--ploc;
_processors.erase(ploc);
configure_processors_unlocked (0); // it worked before we tried to add it ...
+ cerr << "configure failed\n";
return -1;
}
-
+
if ((pi = boost::dynamic_pointer_cast<PluginInsert>(processor)) != 0) {
-
+
if (pi->natural_input_streams() == ChanCount::ZERO) {
/* generator plugin */
_have_internal_generator = true;
}
-
- }
-
- 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 ();
+ if (_control_outs != processor) {
+ // 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));
_output->set_user_latency (0);
}
-
+
processors_changed (); /* EMIT SIGNAL */
-
+
return 0;
}
bool
-Route::add_processor_from_xml (const XMLNode& node, Placement placement)
+Route::add_processor_from_xml (const XMLNode& node, ProcessorList::iterator iter)
{
- 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();
+ const XMLProperty *prop;
+
+ if (node.name() != "Processor") {
+ return false;
}
- return add_processor_from_xml (node, loc);
-}
+ try {
+ if ((prop = node.property ("type")) != 0) {
-bool
-Route::add_processor_from_xml (const XMLNode& node, ProcessorList::iterator iter)
-{
- const XMLProperty *prop;
+ boost::shared_ptr<Processor> processor;
- // 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") {
-
- try {
- if ((prop = node.property ("type")) != 0) {
+ if (prop->value() == "ladspa" || prop->value() == "Ladspa" ||
+ prop->value() == "lv2" ||
+ prop->value() == "vst" ||
+ prop->value() == "audiounit") {
+ processor.reset (new PluginInsert(_session, node));
- cerr << _name << " : got processor type " << prop->value() << endl;
+ } else if (prop->value() == "port") {
- boost::shared_ptr<Processor> processor;
- bool have_insert = false;
+ processor.reset (new PortInsert (_session, _mute_master, node));
- 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;
-
- } else if (prop->value() == "port") {
+ } else if (prop->value() == "send") {
- processor.reset (new PortInsert (_session, _mute_master, node));
-
- } else if (prop->value() == "send") {
-
- processor.reset (new Send (_session, _mute_master, node));
- have_insert = true;
-
- } else if (prop->value() == "meter") {
-
- processor = _meter;
- processor->set_state (node);
-
- } else if (prop->value() == "amp") {
-
- processor = _amp;
- processor->set_state (node);
-
- } else if (prop->value() == "listen" || prop->value() == "deliver") {
-
- /* XXX need to generalize */
-
- processor = _control_outs;
- processor->set_state (node);
-
- } else if (prop->value() == "main-outs") {
-
- processor = _main_outs;
- processor->set_state (node);
+ processor.reset (new Send (_session, _mute_master, node));
+
+ } else if (prop->value() == "meter") {
+
+ if (_meter) {
+ if (_meter->set_state (node, Stateful::loading_state_version)) {
+ return false;
+ } else {
+ return true;
+ }
+ }
+
+ _meter.reset (new PeakMeter (_session, node));
+ processor = _meter;
+ } else if (prop->value() == "amp") {
+
+ /* amp always exists */
+
+ processor = _amp;
+ if (processor->set_state (node, Stateful::loading_state_version)) {
+ return false;
} else {
+ /* never any reason to add it */
+ return true;
+ }
- error << string_compose(_("unknown Processor type \"%1\"; ignored"), prop->value()) << endmsg;
+ } else if (prop->value() == "intsend") {
+
+ processor.reset (new InternalSend (_session, _mute_master, node));
+
+ } else if (prop->value() == "intreturn") {
+
+ if (_intreturn) {
+ if (_intreturn->set_state (node, Stateful::loading_state_version)) {
+ return false;
+ } else {
+ return true;
+ }
}
-
- 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;
- cerr << "Let's check " << (*p)->name() << " vis ? " << (*p)->visible() << endl;
- while (!(*p)->visible() && p != _processors.begin()) {
- --p;
+ _intreturn.reset (new InternalReturn (_session, node));
+ processor = _intreturn;
+
+ } else if (prop->value() == "main-outs") {
+
+ if (_main_outs) {
+ if (_main_outs->set_state (node, Stateful::loading_state_version)) {
+ return false;
+ } else {
+ return true;
}
- ++p;
- iter = p;
}
- return (add_processor (processor, iter) == 0);
-
+ _main_outs.reset (new Delivery (_session, _output, _mute_master, node));
+ processor = _main_outs;
+
} else {
- error << _("Processor XML node has no type property") << endmsg;
+ 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();
+ --p;
+ while (!(*p)->visible() && p != _processors.begin()) {
+ --p;
+ }
+ ++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)
+
+bool
+Route::add_processor_from_xml_2X (const XMLNode& node, int version, ProcessorList::iterator iter)
{
- 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();
+ const XMLProperty *prop;
- if (!_processors.empty()) {
+ try {
+ boost::shared_ptr<Processor> processor;
+
+ if (node.name() == "Insert") {
+
+ if ((prop = node.property ("type")) != 0) {
+
+ if (prop->value() == "ladspa" || prop->value() == "Ladspa" ||
+ prop->value() == "lv2" ||
+ prop->value() == "vst" ||
+ prop->value() == "audiounit") {
+
+ processor.reset (new PluginInsert (_session, node));
+
+ } else {
+
+ processor.reset (new PortInsert (_session, _mute_master, node));
+ }
+
+ }
+
+ } else if (node.name() == "Send") {
+
+ processor.reset (new Send (_session, _mute_master, node, version));
+
+ } else {
+
+ error << string_compose(_("unknown Processor type \"%1\"; ignored"), node.name()) << 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();
--p;
- cerr << "Let's check " << (*p)->name() << " vis ? " << (*p)->visible() << endl;
while (!(*p)->visible() && p != _processors.begin()) {
--p;
}
++p;
- loc = p;
+ iter = p;
}
+
+ return (add_processor (processor, iter) == 0);
+ }
+
+ catch (failed_constructor &err) {
+ warning << _("processor could not be created. Ignored.") << endmsg;
+ return false;
+ }
+}
+
+int
+Route::add_processors (const ProcessorList& others, boost::shared_ptr<Processor> before, ProcessorStreams* err)
+{
+ ProcessorList::iterator loc;
+
+ 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);
ChanCount potential_max_streams = ChanCount::max (_input->n_ports(), _output->n_ports());
for (ProcessorList::const_iterator i = others.begin(); i != others.end(); ++i) {
-
+
// Ensure meter only appears in the list once
if (*i == _meter) {
ProcessorList::iterator m = find(_processors.begin(), _processors.end(), *i);
_processors.erase(m);
}
}
-
+
boost::shared_ptr<PluginInsert> pi;
-
+
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)) {
++existing_end;
_processors.erase (existing_end, _processors.end());
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));
}
_output->set_user_latency (0);
}
-
+
processors_changed (); /* EMIT SIGNAL */
return 0;
Route::disable_processors (Placement p)
{
Glib::RWLock::ReaderLock lm (_processor_lock);
-
+
ProcessorList::iterator start, end;
placement_range(p, start, end);
-
+
for (ProcessorList::iterator i = start; i != end; ++i) {
(*i)->deactivate ();
}
_session.set_dirty ();
}
-/** Turn off all redirects
+/** Turn off all redirects
*/
void
Route::disable_processors ()
{
Glib::RWLock::ReaderLock lm (_processor_lock);
-
+
for (ProcessorList::iterator i = _processors.begin(); i != _processors.end(); ++i) {
(*i)->deactivate ();
}
-
+
_session.set_dirty ();
}
Route::disable_plugins (Placement p)
{
Glib::RWLock::ReaderLock lm (_processor_lock);
-
+
ProcessorList::iterator start, end;
placement_range(p, start, end);
-
+
for (ProcessorList::iterator i = start; i != end; ++i) {
if (boost::dynamic_pointer_cast<PluginInsert> (*i)) {
(*i)->deactivate ();
}
}
-
+
_session.set_dirty ();
}
Route::disable_plugins ()
{
Glib::RWLock::ReaderLock lm (_processor_lock);
-
+
for (ProcessorList::iterator i = _processors.begin(); i != _processors.end(); ++i) {
if (boost::dynamic_pointer_cast<PluginInsert> (*i)) {
(*i)->deactivate ();
}
}
-
+
_session.set_dirty ();
}
Route::ab_plugins (bool forward)
{
Glib::RWLock::ReaderLock lm (_processor_lock);
-
+
if (forward) {
/* forward = turn off all active redirects, and mark them so that the next time
}
}
}
-
- _session.set_dirty ();
-}
-
-
-/* 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 ();
- }
+ _session.set_dirty ();
}
if (!_session.engine().connected()) {
return;
}
-
+
bool already_deleting = _session.deletion_in_progress();
if (!already_deleting) {
_session.set_deletion_in_progress();
Glib::RWLock::WriterLock lm (_processor_lock);
ProcessorList new_list;
ProcessorStreams err;
+ bool seen_amp = false;
+
+ for (ProcessorList::iterator i = _processors.begin(); i != _processors.end(); ++i) {
- 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 ();
+ if (*i == _amp) {
+ seen_amp = true;
}
- // Keep the rest
- for (ProcessorList::iterator i = amp_loc; i != _processors.end(); ++i) {
- new_list.push_back (*i);
- }
- } 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;
+ }
+ }
}
}
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;
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));
in = out;
return -1;
}
}
-
+
// We can, so configure everything
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;
}
bool first_is_on = _processors.front()->active();
-
+
for (ProcessorList::iterator i = _processors.begin(); i != _processors.end(); ++i) {
if (first_is_on) {
(*i)->deactivate ();
(*i)->activate ();
}
}
-
+
_session.set_dirty ();
}
}
}
}
-
+
_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;
- niter = new_order.begin();
+ oiter = _processors.begin();
+ niter = new_order.begin();
while (niter != new_order.end()) {
-
+
/* if the next processor in the old list is invisible (i.e. should not be in the new order)
- then append it to the temp list.
+ then append it to the temp list.
Otherwise, see if the next processor in the old list is in the new list. if not,
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
+ /* 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;
} else {
-
+
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;
}
}
-
+
/* now remove from old order - its taken care of no matter what */
oiter = _processors.erase (oiter);
}
-
+
}
_processors.insert (oiter, as_it_will_be.begin(), as_it_will_be.end());
_processors = as_it_was_before;
processor_max_streams = old_pms;
return -1;
- }
- }
+ }
+ }
processors_changed (); /* EMIT SIGNAL */
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("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;
- OrderKeys::iterator x = order_keys.begin();
+ OrderKeys::iterator x = order_keys.begin();
while (x != order_keys.end()) {
order_string += string ((*x).first);
order_string += '=';
snprintf (buf, sizeof(buf), "%ld", (*x).second);
order_string += buf;
-
+
++x;
if (x == order_keys.end()) {
}
node->add_property ("order-keys", order_string);
- 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_master->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_master->get_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);
+ }
+
+ for (i = _processors.begin(); i != _processors.end(); ++i) {
+ node->add_child_nocopy((*i)->state (full_state));
+ }
+
+ if (_extra_xml){
+ node->add_child_copy (*_extra_xml);
+ }
+
+ return *node;
+}
+
+int
+Route::set_state (const XMLNode& node, int version)
+{
+ return _set_state (node, version, true);
+}
+
+int
+Route::_set_state (const XMLNode& node, int version, bool /*call_base*/)
+{
+ if (version < 3000) {
+ return _set_state_2X (node, version);
+ }
+
+ XMLNodeList nlist;
+ XMLNodeConstIterator niter;
+ XMLNode *child;
+ XMLPropertyList plist;
+ const XMLProperty *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());
+ }
+
+ if ((prop = node.property ("id")) != 0) {
+ _id = prop->value ();
+ }
+
+ if ((prop = node.property (X_("flags"))) != 0) {
+ _flags = Flag (string_2_enum (prop->value(), _flags));
+ } 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, version);
+ } else if (prop->value() == "Output") {
+ _output->set_state (*child, version);
+ }
+ }
+
+ 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 (string_is_affirmative (prop->value()), this);
+ }
+
+ if ((prop = node.property (X_("phase-invert"))) != 0) {
+ set_phase_invert (string_is_affirmative (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());
+ _active = !yn; // force switch
+ set_active (yn);
+ }
+
+ if ((prop = node.property (X_("soloed"))) != 0) {
+ bool yn = string_is_affirmative (prop->value());
+
+ /* XXX force reset of solo status */
+
+ set_solo (yn, this);
+ }
+
+ if ((prop = node.property (X_("meter-point"))) != 0) {
+ _meter_point = MeterPoint (string_2_enum (prop->value (), _meter_point));
+ }
+
+ 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;
+
+ 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(), "%ld", &n) != 1) {
+ error << string_compose (_("badly formed order key string in state file! [%1] ... ignored."), remaining)
+ << endmsg;
+ } else {
+ set_order_key (remaining.substr (0, equal), n);
+ }
+ }
+
+ colon = remaining.find_first_of (':');
+
+ if (colon != string::npos) {
+ remaining = remaining.substr (colon+1);
+ } else {
+ break;
+ }
+ }
+ }
+
+ for (niter = nlist.begin(); niter != nlist.end(); ++niter){
+ child = *niter;
+
+ if (child->name() == X_("Comment")) {
+
+ /* XXX this is a terrible API design in libxml++ */
+
+ XMLNode *cmt = *(child->children().begin());
+ _comment = cmt->content();
+
+ } else if (child->name() == X_("Extra")) {
+
+ _extra_xml = new XMLNode (*child);
- 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);
+ } else if (child->name() == X_("Controllable") && (prop = child->property("name")) != 0) {
- if (_comment.length()) {
- XMLNode *cmt = node->add_child ("Comment");
- cmt->add_content (_comment);
- }
+ if (prop->value() == "solo") {
+ _solo_control->set_state (*child, version);
+ _session.add_controllable (_solo_control);
+ }
- for (i = _processors.begin(); i != _processors.end(); ++i) {
- node->add_child_nocopy((*i)->state (full_state));
- }
+ } 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 (x);
+ }
- if (_extra_xml){
- node->add_child_copy (*_extra_xml);
+ } else if (child->name() == X_("MuteMaster")) {
+ _mute_master->set_state (*child, version);
+ }
}
-
- return *node;
-}
-int
-Route::set_state (const XMLNode& node)
-{
- return _set_state (node, true);
+ return 0;
}
int
-Route::_set_state (const XMLNode& node, bool call_base)
+Route::_set_state_2X (const XMLNode& node, int version)
{
-
XMLNodeList nlist;
XMLNodeConstIterator niter;
XMLNode *child;
XMLPropertyList plist;
const XMLProperty *prop;
- if (node.name() != "Route"){
+ /* 2X things which still remain to be handled:
+ * default-type
+ * muted
+ * mute-affects-pre-fader
+ * mute-affects-post-fader
+ * mute-affects-control-outs
+ * mute-affects-main-outs
+ * automation
+ * controlouts
+ */
+
+ 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());
- }
-
if ((prop = node.property (X_("flags"))) != 0) {
_flags = Flag (string_2_enum (prop->value(), _flags));
} else {
_flags = Flag (0);
}
-
+
+ /* add standard processors */
+
+ _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);
+
+ /* IOs */
+
+ nlist = node.children ();
+ for (niter = nlist.begin(); niter != nlist.end(); ++niter) {
+
+ child = *niter;
+
+ if (child->name() == IO::state_node_name) {
+
+ /* there is a note in IO::set_state_2X() about why we have to call
+ this directly.
+ */
+
+ _input->set_state_2X (*child, version, true);
+ _output->set_state_2X (*child, version, false);
+
+ if ((prop = child->property (X_("name"))) != 0) {
+ set_name (prop->value ());
+ }
+
+ if ((prop = child->property (X_("id"))) != 0) {
+ _id = prop->value ();
+ }
+
+ if ((prop = child->property (X_("active"))) != 0) {
+ bool yn = string_is_affirmative (prop->value());
+ _active = !yn; // force switch
+ set_active (yn);
+ }
+ }
+
+ /* XXX: panners? */
+ }
+
if ((prop = node.property (X_("phase-invert"))) != 0) {
- set_phase_invert (prop->value()=="yes"?true:false, this);
+ set_phase_invert (string_is_affirmative (prop->value()));
}
if ((prop = node.property (X_("denormal-protection"))) != 0) {
- set_denormal_protection (prop->value()=="yes"?true:false, this);
- }
-
- if ((prop = node.property (X_("active"))) != 0) {
- bool yn = (prop->value() == "yes");
- _active = !yn; // force switch
- set_active (yn);
+ set_denormal_protection (string_is_affirmative (prop->value()));
}
if ((prop = node.property (X_("soloed"))) != 0) {
- bool yn = (prop->value()=="yes");
+ bool yn = string_is_affirmative (prop->value());
/* XXX force reset of solo status */
if ((prop = node.property (X_("meter-point"))) != 0) {
_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* 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;
+ 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);
}
}
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;
+ << endmsg;
} else {
if (sscanf (remaining.substr (equal+1).c_str(), "%ld", &n) != 1) {
error << string_compose (_("badly formed order key string in state file! [%1] ... ignored."), remaining)
- << endmsg;
+ << 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"));
+ XMLNodeList redirect_nodes;
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);
+ if (child->name() == X_("Send") || child->name() == X_("Insert")) {
+ redirect_nodes.push_back(child);
}
+
}
- set_processor_state (processor_state);
-
+ set_processor_state_2X (redirect_nodes, version);
+
for (niter = nlist.begin(); niter != nlist.end(); ++niter){
child = *niter;
_extra_xml = new XMLNode (*child);
} else if (child->name() == X_("Controllable") && (prop = child->property("name")) != 0) {
-
+
if (prop->value() == "solo") {
- _solo_control->set_state (*child);
+ _solo_control->set_state (*child, version);
_session.add_controllable (_solo_control);
- }
+ }
} else if (child->name() == X_("RemoteControl")) {
if ((prop = child->property (X_("id"))) != 0) {
sscanf (prop->value().c_str(), "%d", &x);
set_remote_control_id (x);
}
- }
- }
- 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;
return *root;
}
+void
+Route::set_processor_state_2X (XMLNodeList const & nList, int version)
+{
+ /* We don't bother removing existing processors not in nList, as this
+ method will only be called when creating a Route from scratch, not
+ for undo purposes. Just put processors in at the appropriate place
+ in the list.
+ */
+
+ for (XMLNodeConstIterator i = nList.begin(); i != nList.end(); ++i) {
+ add_processor_from_xml_2X (**i, version, _processors.begin ());
+ }
+}
+
void
Route::set_processor_state (const XMLNode& node)
{
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;
}
}
-
+
if (!processorInStateList) {
remove_processor (*i);
}
// 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;
- }
+ XMLProperty* prop = (*niter)->property ("type");
o = i;
- if (prop->value() != "meter" && prop->value() != "amp" && prop->value() != "main-outs") {
+ // 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
- // Check whether the next processor in the list
-
+ if (prop->value() != "amp") {
while (o != _processors.end()) {
XMLProperty* id_prop = (*niter)->property(X_("id"));
if (id_prop && (*o)->id() == id_prop->value()) {
break;
}
-
+
++o;
}
}
// 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
}
- (*i)->set_state (**niter);
+ // and make it (just) so
+
+ (*i)->set_state (**niter, Stateful::current_state_version);
}
}
the XML state represents a working signal route.
*/
- if (!has_meter_processor) {
- set_meter_point (_meter_point, NULL);
- }
-
processors_changed ();
}
if (!_silent) {
_output->silence (nframes);
-
- {
+
+ {
Glib::RWLock::ReaderLock lm (_processor_lock, Glib::TRY_LOCK);
-
+
if (lm.locked()) {
for (ProcessorList::iterator i = _processors.begin(); i != _processors.end(); ++i) {
boost::shared_ptr<PluginInsert> pi;
// skip plugins, they don't need anything when we're not active
continue;
}
-
+
(*i)->silence (nframes);
}
}
}
}
-
+
}
-}
+}
-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));
+ if (!_intreturn) {
+ _intreturn.reset (new InternalReturn (_session));
+ add_processor (_intreturn, PreFader);
+ }
+}
- /* 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)
- */
+BufferSet*
+Route::get_return_buffer () const
+{
+ Glib::RWLock::ReaderLock rm (_processor_lock);
- if (listener->output()->ensure_io (n_outputs(), true, this)) {
- return boost::shared_ptr<Delivery>();
+ 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 listener;
+ return 0;
+}
+
+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);
- if (d && d->output() == io) {
+ 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);
+ }
+
/* 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;
}
-
- return 0;
-}
+ 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);
-
- if (d && d->output() == io) {
- /* already listening via the specified IO: do nothing */
- remove_processor (*x, &err);
-
- }
-
- x = tmp;
- }
-}
-void
-Route::set_edit_group (RouteGroup *eg, void *src)
+ boost::shared_ptr<InternalSend> d = boost::dynamic_pointer_cast<InternalSend>(*x);
-{
- if (eg == _edit_group) {
- return;
- }
+ if (d && d->target_route() == route) {
+ rl.release ();
+ remove_processor (*x, &err);
+ rl.acquire ();
- if (_edit_group) {
- _edit_group->remove (this);
- }
+ /* list could have been demolished while we dropped the lock
+ so start over.
+ */
- if ((_edit_group = eg) != 0) {
- _edit_group->add (this);
+ goto again;
+ }
}
- _session.set_dirty ();
- edit_group_changed (src); /* EMIT SIGNAL */
-}
+ rl.release ();
-void
-Route::drop_edit_group (void *src)
-{
- _edit_group = 0;
- _session.set_dirty ();
- edit_group_changed (src); /* EMIT SIGNAL */
+ if (route == _session.control_out()) {
+ _control_outs.reset ();
+ }
}
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();
}
for (ProcessorList::iterator i = _processors.begin(); i != _processors.end(); ++i) {
-
+
if (Config->get_plugins_stop_with_transport() && can_flush_processors) {
(*i)->deactivate ();
(*i)->activate ();
}
-
+
(*i)->transport_stopped (now);
}
}
}
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)) {
-
+
/* XXX resize all listeners to match _main_outs? */
-
+
// configure_processors (0);
}
}
if (n_outputs().n_audio() < 2) {
return 0;
}
-
+
return max (n_inputs ().n_audio(), processor_max_streams.n_audio());
}
-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)
+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*/)
{
if (n_outputs().n_total() == 0) {
return 0;
nframes -= _roll_delay;
silence (_roll_delay);
- /* we've written _roll_delay of samples into the
+ /* we've written _roll_delay of samples into the
output ports, so make a note of that for
future reference.
*/
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
silence (nframes);
return 0;
}
-
+
nframes_t unused = 0;
if ((nframes = check_initial_delay (nframes, unused)) == 0) {
}
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;
// FIXME: what about sends? - they don't return a signal back to ardour?
boost::shared_ptr<const PortInsert> pi;
-
+
for (ProcessorList::const_iterator i = _processors.begin(); i != _processors.end(); ++i) {
if ((pi = boost::dynamic_pointer_cast<const PortInsert>(*i)) != 0) {
for (PortSet::const_iterator port = pi->output()->ports().begin(); port != pi->output()->ports().end(); ++port) {
-
+
string port_name = port->name();
string client_name = port_name.substr (0, port_name.find(':'));
/* only say "yes" if the redirect is actually in use */
-
+
if (client_name != "ardour" && pi->active()) {
return true;
}
break;
}
_processors.insert(loc, _meter);
-
+
meter_change (src); /* EMIT SIGNAL */
processors_changed (); /* EMIT SIGNAL */
_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 ()
#endif
_output->set_port_latency (own_latency);
-
+
if (_output->user_latency() == 0) {
/* this (virtual) function is used for pure Routes,
port, not prerecorded material, and therefore we
have to take into account any input latency.
*/
-
+
own_latency += _input->signal_latency ();
}
_output->set_latency_delay (own_latency);
signal_latency_changed (); /* EMIT SIGNAL */
}
-
+
#ifdef DEBUG_LATENCY
cerr << _name << ": input latency = " << _input->signal_latency() << " total = "
<< own_latency << endl;
}
Route::SoloControllable::SoloControllable (std::string name, Route& r)
- : AutomationControl (r.session(), Evoral::Parameter (SoloAutomation),
+ : AutomationControl (r.session(), Evoral::Parameter (SoloAutomation),
boost::shared_ptr<AutomationList>(), name)
, route (r)
{
Route::SoloControllable::set_value (float val)
{
bool bval = ((val >= 0.5f) ? true: false);
-
+
route.set_solo (bval, this);
}
return route.soloed() ? 1.0f : 0.0f;
}
-void
+void
Route::set_block_size (nframes_t nframes)
{
for (ProcessorList::iterator i = _processors.begin(); i != _processors.end(); ++i) {
*/
void
-Route::shift (nframes64_t pos, nframes64_t frames)
+Route::shift (nframes64_t /*pos*/, nframes64_t /*frames*/)
{
#ifdef THIS_NEEDS_FIXING_FOR_V3
{
Glib::RWLock::ReaderLock lm (redirect_lock);
for (RedirectList::iterator i = _redirects.begin (); i != _redirects.end (); ++i) {
-
+
set<uint32_t> a;
(*i)->what_has_automation (a);
-
+
for (set<uint32_t>::const_iterator j = a.begin (); j != a.end (); ++j) {
AutomationList & al = (*i)->automation_list (*j);
XMLNode &before = al.get_state ();
{
XMLNode& node (state (false));
XMLTree tree;
-
+
IO::set_name_in_state (*node.children().front(), name);
-
+
tree.set_root (&node);
return tree.write (path.c_str());
}
{
bool ret;
string ioproc_name;
-
- SessionObject::set_name (str);
-
- ret = (_input->set_name(str) && _output->set_name(str));
+ string name;
+
+ name = Route::ensure_track_or_route_name (str, _session);
+ SessionObject::set_name (name);
+
+ ret = (_input->set_name(name) && _output->set_name(name));
if (ret) {
-
+
Glib::RWLock::ReaderLock lm (_processor_lock);
-
+
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;
-
- if ((send = boost::dynamic_pointer_cast<Send>(*i)) != 0) {
- if (send->output()->connected_to (target)) {
+ boost::shared_ptr<InternalSend> send;
+
+ if ((send = boost::dynamic_pointer_cast<InternalSend>(*i)) != 0) {
+ if (send->target_route() == target) {
return send;
}
}
}
-
+
return boost::shared_ptr<Send>();
}
void
-Route::set_phase_invert (bool yn, void *src)
+Route::set_phase_invert (bool yn)
{
if (_phase_invert != yn) {
- _phase_invert = yn;
- // phase_invert_changed (src); /* EMIT SIGNAL */
+ _phase_invert = 0xffff; // XXX all channels
+ phase_invert_changed (); /* EMIT SIGNAL */
}
}
}
void
-Route::set_denormal_protection (bool yn, void *src)
+Route::set_denormal_protection (bool yn)
{
if (_denormal_protection != yn) {
_denormal_protection = yn;
- // denormal_protection_changed (src); /* EMIT SIGNAL */
+ denormal_protection_changed (); /* EMIT SIGNAL */
}
}
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>
if (!c) {
/* maybe one of our processors does or ... */
-
+
Glib::RWLock::ReaderLock rm (_processor_lock, Glib::TRY_LOCK);
for (ProcessorList::iterator i = _processors.begin(); i != _processors.end(); ++i) {
if ((c = boost::dynamic_pointer_cast<AutomationControl>((*i)->data().control (param))) != 0) {
}
}
}
-
+
if (!c) {
/* nobody does so we'll make a new one */