#include "ardour/internal_return.h"
#include "ardour/internal_send.h"
#include "ardour/meter.h"
+#include "ardour/delayline.h"
#include "ardour/midi_buffer.h"
#include "ardour/midi_port.h"
#include "ardour/monitor_processor.h"
, GraphNode (sess._process_graph)
, _active (true)
, _signal_latency (0)
+ , _signal_latency_at_amp_position (0)
, _initial_delay (0)
, _roll_delay (0)
, _flags (flg)
, _order_key (0)
, _has_order_key (false)
, _remote_control_id (0)
+ , _track_number (0)
, _in_configure_processors (false)
, _initial_io_setup (false)
, _custom_meter_position_noted (false)
_output->changed.connect_same_thread (*this, boost::bind (&Route::output_change_handler, this, _1, _2));
_output->PortCountChanging.connect_same_thread (*this, boost::bind (&Route::output_port_count_changing, this, _1));
+#if 0 // not used - just yet
+ if (!is_master() && !is_monitor() && !is_auditioner()) {
+ _delayline.reset (new DelayLine (_session, _name));
+ add_processor (_delayline, PreFader);
+ }
+#endif
+
/* add amp processor */
_amp.reset (new Amp (_session));
framepos_t start_frame, framepos_t end_frame, pframes_t nframes,
int declick, bool gain_automation_ok)
{
+ /* Caller must hold process lock */
+ assert (!AudioEngine::instance()->process_lock().trylock());
+
Glib::Threads::RWLock::ReaderLock lm (_processor_lock, Glib::Threads::TRY_LOCK);
- assert(lm.locked());
+ if (!lm.locked()) {
+ bufs.silence (nframes, 0);
+ return;
+ }
/* figure out if we're going to use gain automation */
if (gain_automation_ok) {
_amp->set_gain_automation_buffer (_session.gain_automation_buffer ());
- _amp->setup_gain_automation (start_frame, end_frame, nframes);
+ _amp->setup_gain_automation (
+ start_frame + _signal_latency_at_amp_position,
+ end_frame + _signal_latency_at_amp_position,
+ nframes);
} else {
_amp->apply_gain_automation (false);
}
on a transition between monitoring states we get a de-clicking gain
change in the _main_outs delivery.
*/
+ bool silence = monitoring_state () == MonitoringSilence;
- _main_outs->no_outs_cuz_we_no_monitor (monitoring_state () == MonitoringSilence);
+ //but we override this in the case where we have an internal generator
+ if ( _have_internal_generator )
+ silence = false;
+
+ _main_outs->no_outs_cuz_we_no_monitor (silence);
/* -------------------------------------------------------------------------------------------
GLOBAL DECLICK (for transport changes etc.)
/* set this to be true if the meter will already have been ::run() earlier */
bool const meter_already_run = metering_state() == MeteringInput;
+ framecnt_t latency = 0;
+
for (ProcessorList::iterator i = _processors.begin(); i != _processors.end(); ++i) {
if (meter_already_run && boost::dynamic_pointer_cast<PeakMeter> (*i)) {
do we catch route != active somewhere higher?
*/
- (*i)->run (bufs, start_frame, end_frame, nframes, *i != _processors.back());
+ if (boost::dynamic_pointer_cast<Send>(*i) != 0) {
+ boost::dynamic_pointer_cast<Send>(*i)->set_delay_in(_signal_latency - latency);
+ }
+
+ (*i)->run (bufs, start_frame - latency, end_frame - latency, nframes, *i != _processors.back());
bufs.set_count ((*i)->output_streams());
+
+ if ((*i)->active ()) {
+ latency += (*i)->signal_latency ();
+ }
+ }
+}
+
+void
+Route::bounce_process (BufferSet& buffers, framepos_t start, framecnt_t nframes,
+ boost::shared_ptr<Processor> endpoint,
+ bool include_endpoint, bool for_export, bool for_freeze)
+{
+ /* If no processing is required, there's no need to go any further. */
+ if (!endpoint && !include_endpoint) {
+ return;
+ }
+
+ framecnt_t latency = bounce_get_latency(_amp, false, for_export, for_freeze);
+ _amp->set_gain_automation_buffer (_session.gain_automation_buffer ());
+ _amp->setup_gain_automation (start - latency, start - latency + nframes, nframes);
+
+ latency = 0;
+ for (ProcessorList::iterator i = _processors.begin(); i != _processors.end(); ++i) {
+
+ if (!include_endpoint && (*i) == endpoint) {
+ break;
+ }
+
+ /* if we're not exporting, stop processing if we come across a routing processor. */
+ if (!for_export && boost::dynamic_pointer_cast<PortInsert>(*i)) {
+ break;
+ }
+ if (!for_export && for_freeze && (*i)->does_routing() && (*i)->active()) {
+ break;
+ }
+
+ /* don't run any processors that does routing.
+ * oh, and don't bother with the peak meter either.
+ */
+ if (!(*i)->does_routing() && !boost::dynamic_pointer_cast<PeakMeter>(*i)) {
+ (*i)->run (buffers, start - latency, start - latency + nframes, nframes, true);
+ buffers.set_count ((*i)->output_streams());
+ latency += (*i)->signal_latency ();
+ }
+
+ if ((*i) == endpoint) {
+ break;
+ }
+ }
+}
+
+framecnt_t
+Route::bounce_get_latency (boost::shared_ptr<Processor> endpoint,
+ bool include_endpoint, bool for_export, bool for_freeze) const
+{
+ framecnt_t latency = 0;
+ if (!endpoint && !include_endpoint) {
+ return latency;
+ }
+
+ for (ProcessorList::const_iterator i = _processors.begin(); i != _processors.end(); ++i) {
+ if (!include_endpoint && (*i) == endpoint) {
+ break;
+ }
+ if (!for_export && boost::dynamic_pointer_cast<PortInsert>(*i)) {
+ break;
+ }
+ if (!for_export && for_freeze && (*i)->does_routing() && (*i)->active()) {
+ break;
+ }
+ if (!(*i)->does_routing() && !boost::dynamic_pointer_cast<PeakMeter>(*i)) {
+ latency += (*i)->signal_latency ();
+ }
+ if ((*i) == endpoint) {
+ break;
+ }
}
+ return latency;
+}
+
+ChanCount
+Route::bounce_get_output_streams (ChanCount &cc, boost::shared_ptr<Processor> endpoint,
+ bool include_endpoint, bool for_export, bool for_freeze) const
+{
+ if (!endpoint && !include_endpoint) {
+ return cc;
+ }
+
+ for (ProcessorList::const_iterator i = _processors.begin(); i != _processors.end(); ++i) {
+ if (!include_endpoint && (*i) == endpoint) {
+ break;
+ }
+ if (!for_export && boost::dynamic_pointer_cast<PortInsert>(*i)) {
+ break;
+ }
+ if (!for_export && for_freeze && (*i)->does_routing() && (*i)->active()) {
+ break;
+ }
+ if (!(*i)->does_routing() && !boost::dynamic_pointer_cast<PeakMeter>(*i)) {
+ cc = (*i)->output_streams();
+ }
+ if ((*i) == endpoint) {
+ break;
+ }
+ }
+ return cc;
}
ChanCount
{
assert (is_monitor());
BufferSet& bufs (_session.get_route_buffers (n_process_buffers()));
+ fill_buffers_with_input (bufs, _input, nframes);
passthru (bufs, start_frame, end_frame, nframes, declick);
}
} else if (node.name() == "Send") {
- processor.reset (new Send (_session, _pannable, _mute_master));
+ boost::shared_ptr<Pannable> sendpan (new Pannable (_session));
+ processor.reset (new Send (_session, sendpan, _mute_master));
} else {
seen_amp = true;
}
- if ((*i) == _amp || (*i) == _meter || (*i) == _main_outs) {
+ if ((*i) == _amp || (*i) == _meter || (*i) == _main_outs || (*i) == _delayline) {
/* you can't remove these */
}
int
-Route::remove_processor (boost::shared_ptr<Processor> processor, ProcessorStreams* err, bool)
+Route::remove_processor (boost::shared_ptr<Processor> processor, ProcessorStreams* err, bool need_process_lock)
{
// TODO once the export point can be configured properly, do something smarter here
if (processor == _capturing_processor) {
+ Glib::Threads::Mutex::Lock lx (AudioEngine::instance()->process_lock (), Glib::Threads::NOT_LOCK);
+ if (need_process_lock) {
+ lx.acquire();
+ }
+
_capturing_processor.reset();
+
+ if (need_process_lock) {
+ lx.release();
+ }
}
/* these can never be removed */
- if (processor == _amp || processor == _meter || processor == _main_outs) {
+ if (processor == _amp || processor == _meter || processor == _main_outs || processor == _delayline) {
return 0;
}
processor_max_streams.reset();
{
- Glib::Threads::Mutex::Lock lx (AudioEngine::instance()->process_lock ());
- Glib::Threads::RWLock::WriterLock lm (_processor_lock);
+ Glib::Threads::Mutex::Lock lx (AudioEngine::instance()->process_lock (), Glib::Threads::NOT_LOCK);
+ if (need_process_lock) {
+ lx.acquire();
+ }
+
+ /* Caller must hold process lock */
+ assert (!AudioEngine::instance()->process_lock().trylock());
+
+ Glib::Threads::RWLock::WriterLock lm (_processor_lock); // XXX deadlock after export
+
ProcessorState pstate (this);
ProcessorList::iterator i;
}
}
}
+ if (need_process_lock) {
+ lx.release();
+ }
}
reset_instrument_info ();
/* these can never be removed */
- if (processor == _amp || processor == _meter || processor == _main_outs) {
+ if (processor == _amp || processor == _meter || processor == _main_outs || processor == _delayline) {
++i;
continue;
}
return 0;
}
-void
-Route::set_custom_panner_uri (std::string const panner_uri)
-{
- if (_in_configure_processors) {
- DEBUG_TRACE (DEBUG::Panning, string_compose (_("Route::set_custom_panner_uri '%1' -- called while in_configure_processors\n"), name()));
- return;
- }
-
- if (!_main_outs->panner_shell()->set_user_selected_panner_uri(panner_uri)) {
- DEBUG_TRACE (DEBUG::Panning, string_compose (_("Route::set_custom_panner_uri '%1 '%2' -- no change needed\n"), name(), panner_uri));
- /* no change needed */
- return;
- }
-
- DEBUG_TRACE (DEBUG::Panning, string_compose (_("Route::set_custom_panner_uri '%1 '%2' -- reconfigure I/O\n"), name(), panner_uri));
-
- /* reconfigure I/O -- re-initialize panner modules */
- {
- Glib::Threads::Mutex::Lock lx (AudioEngine::instance()->process_lock ());
- Glib::Threads::RWLock::WriterLock lm (_processor_lock);
-
- for (ProcessorList::iterator p = _processors.begin(); p != _processors.end(); ++p) {
- boost::shared_ptr<Delivery> dl;
- boost::shared_ptr<Panner> panner;
- if ((dl = boost::dynamic_pointer_cast<Delivery> (*p)) == 0) {
- continue;
- }
- if (!dl->panner_shell()) {
- continue;
- }
- if (!(panner = dl->panner_shell()->panner())) {
- continue;
- }
- /* _main_outs has already been set before the loop.
- * Ignore the return status here. It need reconfiguration */
- if (dl->panner_shell() != _main_outs->panner_shell()) {
- if (!dl->panner_shell()->set_user_selected_panner_uri(panner_uri)) {
- continue;
- }
- }
-
- ChanCount in = panner->in();
- ChanCount out = panner->out();
- dl->panner_shell()->configure_io(in, out);
- dl->panner_shell()->pannable()->set_panner(dl->panner_shell()->panner());
- }
- }
-
- processors_changed (RouteProcessorChange ()); /* EMIT SIGNAL */
- _session.set_dirty ();
-}
-
void
Route::reset_instrument_info ()
{
int
Route::configure_processors (ProcessorStreams* err)
{
+#ifndef PLATFORM_WINDOWS
assert (!AudioEngine::instance()->process_lock().trylock());
+#endif
+
if (!_in_configure_processors) {
Glib::Threads::RWLock::WriterLock lm (_processor_lock);
return configure_processors_unlocked (err);
int
Route::configure_processors_unlocked (ProcessorStreams* err)
{
+#ifndef PLATFORM_WINDOWS
assert (!AudioEngine::instance()->process_lock().trylock());
+#endif
if (_in_configure_processors) {
return 0;
node->add_child_nocopy (_mute_control->get_state ());
node->add_child_nocopy (_mute_master->get_state ());
+ if (full_state) {
+ node->add_child_nocopy (Automatable::get_automation_xml_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);
} else if (child->name() == X_("MuteMaster")) {
_mute_master->set_state (*child, version);
+
+ } else if (child->name() == Automatable::xml_node_name) {
+ set_automation_xml_state (*child, Evoral::Parameter(NullAutomation));
}
}
} else if (prop->value() == "meter") {
_meter->set_state (**niter, Stateful::current_state_version);
new_order.push_back (_meter);
+ } else if (prop->value() == "delay") {
+ if (_delayline) {
+ _delayline->set_state (**niter, Stateful::current_state_version);
+ new_order.push_back (_delayline);
+ }
} else if (prop->value() == "main-outs") {
_main_outs->set_state (**niter, Stateful::current_state_version);
} else if (prop->value() == "intreturn") {
if (prop->value() == "intsend") {
- processor.reset (new InternalSend (_session, _pannable, _mute_master, boost::shared_ptr<Route>(), Delivery::Role (0)));
+ processor.reset (new InternalSend (_session, _pannable, _mute_master, boost::dynamic_pointer_cast<ARDOUR::Route>(shared_from_this()), boost::shared_ptr<Route>(), Delivery::Aux, true));
} else if (prop->value() == "ladspa" || prop->value() == "Ladspa" ||
prop->value() == "lv2" ||
} else if (prop->value() == "send") {
- processor.reset (new Send (_session, _pannable, _mute_master));
+ processor.reset (new Send (_session, _pannable, _mute_master, Delivery::Send, true));
} else {
error << string_compose(_("unknown Processor type \"%1\"; ignored"), prop->value()) << endmsg;
/* master never sends to monitor section via the normal mechanism */
assert (!is_master ());
+ assert (!is_monitor ());
/* make sure we have one */
if (!_monitor_send) {
- _monitor_send.reset (new InternalSend (_session, _pannable, _mute_master, _session.monitor_out(), Delivery::Listen));
+ _monitor_send.reset (new InternalSend (_session, _pannable, _mute_master, boost::dynamic_pointer_cast<ARDOUR::Route>(shared_from_this()), _session.monitor_out(), Delivery::Listen));
_monitor_send->set_display_to_user (false);
}
{
Glib::Threads::Mutex::Lock lm (AudioEngine::instance()->process_lock ());
- listener.reset (new InternalSend (_session, _pannable, _mute_master, route, Delivery::Aux));
+ boost::shared_ptr<Pannable> sendpan (new Pannable (_session));
+ listener.reset (new InternalSend (_session, sendpan, _mute_master, boost::dynamic_pointer_cast<ARDOUR::Route>(shared_from_this()), route, Delivery::Aux));
}
add_processor (listener, before);
{
const FedBy& fed_by (other->fed_by());
- for (FedBy::iterator f = fed_by.begin(); f != fed_by.end(); ++f) {
+ for (FedBy::const_iterator f = fed_by.begin(); f != fed_by.end(); ++f) {
boost::shared_ptr<Route> sr = f->r.lock();
if (sr && (sr.get() == this)) {
bool meter_was_visible_to_user = _meter->display_to_user ();
{
+ Glib::Threads::Mutex::Lock lx (AudioEngine::instance()->process_lock ());
Glib::Threads::RWLock::WriterLock lm (_processor_lock);
maybe_note_meter_position ();
boost::shared_ptr<CapturingProcessor>
Route::add_export_point()
{
+ Glib::Threads::RWLock::ReaderLock lm (_processor_lock);
if (!_capturing_processor) {
+ lm.release();
+ Glib::Threads::Mutex::Lock lx (AudioEngine::instance()->process_lock ());
+ Glib::Threads::RWLock::WriterLock lw (_processor_lock);
_capturing_processor.reset (new CapturingProcessor (_session));
_capturing_processor->activate ();
- configure_processors (0);
+ configure_processors_unlocked (0);
}
Route::update_signal_latency ()
{
framecnt_t l = _output->user_latency();
+ framecnt_t lamp = 0;
+ bool before_amp = true;
for (ProcessorList::iterator i = _processors.begin(); i != _processors.end(); ++i) {
if ((*i)->active ()) {
l += (*i)->signal_latency ();
}
+ if ((*i) == _amp) {
+ before_amp = false;
+ }
+ if (before_amp) {
+ lamp = l;
+ }
}
DEBUG_TRACE (DEBUG::Latency, string_compose ("%1: internal signal latency = %2\n", _name, l));
+ _signal_latency_at_amp_position = lamp;
if (_signal_latency != l) {
_signal_latency = l;
signal_latency_changed (); /* EMIT SIGNAL */
}
Route::SoloControllable::SoloControllable (std::string name, boost::shared_ptr<Route> r)
- : AutomationControl (r->session(), Evoral::Parameter (SoloAutomation),
- boost::shared_ptr<AutomationList>(), name)
+ : AutomationControl (r->session(),
+ Evoral::Parameter (SoloAutomation),
+ ParameterDescriptor(Evoral::Parameter (SoloAutomation)),
+ boost::shared_ptr<AutomationList>(), name)
, _route (r)
{
boost::shared_ptr<AutomationList> gl(new AutomationList(Evoral::Parameter(SoloAutomation)));
+ gl->set_interpolation(Evoral::ControlList::Discrete);
set_list (gl);
}
}
Route::MuteControllable::MuteControllable (std::string name, boost::shared_ptr<Route> r)
- : AutomationControl (r->session(), Evoral::Parameter (MuteAutomation),
- boost::shared_ptr<AutomationList>(), name)
+ : AutomationControl (r->session(),
+ Evoral::Parameter (MuteAutomation),
+ ParameterDescriptor (Evoral::Parameter (MuteAutomation)),
+ boost::shared_ptr<AutomationList>(),
+ name)
, _route (r)
{
boost::shared_ptr<AutomationList> gl(new AutomationList(Evoral::Parameter(MuteAutomation)));
+ gl->set_interpolation(Evoral::ControlList::Discrete);
set_list (gl);
}
{
bool bval = ((val >= 0.5f) ? true: false);
- boost::shared_ptr<RouteList> rl (new RouteList);
+ // boost::shared_ptr<RouteList> rl (new RouteList);
boost::shared_ptr<Route> r = _route.lock ();
if (!r) {
return;
}
- rl->push_back (r);
- _session.set_mute (rl, bval);
+ /* I don't know why this apparently "should" be done via the RT event
+ system, but doing so causes a ton of annoying errors... */
+ // rl->push_back (r);
+ // _session.set_mute (rl, bval);
+
+ /* ... but this seems to work. */
+ r->set_mute (bval, this);
+
+ AutomationControl::set_value(bval);
}
double
bool
Route::set_name (const string& str)
{
- bool ret;
- string ioproc_name;
- string name;
+ if (str == name()) {
+ return true;
+ }
- name = Route::ensure_track_or_route_name (str, _session);
+ string name = Route::ensure_track_or_route_name (str, _session);
SessionObject::set_name (name);
- ret = (_input->set_name(name) && _output->set_name(name));
+ bool ret = (_input->set_name(name) && _output->set_name(name));
if (ret) {
/* rename the main outs. Leave other IO processors
void
Route::set_active (bool yn, void* src)
{
+ if (_session.transport_rolling()) {
+ return;
+ }
+
if (_route_group && src != _route_group && _route_group->is_active() && _route_group->is_route_active()) {
_route_group->foreach_route (boost::bind (&Route::set_active, _1, yn, _route_group));
return;
void
Route::meter ()
{
- Glib::Threads::RWLock::ReaderLock rm (_processor_lock, Glib::Threads::TRY_LOCK);
+ Glib::Threads::RWLock::ReaderLock rm (_processor_lock);
assert (_meter);
/* maybe one of our processors does or ... */
- Glib::Threads::RWLock::ReaderLock rm (_processor_lock, Glib::Threads::TRY_LOCK);
+ Glib::Threads::RWLock::ReaderLock rm (_processor_lock);
for (ProcessorList::iterator i = _processors.begin(); i != _processors.end(); ++i) {
if ((c = boost::dynamic_pointer_cast<AutomationControl>((*i)->control (param))) != 0) {
break;
++amp;
}
- assert (amp != _processors.end ());
+ assert (amp != new_processors.end ());
/* and the processor after the amp */
}
}
+#if 0 // not used - just yet
+ if (!is_master() && !is_monitor() && !is_auditioner()) {
+ new_processors.push_front (_delayline);
+ }
+#endif
+
/* MONITOR CONTROL */
if (_monitor_control && is_monitor ()) {
boost::shared_ptr<Processor>
Route::the_instrument () const
{
- Glib::Threads::Mutex::Lock lx (AudioEngine::instance()->process_lock ());
- Glib::Threads::RWLock::WriterLock lm (_processor_lock);
+ Glib::Threads::RWLock::ReaderLock lm (_processor_lock);
return the_instrument_unlocked ();
}
_pannable->transport_located (pos);
}
+ if (_delayline.get()) {
+ _delayline.get()->flush();
+ }
+
{
//Glib::Threads::Mutex::Lock lx (AudioEngine::instance()->process_lock ());
- Glib::Threads::RWLock::WriterLock lm (_processor_lock);
+ Glib::Threads::RWLock::ReaderLock lm (_processor_lock);
for (ProcessorList::iterator i = _processors.begin(); i != _processors.end(); ++i) {
(*i)->transport_located (pos);