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"
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"
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"
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"
, 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)
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);
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"));
return false;
}
+
+void
+MixerStrip::mono_button_clicked ()
+{
+ panners.set_mono (_mono_button.get_active ());
+}
ProcessorBox processor_box;
GainMeter gpm;
PannerUI panners;
+ Gtk::ToggleButton _mono_button;
Gtk::Table button_table;
Gtk::Table middle_button_table;
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;
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:
return (shrt ? _("Abs") : _("Abs"));
}
}
+
+void
+PannerUI::set_mono (bool yn)
+{
+ _panner->set_mono (yn);
+ update_pan_sensitive ();
+}
+
+
void set_meter_strip_name (std::string name);
boost::shared_ptr<PBD::Controllable> get_controllable();
+ void set_mono (bool);
+
private:
friend class MixerStrip;
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);
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; }
float effective_z;
bool _muted;
+ bool _mono;
boost::shared_ptr<AutomationControl> _control;
~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 */
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;
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;
};
+///< Class to pan from some number of inputs to some number of outputs
+
class Panner : public SessionObject, public AutomatableControls
{
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);
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;
assert(param.type() != NullAutomation);
_muted = false;
+ _mono = false;
_control = boost::dynamic_pointer_cast<AutomationControl>( parent.control( param, true ) );
{
}
+void
+StreamPanner::set_mono (bool yn)
+{
+ if (yn != _mono) {
+ _mono = yn;
+ StateChanged ();
+ }
+}
+
void
Panner::PanControllable::set_value (float val)
{
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)
}
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);
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));
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);
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 */
} 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 */
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));
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);
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 */
} 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 */
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.
*/
}
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);
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;
}
}
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;
}
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;
}
}
+/**
+ * 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)
{
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);
+ }
+}