#include "pbd/convert.h"
#include "pbd/boost_debug.h"
-#include "evoral/Curve.hpp"
-
#include "ardour/amp.h"
-#include "ardour/audio_port.h"
+#include "ardour/audio_buffer.h"
#include "ardour/audioengine.h"
#include "ardour/buffer.h"
#include "ardour/buffer_set.h"
-#include "ardour/configuration.h"
-#include "ardour/cycle_timer.h"
+#include "ardour/capturing_processor.h"
#include "ardour/debug.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/internal_send.h"
#include "ardour/meter.h"
-#include "ardour/mix.h"
#include "ardour/monitor_processor.h"
#include "ardour/pannable.h"
-#include "ardour/panner.h"
#include "ardour/panner_shell.h"
#include "ardour/plugin_insert.h"
#include "ardour/port.h"
#include "ardour/port_insert.h"
#include "ardour/processor.h"
-#include "ardour/profile.h"
#include "ardour/route.h"
#include "ardour/route_group.h"
#include "ardour/send.h"
#include "ardour/session.h"
-#include "ardour/timestamps.h"
-#include "ardour/utils.h"
#include "ardour/unknown_processor.h"
-#include "ardour/capturing_processor.h"
+#include "ardour/utils.h"
#include "i18n.h"
_input->changed.connect_same_thread (*this, boost::bind (&Route::input_change_handler, this, _1, _2));
_input->PortCountChanging.connect_same_thread (*this, boost::bind (&Route::input_port_count_changing, this, _1));
+ _output->changed.connect_same_thread (*this, boost::bind (&Route::output_change_handler, this, _1, _2));
+
/* add amp processor */
_amp.reset (new Amp (_session));
for (ProcessorList::iterator i = _processors.begin(); i != _processors.end(); ++i) {
- if (boost::dynamic_pointer_cast<UnknownProcessor> (*i)) {
- break;
- }
-
- if (boost::dynamic_pointer_cast<PeakMeter> (*i) && meter_already_run) {
+ if (meter_already_run && boost::dynamic_pointer_cast<PeakMeter> (*i)) {
/* don't ::run() the meter, otherwise it will have its previous peak corrupted */
continue;
}
-
+
#ifndef NDEBUG
/* if it has any inputs, make sure they match */
- if ((*i)->input_streams() != ChanCount::ZERO) {
+ if (boost::dynamic_pointer_cast<UnknownProcessor> (*i) == 0 && (*i)->input_streams() != ChanCount::ZERO) {
if (bufs.count() != (*i)->input_streams()) {
- cerr << _name << " bufs = " << bufs.count()
- << " input for " << (*i)->name() << " = " << (*i)->input_streams()
- << endl;
- abort ();
+ DEBUG_TRACE (
+ DEBUG::Processors, string_compose (
+ "%1 bufs = %2 input for %3 = %4\n",
+ _name, bufs.count(), (*i)->name(), (*i)->input_streams()
+ )
+ );
+ continue;
}
}
#endif
}
#endif
-int
-Route::add_processor (boost::shared_ptr<Processor> processor, Placement placement, ProcessorStreams* err, bool activation_allowed)
+/** Supposing that we want to insert a Processor at a given Placement, return
+ * the processor to add the new one before (or 0 to add at the end).
+ */
+boost::shared_ptr<Processor>
+Route::before_processor_for_placement (Placement p)
{
- ProcessorList::iterator loc;
-
- /* XXX this is not thread safe - we don't hold the lock across determining the iter
- to add before and actually doing the insertion. dammit.
- */
+ Glib::RWLock::ReaderLock lm (_processor_lock);
- if (placement == PreFader) {
+ ProcessorList::iterator loc;
+
+ if (p == PreFader) {
/* generic pre-fader: insert immediately before the amp */
loc = find (_processors.begin(), _processors.end(), _amp);
} else {
loc = find (_processors.begin(), _processors.end(), _main_outs);
}
- return add_processor (processor, loc, err, activation_allowed);
+ return loc != _processors.end() ? *loc : boost::shared_ptr<Processor> ();
}
-
-/** Add a processor to a route such that it ends up with a given index into the visible processors.
- * @param index Index to add the processor at, or -1 to add at the end of the list.
+/** Supposing that we want to insert a Processor at a given index, return
+ * the processor to add the new one before (or 0 to add at the end).
*/
-
-int
-Route::add_processor_by_index (boost::shared_ptr<Processor> processor, int index, ProcessorStreams* err, bool activation_allowed)
+boost::shared_ptr<Processor>
+Route::before_processor_for_index (int index)
{
- /* XXX this is not thread safe - we don't hold the lock across determining the iter
- to add before and actually doing the insertion. dammit.
- */
-
if (index == -1) {
- return add_processor (processor, _processors.end(), err, activation_allowed);
+ return boost::shared_ptr<Processor> ();
}
+
+ Glib::RWLock::ReaderLock lm (_processor_lock);
ProcessorList::iterator i = _processors.begin ();
int j = 0;
++i;
}
-
- return add_processor (processor, i, err, activation_allowed);
+
+ return (i != _processors.end() ? *i : boost::shared_ptr<Processor> ());
+}
+
+/** Add a processor either pre- or post-fader
+ * @return 0 on success, non-0 on failure.
+ */
+int
+Route::add_processor (boost::shared_ptr<Processor> processor, Placement placement, ProcessorStreams* err, bool activation_allowed)
+{
+ return add_processor (processor, before_processor_for_placement (placement), err, activation_allowed);
+}
+
+
+/** Add a processor to a route such that it ends up with a given index into the visible processors.
+ * @param index Index to add the processor at, or -1 to add at the end of the list.
+ * @return 0 on success, non-0 on failure.
+ */
+int
+Route::add_processor_by_index (boost::shared_ptr<Processor> processor, int index, ProcessorStreams* err, bool activation_allowed)
+{
+ return add_processor (processor, before_processor_for_index (index), err, activation_allowed);
}
/** Add a processor to the route.
- * @param iter an iterator in _processors; the new processor will be inserted immediately before this location.
+ * @param before An existing processor in the list, or 0; the new processor will be inserted immediately before it (or at the end).
+ * @return 0 on success, non-0 on failure.
*/
int
-Route::add_processor (boost::shared_ptr<Processor> processor, ProcessorList::iterator iter, ProcessorStreams* err, bool activation_allowed)
+Route::add_processor (boost::shared_ptr<Processor> processor, boost::shared_ptr<Processor> before, ProcessorStreams* err, bool activation_allowed)
{
assert (processor != _meter);
assert (processor != _main_outs);
boost::shared_ptr<PluginInsert> pi;
boost::shared_ptr<PortInsert> porti;
- ProcessorList::iterator loc = find(_processors.begin(), _processors.end(), processor);
-
if (processor == _amp) {
- // Ensure only one amp is in the list at any time
- if (loc != _processors.end()) {
- if (iter == loc) { // Already in place, do nothing
+ /* Ensure that only one amp is in the list at any time */
+ ProcessorList::iterator check = find (_processors.begin(), _processors.end(), processor);
+ if (check != _processors.end()) {
+ if (before == _amp) {
+ /* Already in position; all is well */
return 0;
- } else { // New position given, relocate
- _processors.erase (loc);
+ } else {
+ _processors.erase (check);
}
}
+ }
- } else {
- if (loc != _processors.end()) {
- cerr << "ERROR: Processor added to route twice!" << endl;
+ assert (find (_processors.begin(), _processors.end(), processor) == _processors.end ());
+
+ ProcessorList::iterator loc;
+ if (before) {
+ /* inserting before a processor; find it */
+ loc = find (_processors.begin(), _processors.end(), before);
+ if (loc == _processors.end ()) {
+ /* Not found */
return 1;
}
-
- loc = iter;
+ } else {
+ /* inserting at end */
+ loc = _processors.end ();
}
_processors.insert (loc, processor);
}
for (i = _processors.begin(); i != _processors.end(); ++i) {
+ if (!full_state) {
+ /* template save: do not include internal sends functioning as
+ aux sends because the chance of the target ID
+ in the session where this template is used
+ is not very likely.
+
+ similarly, do not save listen sends which connect to
+ the monitor section, because these will always be
+ added if necessary.
+ */
+ boost::shared_ptr<InternalSend> is;
+
+ if ((is = boost::dynamic_pointer_cast<InternalSend> (*i)) != 0) {
+ if (is->role() == Delivery::Aux || is->role() == Delivery::Listen) {
+ continue;
+ }
+ }
+ }
node->add_child_nocopy((*i)->state (full_state));
}
void
Route::remove_send_from_internal_return (InternalSend* send)
{
- Glib::Mutex::Lock lm (AudioEngine::instance()->process_lock ());
Glib::RWLock::ReaderLock rm (_processor_lock);
for (ProcessorList::const_iterator x = _processors.begin(); x != _processors.end(); ++x) {
/** Add an aux send to a route.
* @param route route to send to.
- * @param placement placement for the send.
+ * @param before Processor to insert before, or 0 to insert at the end.
*/
int
-Route::add_aux_send (boost::shared_ptr<Route> route, Placement placement)
+Route::add_aux_send (boost::shared_ptr<Route> route, boost::shared_ptr<Processor> before)
{
assert (route != _session.monitor_out ());
listener.reset (new InternalSend (_session, _pannable, _mute_master, route, Delivery::Aux));
}
- add_processor (listener, placement);
+ add_processor (listener, before);
} catch (failed_constructor& err) {
return -1;
_roll_delay = _initial_delay;
}
-/** Called with the process lock held if change contains ConfigurationChanged */
void
Route::input_change_handler (IOChange change, void * /*src*/)
{
+ bool need_to_queue_solo_change = true;
+
if ((change.type & IOChange::ConfigurationChanged)) {
+ /* This is called with the process lock held if change
+ contains ConfigurationChanged
+ */
+ need_to_queue_solo_change = false;
configure_processors (0);
_phase_invert.resize (_input->n_ports().n_audio ());
io_changed (); /* EMIT SIGNAL */
}
+
+ if (!_input->connected() && _soloed_by_others_upstream) {
+ if (need_to_queue_solo_change) {
+ _session.cancel_solo_after_disconnect (shared_from_this(), true);
+ } else {
+ cancel_solo_after_disconnect (true);
+ }
+ }
+}
+
+void
+Route::output_change_handler (IOChange change, void * /*src*/)
+{
+ bool need_to_queue_solo_change = true;
+
+ if ((change.type & IOChange::ConfigurationChanged)) {
+ /* This is called with the process lock held if change
+ contains ConfigurationChanged
+ */
+ need_to_queue_solo_change = false;
+ }
+
+ if (!_output->connected() && _soloed_by_others_downstream) {
+ if (need_to_queue_solo_change) {
+ _session.cancel_solo_after_disconnect (shared_from_this(), false);
+ } else {
+ cancel_solo_after_disconnect (false);
+ }
+ }
+}
+
+void
+Route::cancel_solo_after_disconnect (bool upstream)
+{
+ if (upstream) {
+ _soloed_by_others_upstream = 0;
+ } else {
+ _soloed_by_others_downstream = 0;
+ }
+ set_mute_master_solo ();
+ solo_changed (false, this);
}
uint32_t
return 0;
}
- framecnt_t unused = 0;
+ framepos_t unused = 0;
if ((nframes = check_initial_delay (nframes, unused)) == 0) {
return 0;
return 0;
}
-bool
-Route::has_external_redirects () const
-{
- // 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;
- }
- }
- }
- }
-
- return false;
-}
-
void
Route::flush_processors ()
{
{
return MeteringRoute;
}
+
+bool
+Route::has_external_redirects () const
+{
+ for (ProcessorList::const_iterator i = _processors.begin(); i != _processors.end(); ++i) {
+
+ /* ignore inactive processors and obviously ignore the main
+ * outs since everything has them and we don't care.
+ */
+
+ if ((*i)->active() && (*i) != _main_outs && (*i)->does_routing()) {
+ return true;;
+ }
+ }
+
+ return false;
+}
+
+boost::shared_ptr<Processor>
+Route::the_instrument () const
+{
+ Glib::RWLock::WriterLock lm (_processor_lock);
+ for (ProcessorList::const_iterator i = _processors.begin(); i != _processors.end(); ++i) {
+ if ((*i)->input_streams().n_midi() > 0 &&
+ (*i)->output_streams().n_audio() > 0) {
+ return (*i);
+ }
+ }
+ return boost::shared_ptr<Processor>();
+}