X-Git-Url: https://main.carlh.net/gitweb/?a=blobdiff_plain;f=libs%2Fardour%2Finternal_send.cc;h=5ba9954811844369d504138bdda5c4a8f3cb7472;hb=b88e7fdcca8ef8fa4c22f93c2934b30713ab4716;hp=60c0cde6afd6ba104be5e682c297112fc37d6d36;hpb=15554385d6227e46457b0ab8fa36179ce237b0fe;p=ardour.git diff --git a/libs/ardour/internal_send.cc b/libs/ardour/internal_send.cc index 60c0cde6af..5ba9954811 100644 --- a/libs/ardour/internal_send.cc +++ b/libs/ardour/internal_send.cc @@ -1,5 +1,5 @@ /* - Copyright (C) 2009 Paul Davis + Copyright (C) 2009 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 @@ -17,196 +17,258 @@ */ -#include +#include -#include +#include "pbd/error.h" +#include "pbd/failed_constructor.h" -#include -#include -#include -#include -#include -#include -#include +#include "ardour/amp.h" +#include "ardour/internal_send.h" +#include "ardour/meter.h" +#include "ardour/route.h" +#include "ardour/session.h" #include "i18n.h" -using namespace ARDOUR; using namespace PBD; +using namespace ARDOUR; -InternalSend::InternalSend (Session& s, Placement p, boost::shared_ptr dst) - : IOProcessor (s, string_compose (_(">%1"), dst->name()), p, - -1, -1, -1, -1, - DataType::AUDIO, false) - , destination (dst) +InternalSend::InternalSend (Session& s, boost::shared_ptr mm, boost::shared_ptr sendto, Delivery::Role role) + : Send (s, mm, role) + , _send_to (sendto) { - _metering = false; + if ((target = _send_to->get_return_buffer ()) == 0) { + throw failed_constructor(); + } - destination->input_changed.connect (mem_fun (*this, &InternalSend::destination_io_config_changed)); + set_name (sendto->name()); - destination_io_config_changed (ConfigurationChanged, this); + _send_to->GoingAway.connect (mem_fun (*this, &InternalSend::send_to_going_away)); + _send_to->NameChanged.connect (mem_fun (*this, &InternalSend::send_to_name_changed)); +} - ProcessorCreated (this); /* EMIT SIGNAL */ +InternalSend::InternalSend (Session& s, boost::shared_ptr mm, const XMLNode& node) + : Send (s, mm, node, Stateful::loading_state_version, Delivery::Aux /* will be reset in set_state() */) +{ + /* Send constructor will set its state, so here we just need to set up our own */ + set_our_state (node, Stateful::loading_state_version); } InternalSend::~InternalSend () { - GoingAway (); + if (_send_to) { + _send_to->release_return_buffer (); + } + + connect_c.disconnect (); } void -InternalSend::destination_io_config_changed (IOChange c, void* src) +InternalSend::send_to_going_away () { - if (!(c & ConfigurationChanged)) { + target = 0; + _send_to.reset (); + _send_to_id = "0"; +} + +void +InternalSend::run (BufferSet& bufs, sframes_t start_frame, sframes_t end_frame, nframes_t nframes, bool) +{ + if ((!_active && !_pending_active) || !target || !_send_to) { + _meter->reset (); return; } - _io->disconnect_outputs (this); + // we have to copy the input, because we may alter the buffers with the amp + // in-place, which a send must never do. - _io->ensure_io (ChanCount::ZERO, destination->n_inputs(), false, this); + assert(mixbufs.available() >= bufs.count()); + mixbufs.read_from (bufs, nframes); - PortSet::const_iterator us (_io->outputs().begin()); - PortSet::const_iterator them (destination->inputs().begin ()); + /* gain control */ - for (; us != _io->outputs().end() && them != destination->inputs().end(); ++us, ++them) { - (const_cast(&(*us)))->connect (const_cast(&(*them))); - } -} + gain_t tgain = target_gain (); -XMLNode& -InternalSend::get_state(void) -{ - fatal << X_("InternalSend::get_state() called - should never happen") << endmsg; - /*NOTREACHED*/ - return *(new XMLNode ("foo")); -} + if (tgain != _current_gain) { -int -InternalSend::set_state(const XMLNode& node) -{ - fatal << X_("InternalSend::set_state() called - should never happen") << endmsg; - /*NOTREACHED*/ - return 0; -} + /* target gain has changed */ -void -InternalSend::run_in_place (BufferSet& bufs, nframes_t start_frame, nframes_t end_frame, nframes_t nframes, nframes_t offset) -{ - if (active()) { + Amp::apply_gain (mixbufs, nframes, _current_gain, tgain); + _current_gain = tgain; - // we have to copy the input, because IO::deliver_output may alter the buffers - // in-place, which a send must never do. otherwise its gain settings will - // affect the signal seen later in the parent Route. + } else if (tgain == 0.0) { - // BufferSet& sendbufs = _session.get_mix_buffers(bufs.count()); + /* we were quiet last time, and we're still supposed to be quiet. + */ - // sendbufs.read_from (bufs, nframes); - // assert(sendbufs.count() == bufs.count()); + _meter->reset (); + Amp::apply_simple_gain (mixbufs, nframes, 0.0); + goto out; - _io->deliver_output (bufs, start_frame, end_frame, nframes, offset); + } else if (tgain != 1.0) { - if (_metering) { - if (_io->effective_gain() == 0) { - _io->peak_meter().reset(); - } else { - _io->peak_meter().run_in_place(_io->output_buffers(), start_frame, end_frame, nframes, offset); - } - } + /* target gain has not changed, but is not zero or unity */ + Amp::apply_simple_gain (mixbufs, nframes, tgain); + } - } else { - _io->silence (nframes, offset); - if (_metering) { - _io->peak_meter().reset(); + // Can't automate gain for sends or returns yet because we need different buffers + // so that we don't overwrite the main automation data for the route amp + // _amp->setup_gain_automation (start_frame, end_frame, nframes); + + _amp->run (mixbufs, start_frame, end_frame, nframes, true); + + /* XXX NEED TO PAN */ + + /* consider metering */ + + if (_metering) { + if (_amp->gain_control()->get_value() == 0) { + _meter->reset(); + } else { + _meter->run (mixbufs, start_frame, end_frame, nframes, true); } } + + /* deliver to target */ + + target->merge_from (mixbufs, nframes); + + out: + _active = _pending_active; } void -InternalSend::set_metering (bool yn) +InternalSend::set_block_size (nframes_t nframes) { - _metering = yn; - - if (!_metering) { - /* XXX possible thread hazard here */ - _io->peak_meter().reset(); - } + mixbufs.ensure_buffers (_configured_input, nframes); } bool -InternalSend::can_support_io_configuration (const ChanCount& in, ChanCount& out_is_ignored) const +InternalSend::feeds (boost::shared_ptr other) const +{ + return _send_to == other; +} + +XMLNode& +InternalSend::state (bool full) { - /* number of outputs is fixed (though mutable by changing the I/O configuration - of the destination) - */ + XMLNode& node (Send::state (full)); - cerr << "IS: testing I/O config in=" << in.n_audio() << " out=" << out_is_ignored.n_audio() << endl; + /* this replaces any existing "type" property */ - if (in == _io->n_outputs()) { - return 1; + node.add_property ("type", "intsend"); + + if (_send_to) { + node.add_property ("target", _send_to->id().to_s()); } - return -1; + return node; } -bool -InternalSend::configure_io (ChanCount in, ChanCount out) +XMLNode& +InternalSend::get_state() +{ + return state (true); +} + +int +InternalSend::set_our_state (const XMLNode& node, int version) { - cerr << "Configure IS for in " << in.n_audio() << " out = " << out.n_audio() << endl; + const XMLProperty* prop; + + if ((prop = node.property ("target")) != 0) { + + _send_to_id = prop->value(); - /* we're transparent no matter what. fight the power. */ + /* if we're loading a session, the target route may not have been + create yet. make sure we defer till we are sure that it should + exist. + */ - if (out != in) { - return false; + if (!IO::connecting_legal) { + connect_c = IO::ConnectingLegal.connect (mem_fun (*this, &InternalSend::connect_when_legal)); + } else { + connect_when_legal (); + } } - _io->set_output_maximum (in); - _io->set_output_minimum (in); - _io->set_input_maximum (ChanCount::ZERO); - _io->set_input_minimum (ChanCount::ZERO); + return 0; +} + +int +InternalSend::set_state (const XMLNode& node, int version) +{ + Send::set_state (node, version); + return set_our_state (node, version); +} + +int +InternalSend::connect_when_legal () +{ + connect_c.disconnect (); + + if (_send_to_id == "0") { + /* it vanished before we could connect */ + return 0; + } - out = _io->n_outputs(); + if ((_send_to = _session.route_by_id (_send_to_id)) == 0) { + error << X_("cannot find route to connect to") << endmsg; + return -1; + } - Processor::configure_io(in, out); + if ((target = _send_to->get_return_buffer ()) == 0) { + error << X_("target for internal send has no return buffer") << endmsg; + return -1; + } - _io->reset_panner(); + return 0; +} +bool +InternalSend::can_support_io_configuration (const ChanCount& in, ChanCount& out) const +{ + out = in; return true; } -ChanCount -InternalSend::output_streams() const +bool +InternalSend::configure_io (ChanCount in, ChanCount out) { - // 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 _io->n_outputs (); + bool ret = Send::configure_io (in, out); + set_block_size (_session.engine().frames_per_cycle()); + return ret; } -ChanCount -InternalSend::input_streams() const +bool +InternalSend::set_name (const std::string& str) { - return _configured_input; + /* rules for external sends don't apply to us */ + return IOProcessor::set_name (str); } - -void -InternalSend::expect_inputs (const ChanCount& expected) +std::string +InternalSend::display_name () const { - if (expected != expected_inputs) { - expected_inputs = expected; - _io->reset_panner (); + if (_role == Aux) { + return string_compose (X_("aux-%1"), _name); + } else { + return _name; } } -void -InternalSend::activate () +bool +InternalSend::visible () const { - Processor::activate (); + if (_role == Aux) { + return true; + } + + return false; } void -InternalSend::deactivate () +InternalSend::send_to_name_changed () { - Processor::deactivate (); + set_name (_send_to->name ()); }