X-Git-Url: https://main.carlh.net/gitweb/?a=blobdiff_plain;f=libs%2Fardour%2Fdelivery.cc;h=463bfce88bc989eb2e1e1e1163cb400a94f2d17a;hb=903c3ec5a;hp=30f7ef63fa2761245543d347d1f7a45ee57eedac;hpb=23e7cf10191270d70357ccf0ed9294f020c7b7ab;p=ardour.git diff --git a/libs/ardour/delivery.cc b/libs/ardour/delivery.cc index 30f7ef63fa..463bfce88b 100644 --- a/libs/ardour/delivery.cc +++ b/libs/ardour/delivery.cc @@ -1,26 +1,30 @@ /* - 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. -*/ + * Copyright (C) 2009-2012 Carl Hetherington + * Copyright (C) 2009-2012 David Robillard + * Copyright (C) 2009-2017 Paul Davis + * Copyright (C) 2013-2017 Robin Gareus + * Copyright (C) 2018 Len Ovens + * + * 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., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ #include #include #include "pbd/enumwriter.h" -#include "pbd/convert.h" +#include "pbd/enum_convert.h" #include "ardour/amp.h" #include "ardour/audioengine.h" @@ -34,7 +38,11 @@ #include "ardour/port.h" #include "ardour/session.h" -#include "i18n.h" +#include "pbd/i18n.h" + +namespace PBD { + DEFINE_ENUM_CONVERT(ARDOUR::Delivery::Role); +} namespace ARDOUR { class Panner; } @@ -52,14 +60,14 @@ Delivery::Delivery (Session& s, boost::shared_ptr io, boost::shared_ptr(), (role_requires_output_ports (r) ? io : boost::shared_ptr()), name) , _role (r) , _output_buffers (new BufferSet()) - , _current_gain (1.0) + , _current_gain (GAIN_COEFF_UNITY) , _no_outs_cuz_we_no_monitor (false) , _mute_master (mm) , _no_panner_reset (false) { if (pannable) { bool is_send = false; - if (r & (Delivery::Send|Delivery::Aux)) is_send = true; + if (r & (Delivery::Send|Delivery::Aux|Delivery::Foldback)) is_send = true; _panshell = boost::shared_ptr(new PannerShell (_name, _session, pannable, is_send)); } @@ -76,14 +84,14 @@ Delivery::Delivery (Session& s, boost::shared_ptr pannable, boost::sha : 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) + , _current_gain (GAIN_COEFF_UNITY) , _no_outs_cuz_we_no_monitor (false) , _mute_master (mm) , _no_panner_reset (false) { if (pannable) { bool is_send = false; - if (r & (Delivery::Send|Delivery::Aux)) is_send = true; + if (r & (Delivery::Send|Delivery::Aux|Delivery::Foldback)) is_send = true; _panshell = boost::shared_ptr(new PannerShell (_name, _session, pannable, is_send)); } @@ -97,11 +105,11 @@ Delivery::Delivery (Session& s, boost::shared_ptr pannable, boost::sha Delivery::~Delivery() { - DEBUG_TRACE (DEBUG::Destruction, string_compose ("delivery %1 destructor\n", _name)); + DEBUG_TRACE (DEBUG::Destruction, string_compose ("delivery %1 destructor\n", _name)); /* 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 + of the method name is not necessary, but is here to make it clear that this call is about signals, not data flow connections. */ @@ -148,7 +156,7 @@ Delivery::can_support_io_configuration (const ChanCount& in, ChanCount& out) } } else { fatal << "programming error: this should never be reached" << endmsg; - /*NOTREACHED*/ + abort(); /*NOTREACHED*/ } @@ -169,7 +177,7 @@ Delivery::can_support_io_configuration (const ChanCount& in, ChanCount& out) } } else { fatal << "programming error: this should never be reached" << endmsg; - /*NOTREACHED*/ + abort(); /*NOTREACHED*/ } } else { @@ -210,7 +218,7 @@ Delivery::configure_io (ChanCount in, ChanCount out) 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*/ + abort(); /*NOTREACHED*/ } else { /* I/O not yet configured */ } @@ -229,14 +237,14 @@ Delivery::configure_io (ChanCount in, ChanCount out) } void -Delivery::run (BufferSet& bufs, framepos_t start_frame, framepos_t end_frame, pframes_t nframes, bool result_required) +Delivery::run (BufferSet& bufs, samplepos_t start_sample, samplepos_t end_sample, double /*speed*/, pframes_t nframes, bool result_required) { assert (_output); PortSet& ports (_output->ports()); gain_t tgain; - if (_output->n_ports ().get (_output->default_type()) == 0) { + if (ports.num_ports () == 0) { goto out; } @@ -246,8 +254,8 @@ Delivery::run (BufferSet& bufs, framepos_t start_frame, framepos_t end_frame, pf } /* 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. - */ + * processing pathway that wants to use this->output_buffers() for some reason. + */ // TODO delayline -- latency-compensation output_buffers().get_backend_port_addresses (ports, nframes); @@ -262,75 +270,108 @@ Delivery::run (BufferSet& bufs, framepos_t start_frame, framepos_t end_frame, pf if (tgain != _current_gain) { /* target gain has changed */ - Amp::apply_gain (bufs, nframes, _current_gain, tgain); - _current_gain = tgain; + _current_gain = Amp::apply_gain (bufs, _session.nominal_sample_rate(), nframes, _current_gain, tgain); - } else if (tgain == 0.0) { + } else if (tgain < GAIN_COEFF_SMALL) { /* we were quiet last time, and we're still supposed to be quiet. - Silence the outputs, and make sure the buffers are quiet too, - */ + Silence the outputs, and make sure the buffers are quiet too, + */ _output->silence (nframes); if (result_required) { bufs.set_count (output_buffers().count ()); - Amp::apply_simple_gain (bufs, nframes, 0.0); + Amp::apply_simple_gain (bufs, nframes, GAIN_COEFF_ZERO); } goto out; - } else if (tgain != 1.0) { + } else if (tgain != GAIN_COEFF_UNITY) { /* target gain has not changed, but is not unity */ Amp::apply_simple_gain (bufs, nframes, tgain); } + // Speed quietning + + if (fabs (_session.transport_speed()) > 1.5 && Config->get_quieten_at_speed ()) { + Amp::apply_simple_gain (bufs, nframes, speed_quietning, false); + } + + // Panning + if (_panshell && !_panshell->bypassed() && _panshell->panner()) { // Use the panner to distribute audio to output port buffers - _panshell->run (bufs, output_buffers(), start_frame, end_frame, nframes); + _panshell->run (bufs, output_buffers(), start_sample, end_sample, nframes); - // MIDI data will not have been delivered by the panner + // non-audio 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); + for (DataType::iterator t = DataType::begin(); t != DataType::end(); ++t) { + if (*t != DataType::AUDIO && bufs.count().get(*t) > 0) { + _output->copy_to_outputs (bufs, *t, nframes, Port::port_offset()); + } } } else { - // Do a 1:1 copy of data to output ports + /* Do a 1:1 copy of data to output ports + + Audio is handled separately because we use 0 for the offset, + since the port offset is only used for timestamped events + (i.e. MIDI). + */ - if (bufs.count().n_audio() > 0 && ports.count().n_audio () > 0) { + if (bufs.count().n_audio() > 0) { _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, 0); + for (DataType::iterator t = DataType::begin(); t != DataType::end(); ++t) { + if (*t != DataType::AUDIO && bufs.count().get(*t) > 0) { + _output->copy_to_outputs (bufs, *t, nframes, Port::port_offset()); + } } } - if (result_required) { - bufs.read_from (output_buffers (), nframes); - } + if (result_required) { + /* "bufs" are internal, meaning they should never reflect + split-cycle offsets. So shift events back in time from where + they were for the external buffers associated with Ports. + */ + + const BufferSet& outs (output_buffers()); + bufs.set_count (output_buffers().count ()); + + for (DataType::iterator t = DataType::begin(); t != DataType::end(); ++t) { + + uint32_t n = 0; + for (BufferSet::iterator b = bufs.begin (*t); b != bufs.end (*t); ++b) { + if (outs.count ().get (*t) <= n) { + continue; + } + b->read_from (outs.get_available (*t, n++), nframes, (*t == DataType::AUDIO ? 0 : -Port::port_offset())); + } + } + } - out: +out: _active = _pending_active; } XMLNode& -Delivery::state (bool full_state) +Delivery::state () { - XMLNode& node (IOProcessor::state (full_state)); + XMLNode& node (IOProcessor::state ()); if (_role & Main) { - node.add_property("type", "main-outs"); + node.set_property("type", "main-outs"); } else if (_role & Listen) { - node.add_property("type", "listen"); + node.set_property("type", "listen"); } else { - node.add_property("type", "delivery"); + node.set_property("type", "delivery"); } - node.add_property("role", enum_2_string(_role)); + node.set_property("role", _role); if (_panshell) { node.add_child_nocopy (_panshell->get_state ()); @@ -345,14 +386,11 @@ Delivery::state (bool full_state) int Delivery::set_state (const XMLNode& node, int version) { - const XMLProperty* prop; - if (IOProcessor::set_state (node, version)) { return -1; } - if ((prop = node.property ("role")) != 0) { - _role = Role (string_2_enum (prop->value(), _role)); + if (node.get_property ("role", _role)) { // std::cerr << this << ' ' << _name << " set role to " << enum_2_string (_role) << std::endl; } else { // std::cerr << this << ' ' << _name << " NO ROLE INFO\n"; @@ -387,7 +425,7 @@ Delivery::pan_outs () const { if (_output) { return _output->n_ports().n_audio(); - } + } return _configured_output.n_audio(); } @@ -448,14 +486,14 @@ Delivery::reset_panners () } void -Delivery::flush_buffers (framecnt_t nframes) +Delivery::flush_buffers (samplecnt_t nframes) { /* 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) { @@ -464,33 +502,33 @@ Delivery::flush_buffers (framecnt_t nframes) } void -Delivery::transport_stopped (framepos_t now) +Delivery::non_realtime_transport_stop (samplepos_t now, bool flush) { - Processor::transport_stopped (now); + Processor::non_realtime_transport_stop (now, flush); if (_panshell) { - _panshell->pannable()->transport_stopped (now); + _panshell->pannable()->non_realtime_transport_stop (now, flush); } - if (_output) { - PortSet& ports (_output->ports()); + if (_output) { + PortSet& ports (_output->ports()); - for (PortSet::iterator i = ports.begin(); i != ports.end(); ++i) { - i->transport_stopped (); - } - } + for (PortSet::iterator i = ports.begin(); i != ports.end(); ++i) { + i->transport_stopped (); + } + } } void Delivery::realtime_locate () { - if (_output) { - PortSet& ports (_output->ports()); + if (_output) { + PortSet& ports (_output->ports()); - for (PortSet::iterator i = ports.begin(); i != ports.end(); ++i) { - i->realtime_locate (); - } - } + for (PortSet::iterator i = ports.begin(); i != ports.end(); ++i) { + i->realtime_locate (); + } + } } gain_t @@ -499,7 +537,7 @@ Delivery::target_gain () /* if we've been requested to deactivate, our target gain is zero */ if (!_pending_active) { - return 0.0; + return GAIN_COEFF_ZERO; } /* if we've been told not to output because its a monitoring situation and @@ -507,41 +545,41 @@ Delivery::target_gain () */ if (_no_outs_cuz_we_no_monitor) { - return 0.0; - } - - 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; - } else { - mp = MuteMaster::PostFader; - } - break; - } + return GAIN_COEFF_ZERO; + } + + MuteMaster::MutePoint mp = MuteMaster::Main; // stupid gcc uninit warning - gain_t desired_gain = _mute_master->mute_gain_at (mp); + switch (_role) { + case Main: + mp = MuteMaster::Main; + break; + case Listen: + mp = MuteMaster::Listen; + break; + case Send: + case Insert: + case Aux: + case Foldback: + if (_pre_fader) { + mp = MuteMaster::PreFader; + } else { + mp = MuteMaster::PostFader; + } + break; + } - if (_role == Listen && _session.monitor_out() && !_session.listening()) { + gain_t desired_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. - */ + if (_role == Listen && _session.monitor_out() && !_session.listening()) { - desired_gain = 0.0; + /* 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 = GAIN_COEFF_ZERO; + } return desired_gain; }