#include "ardour/timestamps.h"
#include "ardour/utils.h"
#include "ardour/graph.h"
+#include "ardour/unknown_processor.h"
#include "i18n.h"
/* add standard controls */
_solo_control->set_flags (Controllable::Flag (_solo_control->flags() | Controllable::Toggle));
- _mute_control->set_flags (Controllable::Flag (_solo_control->flags() | Controllable::Toggle));
+ _mute_control->set_flags (Controllable::Flag (_mute_control->flags() | Controllable::Toggle));
add_control (_solo_control);
add_control (_mute_control);
_input->changed.connect_same_thread (*this, boost::bind (&Route::input_change_handler, this, _1, _2));
_output->changed.connect_same_thread (*this, boost::bind (&Route::output_change_handler, this, _1, _2));
+ _input->PortCountChanging.connect_same_thread (*this, boost::bind (&Route::input_port_count_changing, this, _1));
+
/* add amp processor */
_amp.reset (new Amp (_session));
}
}
- _route_group->apply (&Route::inc_gain, factor, _route_group);
+ _route_group->foreach_route (boost::bind (&Route::inc_gain, _1, factor, _route_group));
} else {
- _route_group->apply (&Route::set_gain, val, _route_group);
+ _route_group->foreach_route (boost::bind (&Route::set_gain, _1, val, _route_group));
}
return;
*/
void
Route::process_output_buffers (BufferSet& bufs,
- framepos_t start_frame, framepos_t end_frame, nframes_t nframes,
- bool /*with_processors*/, int declick)
+ framepos_t start_frame, framepos_t end_frame, pframes_t nframes,
+ bool /*with_processors*/, int declick,
+ bool gain_automation_ok)
{
bool monitor;
}
/* figure out if we're going to use gain automation */
- _amp->setup_gain_automation (start_frame, end_frame, nframes);
-
-
+ if (gain_automation_ok) {
+ _amp->setup_gain_automation (start_frame, end_frame, nframes);
+ } else {
+ _amp->apply_gain_automation (false);
+ }
+
/* tell main outs what to do about monitoring */
_main_outs->no_outs_cuz_we_no_monitor (!monitor);
Sample* const sp = i->data();
if (_phase_invert[chn]) {
- for (nframes_t nx = 0; nx < nframes; ++nx) {
+ for (pframes_t nx = 0; nx < nframes; ++nx) {
sp[nx] = -sp[nx];
sp[nx] += 1.0e-27f;
}
} else {
- for (nframes_t nx = 0; nx < nframes; ++nx) {
+ for (pframes_t nx = 0; nx < nframes; ++nx) {
sp[nx] += 1.0e-27f;
}
}
Sample* const sp = i->data();
if (_phase_invert[chn]) {
- for (nframes_t nx = 0; nx < nframes; ++nx) {
+ for (pframes_t nx = 0; nx < nframes; ++nx) {
sp[nx] = -sp[nx];
}
}
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) {
+ for (pframes_t nx = 0; nx < nframes; ++nx) {
sp[nx] += 1.0e-27f;
}
}
for (ProcessorList::iterator i = _processors.begin(); i != _processors.end(); ++i) {
+ if (boost::dynamic_pointer_cast<UnknownProcessor> (*i)) {
+ break;
+ }
+
if (bufs.count() != (*i)->input_streams()) {
cerr << _name << " bufs = " << bufs.count()
<< " input for " << (*i)->name() << " = " << (*i)->input_streams()
}
void
-Route::passthru (framepos_t start_frame, framepos_t end_frame, nframes_t nframes, int declick)
+Route::passthru (framepos_t start_frame, framepos_t end_frame, pframes_t nframes, int declick)
{
BufferSet& bufs = _session.get_scratch_buffers (n_process_buffers());
}
write_out_of_band_data (bufs, start_frame, end_frame, nframes);
- process_output_buffers (bufs, start_frame, end_frame, nframes, true, declick);
+ process_output_buffers (bufs, start_frame, end_frame, nframes, true, declick, true);
}
void
-Route::passthru_silence (framepos_t start_frame, framepos_t end_frame, nframes_t nframes, int declick)
+Route::passthru_silence (framepos_t start_frame, framepos_t end_frame, pframes_t nframes, int 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);
+ process_output_buffers (bufs, start_frame, end_frame, nframes, true, declick, false);
}
void
}
if (_route_group && src != _route_group && _route_group->is_active() && _route_group->is_solo()) {
- _route_group->apply (&Route::set_solo, yn, _route_group);
+ _route_group->foreach_route (boost::bind (&Route::set_solo, _1, yn, _route_group));
return;
}
}
if (_route_group && src != _route_group && _route_group->is_active() && _route_group->is_solo()) {
- _route_group->apply (&Route::set_solo_isolated, yn, _route_group);
+ _route_group->foreach_route (boost::bind (&Route::set_solo_isolated, _1, yn, _route_group));
return;
}
if (_mute_master->muted_by_self()) {
mute_changed (this); /* EMIT SIGNAL */
+ _mute_control->Changed (); /* EMIT SIGNAL */
}
}
Route::set_mute (bool yn, void *src)
{
if (_route_group && src != _route_group && _route_group->is_active() && _route_group->is_mute()) {
- _route_group->apply (&Route::set_mute, yn, _route_group);
+ _route_group->foreach_route (boost::bind (&Route::set_mute, _1, yn, _route_group));
return;
}
if (muted() != yn) {
_mute_master->set_muted_by_self (yn);
mute_changed (src); /* EMIT SIGNAL */
+ _mute_control->Changed (); /* EMIT SIGNAL */
}
}
// 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;
+ {
+ Glib::Mutex::Lock lm (AudioEngine::instance()->process_lock ());
+
+ 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) {
(*i)->activate ();
}
- if (configure_processors_unlocked (err)) {
- _processors.erase (inserted);
- configure_processors_unlocked (0); // it worked before we tried to add it ...
- return -1;
+ {
+ Glib::Mutex::Lock lm (AudioEngine::instance()->process_lock ());
+ if (configure_processors_unlocked (err)) {
+ _processors.erase (inserted);
+ configure_processors_unlocked (0); // it worked before we tried to add it ...
+ return -1;
+ }
}
(*i)->ActiveChanged.connect_same_thread (*this, boost::bind (&Session::update_latency_compensation, &_session, false, false));
}
_processors = new_list;
- configure_processors_unlocked (&err); // this can't fail
+
+ {
+ Glib::Mutex::Lock lm (AudioEngine::instance()->process_lock ());
+ configure_processors_unlocked (&err); // this can't fail
+ }
}
processor_max_streams.reset();
return 1;
}
- if (configure_processors_unlocked (err)) {
- /* get back to where we where */
- _processors.insert (i, processor);
- /* we know this will work, because it worked before :) */
- configure_processors_unlocked (0);
- return -1;
+ {
+ Glib::Mutex::Lock lm (AudioEngine::instance()->process_lock ());
+
+ if (configure_processors_unlocked (err)) {
+ /* get back to where we where */
+ _processors.insert (i, processor);
+ /* we know this will work, because it worked before :) */
+ configure_processors_unlocked (0);
+ return -1;
+ }
}
_have_internal_generator = false;
_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;
+ {
+ Glib::Mutex::Lock lm (AudioEngine::instance()->process_lock ());
+
+ 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;
return 0;
}
-
+/** Caller must hold process lock */
int
Route::configure_processors (ProcessorStreams* err)
{
+ assert (!AudioEngine::instance()->process_lock().trylock());
+
if (!_in_configure_processors) {
Glib::RWLock::WriterLock lm (_processor_lock);
return configure_processors_unlocked (err);
return _input->n_ports ();
}
-/** Configure the input/output configuration of each processor in the processors list.
- * Return 0 on success, otherwise configuration is impossible.
- */
-int
-Route::configure_processors_unlocked (ProcessorStreams* err)
+list<pair<ChanCount, ChanCount> >
+Route::try_configure_processors (ChanCount in, ProcessorStreams* err)
{
- if (_in_configure_processors) {
- return 0;
- }
+ Glib::RWLock::ReaderLock lm (_processor_lock);
- _in_configure_processors = true;
+ return try_configure_processors_unlocked (in, err);
+}
+list<pair<ChanCount, ChanCount> >
+Route::try_configure_processors_unlocked (ChanCount in, ProcessorStreams* err)
+{
// Check each processor in order to see if we can configure as requested
- ChanCount in = input_streams ();
ChanCount out;
- list< pair<ChanCount,ChanCount> > configuration;
+ list<pair<ChanCount, ChanCount> > configuration;
uint32_t index = 0;
DEBUG_TRACE (DEBUG::Processors, string_compose ("%1: configure processors\n", _name));
DEBUG_TRACE (DEBUG::Processors, "{\n");
- for (list<boost::shared_ptr<Processor> >::const_iterator p = _processors.begin(); p != _processors.end(); ++p) {
- DEBUG_TRACE (DEBUG::Processors, string_compose ("\t%1 ID = %2\n", (*p)->name(), (*p)->id()));
- }
- DEBUG_TRACE (DEBUG::Processors, "}\n");
for (ProcessorList::iterator p = _processors.begin(); p != _processors.end(); ++p, ++index) {
+ if (boost::dynamic_pointer_cast<UnknownProcessor> (*p)) {
+ DEBUG_TRACE (DEBUG::Processors, "--- CONFIGURE ABORTED due to unknown processor.\n");
+ break;
+ }
+
if ((*p)->can_support_io_configuration(in, out)) {
- DEBUG_TRACE (DEBUG::Processors, string_compose ("\t%1 in = %2 out = %3\n",(*p)->name(), in, out));
+ DEBUG_TRACE (DEBUG::Processors, string_compose ("\t%1 ID=%2 in=%3 out=%4\n",(*p)->name(), (*p)->id(), in, out));
configuration.push_back(make_pair(in, out));
in = out;
} else {
err->index = index;
err->count = in;
}
- _in_configure_processors = false;
- return -1;
+ DEBUG_TRACE (DEBUG::Processors, "---- CONFIGURATION FAILED.\n");
+ DEBUG_TRACE (DEBUG::Processors, string_compose ("---- %1 cannot support in=%2 out=%3\n", (*p)->name(), in, out));
+ DEBUG_TRACE (DEBUG::Processors, "}\n");
+ return list<pair<ChanCount, ChanCount> > ();
}
}
- // We can, so configure everything
+ DEBUG_TRACE (DEBUG::Processors, "}\n");
+
+ return configuration;
+}
+
+/** Set the input/output configuration of each processor in the processors list.
+ * Caller must hold process lock.
+ * Return 0 on success, otherwise configuration is impossible.
+ */
+int
+Route::configure_processors_unlocked (ProcessorStreams* err)
+{
+ assert (!AudioEngine::instance()->process_lock().trylock());
+
+ if (_in_configure_processors) {
+ return 0;
+ }
+
+ _in_configure_processors = true;
+
+ list<pair<ChanCount, ChanCount> > configuration = try_configure_processors_unlocked (input_streams (), err);
+
+ if (configuration.empty ()) {
+ _in_configure_processors = false;
+ return -1;
+ }
+
+ ChanCount out;
+
list< pair<ChanCount,ChanCount> >::iterator c = configuration.begin();
for (ProcessorList::iterator p = _processors.begin(); p != _processors.end(); ++p, ++c) {
+
+ if (boost::dynamic_pointer_cast<UnknownProcessor> (*p)) {
+ break;
+ }
+
(*p)->configure_io(c->first, c->second);
processor_max_streams = ChanCount::max(processor_max_streams, c->first);
processor_max_streams = ChanCount::max(processor_max_streams, c->second);
/* make sure we have sufficient scratch buffers to cope with the new processor
configuration */
- {
- Glib::Mutex::Lock em (_session.engine().process_lock ());
- _session.ensure_buffers (n_process_buffers ());
- }
+ _session.ensure_buffers (n_process_buffers ());
DEBUG_TRACE (DEBUG::Processors, string_compose ("%1: configuration complete\n", _name));
_processors.insert (oiter, as_it_will_be.begin(), as_it_will_be.end());
- if (configure_processors_unlocked (err)) {
- _processors = as_it_was_before;
- processor_max_streams = old_pms;
- return -1;
+ {
+ Glib::Mutex::Lock lm (AudioEngine::instance()->process_lock ());
+
+ if (configure_processors_unlocked (err)) {
+ _processors = as_it_was_before;
+ processor_max_streams = old_pms;
+ return -1;
+ }
}
}
node->add_child_nocopy (_input->state (full_state));
node->add_child_nocopy (_output->state (full_state));
node->add_child_nocopy (_solo_control->get_state ());
+ node->add_child_nocopy (_mute_control->get_state ());
node->add_child_nocopy (_mute_master->get_state ());
XMLNode* remote_control_node = new XMLNode (X_("RemoteControl"));
_extra_xml = new XMLNode (*child);
- } else if (child->name() == X_("Controllable") && (prop = child->property("name")) != 0) {
-
+ } else if (child->name() == Controllable::xml_node_name && (prop = child->property("name")) != 0) {
if (prop->value() == "solo") {
_solo_control->set_state (*child, version);
- _session.add_controllable (_solo_control);
}
} else if (child->name() == X_("RemoteControl")) {
_extra_xml = new XMLNode (*child);
- } else if (child->name() == X_("Controllable") && (prop = child->property("name")) != 0) {
-
- if (prop->value() == "solo") {
+ } else if (child->name() == Controllable::xml_node_name && (prop = child->property("name")) != 0) {
+ if (prop->value() == X_("solo")) {
_solo_control->set_state (*child, version);
- _session.add_controllable (_solo_control);
- }
-
+ } else if (prop->value() == X_("mute")) {
+ _mute_control->set_state (*child, version);
+ }
+
} else if (child->name() == X_("RemoteControl")) {
if ((prop = child->property (X_("id"))) != 0) {
int32_t x;
continue;
}
- if (processor->set_state (**niter, Stateful::current_state_version) == 0) {
- new_order.push_back (processor);
- must_configure = true;
+ if (processor->set_state (**niter, Stateful::current_state_version) != 0) {
+ /* This processor could not be configured. Turn it into a UnknownProcessor */
+ processor.reset (new UnknownProcessor (_session, **niter));
}
+
+ new_order.push_back (processor);
+ must_configure = true;
}
}
}
Glib::RWLock::WriterLock lm (_processor_lock);
_processors = new_order;
if (must_configure) {
+ Glib::Mutex::Lock lm (AudioEngine::instance()->process_lock ());
configure_processors_unlocked (0);
}
}
}
void
-Route::silence (nframes_t nframes)
+Route::silence (framecnt_t nframes)
{
Glib::RWLock::ReaderLock lm (_processor_lock, Glib::TRY_LOCK);
if (!lm.locked()) {
}
void
-Route::silence_unlocked (nframes_t nframes)
+Route::silence_unlocked (framecnt_t nframes)
{
/* Must be called with the processor lock held */
return false;
}
+/** Called from the (non-realtime) butler thread when the transport is stopped */
void
-Route::handle_transport_stopped (bool /*abort_ignored*/, bool did_locate, bool can_flush_processors)
+Route::nonrealtime_handle_transport_stopped (bool /*abort_ignored*/, bool did_locate, bool can_flush_processors)
{
framepos_t now = _session.transport_frame();
_roll_delay = _initial_delay;
}
+/** Called with the process lock held if change contains ConfigurationChanged */
void
Route::input_change_handler (IOChange change, void * /*src*/)
{
}
}
+/** Called with the process lock held if change contains ConfigurationChanged */
void
Route::output_change_handler (IOChange change, void * /*src*/)
{
}
int
-Route::no_roll (nframes_t nframes, framepos_t start_frame, framepos_t end_frame,
+Route::no_roll (pframes_t nframes, framepos_t start_frame, framepos_t end_frame,
bool session_state_changing, bool /*can_record*/, bool /*rec_monitors_input*/)
{
Glib::RWLock::ReaderLock lm (_processor_lock, Glib::TRY_LOCK);
return 0;
}
-nframes_t
-Route::check_initial_delay (nframes_t nframes, nframes_t& transport_frame)
+framecnt_t
+Route::check_initial_delay (framecnt_t nframes, framecnt_t& transport_frame)
{
if (_roll_delay > nframes) {
}
int
-Route::roll (nframes_t nframes, framepos_t start_frame, framepos_t end_frame, int declick,
+Route::roll (pframes_t nframes, framepos_t start_frame, framepos_t end_frame, int declick,
bool /*can_record*/, bool /*rec_monitors_input*/, bool& /* need_butler */)
{
Glib::RWLock::ReaderLock lm (_processor_lock, Glib::TRY_LOCK);
return 0;
}
- nframes_t unused = 0;
+ framecnt_t unused = 0;
if ((nframes = check_initial_delay (nframes, unused)) == 0) {
return 0;
}
int
-Route::silent_roll (nframes_t nframes, framepos_t /*start_frame*/, framepos_t /*end_frame*/,
+Route::silent_roll (pframes_t nframes, framepos_t /*start_frame*/, framepos_t /*end_frame*/,
bool /*can_record*/, bool /*rec_monitors_input*/, bool& /* need_butler */)
{
silence (nframes);
_processors.insert (loc, _monitor_send);
- if (configure_processors_unlocked (0)) {
- _processors = as_it_was;
- configure_processors_unlocked (0); // it worked before we tried to add it ...
- return;
+ {
+ Glib::Mutex::Lock lm (AudioEngine::instance()->process_lock ());
+
+ if (configure_processors_unlocked (0)) {
+ _processors = as_it_was;
+ configure_processors_unlocked (0); // it worked before we tried to add it ...
+ return;
+ }
}
}
_session.set_dirty ();
}
-nframes_t
+framecnt_t
Route::update_total_latency ()
{
- nframes_t old = _output->effective_latency();
- nframes_t own_latency = _output->user_latency();
+ framecnt_t old = _output->effective_latency();
+ framecnt_t own_latency = _output->user_latency();
for (ProcessorList::iterator i = _processors.begin(); i != _processors.end(); ++i) {
if ((*i)->active ()) {
}
void
-Route::set_user_latency (nframes_t nframes)
+Route::set_user_latency (framecnt_t nframes)
{
_output->set_user_latency (nframes);
_session.update_latency_compensation (false, false);
}
void
-Route::set_latency_delay (nframes_t longest_session_latency)
+Route::set_latency_delay (framecnt_t longest_session_latency)
{
- nframes_t old = _initial_delay;
+ framecnt_t old = _initial_delay;
if (_output->effective_latency() < longest_session_latency) {
_initial_delay = longest_session_latency - _output->effective_latency();
}
void
-Route::automation_snapshot (nframes_t now, bool force)
+Route::automation_snapshot (framepos_t now, bool force)
{
panner()->automation_snapshot (now, force);
}
void
-Route::set_block_size (nframes_t nframes)
+Route::set_block_size (pframes_t nframes)
{
for (ProcessorList::iterator i = _processors.begin(); i != _processors.end(); ++i) {
(*i)->set_block_size (nframes);
*/
void
-Route::shift (framepos_t /*pos*/, framecnt_t /*frames*/)
+Route::shift (framepos_t pos, framecnt_t frames)
{
-#ifdef THIS_NEEDS_FIXING_FOR_V3
-
/* gain automation */
- XMLNode &before = _gain_control->get_state ();
- _gain_control->shift (pos, frames);
- XMLNode &after = _gain_control->get_state ();
- _session.add_command (new MementoCommand<AutomationList> (_gain_automation_curve, &before, &after));
+ {
+ boost::shared_ptr<AutomationControl> gc = _amp->gain_control();
+
+ XMLNode &before = gc->alist()->get_state ();
+ gc->alist()->shift (pos, frames);
+ XMLNode &after = gc->alist()->get_state ();
+ _session.add_command (new MementoCommand<AutomationList> (*gc->alist().get(), &before, &after));
+ }
/* pan automation */
- for (std::vector<StreamPanner*>::iterator i = _panner->begin (); i != _panner->end (); ++i) {
- Curve & c = (*i)->automation ();
- XMLNode &before = c.get_state ();
- c.shift (pos, frames);
- XMLNode &after = c.get_state ();
- _session.add_command (new MementoCommand<AutomationList> (c, &before, &after));
- }
+ {
+ boost::shared_ptr<AutomationControl> pc;
+ uint32_t npans = _main_outs->panner()->npanners();
+
+ for (uint32_t p = 0; p < npans; ++p) {
+ pc = _main_outs->panner()->pan_control (0, p);
+ if (pc) {
+ boost::shared_ptr<AutomationList> al = pc->alist();
+ XMLNode& before = al->get_state ();
+ al->shift (pos, frames);
+ XMLNode& after = al->get_state ();
+ _session.add_command (new MementoCommand<AutomationList> (*al.get(), &before, &after));
+ }
+ }
+ }
/* redirect automation */
{
- 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 ();
- al.shift (pos, frames);
- XMLNode &after = al.get_state ();
- _session.add_command (new MementoCommand<AutomationList> (al, &before, &after));
- }
- }
+ Glib::RWLock::ReaderLock lm (_processor_lock);
+ for (ProcessorList::iterator i = _processors.begin (); i != _processors.end (); ++i) {
+
+ set<Evoral::Parameter> parameters = (*i)->what_can_be_automated();
+
+ for (set<Evoral::Parameter>::const_iterator p = parameters.begin (); p != parameters.end (); ++p) {
+ boost::shared_ptr<AutomationControl> ac = (*i)->automation_control (*p);
+ if (ac) {
+ boost::shared_ptr<AutomationList> al = ac->alist();
+ XMLNode &before = al->get_state ();
+ al->shift (pos, frames);
+ XMLNode &after = al->get_state ();
+ _session.add_command (new MementoCommand<AutomationList> (*al.get(), &before, &after));
+ }
+ }
+ }
}
-#endif
-
}
}
}
+/** Called when there is a proposed change to the input port count */
+bool
+Route::input_port_count_changing (ChanCount to)
+{
+ list<pair<ChanCount, ChanCount> > c = try_configure_processors (to, 0);
+ if (c.empty()) {
+ /* The processors cannot be configured with the new input arrangement, so
+ block the change.
+ */
+ return true;
+ }
+
+ /* The change is ok */
+ return false;
+}
+
+list<string>
+Route::unknown_processors () const
+{
+ list<string> p;
+
+ Glib::RWLock::ReaderLock lm (_processor_lock);
+ for (ProcessorList::const_iterator i = _processors.begin(); i != _processors.end(); ++i) {
+ if (boost::dynamic_pointer_cast<UnknownProcessor const> (*i)) {
+ p.push_back ((*i)->name ());
+ }
+ }
+
+ return p;
+}