Don't add standard processors twice to routes from 2.X sessions. Fixes #3434.
[ardour.git] / libs / ardour / monitor_processor.cc
index b7ce225f75721d036ff9eeee9030448c3da421a5..8ea5e70aab6088df661a337d058bd4eed97abefe 100644 (file)
@@ -1,7 +1,10 @@
+#include "pbd/convert.h"
+#include "pbd/error.h"
 #include "pbd/xml++.h"
 
 #include "ardour/amp.h"
 #include "ardour/dB.h"
+#include "ardour/debug.h"
 #include "ardour/audio_buffer.h"
 #include "ardour/monitor_processor.h"
 #include "ardour/session.h"
 #include "i18n.h"
 
 using namespace ARDOUR;
+using namespace PBD;
 using namespace std;
 
+/* specialize for bool because of set_value() semantics */
+
+namespace ARDOUR {
+        template<> void MPControl<bool>::set_value (double v) {
+                bool newval = fabs (v) >= 0.5;
+                if (newval != _value) {
+                        _value = newval;
+                        Changed(); /* EMIT SIGNAL */
+                }
+        }
+}
+
 MonitorProcessor::MonitorProcessor (Session& s)
         : Processor (s, X_("MonitorOut"))
+        , solo_cnt (0)
+
+        , _dim_all_ptr (new MPControl<bool> (false, _("monitor dim"), Controllable::Toggle))
+        , _cut_all_ptr (new MPControl<bool> (false, _("monitor cut"), Controllable::Toggle))
+        , _mono_ptr (new MPControl<bool> (false, _("monitor mono"), Controllable::Toggle))
+        , _dim_level_ptr (new MPControl<volatile gain_t> 
+                          (0.2, _("monitor mono"), Controllable::Flag (0), 0.0f, 1.0f))
+        , _solo_boost_level_ptr (new MPControl<volatile gain_t> 
+                                 (1.0, _("monitor mono"), Controllable::Flag (0), 1.0f, 3.0f))
+          
+        , _dim_all_control (_dim_all_ptr)
+        , _cut_all_control (_cut_all_ptr)
+        , _mono_control (_mono_ptr)
+        , _dim_level_control (_dim_level_ptr)
+        , _solo_boost_level_control (_solo_boost_level_ptr)
+
+        , _dim_all (*_dim_all_ptr)
+        , _cut_all (*_cut_all_ptr)
+        , _mono (*_mono_ptr)
+        , _dim_level (*_dim_level_ptr)
+        , _solo_boost_level (*_solo_boost_level_ptr)
+
 {
-        solo_cnt = 0;
-        _cut_all = false;
-        _dim_all = false;
-        _dim_level = 0.2;
-        _solo_boost_level = 1.0;
 }
 
-MonitorProcessor::MonitorProcessor (Session& s, const XMLNode& node)
-        : Processor (s, node)
+MonitorProcessor::~MonitorProcessor ()
 {
-        set_state (node, Stateful::loading_state_version);
+        allocate_channels (0);
+}
+
+void
+MonitorProcessor::allocate_channels (uint32_t size)
+{
+        while (_channels.size() > size) {
+                if (_channels.back()->soloed) {
+                        if (solo_cnt > 0) {
+                                --solo_cnt;
+                        }
+                }
+                ChannelRecord* cr = _channels.back();
+                _channels.pop_back();
+                delete cr;
+        }
+
+        uint32_t n = _channels.size() + 1;
+
+        while (_channels.size() < size) {
+                _channels.push_back (new ChannelRecord (n));
+        }
 }
 
 int
 MonitorProcessor::set_state (const XMLNode& node, int version)
 {
-        return Processor::set_state (node, version);
+        int ret = Processor::set_state (node, version);
+
+        if (ret != 0) {
+                return ret;
+        }
+
+        const XMLProperty* prop;
+
+        if ((prop = node.property (X_("type"))) == 0) {
+                error << string_compose (X_("programming error: %1"), X_("MonitorProcessor XML settings have no type information"))
+                      << endmsg;
+                return -1;
+        }
+
+        if (prop->value() != X_("monitor")) {
+                error << string_compose (X_("programming error: %1"), X_("MonitorProcessor given unknown XML settings"))
+                      << endmsg;
+                return -1;
+        }
+
+        if ((prop = node.property (X_("channels"))) == 0) {
+                error << string_compose (X_("programming error: %1"), X_("MonitorProcessor XML settings are missing a channel cnt"))
+                      << endmsg;
+                return -1;
+        }
+        
+        allocate_channels (atoi (prop->value()));
+
+        if ((prop = node.property (X_("dim-level"))) != 0) {
+                gain_t val = atof (prop->value());
+                _dim_level = val;
+        }
+
+        if ((prop = node.property (X_("solo-boost-level"))) != 0) {
+                gain_t val = atof (prop->value());
+                _solo_boost_level = val;
+        }
+
+        if ((prop = node.property (X_("cut-all"))) != 0) {
+                bool val = string_is_affirmative (prop->value());
+                _cut_all = val;
+        }
+        if ((prop = node.property (X_("dim-all"))) != 0) {
+                bool val = string_is_affirmative (prop->value());
+                _dim_all = val;
+        }
+        if ((prop = node.property (X_("mono"))) != 0) {
+                bool val = string_is_affirmative (prop->value());
+                _mono = val;
+        }
+
+        for (XMLNodeList::const_iterator i = node.children().begin(); i != node.children().end(); ++i) {
+
+                if ((*i)->name() == X_("Channel")) {
+                        if ((prop = (*i)->property (X_("id"))) == 0) {
+                                error << string_compose (X_("programming error: %1"), X_("MonitorProcessor XML settings are missing an ID"))
+                                      << endmsg;
+                                return -1;
+                        }
+
+                        uint32_t chn;
+
+                        if (sscanf (prop->value().c_str(), "%u", &chn) != 1) {
+                                error << string_compose (X_("programming error: %1"), X_("MonitorProcessor XML settings has an unreadable channel ID"))
+                                      << endmsg;
+                                return -1;
+                        }
+                        
+                        if (chn >= _channels.size()) {
+                                error << string_compose (X_("programming error: %1"), X_("MonitorProcessor XML settings has an illegal channel count"))
+                                      << endmsg;
+                                return -1;
+                        }
+                        ChannelRecord& cr (*_channels[chn]);
+
+                        if ((prop = (*i)->property ("cut")) != 0) {
+                                if (string_is_affirmative (prop->value())){
+                                        cr.cut = 0.0f;
+                                } else {
+                                        cr.cut = 1.0f;
+                                }
+                        }
+
+                        if ((prop = (*i)->property ("dim")) != 0) {
+                                bool val = string_is_affirmative (prop->value());
+                                cr.dim = val;
+                        }
+
+                        if ((prop = (*i)->property ("invert")) != 0) {
+                                if (string_is_affirmative (prop->value())) {
+                                        cr.polarity = -1.0f;
+                                } else {
+                                        cr.polarity = 1.0f;
+                                }
+                        }
+
+                        if ((prop = (*i)->property ("solo")) != 0) {
+                                bool val = string_is_affirmative (prop->value());
+                                cr.soloed = val;
+                        }
+                }
+        }
+        
+        /* reset solo cnt */
+
+        solo_cnt = 0;
+
+        for (vector<ChannelRecord*>::const_iterator x = _channels.begin(); x != _channels.end(); ++x) {
+                if ((*x)->soloed) {
+                        solo_cnt++;
+                }
+        }
+        
+        return 0;
 }
 
 XMLNode&
 MonitorProcessor::state (bool full)
 {
         XMLNode& node (Processor::state (full));
+        char buf[64];
 
        /* this replaces any existing "type" property */
 
        node.add_property (X_("type"), X_("monitor"));
+        
+        snprintf (buf, sizeof(buf), "%.12g", _dim_level.val());
+        node.add_property (X_("dim-level"), buf);
+
+        snprintf (buf, sizeof(buf), "%.12g", _solo_boost_level.val());
+        node.add_property (X_("solo-boost-level"), buf);
+
+        node.add_property (X_("cut-all"), (_cut_all ? "yes" : "no"));
+        node.add_property (X_("dim-all"), (_dim_all ? "yes" : "no"));
+        node.add_property (X_("mono"), (_mono ? "yes" : "no"));
+        
+        uint32_t limit = _channels.size();
+
+        snprintf (buf, sizeof (buf), "%u", limit);
+        node.add_property (X_("channels"), buf);
+
+        XMLNode* chn_node;
+        uint32_t chn = 0;
+
+        for (vector<ChannelRecord*>::const_iterator x = _channels.begin(); x != _channels.end(); ++x, ++chn) {
+                chn_node = new XMLNode (X_("Channel"));
+
+                snprintf (buf, sizeof (buf), "%u", chn);
+                chn_node->add_property ("id", buf);
+                
+                chn_node->add_property (X_("cut"), (*x)->cut == 1.0f ? "no" : "yes");
+                chn_node->add_property (X_("invert"), (*x)->polarity == 1.0f ? "no" : "yes");
+                chn_node->add_property (X_("dim"), (*x)->dim ? "yes" : "no");
+                chn_node->add_property (X_("solo"), (*x)->soloed ? "yes" : "no");
+                
+                node.add_child_nocopy (*chn_node);
+        }
 
         return node;
 }
 
 void
-MonitorProcessor::run (BufferSet& bufs, sframes_t /*start_frame*/, sframes_t /*end_frame*/, nframes_t nframes, bool /*result_required*/)
+MonitorProcessor::run (BufferSet& bufs, framepos_t /*start_frame*/, framepos_t /*end_frame*/, nframes_t nframes, bool /*result_required*/)
 {
         uint32_t chn = 0;
         gain_t target_gain;
@@ -65,28 +264,46 @@ MonitorProcessor::run (BufferSet& bufs, sframes_t /*start_frame*/, sframes_t /*e
 
                 /* don't double-scale by both track dim and global dim coefficients */
 
-                gain_t dim_level = (global_dim == 1.0 ? (_dim[chn] ? dim_level_this_time : 1.0) : 1.0);
+                gain_t dim_level = (global_dim == 1.0 ? (_channels[chn]->dim ? dim_level_this_time : 1.0) : 1.0);
 
-                if (_soloed[chn]) {
-                        target_gain = _polarity[chn] * _cut[chn] * dim_level * global_cut * global_dim * solo_boost;
+                if (_channels[chn]->soloed) {
+                        target_gain = _channels[chn]->polarity * _channels[chn]->cut * dim_level * global_cut * global_dim * solo_boost;
                 } else {
                         if (solo_cnt == 0) {
-                                target_gain = _polarity[chn] * _cut[chn] * dim_level * global_cut * global_dim * solo_boost;
+                                target_gain = _channels[chn]->polarity * _channels[chn]->cut * dim_level * global_cut * global_dim * solo_boost;
                         } else {
                                 target_gain = 0.0;
                         }
                 }
 
-                if (target_gain != current_gain[chn] || target_gain != 1.0f) {
-
-                        Amp::apply_gain (*b, nframes, current_gain[chn], target_gain);
-                        current_gain[chn] = target_gain;
+                DEBUG_TRACE (DEBUG::Monitor, 
+                             string_compose("channel %1 SB %12 sb %2 gc %3 gd %4 cd %5 dl %6 cp %7 cc %8 cs %9 sc %10 TG %11\n", 
+                                            chn, 
+                                            solo_boost,
+                                            global_cut,
+                                            global_dim,
+                                            _channels[chn]->dim,
+                                            dim_level,
+                                            _channels[chn]->polarity,
+                                            _channels[chn]->cut,
+                                            _channels[chn]->soloed,
+                                            solo_cnt,
+                                            target_gain, 
+                                            (float) _solo_boost_level.val()
+                                     ));
+                
+                if (target_gain != _channels[chn]->current_gain || target_gain != 1.0f) {
+
+                        Amp::apply_gain (*b, nframes, _channels[chn]->current_gain, target_gain);
+                        _channels[chn]->current_gain = target_gain;
                 }
 
                 ++chn;
         }
 
         if (_mono) {
+                DEBUG_TRACE (DEBUG::Monitor, "mono-izing\n");
+
                 /* chn is now the number of channels, use as a scaling factor when mixing
                  */
                 gain_t scale = 1.0/chn;
@@ -126,31 +343,7 @@ MonitorProcessor::run (BufferSet& bufs, sframes_t /*start_frame*/, sframes_t /*e
 bool
 MonitorProcessor::configure_io (ChanCount in, ChanCount out)
 {
-        uint32_t needed = in.n_audio();
-
-        while (current_gain.size() > needed) {
-                current_gain.pop_back ();
-                _dim.pop_back ();
-                _cut.pop_back ();
-                _polarity.pop_back ();
-
-                if (_soloed.back()) {
-                        if (solo_cnt > 0) {
-                                --solo_cnt;
-                        }
-                }
-
-                _soloed.pop_back ();
-        }
-        
-        while (current_gain.size() < needed) {
-                current_gain.push_back (1.0);
-                _dim.push_back (false);
-                _cut.push_back (1.0);
-                _polarity.push_back (1.0);
-                _soloed.push_back (false);
-        }
-
+        allocate_channels (in.n_audio());
         return Processor::configure_io (in, out);
 }
 
@@ -164,38 +357,40 @@ void
 MonitorProcessor::set_polarity (uint32_t chn, bool invert)
 {
         if (invert) {
-                _polarity[chn] = -1.0f;
+                _channels[chn]->polarity = -1.0f;
         } else {
-                _polarity[chn] = 1.0f;
+                _channels[chn]->polarity = 1.0f;
         }
 }       
 
 void
 MonitorProcessor::set_dim (uint32_t chn, bool yn)
 {
-        _dim[chn] = yn;
+        _channels[chn]->dim = yn;
 }
 
 void
 MonitorProcessor::set_cut (uint32_t chn, bool yn)
 {
         if (yn) {
-                _cut[chn] = 0.0f;
+                _channels[chn]->cut = 0.0f;
         } else {
-                _cut[chn] = 1.0f;
+                _channels[chn]->cut = 1.0f;
         }
 }
 
 void
 MonitorProcessor::set_solo (uint32_t chn, bool solo)
 {
-        _soloed[chn] = solo;
-
-        if (solo) {
-                solo_cnt++;
-        } else {
-                if (solo_cnt > 0) {
-                        solo_cnt--;
+        if (solo != _channels[chn]->soloed) {
+                _channels[chn]->soloed = solo;
+                
+                if (solo) {
+                        solo_cnt++;
+                } else {
+                        if (solo_cnt > 0) {
+                                solo_cnt--;
+                        }
                 }
         }
 }
@@ -239,27 +434,27 @@ MonitorProcessor::set_solo_boost_level (gain_t val)
 bool 
 MonitorProcessor::soloed (uint32_t chn) const
 {
-        return _soloed[chn];
+        return _channels[chn]->soloed;
 }
 
 
 bool 
 MonitorProcessor::inverted (uint32_t chn) const
 {
-        return _polarity[chn] < 0.0f;
+        return _channels[chn]->polarity < 0.0f;
 }
 
 
 bool 
 MonitorProcessor::cut (uint32_t chn) const
 {
-        return _cut[chn] == 0.0f;
+        return _channels[chn]->cut == 0.0f;
 }
 
 bool 
 MonitorProcessor::dimmed (uint32_t chn) const
 {
-        return _dim[chn];
+        return _channels[chn]->dim;
 }
 
 bool
@@ -267,3 +462,71 @@ MonitorProcessor::mono () const
 {
         return _mono;
 }
+
+bool
+MonitorProcessor::dim_all () const
+{
+        return _dim_all;
+}
+
+bool
+MonitorProcessor::cut_all () const
+{
+        return _cut_all;
+}
+
+boost::shared_ptr<Controllable>
+MonitorProcessor::channel_cut_control (uint32_t chn) const
+{
+        if (chn < _channels.size()) {
+                return _channels[chn]->cut_control;
+        }
+        return boost::shared_ptr<Controllable>();
+}
+
+boost::shared_ptr<Controllable>
+MonitorProcessor::channel_dim_control (uint32_t chn) const
+{
+        if (chn < _channels.size()) {
+                return _channels[chn]->dim_control;
+        }
+        return boost::shared_ptr<Controllable>();
+}
+
+boost::shared_ptr<Controllable>
+MonitorProcessor::channel_polarity_control (uint32_t chn) const
+{
+        if (chn < _channels.size()) {
+                return _channels[chn]->polarity_control;
+        }
+        return boost::shared_ptr<Controllable>();
+}
+
+boost::shared_ptr<Controllable>
+MonitorProcessor::channel_solo_control (uint32_t chn) const
+{
+        if (chn < _channels.size()) {
+                return _channels[chn]->soloed_control;
+        }
+        return boost::shared_ptr<Controllable>();
+}
+
+MonitorProcessor::ChannelRecord::ChannelRecord (uint32_t chn)
+       : current_gain (1.0)
+       , cut_ptr (new MPControl<gain_t> (1.0, string_compose (_("cut control %1"), chn), PBD::Controllable::GainLike))
+       , dim_ptr (new MPControl<bool> (false, string_compose (_("dim control"), chn), PBD::Controllable::Toggle))
+       , polarity_ptr (new MPControl<gain_t> (1.0, string_compose (_("polarity control"), chn), PBD::Controllable::Toggle))
+       , soloed_ptr (new MPControl<bool> (false, string_compose (_("solo control"), chn), PBD::Controllable::Toggle))
+         
+       , cut_control (cut_ptr)
+       , dim_control (dim_ptr)
+       , polarity_control (polarity_ptr)
+       , soloed_control (soloed_ptr)
+         
+       , cut (*cut_ptr)
+       , dim (*dim_ptr)
+       , polarity (*polarity_ptr)
+       , soloed (*soloed_ptr)
+{
+
+}