/*
- 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 <iostream>
#include <algorithm>
-#include <pbd/xml++.h>
+#include "pbd/xml++.h"
+#include "pbd/boost_debug.h"
+
+#include "ardour/amp.h"
+#include "ardour/send.h"
+#include "ardour/session.h"
+#include "ardour/buffer_set.h"
+#include "ardour/meter.h"
+#include "ardour/io.h"
+#include "ardour/panner_shell.h"
-#include <ardour/send.h>
-#include <ardour/session.h>
-#include <ardour/port.h>
-#include <ardour/audio_port.h>
-#include <ardour/buffer_set.h>
-#include <ardour/meter.h>
#include "i18n.h"
+namespace ARDOUR {
+class AutomationControl;
+class MuteMaster;
+class Pannable;
+}
+
using namespace ARDOUR;
using namespace PBD;
+using namespace std;
-Send::Send (Session& s, Placement p)
- : IOProcessor (s, string_compose (_("send %1"), (bitslot = s.next_send_id()) + 1), p)
+string
+Send::name_and_id_new_send (Session& s, Role r, uint32_t& bitslot, bool ignore_bitslot)
{
- _metering = false;
- ProcessorCreated (this); /* EMIT SIGNAL */
+ if (ignore_bitslot) {
+ /* this happens during initial construction of sends from XML,
+ before they get ::set_state() called. lets not worry about
+ it.
+ */
+ bitslot = 0;
+ return string ();
+ }
+
+ switch (r) {
+ case Delivery::Aux:
+ return string_compose (_("aux %1"), (bitslot = s.next_aux_send_id ()) + 1);
+ case Delivery::Listen:
+ return _("listen"); // no ports, no need for numbering
+ case Delivery::Send:
+ return string_compose (_("send %1"), (bitslot = s.next_send_id ()) + 1);
+ default:
+ fatal << string_compose (_("programming error: send created using role %1"), enum_2_string (r)) << endmsg;
+ /*NOTREACHED*/
+ return string();
+ }
+
}
-Send::Send (Session& s, const XMLNode& node)
- : IOProcessor (s, "send", PreFader)
+Send::Send (Session& s, boost::shared_ptr<Pannable> p, boost::shared_ptr<MuteMaster> mm, Role r, bool ignore_bitslot)
+ : Delivery (s, p, mm, name_and_id_new_send (s, r, _bitslot, ignore_bitslot), r)
+ , _metering (false)
{
- _metering = false;
+ if (_role == Listen) {
+ /* we don't need to do this but it keeps things looking clean
+ in a debugger. _bitslot is not used by listen sends.
+ */
+ _bitslot = 0;
+ }
+
+ //boost_debug_shared_ptr_mark_interesting (this, "send");
- if (set_state (node)) {
- throw failed_constructor();
+ _amp.reset (new Amp (_session));
+ _meter.reset (new PeakMeter (_session, name()));
+
+ add_control (_amp->gain_control ());
+
+ if (panner_shell()) {
+ panner_shell()->Changed.connect_same_thread (*this, boost::bind (&Send::panshell_changed, this));
}
+}
- ProcessorCreated (this); /* EMIT SIGNAL */
+Send::~Send ()
+{
+ _session.unmark_send_id (_bitslot);
}
-Send::Send (const Send& other)
- : IOProcessor (other._session, string_compose (_("send %1"), (bitslot = other._session.next_send_id()) + 1), other.placement())
+void
+Send::activate ()
{
- _metering = false;
- ProcessorCreated (this); /* EMIT SIGNAL */
+ _amp->activate ();
+ _meter->activate ();
+
+ Processor::activate ();
}
-Send::~Send ()
+void
+Send::deactivate ()
{
- GoingAway ();
+ _amp->deactivate ();
+ _meter->deactivate ();
+ _meter->reset ();
+
+ Processor::deactivate ();
+}
+
+void
+Send::run (BufferSet& bufs, framepos_t start_frame, framepos_t end_frame, pframes_t nframes, bool)
+{
+ if (_output->n_ports() == ChanCount::ZERO) {
+ _meter->reset ();
+ _active = _pending_active;
+ return;
+ }
+
+ if (!_active && !_pending_active) {
+ _meter->reset ();
+ _output->silence (nframes);
+ _active = _pending_active;
+ return;
+ }
+
+ // we have to copy the input, because deliver_output() may alter the buffers
+ // in-place, which a send must never do.
+
+ BufferSet& sendbufs = _session.get_mix_buffers (bufs.count());
+ sendbufs.read_from (bufs, nframes);
+ assert(sendbufs.count() == bufs.count());
+
+ /* gain control */
+
+ _amp->set_gain_automation_buffer (_session.send_gain_automation_buffer ());
+ _amp->setup_gain_automation (start_frame, end_frame, nframes);
+ _amp->run (sendbufs, start_frame, end_frame, nframes, true);
+
+ /* deliver to outputs */
+
+ Delivery::run (sendbufs, start_frame, end_frame, nframes, true);
+
+ /* consider metering */
+
+ if (_metering) {
+ if (_amp->gain_control()->get_value() == 0) {
+ _meter->reset();
+ } else {
+ _meter->run (*_output_buffers, start_frame, end_frame, nframes, true);
+ }
+ }
+
+ /* _active was set to _pending_active by Delivery::run() */
}
XMLNode&
}
XMLNode&
-Send::state(bool full)
+Send::state (bool full)
{
- XMLNode& node = IOProcessor::state(full);
+ XMLNode& node = Delivery::state(full);
char buf[32];
+
node.add_property ("type", "send");
- snprintf (buf, sizeof (buf), "%" PRIu32, bitslot);
- node.add_property ("bitslot", buf);
+ snprintf (buf, sizeof (buf), "%" PRIu32, _bitslot);
+
+ if (_role != Listen) {
+ node.add_property ("bitslot", buf);
+ }
+
+ node.add_child_nocopy (_amp->state (full));
return node;
}
int
-Send::set_state(const XMLNode& node)
+Send::set_state (const XMLNode& node, int version)
{
- XMLNodeList nlist = node.children();
- XMLNodeIterator niter;
- const XMLProperty* prop;
-
- if ((prop = node.property ("bitslot")) == 0) {
- bitslot = _session.next_send_id();
- } else {
- sscanf (prop->value().c_str(), "%" PRIu32, &bitslot);
- _session.mark_send_id (bitslot);
+ if (version < 3000) {
+ return set_state_2X (node, version);
}
- const XMLNode* insert_node = &node;
+ const XMLProperty* prop;
- /* Send has regular IO automation (gain, pan) */
+ Delivery::set_state (node, version);
- for (niter = nlist.begin(); niter != nlist.end(); ++niter) {
- if ((*niter)->name() == "IOProcessor") {
- insert_node = *niter;
- } else if ((*niter)->name() == X_("Automation")) {
- _io->set_automation_state (*(*niter), Parameter(GainAutomation));
+ if (node.property ("ignore-bitslot") == 0) {
+
+ /* don't try to reset bitslot if there is a node for it already: this can cause
+ issues with the session's accounting of send ID's
+ */
+
+ if ((prop = node.property ("bitslot")) == 0) {
+ if (_role == Delivery::Aux) {
+ _bitslot = _session.next_aux_send_id ();
+ } else if (_role == Delivery::Send) {
+ _bitslot = _session.next_send_id ();
+ } else {
+ // bitslot doesn't matter but make it zero anyway
+ _bitslot = 0;
+ }
+ } else {
+ if (_role == Delivery::Aux) {
+ _session.unmark_aux_send_id (_bitslot);
+ sscanf (prop->value().c_str(), "%" PRIu32, &_bitslot);
+ _session.mark_aux_send_id (_bitslot);
+ } else if (_role == Delivery::Send) {
+ _session.unmark_send_id (_bitslot);
+ sscanf (prop->value().c_str(), "%" PRIu32, &_bitslot);
+ _session.mark_send_id (_bitslot);
+ } else {
+ // bitslot doesn't matter but make it zero anyway
+ _bitslot = 0;
+ }
}
}
- IOProcessor::set_state (*insert_node);
+ XMLNodeList nlist = node.children();
+ for (XMLNodeIterator i = nlist.begin(); i != nlist.end(); ++i) {
+ if ((*i)->name() == X_("Processor")) {
+ _amp->set_state (**i, version);
+ }
+ }
return 0;
}
-void
-Send::run_in_place (BufferSet& bufs, nframes_t start_frame, nframes_t end_frame, nframes_t nframes, nframes_t offset)
+int
+Send::set_state_2X (const XMLNode& node, int /* version */)
{
- if (active()) {
-
- // we have to copy the input, because IO::deliver_output may alter the buffers
- // in-place, which a send must never do.
+ /* use the IO's name for the name of the send */
+ XMLNodeList const & children = node.children ();
- BufferSet& sendbufs = _session.get_mix_buffers(bufs.count());
+ XMLNodeList::const_iterator i = children.begin();
+ while (i != children.end() && (*i)->name() != X_("Redirect")) {
+ ++i;
+ }
- sendbufs.read_from(bufs, nframes);
- assert(sendbufs.count() == bufs.count());
+ if (i == children.end()) {
+ return -1;
+ }
- _io->deliver_output (sendbufs, start_frame, end_frame, nframes, offset);
+ XMLNodeList const & grand_children = (*i)->children ();
+ XMLNodeList::const_iterator j = grand_children.begin ();
+ while (j != grand_children.end() && (*j)->name() != X_("IO")) {
+ ++j;
+ }
- if (_metering) {
- if (_io->_gain == 0) {
- _io->_meter->reset();
- } else {
- _io->_meter->run_in_place(_io->output_buffers(), start_frame, end_frame, nframes, offset);
- }
- }
+ if (j == grand_children.end()) {
+ return -1;
+ }
- } else {
- _io->silence (nframes, offset);
-
- if (_metering) {
- _io->_meter->reset();
- }
+ XMLProperty const * prop = (*j)->property (X_("name"));
+ if (!prop) {
+ return -1;
}
-}
-void
-Send::set_metering (bool yn)
-{
- _metering = yn;
+ set_name (prop->value ());
- if (!_metering) {
- /* XXX possible thread hazard here */
- _io->peak_meter().reset();
- }
+ return 0;
}
bool
-Send::can_support_input_configuration (ChanCount in) const
+Send::can_support_io_configuration (const ChanCount& in, ChanCount& out)
{
- if (_io->input_maximum() == ChanCount::INFINITE && _io->output_maximum() == ChanCount::INFINITE) {
-
- /* not configured yet */
-
- return true; /* we can support anything the first time we're asked */
+ /* sends have no impact at all on the channel configuration of the
+ streams passing through the route. so, out == in.
+ */
- } else {
+ out = in;
+ return true;
+}
- /* the "input" config for a port insert corresponds to how
- many output ports it will have.
- */
+/** Caller must hold process lock */
+bool
+Send::configure_io (ChanCount in, ChanCount out)
+{
+ if (!_amp->configure_io (in, out)) {
+ return false;
+ }
- if (_io->output_maximum() == in) {
+ if (!Processor::configure_io (in, out)) {
+ return false;
+ }
- return true;
- }
+ if (!_meter->configure_io (ChanCount (DataType::AUDIO, pan_outs()), ChanCount (DataType::AUDIO, pan_outs()))) {
+ return false;
}
- return false;
+ reset_panner ();
+
+ return true;
}
-ChanCount
-Send::output_for_input_configuration (ChanCount in) const
+void
+Send::panshell_changed ()
{
- // from the internal (Insert) perspective a Send does not modify its input whatsoever
- return in;
+ _meter->configure_io (ChanCount (DataType::AUDIO, pan_outs()), ChanCount (DataType::AUDIO, pan_outs()));
}
bool
-Send::configure_io (ChanCount in, ChanCount out)
+Send::set_name (const string& new_name)
{
- /* we're transparent no matter what. fight the power. */
- if (out != in)
- return false;
+ string unique_name;
+
+ if (_role == Delivery::Send) {
+ char buf[32];
- _io->set_output_maximum (in);
- _io->set_output_minimum (in);
- _io->set_input_maximum (ChanCount::ZERO);
- _io->set_input_minimum (ChanCount::ZERO);
+ /* rip any existing numeric part of the name, and append the bitslot
+ */
- bool success = _io->ensure_io (ChanCount::ZERO, in, false, this) == 0;
+ string::size_type last_letter = new_name.find_last_not_of ("0123456789");
+
+ if (last_letter != string::npos) {
+ unique_name = new_name.substr (0, last_letter + 1);
+ } else {
+ unique_name = new_name;
+ }
+
+ snprintf (buf, sizeof (buf), "%u", (_bitslot + 1));
+ unique_name += buf;
- if (success) {
- Processor::configure_io(in, out);
- _io->reset_panner();
- return true;
} else {
- return false;
+ unique_name = new_name;
}
+
+ return Delivery::set_name (unique_name);
}
-ChanCount
-Send::output_streams() const
+bool
+Send::display_to_user () const
{
- // this method reflects the idea that from the perspective of the Route's ProcessorList,
- // a send is just a passthrough. that doesn't match what the Send actually does with its
- // data, but since what it does is invisible to the Route, it appears to be a passthrough.
-
- return _configured_input;
+ /* we ignore Deliver::_display_to_user */
+
+ if (_role == Listen) {
+ /* don't make the monitor/control/listen send visible */
+ return false;
+ }
+
+ return true;
}
-ChanCount
-Send::input_streams() const
+string
+Send::value_as_string (boost::shared_ptr<AutomationControl> ac) const
{
- return _configured_input;
+ return _amp->value_as_string (ac);
}
-
+