X-Git-Url: https://main.carlh.net/gitweb/?a=blobdiff_plain;f=libs%2Fardour%2Fdelivery.cc;h=9eaf843f7c359d2f642c0194ffe3621580c9d930;hb=07112b55e0bb7ceb9e5c05ab4df167ecaf7edd9b;hp=5a7365df96664d03e8e64af107ed0a8d60e94c5f;hpb=b65f8073ba306ac2d85133875746767e7c6b0eb6;p=ardour.git diff --git a/libs/ardour/delivery.cc b/libs/ardour/delivery.cc index 5a7365df96..9eaf843f7c 100644 --- a/libs/ardour/delivery.cc +++ b/libs/ardour/delivery.cc @@ -1,16 +1,16 @@ /* - 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 the Free Software Foundation; either version 2 of the License, or (at your option) any later version. - + This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. - + You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. @@ -22,127 +22,88 @@ #include "pbd/enumwriter.h" #include "pbd/convert.h" -#include "ardour/delivery.h" -#include "ardour/audio_buffer.h" #include "ardour/amp.h" +#include "ardour/audioengine.h" #include "ardour/buffer_set.h" -#include "ardour/configuration.h" +#include "ardour/debug.h" +#include "ardour/delivery.h" #include "ardour/io.h" -#include "ardour/meter.h" #include "ardour/mute_master.h" -#include "ardour/panner.h" +#include "ardour/pannable.h" +#include "ardour/panner_shell.h" #include "ardour/port.h" #include "ardour/session.h" #include "i18n.h" +namespace ARDOUR { class Panner; } + using namespace std; using namespace PBD; using namespace ARDOUR; -sigc::signal Delivery::CycleStart; -sigc::signal Delivery::PannersLegal; -bool Delivery::panners_legal = false; +PBD::Signal0 Delivery::PannersLegal; +bool Delivery::panners_legal = false; /* deliver to an existing IO object */ -Delivery::Delivery (Session& s, boost::shared_ptr io, boost::shared_ptr mm, const string& name, Role r) +Delivery::Delivery (Session& s, boost::shared_ptr io, boost::shared_ptr pannable, + boost::shared_ptr mm, const string& name, Role r) : IOProcessor(s, boost::shared_ptr(), (role_requires_output_ports (r) ? io : boost::shared_ptr()), name) , _role (r) , _output_buffers (new BufferSet()) , _current_gain (1.0) - , _output_offset (0) , _no_outs_cuz_we_no_monitor (false) - , _solo_level (0) - , _solo_isolated (false) , _mute_master (mm) - , no_panner_reset (false) - + , _no_panner_reset (false) { - _panner = boost::shared_ptr(new Panner (_name, _session)); - - if (_output) { - _output->changed.connect (mem_fun (*this, &Delivery::output_changed)); + if (pannable) { + _panshell = boost::shared_ptr(new PannerShell (_name, _session, pannable)); } - CycleStart.connect (mem_fun (*this, &Delivery::cycle_start)); -} - -/* deliver to a new IO object */ - -Delivery::Delivery (Session& s, boost::shared_ptr mm, const string& name, Role r) - : IOProcessor(s, false, (role_requires_output_ports (r) ? true : false), name) - , _role (r) - , _output_buffers (new BufferSet()) - , _current_gain (1.0) - , _output_offset (0) - , _no_outs_cuz_we_no_monitor (false) - , _solo_level (0) - , _solo_isolated (false) - , _mute_master (mm) - , no_panner_reset (false) -{ - _panner = boost::shared_ptr(new Panner (_name, _session)); + _display_to_user = false; if (_output) { - _output->changed.connect (mem_fun (*this, &Delivery::output_changed)); + _output->changed.connect_same_thread (*this, boost::bind (&Delivery::output_changed, this, _1, _2)); } - - CycleStart.connect (mem_fun (*this, &Delivery::cycle_start)); } -/* deliver to a new IO object, reconstruct from XML */ +/* deliver to a new IO object */ -Delivery::Delivery (Session& s, boost::shared_ptr mm, const XMLNode& node) - : IOProcessor (s, false, true, "reset") - , _role (Role (0)) +Delivery::Delivery (Session& s, boost::shared_ptr pannable, boost::shared_ptr mm, const string& name, Role r) + : IOProcessor(s, false, (role_requires_output_ports (r) ? true : false), name, "", DataType::AUDIO, (r == Send)) + , _role (r) , _output_buffers (new BufferSet()) , _current_gain (1.0) - , _output_offset (0) , _no_outs_cuz_we_no_monitor (false) - , _solo_level (0) - , _solo_isolated (false) , _mute_master (mm) - , no_panner_reset (false) + , _no_panner_reset (false) { - _panner = boost::shared_ptr(new Panner (_name, _session)); - - if (set_state (node)) { - throw failed_constructor (); + if (pannable) { + _panshell = boost::shared_ptr(new PannerShell (_name, _session, pannable)); } + _display_to_user = false; + if (_output) { - _output->changed.connect (mem_fun (*this, &Delivery::output_changed)); + _output->changed.connect_same_thread (*this, boost::bind (&Delivery::output_changed, this, _1, _2)); } - - CycleStart.connect (mem_fun (*this, &Delivery::cycle_start)); } -/* deliver to an existing IO object, reconstruct from XML */ -Delivery::Delivery (Session& s, boost::shared_ptr out, boost::shared_ptr mm, const XMLNode& node) - : IOProcessor (s, boost::shared_ptr(), out, "reset") - , _role (Role (0)) - , _output_buffers (new BufferSet()) - , _current_gain (1.0) - , _output_offset (0) - , _no_outs_cuz_we_no_monitor (false) - , _solo_level (0) - , _solo_isolated (false) - , _mute_master (mm) - , no_panner_reset (false) +Delivery::~Delivery() { - _panner = boost::shared_ptr(new Panner (_name, _session)); + DEBUG_TRACE (DEBUG::Destruction, string_compose ("delivery %1 destructor\n", _name)); - if (set_state (node)) { - throw failed_constructor (); - } + /* this object should vanish from any signal callback lists + that it is on before we get any further. The full qualification + of the method name is not necessary, but is here to make it + clear that this call is about signals, not data flow connections. + */ - if (_output) { - _output->changed.connect (mem_fun (*this, &Delivery::output_changed)); - } + ScopedConnectionList::drop_connections (); - CycleStart.connect (mem_fun (*this, &Delivery::cycle_start)); + delete _output_buffers; } std::string @@ -162,25 +123,6 @@ Delivery::display_name () const } } -void -Delivery::cycle_start (nframes_t /*nframes*/) -{ - _output_offset = 0; - _no_outs_cuz_we_no_monitor = false; -} - -void -Delivery::increment_output_offset (nframes_t n) -{ - _output_offset += n; -} - -bool -Delivery::visible () const -{ - return true; -} - bool Delivery::can_support_io_configuration (const ChanCount& in, ChanCount& out) const { @@ -190,9 +132,10 @@ Delivery::can_support_io_configuration (const ChanCount& in, ChanCount& out) con of our output object. */ - if (_output) { + if (_output) { if (_output->n_ports() != ChanCount::ZERO) { - out = _output->n_ports(); + /* increase number of output ports if the processor chain requires it */ + out = ChanCount::max (_output->n_ports(), in); return true; } else { /* not configured yet - we will passthru */ @@ -208,10 +151,10 @@ Delivery::can_support_io_configuration (const ChanCount& in, ChanCount& out) con } else if (_role == Insert) { /* the output buffers will be filled with data from the *input* ports - of this Insert. + of this Insert. */ - if (_input) { + if (_input) { if (_input->n_ports() != ChanCount::ZERO) { out = _input->n_ports(); return true; @@ -232,29 +175,34 @@ Delivery::can_support_io_configuration (const ChanCount& in, ChanCount& out) con return false; } +/** Caller must hold process lock */ bool Delivery::configure_io (ChanCount in, ChanCount out) { +#ifndef NDEBUG + bool r = AudioEngine::instance()->process_lock().trylock(); + assert (!r && "trylock inside Delivery::configure_io"); +#endif + /* check configuration by comparison with our I/O port configuration, if appropriate. - see ::can_support_io_configuration() for comments + see ::can_support_io_configuration() for comments */ if (_role == Main) { - if (_output) { + if (_output) { if (_output->n_ports() != out) { if (_output->n_ports() != ChanCount::ZERO) { - fatal << _name << " programming error: configure_io with nports = " << _output->n_ports() << " called with " << in << " and " << out << " with " << _output->n_ports() << " output ports" << endmsg; - /*NOTREACHED*/ + _output->ensure_io (out, false, this); } else { /* I/O not yet configured */ } - } + } } } else if (_role == Insert) { - if (_input) { + if (_input) { if (_input->n_ports() != in) { if (_input->n_ports() != ChanCount::ZERO) { fatal << _name << " programming error: configure_io called with " << in << " and " << out << " with " << _input->n_ports() << " input ports" << endmsg; @@ -264,6 +212,7 @@ Delivery::configure_io (ChanCount in, ChanCount out) } } } + } if (!Processor::configure_io (in, out)) { @@ -276,7 +225,7 @@ Delivery::configure_io (ChanCount in, ChanCount out) } void -Delivery::run (BufferSet& bufs, sframes_t start_frame, sframes_t end_frame, nframes_t nframes) +Delivery::run (BufferSet& bufs, framepos_t start_frame, framepos_t end_frame, pframes_t nframes, bool result_required) { assert (_output); @@ -292,11 +241,11 @@ Delivery::run (BufferSet& bufs, sframes_t start_frame, sframes_t end_frame, nfra goto out; } - /* this setup is not just for our purposes, but for anything that comes after us in the + /* this setup is not just for our purposes, but for anything that comes after us in the processing pathway that wants to use this->output_buffers() for some reason. */ - output_buffers().attach_buffers (ports, nframes, _output_offset); + output_buffers().get_jack_port_addresses (ports, nframes); // this Delivery processor is not a derived type, and thus we assume // we really can modify the buffers passed in (it is almost certainly @@ -304,9 +253,8 @@ Delivery::run (BufferSet& bufs, sframes_t start_frame, sframes_t end_frame, nfra // which cannot do this. tgain = target_gain (); - + if (tgain != _current_gain) { - /* target gain has changed */ Amp::apply_gain (bufs, nframes, _current_gain, tgain); @@ -317,9 +265,12 @@ Delivery::run (BufferSet& bufs, sframes_t start_frame, sframes_t end_frame, nfra /* we were quiet last time, and we're still supposed to be quiet. Silence the outputs, and make sure the buffers are quiet too, */ - + _output->silence (nframes); - Amp::apply_simple_gain (bufs, nframes, 0.0); + if (result_required) { + bufs.set_count (output_buffers().count ()); + Amp::apply_simple_gain (bufs, nframes, 0.0); + } goto out; } else if (tgain != 1.0) { @@ -328,26 +279,35 @@ Delivery::run (BufferSet& bufs, sframes_t start_frame, sframes_t end_frame, nfra Amp::apply_simple_gain (bufs, nframes, tgain); } - // Attach output buffers to port buffers - - if (_panner && _panner->npanners() && !_panner->bypassed()) { + if (_panshell && !_panshell->bypassed() && _panshell->panner()) { // Use the panner to distribute audio to output port buffers - _panner->run (bufs, output_buffers(), start_frame, end_frame, nframes); + _panshell->run (bufs, output_buffers(), start_frame, end_frame, nframes); + + // MIDI data will not have been delivered by the panner + + if (bufs.count().n_midi() > 0 && ports.count().n_midi () > 0) { + _output->copy_to_outputs (bufs, DataType::MIDI, nframes, 0); + } } else { + // Do a 1:1 copy of data to output ports if (bufs.count().n_audio() > 0 && ports.count().n_audio () > 0) { - _output->copy_to_outputs (bufs, DataType::AUDIO, nframes, _output_offset); - } + _output->copy_to_outputs (bufs, DataType::AUDIO, nframes, 0); + } if (bufs.count().n_midi() > 0 && ports.count().n_midi () > 0) { - _output->copy_to_outputs (bufs, DataType::MIDI, nframes, _output_offset); + _output->copy_to_outputs (bufs, DataType::MIDI, nframes, 0); } } + if (result_required) { + bufs.read_from (output_buffers (), nframes); + } + out: _active = _pending_active; } @@ -366,20 +326,23 @@ Delivery::state (bool full_state) } node.add_property("role", enum_2_string(_role)); - node.add_child_nocopy (_panner->state (full_state)); + + if (_panshell) { + node.add_child_nocopy (_panshell->get_state ()); + } return node; } int -Delivery::set_state (const XMLNode& node) +Delivery::set_state (const XMLNode& node, int version) { const XMLProperty* prop; - if (IOProcessor::set_state (node)) { + if (IOProcessor::set_state (node, version)) { return -1; } - + if ((prop = node.property ("role")) != 0) { _role = Role (string_2_enum (prop->value(), _role)); // std::cerr << this << ' ' << _name << " set role to " << enum_2_string (_role) << std::endl; @@ -387,118 +350,142 @@ Delivery::set_state (const XMLNode& node) // std::cerr << this << ' ' << _name << " NO ROLE INFO\n"; } - XMLNode* pan_node = node.child (X_("Panner")); - - if (pan_node) { - _panner->set_state (*pan_node); - } + XMLNode* pan_node = node.child (X_("PannerShell")); + + if (pan_node && _panshell) { + _panshell->set_state (*pan_node, version); + } reset_panner (); return 0; } +void +Delivery::unpan () +{ + /* caller must hold process lock */ + + _panshell.reset (); +} + +uint32_t +Delivery::pan_outs () const +{ + if (_output) { + return _output->n_ports().n_audio(); + } + + return _configured_output.n_audio(); +} + void Delivery::reset_panner () { if (panners_legal) { - if (!no_panner_reset) { + if (!_no_panner_reset) { - uint32_t ntargets; - - if (_output) { - ntargets = _output->n_ports().n_audio(); - } else { - ntargets = _configured_output.n_audio(); + if (_panshell) { + _panshell->configure_io (ChanCount (DataType::AUDIO, pans_required()), ChanCount (DataType::AUDIO, pan_outs())); + + if (_role == Main) { + _panshell->pannable()->set_panner (_panshell->panner()); + } } - - _panner->reset (ntargets, pans_required()); } + } else { panner_legal_c.disconnect (); - panner_legal_c = PannersLegal.connect (mem_fun (*this, &Delivery::panners_became_legal)); + PannersLegal.connect_same_thread (panner_legal_c, boost::bind (&Delivery::panners_became_legal, this)); } } -int +void Delivery::panners_became_legal () { - uint32_t ntargets; - - if (_output) { - ntargets = _output->n_ports().n_audio(); - } else { - ntargets = _configured_output.n_audio(); + if (_panshell) { + _panshell->configure_io (ChanCount (DataType::AUDIO, pans_required()), ChanCount (DataType::AUDIO, pan_outs())); + + if (_role == Main) { + _panshell->pannable()->set_panner (_panshell->panner()); + } } - _panner->reset (ntargets, pans_required()); - _panner->load (); // automation panner_legal_c.disconnect (); - return 0; } void Delivery::defer_pan_reset () { - no_panner_reset = true; + _no_panner_reset = true; } void Delivery::allow_pan_reset () { - no_panner_reset = false; + _no_panner_reset = false; reset_panner (); } int -Delivery::disable_panners (void) +Delivery::disable_panners () { panners_legal = false; return 0; } -int +void Delivery::reset_panners () { panners_legal = true; - return PannersLegal (); + PannersLegal (); } - void -Delivery::start_pan_touch (uint32_t which) +Delivery::flush_buffers (framecnt_t nframes) { - if (which < _panner->npanners()) { - _panner->pan_control(which)->start_touch(); + /* io_lock, not taken: function must be called from Session::process() calltree */ + + if (!_output) { + return; + } + + PortSet& ports (_output->ports()); + + for (PortSet::iterator i = ports.begin(); i != ports.end(); ++i) { + i->flush_buffers (nframes); } } void -Delivery::end_pan_touch (uint32_t which) +Delivery::transport_stopped (framepos_t now) { - if (which < _panner->npanners()) { - _panner->pan_control(which)->stop_touch(); + Processor::transport_stopped (now); + + if (_panshell) { + _panshell->pannable()->transport_stopped (now); } -} + if (_output) { + PortSet& ports (_output->ports()); -void -Delivery::transport_stopped (sframes_t frame) -{ - _panner->transport_stopped (frame); + for (PortSet::iterator i = ports.begin(); i != ports.end(); ++i) { + i->transport_stopped (); + } + } } void -Delivery::flush (nframes_t nframes) +Delivery::realtime_locate () { - /* io_lock, not taken: function must be called from Session::process() calltree */ - - PortSet& ports (_output->ports()); + if (_output) { + PortSet& ports (_output->ports()); - for (PortSet::iterator i = ports.begin(); i != ports.end(); ++i) { - (*i).flush_buffers (nframes, _output_offset); - } + for (PortSet::iterator i = ports.begin(); i != ports.end(); ++i) { + i->realtime_locate (); + } + } } gain_t @@ -518,43 +505,38 @@ Delivery::target_gain () return 0.0; } - gain_t desired_gain; - - if (_solo_level) { - desired_gain = 1.0; - } else { - - MuteMaster::MutePoint mp; - - switch (_role) { - case Main: - mp = MuteMaster::Main; - break; - case Listen: - mp = MuteMaster::Listen; - break; - case Send: - case Insert: - case Aux: - /* XXX FIX ME this is wrong, we need per-delivery muting */ + MuteMaster::MutePoint mp = MuteMaster::Main; // stupid gcc uninit warning + + switch (_role) { + case Main: + mp = MuteMaster::Main; + break; + case Listen: + mp = MuteMaster::Listen; + break; + case Send: + case Insert: + case Aux: + if (_pre_fader) { mp = MuteMaster::PreFader; - break; + } else { + mp = MuteMaster::PostFader; } + break; + } - if (_solo_isolated) { - - /* ... but we are isolated from all that nonsense */ - - desired_gain = _mute_master->mute_gain_at (mp); + gain_t desired_gain = _mute_master->mute_gain_at (mp); - } else if (_session.soloing()) { + if (_role == Listen && _session.monitor_out() && !_session.listening()) { - desired_gain = min (Config->get_solo_mute_gain(), _mute_master->mute_gain_at (mp)); + /* nobody is soloed, and this delivery is a listen-send to the + control/monitor/listen bus, we should be silent since + it gets its signal from the master out. + */ - } else { - desired_gain = _mute_master->mute_gain_at (mp); - } - } + desired_gain = 0.0; + + } return desired_gain; } @@ -571,17 +553,30 @@ Delivery::set_name (const std::string& name) bool ret = IOProcessor::set_name (name); if (ret) { - ret = _panner->set_name (name); + ret = _panshell->set_name (name); } return ret; } +bool ignore_output_change = false; + void Delivery::output_changed (IOChange change, void* /*src*/) { - if (change & ARDOUR::ConfigurationChanged) { + if (change.type & IOChange::ConfigurationChanged) { reset_panner (); + _output_buffers->attach_buffers (_output->ports ()); + } +} + +boost::shared_ptr +Delivery::panner () const +{ + if (_panshell) { + return _panshell->panner(); + } else { + return boost::shared_ptr(); } }