X-Git-Url: https://main.carlh.net/gitweb/?a=blobdiff_plain;f=libs%2Fardour%2Fdelivery.cc;h=86a2f99c5893a2bb830e5a6447e5294228ff8d91;hb=6dac4da98344ed8ce609a4e7d567ef2f9fbb9b31;hp=8a13a4b047553d4e9ddf9b320f75b6d7e5a0bcd2;hpb=b10ace15cd23d4cfb999626711b8afc9acc85e04;p=ardour.git diff --git a/libs/ardour/delivery.cc b/libs/ardour/delivery.cc index 8a13a4b047..86a2f99c58 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,6 +22,8 @@ #include "pbd/enumwriter.h" #include "pbd/convert.h" +#include "ardour/midi_buffer.h" + #include "ardour/delivery.h" #include "ardour/audio_buffer.h" #include "ardour/amp.h" @@ -40,97 +42,79 @@ using namespace std; using namespace PBD; using namespace ARDOUR; -sigc::signal Delivery::CycleStart; -sigc::signal Delivery::PannersLegal; -bool Delivery::panners_legal = false; +PBD::Signal1 Delivery::CycleStart; +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) - : IOProcessor(s, boost::shared_ptr(), (r == Listen ? boost::shared_ptr() : io), name) + : IOProcessor(s, boost::shared_ptr(), (role_requires_output_ports (r) ? io : boost::shared_ptr()), name) , _role (r) , _output_buffers (new BufferSet()) - , _solo_level (0) - , _solo_isolated (false) + , _current_gain (1.0) + , _output_offset (0) + , _no_outs_cuz_we_no_monitor (false) , _mute_master (mm) - + , no_panner_reset (false) { - _output_offset = 0; - _current_gain = 1.0; _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_same_thread (*this, boost::bind (&Delivery::cycle_start, this, _1)); } /* deliver to a new IO object */ Delivery::Delivery (Session& s, boost::shared_ptr mm, const string& name, Role r) - : IOProcessor(s, false, (r == Listen ? false : true), name) + : IOProcessor(s, false, (role_requires_output_ports (r) ? true : false), name) , _role (r) , _output_buffers (new BufferSet()) - , _solo_level (0) - , _solo_isolated (false) + , _current_gain (1.0) + , _output_offset (0) + , _no_outs_cuz_we_no_monitor (false) , _mute_master (mm) + , no_panner_reset (false) { - _output_offset = 0; - _current_gain = 1.0; _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_same_thread (*this, boost::bind (&Delivery::cycle_start, this, _1)); } -/* deliver to a new IO object, reconstruct from XML */ -Delivery::Delivery (Session& s, boost::shared_ptr mm, const XMLNode& node) - : IOProcessor (s, false, true, "reset") - , _role (Role (0)) - , _output_buffers (new BufferSet()) - , _solo_level (0) - , _solo_isolated (false) - , _mute_master (mm) +Delivery::~Delivery() { - _output_offset = 0; - _current_gain = 1.0; - _panner = boost::shared_ptr(new Panner (_name, _session)); - - if (set_state (node)) { - throw failed_constructor (); - } - - if (_output) { - _output->changed.connect (mem_fun (*this, &Delivery::output_changed)); - } + delete _output_buffers; } -/* 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()) - , _solo_level (0) - , _solo_isolated (false) - , _mute_master (mm) +std::string +Delivery::display_name () const { - _output_offset = 0; - _current_gain = 1.0; - _panner = boost::shared_ptr(new Panner (_name, _session)); - - if (set_state (node)) { - throw failed_constructor (); - } - - if (_output) { - _output->changed.connect (mem_fun (*this, &Delivery::output_changed)); + switch (_role) { + case Main: + return _("main outs"); + break; + case Listen: + return _("listen"); + break; + case Send: + case Insert: + default: + return name(); } } void -Delivery::cycle_start (nframes_t nframes) +Delivery::cycle_start (nframes_t /*nframes*/) { _output_offset = 0; _no_outs_cuz_we_no_monitor = false; @@ -143,46 +127,120 @@ Delivery::increment_output_offset (nframes_t n) } bool -Delivery::visible () const +Delivery::can_support_io_configuration (const ChanCount& in, ChanCount& out) const { - if (_role & Main) { - return false; - } + if (_role == Main) { - return true; -} + /* the out buffers will be set to point to the port output buffers + of our output object. + */ -bool -Delivery::can_support_io_configuration (const ChanCount& in, ChanCount& out) const -{ - out = in; - return true; + if (_output) { + if (_output->n_ports() != ChanCount::ZERO) { + out = _output->n_ports(); + return true; + } else { + /* not configured yet - we will passthru */ + out = in; + return true; + } + } else { + fatal << "programming error: this should never be reached" << endmsg; + /*NOTREACHED*/ + } + + + } else if (_role == Insert) { + + /* the output buffers will be filled with data from the *input* ports + of this Insert. + */ + + if (_input) { + if (_input->n_ports() != ChanCount::ZERO) { + out = _input->n_ports(); + return true; + } else { + /* not configured yet - we will passthru */ + out = in; + return true; + } + } else { + fatal << "programming error: this should never be reached" << endmsg; + /*NOTREACHED*/ + } + + } else { + fatal << "programming error: this should never be reached" << endmsg; + } + + return false; } bool Delivery::configure_io (ChanCount in, ChanCount out) { - if (out != in) { // always 1:1 + /* check configuration by comparison with our I/O port configuration, if appropriate. + see ::can_support_io_configuration() for comments + */ + + if (_role == Main) { + + 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*/ + } else { + /* I/O not yet configured */ + } + } + } + + } else if (_role == Insert) { + + 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; + /*NOTREACHED*/ + } else { + /* I/O not yet configured */ + } + } + } + } + + if (!Processor::configure_io (in, out)) { return false; } reset_panner (); - - return Processor::configure_io (in, out); + + return true; } void -Delivery::run (BufferSet& bufs, sframes_t start_frame, sframes_t end_frame, nframes_t nframes) +Delivery::run (BufferSet& bufs, sframes_t start_frame, sframes_t end_frame, nframes_t nframes, bool result_required) { + assert (_output); + + PortSet& ports (_output->ports()); + gain_t tgain; + if (_output->n_ports ().get (_output->default_type()) == 0) { - return; + goto out; } - /* this setup is not just for our purposes, but for anything that comes after us in the + if (!_active && !_pending_active) { + _output->silence (nframes); + goto out; + } + + /* 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. */ - PortSet& ports (_output->ports()); output_buffers().attach_buffers (ports, nframes, _output_offset); // this Delivery processor is not a derived type, and thus we assume @@ -190,10 +248,9 @@ Delivery::run (BufferSet& bufs, sframes_t start_frame, sframes_t end_frame, nfra // the main output stage of a Route). Contrast with Send::run() // which cannot do this. - gain_t tgain = target_gain (); - + tgain = target_gain (); + if (tgain != _current_gain) { - /* target gain has changed */ Amp::apply_gain (bufs, nframes, _current_gain, tgain); @@ -206,9 +263,10 @@ Delivery::run (BufferSet& bufs, sframes_t start_frame, sframes_t end_frame, nfra */ _output->silence (nframes); - Amp::apply_simple_gain (bufs, nframes, 0.0); - - return; + if (result_required) { + Amp::apply_simple_gain (bufs, nframes, 0.0); + } + goto out; } else if (tgain != 1.0) { @@ -216,27 +274,58 @@ 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()) { // Use the panner to distribute audio to output port buffers _panner->run (bufs, output_buffers(), start_frame, end_frame, nframes); + if (result_required) { + bufs.read_from (output_buffers (), nframes); + } + } 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); - } + + bool silent; + + for (uint32_t b = 0; b < bufs.count().n_audio(); ++b) { + + AudioBuffer& ab (bufs.get_audio (b)); + Sample* s = ab.data(); + nframes_t n; + + silent = false; + + for (n = 0; nframes < nframes; ++n) { + if (s[n] != 0) { + break; + } + } + if (n == nframes) { + silent = true; + } + + if (silent) { + cerr << _name << ": Buffer " << b << " is silent\n"; + } + } + } if (bufs.count().n_midi() > 0 && ports.count().n_midi () > 0) { _output->copy_to_outputs (bufs, DataType::MIDI, nframes, _output_offset); } + + } -} + out: + _active = _pending_active; +} XMLNode& Delivery::state (bool full_state) @@ -258,14 +347,14 @@ Delivery::state (bool full_state) } 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; @@ -274,10 +363,10 @@ Delivery::set_state (const XMLNode& node) } XMLNode* pan_node = node.child (X_("Panner")); - + if (pan_node) { - _panner->set_state (*pan_node); - } + _panner->set_state (*pan_node, version); + } reset_panner (); @@ -291,7 +380,7 @@ Delivery::reset_panner () if (!no_panner_reset) { uint32_t ntargets; - + if (_output) { ntargets = _output->n_ports().n_audio(); } else { @@ -302,7 +391,7 @@ Delivery::reset_panner () } } 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)); } } @@ -348,7 +437,7 @@ int Delivery::reset_panners () { panners_legal = true; - return PannersLegal (); + return *PannersLegal (); } @@ -376,20 +465,38 @@ Delivery::transport_stopped (sframes_t frame) } void -Delivery::flush (nframes_t nframes) +Delivery::flush (nframes_t nframes, nframes64_t time) { /* io_lock, not taken: function must be called from Session::process() calltree */ - + PortSet& ports (_output->ports()); for (PortSet::iterator i = ports.begin(); i != ports.end(); ++i) { - (*i).flush_buffers (nframes, _output_offset); + (*i).flush_buffers (nframes, time, _output_offset); + } +} + +void +Delivery::transport_stopped () +{ + /* turn off any notes that are on */ + + PortSet& ports (_output->ports()); + + for (PortSet::iterator i = ports.begin(); i != ports.end(); ++i) { + (*i).transport_stopped (); } } gain_t Delivery::target_gain () { + /* if we've been requested to deactivate, our target gain is zero */ + + if (!_pending_active) { + return 0.0; + } + /* if we've been told not to output because its a monitoring situation and we're not monitoring, then be quiet. */ @@ -398,45 +505,37 @@ 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: - if (_placement == PreFader) { - mp = MuteMaster::PreFader; - } else { - mp = MuteMaster::PostFader; - } - break; - } - - if (_solo_isolated) { - - /* ... but we are isolated from all that nonsense */ - - desired_gain = _mute_master->mute_gain_at (mp); - - } else if (_session.soloing()) { - - desired_gain = min (Config->get_solo_mute_gain(), _mute_master->mute_gain_at (mp)); - - } else { - desired_gain = _mute_master->mute_gain_at (mp); - } - } + gain_t desired_gain = -1.0f; + + 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 */ + mp = MuteMaster::PreFader; + break; + } + + desired_gain = _mute_master->mute_gain_at (mp); + + if (_role == Listen && _session.monitor_out() && !_session.listening()) { + + /* 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. + */ + + desired_gain = 0.0; + + } return desired_gain; } @@ -460,7 +559,7 @@ Delivery::set_name (const std::string& name) } void -Delivery::output_changed (IOChange change, void* src) +Delivery::output_changed (IOChange change, void* /*src*/) { if (change & ARDOUR::ConfigurationChanged) { reset_panner ();