enough with umpteen "i18n.h" files. Consolidate on pbd/i18n.h
[ardour.git] / libs / panners / 2in2out / panner_2in2out.cc
index 2c2856361c8014708609144880ac416ff45fd820..9105eff7134800b8294817bac5026b6f58abfcb3 100644 (file)
 
 #include <cmath>
 #include <cerrno>
-#include <fstream>
 #include <cstdlib>
 #include <string>
 #include <cstdio>
 #include <locale.h>
 #include <unistd.h>
 #include <float.h>
-#include <iomanip>
 
 #include <glibmm.h>
 
 #include "ardour/runtime_functions.h"
 #include "ardour/session.h"
 #include "ardour/utils.h"
+#include "ardour/mix.h"
 
 #include "panner_2in2out.h"
 
-#include "i18n.h"
+#include "pbd/i18n.h"
 
 #include "pbd/mathfix.h"
 
@@ -62,30 +61,40 @@ using namespace PBD;
 
 static PanPluginDescriptor _descriptor = {
         "Equal Power Stereo",
+        "http://ardour.org/plugin/panner_2in2out",
+        "http://ardour.org/plugin/panner_2in2out#ui",
         2, 2,
+        10000,
         Panner2in2out::factory
 };
 
-extern "C" { PanPluginDescriptor* panner_descriptor () { return &_descriptor; } }
+extern "C" ARDOURPANNER_API PanPluginDescriptor* panner_descriptor () { return &_descriptor; }
 
 Panner2in2out::Panner2in2out (boost::shared_ptr<Pannable> p)
        : Panner (p)
 {
         if (!_pannable->has_state()) {
-                _pannable->pan_azimuth_control->set_value (0.5);
-                _pannable->pan_width_control->set_value (1.0);
-        } 
-        
+               _pannable->pan_azimuth_control->set_value (0.5, Controllable::NoGroup);
+               _pannable->pan_width_control->set_value (1.0, Controllable::NoGroup);
+        }
+
+        double const w = width();
+        double const wrange = min (position(), (1 - position())) * 2;
+        if (fabs(w) > wrange) {
+                set_width(w > 0 ? wrange : -wrange);
+        }
+
+
         update ();
-        
+
         /* LEFT SIGNAL */
         left_interp[0] = left[0] = desired_left[0];
-        right_interp[0] = right[0] = desired_right[0]; 
-        
+        right_interp[0] = right[0] = desired_right[0];
+
         /* RIGHT SIGNAL */
         left_interp[1] = left[1] = desired_left[1];
         right_interp[1] = right[1] = desired_right[1];
-        
+
         _pannable->pan_azimuth_control->Changed.connect_same_thread (*this, boost::bind (&Panner2in2out::update, this));
         _pannable->pan_width_control->Changed.connect_same_thread (*this, boost::bind (&Panner2in2out::update, this));
 }
@@ -94,13 +103,13 @@ Panner2in2out::~Panner2in2out ()
 {
 }
 
-double 
+double
 Panner2in2out::position () const
 {
         return _pannable->pan_azimuth_control->get_value();
 }
 
-double 
+double
 Panner2in2out::width () const
 {
         return _pannable->pan_width_control->get_value();
@@ -110,7 +119,7 @@ void
 Panner2in2out::set_position (double p)
 {
         if (clamp_position (p)) {
-                _pannable->pan_azimuth_control->set_value (p);
+               _pannable->pan_azimuth_control->set_value (p, Controllable::NoGroup);
         }
 }
 
@@ -118,53 +127,72 @@ void
 Panner2in2out::set_width (double p)
 {
         if (clamp_width (p)) {
-                _pannable->pan_width_control->set_value (p);
+               _pannable->pan_width_control->set_value (p, Controllable::NoGroup);
         }
 }
 
+void
+Panner2in2out::thaw ()
+{
+       Panner::thaw ();
+       if (_frozen == 0) {
+               update ();
+       }
+}
+
 void
 Panner2in2out::update ()
 {
+       if (_frozen) {
+               return;
+       }
+
         /* it would be very nice to split this out into a virtual function
            that can be accessed from BaseStereoPanner and used in do_distribute_automated().
-           
+
            but the place where its used in do_distribute_automated() is a tight inner loop,
            and making "nframes" virtual function calls to compute values is an absurd
            overhead.
         */
-        
+
         /* x == 0 => hard left = 180.0 degrees
            x == 1 => hard right = 0.0 degrees
         */
-        
+
         float pos[2];
-        const double width = _pannable->pan_width_control->get_value();
-        const double direction_as_lr_fract = _pannable->pan_azimuth_control->get_value();
+        double width = this->width ();
+        const double direction_as_lr_fract = position ();
+
+        double const wrange = min (position(), (1 - position())) * 2;
+        if (fabs(width) > wrange) {
+                width = (width  > 0 ? wrange : -wrange);
+        }
 
         if (width < 0.0) {
+                width = -width;
                 pos[0] = direction_as_lr_fract + (width/2.0); // left signal lr_fract
                 pos[1] = direction_as_lr_fract - (width/2.0); // right signal lr_fract
         } else {
                 pos[1] = direction_as_lr_fract + (width/2.0); // right signal lr_fract
                 pos[0] = direction_as_lr_fract - (width/2.0); // left signal lr_fract
         }
-        
+
         /* compute target gain coefficients for both input signals */
-        
+
         float const pan_law_attenuation = -3.0f;
         float const scale = 2.0f - 4.0f * powf (10.0f,pan_law_attenuation/20.0f);
         float panR;
         float panL;
-        
+
         /* left signal */
-        
+
         panR = pos[0];
         panL = 1 - panR;
         desired_left[0] = panL * (scale * panL + 1.0f - scale);
         desired_right[0] = panR * (scale * panR + 1.0f - scale);
-        
+
         /* right signal */
-        
+
         panR = pos[1];
         panL = 1 - panR;
         desired_left[1] = panL * (scale * panL + 1.0f - scale);
@@ -174,17 +202,30 @@ Panner2in2out::update ()
 bool
 Panner2in2out::clamp_position (double& p)
 {
-        double w = _pannable->pan_width_control->get_value();
+        double w = width ();
         return clamp_stereo_pan (p, w);
 }
 
 bool
 Panner2in2out::clamp_width (double& w)
 {
-        double p = _pannable->pan_azimuth_control->get_value();
+        double p = position ();
         return clamp_stereo_pan (p, w);
 }
 
+pair<double, double>
+Panner2in2out::position_range () const
+{
+       return make_pair (0.5 - (1 - width()) / 2, 0.5 + (1 - width()) / 2);
+}
+
+pair<double, double>
+Panner2in2out::width_range () const
+{
+       double const w = min (position(), (1 - position())) * 2;
+       return make_pair (-w, w);
+}
+
 bool
 Panner2in2out::clamp_stereo_pan (double& direction_as_lr_fract, double& width)
 {
@@ -202,20 +243,20 @@ Panner2in2out::clamp_stereo_pan (double& direction_as_lr_fract, double& width)
         }
 
         /* if the new left position is less than or equal to zero (hard left) and the left panner
-           is already there, we're not moving the left signal. 
+           is already there, we're not moving the left signal.
         */
-        
+
         if (l_pos < 0.0) {
                 return false;
         }
 
         /* if the new right position is less than or equal to 1.0 (hard right) and the right panner
-           is already there, we're not moving the right signal. 
+           is already there, we're not moving the right signal.
         */
-        
+
         if (r_pos > 1.0) {
                 return false;
-                
+
         }
 
         return true;
@@ -231,7 +272,7 @@ Panner2in2out::distribute_one (AudioBuffer& srcbuf, BufferSet& obufs, gain_t gai
        pan_t pan;
 
        Sample* const src = srcbuf.data();
-        
+
        /* LEFT OUTPUT */
 
        dst = obufs.get_audio(0).data();
@@ -269,6 +310,7 @@ Panner2in2out::distribute_one (AudioBuffer& srcbuf, BufferSet& obufs, gain_t gai
 
                                /* pan is 1 but also not 0, so we must do it "properly" */
 
+                               //obufs.get_audio(1).read_from (srcbuf, nframes);
                                mix_buffers_with_gain(dst,src,nframes,pan);
 
                                /* mark that we wrote into the buffer */
@@ -282,7 +324,7 @@ Panner2in2out::distribute_one (AudioBuffer& srcbuf, BufferSet& obufs, gain_t gai
                        /* pan is 1 so we can just copy the input samples straight in */
 
                        mix_buffers_no_gain(dst,src,nframes);
-                        
+
                        /* XXX it would be nice to mark that we wrote into the buffer */
                }
        }
@@ -325,8 +367,9 @@ Panner2in2out::distribute_one (AudioBuffer& srcbuf, BufferSet& obufs, gain_t gai
                        if (pan != 0.0f) {
 
                                /* pan is not 1 but also not 0, so we must do it "properly" */
-                               
+
                                mix_buffers_with_gain(dst,src,nframes,pan);
+                               // obufs.get_audio(1).read_from (srcbuf, nframes);
 
                                /* XXX it would be nice to mark the buffer as written to */
                        }
@@ -334,7 +377,7 @@ Panner2in2out::distribute_one (AudioBuffer& srcbuf, BufferSet& obufs, gain_t gai
                } else {
 
                        /* pan is 1 so we can just copy the input samples straight in */
-                       
+
                        mix_buffers_no_gain(dst,src,nframes);
 
                        /* XXX it would be nice to mark the buffer as written to */
@@ -380,7 +423,7 @@ Panner2in2out::distribute_one_automated (AudioBuffer& srcbuf, BufferSet& obufs,
 
                 float panR;
 
-                if (which == 0) { 
+                if (which == 0) {
                         // panning left signal
                         panR = position[n] - (width[n]/2.0f); // center - width/2
                 } else {
@@ -388,15 +431,17 @@ Panner2in2out::distribute_one_automated (AudioBuffer& srcbuf, BufferSet& obufs,
                         panR = position[n] + (width[n]/2.0f); // center - width/2
                 }
 
+                panR = max(0.f, min(1.f, panR));
+
                 const float panL = 1 - panR;
 
                 /* note that are overwriting buffers, but its OK
                    because we're finished with their old contents
                    (position/width automation data) and are
-                   replacing it with panning/gain coefficients 
+                   replacing it with panning/gain coefficients
                    that we need to actually process the data.
                 */
-                
+
                 buffers[0][n] = panL * (scale * panL + 1.0f - scale);
                 buffers[1][n] = panR * (scale * panR + 1.0f - scale);
         }
@@ -425,34 +470,22 @@ Panner2in2out::distribute_one_automated (AudioBuffer& srcbuf, BufferSet& obufs,
 }
 
 Panner*
-Panner2in2out::factory (boost::shared_ptr<Pannable> p, Speakers& /* ignored */)
+Panner2in2out::factory (boost::shared_ptr<Pannable> p, boost::shared_ptr<Speakers> /* ignored */)
 {
        return new Panner2in2out (p);
 }
 
 XMLNode&
-Panner2in2out::get_state (void)
-{
-       return state (true);
-}
-
-XMLNode&
-Panner2in2out::state (bool /*full_state*/)
+Panner2in2out::get_state ()
 {
        XMLNode& root (Panner::get_state ());
+       root.add_property (X_("uri"), _descriptor.panner_uri);
+       /* this is needed to allow new sessions to load with old Ardour: */
        root.add_property (X_("type"), _descriptor.name);
        return root;
 }
 
-int
-Panner2in2out::set_state (const XMLNode& node, int version)
-{
-       LocaleGuard lg (X_("POSIX"));
-       Panner::set_state (node, version);
-       return 0;
-}
-
-std::set<Evoral::Parameter> 
+std::set<Evoral::Parameter>
 Panner2in2out::what_can_be_automated() const
 {
         set<Evoral::Parameter> s;
@@ -473,3 +506,42 @@ Panner2in2out::describe_parameter (Evoral::Parameter p)
                 return _pannable->describe_parameter (p);
         }
 }
+
+string
+Panner2in2out::value_as_string (boost::shared_ptr<const AutomationControl> ac) const
+{
+        /* DO NOT USE LocaleGuard HERE */
+        double val = ac->get_value();
+
+        switch (ac->parameter().type()) {
+        case PanAzimuthAutomation:
+                /* We show the position of the center of the image relative to the left & right.
+                   This is expressed as a pair of percentage values that ranges from (100,0)
+                   (hard left) through (50,50) (hard center) to (0,100) (hard right).
+
+                   This is pretty wierd, but its the way audio engineers expect it. Just remember that
+                   the center of the USA isn't Kansas, its (50LA, 50NY) and it will all make sense.
+
+                  This is designed to be as narrow as possible. Dedicated
+                  panner GUIs can do their own version of this if they need
+                  something less compact.
+                */
+
+                return string_compose (_("L%1R%2"), (int) rint (100.0 * (1.0 - val)),
+                                       (int) rint (100.0 * val));
+
+        case PanWidthAutomation:
+                return string_compose (_("Width: %1%%"), (int) floor (100.0 * val));
+
+        default:
+                return _("unused");
+        }
+}
+
+void
+Panner2in2out::reset ()
+{
+       set_position (0.5);
+       set_width (1);
+       update ();
+}