Index: gtk2_ardour/mixer_strip.cc
===================================================================
--- gtk2_ardour/mixer_strip.cc	(revision 1560)
+++ gtk2_ardour/mixer_strip.cc	(working copy)
@@ -89,6 +89,7 @@
 	  post_redirect_box (PostFader, sess, rt, mx.plugin_selector(), mx.selection(), in_mixer),
 	  gpm (_route, sess),
 	  panners (_route, sess),
+	  mono_button (_("Mono")),
 	  button_table (3, 2),
 	  middle_button_table (1, 2),
 	  bottom_button_table (1, 2),
@@ -215,6 +216,9 @@
 	        name_label.set_text (_route->name());
 	}
 
+	mono_button.set_name ("MixerMonoButton");
+	mono_button.signal_clicked().connect (mem_fun(*this, &MixerStrip::mono_button_clicked));
+
 	group_button.add (group_label);
 	group_button.set_name ("MixerGroupButton");
 	group_label.set_name ("MixerGroupButtonLabel");
@@ -256,6 +260,7 @@
 	global_vpacker.pack_start (bottom_button_table,Gtk::PACK_SHRINK);
 	global_vpacker.pack_start (post_redirect_box, true, true);
 	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);
 
@@ -1219,3 +1224,8 @@
 		gpm.setup_meters ();
 }
 
+void
+MixerStrip::mono_button_clicked ()
+{
+	panners.set_mono (mono_button.get_active ());
+}
Index: gtk2_ardour/panner_ui.h
===================================================================
--- gtk2_ardour/panner_ui.h	(revision 1560)
+++ gtk2_ardour/panner_ui.h	(working copy)
@@ -69,6 +69,8 @@
 
 	void set_meter_strip_name (string name);
 
+	void set_mono (bool yn);
+
   private:
 	friend class MixerStrip;
 	boost::shared_ptr<ARDOUR::IO> _io;
Index: gtk2_ardour/panner_ui.cc
===================================================================
--- gtk2_ardour/panner_ui.cc	(revision 1560)
+++ gtk2_ardour/panner_ui.cc	(working copy)
@@ -604,7 +604,7 @@
 void
 PannerUI::update_pan_sensitive ()
 {
-	bool sensitive = !(_io->panner().automation_state() & Play);
+	const bool sensitive = !(_io->panner().mono()) && !(_io->panner().automation_state() & Play);
 
 	switch (_io->n_outputs()) {
 	case 0:
@@ -777,3 +777,10 @@
 		return (shrt ? _("Abs") : _("Abs"));
 	}
 }
+
+void
+PannerUI::set_mono (bool yn)
+{
+	_io->panner().set_mono (yn);
+	update_pan_sensitive ();
+}
Index: gtk2_ardour/mixer_strip.h
===================================================================
--- gtk2_ardour/mixer_strip.h	(revision 1560)
+++ gtk2_ardour/mixer_strip.h	(working copy)
@@ -123,6 +123,7 @@
 	RedirectBox post_redirect_box;
 	GainMeter   gpm;
        	PannerUI    panners;
+	Gtk::ToggleButton mono_button;
 	
 	Gtk::Table button_table;
 	Gtk::Table middle_button_table;
@@ -157,6 +158,7 @@
 	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;

Property changes on: libs/fst
___________________________________________________________________
Name: svn:ignore
   + *.zip
vstsdk2.3



Index: libs/ardour/ardour/panner.h
===================================================================
--- libs/ardour/ardour/panner.h	(revision 1560)
+++ libs/ardour/ardour/panner.h	(working copy)
@@ -48,6 +48,7 @@
 
 	void set_muted (bool yn);
 	bool muted() const { return _muted; }
+	void set_mono (bool yn);
 
 	void set_position (float x, bool link_call = false);
 	void set_position (float x, float y, bool link_call = false);
@@ -61,10 +62,22 @@
 	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 (Sample* src, Sample** obufs, gain_t gain_coeff, nframes_t nframes);
+	void distribute_automated (Sample* src, Sample** obufs, 
+				   nframes_t start, nframes_t end, nframes_t nframes, pan_t** buffers);
+
 	/* the basic panner API */
 
-	virtual void distribute (Sample* src, Sample** obufs, gain_t gain_coeff, nframes_t nframes) = 0;
-	virtual void distribute_automated (Sample* src, Sample** obufs, 
+	/**
+	 *  Pan some input samples to a number of output buffers.
+	 *
+	 *  \param src Input samples
+	 *  \param obufs Output buffers (one per panner output)
+	 *  \param gain_coeff Gain coefficient to apply to output samples.
+	 *  \param nframes Number of frames in the input
+	 */
+	virtual void do_distribute (Sample* src, Sample** obufs, gain_t gain_coeff, nframes_t nframes) = 0;
+	virtual void do_distribute_automated (Sample* src, Sample** obufs, 
 				     nframes_t start, nframes_t end, nframes_t nframes, pan_t** buffers) = 0;
 
 	/* automation */
@@ -111,6 +124,7 @@
 	float effective_z;
 
 	bool             _muted;
+	bool             _mono;
 
 	struct PanControllable : public PBD::Controllable {
 	    PanControllable (std::string name, StreamPanner& p) : Controllable (name), panner (p) {}
@@ -135,12 +149,12 @@
 	~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 (Sample* src, Sample** obufs, gain_t gain_coeff, nframes_t nframes);
+	void do_distribute (Sample* src, Sample** obufs, gain_t gain_coeff, nframes_t nframes);
 
 	void snapshot (nframes_t now);
 	void transport_stopped (nframes_t frame);
@@ -170,7 +184,7 @@
 	EqualPowerStereoPanner (Panner&);
 	~EqualPowerStereoPanner ();
 
-	void distribute_automated (Sample* src, Sample** obufs, 
+	void do_distribute_automated (Sample* src, Sample** obufs, 
 			     nframes_t start, nframes_t end, nframes_t nframes, pan_t** buffers);
 
 	void get_current_coefficients (pan_t*) const;
@@ -204,8 +218,8 @@
 
 	Curve& automation() { return _automation; }
 
-	void distribute (Sample* src, Sample** obufs, gain_t gain_coeff, nframes_t nframes);
-	void distribute_automated (Sample* src, Sample** obufs, 
+	void do_distribute (Sample* src, Sample** obufs, gain_t gain_coeff, nframes_t nframes);
+	void do_distribute_automated (Sample* src, Sample** obufs, 
 				   nframes_t start, nframes_t end, nframes_t nframes, pan_t** buffers);
 
 	static StreamPanner* factory (Panner&);
@@ -224,6 +238,14 @@
 	void update ();
 };
 
+
+/// Class to pan from some number of inputs to some number of outputs
+
+/**
+ *    Panner is derived from std::vector, and contains one StreamPanner* per input.
+ *    It also has a number of outputs which are defined by Output structs.
+ */
+
 class Panner : public std::vector<StreamPanner*>, public Stateful, public sigc::trackable
 {
   public:
@@ -243,6 +265,8 @@
 
 	bool bypassed() const { return _bypassed; }
 	void set_bypassed (bool yn);
+	bool mono() const { return _mono; }
+	void set_mono (bool yn);
 
 	StreamPanner* add ();
 	void remove (uint32_t which);
@@ -267,7 +291,7 @@
 	sigc::signal<void> Changed;
 	
 	static bool equivalent (pan_t a, pan_t b) {
-		return fabsf (a - b) < 0.002; // about 1 degree of arc for a stereo panner
+		return fabsf (a - b) < 0.002;  // about 1 degree of arc for a stereo panner
 	}
 
 	void move_output (uint32_t, float x, float y);
@@ -307,6 +331,7 @@
 	uint32_t     current_outs;
 	bool             _linked;
 	bool             _bypassed;
+	bool             _mono;
 	LinkDirection    _link_direction;
 
 	static float current_automation_version_number;
Index: libs/ardour/panner.cc
===================================================================
--- libs/ardour/panner.cc	(revision 1560)
+++ libs/ardour/panner.cc	(working copy)
@@ -70,6 +70,7 @@
 	  _control (X_("panner"), *this)
 {
 	_muted = false;
+	_mono = false;
 
 	parent.session().add_controllable (&_control);
 
@@ -118,6 +119,15 @@
 }
 
 void
+StreamPanner::set_mono (bool yn)
+{
+	if (yn != _mono) {
+		_mono = yn;
+		StateChanged ();
+	}
+}
+
+void
 StreamPanner::set_position (float xpos, bool link_call)
 {
 	if (!link_call && parent.linked()) {
@@ -183,6 +193,38 @@
 	node.add_property (X_("muted"), (muted() ? "yes" : "no"));
 }
 
+void
+StreamPanner::distribute (Sample* src, Sample** obufs, gain_t gain_coeff, nframes_t nframes)
+{
+	if (_mono) {
+		/* we're in mono mode, so just pan the input to all outputs equally */
+		const int N = parent.nouts ();
+		for (int i = 0; i < N; ++i) {
+			Session::mix_buffers_with_gain (obufs[i], src, nframes, gain_coeff);
+		}
+	} else {
+		/* normal mode, call the `real' distribute method */
+		do_distribute (src, obufs, gain_coeff, nframes);
+	}
+}
+
+void
+StreamPanner::distribute_automated (Sample* src, Sample** 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 */
+		const int N = parent.nouts ();
+		for (int i = 0; i < N; ++i) {
+			Session::mix_buffers_with_gain (obufs[i], src, nframes, 1.0);
+		}
+	} else {
+		/* normal mode, call the `real' distribute method */
+		do_distribute_automated (src, obufs, start, end, nframes, buffers);
+	}
+	
+}
+
 /*---------------------------------------------------------------------- */
 
 BaseStereoPanner::BaseStereoPanner (Panner& p)
@@ -264,8 +306,9 @@
 	return 0;
 }
 
+
 void
-BaseStereoPanner::distribute (Sample* src, Sample** obufs, gain_t gain_coeff, nframes_t nframes)
+BaseStereoPanner::do_distribute (Sample* src, Sample** obufs, gain_t gain_coeff, nframes_t nframes)
 {
 	pan_t delta;
 	Sample* dst;
@@ -281,9 +324,10 @@
 
 	if (fabsf ((delta = (left - desired_left))) > 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);
+		const nframes_t limit = min ((nframes_t)64, nframes);
 		nframes_t n;
 
 		delta = -(delta / (float) (limit));
@@ -293,6 +337,8 @@
 			left = left_interp + 0.9 * (left - left_interp);
 			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;
 
@@ -306,6 +352,8 @@
 		if ((pan = (left * gain_coeff)) != 1.0f) {
 			
 			if (pan != 0.0f) {
+
+				/* pan is not 1 but also not 0, so we must do it "properly" */
 				
 				Session::mix_buffers_with_gain(dst,src,nframes,pan);
 
@@ -316,6 +364,8 @@
 			} 
 			
 		} else {
+
+			/* pan is 1 so we can just copy the input samples straight in */
 			
 			Session::mix_buffers_no_gain(dst,src,nframes);
 			
@@ -328,12 +378,13 @@
 	/* RIGHT */
 
 	dst = obufs[1];
-	
+
 	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);
+		const nframes_t limit = min ((nframes_t)64, nframes);
 		nframes_t n;
 
 		delta = -(delta / (float) (limit));
@@ -344,6 +395,8 @@
 			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;
 		
 		Session::mix_buffers_with_gain(dst+n,src+n,nframes-n,pan);
@@ -359,6 +412,8 @@
 			
 			if (pan != 0.0f) {
 				
+				/* pan is not 1 but also not 0, so we must do it "properly" */
+
 				Session::mix_buffers_with_gain(dst,src,nframes,pan);
 				
 				/* XXX it would be nice to mark the buffer as written to */
@@ -366,6 +421,8 @@
 			
 		} else {
 			
+			/* pan is 1 so we can just copy the input samples straight in */
+
 			Session::mix_buffers_no_gain(dst,src,nframes);
 			
 			/* XXX it would be nice to mark the buffer as written to */
@@ -394,9 +451,9 @@
 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.
 	*/
@@ -418,7 +475,7 @@
 }
 
 void
-EqualPowerStereoPanner::distribute_automated (Sample* src, Sample** obufs, 
+EqualPowerStereoPanner::do_distribute_automated (Sample* src, Sample** obufs, 
 					      nframes_t start, nframes_t end, nframes_t nframes,
 					      pan_t** buffers)
 {
@@ -430,7 +487,7 @@
 	if (!_automation.rt_safe_get_vector (start, end, buffers[0], nframes)) {
 		/* fallback */
 		if (!_muted) {
-			distribute (src, obufs, 1.0, nframes);
+			do_distribute (src, obufs, 1.0, nframes);
 		}
 		return;
 	}
@@ -620,7 +677,7 @@
 }
 
 void
-Multi2dPanner::distribute (Sample* src, Sample** obufs, gain_t gain_coeff, nframes_t nframes)
+Multi2dPanner::do_distribute (Sample* src, Sample** obufs, gain_t gain_coeff, nframes_t nframes)
 {
 	Sample* dst;
 	pan_t pan;
@@ -691,9 +748,9 @@
 }
 
 void
-Multi2dPanner::distribute_automated (Sample* src, Sample** obufs, 
-				     nframes_t start, nframes_t end, nframes_t nframes,
-				     pan_t** buffers)
+Multi2dPanner::do_distribute_automated (Sample* src, Sample** obufs, 
+					nframes_t start, nframes_t end, nframes_t nframes,
+					pan_t** buffers)
 {
 	if (_muted) {
 		return;
@@ -813,7 +870,27 @@
 	}
 }
 
+void
+Panner::set_mono (bool yn)
+{
+	if (yn != _mono) {
+		_mono = yn;
+		StateChanged ();
+	}
 
+	for (iterator i = begin(); i != end(); ++i) {
+		(*i)->set_mono (yn);
+	}
+}
+
+
+/**
+ *    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)
 {
@@ -824,6 +901,7 @@
 		return;
 	} 
 
+	/* clear panners vector */
 	n = size();
 	clear ();
 
@@ -831,6 +909,7 @@
 		changed = true;
 	}
 
+	/* clear outputs vector */
 	n = outputs.size();
 	outputs.clear ();
 
@@ -838,6 +917,7 @@
 		changed = true;
 	}
 
+	/* decide how we pan based on the number of outputs */
 	switch (nouts) {
 	case 0:
 		break;
@@ -849,8 +929,7 @@
 		/*NOTREACHED*/
 		break;
 
-	case 2:
-		/* line */
+	case 2: // line
 		outputs.push_back (Output (0, 0));
 		outputs.push_back (Output (1.0, 0));
 
Index: libs/ardour/mix.cc
===================================================================
--- libs/ardour/mix.cc	(revision 1560)
+++ libs/ardour/mix.cc	(working copy)
@@ -97,6 +97,17 @@
 		buf[i] *= gain;
 }
 
+
+/**
+ *    Apply some gain to a set of source samples and then add them to the current
+ *    contents of a destination buffer.
+ *
+ *    \param dst Destination buffer.
+ *    \param src Source samples.
+ *    \param nframes Number of frames.
+ *    \param gain Gain to apply to source samples.
+ */
+
 void
 mix_buffers_with_gain (ARDOUR::Sample *dst, ARDOUR::Sample *src, nframes_t nframes, float gain)
 {

