2 Copyright (C) 2010 Paul Davis
4 This program is free software; you can redistribute it and/or modify
5 it under the terms of the GNU General Public License as published by
6 the Free Software Foundation; either version 2 of the License, or
7 (at your option) any later version.
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 GNU General Public License for more details.
14 You should have received a copy of the GNU General Public License
15 along with this program; if not, write to the Free Software
16 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
20 #include "pbd/error.h"
21 #include "pbd/xml++.h"
23 #include "ardour/amp.h"
24 #include "ardour/debug.h"
25 #include "ardour/audio_buffer.h"
26 #include "ardour/monitor_processor.h"
27 #include "ardour/session.h"
31 using namespace ARDOUR;
35 /* specialize for bool because of set_value() semantics */
38 template<> void MPControl<bool>::set_value (double v, PBD::Controllable::GroupControlDisposition gcd) {
39 bool newval = fabs (v) >= 0.5;
40 if (newval != _value) {
42 Changed (true, gcd); /* EMIT SIGNAL */
47 MonitorProcessor::MonitorProcessor (Session& s)
48 : Processor (s, X_("MonitorOut"))
50 , _monitor_active (false)
52 , _dim_all_ptr (new MPControl<bool> (false, _("monitor dim"), Controllable::Toggle))
53 , _cut_all_ptr (new MPControl<bool> (false, _("monitor cut"), Controllable::Toggle))
54 , _mono_ptr (new MPControl<bool> (false, _("monitor mono"), Controllable::Toggle))
55 , _dim_level_ptr (new MPControl<volatile gain_t>
56 /* default is -12dB, range is -20dB to 0dB */
57 (dB_to_coefficient(-12.0), _("monitor dim level"), Controllable::Flag (0),
58 dB_to_coefficient(-20.0), dB_to_coefficient (0.0)))
59 , _solo_boost_level_ptr (new MPControl<volatile gain_t>
60 /* default is 0dB, range is 0dB to +20dB */
61 (dB_to_coefficient(0.0), _("monitor solo boost level"), Controllable::Flag (0),
62 dB_to_coefficient(0.0), dB_to_coefficient(10.0)))
63 , _dim_all_control (_dim_all_ptr)
64 , _cut_all_control (_cut_all_ptr)
65 , _mono_control (_mono_ptr)
66 , _dim_level_control (_dim_level_ptr)
67 , _solo_boost_level_control (_solo_boost_level_ptr)
69 , _dim_all (*_dim_all_ptr)
70 , _cut_all (*_cut_all_ptr)
72 , _dim_level (*_dim_level_ptr)
73 , _solo_boost_level (*_solo_boost_level_ptr)
78 MonitorProcessor::~MonitorProcessor ()
80 allocate_channels (0);
82 /* special case for MPControl */
83 _dim_all_control->DropReferences (); /* EMIT SIGNAL */
84 _cut_all_control->DropReferences (); /* EMIT SIGNAL */
85 _mono_control->DropReferences (); /* EMIT SIGNAL */
86 _dim_level_control->DropReferences (); /* EMIT SIGNAL */
87 _solo_boost_level_control->DropReferences (); /* EMIT SIGNAL */
91 MonitorProcessor::allocate_channels (uint32_t size)
93 while (_channels.size() > size) {
94 if (_channels.back()->soloed) {
99 ChannelRecord* cr = _channels.back();
100 _channels.pop_back();
104 uint32_t n = _channels.size() + 1;
106 while (_channels.size() < size) {
107 _channels.push_back (new ChannelRecord (n));
112 MonitorProcessor::set_state (const XMLNode& node, int version)
114 int ret = Processor::set_state (node, version);
120 std::string type_name;
121 if (!node.get_property (X_("type"), type_name)) {
122 error << string_compose (X_("programming error: %1"), X_("MonitorProcessor XML settings have no type information"))
127 if (type_name != X_("monitor")) {
128 error << string_compose (X_("programming error: %1"), X_("MonitorProcessor given unknown XML settings"))
133 uint32_t channels = 0;
134 if (!node.get_property (X_("channels"), channels)) {
135 error << string_compose (X_("programming error: %1"), X_("MonitorProcessor XML settings are missing a channel cnt"))
140 allocate_channels (channels);
142 // need to check that these conversions are working as expected
144 if (node.get_property (X_("dim-level"), val)) {
148 if (node.get_property (X_("solo-boost-level"), val)) {
149 _solo_boost_level = val;
153 if (node.get_property (X_("cut-all"), bool_val)) {
157 if (node.get_property (X_("dim-all"), bool_val)) {
161 if (node.get_property (X_("mono"), bool_val)) {
165 for (XMLNodeList::const_iterator i = node.children().begin(); i != node.children().end(); ++i) {
167 if ((*i)->name() == X_("Channel")) {
170 if (!(*i)->get_property (X_("id"), chn)) {
171 error << string_compose (X_("programming error: %1"), X_("MonitorProcessor XML settings are missing an ID"))
176 if (chn >= _channels.size()) {
177 error << string_compose (X_("programming error: %1"), X_("MonitorProcessor XML settings has an illegal channel count"))
181 ChannelRecord& cr (*_channels[chn]);
183 bool gain_coeff_zero;
184 if ((*i)->get_property ("cut", gain_coeff_zero)) {
185 if (gain_coeff_zero) {
186 cr.cut = GAIN_COEFF_ZERO;
188 cr.cut = GAIN_COEFF_UNITY;
193 if ((*i)->get_property ("dim", dim)) {
197 bool invert_polarity;
198 if ((*i)->get_property ("invert", invert_polarity)) {
199 if (invert_polarity) {
207 if ((*i)->get_property ("solo", soloed)) {
217 for (vector<ChannelRecord*>::const_iterator x = _channels.begin(); x != _channels.end(); ++x) {
223 update_monitor_state ();
228 MonitorProcessor::state ()
230 XMLNode& node(Processor::state ());
232 /* this replaces any existing "type" property */
234 node.set_property (X_("type"), X_("monitor"));
236 node.set_property (X_ ("dim-level"), (float)_dim_level.val ());
237 node.set_property (X_ ("solo-boost-level"), (float)_solo_boost_level.val ());
239 node.set_property (X_("cut-all"), _cut_all.val());
240 node.set_property (X_("dim-all"), _dim_all.val());
241 node.set_property (X_("mono"), _mono.val());
243 node.set_property (X_("channels"), (uint32_t)_channels.size ());
248 for (vector<ChannelRecord*>::const_iterator x = _channels.begin (); x != _channels.end ();
250 chn_node = new XMLNode (X_("Channel"));
252 chn_node->set_property ("id", chn);
254 // implicitly cast these to bool
255 chn_node->set_property (X_("cut"), (*x)->cut != GAIN_COEFF_UNITY);
256 chn_node->set_property (X_("invert"), (*x)->polarity != GAIN_COEFF_UNITY);
257 chn_node->set_property (X_("dim"), (*x)->dim == true);
258 chn_node->set_property (X_("solo"), (*x)->soloed == true);
260 node.add_child_nocopy (*chn_node);
267 MonitorProcessor::run (BufferSet& bufs, samplepos_t /*start_sample*/, samplepos_t /*end_sample*/, double /*speed*/, pframes_t nframes, bool /*result_required*/)
271 gain_t dim_level_this_time = _dim_level;
272 gain_t global_cut = (_cut_all ? GAIN_COEFF_ZERO : GAIN_COEFF_UNITY);
273 gain_t global_dim = (_dim_all ? dim_level_this_time : GAIN_COEFF_UNITY);
276 if (_session.listening() || _session.soloing()) {
277 solo_boost = _solo_boost_level;
279 solo_boost = GAIN_COEFF_UNITY;
282 for (BufferSet::audio_iterator b = bufs.audio_begin(); b != bufs.audio_end(); ++b) {
284 /* don't double-scale by both track dim and global dim coefficients */
286 gain_t dim_level = (global_dim == GAIN_COEFF_UNITY ? (_channels[chn]->dim ? dim_level_this_time : GAIN_COEFF_UNITY) : GAIN_COEFF_UNITY);
288 if (_channels[chn]->soloed) {
289 target_gain = _channels[chn]->polarity * _channels[chn]->cut * dim_level * global_cut * global_dim * solo_boost;
292 target_gain = _channels[chn]->polarity * _channels[chn]->cut * dim_level * global_cut * global_dim * solo_boost;
294 target_gain = GAIN_COEFF_ZERO;
298 if (target_gain != _channels[chn]->current_gain || target_gain != GAIN_COEFF_UNITY) {
300 _channels[chn]->current_gain = Amp::apply_gain (*b, _session.nominal_sample_rate(), nframes, _channels[chn]->current_gain, target_gain);
307 DEBUG_TRACE (DEBUG::Monitor, "mono-izing\n");
309 /* chn is now the number of channels, use as a scaling factor when mixing
311 gain_t scale = 1.f / (float)chn;
312 BufferSet::audio_iterator b = bufs.audio_begin();
313 AudioBuffer& ab (*b);
314 Sample* buf = ab.data();
316 /* scale the first channel */
318 for (pframes_t n = 0; n < nframes; ++n) {
322 /* add every other channel into the first channel's buffer */
325 for (; b != bufs.audio_end(); ++b) {
326 AudioBuffer& ob (*b);
327 Sample* obuf = ob.data ();
328 for (pframes_t n = 0; n < nframes; ++n) {
329 buf[n] += obuf[n] * scale;
333 /* copy the first channel to every other channel's buffer */
335 b = bufs.audio_begin();
337 for (; b != bufs.audio_end(); ++b) {
338 AudioBuffer& ob (*b);
339 Sample* obuf = ob.data ();
340 memcpy (obuf, buf, sizeof (Sample) * nframes);
346 MonitorProcessor::configure_io (ChanCount in, ChanCount out)
348 allocate_channels (in.n_audio());
349 return Processor::configure_io (in, out);
353 MonitorProcessor::can_support_io_configuration (const ChanCount& in, ChanCount& out)
360 MonitorProcessor::set_polarity (uint32_t chn, bool invert)
363 _channels[chn]->polarity = -1.0f;
365 _channels[chn]->polarity = 1.0f;
367 update_monitor_state ();
371 MonitorProcessor::set_dim (uint32_t chn, bool yn)
373 _channels[chn]->dim = yn;
374 update_monitor_state ();
378 MonitorProcessor::set_cut (uint32_t chn, bool yn)
381 _channels[chn]->cut = GAIN_COEFF_ZERO;
383 _channels[chn]->cut = GAIN_COEFF_UNITY;
385 update_monitor_state ();
389 MonitorProcessor::set_solo (uint32_t chn, bool solo)
391 if (solo != _channels[chn]->soloed) {
392 _channels[chn]->soloed = solo;
402 update_monitor_state ();
406 MonitorProcessor::set_mono (bool yn)
409 update_monitor_state ();
413 MonitorProcessor::set_cut_all (bool yn)
416 update_monitor_state ();
420 MonitorProcessor::set_dim_all (bool yn)
423 update_monitor_state ();
427 MonitorProcessor::display_to_user () const
433 MonitorProcessor::soloed (uint32_t chn) const
435 return _channels[chn]->soloed;
439 MonitorProcessor::inverted (uint32_t chn) const
441 return _channels[chn]->polarity < 0.0f;
445 MonitorProcessor::cut (uint32_t chn) const
447 return _channels[chn]->cut == GAIN_COEFF_ZERO;
451 MonitorProcessor::dimmed (uint32_t chn) const
453 return _channels[chn]->dim;
457 MonitorProcessor::mono () const
463 MonitorProcessor::dim_all () const
469 MonitorProcessor::cut_all () const
475 MonitorProcessor::update_monitor_state ()
479 if (_cut_all || _dim_all || _mono) {
483 const uint32_t nchans = _channels.size();
484 for (uint32_t i = 0; i < nchans && !en; ++i) {
485 if (cut (i) || dimmed (i) || soloed (i) || inverted (i)) {
491 if (_monitor_active != en) {
492 _monitor_active = en;
493 _session.MonitorChanged();
497 boost::shared_ptr<Controllable>
498 MonitorProcessor::channel_cut_control (uint32_t chn) const
500 if (chn < _channels.size()) {
501 return _channels[chn]->cut_control;
503 return boost::shared_ptr<Controllable>();
506 boost::shared_ptr<Controllable>
507 MonitorProcessor::channel_dim_control (uint32_t chn) const
509 if (chn < _channels.size()) {
510 return _channels[chn]->dim_control;
512 return boost::shared_ptr<Controllable>();
515 boost::shared_ptr<Controllable>
516 MonitorProcessor::channel_polarity_control (uint32_t chn) const
518 if (chn < _channels.size()) {
519 return _channels[chn]->polarity_control;
521 return boost::shared_ptr<Controllable>();
524 boost::shared_ptr<Controllable>
525 MonitorProcessor::channel_solo_control (uint32_t chn) const
527 if (chn < _channels.size()) {
528 return _channels[chn]->soloed_control;
530 return boost::shared_ptr<Controllable>();
533 MonitorProcessor::ChannelRecord::ChannelRecord (uint32_t chn)
534 : current_gain (GAIN_COEFF_UNITY)
535 , cut_ptr (new MPControl<gain_t> (1.0, string_compose (_("cut control %1"), chn), PBD::Controllable::GainLike))
536 , dim_ptr (new MPControl<bool> (false, string_compose (_("dim control"), chn), PBD::Controllable::Toggle))
537 , polarity_ptr (new MPControl<gain_t> (1.0, string_compose (_("polarity control"), chn), PBD::Controllable::Toggle, -1, 1))
538 , soloed_ptr (new MPControl<bool> (false, string_compose (_("solo control"), chn), PBD::Controllable::Toggle))
540 , cut_control (cut_ptr)
541 , dim_control (dim_ptr)
542 , polarity_control (polarity_ptr)
543 , soloed_control (soloed_ptr)
547 , polarity (*polarity_ptr)
548 , soloed (*soloed_ptr)
552 MonitorProcessor::ChannelRecord::~ChannelRecord ()
554 /* special case for MPControl */
555 cut_control->DropReferences(); /* EMIT SIGNAL */
556 dim_control->DropReferences(); /* EMIT SIGNAL */
557 polarity_control->DropReferences(); /* EMIT SIGNAL */
558 soloed_control->DropReferences(); /* EMIT SIGNAL */