Add mono switch to mixer strips (mantis 1068)
authorCarl Hetherington <carl@carlh.net>
Mon, 19 Oct 2009 14:46:15 +0000 (14:46 +0000)
committerCarl Hetherington <carl@carlh.net>
Mon, 19 Oct 2009 14:46:15 +0000 (14:46 +0000)
git-svn-id: svn://localhost/ardour2/branches/3.0@5797 d708f5d6-7413-0410-9779-e7cbd77b26cf

gtk2_ardour/ardour3_ui_dark.rc.in
gtk2_ardour/ardour3_ui_dark_sae.rc.in
gtk2_ardour/ardour3_ui_light.rc.in
gtk2_ardour/ardour3_ui_light_sae.rc.in
gtk2_ardour/mixer_strip.cc
gtk2_ardour/mixer_strip.h
gtk2_ardour/panner_ui.cc
gtk2_ardour/panner_ui.h
libs/ardour/ardour/panner.h
libs/ardour/panner.cc

index ec4d61dfd32dddca20a9c27fc318704e2743cfa3..7fd11df4f3fd654af379b2d0014a0bb7f78998b5 100644 (file)
@@ -1357,6 +1357,7 @@ widget "*MixerGroupButton" style:highest "very_small_button"
 widget "*MixerGroupButtonLabel" style:highest "very_small_button"
 widget "*MixerCommentButton" style:highest "very_small_button"
 widget "*MixerCommentButton*" style:highest "very_small_button"
+widget "*MixerMonoButton*" style:highest "very_small_button"
 widget "*EditGroupButton" style:highest "very_small_button"
 widget "*EditGroupButtonLabel" style:highest "very_small_button"
 widget "*TransportButton" style:highest "transport_rec_button"
index cefd45cbd0d3085375dc8c6fa2ae2e992cb22c2d..1cb58e328540f830035705b7b28e9abc3d17f872 100644 (file)
@@ -1306,6 +1306,7 @@ widget "*MixerGroupButton" style:highest "very_small_button"
 widget "*MixerGroupButtonLabel" style:highest "very_small_button"
 widget "*MixerCommentButton" style:highest "very_small_button"
 widget "*MixerCommentButton*" style:highest "very_small_button"
+widget "*MixerMonoButton*" style:highest "very_small_button"
 widget "*EditGroupButton" style:highest "very_small_button"
 widget "*EditGroupButtonLabel" style:highest "very_small_button"
 widget "*TransportButton" style:highest "transport_rec_button"
index 5176556ba8c16eb301a7abaa17ca5ba60d4ab246..62b107ce766a2d16ac4b442a1e42bb90a96ce0c3 100644 (file)
@@ -1310,6 +1310,7 @@ widget "*MixerGroupButton" style:highest "very_small_button"
 widget "*MixerGroupButtonLabel" style:highest "very_small_button"
 widget "*MixerCommentButton" style:highest "very_small_button"
 widget "*MixerCommentButton*" style:highest "very_small_button"
+widget "*MixerMonoButton*" style:highest "very_small_button"
 widget "*EditGroupButton" style:highest "very_small_button"
 widget "*EditGroupButtonLabel" style:highest "very_small_button"
 widget "*TransportButton" style:highest "transport_rec_button"
index d41ac4fb97215e345aa3b9bc81030b24862c03df..c1b10a40bec43afe7431f74d5e9891d38a6602be 100644 (file)
@@ -1310,6 +1310,7 @@ widget "*MixerGroupButton" style:highest "very_small_button"
 widget "*MixerGroupButtonLabel" style:highest "very_small_button"
 widget "*MixerCommentButton" style:highest "very_small_button"
 widget "*MixerCommentButton*" style:highest "very_small_button"
+widget "*MixerMonoButton*" style:highest "very_small_button"
 widget "*EditGroupButton" style:highest "very_small_button"
 widget "*EditGroupButtonLabel" style:highest "very_small_button"
 widget "*TransportButton" style:highest "transport_rec_button"
index 17761798c27901a26cc5558eac67bc9d6cdf3333..10e6999d053331e2c73d02287add13af6024bd3a 100644 (file)
@@ -81,6 +81,7 @@ MixerStrip::MixerStrip (Mixer_UI& mx, Session& sess, bool in_mixer)
        , processor_box (sess, mx.plugin_selector(), mx.selection(), this, in_mixer)
        , gpm (sess)
        , panners (sess)
+       , _mono_button (_("Mono"))
        , button_table (3, 2)
        , middle_button_table (1, 2)
        , bottom_button_table (1, 2)
@@ -236,6 +237,7 @@ MixerStrip::init ()
        if (!is_midi_track()) {
                global_vpacker.pack_start (panners, Gtk::PACK_SHRINK);
        }
+       global_vpacker.pack_start (_mono_button, Gtk::PACK_SHRINK);
        global_vpacker.pack_start (output_button, Gtk::PACK_SHRINK);
        global_vpacker.pack_start (comment_button, Gtk::PACK_SHRINK);
 
@@ -387,6 +389,9 @@ MixerStrip::set_route (boost::shared_ptr<Route> rt)
                name_label.set_text (_route->name());
        }
 
+       _mono_button.set_name ("MixerMonoButton");
+       _mono_button.signal_clicked().connect (mem_fun (*this, &MixerStrip::mono_button_clicked));
+
        switch (_route->meter_point()) {
        case MeterInput:
                meter_point_label.set_text (_("input"));
@@ -1782,3 +1787,9 @@ MixerStrip::on_leave_notify_event (GdkEventCrossing* ev)
 
        return false;
 }
+
+void
+MixerStrip::mono_button_clicked ()
+{
+       panners.set_mono (_mono_button.get_active ());
+}
index afacd4a32836a3aca2569d352c138504bffb3bb4..c83c260845cf3ee7889402c2875d7ec31e4306d6 100644 (file)
@@ -142,6 +142,7 @@ class MixerStrip : public RouteUI, public Gtk::EventBox
        ProcessorBox processor_box;
        GainMeter   gpm;
        PannerUI    panners;
+       Gtk::ToggleButton _mono_button;
 
        Gtk::Table button_table;
        Gtk::Table middle_button_table;
@@ -176,6 +177,7 @@ class MixerStrip : public RouteUI, public Gtk::EventBox
        void comment_editor_done_editing();
        void setup_comment_editor ();
        void comment_button_clicked ();
+       void mono_button_clicked ();
 
        Gtk::Button   group_button;
        Gtk::Label    group_label;
index 31403220fe503e382274219e0a5890ba4a9ff37f..a643e1b318763d21156b2bceb5d875d9f9a92d90 100644 (file)
@@ -708,7 +708,7 @@ PannerUI::update_pan_bars (bool only_if_aplay)
 void
 PannerUI::update_pan_sensitive ()
 {
-       bool sensitive = !(_panner->automation_state() & Play);
+       bool const sensitive = !(_panner->mono()) && !(_panner->automation_state() & Play);
 
        switch (_panner->nouts()) {
        case 0:
@@ -890,3 +890,12 @@ PannerUI::_astyle_string (AutoStyle style, bool shrt)
                return (shrt ? _("Abs") : _("Abs"));
        }
 }
+
+void
+PannerUI::set_mono (bool yn)
+{
+       _panner->set_mono (yn);
+       update_pan_sensitive ();
+}
+
+       
index 724b8b86e7d8d36b010475c883b87f71a45e1aa0..bebffec6c00d532501d1d7b80a2b43e0bb2ddff4 100644 (file)
@@ -74,6 +74,8 @@ class PannerUI : public Gtk::HBox
        void set_meter_strip_name (std::string name);
        boost::shared_ptr<PBD::Controllable> get_controllable();
 
+       void set_mono (bool);
+
   private:
        friend class MixerStrip;
 
index 86cd4861136b3c7d6aa8ec1a0c25463f9e5ac18c..7ea37e99743cd7d657e39f93b1783ae9280b027d 100644 (file)
@@ -49,6 +49,7 @@ class StreamPanner : public sigc::trackable, public PBD::Stateful
 
        void set_muted (bool yn);
        bool muted() const { return _muted; }
+       void set_mono (bool);
 
        void set_position (float x, bool link_call = false);
        void set_position (float x, float y, bool link_call = false);
@@ -62,11 +63,22 @@ class StreamPanner : public sigc::trackable, public PBD::Stateful
        void get_effective_position (float& xpos, float& ypos) const { xpos = effective_x; ypos = effective_y; }
        void get_effective_position (float& xpos, float& ypos, float& zpos) const { xpos = effective_x; ypos = effective_y; zpos = effective_z; }
 
+       void distribute (AudioBuffer &, BufferSet &, gain_t, nframes_t);
+       void distribute_automated (AudioBuffer &, BufferSet &, nframes_t, nframes_t, nframes_t, pan_t **);
+
        /* the basic StreamPanner API */
 
-       virtual void distribute (AudioBuffer& src, BufferSet& obufs, gain_t gain_coeff, nframes_t nframes) = 0;
-       virtual void distribute_automated (AudioBuffer& src, BufferSet& obufs,
-                       nframes_t start, nframes_t end, nframes_t nframes, pan_t** buffers) = 0;
+       /**
+        *  Pan some input samples to a number of output buffers.
+        *
+        *  @param src Input buffer.
+        *  @param obufs Output buffers (one per panner output).
+        *  @param gain_coeff Gain coefficient to apply to output samples.
+        *  @param nframes Numbner of frames in the input.
+        */
+       virtual void do_distribute (AudioBuffer& src, BufferSet& obufs, gain_t gain_coeff, nframes_t nframes) = 0;
+       virtual void do_distribute_automated (AudioBuffer& src, BufferSet& obufs,
+                                             nframes_t start, nframes_t end, nframes_t nframes, pan_t** buffers) = 0;
 
        boost::shared_ptr<AutomationControl> pan_control()  { return _control; }
 
@@ -99,6 +111,7 @@ class StreamPanner : public sigc::trackable, public PBD::Stateful
        float effective_z;
 
        bool _muted;
+       bool _mono;
 
        boost::shared_ptr<AutomationControl> _control;
 
@@ -113,12 +126,12 @@ class BaseStereoPanner : public StreamPanner
        ~BaseStereoPanner ();
 
        /* this class just leaves the pan law itself to be defined
-          by the update(), distribute_automated()
+          by the update(), do_distribute_automated()
           methods. derived classes also need a factory method
           and a type name. See EqualPowerStereoPanner as an example.
        */
 
-       void distribute (AudioBuffer& src, BufferSet& obufs, gain_t gain_coeff, nframes_t nframes);
+       void do_distribute (AudioBuffer& src, BufferSet& obufs, gain_t gain_coeff, nframes_t nframes);
 
        /* old school automation loading */
 
@@ -139,8 +152,8 @@ class EqualPowerStereoPanner : public BaseStereoPanner
        EqualPowerStereoPanner (Panner&, Evoral::Parameter param);
        ~EqualPowerStereoPanner ();
 
-       void distribute_automated (AudioBuffer& src, BufferSet& obufs,
-                       nframes_t start, nframes_t end, nframes_t nframes, pan_t** buffers);
+       void do_distribute_automated (AudioBuffer& src, BufferSet& obufs,
+                                     nframes_t start, nframes_t end, nframes_t nframes, pan_t** buffers);
 
        void get_current_coefficients (pan_t*) const;
        void get_desired_coefficients (pan_t*) const;
@@ -162,9 +175,9 @@ class Multi2dPanner : public StreamPanner
        Multi2dPanner (Panner& parent, Evoral::Parameter);
        ~Multi2dPanner ();
 
-       void distribute (AudioBuffer& src, BufferSet& obufs, gain_t gain_coeff, nframes_t nframes);
-       void distribute_automated (AudioBuffer& src, BufferSet& obufs,
-                       nframes_t start, nframes_t end, nframes_t nframes, pan_t** buffers);
+       void do_distribute (AudioBuffer& src, BufferSet& obufs, gain_t gain_coeff, nframes_t nframes);
+       void do_distribute_automated (AudioBuffer& src, BufferSet& obufs,
+                                     nframes_t start, nframes_t end, nframes_t nframes, pan_t** buffers);
 
        static StreamPanner* factory (Panner&, Evoral::Parameter);
        static std::string name;
@@ -182,6 +195,8 @@ class Multi2dPanner : public StreamPanner
 };
 
 
+///< Class to pan from some number of inputs to some number of outputs
+
 class Panner : public SessionObject, public AutomatableControls
 {
 public:
@@ -219,6 +234,8 @@ public:
 
        bool bypassed() const { return _bypassed; }
        void set_bypassed (bool yn);
+       bool mono () const { return _mono; }
+       void set_mono (bool);
 
        StreamPanner* add ();
        void remove (uint32_t which);
@@ -295,10 +312,11 @@ public:
        Panner (Panner const &);
 
        void distribute_no_automation(BufferSet& src, BufferSet& dest, nframes_t nframes, gain_t gain_coeff);
-       std::vector<StreamPanner*> _streampanners;
+       std::vector<StreamPanner*> _streampanners; ///< one StreamPanner per input
        uint32_t     current_outs;
        bool             _linked;
        bool             _bypassed;
+       bool             _mono;
        LinkDirection    _link_direction;
 
        static float current_automation_version_number;
index a867130b515f5a5d6ad512e1e6c400eb2121a97e..6c8dc153bbb8e2ad83b9ad61135743ef09cd4b54 100644 (file)
@@ -80,6 +80,7 @@ StreamPanner::StreamPanner (Panner& p, Evoral::Parameter param)
        assert(param.type() != NullAutomation);
 
        _muted = false;
+       _mono = false;
 
        _control = boost::dynamic_pointer_cast<AutomationControl>( parent.control( param, true ) );
 
@@ -92,6 +93,15 @@ StreamPanner::~StreamPanner ()
 {
 }
 
+void
+StreamPanner::set_mono (bool yn)
+{
+       if (yn != _mono) {
+               _mono = yn;
+               StateChanged ();
+       }
+}
+
 void
 Panner::PanControllable::set_value (float val)
 {
@@ -180,6 +190,39 @@ StreamPanner::add_state (XMLNode& node)
        node.add_property (X_("muted"), (muted() ? "yes" : "no"));
 }
 
+void
+StreamPanner::distribute (AudioBuffer& src, BufferSet& obufs, gain_t gain_coeff, nframes_t nframes)
+{
+       if (_mono) {
+               /* we're in mono mode, so just pan the input to all outputs equally */
+               int const N = parent.nouts ();
+               for (int i = 0; i < N; ++i) {
+                       mix_buffers_with_gain (obufs.get_audio(i).data(), src.data(), nframes, gain_coeff);
+               }
+       } else {
+               /* normal mode, call the `real' distribute method */
+               do_distribute (src, obufs, gain_coeff, nframes);
+       }
+}
+
+void
+StreamPanner::distribute_automated (AudioBuffer& src, BufferSet& obufs,
+                                   nframes_t start, nframes_t end, nframes_t nframes, pan_t** buffers)
+{
+       if (_mono) {
+               /* we're in mono mode, so just pan the input to all outputs equally */
+               int const N = parent.nouts ();
+               for (int i = 0; i < N; ++i) {
+                       mix_buffers_with_gain (obufs.get_audio(i).data(), src.data(), nframes, 1.0);
+               }
+       } else {
+               /* normal mode, call the `real' distribute method */
+               do_distribute_automated (src, obufs, start, end, nframes, buffers);
+       }
+       
+}
+
+
 /*---------------------------------------------------------------------- */
 
 BaseStereoPanner::BaseStereoPanner (Panner& p, Evoral::Parameter param)
@@ -225,7 +268,7 @@ BaseStereoPanner::load (istream& in, string path, uint32_t& linecnt)
 }
 
 void
-BaseStereoPanner::distribute (AudioBuffer& srcbuf, BufferSet& obufs, gain_t gain_coeff, nframes_t nframes)
+BaseStereoPanner::do_distribute (AudioBuffer& srcbuf, BufferSet& obufs, gain_t gain_coeff, nframes_t nframes)
 {
        assert(obufs.count().n_audio() == 2);
 
@@ -245,9 +288,10 @@ BaseStereoPanner::distribute (AudioBuffer& srcbuf, BufferSet& obufs, gain_t gain
 
        if (fabsf ((delta = (left - desired_left))) > 0.002) { // about 1 degree of arc
 
-               /* interpolate over 64 frames or nframes, whichever is smaller */
+               /* we've moving the pan by an appreciable amount, so we must
+                  interpolate over 64 frames or nframes, whichever is smaller */
 
-               nframes_t limit = min ((nframes_t)64, nframes);
+               nframes_t const limit = min ((nframes_t)64, nframes);
                nframes_t n;
 
                delta = -(delta / (float) (limit));
@@ -258,6 +302,8 @@ BaseStereoPanner::distribute (AudioBuffer& srcbuf, BufferSet& obufs, gain_t gain
                        dst[n] += src[n] * left * gain_coeff;
                }
 
+               /* then pan the rest of the buffer; no need for interpolation for this bit */
+
                pan = left * gain_coeff;
 
                mix_buffers_with_gain (dst+n,src+n,nframes-n,pan);
@@ -271,6 +317,8 @@ BaseStereoPanner::distribute (AudioBuffer& srcbuf, BufferSet& obufs, gain_t gain
 
                        if (pan != 0.0f) {
 
+                               /* pan is 1 but also not 0, so we must do it "properly" */
+
                                mix_buffers_with_gain(dst,src,nframes,pan);
 
                                /* mark that we wrote into the buffer */
@@ -281,6 +329,8 @@ BaseStereoPanner::distribute (AudioBuffer& srcbuf, BufferSet& obufs, gain_t gain
 
                } else {
 
+                       /* pan is 1 so we can just copy the input samples straight in */
+
                        mix_buffers_no_gain(dst,src,nframes);
 
                        /* mark that we wrote into the buffer */
@@ -295,9 +345,10 @@ BaseStereoPanner::distribute (AudioBuffer& srcbuf, BufferSet& obufs, gain_t gain
 
        if (fabsf ((delta = (right - desired_right))) > 0.002) { // about 1 degree of arc
 
-               /* interpolate over 64 frames or nframes, whichever is smaller */
+               /* we're moving the pan by an appreciable amount, so we must
+                  interpolate over 64 frames or nframes, whichever is smaller */
 
-               nframes_t limit = min ((nframes_t)64, nframes);
+               nframes_t const limit = min ((nframes_t)64, nframes);
                nframes_t n;
 
                delta = -(delta / (float) (limit));
@@ -308,6 +359,8 @@ BaseStereoPanner::distribute (AudioBuffer& srcbuf, BufferSet& obufs, gain_t gain
                        dst[n] += src[n] * right * gain_coeff;
                }
 
+               /* then pan the rest of the buffer, no need for interpolation for this bit */
+
                pan = right * gain_coeff;
 
                mix_buffers_with_gain(dst+n,src+n,nframes-n,pan);
@@ -323,6 +376,8 @@ BaseStereoPanner::distribute (AudioBuffer& srcbuf, BufferSet& obufs, gain_t gain
 
                        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);
 
                                /* XXX it would be nice to mark the buffer as written to */
@@ -330,6 +385,8 @@ BaseStereoPanner::distribute (AudioBuffer& srcbuf, BufferSet& obufs, gain_t gain
 
                } 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 */
@@ -358,9 +415,9 @@ void
 EqualPowerStereoPanner::update ()
 {
        /* it would be very nice to split this out into a virtual function
-          that can be accessed from BaseStereoPanner and used in distribute_automated().
+          that can be accessed from BaseStereoPanner and used in do_distribute_automated().
 
-          but the place where its used in distribute_automated() is a tight inner loop,
+          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.
        */
@@ -383,9 +440,9 @@ EqualPowerStereoPanner::update ()
 }
 
 void
-EqualPowerStereoPanner::distribute_automated (AudioBuffer& srcbuf, BufferSet& obufs,
-                                             nframes_t start, nframes_t end, nframes_t nframes,
-                                             pan_t** buffers)
+EqualPowerStereoPanner::do_distribute_automated (AudioBuffer& srcbuf, BufferSet& obufs,
+                                                nframes_t start, nframes_t end, nframes_t nframes,
+                                                pan_t** buffers)
 {
        assert(obufs.count().n_audio() == 2);
 
@@ -398,7 +455,7 @@ EqualPowerStereoPanner::distribute_automated (AudioBuffer& srcbuf, BufferSet& ob
        if (!_control->list()->curve().rt_safe_get_vector (start, end, buffers[0], nframes)) {
                /* fallback */
                if (!_muted) {
-                       distribute (srcbuf, obufs, 1.0, nframes);
+                       do_distribute (srcbuf, obufs, 1.0, nframes);
                }
                return;
        }
@@ -563,7 +620,7 @@ Multi2dPanner::update ()
 }
 
 void
-Multi2dPanner::distribute (AudioBuffer& srcbuf, BufferSet& obufs, gain_t gain_coeff, nframes_t nframes)
+Multi2dPanner::do_distribute (AudioBuffer& srcbuf, BufferSet& obufs, gain_t gain_coeff, nframes_t nframes)
 {
        Sample* dst;
        pan_t pan;
@@ -623,9 +680,9 @@ Multi2dPanner::distribute (AudioBuffer& srcbuf, BufferSet& obufs, gain_t gain_co
 }
 
 void
-Multi2dPanner::distribute_automated (AudioBuffer& /*src*/, BufferSet& /*obufs*/,
-                                    nframes_t /*start*/, nframes_t /*end*/, nframes_t /*nframes*/,
-                                    pan_t** /*buffers*/)
+Multi2dPanner::do_distribute_automated (AudioBuffer& /*src*/, BufferSet& /*obufs*/,
+                                       nframes_t /*start*/, nframes_t /*end*/, nframes_t /*nframes*/,
+                                       pan_t** /*buffers*/)
 {
        if (_muted) {
                return;
@@ -823,6 +880,12 @@ Panner::reset_streampanner (uint32_t which)
        }
 }
 
+/**
+ *    Reset the panner with a given number of outs and panners (and hence inputs)
+ *
+ *    \param nouts Number of outputs.
+ *    \param npans Number of panners.
+ */
 void
 Panner::reset (uint32_t nouts, uint32_t npans)
 {
@@ -1557,3 +1620,16 @@ Panner::load ()
 
        return 0;
 }
+
+void
+Panner::set_mono (bool yn)
+{
+       if (yn != _mono) {
+               _mono = yn;
+               StateChanged ();
+       }
+
+       for (vector<StreamPanner*>::iterator i = _streampanners.begin(); i != _streampanners.end(); ++i) {
+               (*i)->set_mono (yn);
+       }
+}