summaryrefslogtreecommitdiff
path: root/libs
diff options
context:
space:
mode:
authorPaul Davis <paul@linuxaudiosystems.com>2011-01-27 01:31:03 +0000
committerPaul Davis <paul@linuxaudiosystems.com>2011-01-27 01:31:03 +0000
commit15b5fce90480490455237da917167b0bcb5ce946 (patch)
tree5c1c5929a83c05db1a901e775fefe4f6cf8dc1b7 /libs
parent1385643131a2b2231bbbc0c584c76883fcfb580a (diff)
merge 3.0-panexp (pan experiments) branch, revisions 8534-8585 into 3.0, thus ending 3.0-panexp. THIS COMMIT WILL BREAK ALL EXISTING 3.0 SESSIONS IN SOME WAY (possibly not fatally).
git-svn-id: svn://localhost/ardour2/branches/3.0@8586 d708f5d6-7413-0410-9779-e7cbd77b26cf
Diffstat (limited to 'libs')
-rw-r--r--libs/ardour/ardour/automatable.h3
-rw-r--r--libs/ardour/ardour/debug.h1
-rw-r--r--libs/ardour/ardour/delivery.h13
-rw-r--r--libs/ardour/ardour/directory_names.h1
-rw-r--r--libs/ardour/ardour/internal_send.h2
-rw-r--r--libs/ardour/ardour/pannable.h18
-rw-r--r--libs/ardour/ardour/panner.h339
-rw-r--r--libs/ardour/ardour/port_insert.h3
-rw-r--r--libs/ardour/ardour/route.h7
-rw-r--r--libs/ardour/ardour/send.h2
-rw-r--r--libs/ardour/ardour/speakers.h12
-rw-r--r--libs/ardour/ardour/types.h35
-rw-r--r--libs/ardour/ardour/vbap.h70
-rw-r--r--libs/ardour/ardour/vbap_speakers.h107
-rw-r--r--libs/ardour/audioengine.cc4
-rw-r--r--libs/ardour/auditioner.cc4
-rw-r--r--libs/ardour/automatable.cc31
-rw-r--r--libs/ardour/automation_list.cc5
-rw-r--r--libs/ardour/debug.cc1
-rw-r--r--libs/ardour/delivery.cc59
-rw-r--r--libs/ardour/directory_names.cc1
-rw-r--r--libs/ardour/diskstream.cc31
-rw-r--r--libs/ardour/enums.cc9
-rw-r--r--libs/ardour/event_type_map.cc43
-rw-r--r--libs/ardour/globals.cc3
-rw-r--r--libs/ardour/internal_send.cc4
-rw-r--r--libs/ardour/pannable.cc173
-rw-r--r--libs/ardour/panner.cc1513
-rw-r--r--libs/ardour/panner_shell.cc6
-rw-r--r--libs/ardour/port_insert.cc4
-rw-r--r--libs/ardour/route.cc62
-rw-r--r--libs/ardour/send.cc4
-rw-r--r--libs/ardour/session.cc6
-rw-r--r--libs/ardour/session_state.cc29
-rw-r--r--libs/ardour/speakers.cc122
-rw-r--r--libs/ardour/thread_buffers.cc5
-rw-r--r--libs/ardour/vbap.cc240
-rw-r--r--libs/ardour/vbap_speakers.cc658
-rw-r--r--libs/ardour/wscript9
-rw-r--r--libs/evoral/evoral/ControlList.hpp2
-rw-r--r--libs/evoral/src/ControlList.cpp4
-rw-r--r--libs/gtkmm2ext/gtkmm2ext/utils.h3
-rw-r--r--libs/gtkmm2ext/utils.cc9
-rw-r--r--libs/panners/1in2out/panner_1in2out.cc223
-rw-r--r--libs/panners/1in2out/panner_1in2out.h32
-rw-r--r--libs/panners/1in2out/wscript35
-rw-r--r--libs/panners/2in2out/panner_2in2out.cc82
-rw-r--r--libs/panners/2in2out/panner_2in2out.h4
-rw-r--r--libs/panners/2in2out/wscript6
-rw-r--r--libs/panners/vbap/vbap.cc183
-rw-r--r--libs/panners/vbap/vbap.h26
-rw-r--r--libs/panners/vbap/vbap_speakers.cc3
-rw-r--r--libs/panners/vbap/wscript35
-rw-r--r--libs/panners/wscript15
-rw-r--r--libs/pbd/pbd/controllable_descriptor.h1
-rw-r--r--libs/surfaces/mackie/mackie_control_protocol.cc27
-rw-r--r--libs/surfaces/mackie/route_signal.cc4
-rw-r--r--libs/surfaces/osc/osc.cc4
58 files changed, 1190 insertions, 3147 deletions
diff --git a/libs/ardour/ardour/automatable.h b/libs/ardour/ardour/automatable.h
index 541e9d562d..c9e900e6a9 100644
--- a/libs/ardour/ardour/automatable.h
+++ b/libs/ardour/ardour/automatable.h
@@ -44,7 +44,6 @@ class Automatable : virtual public Evoral::ControlSet
public:
Automatable(Session&);
Automatable (const Automatable& other);
- Automatable();
virtual ~Automatable() {}
@@ -93,6 +92,8 @@ public:
typedef Evoral::ControlSet::Controls Controls;
+ static const std::string xml_node_name;
+
int set_automation_xml_state (const XMLNode&, Evoral::Parameter default_param);
XMLNode& get_automation_xml_state();
diff --git a/libs/ardour/ardour/debug.h b/libs/ardour/ardour/debug.h
index 43fb468b7a..69421662f3 100644
--- a/libs/ardour/ardour/debug.h
+++ b/libs/ardour/ardour/debug.h
@@ -48,6 +48,7 @@ namespace PBD {
extern uint64_t Monitor;
extern uint64_t Solo;
extern uint64_t AudioPlayback;
+ extern uint64_t Panning;
}
}
diff --git a/libs/ardour/ardour/delivery.h b/libs/ardour/ardour/delivery.h
index e1aa389df7..99893976ba 100644
--- a/libs/ardour/ardour/delivery.h
+++ b/libs/ardour/ardour/delivery.h
@@ -30,7 +30,9 @@ namespace ARDOUR {
class BufferSet;
class IO;
class MuteMaster;
+class PannerShell;
class Panner;
+class Pannable;
class Delivery : public IOProcessor
{
@@ -52,11 +54,11 @@ public:
/* Delivery to an existing output */
- Delivery (Session& s, boost::shared_ptr<IO> io, boost::shared_ptr<MuteMaster> mm, const std::string& name, Role);
+ Delivery (Session& s, boost::shared_ptr<IO> io, boost::shared_ptr<Pannable>, boost::shared_ptr<MuteMaster> mm, const std::string& name, Role);
/* Delivery to a new output owned by this object */
- Delivery (Session& s, boost::shared_ptr<MuteMaster> mm, const std::string& name, Role);
+ Delivery (Session& s, boost::shared_ptr<Pannable>, boost::shared_ptr<MuteMaster> mm, const std::string& name, Role);
~Delivery ();
bool set_name (const std::string& name);
@@ -90,15 +92,14 @@ public:
static int disable_panners (void);
static int reset_panners (void);
- boost::shared_ptr<Panner> panner() const { return _panner; }
+ boost::shared_ptr<PannerShell> panner_shell() const { return _panshell; }
+ boost::shared_ptr<Panner> panner() const;
void reset_panner ();
void defer_pan_reset ();
void allow_pan_reset ();
uint32_t pans_required() const { return _configured_input.n_audio(); }
- void start_pan_touch (uint32_t which, double when);
- void end_pan_touch (uint32_t which, bool mark, double when);
protected:
Role _role;
@@ -108,7 +109,7 @@ public:
bool _no_outs_cuz_we_no_monitor;
boost::shared_ptr<MuteMaster> _mute_master;
bool no_panner_reset;
- boost::shared_ptr<Panner> _panner;
+ boost::shared_ptr<PannerShell> _panshell;
static bool panners_legal;
static PBD::Signal0<int> PannersLegal;
diff --git a/libs/ardour/ardour/directory_names.h b/libs/ardour/ardour/directory_names.h
index 9cce077ad0..a5fd6dc754 100644
--- a/libs/ardour/ardour/directory_names.h
+++ b/libs/ardour/ardour/directory_names.h
@@ -21,6 +21,7 @@ extern const char* const route_templates_dir_name;
extern const char* const surfaces_dir_name;
extern const char* const user_config_dir_name;
extern const char* const stub_dir_name;
+extern const char* const panner_dir_name;
};
diff --git a/libs/ardour/ardour/internal_send.h b/libs/ardour/ardour/internal_send.h
index 903e4673df..53c6686887 100644
--- a/libs/ardour/ardour/internal_send.h
+++ b/libs/ardour/ardour/internal_send.h
@@ -28,7 +28,7 @@ namespace ARDOUR {
class InternalSend : public Send
{
public:
- InternalSend (Session&, boost::shared_ptr<MuteMaster>, boost::shared_ptr<Route> send_to, Delivery::Role role);
+ InternalSend (Session&, boost::shared_ptr<Pannable>, boost::shared_ptr<MuteMaster>, boost::shared_ptr<Route> send_to, Delivery::Role role);
virtual ~InternalSend ();
std::string display_name() const;
diff --git a/libs/ardour/ardour/pannable.h b/libs/ardour/ardour/pannable.h
index e6ed2e41c1..dff64cec4b 100644
--- a/libs/ardour/ardour/pannable.h
+++ b/libs/ardour/ardour/pannable.h
@@ -24,6 +24,7 @@
#include <boost/shared_ptr.hpp>
+#include "pbd/stateful.h"
#include "evoral/Parameter.hpp"
#include "ardour/automatable.h"
@@ -33,8 +34,9 @@ namespace ARDOUR {
class Session;
class AutomationControl;
+class Panner;
-struct Pannable : public Automatable, public SessionHandleRef {
+struct Pannable : public PBD::Stateful, public Automatable, public SessionHandleRef {
Pannable (Session& s);
boost::shared_ptr<AutomationControl> pan_azimuth_control;
@@ -42,6 +44,9 @@ struct Pannable : public Automatable, public SessionHandleRef {
boost::shared_ptr<AutomationControl> pan_width_control;
boost::shared_ptr<AutomationControl> pan_frontback_control;
boost::shared_ptr<AutomationControl> pan_lfe_control;
+
+ boost::shared_ptr<Panner> panner() const { return _panner; }
+ void set_panner(boost::shared_ptr<Panner>);
Session& session() { return _session; }
@@ -66,10 +71,21 @@ struct Pannable : public Automatable, public SessionHandleRef {
bool writing() const { return _auto_state == Write; }
bool touch_enabled() const { return _auto_state == Touch; }
+ XMLNode& get_state ();
+ XMLNode& state (bool full_state);
+ int set_state (const XMLNode&, int version);
+
+ bool has_state() const { return _has_state; }
+
protected:
+ boost::shared_ptr<Panner> _panner;
AutoState _auto_state;
AutoStyle _auto_style;
gint _touching;
+ bool _has_state;
+ uint32_t _responding_to_control_auto_state_change;
+
+ void control_auto_state_changed (AutoState);
};
} // namespace
diff --git a/libs/ardour/ardour/panner.h b/libs/ardour/ardour/panner.h
index b07167862e..f43be2dbbc 100644
--- a/libs/ardour/ardour/panner.h
+++ b/libs/ardour/ardour/panner.h
@@ -1,5 +1,5 @@
/*
- Copyright (C) 2004 Paul Davis
+ Copyright (C) 2004-2011 Paul Davis
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -26,116 +26,75 @@
#include <string>
#include <iostream>
-#include "pbd/stateful.h"
-#include "pbd/controllable.h"
#include "pbd/cartesian.h"
+#include "pbd/signals.h"
+#include "pbd/stateful.h"
#include "ardour/types.h"
#include "ardour/automation_control.h"
-#include "ardour/processor.h"
+#include "ardour/automatable.h"
namespace ARDOUR {
class Session;
-class Panner;
+class Pannable;
class BufferSet;
class AudioBuffer;
class Speakers;
-class StreamPanner : public PBD::Stateful
+class Panner : public PBD::Stateful, public PBD::ScopedConnectionList
{
public:
- StreamPanner (Panner& p, Evoral::Parameter param);
- ~StreamPanner ();
-
- void set_muted (bool yn);
- bool muted() const { return _muted; }
-
- const PBD::AngularVector& get_position() const { return _angles; }
- const PBD::AngularVector& get_effective_position() const { return _effective_angles; }
- void set_position (const PBD::AngularVector&, bool link_call = false);
- void set_diffusion (double);
-
- void distribute (AudioBuffer &, BufferSet &, gain_t, pframes_t);
- void distribute_automated (AudioBuffer &, BufferSet &, framepos_t, framepos_t, pframes_t, pan_t **);
-
- /* the basic StreamPanner API */
-
- /**
- * 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 Number of frames in the input.
- */
- virtual void do_distribute (AudioBuffer& src, BufferSet& obufs, gain_t gain_coeff, pframes_t nframes) = 0;
- virtual void do_distribute_automated (AudioBuffer& src, BufferSet& obufs,
- framepos_t start, framepos_t end, pframes_t nframes,
- pan_t** buffers) = 0;
-
- boost::shared_ptr<AutomationControl> pan_control() { return _control; }
-
- PBD::Signal0<void> Changed; /* for position or diffusion */
- PBD::Signal0<void> StateChanged; /* for mute, mono */
-
- int set_state (const XMLNode&, int version);
- virtual XMLNode& state (bool full_state) = 0;
-
- Panner & get_parent() { return parent; }
-
- /* old school automation loading */
- virtual int load (std::istream&, std::string path, uint32_t&) = 0;
-
- struct PanControllable : public AutomationControl {
- PanControllable (Session& s, std::string name, StreamPanner* p, Evoral::Parameter param)
- : AutomationControl (s, param,
- boost::shared_ptr<AutomationList>(new AutomationList(param)), name)
- , streampanner (p)
- { assert (param.type() == PanAutomation); }
-
- AutomationList* alist() { return (AutomationList*)_list.get(); }
- StreamPanner* streampanner;
-
- void set_value (double);
- double get_value (void) const;
- double lower () const;
- };
-
- protected:
- friend class Panner;
- Panner& parent;
+ Panner (boost::shared_ptr<Pannable>);
+ ~Panner ();
- void set_mono (bool);
-
- PBD::AngularVector _angles;
- PBD::AngularVector _effective_angles;
- double _diffusion;
+ virtual ChanCount in() const = 0;
+ virtual ChanCount out() const = 0;
- bool _muted;
- bool _mono;
+ virtual void configure_io (ARDOUR::ChanCount in, ARDOUR::ChanCount out) {}
+
+ /* derived implementations of these methods must indicate
+ whether it is legal for a Controllable to use the
+ value of the argument (post-call) in a call to
+ Controllable::set_value().
+
+ they have a choice of:
+
+ * return true, leave argument unchanged
+ * return true, modify argument
+ * return false
+
+ */
+
+ virtual bool clamp_position (double&) { return true; }
+ virtual bool clamp_width (double&) { return true; }
+ virtual bool clamp_elevation (double&) { return true; }
+
+ virtual void set_position (double) { }
+ virtual void set_width (double) { }
+ virtual void set_elevation (double) { }
- boost::shared_ptr<AutomationControl> _control;
+ virtual double position () const { return 0.0; }
+ virtual double width () const { return 0.0; }
+ virtual double elevation () const { return 0.0; }
- XMLNode& get_state ();
+ virtual void reset() {}
- /* Update internal parameters based on this.angles */
- virtual void update () = 0;
-};
+ virtual bool bypassed() const { return _bypassed; }
+ virtual void set_bypassed (bool yn);
-class BaseStereoPanner : public StreamPanner
-{
- public:
- BaseStereoPanner (Panner&, Evoral::Parameter param);
- ~BaseStereoPanner ();
+ virtual bool is_mono () const { return _mono; }
+ virtual void set_mono (bool);
+
+ void set_automation_state (AutoState);
+ AutoState automation_state() const;
+ void set_automation_style (AutoStyle);
+ AutoStyle automation_style() const;
- /* this class just leaves the pan law itself to be defined
- by the update(), do_distribute_automated()
- methods. derived classes also need a factory method
- and a type name. See EqualPowerStereoPanner as an example.
- */
+ virtual std::set<Evoral::Parameter> what_can_be_automated() const;
+ virtual std::string describe_parameter (Evoral::Parameter);
- void do_distribute (AudioBuffer& src, BufferSet& obufs, gain_t gain_coeff, pframes_t nframes);
+ bool touching() const;
static double azimuth_to_lr_fract (double azi) {
/* 180.0 degrees=> left => 0.0 */
@@ -159,176 +118,70 @@ class BaseStereoPanner : public StreamPanner
return rint (180.0 - (fract * 180.0));
}
- /* old school automation loading */
-
- int load (std::istream&, std::string path, uint32_t&);
-
- protected:
- float left;
- float right;
- float desired_left;
- float desired_right;
- float left_interp;
- float right_interp;
-};
-
-class EqualPowerStereoPanner : public BaseStereoPanner
-{
- public:
- EqualPowerStereoPanner (Panner&, Evoral::Parameter param);
- ~EqualPowerStereoPanner ();
-
- void do_distribute_automated (AudioBuffer& src, BufferSet& obufs,
- framepos_t start, framepos_t end, pframes_t nframes,
- pan_t** buffers);
-
- void get_current_coefficients (pan_t*) const;
- void get_desired_coefficients (pan_t*) const;
-
- static StreamPanner* factory (Panner&, Evoral::Parameter param, Speakers&);
- static std::string name;
-
- XMLNode& state (bool full_state);
- XMLNode& get_state (void);
- int set_state (const XMLNode&, int version);
-
- private:
- void update ();
-};
-
-/** Class to pan from some number of inputs to some number of outputs.
- * This class has a number of StreamPanners, one for each input.
- */
-class Panner : public SessionObject, public Automatable
-{
-public:
- struct Output {
- PBD::AngularVector position;
- pan_t current_pan;
- pan_t desired_pan;
-
- Output (const PBD::AngularVector& a)
- : position (a), current_pan (0), desired_pan (0) {}
-
- };
-
- Panner (std::string name, Session&);
- virtual ~Panner ();
-
- void clear_panners ();
- bool empty() const { return _streampanners.empty(); }
-
- void set_automation_state (AutoState);
- AutoState automation_state() const;
- void set_automation_style (AutoStyle);
- AutoStyle automation_style() const;
- bool touching() const;
-
- std::string describe_parameter (Evoral::Parameter param);
-
- bool can_support_io_configuration (const ChanCount& /*in*/, ChanCount& /*out*/) const { return true; };
+ /**
+ * Pan some input buffers to a number of output buffers.
+ *
+ * @param ibufs Input buffers (one per panner input)
+ * @param obufs Output buffers (one per panner output).
+ * @param gain_coeff fixed, additional gain coefficient to apply to output samples.
+ * @param nframes Number of frames in the input.
+ *
+ * Derived panners can choose to implement these if they need to gain more control over the panning algorithm.
+ * the default is to (1) check if _mono is true, and if so, just deliver .. (2) otherwise, call
+ * distribute_one() or distribute_one_automated() on each input buffer to deliver it to each output
+ * buffer.
+ *
+ * If a panner does not need to override this default behaviour, it can just implement
+ * distribute_one() and distribute_one_automated() (below).
+ */
+ virtual void distribute (BufferSet& ibufs, BufferSet& obufs, gain_t gain_coeff, pframes_t nframes);
+ virtual void distribute_automated (BufferSet& ibufs, BufferSet& obufs,
+ framepos_t start, framepos_t end, pframes_t nframes,
+ pan_t** buffers);
- /// The fundamental Panner function
- void run (BufferSet& src, BufferSet& dest, framepos_t start_frame, framepos_t end_frames, pframes_t nframes);
+ PBD::Signal0<void> Changed; /* for positional info */
+ PBD::Signal0<void> StateChanged; /* for mute, mono */
- bool bypassed() const { return _bypassed; }
- void set_bypassed (bool yn);
- bool mono () const { return _mono; }
- void set_mono (bool);
+ int set_state (const XMLNode&, int version);
+ virtual XMLNode& state (bool full_state) = 0;
- StreamPanner* add ();
- void remove (uint32_t which);
- void reset (uint32_t noutputs, uint32_t npans);
- void reset_streampanner (uint32_t which_panner);
- void reset_to_default ();
+ boost::shared_ptr<Pannable> pannable() const { return _pannable; }
- XMLNode& get_state (void);
- XMLNode& state (bool full);
- int set_state (const XMLNode&, int version);
+ //virtual std::string describe_parameter (Evoral::Parameter);
+ //virtual std::string value_as_string (Evoral::Parameter, double val);
static bool equivalent (pan_t a, pan_t b) {
return fabsf (a - b) < 0.002; // about 1 degree of arc for a stereo panner
}
+
static bool equivalent (const PBD::AngularVector& a, const PBD::AngularVector& b) {
/* XXX azimuth only, at present */
return fabs (a.azi - b.azi) < 1.0;
}
- void move_output (uint32_t, float x, float y);
- uint32_t nouts() const { return outputs.size(); }
- Output& output (uint32_t n) { return outputs[n]; }
-
- enum LinkDirection {
- SameDirection,
- OppositeDirection
- };
-
- LinkDirection link_direction() const { return _link_direction; }
- void set_link_direction (LinkDirection);
-
- bool linked() const { return _linked; }
- void set_linked (bool yn);
-
- StreamPanner &streampanner( uint32_t n ) const { assert( n < _streampanners.size() ); return *_streampanners[n]; }
- uint32_t npanners() const { return _streampanners.size(); }
-
- PBD::Signal0<void> Changed; /* panner and/or outputs count changed */
- PBD::Signal0<void> LinkStateChanged;
- PBD::Signal0<void> StateChanged; /* for bypass */
-
- /* only StreamPanner should call these */
-
- void set_position (const PBD::AngularVector&, StreamPanner& orig);
-
- /* old school automation */
-
- int load ();
-
- boost::shared_ptr<AutomationControl> pan_control (int id, uint32_t chan=0) {
- return automation_control (Evoral::Parameter (PanAutomation, chan, id));
- }
-
- boost::shared_ptr<const AutomationControl> pan_control (int id, uint32_t chan=0) const {
- return automation_control (Evoral::Parameter (PanAutomation, chan, id));
- }
-
- boost::shared_ptr<AutomationControl> direction_control () {
- return automation_control (Evoral::Parameter (PanAutomation, 0, 100));
- }
-
- boost::shared_ptr<AutomationControl> width_control () {
- return automation_control (Evoral::Parameter (PanAutomation, 0, 200));
- }
-
- void set_stereo_position (double);
- void set_stereo_width (double);
- bool set_stereo_pan (double pos, double width);
-
- static std::string value_as_string (double);
-
- private:
- /* disallow copy construction */
- Panner (Panner const &);
-
- void distribute_no_automation(BufferSet& src, BufferSet& dest, pframes_t nframes, gain_t gain_coeff);
- std::vector<StreamPanner*> _streampanners; ///< one StreamPanner per input
- std::vector<Output> outputs;
- uint32_t current_outs;
- bool _linked;
- bool _bypassed;
- bool _mono;
- LinkDirection _link_direction;
-
- static float current_automation_version_number;
+ protected:
+ boost::shared_ptr<Pannable> _pannable;
+ bool _mono;
+ bool _bypassed;
- void setup_speakers (uint32_t nouts);
- void setup_meta_controls ();
+ XMLNode& get_state ();
- /* old school automation handling */
- std::string automation_path;
+ virtual void distribute_one (AudioBuffer&, BufferSet& obufs, gain_t gain_coeff, pframes_t nframes, uint32_t which) = 0;
+ virtual void distribute_one_automated (AudioBuffer&, BufferSet& obufs,
+ framepos_t start, framepos_t end, pframes_t nframes,
+ pan_t** buffers, uint32_t which) = 0;
};
-} // namespace ARDOUR
+} // namespace
+
+extern "C" {
+ struct PanPluginDescriptor {
+ std::string name;
+ int32_t in;
+ int32_t out;
+ ARDOUR::Panner* (*factory)(boost::shared_ptr<ARDOUR::Pannable>, ARDOUR::Speakers&);
+ };
+}
-#endif /*__ardour_panner_h__ */
+#endif /* __ardour_panner_h__ */
diff --git a/libs/ardour/ardour/port_insert.h b/libs/ardour/ardour/port_insert.h
index d460aa5546..959e46b4a5 100644
--- a/libs/ardour/ardour/port_insert.h
+++ b/libs/ardour/ardour/port_insert.h
@@ -37,13 +37,14 @@ class Session;
class IO;
class Delivery;
class MuteMaster;
+class Pannable;
/** Port inserts: send output to a Jack port, pick up input at a Jack port
*/
class PortInsert : public IOProcessor
{
public:
- PortInsert (Session&, boost::shared_ptr<MuteMaster> mm);
+ PortInsert (Session&, boost::shared_ptr<Pannable>, boost::shared_ptr<MuteMaster> mm);
~PortInsert ();
XMLNode& state(bool full);
diff --git a/libs/ardour/ardour/route.h b/libs/ardour/ardour/route.h
index 90f5664eba..f90ec77b7e 100644
--- a/libs/ardour/ardour/route.h
+++ b/libs/ardour/ardour/route.h
@@ -54,11 +54,13 @@ class Amp;
class Delivery;
class IOProcessor;
class Panner;
+class PannerShell;
class Processor;
class RouteGroup;
class Send;
class InternalReturn;
class MonitorProcessor;
+class Pannable;
class CapturingProcessor;
class Route : public SessionObject, public Automatable, public RouteGroupMember, public GraphNode
@@ -365,8 +367,10 @@ class Route : public SessionObject, public Automatable, public RouteGroupMember,
here.
*/
- boost::shared_ptr<Panner> panner() const;
+ boost::shared_ptr<Panner> panner() const; /* may return null */
+ boost::shared_ptr<PannerShell> panner_shell() const;
boost::shared_ptr<AutomationControl> gain_control() const;
+ boost::shared_ptr<Pannable> pannable() const;
void automation_snapshot (framepos_t now, bool force=false);
void protect_automation ();
@@ -423,6 +427,7 @@ class Route : public SessionObject, public Automatable, public RouteGroupMember,
boost::shared_ptr<Delivery> _monitor_send;
boost::shared_ptr<InternalReturn> _intreturn;
boost::shared_ptr<MonitorProcessor> _monitor_control;
+ boost::shared_ptr<Pannable> _pannable;
Flag _flags;
int _pending_declick;
diff --git a/libs/ardour/ardour/send.h b/libs/ardour/ardour/send.h
index bf5c5d7370..40045d6b85 100644
--- a/libs/ardour/ardour/send.h
+++ b/libs/ardour/ardour/send.h
@@ -36,7 +36,7 @@ class Amp;
class Send : public Delivery
{
public:
- Send (Session&, boost::shared_ptr<MuteMaster>, Delivery::Role r = Delivery::Send);
+ Send (Session&, boost::shared_ptr<Pannable> pannable, boost::shared_ptr<MuteMaster>, Delivery::Role r = Delivery::Send);
virtual ~Send ();
uint32_t bit_slot() const { return _bitslot; }
diff --git a/libs/ardour/ardour/speakers.h b/libs/ardour/ardour/speakers.h
index bae8cb96db..f6e6e22848 100644
--- a/libs/ardour/ardour/speakers.h
+++ b/libs/ardour/ardour/speakers.h
@@ -23,12 +23,15 @@
#include <iostream>
#include <pbd/signals.h>
+#include <pbd/stateful.h>
#include "ardour/speaker.h"
+class XMLNode;
+
namespace ARDOUR {
-class Speakers {
+class Speakers : public PBD::Stateful {
public:
Speakers ();
virtual ~Speakers ();
@@ -38,12 +41,17 @@ public:
virtual void move_speaker (int id, const PBD::AngularVector& new_position);
virtual void clear_speakers ();
+ void setup_default_speakers (uint32_t nspeakers);
+
std::vector<Speaker>& speakers() { return _speakers; }
void dump_speakers (std::ostream&);
- PBD::Signal0<void> Changed;
+ XMLNode& get_state ();
+ int set_state (const XMLNode&, int version);
+ PBD::Signal0<void> Changed;
+
protected:
std::vector<Speaker> _speakers;
diff --git a/libs/ardour/ardour/types.h b/libs/ardour/ardour/types.h
index 78e21d5a9d..744c06f552 100644
--- a/libs/ardour/ardour/types.h
+++ b/libs/ardour/ardour/types.h
@@ -122,24 +122,27 @@ namespace ARDOUR {
InsertMergeExtend // extend new (or old) to the range of old+new
};
- /** See parameter.h
- * XXX: I don't think/hope these hex values matter anymore.
+ /** See evoral/Parameter.hpp
*/
enum AutomationType {
- NullAutomation = 0x0,
- GainAutomation = 0x1,
- PanAutomation = 0x2,
- PluginAutomation = 0x4,
- SoloAutomation = 0x8,
- MuteAutomation = 0x10,
- MidiCCAutomation = 0x20,
- MidiPgmChangeAutomation = 0x21,
- MidiPitchBenderAutomation = 0x22,
- MidiChannelPressureAutomation = 0x23,
- MidiSystemExclusiveAutomation = 0x24,
- FadeInAutomation = 0x40,
- FadeOutAutomation = 0x80,
- EnvelopeAutomation = 0x100
+ NullAutomation,
+ GainAutomation,
+ PanAzimuthAutomation,
+ PanElevationAutomation,
+ PanWidthAutomation,
+ PanFrontBackAutomation,
+ PanLFEAutomation,
+ PluginAutomation,
+ SoloAutomation,
+ MuteAutomation,
+ MidiCCAutomation,
+ MidiPgmChangeAutomation,
+ MidiPitchBenderAutomation,
+ MidiChannelPressureAutomation,
+ MidiSystemExclusiveAutomation,
+ FadeInAutomation,
+ FadeOutAutomation,
+ EnvelopeAutomation
};
enum AutoState {
diff --git a/libs/ardour/ardour/vbap.h b/libs/ardour/ardour/vbap.h
deleted file mode 100644
index 419930c574..0000000000
--- a/libs/ardour/ardour/vbap.h
+++ /dev/null
@@ -1,70 +0,0 @@
-/*
- Copyright (C) 2010 Paul Davis
-
- This program is free software; you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation; either version 2 of the License, or
- (at your option) any later version.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with this program; if not, write to the Free Software
- Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
-*/
-
-#ifndef __libardour_vbap_h__
-#define __libardour_vbap_h__
-
-#include <string>
-#include <map>
-
-#include "ardour/panner.h"
-#include "ardour/vbap_speakers.h"
-
-namespace ARDOUR {
-
-class Speakers;
-
-class VBAPanner : public StreamPanner {
-public:
- VBAPanner (Panner& parent, Evoral::Parameter param, Speakers& s);
- ~VBAPanner ();
-
- static StreamPanner* factory (Panner& parent, Evoral::Parameter param, Speakers& s);
- static std::string name;
-
- void do_distribute (AudioBuffer&, BufferSet& obufs, gain_t gain_coeff, pframes_t nframes);
- void do_distribute_automated (AudioBuffer& src, BufferSet& obufs,
- framepos_t start, framepos_t end, pframes_t nframes, pan_t** buffers);
-
- void set_azimuth_elevation (double azimuth, double elevation);
-
- XMLNode& state (bool full_state);
- XMLNode& get_state ();
- int set_state (const XMLNode&, int version);
-
- /* there never was any old-school automation */
-
- int load (std::istream&, std::string path, uint32_t&) { return 0; }
-
-private:
- bool _dirty;
- double gains[3];
- double desired_gains[3];
- int outputs[3];
- int desired_outputs[3];
-
- VBAPSpeakers& _speakers;
-
- void compute_gains (double g[3], int ls[3], int azi, int ele);
-
- void update ();
-};
-
-} /* namespace */
-
-#endif /* __libardour_vbap_h__ */
diff --git a/libs/ardour/ardour/vbap_speakers.h b/libs/ardour/ardour/vbap_speakers.h
deleted file mode 100644
index 80989646bc..0000000000
--- a/libs/ardour/ardour/vbap_speakers.h
+++ /dev/null
@@ -1,107 +0,0 @@
-/*
- Copyright (C) 2010 Paul Davis
-
- This program is free software; you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation; either version 2 of the License, or
- (at your option) any later version.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with this program; if not, write to the Free Software
- Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
-*/
-
-#ifndef __libardour_vbap_speakers_h__
-#define __libardour_vbap_speakers_h__
-
-#include <string>
-#include <vector>
-
-#include <boost/utility.hpp>
-
-#include <pbd/signals.h>
-
-#include "ardour/panner.h"
-#include "ardour/speakers.h"
-
-namespace ARDOUR {
-
-class Speakers;
-
-class VBAPSpeakers : public boost::noncopyable {
-public:
- typedef std::vector<double> dvector;
-
- const dvector matrix (int tuple) const { return _matrices[tuple]; }
- int speaker_for_tuple (int tuple, int which) const { return _speaker_tuples[tuple][which]; }
-
- int n_tuples () const { return _matrices.size(); }
- int dimension() const { return _dimension; }
-
- static VBAPSpeakers& instance (Speakers&);
-
- ~VBAPSpeakers ();
-
-private:
- static VBAPSpeakers* _instance;
- static const double MIN_VOL_P_SIDE_LGTH = 0.01;
- int _dimension;
- std::vector<Speaker>& _speakers;
- PBD::ScopedConnection speaker_connection;
-
- VBAPSpeakers (Speakers&);
-
- struct azimuth_sorter {
- bool operator() (const Speaker& s1, const Speaker& s2) {
- return s1.angles().azi < s2.angles().azi;
- }
- };
-
- struct twoDmatrix : public dvector {
- twoDmatrix() : dvector (4, 0.0) {}
- };
-
- struct threeDmatrix : public dvector {
- threeDmatrix() : dvector (9, 0.0) {}
- };
-
- struct tmatrix : public dvector {
- tmatrix() : dvector (3, 0.0) {}
- };
-
- std::vector<dvector> _matrices; /* holds matrices for a given speaker combinations */
- std::vector<tmatrix> _speaker_tuples; /* holds speakers IDs for a given combination */
-
- /* A struct for all loudspeakers */
- struct ls_triplet_chain {
- int ls_nos[3];
- float inv_mx[9];
- struct ls_triplet_chain *next;
- };
-
- static float vec_angle(PBD::CartesianVector v1, PBD::CartesianVector v2);
- static float vec_length(PBD::CartesianVector v1);
- static float vec_prod(PBD::CartesianVector v1, PBD::CartesianVector v2);
- static float vol_p_side_lgth(int i, int j,int k, const std::vector<Speaker>&);
- static void cross_prod(PBD::CartesianVector v1,PBD::CartesianVector v2, PBD::CartesianVector *res);
-
- void update ();
- int any_ls_inside_triplet (int a, int b, int c);
- void add_ldsp_triplet (int i, int j, int k, struct ls_triplet_chain **ls_triplets);
- int lines_intersect (int i,int j,int k,int l);
- void calculate_3x3_matrixes (struct ls_triplet_chain *ls_triplets);
- void choose_speaker_triplets (struct ls_triplet_chain **ls_triplets);
- void choose_speaker_pairs ();
- void sort_2D_lss (int* sorted_lss);
- int calc_2D_inv_tmatrix (double azi1,double azi2, double* inv_mat);
-
-};
-
-} /* namespace */
-
-#endif /* __libardour_vbap_speakers_h__ */
diff --git a/libs/ardour/audioengine.cc b/libs/ardour/audioengine.cc
index ef94d0d740..f6557fbc4f 100644
--- a/libs/ardour/audioengine.cc
+++ b/libs/ardour/audioengine.cc
@@ -99,7 +99,9 @@ AudioEngine::AudioEngine (string client_name, string session_uuid)
Evoral::Parameter p(NullAutomation);
p = EventTypeMap::instance().new_parameter(NullAutomation);
p = EventTypeMap::instance().new_parameter(GainAutomation);
- p = EventTypeMap::instance().new_parameter(PanAutomation);
+ p = EventTypeMap::instance().new_parameter(PanAzimuthAutomation);
+ p = EventTypeMap::instance().new_parameter(PanElevationAutomation);
+ p = EventTypeMap::instance().new_parameter(PanWidthAutomation);
p = EventTypeMap::instance().new_parameter(PluginAutomation);
p = EventTypeMap::instance().new_parameter(SoloAutomation);
p = EventTypeMap::instance().new_parameter(MuteAutomation);
diff --git a/libs/ardour/auditioner.cc b/libs/ardour/auditioner.cc
index d7605f26e1..209d26dc30 100644
--- a/libs/ardour/auditioner.cc
+++ b/libs/ardour/auditioner.cc
@@ -30,6 +30,7 @@
#include "ardour/auditioner.h"
#include "ardour/audioplaylist.h"
#include "ardour/audio_port.h"
+#include "ardour/panner_shell.h"
#include "ardour/panner.h"
#include "ardour/data_type.h"
#include "ardour/region_factory.h"
@@ -139,7 +140,8 @@ Auditioner::audition_current_playlist ()
/* force a panner reset now that we have all channels */
- _main_outs->panner()->reset (n_outputs().n_audio(), _diskstream->n_channels().n_audio());
+ _main_outs->panner_shell()->configure_io (ChanCount (DataType::AUDIO, _diskstream->n_channels().n_audio()),
+ ChanCount (DataType::AUDIO, n_outputs().n_audio()));
g_atomic_int_set (&_auditioning, 1);
}
diff --git a/libs/ardour/automatable.cc b/libs/ardour/automatable.cc
index 03e5ad97c3..0d24135710 100644
--- a/libs/ardour/automatable.cc
+++ b/libs/ardour/automatable.cc
@@ -35,7 +35,9 @@
#include "ardour/amp.h"
#include "ardour/event_type_map.h"
#include "ardour/midi_track.h"
+#include "ardour/pannable.h"
#include "ardour/panner.h"
+#include "ardour/pan_controllable.h"
#include "ardour/plugin_insert.h"
#include "ardour/session.h"
@@ -46,6 +48,7 @@ using namespace ARDOUR;
using namespace PBD;
framecnt_t Automatable::_automation_interval = 0;
+const string Automatable::xml_node_name = X_("Automation");
Automatable::Automatable(Session& session)
: _a_session(session)
@@ -182,9 +185,6 @@ Automatable::describe_parameter (Evoral::Parameter param)
if (param == Evoral::Parameter(GainAutomation)) {
return _("Fader");
- } else if (param.type() == PanAutomation) {
- /* ID's are zero-based, present them as 1-based */
- return (string_compose(_("Pan %1"), param.id() + 1));
} else if (param.type() == MidiCCAutomation) {
return string_compose("%1: %2 [%3]",
param.id() + 1, midi_name(param.id()), int(param.channel()) + 1);
@@ -255,19 +255,21 @@ Automatable::set_automation_xml_state (const XMLNode& node, Evoral::Parameter le
continue;
}
- boost::shared_ptr<AutomationList> al (new AutomationList(**niter, param));
+
if (!id_prop) {
warning << "AutomationList node without automation-id property, "
<< "using default: " << EventTypeMap::instance().to_symbol(legacy_param) << endmsg;
}
- boost::shared_ptr<Evoral::Control> existing = control(param);
+ boost::shared_ptr<AutomationControl> existing = automation_control (param);
+
if (existing) {
- existing->set_list(al);
+ existing->alist()->set_state (**niter, 3000);
} else {
- boost::shared_ptr<Evoral::Control> newcontrol = control_factory(param);
- add_control(newcontrol);
+ boost::shared_ptr<Evoral::Control> newcontrol = control_factory(param);
+ add_control (newcontrol);
+ boost::shared_ptr<AutomationList> al (new AutomationList(**niter, param));
newcontrol->set_list(al);
}
@@ -285,7 +287,7 @@ XMLNode&
Automatable::get_automation_xml_state ()
{
Glib::Mutex::Lock lm (control_lock());
- XMLNode* node = new XMLNode (X_("Automation"));
+ XMLNode* node = new XMLNode (Automatable::xml_node_name);
if (controls().empty()) {
return *node;
@@ -455,13 +457,12 @@ Automatable::control_factory(const Evoral::Parameter& param)
} else {
warning << "GainAutomation for non-Amp" << endl;
}
- } else if (param.type() == PanAutomation) {
- Panner* panner = dynamic_cast<Panner*>(this);
- if (panner) {
- StreamPanner& sp (panner->streampanner (param.channel()));
- control = new StreamPanner::PanControllable (_a_session, X_("direction"), &sp, param);
+ } else if (param.type() == PanAzimuthAutomation || param.type() == PanWidthAutomation || param.type() == PanElevationAutomation) {
+ Pannable* pannable = dynamic_cast<Pannable*>(this);
+ if (pannable) {
+ control = new PanControllable (_a_session, pannable->describe_parameter (param), pannable, param);
} else {
- warning << "PanAutomation for non-Panner" << endl;
+ warning << "PanAutomation for non-Pannable" << endl;
}
}
diff --git a/libs/ardour/automation_list.cc b/libs/ardour/automation_list.cc
index f4f4f95238..5f18bbcebb 100644
--- a/libs/ardour/automation_list.cc
+++ b/libs/ardour/automation_list.cc
@@ -124,7 +124,9 @@ AutomationList::create_curve_if_necessary()
{
switch (_parameter.type()) {
case GainAutomation:
- case PanAutomation:
+ case PanAzimuthAutomation:
+ case PanElevationAutomation:
+ case PanWidthAutomation:
case FadeInAutomation:
case FadeOutAutomation:
case EnvelopeAutomation:
@@ -184,7 +186,6 @@ AutomationList::set_automation_state (AutoState s)
Glib::Mutex::Lock lm (ControlList::_lock);
nascent.push_back (new NascentInfo (false));
}
-
automation_state_changed (s); /* EMIT SIGNAL */
}
}
diff --git a/libs/ardour/debug.cc b/libs/ardour/debug.cc
index 7ddb6ddcc8..2c413f407e 100644
--- a/libs/ardour/debug.cc
+++ b/libs/ardour/debug.cc
@@ -45,4 +45,5 @@ uint64_t PBD::DEBUG::MidiClock = PBD::new_debug_bit ("midiclock");
uint64_t PBD::DEBUG::Monitor = PBD::new_debug_bit ("monitor");
uint64_t PBD::DEBUG::Solo = PBD::new_debug_bit ("solo");
uint64_t PBD::DEBUG::AudioPlayback = PBD::new_debug_bit ("audioplayback");
+uint64_t PBD::DEBUG::Panning = PBD::new_debug_bit ("panning");
diff --git a/libs/ardour/delivery.cc b/libs/ardour/delivery.cc
index 80135baf28..5c535b8918 100644
--- a/libs/ardour/delivery.cc
+++ b/libs/ardour/delivery.cc
@@ -33,6 +33,8 @@
#include "ardour/meter.h"
#include "ardour/mute_master.h"
#include "ardour/panner.h"
+#include "ardour/panner_shell.h"
+#include "ardour/pannable.h"
#include "ardour/port.h"
#include "ardour/session.h"
#include "ardour/audioengine.h"
@@ -49,7 +51,8 @@ bool Delivery::panners_legal = false;
/* deliver to an existing IO object */
-Delivery::Delivery (Session& s, boost::shared_ptr<IO> io, boost::shared_ptr<MuteMaster> mm, const string& name, Role r)
+Delivery::Delivery (Session& s, boost::shared_ptr<IO> io, boost::shared_ptr<Pannable> pannable,
+ boost::shared_ptr<MuteMaster> mm, const string& name, Role r)
: IOProcessor(s, boost::shared_ptr<IO>(), (role_requires_output_ports (r) ? io : boost::shared_ptr<IO>()), name)
, _role (r)
, _output_buffers (new BufferSet())
@@ -59,7 +62,7 @@ Delivery::Delivery (Session& s, boost::shared_ptr<IO> io, boost::shared_ptr<Mute
, _mute_master (mm)
, no_panner_reset (false)
{
- _panner = boost::shared_ptr<Panner>(new Panner (_name, _session));
+ _panshell = boost::shared_ptr<PannerShell>(new PannerShell (_name, _session, pannable));
_display_to_user = false;
if (_output) {
@@ -71,7 +74,7 @@ Delivery::Delivery (Session& s, boost::shared_ptr<IO> io, boost::shared_ptr<Mute
/* deliver to a new IO object */
-Delivery::Delivery (Session& s, boost::shared_ptr<MuteMaster> mm, const string& name, Role r)
+Delivery::Delivery (Session& s, boost::shared_ptr<Pannable> pannable, boost::shared_ptr<MuteMaster> mm, const string& name, Role r)
: IOProcessor(s, false, (role_requires_output_ports (r) ? true : false), name)
, _role (r)
, _output_buffers (new BufferSet())
@@ -81,7 +84,7 @@ Delivery::Delivery (Session& s, boost::shared_ptr<MuteMaster> mm, const string&
, _mute_master (mm)
, no_panner_reset (false)
{
- _panner = boost::shared_ptr<Panner>(new Panner (_name, _session));
+ _panshell = boost::shared_ptr<PannerShell>(new PannerShell (_name, _session, pannable));
_display_to_user = false;
if (_output) {
@@ -228,6 +231,8 @@ Delivery::configure_io (ChanCount in, ChanCount out)
void
Delivery::run (BufferSet& bufs, framepos_t start_frame, framepos_t end_frame, pframes_t nframes, bool result_required)
{
+ boost::shared_ptr<Panner> panner;
+
assert (_output);
PortSet& ports (_output->ports());
@@ -279,11 +284,13 @@ Delivery::run (BufferSet& bufs, framepos_t start_frame, framepos_t end_frame, pf
Amp::apply_simple_gain (bufs, nframes, tgain);
}
- if (_panner && _panner->npanners() && !_panner->bypassed()) {
+ panner = _panshell->panner();
+
+ if (panner && !panner->bypassed()) {
// Use the panner to distribute audio to output port buffers
- _panner->run (bufs, output_buffers(), start_frame, end_frame, nframes);
+ _panshell->run (bufs, output_buffers(), start_frame, end_frame, nframes);
if (result_required) {
bufs.read_from (output_buffers (), nframes);
@@ -322,7 +329,7 @@ Delivery::state (bool full_state)
}
node.add_property("role", enum_2_string(_role));
- node.add_child_nocopy (_panner->state (full_state));
+ node.add_child_nocopy (_panshell->state (full_state));
return node;
}
@@ -346,7 +353,7 @@ Delivery::set_state (const XMLNode& node, int version)
XMLNode* pan_node = node.child (X_("Panner"));
if (pan_node) {
- _panner->set_state (*pan_node, version);
+ _panshell->set_state (*pan_node, version);
}
reset_panner ();
@@ -368,7 +375,7 @@ Delivery::reset_panner ()
ntargets = _configured_output.n_audio();
}
- _panner->reset (ntargets, pans_required());
+ _panshell->configure_io (ChanCount (DataType::AUDIO, pans_required()), ChanCount (DataType::AUDIO, ntargets));
}
} else {
panner_legal_c.disconnect ();
@@ -387,8 +394,10 @@ Delivery::panners_became_legal ()
ntargets = _configured_output.n_audio();
}
- _panner->reset (ntargets, pans_required());
+ _panshell->configure_io (ChanCount (DataType::AUDIO, pans_required()), ChanCount (DataType::AUDIO, ntargets));
+#ifdef PANNER_HACKS
_panner->load (); // automation
+#endif
panner_legal_c.disconnect ();
return 0;
}
@@ -421,25 +430,6 @@ Delivery::reset_panners ()
return *PannersLegal ();
}
-
-void
-Delivery::start_pan_touch (uint32_t which, double when)
-{
- if (which < _panner->npanners()) {
- _panner->pan_control(which)->start_touch(when);
- }
-}
-
-void
-Delivery::end_pan_touch (uint32_t which, bool mark, double when)
-{
- if (which < _panner->npanners()) {
- _panner->pan_control(which)->stop_touch(mark, when);
- }
-
-}
-
-
void
Delivery::flush_buffers (framecnt_t nframes, framepos_t time)
{
@@ -456,8 +446,7 @@ void
Delivery::transport_stopped (framepos_t now)
{
Processor::transport_stopped (now);
-
- _panner->transport_stopped (now);
+ _panshell->pannable()->transport_stopped (now);
if (_output) {
PortSet& ports (_output->ports());
@@ -533,7 +522,7 @@ Delivery::set_name (const std::string& name)
bool ret = IOProcessor::set_name (name);
if (ret) {
- ret = _panner->set_name (name);
+ ret = _panshell->set_name (name);
}
return ret;
@@ -547,3 +536,9 @@ Delivery::output_changed (IOChange change, void* /*src*/)
_output_buffers->attach_buffers (_output->ports ());
}
}
+
+boost::shared_ptr<Panner>
+Delivery::panner () const
+{
+ return _panshell->panner();
+}
diff --git a/libs/ardour/directory_names.cc b/libs/ardour/directory_names.cc
index d6e2f94455..18d43f7ec8 100644
--- a/libs/ardour/directory_names.cc
+++ b/libs/ardour/directory_names.cc
@@ -18,5 +18,6 @@ const char* const route_templates_dir_name = X_("route_templates");
const char* const surfaces_dir_name = X_("surfaces");
const char* const user_config_dir_name = X_("ardour3");
const char* const stub_dir_name = X_(".stubs");
+const char* const panner_dir_name = X_("panners");
}
diff --git a/libs/ardour/diskstream.cc b/libs/ardour/diskstream.cc
index bf77d7d6ad..df9d1a9b4d 100644
--- a/libs/ardour/diskstream.cc
+++ b/libs/ardour/diskstream.cc
@@ -46,6 +46,8 @@
#include "ardour/configuration.h"
#include "ardour/audiofilesource.h"
#include "ardour/send.h"
+#include "ardour/pannable.h"
+#include "ardour/panner_shell.h"
#include "ardour/playlist.h"
#include "ardour/cycle_timer.h"
#include "ardour/region.h"
@@ -458,18 +460,23 @@ Diskstream::playlist_ranges_moved (list< Evoral::RangeMove<framepos_t> > const &
}
/* move panner automation */
- boost::shared_ptr<Panner> p = _track->main_outs()->panner ();
- if (p) {
- for (uint32_t i = 0; i < p->npanners (); ++i) {
- boost::shared_ptr<AutomationList> pan_alist = p->streampanner(i).pan_control()->alist();
- XMLNode & before = pan_alist->get_state ();
- bool const things_moved = pan_alist->move_ranges (movements);
- if (things_moved) {
- _session.add_command (new MementoCommand<AutomationList> (
- *pan_alist.get(), &before, &pan_alist->get_state ()));
- }
- }
- }
+ boost::shared_ptr<Pannable> pannable = _track->pannable();
+ Evoral::ControlSet::Controls& c (pannable->controls());
+
+ for (Evoral::ControlSet::Controls::iterator ci = c.begin(); ci != c.end(); ++ci) {
+ boost::shared_ptr<AutomationControl> ac = boost::dynamic_pointer_cast<AutomationControl>(ci->second);
+ if (!ac) {
+ continue;
+ }
+ boost::shared_ptr<AutomationList> alist = ac->alist();
+
+ XMLNode & before = alist->get_state ();
+ bool const things_moved = alist->move_ranges (movements);
+ if (things_moved) {
+ _session.add_command (new MementoCommand<AutomationList> (
+ *alist.get(), &before, &alist->get_state ()));
+ }
+ }
/* move processor automation */
_track->foreach_processor (boost::bind (&Diskstream::move_processor_automation, this, _1, movements_frames));
diff --git a/libs/ardour/enums.cc b/libs/ardour/enums.cc
index cde3f418fd..5987c874fb 100644
--- a/libs/ardour/enums.cc
+++ b/libs/ardour/enums.cc
@@ -83,7 +83,6 @@ setup_enum_writer ()
TimecodeFormat _Session_TimecodeFormat;
Session::PullupFormat _Session_PullupFormat;
FadeShape _FadeShape;
- Panner::LinkDirection _Panner_LinkDirection;
IOChange _IOChange;
AutomationType _AutomationType;
AutoState _AutoState;
@@ -135,7 +134,9 @@ setup_enum_writer ()
REGISTER (_OverlapType);
REGISTER_ENUM (GainAutomation);
- REGISTER_ENUM (PanAutomation);
+ REGISTER_ENUM (PanAzimuthAutomation);
+ REGISTER_ENUM (PanElevationAutomation);
+ REGISTER_ENUM (PanWidthAutomation);
REGISTER_ENUM (PluginAutomation);
REGISTER_ENUM (SoloAutomation);
REGISTER_ENUM (MuteAutomation);
@@ -410,10 +411,6 @@ setup_enum_writer ()
REGISTER_CLASS_ENUM (Location, IsRangeMarker);
REGISTER_BITS (_Location_Flags);
- REGISTER_CLASS_ENUM (Panner, SameDirection);
- REGISTER_CLASS_ENUM (Panner, OppositeDirection);
- REGISTER (_Panner_LinkDirection);
-
REGISTER_CLASS_ENUM (Track, NoFreeze);
REGISTER_CLASS_ENUM (Track, Frozen);
REGISTER_CLASS_ENUM (Track, UnFrozen);
diff --git a/libs/ardour/event_type_map.cc b/libs/ardour/event_type_map.cc
index d88e5afd26..c8c48c5b86 100644
--- a/libs/ardour/event_type_map.cc
+++ b/libs/ardour/event_type_map.cc
@@ -140,15 +140,25 @@ EventTypeMap::new_parameter(uint32_t type, uint8_t channel, uint32_t id) const
double min = 0.0f;
double max = 1.0f;
double normal = 0.0f;
+
switch((AutomationType)type) {
case NullAutomation:
case GainAutomation:
max = 2.0f;
normal = 1.0f;
break;
- case PanAutomation:
- normal = 0.5f;
- break;
+ case PanAzimuthAutomation:
+ normal = 0.5f; // there really is no normal but this works for stereo, sort of
+ break;
+ case PanWidthAutomation:
+ min = -1.0;
+ max = 1.0;
+ normal = 0.0f;
+ break;
+ case PanElevationAutomation:
+ case PanFrontBackAutomation:
+ case PanLFEAutomation:
+ break;
case PluginAutomation:
case SoloAutomation:
case MuteAutomation:
@@ -191,11 +201,16 @@ EventTypeMap::new_parameter(const string& str) const
p_type = FadeOutAutomation;
} else if (str == "envelope") {
p_type = EnvelopeAutomation;
- } else if (str == "pan") {
- p_type = PanAutomation;
- } else if (str.length() > 4 && str.substr(0, 4) == "pan-") {
- p_type = PanAutomation;
- p_id = atoi(str.c_str()+4);
+ } else if (str == "pan-azimuth") {
+ p_type = PanAzimuthAutomation;
+ } else if (str == "pan-width") {
+ p_type = PanWidthAutomation;
+ } else if (str == "pan-elevation") {
+ p_type = PanElevationAutomation;
+ } else if (str == "pan-frontback") {
+ p_type = PanFrontBackAutomation;
+ } else if (str == "pan-lfe") {
+ p_type = PanLFEAutomation;
} else if (str.length() > 10 && str.substr(0, 10) == "parameter-") {
p_type = PluginAutomation;
p_id = atoi(str.c_str()+10);
@@ -243,8 +258,16 @@ EventTypeMap::to_symbol(const Evoral::Parameter& param) const
if (t == GainAutomation) {
return "gain";
- } else if (t == PanAutomation) {
- return string_compose("pan-%1", param.id());
+ } else if (t == PanAzimuthAutomation) {
+ return "pan-azimuth";
+ } else if (t == PanElevationAutomation) {
+ return "pan-elevation";
+ } else if (t == PanWidthAutomation) {
+ return "pan-width";
+ } else if (t == PanFrontBackAutomation) {
+ return "pan-frontback";
+ } else if (t == PanLFEAutomation) {
+ return "pan-lfe";
} else if (t == SoloAutomation) {
return "solo";
} else if (t == MuteAutomation) {
diff --git a/libs/ardour/globals.cc b/libs/ardour/globals.cc
index d5579b47da..afcaa4dfcd 100644
--- a/libs/ardour/globals.cc
+++ b/libs/ardour/globals.cc
@@ -70,6 +70,7 @@
#include "ardour/midi_region.h"
#include "ardour/mix.h"
#include "ardour/audioplaylist.h"
+#include "ardour/panner_manager.h"
#include "ardour/plugin_manager.h"
#include "ardour/process_thread.h"
#include "ardour/profile.h"
@@ -323,6 +324,8 @@ ARDOUR::init (bool use_vst, bool try_optimization)
ProcessThread::init ();
BufferManager::init (10); // XX should be num_processors_for_dsp
+ PannerManager::instance().discover_panners();
+
return 0;
}
diff --git a/libs/ardour/internal_send.cc b/libs/ardour/internal_send.cc
index 43276516dd..4c3b385304 100644
--- a/libs/ardour/internal_send.cc
+++ b/libs/ardour/internal_send.cc
@@ -32,8 +32,8 @@ using namespace PBD;
using namespace ARDOUR;
using namespace std;
-InternalSend::InternalSend (Session& s, boost::shared_ptr<MuteMaster> mm, boost::shared_ptr<Route> sendto, Delivery::Role role)
- : Send (s, mm, role)
+InternalSend::InternalSend (Session& s, boost::shared_ptr<Pannable> p, boost::shared_ptr<MuteMaster> mm, boost::shared_ptr<Route> sendto, Delivery::Role role)
+ : Send (s, p, mm, role)
, target (0)
{
if (sendto) {
diff --git a/libs/ardour/pannable.cc b/libs/ardour/pannable.cc
index e2f8ccc30e..4243ab45df 100644
--- a/libs/ardour/pannable.cc
+++ b/libs/ardour/pannable.cc
@@ -1,31 +1,87 @@
+/*
+ Copyright (C) 2011 Paul Davis
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+*/
+
+#include "pbd/error.h"
+#include "pbd/convert.h"
+
#include "ardour/automation_control.h"
#include "ardour/automation_list.h"
#include "ardour/pannable.h"
+#include "ardour/pan_controllable.h"
#include "ardour/session.h"
+using namespace PBD;
using namespace ARDOUR;
Pannable::Pannable (Session& s)
: Automatable (s)
, SessionHandleRef (s)
- , pan_azimuth_control (new AutomationControl (s, PanAzimuthAutomation,
- boost::shared_ptr<AutomationList>(new AutomationList(PanAzimuthAutomation)), ""))
- , pan_elevation_control (new AutomationControl (s, PanElevationAutomation,
- boost::shared_ptr<AutomationList>(new AutomationList(PanElevationAutomation)), ""))
- , pan_width_control (new AutomationControl (s, PanWidthAutomation,
- boost::shared_ptr<AutomationList>(new AutomationList(PanWidthAutomation)), ""))
- , pan_frontback_control (new AutomationControl (s, PanFrontBackAutomation,
- boost::shared_ptr<AutomationList>(new AutomationList(PanFrontBackAutomation)), ""))
- , pan_lfe_control (new AutomationControl (s, PanLFEAutomation,
- boost::shared_ptr<AutomationList>(new AutomationList(PanLFEAutomation)), ""))
+ , pan_azimuth_control (new PanControllable (s, "", this, PanAzimuthAutomation))
+ , pan_elevation_control (new PanControllable (s, "", this, PanElevationAutomation))
+ , pan_width_control (new PanControllable (s, "", this, PanWidthAutomation))
+ , pan_frontback_control (new PanControllable (s, "", this, PanFrontBackAutomation))
+ , pan_lfe_control (new PanControllable (s, "", this, PanLFEAutomation))
, _auto_state (Off)
, _auto_style (Absolute)
+ , _has_state (false)
+ , _responding_to_control_auto_state_change (0)
{
add_control (pan_azimuth_control);
add_control (pan_elevation_control);
add_control (pan_width_control);
add_control (pan_frontback_control);
add_control (pan_lfe_control);
+
+ /* all controls change state together */
+
+ pan_azimuth_control->alist()->automation_state_changed.connect_same_thread (*this, boost::bind (&Pannable::control_auto_state_changed, this, _1));
+ pan_elevation_control->alist()->automation_state_changed.connect_same_thread (*this, boost::bind (&Pannable::control_auto_state_changed, this, _1));
+ pan_width_control->alist()->automation_state_changed.connect_same_thread (*this, boost::bind (&Pannable::control_auto_state_changed, this, _1));
+ pan_frontback_control->alist()->automation_state_changed.connect_same_thread (*this, boost::bind (&Pannable::control_auto_state_changed, this, _1));
+ pan_lfe_control->alist()->automation_state_changed.connect_same_thread (*this, boost::bind (&Pannable::control_auto_state_changed, this, _1));
+}
+
+void
+Pannable::control_auto_state_changed (AutoState new_state)
+{
+ if (_responding_to_control_auto_state_change) {
+ return;
+ }
+
+ _responding_to_control_auto_state_change++;
+
+ pan_azimuth_control->set_automation_state (new_state);
+ pan_width_control->set_automation_state (new_state);
+ pan_elevation_control->set_automation_state (new_state);
+ pan_frontback_control->set_automation_state (new_state);
+ pan_lfe_control->set_automation_state (new_state);
+
+ _responding_to_control_auto_state_change--;
+
+ _auto_state = new_state;
+ automation_state_changed (new_state); /* EMIT SIGNAL */
+}
+
+void
+Pannable::set_panner (boost::shared_ptr<Panner> p)
+{
+ _panner = p;
}
void
@@ -95,3 +151,100 @@ Pannable::stop_touch (bool mark, double when)
}
g_atomic_int_set (&_touching, 0);
}
+
+XMLNode&
+Pannable::get_state ()
+{
+ return state (true);
+}
+
+XMLNode&
+Pannable::state (bool full)
+{
+ XMLNode* node = new XMLNode (X_("Pannable"));
+ XMLNode* control_node;
+ char buf[32];
+
+ control_node = new XMLNode (X_("azimuth"));
+ snprintf (buf, sizeof(buf), "%.12g", pan_azimuth_control->get_value());
+ control_node->add_property (X_("value"), buf);
+ node->add_child_nocopy (*control_node);
+
+ control_node = new XMLNode (X_("width"));
+ snprintf (buf, sizeof(buf), "%.12g", pan_width_control->get_value());
+ control_node->add_property (X_("value"), buf);
+ node->add_child_nocopy (*control_node);
+
+ control_node = new XMLNode (X_("elevation"));
+ snprintf (buf, sizeof(buf), "%.12g", pan_elevation_control->get_value());
+ control_node->add_property (X_("value"), buf);
+ node->add_child_nocopy (*control_node);
+
+ control_node = new XMLNode (X_("frontback"));
+ snprintf (buf, sizeof(buf), "%.12g", pan_frontback_control->get_value());
+ control_node->add_property (X_("value"), buf);
+ node->add_child_nocopy (*control_node);
+
+ control_node = new XMLNode (X_("lfe"));
+ snprintf (buf, sizeof(buf), "%.12g", pan_lfe_control->get_value());
+ control_node->add_property (X_("value"), buf);
+ node->add_child_nocopy (*control_node);
+
+ node->add_child_nocopy (get_automation_xml_state ());
+
+ return *node;
+}
+
+int
+Pannable::set_state (const XMLNode& root, int /*version - not used*/)
+{
+ if (root.name() != X_("Pannable")) {
+ warning << string_compose (_("Pannable given XML data for %1 - ignored"), root.name()) << endmsg;
+ return -1;
+ }
+
+ XMLNodeList nlist;
+ XMLNodeConstIterator niter;
+ const XMLProperty *prop;
+
+ nlist = root.children();
+
+ for (niter = nlist.begin(); niter != nlist.end(); ++niter) {
+ if ((*niter)->name() == X_("azimuth")) {
+ prop = (*niter)->property (X_("value"));
+ if (prop) {
+ pan_azimuth_control->set_value (atof (prop->value()));
+ }
+ } else if ((*niter)->name() == X_("width")) {
+ prop = (*niter)->property (X_("value"));
+ if (prop) {
+ pan_width_control->set_value (atof (prop->value()));
+ }
+ } else if ((*niter)->name() == X_("elevation")) {
+ prop = (*niter)->property (X_("value"));
+ if (prop) {
+ pan_elevation_control->set_value (atof (prop->value()));
+ }
+ } else if ((*niter)->name() == X_("azimuth")) {
+ prop = (*niter)->property (X_("value"));
+ if (prop) {
+ pan_frontback_control->set_value (atof (prop->value()));
+ }
+ } else if ((*niter)->name() == X_("lfe")) {
+ prop = (*niter)->property (X_("value"));
+ if (prop) {
+ pan_lfe_control->set_value (atof (prop->value()));
+ }
+ } else if ((*niter)->name() == Automatable::xml_node_name) {
+ set_automation_xml_state (**niter, PanAzimuthAutomation);
+ }
+ }
+
+ _has_state = true;
+
+ return 0;
+}
+
+
+
+
diff --git a/libs/ardour/panner.cc b/libs/ardour/panner.cc
index e4dc417c83..bfee68a342 100644
--- a/libs/ardour/panner.cc
+++ b/libs/ardour/panner.cc
@@ -1,6 +1,5 @@
-
/*
- Copyright (C) 2004 Paul Davis
+ Copyright (C) 2004-2011 Paul Davis
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -18,1539 +17,147 @@
*/
-#include <inttypes.h>
-
-#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 "pbd/cartesian.h"
-#include "pbd/convert.h"
-#include "pbd/error.h"
-#include "pbd/failed_constructor.h"
-#include "pbd/xml++.h"
-#include "pbd/enumwriter.h"
-
-#include "evoral/Curve.hpp"
-
-#include "ardour/session.h"
-#include "ardour/panner.h"
-#include "ardour/utils.h"
#include "ardour/audio_buffer.h"
-
-#include "ardour/runtime_functions.h"
#include "ardour/buffer_set.h"
-#include "ardour/audio_buffer.h"
-#include "ardour/vbap.h"
-
-#include "i18n.h"
-
-#include "pbd/mathfix.h"
+#include "ardour/panner.h"
+#include "ardour/pannable.h"
+#include "ardour/session.h"
+#include "ardour/utils.h"
using namespace std;
using namespace ARDOUR;
-using namespace PBD;
-
-float Panner::current_automation_version_number = 1.0;
-
-string EqualPowerStereoPanner::name = "Equal Power Stereo";
-/* this is a default mapper of control values to a pan position
- others can be imagined.
-*/
-
-static double direct_control_to_stereo_pan (double fract)
+Panner::Panner (boost::shared_ptr<Pannable> p)
+ : _pannable (p)
+ , _mono (0)
{
- return BaseStereoPanner::lr_fract_to_azimuth (fract);
}
-StreamPanner::StreamPanner (Panner& p, Evoral::Parameter param)
- : parent (p)
- , _control (new PanControllable (parent.session(), _("direction"), this, param))
-{
- assert (param.type() != NullAutomation);
-
- _muted = false;
- _mono = false;
-
- p.add_control (_control);
-}
-
-StreamPanner::~StreamPanner ()
+Panner::~Panner ()
{
}
void
-StreamPanner::set_mono (bool yn)
+Panner::set_bypassed (bool yn)
{
- if (yn != _mono) {
- _mono = yn;
+ if (yn != _bypassed) {
+ _bypassed = yn;
StateChanged ();
}
}
-double
-StreamPanner::PanControllable::lower () const
-{
- switch (parameter().id()) {
- case 200: /* width */
- return -1.0;
- default:
- return 0.0;
- }
-}
-
void
-StreamPanner::PanControllable::set_value (double val)
-{
- Panner& p (streampanner->get_parent());
- switch (parameter().id()) {
- case 100:
- /* position */
- val = max (min (val, 1.0), 0.0);
- if (p.set_stereo_pan (val, p.width_control()->get_value())) {
- AutomationControl::set_value(val);
- p.session().set_dirty ();
- }
- break;
-
- case 200:
- /* width */
- val = max (min (val, 1.0), -1.0);
- if (p.set_stereo_pan (p.direction_control()->get_value(), val)) {
- AutomationControl::set_value(val);
- p.session().set_dirty ();
- }
- break;
-
- default: /* positional */
- val = max (min (val, 1.0), 0.0);
- streampanner->set_position (AngularVector (direct_control_to_stereo_pan (val), 0.0));
- AutomationControl::set_value(val);
- p.session().set_dirty ();
- break;
- }
-
-}
-
-double
-StreamPanner::PanControllable::get_value (void) const
-{
- return AutomationControl::get_value();
-}
-
-void
-StreamPanner::set_muted (bool yn)
+Panner::set_mono (bool yn)
{
- if (yn != _muted) {
- _muted = yn;
+ if (yn != _mono) {
+ _mono = yn;
StateChanged ();
}
}
-void
-StreamPanner::set_position (const AngularVector& av, bool link_call)
-{
- if (!link_call && parent.linked()) {
- parent.set_position (av, *this);
- }
-
- if (_angles != av) {
- _angles = av;
- update ();
- Changed ();
- _control->Changed ();
- }
-}
-
int
-StreamPanner::set_state (const XMLNode& node, int version)
+Panner::set_state (const XMLNode& node, int version)
{
const XMLProperty* prop;
XMLNodeConstIterator iter;
- if ((prop = node.property (X_("muted")))) {
- set_muted (string_is_affirmative (prop->value()));
- }
-
if ((prop = node.property (X_("mono")))) {
set_mono (string_is_affirmative (prop->value()));
}
- for (XMLNodeConstIterator iter = node.children().begin(); iter != node.children().end(); ++iter) {
- if ((*iter)->name() == Controllable::xml_node_name) {
- if ((prop = (*iter)->property ("name")) != 0 && prop->value() == "direction") {
- _control->set_state (**iter, version);
- }
- }
- }
-
- return 0;
-}
-
-XMLNode&
-StreamPanner::get_state ()
-{
- XMLNode* node = new XMLNode (X_("StreamPanner"));
- node->add_property (X_("muted"), (muted() ? "yes" : "no"));
- node->add_property (X_("mono"), (_mono ? "yes" : "no"));
- node->add_child_nocopy (_control->get_state ());
- return *node;
-}
-
-void
-StreamPanner::distribute (AudioBuffer& src, BufferSet& obufs, gain_t gain_coeff, pframes_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,
- framepos_t start, framepos_t end, pframes_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)
- : StreamPanner (p, param)
- , left (0.5)
- , right (0.5)
- , left_interp (left)
- , right_interp (right)
-{
-}
-
-BaseStereoPanner::~BaseStereoPanner ()
-{
-}
-
-int
-BaseStereoPanner::load (istream& in, string path, uint32_t& linecnt)
-{
- char line[128];
- LocaleGuard lg (X_("POSIX"));
-
- _control->list()->clear ();
-
- while (in.getline (line, sizeof (line), '\n')) {
- framepos_t when;
- double value;
-
- ++linecnt;
-
- if (strcmp (line, "end") == 0) {
- break;
- }
-
- if (sscanf (line, "%" PRIu64 " %lf", &when, &value) != 2) {
- warning << string_compose(_("badly formatted pan automation event record at line %1 of %2 (ignored) [%3]"), linecnt, path, line) << endmsg;
- continue;
- }
-
- _control->list()->fast_simple_add (when, value);
+ if ((prop = node.property (X_("bypassed"))) != 0) {
+ set_bypassed (string_is_affirmative (prop->value()));
}
- /* now that we are done loading */
-
- ((AutomationList*)_control->list().get())->StateChanged ();
-
return 0;
}
-void
-BaseStereoPanner::do_distribute (AudioBuffer& srcbuf, BufferSet& obufs, gain_t gain_coeff, pframes_t nframes)
-{
- assert(obufs.count().n_audio() == 2);
-
- pan_t delta;
- Sample* dst;
- pan_t pan;
-
- if (_muted) {
- return;
- }
-
- Sample* const src = srcbuf.data();
-
- /* LEFT */
-
- dst = obufs.get_audio(0).data();
-
- if (fabsf ((delta = (left - desired_left))) > 0.002) { // about 1 degree of arc
-
- /* we've moving the pan by an appreciable amount, so we must
- interpolate over 64 frames or nframes, whichever is smaller */
-
- pframes_t const limit = min ((pframes_t) 64, nframes);
- pframes_t n;
-
- delta = -(delta / (float) (limit));
-
- for (n = 0; n < limit; n++) {
- left_interp = left_interp + delta;
- 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;
-
- mix_buffers_with_gain (dst+n,src+n,nframes-n,pan);
-
- } else {
-
- left = desired_left;
- left_interp = left;
-
- if ((pan = (left * gain_coeff)) != 1.0f) {
-
- 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 */
-
- // obufs[0] = 0;
-
- }
-
- } 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 */
-
- // obufs[0] = 0;
- }
- }
-
- /* RIGHT */
-
- dst = obufs.get_audio(1).data();
-
- if (fabsf ((delta = (right - desired_right))) > 0.002) { // about 1 degree of arc
-
- /* we're moving the pan by an appreciable amount, so we must
- interpolate over 64 frames or nframes, whichever is smaller */
-
- pframes_t const limit = min ((pframes_t) 64, nframes);
- pframes_t n;
-
- delta = -(delta / (float) (limit));
-
- for (n = 0; n < limit; n++) {
- right_interp = right_interp + delta;
- right = right_interp + 0.9 * (right - right_interp);
- 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);
-
- /* XXX it would be nice to mark the buffer as written to */
-
- } else {
-
- right = desired_right;
- right_interp = right;
-
- if ((pan = (right * gain_coeff)) != 1.0f) {
-
- 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::EqualPowerStereoPanner (Panner& p, Evoral::Parameter param)
- : BaseStereoPanner (p, param)
-{
- update ();
-
- left = desired_left;
- right = desired_right;
- left_interp = left;
- right_interp = right;
-}
-
-EqualPowerStereoPanner::~EqualPowerStereoPanner ()
-{
-}
-
-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 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
- */
-
- double _x = BaseStereoPanner::azimuth_to_lr_fract (_angles.azi);
-
- float const panR = _x;
- float const panL = 1 - panR;
-
- float const pan_law_attenuation = -3.0f;
- float const scale = 2.0f - 4.0f * powf (10.0f,pan_law_attenuation/20.0f);
-
- desired_left = panL * (scale * panL + 1.0f - scale);
- desired_right = panR * (scale * panR + 1.0f - scale);
-
- _effective_angles = _angles;
- //_control->set_value(x);
-}
-
-void
-EqualPowerStereoPanner::do_distribute_automated (AudioBuffer& srcbuf, BufferSet& obufs,
- framepos_t start, framepos_t end, pframes_t nframes,
- pan_t** buffers)
-{
- assert (obufs.count().n_audio() == 2);
-
- Sample* dst;
- pan_t* pbuf;
- Sample* const src = srcbuf.data();
-
- /* fetch positional data */
-
- if (!_control->list()->curve().rt_safe_get_vector (start, end, buffers[0], nframes)) {
- /* fallback */
- if (!_muted) {
- do_distribute (srcbuf, obufs, 1.0, nframes);
- }
- return;
- }
-
- /* store effective pan position. do this even if we are muted */
-
- if (nframes > 0) {
- _effective_angles.azi = BaseStereoPanner::lr_fract_to_azimuth (buffers[0][nframes-1]);
- }
-
- if (_muted) {
- return;
- }
-
- /* apply pan law to convert positional data into pan coefficients for
- each buffer (output)
- */
-
- const float pan_law_attenuation = -3.0f;
- const float scale = 2.0f - 4.0f * powf (10.0f,pan_law_attenuation/20.0f);
-
- for (pframes_t n = 0; n < nframes; ++n) {
-
- float const panR = buffers[0][n];
- float const panL = 1 - panR;
-
- buffers[0][n] = panL * (scale * panL + 1.0f - scale);
- buffers[1][n] = panR * (scale * panR + 1.0f - scale);
- }
-
- /* LEFT */
-
- dst = obufs.get_audio(0).data();
- pbuf = buffers[0];
-
- for (pframes_t n = 0; n < nframes; ++n) {
- dst[n] += src[n] * pbuf[n];
- }
-
- /* XXX it would be nice to mark the buffer as written to */
-
- /* RIGHT */
-
- dst = obufs.get_audio(1).data();
- pbuf = buffers[1];
-
- for (pframes_t n = 0; n < nframes; ++n) {
- dst[n] += src[n] * pbuf[n];
- }
-
- /* XXX it would be nice to mark the buffer as written to */
-}
-
-StreamPanner*
-EqualPowerStereoPanner::factory (Panner& parent, Evoral::Parameter param, Speakers& /* ignored */)
-{
- return new EqualPowerStereoPanner (parent, param);
-}
-
XMLNode&
-EqualPowerStereoPanner::get_state (void)
+Panner::get_state ()
{
- return state (true);
-}
-
-XMLNode&
-EqualPowerStereoPanner::state (bool /*full_state*/)
-{
- XMLNode& root (StreamPanner::get_state ());
- root.add_property (X_("type"), EqualPowerStereoPanner::name);
- return root;
-}
-
-int
-EqualPowerStereoPanner::set_state (const XMLNode& node, int version)
-{
- LocaleGuard lg (X_("POSIX"));
-
- StreamPanner::set_state (node, version);
-
- for (XMLNodeConstIterator iter = node.children().begin(); iter != node.children().end(); ++iter) {
-
- if ((*iter)->name() == X_("Automation")) {
-
- _control->alist()->set_state (*((*iter)->children().front()), version);
-
- if (_control->alist()->automation_state() != Off) {
- double degrees = BaseStereoPanner::lr_fract_to_azimuth (_control->list()->eval (parent.session().transport_frame()));
- set_position (AngularVector (degrees, 0.0));
- }
- }
- }
-
- return 0;
-}
-
-Panner::Panner (string name, Session& s)
- : SessionObject (s, name)
- , Automatable (s)
-{
- //set_name_old_auto (name);
- set_name (name);
-
- _linked = false;
- _link_direction = SameDirection;
- _bypassed = false;
- _mono = false;
-}
-
-Panner::~Panner ()
-{
-}
-
-void
-Panner::set_linked (bool yn)
-{
- if (yn != _linked) {
- _linked = yn;
- _session.set_dirty ();
- LinkStateChanged (); /* EMIT SIGNAL */
- }
-}
-
-void
-Panner::set_link_direction (LinkDirection ld)
-{
- if (ld != _link_direction) {
- _link_direction = ld;
- _session.set_dirty ();
- LinkStateChanged (); /* EMIT SIGNAL */
- }
-}
-
-
-void
-Panner::set_bypassed (bool yn)
-{
- if (yn != _bypassed) {
- _bypassed = yn;
- StateChanged ();
- }
-}
-
-
-void
-Panner::reset_to_default ()
-{
- vector<float> positions;
-
- switch (outputs.size()) {
- case 0:
- case 1:
- return;
- }
-
- if (outputs.size() == 2) {
- AngularVector a;
- switch (_streampanners.size()) {
- case 1:
- a.azi = 90.0; /* "front" or "top", in degrees */
- _streampanners.front()->set_position (a);
- _streampanners.front()->pan_control()->list()->reset_default (0.5);
- return;
- break;
- case 2:
- a.azi = 180.0; /* "left", in degrees */
- _streampanners.front()->set_position (a);
- _streampanners.front()->pan_control()->list()->reset_default (0.0);
- a.azi = 0.0; /* "right", in degrees */
- _streampanners.back()->set_position (a);
- _streampanners.back()->pan_control()->list()->reset_default (1.0);
- return;
- default:
- break;
- }
- }
-
- vector<Output>::iterator o;
- vector<StreamPanner*>::iterator p;
-
- for (o = outputs.begin(), p = _streampanners.begin(); o != outputs.end() && p != _streampanners.end(); ++o, ++p) {
- (*p)->set_position ((*o).position);
- }
-}
-
-void
-Panner::reset_streampanner (uint32_t which)
-{
- AngularVector a;
-
- if (which >= _streampanners.size() || which >= outputs.size()) {
- return;
- }
-
- switch (outputs.size()) {
- case 0:
- case 1:
- return;
-
- case 2:
- switch (_streampanners.size()) {
- case 1:
- /* stereo out, 1 stream, default = middle */
- a.azi = 90.0; /* "front" or "top", in degrees */
- _streampanners.front()->set_position (a);
- _streampanners.front()->pan_control()->list()->reset_default (0.5);
- break;
- case 2:
- /* stereo out, 2 streams, default = hard left/right */
- if (which == 0) {
- a.azi = 180.0; /* "left", in degrees */
- _streampanners.front()->set_position (a);
- _streampanners.front()->pan_control()->list()->reset_default (0.0);
- } else {
- a.azi = 0.0; /* "right", in degrees */
- _streampanners.back()->set_position (a);
- _streampanners.back()->pan_control()->list()->reset_default (1.0);
- }
- break;
- }
- return;
-
- default:
- _streampanners[which]->set_position (outputs[which].position);
- }
-}
-
-/**
- * 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)
-{
- uint32_t n;
- bool changed = false;
- bool do_not_and_did_not_need_panning = ((nouts < 2) && (outputs.size() < 2));
-
- /* if new and old config don't need panning, or if
- the config hasn't changed, we're done.
- */
+ XMLNode* node = new XMLNode (X_("Panner"));
- if (do_not_and_did_not_need_panning ||
- ((nouts == outputs.size()) && (npans == _streampanners.size()))) {
- return;
- }
-
- n = _streampanners.size();
- clear_panners ();
-
- if (n != npans) {
- changed = true;
- }
-
- n = outputs.size();
- outputs.clear ();
-
- if (n != nouts) {
- changed = true;
- }
-
- if (nouts < 2) {
- /* no need for panning with less than 2 outputs */
- if (changed) {
- Changed (); /* EMIT SIGNAL */
- }
- return;
- }
-
- switch (nouts) {
- case 0:
- /* XXX: this can never happen */
- break;
-
- case 1:
- /* XXX: this can never happen */
- fatal << _("programming error:")
- << X_("Panner::reset() called with a single output")
- << endmsg;
- /*NOTREACHED*/
- break;
-
- case 2: // line
- outputs.push_back (Output (AngularVector (180.0, 0.0)));
- outputs.push_back (Output (AngularVector (0.0, 0,0)));
- for (n = 0; n < npans; ++n) {
- _streampanners.push_back (new EqualPowerStereoPanner (*this, Evoral::Parameter(PanAutomation, 0, n)));
- }
- break;
-
- default:
- setup_speakers (nouts);
- for (n = 0; n < npans; ++n) {
- _streampanners.push_back (new VBAPanner (*this, Evoral::Parameter(PanAutomation, 0, n), _session.get_speakers()));
- }
- break;
- }
-
- for (std::vector<StreamPanner*>::iterator x = _streampanners.begin(); x != _streampanners.end(); ++x) {
- (*x)->update ();
- }
-
- setup_meta_controls ();
-
- /* must emit Changed here, otherwise the changes to the pan_control below raise further
- signals which the GUI is not prepared for until it has seen the Changed here.
- */
-
- if (changed) {
- Changed (); /* EMIT SIGNAL */
- }
-
- /* force hard left/right panning in a common case: 2in/2out
- */
-
- if (npans == 2 && outputs.size() == 2) {
-
- /* Do this only if we changed configuration, or our configuration
- appears to be the default set up (zero degrees)
- */
-
- AngularVector left;
- AngularVector right;
-
- left = _streampanners.front()->get_position ();
- right = _streampanners.back()->get_position ();
-
- if (changed || ((left.azi == 0.0) && (right.azi == 0.0))) {
-
- _streampanners.front()->set_position (AngularVector (180.0, 0.0));
- _streampanners.front()->pan_control()->list()->reset_default (0.0);
-
- _streampanners.back()->set_position (AngularVector (0.0, 0.0));
- _streampanners.back()->pan_control()->list()->reset_default (1.0);
- }
-
- } else if (npans > 1 && outputs.size() > 2) {
-
- /* 2d panning: spread signals equally around a circle */
-
- double degree_step = 360.0 / nouts;
- double deg;
-
- /* even number of signals? make sure the top two are either side of "top".
- otherwise, just start at the "top" (90.0 degrees) and rotate around
- */
-
- if (npans % 2) {
- deg = 90.0 - degree_step;
- } else {
- deg = 90.0;
- }
+ node->add_property (X_("mono"), (_mono ? "yes" : "no"));
+ node->add_property (X_("bypassed"), (bypassed() ? "yes" : "no"));
- for (std::vector<StreamPanner*>::iterator x = _streampanners.begin(); x != _streampanners.end(); ++x) {
- (*x)->set_position (AngularVector (deg, 0.0));
- deg += degree_step;
- }
- }
+ return *node;
}
void
-Panner::remove (uint32_t which)
+Panner::distribute (BufferSet& ibufs, BufferSet& obufs, gain_t gain_coeff, pframes_t nframes)
{
- vector<StreamPanner*>::iterator i;
- for (i = _streampanners.begin(); i != _streampanners.end() && which; ++i, --which) {}
+ uint32_t which = 0;
- if (i != _streampanners.end()) {
- delete *i;
- _streampanners.erase (i);
- }
+ for (BufferSet::audio_iterator src = ibufs.audio_begin(); src != ibufs.audio_end(); ++src, ++which) {
+ if (_mono) {
+ /* we're in mono mode, so just pan the input to all outputs equally (XXX should we scale?) */
+ for (BufferSet::audio_iterator o = obufs.audio_begin(); o != obufs.audio_end(); ++o) {
+ mix_buffers_with_gain ((*o).data(), (*src).data(), nframes, gain_coeff);
+ }
+ } else {
+ /* normal mode, call the `real' distribute method */
+ distribute_one (*src, obufs, gain_coeff, nframes, which);
+ }
+ }
}
-
-/** Remove all our StreamPanners */
void
-Panner::clear_panners ()
+Panner::distribute_automated (BufferSet& ibufs, BufferSet& obufs,
+ framepos_t start, framepos_t end, pframes_t nframes, pan_t** buffers)
{
- for (vector<StreamPanner*>::iterator i = _streampanners.begin(); i != _streampanners.end(); ++i) {
- delete *i;
- }
+ uint32_t which = 0;
- _streampanners.clear ();
+ for (BufferSet::audio_iterator src = ibufs.audio_begin(); src != ibufs.audio_end(); ++src, ++which) {
+ if (_mono) {
+ /* we're in mono mode, so just pan the input to all outputs equally (XXX should we scale?) */
+ for (BufferSet::audio_iterator o = obufs.audio_begin(); o != obufs.audio_end(); ++o) {
+ mix_buffers_with_gain ((*o).data(), (*src).data(), nframes, 1.0);
+ }
+ } else {
+ /* normal mode, call the `real' distribute method */
+ distribute_one_automated (*src, obufs, start, end, nframes, buffers, which);
+ }
+ }
}
void
Panner::set_automation_style (AutoStyle style)
{
- for (vector<StreamPanner*>::iterator i = _streampanners.begin(); i != _streampanners.end(); ++i) {
- ((AutomationList*)(*i)->pan_control()->list().get())->set_automation_style (style);
- }
- _session.set_dirty ();
+ _pannable->set_automation_style (style);
}
void
Panner::set_automation_state (AutoState state)
{
- for (vector<StreamPanner*>::iterator i = _streampanners.begin(); i != _streampanners.end(); ++i) {
- ((AutomationList*)(*i)->pan_control()->list().get())->set_automation_state (state);
- }
- _session.set_dirty ();
+ _pannable->set_automation_state (state);
}
AutoState
Panner::automation_state () const
{
- boost::shared_ptr<AutomationList> l;
- if (!empty()) {
- boost::shared_ptr<AutomationControl> control = _streampanners.front()->pan_control();
- if (control) {
- l = boost::dynamic_pointer_cast<AutomationList>(control->list());
- }
- }
-
- return l ? l->automation_state() : Off;
+ return _pannable->automation_state();
}
AutoStyle
Panner::automation_style () const
{
- boost::shared_ptr<AutomationList> l;
- if (!empty()) {
- boost::shared_ptr<AutomationControl> control = _streampanners.front()->pan_control();
- if (control) {
- l = boost::dynamic_pointer_cast<AutomationList>(control->list());
- }
- }
-
- return l ? l->automation_style() : Absolute;
-}
-
-struct PanPlugins {
- string name;
- uint32_t nouts;
- StreamPanner* (*factory)(Panner&, Evoral::Parameter, Speakers&);
-};
-
-PanPlugins pan_plugins[] = {
- { EqualPowerStereoPanner::name, 2, EqualPowerStereoPanner::factory },
- { VBAPanner::name, 3, VBAPanner::factory },
- { string (""), 0, 0 }
-};
-
-XMLNode&
-Panner::get_state (void)
-{
- return state (true);
-}
-
-XMLNode&
-Panner::state (bool full)
-{
- XMLNode* node = new XMLNode ("Panner");
-
- char buf[32];
-
- node->add_property (X_("linked"), (_linked ? "yes" : "no"));
- node->add_property (X_("link_direction"), enum_2_string (_link_direction));
- node->add_property (X_("bypassed"), (bypassed() ? "yes" : "no"));
- snprintf (buf, sizeof (buf), "%zd", outputs.size());
- node->add_property (X_("outputs"), buf);
-
- for (vector<StreamPanner*>::const_iterator i = _streampanners.begin(); i != _streampanners.end(); ++i) {
- node->add_child_nocopy ((*i)->state (full));
- }
-
- node->add_child_nocopy (get_automation_xml_state ());
-
- return *node;
-}
-
-int
-Panner::set_state (const XMLNode& node, int version)
-{
- XMLNodeList nlist = node.children ();
- XMLNodeConstIterator niter;
- const XMLProperty *prop;
- uint32_t i;
- uint32_t num_panners = 0;
- StreamPanner* sp;
- LocaleGuard lg (X_("POSIX"));
-
- clear_panners ();
-
- ChanCount ins = ChanCount::ZERO;
- ChanCount outs = ChanCount::ZERO;
-
- // XXX: this might not be necessary anymore
- outputs.clear ();
-
- if ((prop = node.property (X_("linked"))) != 0) {
- set_linked (string_is_affirmative (prop->value()));
- }
-
- if ((prop = node.property (X_("bypassed"))) != 0) {
- set_bypassed (string_is_affirmative (prop->value()));
- }
-
- if ((prop = node.property (X_("link_direction"))) != 0) {
- LinkDirection ld; /* here to provide type information */
- set_link_direction (LinkDirection (string_2_enum (prop->value(), ld)));
- }
-
- if ((prop = node.property (X_("outputs"))) != 0) {
- uint32_t n = atoi (prop->value());
-
- while (n--) {
- AngularVector a; // value is irrelevant
- outputs.push_back (Output (a));
- }
-
- } else {
-
- /* old school */
-
- for (niter = nlist.begin(); niter != nlist.end(); ++niter) {
- if ((*niter)->name() == X_("Output")) {
-
- AngularVector a;
-
- if ((prop = (*niter)->property (X_("azimuth")))) {
- sscanf (prop->value().c_str(), "%lg", &a.azi);
- } else if ((prop = (*niter)->property (X_("x")))) {
- /* old school cartesian */
- a.azi = BaseStereoPanner::lr_fract_to_azimuth (atof (prop->value().c_str()));
- }
-
- if ((prop = (*niter)->property (X_("elevation")))) {
- sscanf (prop->value().c_str(), "%lg", &a.ele);
- }
-
- outputs.push_back (Output (a));
- }
- }
- }
-
- for (niter = nlist.begin(); niter != nlist.end(); ++niter) {
-
- if ((*niter)->name() == X_("StreamPanner")) {
-
- if ((prop = (*niter)->property (X_("type")))) {
-
- for (i = 0; pan_plugins[i].factory; ++i) {
- if (prop->value() == pan_plugins[i].name) {
-
-
- /* note that we assume that all the stream panners
- are of the same type. pretty good
- assumption, but it's still an assumption.
- */
-
- sp = pan_plugins[i].factory (*this, Evoral::Parameter(PanAutomation, 0, num_panners), _session.get_speakers ());
- num_panners++;
-
- if (sp->set_state (**niter, version) == 0) {
- _streampanners.push_back (sp);
- }
-
- break;
- }
- }
-
- if (!pan_plugins[i].factory) {
- error << string_compose (_("Unknown panner plugin \"%1\" found in pan state - ignored"),
- prop->value())
- << endmsg;
- }
-
- } else {
- error << _("panner plugin node has no type information!")
- << endmsg;
- return -1;
- }
-
- }
- }
-
- setup_meta_controls ();
-
- reset (outputs.size (), num_panners);
-
- /* don't try to do old-school automation loading if it wasn't marked as existing */
-
- if ((prop = node.property (X_("automation")))) {
-
- /* automation path is relative */
-
- automation_path = Glib::build_filename(_session.automation_dir(), prop->value ());
- }
-
- for (niter = nlist.begin(); niter != nlist.end(); ++niter) {
- if ((*niter)->name() == X_("Automation")) {
- set_automation_xml_state (**niter, Evoral::Parameter (PanAutomation));
- }
- }
-
- return 0;
+ return _pannable->automation_style ();
}
bool
Panner::touching () const
{
- for (vector<StreamPanner*>::const_iterator i = _streampanners.begin(); i != _streampanners.end(); ++i) {
- if (((AutomationList*)(*i)->pan_control()->list().get())->touching ()) {
- return true;
- }
- }
-
- return false;
-}
-
-void
-Panner::set_position (const AngularVector& a, StreamPanner& orig)
-{
- AngularVector delta;
- AngularVector new_position;
-
- delta = orig.get_position() - a;
-
- if (_link_direction == SameDirection) {
-
- for (vector<StreamPanner*>::iterator i = _streampanners.begin(); i != _streampanners.end(); ++i) {
- if (*i == &orig) {
- (*i)->set_position (a, true);
- } else {
- new_position = (*i)->get_position() + delta;
- (*i)->set_position (new_position, true);
- }
- }
-
- } else {
-
- for (vector<StreamPanner*>::iterator i = _streampanners.begin(); i != _streampanners.end(); ++i) {
- if (*i == &orig) {
- (*i)->set_position (a, true);
- } else {
- new_position = (*i)->get_position() - delta;
- (*i)->set_position (new_position, true);
- }
- }
- }
-}
-
-void
-Panner::distribute_no_automation (BufferSet& inbufs, BufferSet& outbufs, pframes_t nframes, gain_t gain_coeff)
-{
- if (outbufs.count().n_audio() == 0) {
- // Don't want to lose audio...
- assert(inbufs.count().n_audio() == 0);
- return;
- }
-
- // We shouldn't be called in the first place...
- assert(!bypassed());
- assert(!empty());
-
-
- if (outbufs.count().n_audio() == 1) {
-
- AudioBuffer& dst = outbufs.get_audio(0);
-
- if (gain_coeff == 0.0f) {
-
- /* only one output, and gain was zero, so make it silent */
-
- dst.silence (nframes);
-
- } else if (gain_coeff == 1.0f){
-
- /* mix all buffers into the output */
-
- // copy the first
- dst.read_from(inbufs.get_audio(0), nframes);
-
- // accumulate starting with the second
- if (inbufs.count().n_audio() > 0) {
- BufferSet::audio_iterator i = inbufs.audio_begin();
- for (++i; i != inbufs.audio_end(); ++i) {
- dst.merge_from(*i, nframes);
- }
- }
-
- } else {
-
- /* mix all buffers into the output, scaling them all by the gain */
-
- // copy the first
- dst.read_from(inbufs.get_audio(0), nframes);
-
- // accumulate (with gain) starting with the second
- if (inbufs.count().n_audio() > 0) {
- BufferSet::audio_iterator i = inbufs.audio_begin();
- for (++i; i != inbufs.audio_end(); ++i) {
- dst.accumulate_with_gain_from(*i, nframes, gain_coeff);
- }
- }
-
- }
-
- return;
- }
-
- /* the terrible silence ... */
- for (BufferSet::audio_iterator i = outbufs.audio_begin(); i != outbufs.audio_end(); ++i) {
- i->silence(nframes);
- }
-
- BufferSet::audio_iterator i = inbufs.audio_begin();
-
- for (vector<StreamPanner*>::iterator pan = _streampanners.begin(); pan != _streampanners.end() && i != inbufs.audio_end(); ++pan, ++i) {
- (*pan)->distribute (*i, outbufs, gain_coeff, nframes);
- }
-}
-
-void
-Panner::run (BufferSet& inbufs, BufferSet& outbufs, framepos_t start_frame, framepos_t end_frame, pframes_t nframes)
-{
- if (outbufs.count().n_audio() == 0) {
- // Failing to deliver audio we were asked to deliver is a bug
- assert(inbufs.count().n_audio() == 0);
- return;
- }
-
- // We shouldn't be called in the first place...
- assert(!bypassed());
- assert(!empty());
-
- // If we shouldn't play automation defer to distribute_no_automation
- if (!(automation_state() & Play || ((automation_state() & Touch) && !touching()))) {
-
- // Speed quietning
- gain_t gain_coeff = 1.0;
-
- if (fabsf(_session.transport_speed()) > 1.5f && Config->get_quieten_at_speed ()) {
- gain_coeff = speed_quietning;
- }
-
- distribute_no_automation (inbufs, outbufs, nframes, gain_coeff);
- return;
- }
-
- // Otherwise.. let the automation flow, baby
-
- if (outbufs.count().n_audio() == 1) {
-
- AudioBuffer& dst = outbufs.get_audio(0);
-
- // FIXME: apply gain automation?
-
- // copy the first
- dst.read_from(inbufs.get_audio(0), nframes);
-
- // accumulate starting with the second
- BufferSet::audio_iterator i = inbufs.audio_begin();
- for (++i; i != inbufs.audio_end(); ++i) {
- dst.merge_from(*i, nframes);
- }
-
- return;
- }
-
- // More than 1 output, we should have 1 panner for each input
- //assert(_streampanners.size() == inbufs.count().n_audio());
-
- /* the terrible silence ... */
- for (BufferSet::audio_iterator i = outbufs.audio_begin(); i != outbufs.audio_end(); ++i) {
- i->silence(nframes);
- }
-
- BufferSet::audio_iterator i = inbufs.audio_begin();
- for (vector<StreamPanner*>::iterator pan = _streampanners.begin(); pan != _streampanners.end(); ++pan, ++i) {
- (*pan)->distribute_automated (*i, outbufs, start_frame, end_frame, nframes, _session.pan_automation_buffer());
- }
-}
-
-/* old school automation handling */
-
-/*
-void
-Panner::set_name (string str)
-{
- automation_path = Glib::build_filename(_session.automation_dir(),
- _session.snap_name() + "-pan-" + legalize_for_path (str) + ".automation");
-}
-*/
-
-int
-Panner::load ()
-{
- char line[128];
- uint32_t linecnt = 0;
- float version;
- vector<StreamPanner*>::iterator sp;
- LocaleGuard lg (X_("POSIX"));
-
- if (automation_path.length() == 0) {
- return 0;
- }
-
- if (access (automation_path.c_str(), F_OK)) {
- return 0;
- }
-
- ifstream in (automation_path.c_str());
-
- if (!in) {
- error << string_compose (_("cannot open pan automation file %1 (%2)"),
- automation_path, strerror (errno))
- << endmsg;
- return -1;
- }
-
- sp = _streampanners.begin();
-
- while (in.getline (line, sizeof(line), '\n')) {
-
- if (++linecnt == 1) {
- if (memcmp (line, X_("version"), 7) == 0) {
- if (sscanf (line, "version %f", &version) != 1) {
- error << string_compose(_("badly formed version number in pan automation event file \"%1\""), automation_path) << endmsg;
- return -1;
- }
- } else {
- error << string_compose(_("no version information in pan automation event file \"%1\" (first line = %2)"),
- automation_path, line) << endmsg;
- return -1;
- }
-
- continue;
- }
-
- if (strlen (line) == 0 || line[0] == '#') {
- continue;
- }
-
- if (strcmp (line, "begin") == 0) {
-
- if (sp == _streampanners.end()) {
- error << string_compose (_("too many panner states found in pan automation file %1"),
- automation_path)
- << endmsg;
- return -1;
- }
-
- if ((*sp)->load (in, automation_path, linecnt)) {
- return -1;
- }
-
- ++sp;
- }
- }
-
- 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);
- }
+ return _pannable->touching ();
}
-string
-Panner::value_as_string (double v)
+set<Evoral::Parameter>
+Panner::what_can_be_automated() const
{
- if (Panner::equivalent (v, 0.5)) {
- return _("C");
- } else if (equivalent (v, 0)) {
- return _("L");
- } else if (equivalent (v, 1)) {
- return _("R");
- } else if (v < 0.5) {
- stringstream s;
- s << fixed << setprecision (0) << _("L") << ((0.5 - v) * 200) << "%";
- return s.str();
- } else {
- stringstream s;
- s << fixed << setprecision (0) << _("R") << ((v -0.5) * 200) << "%";
- return s.str ();
- }
-
- return "";
-}
-
-void
-Panner::setup_speakers (uint32_t nouts)
-{
- switch (nouts) {
- case 3:
- /* top, bottom kind-of-left & bottom kind-of-right */
- outputs.push_back (AngularVector (90.0, 0.0));
- outputs.push_back (AngularVector (215.0, 0,0));
- outputs.push_back (AngularVector (335.0, 0,0));
- break;
- case 4:
- /* clockwise from top left */
- outputs.push_back (AngularVector (135.0, 0.0));
- outputs.push_back (AngularVector (45.0, 0.0));
- outputs.push_back (AngularVector (335.0, 0.0));
- outputs.push_back (AngularVector (215.0, 0.0));
- break;
-
- default:
- {
- double degree_step = 360.0 / nouts;
- double deg;
- uint32_t n;
-
- /* even number of speakers? make sure the top two are either side of "top".
- otherwise, just start at the "top" (90.0 degrees) and rotate around
- */
-
- if (nouts % 2) {
- deg = 90.0 - degree_step;
- } else {
- deg = 90.0;
- }
- for (n = 0; n < nouts; ++n, deg += degree_step) {
- outputs.push_back (Output (AngularVector (deg, 0.0)));
- }
- }
- }
-
- Speakers& speakers (_session.get_speakers());
-
- speakers.clear_speakers ();
-
- for (vector<Output>::iterator o = outputs.begin(); o != outputs.end(); ++o) {
- speakers.add_speaker ((*o).position);
- }
-}
-
-void
-Panner::set_stereo_width (double val)
-{
- boost::shared_ptr<AutomationControl> dc = direction_control();
- if (dc) {
- dc->set_value (val);
- }
-}
-
-void
-Panner::set_stereo_position (double val)
-{
- boost::shared_ptr<AutomationControl> wc = width_control();
- if (wc) {
- wc->set_value (val);
- }
-}
-
-bool
-Panner::set_stereo_pan (double direction_as_lr_fract, double width)
-{
- AngularVector p (BaseStereoPanner::lr_fract_to_azimuth (direction_as_lr_fract), 0.0);
- /* width parameter ranges from -1..+1 with 0.0 at center. we want 0..+1 plus knowing
- whether its "reversed" (i.e. left signal pans right and right signal pans left).
- full width = 180 degrees
- */
- double spread = 2.0 * fabs(width/2.0) * 180.0;
- double l_pos = p.azi + (spread/2.0); /* more left is "increasing degrees" */
- double r_pos = p.azi - (spread/2.0); /* more right is "decreasing degrees" */
- bool move_left = true;
- bool move_right = true;
- int l_index = 0;
- int r_index = 1;
-
- assert (_streampanners.size() > 1);
-
- if (width < 0.0) {
- swap (l_index, r_index);
- }
-
- l_pos = max (min (l_pos, 180.0), 0.0);
- r_pos = max (min (r_pos, 180.0), 0.0);
-
- /* if the new left position is less than or equal to 180 (hard left) and the left panner
- is already there, we're not moving the left signal.
- */
-
- if (l_pos >= 180.0 &&_streampanners[l_index]->get_position().azi == 180.0) {
- move_left = false;
- }
-
- /* if the new right position is less than or equal to zero (hard right) and the right panner
- is already there, we're not moving the right signal.
- */
-
- if (r_pos <= 0.0 && _streampanners[r_index]->get_position().azi == 0.0) {
- move_right = false;
- }
-
- if (move_left && move_right) {
- _streampanners[l_index]->set_position (AngularVector (l_pos, 0.0));
- _streampanners[r_index]->set_position (AngularVector (r_pos, 0.0));
- }
-
- return move_left && move_right;
-}
-
-void
-Panner::setup_meta_controls ()
-{
- if (_streampanners.size() != 2 || outputs.size() != 2) {
- return;
- }
-
- /* 2 signals to 2 outputs: provide "classic" controls for easier manipulation.
-
- The ID numbers used here don't really matter that much, because Parameters are scoped by owner,
- but they keep us out of the ordinary range of pan-related parameters.
- */
-
- Evoral::Parameter lr_param (PanAutomation, 0, 100);
- Evoral::Parameter width_param (PanAutomation, 0, 200);
- boost::shared_ptr<AutomationControl> dc = automation_control (lr_param);
- boost::shared_ptr<AutomationControl> wc = automation_control (width_param);
-
- if (dc) {
- /* reset parent StreamPanner as the current one may have been deleted */
- boost::shared_ptr<StreamPanner::PanControllable> p = boost::dynamic_pointer_cast<StreamPanner::PanControllable> (dc);
- assert (p);
- p->streampanner = _streampanners.front ();
- } else {
- dc.reset (new StreamPanner::PanControllable (_session, _("lr"), _streampanners.front(), lr_param));
- add_control (dc);
- }
-
- if (wc) {
- /* reset parent as above */
- boost::shared_ptr<StreamPanner::PanControllable> p = boost::dynamic_pointer_cast<StreamPanner::PanControllable> (wc);
- assert (p);
- p->streampanner = _streampanners.front ();
- } else {
- wc.reset (new StreamPanner::PanControllable (_session, _("width"), _streampanners.front(), width_param));
- add_control (wc);
- }
-
- dc->set_value (0.5);
- wc->set_value (1.0); // full width
+ return _pannable->what_can_be_automated ();
}
string
-Panner::describe_parameter (Evoral::Parameter param)
+Panner::describe_parameter (Evoral::Parameter p)
{
- if (param.type() == PanAutomation) {
- switch (param.id()) {
- case 100:
- return "Pan:position";
- case 200:
- return "Pan:width";
- default:
- if (_streampanners.size() == 2) {
- switch (param.id()) {
- case 0:
- return "Pan L";
- default:
- return "Pan R";
- }
- } else {
- stringstream ss;
- ss << "Pan " << param.id() + 1;
- return ss.str ();
- }
- }
- }
-
- return Automatable::describe_parameter (param);
+ return _pannable->describe_parameter (p);
}
diff --git a/libs/ardour/panner_shell.cc b/libs/ardour/panner_shell.cc
index c6cbbe36ab..7963682bcf 100644
--- a/libs/ardour/panner_shell.cc
+++ b/libs/ardour/panner_shell.cc
@@ -102,6 +102,12 @@ PannerShell::configure_io (ChanCount in, ChanCount out)
}
_panner.reset (pi->descriptor.factory (_pannable, _session.get_speakers()));
+ _panner->configure_io (in, out);
+
+ /* PANNER_HACKS: only the real owner should be able to claim the pannable
+ */
+
+ _pannable->set_panner (_panner);
Changed (); /* EMIT SIGNAL */
}
diff --git a/libs/ardour/port_insert.cc b/libs/ardour/port_insert.cc
index f0940670d4..df758e2230 100644
--- a/libs/ardour/port_insert.cc
+++ b/libs/ardour/port_insert.cc
@@ -41,9 +41,9 @@ using namespace std;
using namespace ARDOUR;
using namespace PBD;
-PortInsert::PortInsert (Session& s, boost::shared_ptr<MuteMaster> mm)
+PortInsert::PortInsert (Session& s, boost::shared_ptr<Pannable> pannable, boost::shared_ptr<MuteMaster> mm)
: IOProcessor (s, true, true, string_compose (_("insert %1"), (bitslot = s.next_insert_id()) + 1), "")
- , _out (new Delivery (s, _output, mm, _name, Delivery::Insert))
+ , _out (new Delivery (s, _output, pannable, mm, _name, Delivery::Insert))
{
_mtdm = 0;
_latency_detect = false;
diff --git a/libs/ardour/route.cc b/libs/ardour/route.cc
index c1004a18f9..b732fd09e8 100644
--- a/libs/ardour/route.cc
+++ b/libs/ardour/route.cc
@@ -46,7 +46,9 @@
#include "ardour/meter.h"
#include "ardour/mix.h"
#include "ardour/monitor_processor.h"
+#include "ardour/pannable.h"
#include "ardour/panner.h"
+#include "ardour/panner_shell.h"
#include "ardour/plugin_insert.h"
#include "ardour/port.h"
#include "ardour/port_insert.h"
@@ -106,7 +108,7 @@ Route::Route (Session& sess, string name, Flag flg, DataType default_type)
int
Route::init ()
{
- /* add standard controls */
+ /* add standard controls */
_solo_control->set_flags (Controllable::Flag (_solo_control->flags() | Controllable::Toggle));
_mute_control->set_flags (Controllable::Flag (_mute_control->flags() | Controllable::Toggle));
@@ -114,6 +116,10 @@ Route::init ()
add_control (_solo_control);
add_control (_mute_control);
+ /* panning */
+
+ _pannable.reset (new Pannable (_session));
+
/* input and output objects */
_input.reset (new IO (_session, _name, IO::Input, _default_type));
@@ -136,7 +142,7 @@ Route::init ()
add_processor (_meter, PostFader);
- _main_outs.reset (new Delivery (_session, _output, _mute_master, _name, Delivery::Main));
+ _main_outs.reset (new Delivery (_session, _output, _pannable, _mute_master, _name, Delivery::Main));
add_processor (_main_outs, PostFader);
@@ -163,7 +169,9 @@ Route::init ()
/* no panning on the monitor main outs */
+#ifdef PANNER_HACKS
_main_outs->panner()->set_bypassed (true);
+#endif
}
if (is_master() || is_monitor() || is_hidden()) {
@@ -975,14 +983,14 @@ Route::add_processor_from_xml_2X (const XMLNode& node, int version)
} else {
- processor.reset (new PortInsert (_session, _mute_master));
+ processor.reset (new PortInsert (_session, _pannable, _mute_master));
}
}
} else if (node.name() == "Send") {
- processor.reset (new Send (_session, _mute_master));
+ processor.reset (new Send (_session, _pannable, _mute_master));
} else {
@@ -1852,6 +1860,8 @@ Route::state(bool full_state)
cmt->add_content (_comment);
}
+ node->add_child_nocopy (_pannable->state (full_state));
+
for (i = _processors.begin(); i != _processors.end(); ++i) {
node->add_child_nocopy((*i)->state (full_state));
}
@@ -1928,6 +1938,11 @@ Route::_set_state (const XMLNode& node, int version, bool /*call_base*/)
if (child->name() == X_("Processor")) {
processor_state.add_child_copy (*child);
}
+
+
+ if (child->name() == X_("Pannable")) {
+ _pannable->set_state (*child, version);
+ }
}
set_processor_state (processor_state);
@@ -2233,7 +2248,7 @@ Route::_set_state_2X (const XMLNode& node, int version)
io_child = *io_niter;
if (io_child->name() == X_("Panner")) {
- _main_outs->panner()->set_state(*io_child, version);
+ _main_outs->panner_shell()->set_state(*io_child, version);
} else if (io_child->name() == X_("Automation")) {
/* IO's automation is for the fader */
_amp->set_automation_xml_state (*io_child, Evoral::Parameter (GainAutomation));
@@ -2370,7 +2385,7 @@ Route::set_processor_state (const XMLNode& node)
if (prop->value() == "intsend") {
- processor.reset (new InternalSend (_session, _mute_master, boost::shared_ptr<Route>(), Delivery::Role (0)));
+ processor.reset (new InternalSend (_session, _pannable, _mute_master, boost::shared_ptr<Route>(), Delivery::Role (0)));
} else if (prop->value() == "ladspa" || prop->value() == "Ladspa" ||
prop->value() == "lv2" ||
@@ -2381,11 +2396,11 @@ Route::set_processor_state (const XMLNode& node)
} else if (prop->value() == "port") {
- processor.reset (new PortInsert (_session, _mute_master));
+ processor.reset (new PortInsert (_session, _pannable, _mute_master));
} else if (prop->value() == "send") {
- processor.reset (new Send (_session, _mute_master));
+ processor.reset (new Send (_session, _pannable, _mute_master));
} else {
error << string_compose(_("unknown Processor type \"%1\"; ignored"), prop->value()) << endmsg;
@@ -2540,11 +2555,11 @@ Route::listen_via (boost::shared_ptr<Route> route, Placement placement, bool /*a
/* master never sends to control outs */
return 0;
} else {
- listener.reset (new InternalSend (_session, _mute_master, route, (aux ? Delivery::Aux : Delivery::Listen)));
+ listener.reset (new InternalSend (_session, _pannable, _mute_master, route, (aux ? Delivery::Aux : Delivery::Listen)));
}
} else {
- listener.reset (new InternalSend (_session, _mute_master, route, (aux ? Delivery::Aux : Delivery::Listen)));
+ listener.reset (new InternalSend (_session, _pannable, _mute_master, route, (aux ? Delivery::Aux : Delivery::Listen)));
}
} catch (failed_constructor& err) {
@@ -3141,8 +3156,7 @@ Route::set_latency_delay (framecnt_t longest_session_latency)
void
Route::automation_snapshot (framepos_t now, bool force)
{
- panner()->automation_snapshot (now, force);
-
+ _pannable->automation_snapshot (now, force);
for (ProcessorList::iterator i = _processors.begin(); i != _processors.end(); ++i) {
(*i)->automation_snapshot (now, force);
}
@@ -3271,11 +3285,10 @@ Route::shift (framepos_t pos, framecnt_t frames)
/* pan automation */
{
- boost::shared_ptr<AutomationControl> pc;
- uint32_t npans = _main_outs->panner()->npanners();
-
- for (uint32_t p = 0; p < npans; ++p) {
- pc = _main_outs->panner()->pan_control (0, p);
+ ControlSet::Controls& c (_pannable->controls());
+
+ for (ControlSet::Controls::const_iterator ci = c.begin(); ci != c.end(); ++ci) {
+ boost::shared_ptr<AutomationControl> pc = boost::dynamic_pointer_cast<AutomationControl> (ci->second);
if (pc) {
boost::shared_ptr<AutomationList> al = pc->alist();
XMLNode& before = al->get_state ();
@@ -3461,10 +3474,23 @@ Route::meter ()
}
}
+boost::shared_ptr<Pannable>
+Route::pannable() const
+{
+ return _pannable;
+}
+
boost::shared_ptr<Panner>
Route::panner() const
{
- return _main_outs->panner();
+ /* may be null ! */
+ return _main_outs->panner_shell()->panner();
+}
+
+boost::shared_ptr<PannerShell>
+Route::panner_shell() const
+{
+ return _main_outs->panner_shell();
}
boost::shared_ptr<AutomationControl>
diff --git a/libs/ardour/send.cc b/libs/ardour/send.cc
index 33b4277d93..57c8500454 100644
--- a/libs/ardour/send.cc
+++ b/libs/ardour/send.cc
@@ -38,8 +38,8 @@ using namespace ARDOUR;
using namespace PBD;
using namespace std;
-Send::Send (Session& s, boost::shared_ptr<MuteMaster> mm, Role r)
- : Delivery (s, mm, string_compose (_("send %1"), (_bitslot = s.next_send_id()) + 1), r)
+Send::Send (Session& s, boost::shared_ptr<Pannable> p, boost::shared_ptr<MuteMaster> mm, Role r)
+ : Delivery (s, p, mm, string_compose (_("send %1"), (_bitslot = s.next_send_id()) + 1), r)
, _metering (false)
{
_amp.reset (new Amp (_session));
diff --git a/libs/ardour/session.cc b/libs/ardour/session.cc
index 9ac7dcc908..e569188a38 100644
--- a/libs/ardour/session.cc
+++ b/libs/ardour/session.cc
@@ -99,7 +99,7 @@
#include "ardour/tempo.h"
#include "ardour/utils.h"
#include "ardour/graph.h"
-#include "ardour/vbap_speakers.h"
+#include "ardour/speakers.h"
#include "ardour/operations.h"
#include "midi++/port.h"
@@ -4207,10 +4207,6 @@ Session::ensure_search_path_includes (const string& path, DataType type)
Speakers&
Session::get_speakers()
{
- if (!_speakers) {
- _speakers = new Speakers;
- }
-
return *_speakers;
}
diff --git a/libs/ardour/session_state.cc b/libs/ardour/session_state.cc
index 4f11291637..54c36a3459 100644
--- a/libs/ardour/session_state.cc
+++ b/libs/ardour/session_state.cc
@@ -92,7 +92,7 @@
#include "ardour/midi_source.h"
#include "ardour/midi_track.h"
#include "ardour/named_selection.h"
-#include "ardour/panner.h"
+#include "ardour/pannable.h"
#include "ardour/processor.h"
#include "ardour/port.h"
#include "ardour/region_factory.h"
@@ -220,7 +220,7 @@ Session::first_stage_init (string fullpath, string snapshot_name)
midi_control_ui = 0;
_step_editors = 0;
no_questions_about_missing_files = false;
- _speakers = 0;
+ _speakers = new Speakers;
AudioDiskstream::allocate_working_buffers();
@@ -1174,6 +1174,8 @@ Session::state(bool full_state)
}
}
+ node->add_child_nocopy (_speakers->get_state());
+
node->add_child_nocopy (_tempo_map->get_state());
node->add_child_nocopy (get_control_protocol_state());
@@ -1294,6 +1296,13 @@ Session::set_state (const XMLNode& node, int version)
goto out;
}
+ if ((child = find_named_node (node, X_("Speakers"))) == 0) {
+ warning << _("Session: XML state has no speakers section - assuming simple stereo") << endmsg;
+ _speakers->setup_default_speakers (2);
+ } else {
+ _speakers->set_state (*child, version);
+ }
+
Location* location;
if ((location = _locations->auto_loop_location()) != 0) {
@@ -2990,19 +2999,19 @@ Session::controllable_by_descriptor (const ControllableDescriptor& desc)
case ControllableDescriptor::PanDirection:
{
- boost::shared_ptr<Panner> p = r->panner();
- if (p) {
- c = p->direction_control();
- }
+ c = r->pannable()->pan_azimuth_control;
break;
}
case ControllableDescriptor::PanWidth:
{
- boost::shared_ptr<Panner> p = r->panner();
- if (p) {
- c = p->width_control();
- }
+ c = r->pannable()->pan_width_control;
+ break;
+ }
+
+ case ControllableDescriptor::PanElevation:
+ {
+ c = r->pannable()->pan_elevation_control;
break;
}
diff --git a/libs/ardour/speakers.cc b/libs/ardour/speakers.cc
index 7d0461ff52..4229e77c65 100644
--- a/libs/ardour/speakers.cc
+++ b/libs/ardour/speakers.cc
@@ -16,9 +16,15 @@
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
+#include "pbd/error.h"
+#include "pbd/convert.h"
+#include "pbd/locale_guard.h"
+
#include "ardour/speaker.h"
#include "ardour/speakers.h"
+#include "i18n.h"
+
using namespace ARDOUR;
using namespace PBD;
using namespace std;
@@ -103,3 +109,119 @@ Speakers::move_speaker (int id, const AngularVector& new_position)
}
}
}
+
+void
+Speakers::setup_default_speakers (uint32_t n)
+{
+ /* default assignment of speaker position for n speakers */
+
+ assert (n>0);
+
+ switch (n) {
+ case 1:
+ add_speaker (AngularVector (0.0, 0.0));
+ break;
+
+ case 2:
+ add_speaker (AngularVector (0.0, 0.0));
+ add_speaker (AngularVector (180.0, 0,0));
+ break;
+
+ case 3:
+ /* top, bottom kind-of-left & bottom kind-of-right */
+ add_speaker (AngularVector (90.0, 0.0));
+ add_speaker (AngularVector (215.0, 0,0));
+ add_speaker (AngularVector (335.0, 0,0));
+ break;
+ case 4:
+ /* clockwise from top left */
+ add_speaker (AngularVector (135.0, 0.0));
+ add_speaker (AngularVector (45.0, 0.0));
+ add_speaker (AngularVector (335.0, 0.0));
+ add_speaker (AngularVector (215.0, 0.0));
+ break;
+
+ default:
+ {
+ double degree_step = 360.0 / n;
+ double deg;
+ uint32_t i;
+
+ /* even number of speakers? make sure the top two are either side of "top".
+ otherwise, just start at the "top" (90.0 degrees) and rotate around
+ */
+
+ if (n % 2) {
+ deg = 90.0 - degree_step;
+ } else {
+ deg = 90.0;
+ }
+ for (i = 0; i < n; ++i, deg += degree_step) {
+ add_speaker (AngularVector (deg, 0.0));
+ }
+ }
+ }
+}
+
+XMLNode&
+Speakers::get_state ()
+{
+ XMLNode* node = new XMLNode (X_("Speakers"));
+ char buf[32];
+ LocaleGuard lg (X_("POSIX"));
+
+ for (vector<Speaker>::const_iterator i = _speakers.begin(); i != _speakers.end(); ++i) {
+ XMLNode* speaker = new XMLNode (X_("Speaker"));
+
+ snprintf (buf, sizeof (buf), "%.12g", (*i).angles().azi);
+ speaker->add_property (X_("azimuth"), buf);
+ snprintf (buf, sizeof (buf), "%.12g", (*i).angles().ele);
+ speaker->add_property (X_("elevation"), buf);
+ snprintf (buf, sizeof (buf), "%.12g", (*i).angles().length);
+ speaker->add_property (X_("distance"), buf);
+
+ node->add_child_nocopy (*speaker);
+ }
+
+ return *node;
+}
+
+int
+Speakers::set_state (const XMLNode& node, int /*version*/)
+{
+ XMLNodeConstIterator i;
+ const XMLProperty* prop;
+ double a, e, d;
+ LocaleGuard lg (X_("POSIX"));
+ int n = 0;
+
+ _speakers.clear ();
+
+ for (i = node.children().begin(); i != node.children().end(); ++i, ++n) {
+ if ((*i)->name() == X_("Speaker")) {
+ if ((prop = (*i)->property (X_("azimuth"))) == 0) {
+ warning << _("Speaker information is missing azimuth - speaker ignored") << endmsg;
+ continue;
+ }
+ a = atof (prop->value());
+
+ if ((prop = (*i)->property (X_("elevation"))) == 0) {
+ warning << _("Speaker information is missing elevation - speaker ignored") << endmsg;
+ continue;
+ }
+ e = atof (prop->value());
+
+ if ((prop = (*i)->property (X_("distance"))) == 0) {
+ warning << _("Speaker information is missing distance - speaker ignored") << endmsg;
+ continue;
+ }
+ d = atof (prop->value());
+
+ add_speaker (AngularVector (a, e, d));
+ }
+ }
+
+ update ();
+
+ return 0;
+}
diff --git a/libs/ardour/thread_buffers.cc b/libs/ardour/thread_buffers.cc
index 2436b6d365..f30edee572 100644
--- a/libs/ardour/thread_buffers.cc
+++ b/libs/ardour/thread_buffers.cc
@@ -18,6 +18,7 @@
*/
#include <iostream>
+#include <algorithm>
#include "ardour/audioengine.h"
#include "ardour/buffer_set.h"
@@ -68,6 +69,10 @@ ThreadBuffers::ensure_buffers (ChanCount howmany)
void
ThreadBuffers::allocate_pan_automation_buffers (framecnt_t nframes, uint32_t howmany, bool force)
{
+ /* we always need at least 2 pan buffers */
+
+ howmany = max (2U, howmany);
+
if (!force && howmany <= npan_buffers) {
return;
}
diff --git a/libs/ardour/vbap.cc b/libs/ardour/vbap.cc
deleted file mode 100644
index 96fc1336fb..0000000000
--- a/libs/ardour/vbap.cc
+++ /dev/null
@@ -1,240 +0,0 @@
-
-/*
- This software is being provided to you, the licensee, by Ville Pulkki,
- under the following license. By obtaining, using and/or copying this
- software, you agree that you have read, understood, and will comply
- with these terms and conditions: Permission to use, copy, modify and
- distribute, including the right to grant others rights to distribute
- at any tier, this software and its documentation for any purpose and
- without fee or royalty is hereby granted, provided that you agree to
- comply with the following copyright notice and statements, including
- the disclaimer, and that the same appear on ALL copies of the software
- and documentation, including modifications that you make for internal
- use or for distribution:
-
- Copyright 1998 by Ville Pulkki, Helsinki University of Technology. All
- rights reserved.
-
- The software may be used, distributed, and included to commercial
- products without any charges. When included to a commercial product,
- the method "Vector Base Amplitude Panning" and its developer Ville
- Pulkki must be referred to in documentation.
-
- This software is provided "as is", and Ville Pulkki or Helsinki
- University of Technology make no representations or warranties,
- expressed or implied. By way of example, but not limitation, Helsinki
- University of Technology or Ville Pulkki make no representations or
- warranties of merchantability or fitness for any particular purpose or
- that the use of the licensed software or documentation will not
- infringe any third party patents, copyrights, trademarks or other
- rights. The name of Ville Pulkki or Helsinki University of Technology
- may not be used in advertising or publicity pertaining to distribution
- of the software.
-*/
-
-#include <cmath>
-#include <cstdlib>
-#include <cstdio>
-#include <cstring>
-
-#include <iostream>
-#include <string>
-
-#include "pbd/cartesian.h"
-
-#include "ardour/speakers.h"
-#include "ardour/vbap.h"
-#include "ardour/vbap_speakers.h"
-#include "ardour/audio_buffer.h"
-#include "ardour/buffer_set.h"
-
-using namespace PBD;
-using namespace ARDOUR;
-using namespace std;
-
-string VBAPanner::name = X_("VBAP");
-
-VBAPanner::VBAPanner (Panner& parent, Evoral::Parameter param, Speakers& s)
- : StreamPanner (parent, param)
- , _dirty (true)
- , _speakers (VBAPSpeakers::instance (s))
-{
-}
-
-VBAPanner::~VBAPanner ()
-{
-}
-
-void
-VBAPanner::update ()
-{
- /* force 2D for now */
- _angles.ele = 0.0;
- _dirty = true;
-
- Changed ();
-}
-
-void
-VBAPanner::compute_gains (double gains[3], int speaker_ids[3], int azi, int ele)
-{
- /* calculates gain factors using loudspeaker setup and given direction */
- double cartdir[3];
- double power;
- int i,j,k;
- double small_g;
- double big_sm_g, gtmp[3];
-
- azi_ele_to_cart (azi,ele, cartdir[0], cartdir[1], cartdir[2]);
- big_sm_g = -100000.0;
-
- gains[0] = gains[1] = gains[2] = 0;
- speaker_ids[0] = speaker_ids[1] = speaker_ids[2] = 0;
-
- for (i = 0; i < _speakers.n_tuples(); i++) {
-
- small_g = 10000000.0;
-
- for (j = 0; j < _speakers.dimension(); j++) {
-
- gtmp[j] = 0.0;
-
- for (k = 0; k < _speakers.dimension(); k++) {
- gtmp[j] += cartdir[k] * _speakers.matrix(i)[j*_speakers.dimension()+k];
- }
-
- if (gtmp[j] < small_g) {
- small_g = gtmp[j];
- }
- }
-
- if (small_g > big_sm_g) {
-
- big_sm_g = small_g;
-
- gains[0] = gtmp[0];
- gains[1] = gtmp[1];
-
- speaker_ids[0] = _speakers.speaker_for_tuple (i, 0);
- speaker_ids[1] = _speakers.speaker_for_tuple (i, 1);
-
- if (_speakers.dimension() == 3) {
- gains[2] = gtmp[2];
- speaker_ids[2] = _speakers.speaker_for_tuple (i, 2);
- } else {
- gains[2] = 0.0;
- speaker_ids[2] = -1;
- }
- }
- }
-
- power = sqrt (gains[0]*gains[0] + gains[1]*gains[1] + gains[2]*gains[2]);
-
- if (power > 0) {
- gains[0] /= power;
- gains[1] /= power;
- gains[2] /= power;
- }
-
- _dirty = false;
-}
-
-void
-VBAPanner::do_distribute (AudioBuffer& srcbuf, BufferSet& obufs, gain_t gain_coefficient, pframes_t nframes)
-{
- if (_muted) {
- return;
- }
-
- Sample* const src = srcbuf.data();
- Sample* dst;
- pan_t pan;
- uint32_t n_audio = obufs.count().n_audio();
- bool was_dirty;
-
- if ((was_dirty = _dirty)) {
- compute_gains (desired_gains, desired_outputs, _angles.azi, _angles.ele);
- cerr << " @ " << _angles.azi << " /= " << _angles.ele
- << " Outputs: "
- << desired_outputs[0] + 1 << ' '
- << desired_outputs[1] + 1 << ' '
- << " Gains "
- << desired_gains[0] << ' '
- << desired_gains[1] << ' '
- << endl;
- }
-
- bool todo[n_audio];
-
- for (uint32_t o = 0; o < n_audio; ++o) {
- todo[o] = true;
- }
-
-
- /* VBAP may distribute the signal across up to 3 speakers depending on
- the configuration of the speakers.
- */
-
- for (int o = 0; o < 3; ++o) {
- if (desired_outputs[o] != -1) {
-
- pframes_t n = 0;
-
- /* XXX TODO: interpolate across changes in gain and/or outputs
- */
-
- dst = obufs.get_audio(desired_outputs[o]).data();
-
- pan = gain_coefficient * desired_gains[o];
- mix_buffers_with_gain (dst+n,src+n,nframes-n,pan);
-
- todo[o] = false;
- }
- }
-
- for (uint32_t o = 0; o < n_audio; ++o) {
- if (todo[o]) {
- /* VBAP decided not to deliver any audio to this output, so we write silence */
- dst = obufs.get_audio(o).data();
- memset (dst, 0, sizeof (Sample) * nframes);
- }
- }
-
- if (was_dirty) {
- memcpy (gains, desired_gains, sizeof (gains));
- memcpy (outputs, desired_outputs, sizeof (outputs));
- }
-}
-
-void
-VBAPanner::do_distribute_automated (AudioBuffer& src, BufferSet& obufs,
- framepos_t start, framepos_t end, pframes_t nframes, pan_t** buffers)
-{
-}
-
-XMLNode&
-VBAPanner::get_state ()
-{
- return state (true);
-}
-
-XMLNode&
-VBAPanner::state (bool full_state)
-{
- XMLNode& node (StreamPanner::get_state());
- node.add_property (X_("type"), VBAPanner::name);
- return node;
-}
-
-int
-VBAPanner::set_state (const XMLNode& node, int /*version*/)
-{
- return 0;
-}
-
-StreamPanner*
-VBAPanner::factory (Panner& parent, Evoral::Parameter param, Speakers& s)
-{
- return new VBAPanner (parent, param, s);
-}
-
diff --git a/libs/ardour/vbap_speakers.cc b/libs/ardour/vbap_speakers.cc
deleted file mode 100644
index 9090ed65e1..0000000000
--- a/libs/ardour/vbap_speakers.cc
+++ /dev/null
@@ -1,658 +0,0 @@
-/*
- This software is being provided to you, the licensee, by Ville Pulkki,
- under the following license. By obtaining, using and/or copying this
- software, you agree that you have read, understood, and will comply
- with these terms and conditions: Permission to use, copy, modify and
- distribute, including the right to grant others rights to distribute
- at any tier, this software and its documentation for any purpose and
- without fee or royalty is hereby granted, provided that you agree to
- comply with the following copyright notice and statements, including
- the disclaimer, and that the same appear on ALL copies of the software
- and documentation, including modifications that you make for internal
- use or for distribution:
-
- Copyright 1998 by Ville Pulkki, Helsinki University of Technology. All
- rights reserved.
-
- The software may be used, distributed, and included to commercial
- products without any charges. When included to a commercial product,
- the method "Vector Base Amplitude Panning" and its developer Ville
- Pulkki must be referred to in documentation.
-
- This software is provided "as is", and Ville Pulkki or Helsinki
- University of Technology make no representations or warranties,
- expressed or implied. By way of example, but not limitation, Helsinki
- University of Technology or Ville Pulkki make no representations or
- warranties of merchantability or fitness for any particular purpose or
- that the use of the licensed software or documentation will not
- infringe any third party patents, copyrights, trademarks or other
- rights. The name of Ville Pulkki or Helsinki University of Technology
- may not be used in advertising or publicity pertaining to distribution
- of the software.
-*/
-
-#include <cmath>
-#include <algorithm>
-#include <stdlib.h>
-
-#include "pbd/cartesian.h"
-#include "ardour/vbap_speakers.h"
-
-using namespace ARDOUR;
-using namespace PBD;
-using namespace std;
-
-VBAPSpeakers* VBAPSpeakers::_instance = 0;
-
-VBAPSpeakers&
-VBAPSpeakers::instance (Speakers& s)
-{
- if (_instance == 0) {
- _instance = new VBAPSpeakers (s);
- }
-
- return *_instance;
-}
-
-VBAPSpeakers::VBAPSpeakers (Speakers& s)
- : _dimension (2)
- , _speakers (s.speakers())
-{
- s.Changed.connect_same_thread (speaker_connection, boost::bind (&VBAPSpeakers::update, this));
-}
-
-VBAPSpeakers::~VBAPSpeakers ()
-{
-}
-
-void
-VBAPSpeakers::update ()
-{
- int dim = 2;
-
- for (vector<Speaker>::const_iterator i = _speakers.begin(); i != _speakers.end(); ++i) {
- if ((*i).angles().ele != 0.0) {
- cerr << "\n\n\nSPEAKER " << (*i).id << " has ele = " << (*i).angles().ele << "\n\n\n\n";
- dim = 3;
- break;
- }
- }
-
- _dimension = dim;
-
- cerr << "update with dimension = " << dim << " speakers = " << _speakers.size() << endl;
-
- if (_speakers.size() < 2) {
- /* nothing to be done with less than two speakers */
- return;
- }
-
- if (_dimension == 3) {
- ls_triplet_chain *ls_triplets = 0;
- choose_speaker_triplets (&ls_triplets);
- if (ls_triplets) {
- calculate_3x3_matrixes (ls_triplets);
- free (ls_triplets);
- }
- } else {
- choose_speaker_pairs ();
- }
-}
-
-void
-VBAPSpeakers::choose_speaker_triplets(struct ls_triplet_chain **ls_triplets)
-{
- /* Selects the loudspeaker triplets, and
- calculates the inversion matrices for each selected triplet.
- A line (connection) is drawn between each loudspeaker. The lines
- denote the sides of the triangles. The triangles should not be
- intersecting. All crossing connections are searched and the
- longer connection is erased. This yields non-intesecting triangles,
- which can be used in panning.
- */
-
- int i,j,k,l,table_size;
- int n_speakers = _speakers.size ();
- int connections[n_speakers][n_speakers];
- float distance_table[((n_speakers * (n_speakers - 1)) / 2)];
- int distance_table_i[((n_speakers * (n_speakers - 1)) / 2)];
- int distance_table_j[((n_speakers * (n_speakers - 1)) / 2)];
- float distance;
- struct ls_triplet_chain *trip_ptr, *prev, *tmp_ptr;
-
- if (n_speakers == 0) {
- return;
- }
-
- for (i = 0; i < n_speakers; i++) {
- for (j = i+1; j < n_speakers; j++) {
- for(k=j+1;k<n_speakers;k++) {
- if (vol_p_side_lgth(i,j, k, _speakers) > MIN_VOL_P_SIDE_LGTH){
- connections[i][j]=1;
- connections[j][i]=1;
- connections[i][k]=1;
- connections[k][i]=1;
- connections[j][k]=1;
- connections[k][j]=1;
- add_ldsp_triplet(i,j,k,ls_triplets);
- }
- }
- }
- }
-
- /*calculate distancies between all speakers and sorting them*/
- table_size =(((n_speakers - 1) * (n_speakers)) / 2);
- for (i = 0; i < table_size; i++) {
- distance_table[i] = 100000.0;
- }
-
- for (i = 0;i < n_speakers; i++) {
- for (j = i+1; j < n_speakers; j++) {
- if (connections[i][j] == 1) {
- distance = fabs(vec_angle(_speakers[i].coords(),_speakers[j].coords()));
- k=0;
- while(distance_table[k] < distance) {
- k++;
- }
- for (l = table_size - 1; l > k ; l--) {
- distance_table[l] = distance_table[l-1];
- distance_table_i[l] = distance_table_i[l-1];
- distance_table_j[l] = distance_table_j[l-1];
- }
- distance_table[k] = distance;
- distance_table_i[k] = i;
- distance_table_j[k] = j;
- } else
- table_size--;
- }
- }
-
- /* disconnecting connections which are crossing shorter ones,
- starting from shortest one and removing all that cross it,
- and proceeding to next shortest */
- for (i = 0; i < table_size; i++) {
- int fst_ls = distance_table_i[i];
- int sec_ls = distance_table_j[i];
- if (connections[fst_ls][sec_ls] == 1) {
- for (j = 0; j < n_speakers; j++) {
- for (k = j+1; k < n_speakers; k++) {
- if ((j!=fst_ls) && (k != sec_ls) && (k!=fst_ls) && (j != sec_ls)){
- if (lines_intersect(fst_ls, sec_ls, j,k) == 1){
- connections[j][k] = 0;
- connections[k][j] = 0;
- }
- }
- }
- }
- }
- }
-
- /* remove triangles which had crossing sides
- with smaller triangles or include loudspeakers*/
- trip_ptr = *ls_triplets;
- prev = 0;
- while (trip_ptr != 0){
- i = trip_ptr->ls_nos[0];
- j = trip_ptr->ls_nos[1];
- k = trip_ptr->ls_nos[2];
- if (connections[i][j] == 0 ||
- connections[i][k] == 0 ||
- connections[j][k] == 0 ||
- any_ls_inside_triplet(i,j,k) == 1 ){
- if (prev != 0) {
- prev->next = trip_ptr->next;
- tmp_ptr = trip_ptr;
- trip_ptr = trip_ptr->next;
- free(tmp_ptr);
- } else {
- *ls_triplets = trip_ptr->next;
- tmp_ptr = trip_ptr;
- trip_ptr = trip_ptr->next;
- free(tmp_ptr);
- }
- } else {
- prev = trip_ptr;
- trip_ptr = trip_ptr->next;
-
- }
- }
-}
-
-int
-VBAPSpeakers::any_ls_inside_triplet(int a, int b, int c)
-{
- /* returns 1 if there is loudspeaker(s) inside given ls triplet */
- float invdet;
- const CartesianVector* lp1;
- const CartesianVector* lp2;
- const CartesianVector* lp3;
- float invmx[9];
- int i,j;
- float tmp;
- bool any_ls_inside;
- bool this_inside;
- int n_speakers = _speakers.size();
-
- lp1 = &(_speakers[a].coords());
- lp2 = &(_speakers[b].coords());
- lp3 = &(_speakers[c].coords());
-
- /* matrix inversion */
- invdet = 1.0 / ( lp1->x * ((lp2->y * lp3->z) - (lp2->z * lp3->y))
- - lp1->y * ((lp2->x * lp3->z) - (lp2->z * lp3->x))
- + lp1->z * ((lp2->x * lp3->y) - (lp2->y * lp3->x)));
-
- invmx[0] = ((lp2->y * lp3->z) - (lp2->z * lp3->y)) * invdet;
- invmx[3] = ((lp1->y * lp3->z) - (lp1->z * lp3->y)) * -invdet;
- invmx[6] = ((lp1->y * lp2->z) - (lp1->z * lp2->y)) * invdet;
- invmx[1] = ((lp2->x * lp3->z) - (lp2->z * lp3->x)) * -invdet;
- invmx[4] = ((lp1->x * lp3->z) - (lp1->z * lp3->x)) * invdet;
- invmx[7] = ((lp1->x * lp2->z) - (lp1->z * lp2->x)) * -invdet;
- invmx[2] = ((lp2->x * lp3->y) - (lp2->y * lp3->x)) * invdet;
- invmx[5] = ((lp1->x * lp3->y) - (lp1->y * lp3->x)) * -invdet;
- invmx[8] = ((lp1->x * lp2->y) - (lp1->y * lp2->x)) * invdet;
-
- any_ls_inside = false;
- for (i = 0; i < n_speakers; i++) {
- if (i != a && i!=b && i != c) {
- this_inside = true;
- for (j = 0; j < 3; j++) {
- tmp = _speakers[i].coords().x * invmx[0 + j*3];
- tmp += _speakers[i].coords().y * invmx[1 + j*3];
- tmp += _speakers[i].coords().z * invmx[2 + j*3];
- if (tmp < -0.001) {
- this_inside = false;
- }
- }
- if (this_inside) {
- any_ls_inside = true;
- }
- }
- }
-
- return any_ls_inside;
-}
-
-
-void
-VBAPSpeakers::add_ldsp_triplet(int i, int j, int k, struct ls_triplet_chain **ls_triplets)
-{
- /* adds i,j,k triplet to triplet chain*/
-
- struct ls_triplet_chain *trip_ptr, *prev;
- trip_ptr = *ls_triplets;
- prev = 0;
-
- while (trip_ptr != 0){
- prev = trip_ptr;
- trip_ptr = trip_ptr->next;
- }
-
- trip_ptr = (struct ls_triplet_chain*) malloc (sizeof (struct ls_triplet_chain));
-
- if (prev == 0) {
- *ls_triplets = trip_ptr;
- } else {
- prev->next = trip_ptr;
- }
-
- trip_ptr->next = 0;
- trip_ptr->ls_nos[0] = i;
- trip_ptr->ls_nos[1] = j;
- trip_ptr->ls_nos[2] = k;
-}
-
-float
-VBAPSpeakers::vec_angle(CartesianVector v1, CartesianVector v2)
-{
- float inner= ((v1.x*v2.x + v1.y*v2.y + v1.z*v2.z)/
- (vec_length(v1) * vec_length(v2)));
-
- if (inner > 1.0) {
- inner= 1.0;
- }
-
- if (inner < -1.0) {
- inner = -1.0;
- }
-
- return fabsf((float) acos((double) inner));
-}
-
-float
-VBAPSpeakers::vec_length(CartesianVector v1)
-{
- return (sqrt(v1.x*v1.x + v1.y*v1.y + v1.z*v1.z));
-}
-
-float
-VBAPSpeakers::vec_prod(CartesianVector v1, CartesianVector v2)
-{
- return (v1.x*v2.x + v1.y*v2.y + v1.z*v2.z);
-}
-
-float
-VBAPSpeakers::vol_p_side_lgth(int i, int j,int k, const vector<Speaker>& speakers)
-{
- /* calculate volume of the parallelepiped defined by the loudspeaker
- direction vectors and divide it with total length of the triangle sides.
- This is used when removing too narrow triangles. */
-
- float volper, lgth;
- CartesianVector xprod;
-
- cross_prod (speakers[i].coords(), speakers[j].coords(), &xprod);
- volper = fabsf (vec_prod(xprod, speakers[k].coords()));
- lgth = (fabsf (vec_angle(speakers[i].coords(), speakers[j].coords()))
- + fabsf (vec_angle(speakers[i].coords(), speakers[k].coords()))
- + fabsf (vec_angle(speakers[j].coords(), speakers[k].coords())));
-
- if (lgth > 0.00001) {
- return volper / lgth;
- } else {
- return 0.0;
- }
-}
-
-void
-VBAPSpeakers::cross_prod(CartesianVector v1,CartesianVector v2, CartesianVector *res)
-{
- float length;
-
- res->x = (v1.y * v2.z ) - (v1.z * v2.y);
- res->y = (v1.z * v2.x ) - (v1.x * v2.z);
- res->z = (v1.x * v2.y ) - (v1.y * v2.x);
-
- length = vec_length(*res);
- res->x /= length;
- res->y /= length;
- res->z /= length;
-}
-
-int
-VBAPSpeakers::lines_intersect (int i, int j, int k, int l)
-{
- /* checks if two lines intersect on 3D sphere
- see theory in paper Pulkki, V. Lokki, T. "Creating Auditory Displays
- with Multiple Loudspeakers Using VBAP: A Case Study with
- DIVA Project" in International Conference on
- Auditory Displays -98. E-mail Ville.Pulkki@hut.fi
- if you want to have that paper.
- */
-
- CartesianVector v1;
- CartesianVector v2;
- CartesianVector v3, neg_v3;
- float dist_ij,dist_kl,dist_iv3,dist_jv3,dist_inv3,dist_jnv3;
- float dist_kv3,dist_lv3,dist_knv3,dist_lnv3;
-
- cross_prod(_speakers[i].coords(),_speakers[j].coords(),&v1);
- cross_prod(_speakers[k].coords(),_speakers[l].coords(),&v2);
- cross_prod(v1,v2,&v3);
-
- neg_v3.x= 0.0 - v3.x;
- neg_v3.y= 0.0 - v3.y;
- neg_v3.z= 0.0 - v3.z;
-
- dist_ij = (vec_angle(_speakers[i].coords(),_speakers[j].coords()));
- dist_kl = (vec_angle(_speakers[k].coords(),_speakers[l].coords()));
- dist_iv3 = (vec_angle(_speakers[i].coords(),v3));
- dist_jv3 = (vec_angle(v3,_speakers[j].coords()));
- dist_inv3 = (vec_angle(_speakers[i].coords(),neg_v3));
- dist_jnv3 = (vec_angle(neg_v3,_speakers[j].coords()));
- dist_kv3 = (vec_angle(_speakers[k].coords(),v3));
- dist_lv3 = (vec_angle(v3,_speakers[l].coords()));
- dist_knv3 = (vec_angle(_speakers[k].coords(),neg_v3));
- dist_lnv3 = (vec_angle(neg_v3,_speakers[l].coords()));
-
- /* if one of loudspeakers is close to crossing point, don't do anything*/
-
-
- if(fabsf(dist_iv3) <= 0.01 || fabsf(dist_jv3) <= 0.01 ||
- fabsf(dist_kv3) <= 0.01 || fabsf(dist_lv3) <= 0.01 ||
- fabsf(dist_inv3) <= 0.01 || fabsf(dist_jnv3) <= 0.01 ||
- fabsf(dist_knv3) <= 0.01 || fabsf(dist_lnv3) <= 0.01 ) {
- return(0);
- }
-
- if (((fabsf(dist_ij - (dist_iv3 + dist_jv3)) <= 0.01 ) &&
- (fabsf(dist_kl - (dist_kv3 + dist_lv3)) <= 0.01)) ||
- ((fabsf(dist_ij - (dist_inv3 + dist_jnv3)) <= 0.01) &&
- (fabsf(dist_kl - (dist_knv3 + dist_lnv3)) <= 0.01 ))) {
- return (1);
- } else {
- return (0);
- }
-}
-
-void
-VBAPSpeakers::calculate_3x3_matrixes(struct ls_triplet_chain *ls_triplets)
-{
- /* Calculates the inverse matrices for 3D */
- float invdet;
- const CartesianVector* lp1;
- const CartesianVector* lp2;
- const CartesianVector* lp3;
- float *invmx;
- struct ls_triplet_chain *tr_ptr = ls_triplets;
- int triplet_count = 0;
- int triplet;
-
- assert (tr_ptr);
-
- /* counting triplet amount */
-
- while (tr_ptr != 0) {
- triplet_count++;
- tr_ptr = tr_ptr->next;
- }
-
- cerr << "@@@ triplets generate " << triplet_count << " of speaker tuples\n";
-
- triplet = 0;
-
- _matrices.clear ();
- _speaker_tuples.clear ();
-
- for (int n = 0; n < triplet_count; ++n) {
- _matrices.push_back (threeDmatrix());
- _speaker_tuples.push_back (tmatrix());
- }
-
- while (tr_ptr != 0) {
- lp1 = &(_speakers[tr_ptr->ls_nos[0]].coords());
- lp2 = &(_speakers[tr_ptr->ls_nos[1]].coords());
- lp3 = &(_speakers[tr_ptr->ls_nos[2]].coords());
-
- /* matrix inversion */
- invmx = tr_ptr->inv_mx;
- invdet = 1.0 / ( lp1->x * ((lp2->y * lp3->z) - (lp2->z * lp3->y))
- - lp1->y * ((lp2->x * lp3->z) - (lp2->z * lp3->x))
- + lp1->z * ((lp2->x * lp3->y) - (lp2->y * lp3->x)));
-
- invmx[0] = ((lp2->y * lp3->z) - (lp2->z * lp3->y)) * invdet;
- invmx[3] = ((lp1->y * lp3->z) - (lp1->z * lp3->y)) * -invdet;
- invmx[6] = ((lp1->y * lp2->z) - (lp1->z * lp2->y)) * invdet;
- invmx[1] = ((lp2->x * lp3->z) - (lp2->z * lp3->x)) * -invdet;
- invmx[4] = ((lp1->x * lp3->z) - (lp1->z * lp3->x)) * invdet;
- invmx[7] = ((lp1->x * lp2->z) - (lp1->z * lp2->x)) * -invdet;
- invmx[2] = ((lp2->x * lp3->y) - (lp2->y * lp3->x)) * invdet;
- invmx[5] = ((lp1->x * lp3->y) - (lp1->y * lp3->x)) * -invdet;
- invmx[8] = ((lp1->x * lp2->y) - (lp1->y * lp2->x)) * invdet;
-
- /* copy the matrix */
-
- _matrices[triplet][0] = invmx[0];
- _matrices[triplet][1] = invmx[1];
- _matrices[triplet][2] = invmx[2];
- _matrices[triplet][3] = invmx[3];
- _matrices[triplet][4] = invmx[4];
- _matrices[triplet][5] = invmx[5];
- _matrices[triplet][6] = invmx[6];
- _matrices[triplet][7] = invmx[7];
- _matrices[triplet][8] = invmx[8];
-
- _speaker_tuples[triplet][0] = tr_ptr->ls_nos[0];
- _speaker_tuples[triplet][1] = tr_ptr->ls_nos[1];
- _speaker_tuples[triplet][2] = tr_ptr->ls_nos[2];
-
- cerr << "Triplet[" << triplet << "] = "
- << tr_ptr->ls_nos[0] << " + "
- << tr_ptr->ls_nos[1] << " + "
- << tr_ptr->ls_nos[2] << endl;
-
- triplet++;
-
- tr_ptr = tr_ptr->next;
- }
-}
-
-void
-VBAPSpeakers::choose_speaker_pairs (){
-
- /* selects the loudspeaker pairs, calculates the inversion
- matrices and stores the data to a global array
- */
- const int n_speakers = _speakers.size();
- const double AZIMUTH_DELTA_THRESHOLD_DEGREES = (180.0/M_PI) * (M_PI - 0.175);
- int sorted_speakers[n_speakers];
- bool exists[n_speakers];
- double inverse_matrix[n_speakers][4];
- int expected_pairs = 0;
- int pair;
- int speaker;
-
- cerr << "CHOOSE PAIRS\n";
-
- if (n_speakers == 0) {
- return;
- }
-
- for (speaker = 0; speaker < n_speakers; ++speaker) {
- exists[speaker] = false;
- }
-
- /* sort loudspeakers according their aximuth angle */
- sort_2D_lss (sorted_speakers);
-
- /* adjacent loudspeakers are the loudspeaker pairs to be used.*/
- for (speaker = 0; speaker < n_speakers-1; speaker++) {
-
- cerr << "Looking at "
- << _speakers[sorted_speakers[speaker]].id << " @ " << _speakers[sorted_speakers[speaker]].angles().azi
- << " and "
- << _speakers[sorted_speakers[speaker+1]].id << " @ " << _speakers[sorted_speakers[speaker+1]].angles().azi
- << " delta = "
- << _speakers[sorted_speakers[speaker+1]].angles().azi - _speakers[sorted_speakers[speaker]].angles().azi
- << endl;
-
- if ((_speakers[sorted_speakers[speaker+1]].angles().azi -
- _speakers[sorted_speakers[speaker]].angles().azi) <= AZIMUTH_DELTA_THRESHOLD_DEGREES) {
- if (calc_2D_inv_tmatrix( _speakers[sorted_speakers[speaker]].angles().azi,
- _speakers[sorted_speakers[speaker+1]].angles().azi,
- inverse_matrix[speaker]) != 0){
- exists[speaker] = true;
- expected_pairs++;
- }
- }
- }
-
- if (((6.283 - _speakers[sorted_speakers[n_speakers-1]].angles().azi)
- +_speakers[sorted_speakers[0]].angles().azi) <= AZIMUTH_DELTA_THRESHOLD_DEGREES) {
- if (calc_2D_inv_tmatrix(_speakers[sorted_speakers[n_speakers-1]].angles().azi,
- _speakers[sorted_speakers[0]].angles().azi,
- inverse_matrix[n_speakers-1]) != 0) {
- exists[n_speakers-1] = true;
- expected_pairs++;
- }
- }
-
- pair = 0;
-
- _matrices.clear ();
- _speaker_tuples.clear ();
-
- for (int n = 0; n < expected_pairs; ++n) {
- _matrices.push_back (twoDmatrix());
- _speaker_tuples.push_back (tmatrix());
- }
-
- for (speaker = 0; speaker < n_speakers - 1; speaker++) {
- if (exists[speaker]) {
- _matrices[pair][0] = inverse_matrix[speaker][0];
- _matrices[pair][1] = inverse_matrix[speaker][1];
- _matrices[pair][2] = inverse_matrix[speaker][2];
- _matrices[pair][3] = inverse_matrix[speaker][3];
-
- _speaker_tuples[pair][0] = sorted_speakers[speaker];
- _speaker_tuples[pair][1] = sorted_speakers[speaker+1];
-
- cerr << "PAIR[" << pair << "] = " << sorted_speakers[speaker] << " + " << sorted_speakers[speaker+1] << endl;
-
- pair++;
- }
- }
-
- if (exists[n_speakers-1]) {
- _matrices[pair][0] = inverse_matrix[speaker][0];
- _matrices[pair][1] = inverse_matrix[speaker][1];
- _matrices[pair][2] = inverse_matrix[speaker][2];
- _matrices[pair][3] = inverse_matrix[speaker][3];
-
- _speaker_tuples[pair][0] = sorted_speakers[n_speakers-1];
- _speaker_tuples[pair][1] = sorted_speakers[0];
-
- cerr << "PAIR[" << pair << "] = " << sorted_speakers[n_speakers-1] << " + " << sorted_speakers[0] << endl;
-
- }
-}
-
-void
-VBAPSpeakers::sort_2D_lss (int* sorted_speakers)
-{
- vector<Speaker> tmp = _speakers;
- vector<Speaker>::iterator s;
- azimuth_sorter sorter;
- int n;
-
- sort (tmp.begin(), tmp.end(), sorter);
-
- for (n = 0, s = tmp.begin(); s != tmp.end(); ++s, ++n) {
- sorted_speakers[n] = (*s).id;
- cerr << "Sorted[" << n << "] = " << (*s).id << endl;
- }
-}
-
-int
-VBAPSpeakers::calc_2D_inv_tmatrix (double azi1, double azi2, double* inverse_matrix)
-{
- double x1,x2,x3,x4;
- double det;
-
- x1 = cos (azi1);
- x2 = sin (azi1);
- x3 = cos (azi2);
- x4 = sin (azi2);
- det = (x1 * x4) - ( x3 * x2 );
-
- if (fabs(det) <= 0.001) {
-
- inverse_matrix[0] = 0.0;
- inverse_matrix[1] = 0.0;
- inverse_matrix[2] = 0.0;
- inverse_matrix[3] = 0.0;
-
- return 0;
-
- } else {
-
- inverse_matrix[0] = x4 / det;
- inverse_matrix[1] = -x3 / det;
- inverse_matrix[2] = -x2 / det;
- inverse_matrix[3] = x1 / det;
-
- return 1;
- }
-}
-
-
diff --git a/libs/ardour/wscript b/libs/ardour/wscript
index 0a81c306c3..1a3296d4d5 100644
--- a/libs/ardour/wscript
+++ b/libs/ardour/wscript
@@ -137,7 +137,12 @@ libardour_sources = [
'named_selection.cc',
'onset_detector.cc',
'operations.cc',
- 'panner.cc',
+ 'pan_controllable.cc',
+ 'pannable.cc',
+ 'panner.cc',
+ 'panner_manager.cc',
+ 'panner_search_path.cc',
+ 'panner_shell.cc',
'pcm_utils.cc',
'pi_controller.cc',
'playlist.cc',
@@ -205,8 +210,6 @@ libardour_sources = [
'unknown_processor.cc',
'user_bundle.cc',
'utils.cc',
- 'vbap.cc',
- 'vbap_speakers.cc',
'version.cc'
]
diff --git a/libs/evoral/evoral/ControlList.hpp b/libs/evoral/evoral/ControlList.hpp
index 833fae4440..cfb7564ab6 100644
--- a/libs/evoral/evoral/ControlList.hpp
+++ b/libs/evoral/evoral/ControlList.hpp
@@ -115,6 +115,8 @@ public:
void slide (iterator before, double distance);
void shift (double before, double distance);
+ virtual bool clamp_value (double& when, double& value) const { return true; }
+
void rt_add (double when, double value);
void add (double when, double value);
void fast_simple_add (double when, double value);
diff --git a/libs/evoral/src/ControlList.cpp b/libs/evoral/src/ControlList.cpp
index cc8ba45001..6738e8c1ac 100644
--- a/libs/evoral/src/ControlList.cpp
+++ b/libs/evoral/src/ControlList.cpp
@@ -394,6 +394,10 @@ ControlList::add (double when, double value)
control surface (GUI, MIDI, OSC etc)
*/
+ if (!clamp_value (when, value)) {
+ return;
+ }
+
{
Glib::Mutex::Lock lm (_lock);
ControlEvent cp (when, 0.0f);
diff --git a/libs/gtkmm2ext/gtkmm2ext/utils.h b/libs/gtkmm2ext/gtkmm2ext/utils.h
index aea4ff11f5..3528435ed1 100644
--- a/libs/gtkmm2ext/gtkmm2ext/utils.h
+++ b/libs/gtkmm2ext/gtkmm2ext/utils.h
@@ -24,6 +24,7 @@
#include <string>
#include <stdint.h>
+#include <gtkmm/container.h>
#include <gtkmm/treeview.h>
#include <gdkmm/window.h> /* for WMDecoration */
#include <gdkmm/pixbuf.h>
@@ -86,6 +87,8 @@ namespace Gtkmm2ext {
int physical_screen_height (Glib::RefPtr<Gdk::Window>);
int physical_screen_width (Glib::RefPtr<Gdk::Window>);
+
+ void container_clear (Gtk::Container&);
};
#endif /* __gtkmm2ext_utils_h__ */
diff --git a/libs/gtkmm2ext/utils.cc b/libs/gtkmm2ext/utils.cc
index afff784ae8..df79e535a7 100644
--- a/libs/gtkmm2ext/utils.cc
+++ b/libs/gtkmm2ext/utils.cc
@@ -377,3 +377,12 @@ Gtkmm2ext::physical_screen_width (Glib::RefPtr<Gdk::Window> win)
return gdk_screen_get_width (scr);
}
}
+
+void
+Gtkmm2ext::container_clear (Gtk::Container& c)
+{
+ list<Gtk::Widget*> children = c.get_children();
+ for (list<Gtk::Widget*>::iterator child = children.begin(); child != children.end(); ++child) {
+ c.remove (**child);
+ }
+}
diff --git a/libs/panners/1in2out/panner_1in2out.cc b/libs/panners/1in2out/panner_1in2out.cc
index 2851aec095..7a59aa04cc 100644
--- a/libs/panners/1in2out/panner_1in2out.cc
+++ b/libs/panners/1in2out/panner_1in2out.cc
@@ -43,16 +43,17 @@
#include "ardour/session.h"
#include "ardour/panner.h"
-#include "ardour/panner_1in2out.h"
#include "ardour/utils.h"
#include "ardour/audio_buffer.h"
+#include "ardour/debug.h"
#include "ardour/runtime_functions.h"
#include "ardour/buffer_set.h"
#include "ardour/audio_buffer.h"
-#include "ardour/vbap.h"
+#include "ardour/pannable.h"
#include "i18n.h"
+#include "panner_1in2out.h"
#include "pbd/mathfix.h"
@@ -62,22 +63,27 @@ using namespace PBD;
static PanPluginDescriptor _descriptor = {
"Mono to Stereo Panner",
- 1, 1, 2, 2,
+ 1, 2,
Panner1in2out::factory
};
-extern "C" { PanPluginDescriptor* panner_descriptor () { return &_descriptor; }
+extern "C" { PanPluginDescriptor* panner_descriptor () { return &_descriptor; } }
-Panner1in2out::Panner1in2out (PannerShell& p)
+Panner1in2out::Panner1in2out (boost::shared_ptr<Pannable> p)
: Panner (p)
- , _position (new PanControllable (parent.session(), _("position"), this, Evoral::Parameter(PanAzimuthAutomation, 0, 0)))
- , left (0.5)
- , right (0.5)
- , left_interp (left)
- , right_interp (right)
{
- desired_left = left;
- desired_right = right;
+ if (!_pannable->has_state()) {
+ _pannable->pan_azimuth_control->set_value (0.5);
+ }
+
+ update ();
+
+ left = desired_left;
+ right = desired_right;
+ left_interp = left;
+ right_interp = right;
+
+ _pannable->pan_azimuth_control->Changed.connect_same_thread (*this, boost::bind (&Panner1in2out::update, this));
}
Panner1in2out::~Panner1in2out ()
@@ -85,14 +91,44 @@ Panner1in2out::~Panner1in2out ()
}
void
+Panner1in2out::update ()
+{
+ float panR, panL;
+ float const pan_law_attenuation = -3.0f;
+ float const scale = 2.0f - 4.0f * powf (10.0f,pan_law_attenuation/20.0f);
+
+ panR = _pannable->pan_azimuth_control->get_value();
+ panL = 1 - panR;
+
+ desired_left = panL * (scale * panL + 1.0f - scale);
+ desired_right = panR * (scale * panR + 1.0f - scale);
+}
+
+void
Panner1in2out::set_position (double p)
{
- _desired_right = p;
- _desired_left = 1 - p;
+ if (clamp_position (p)) {
+ _pannable->pan_azimuth_control->set_value (p);
+ }
+}
+
+bool
+Panner1in2out::clamp_position (double& p)
+{
+ /* any position between 0.0 and 1.0 is legal */
+ DEBUG_TRACE (DEBUG::Panning, string_compose ("want to move panner to %1 - always allowed in 0.0-1.0 range\n", p));
+ p = max (min (p, 1.0), 0.0);
+ return true;
+}
+
+double
+Panner1in2out::position () const
+{
+ return _pannable->pan_azimuth_control->get_value ();
}
void
-Panner1in2out::do_distribute_one (AudioBuffer& srcbuf, BufferSet& obufs, gain_t gain_coeff, pframes_t nframes, uint32_t /* not used */)
+Panner1in2out::distribute_one (AudioBuffer& srcbuf, BufferSet& obufs, gain_t gain_coeff, pframes_t nframes, uint32_t /* not used */)
{
assert (obufs.count().n_audio() == 2);
@@ -100,17 +136,13 @@ Panner1in2out::do_distribute_one (AudioBuffer& srcbuf, BufferSet& obufs, gain_t
Sample* dst;
pan_t pan;
- if (_muted) {
- return;
- }
-
Sample* const src = srcbuf.data();
/* LEFT OUTPUT */
dst = obufs.get_audio(0).data();
- if (fabsf ((delta = (left[which] - desired_left[which]))) > 0.002) { // about 1 degree of arc
+ if (fabsf ((delta = (left - desired_left))) > 0.002) { // about 1 degree of arc
/* we've moving the pan by an appreciable amount, so we must
interpolate over 64 frames or nframes, whichever is smaller */
@@ -121,23 +153,23 @@ Panner1in2out::do_distribute_one (AudioBuffer& srcbuf, BufferSet& obufs, gain_t
delta = -(delta / (float) (limit));
for (n = 0; n < limit; n++) {
- left_interp[which] = left_interp[which] + delta;
- left = left_interp[which] + 0.9 * (left[which] - left_interp[which]);
- dst[n] += src[n] * left[which] * gain_coeff;
+ left_interp = left_interp + delta;
+ 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[which] * gain_coeff;
+ pan = left * gain_coeff;
mix_buffers_with_gain (dst+n,src+n,nframes-n,pan);
} else {
- left[which] = desired_left[which];
- left_interp[which] = left[which];
+ left = desired_left;
+ left_interp = left;
- if ((pan = (left[which] * gain_coeff)) != 1.0f) {
+ if ((pan = (left * gain_coeff)) != 1.0f) {
if (pan != 0.0f) {
@@ -165,7 +197,7 @@ Panner1in2out::do_distribute_one (AudioBuffer& srcbuf, BufferSet& obufs, gain_t
dst = obufs.get_audio(1).data();
- if (fabsf ((delta = (right[which] - desired_right[which]))) > 0.002) { // about 1 degree of arc
+ if (fabsf ((delta = (right - desired_right))) > 0.002) { // about 1 degree of arc
/* we're moving the pan by an appreciable amount, so we must
interpolate over 64 frames or nframes, whichever is smaller */
@@ -176,14 +208,14 @@ Panner1in2out::do_distribute_one (AudioBuffer& srcbuf, BufferSet& obufs, gain_t
delta = -(delta / (float) (limit));
for (n = 0; n < limit; n++) {
- right_interp[which] = right_interp[which] + delta;
- right[which] = right_interp[which] + 0.9 * (right[which] - right_interp[which]);
- dst[n] += src[n] * right[which] * gain_coeff;
+ right_interp = right_interp + delta;
+ right = right_interp + 0.9 * (right - right_interp);
+ dst[n] += src[n] * right * gain_coeff;
}
/* then pan the rest of the buffer, no need for interpolation for this bit */
- pan = right[which] * gain_coeff;
+ pan = right * gain_coeff;
mix_buffers_with_gain(dst+n,src+n,nframes-n,pan);
@@ -191,10 +223,10 @@ Panner1in2out::do_distribute_one (AudioBuffer& srcbuf, BufferSet& obufs, gain_t
} else {
- right[which] = desired_right[which];
- right_interp[which] = right[which];
+ right = desired_right;
+ right_interp = right;
- if ((pan = (right[which] * gain_coeff)) != 1.0f) {
+ if ((pan = (right * gain_coeff)) != 1.0f) {
if (pan != 0.0f) {
@@ -217,19 +249,116 @@ Panner1in2out::do_distribute_one (AudioBuffer& srcbuf, BufferSet& obufs, gain_t
}
+void
+Panner1in2out::distribute_one_automated (AudioBuffer& srcbuf, BufferSet& obufs,
+ framepos_t start, framepos_t end, pframes_t nframes,
+ pan_t** buffers, uint32_t which)
+{
+ assert (obufs.count().n_audio() == 2);
+
+ Sample* dst;
+ pan_t* pbuf;
+ Sample* const src = srcbuf.data();
+ pan_t* const position = buffers[0];
+
+ /* fetch positional data */
+
+ if (!_pannable->pan_azimuth_control->list()->curve().rt_safe_get_vector (start, end, position, nframes)) {
+ /* fallback */
+ distribute_one (srcbuf, obufs, 1.0, nframes, which);
+ return;
+ }
+
+ /* apply pan law to convert positional data into pan coefficients for
+ each buffer (output)
+ */
+
+ const float pan_law_attenuation = -3.0f;
+ const float scale = 2.0f - 4.0f * powf (10.0f,pan_law_attenuation/20.0f);
+
+ for (pframes_t n = 0; n < nframes; ++n) {
+
+ float panR = position[n];
+ const float panL = 1 - panR;
+
+ /* note that are overwriting buffers, but its OK
+ because we're finished with their old contents
+ (position automation data) and are
+ 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);
+ }
+
+ /* LEFT OUTPUT */
+
+ dst = obufs.get_audio(0).data();
+ pbuf = buffers[0];
+
+ for (pframes_t n = 0; n < nframes; ++n) {
+ dst[n] += src[n] * pbuf[n];
+ }
+
+ /* XXX it would be nice to mark the buffer as written to */
+
+ /* RIGHT OUTPUT */
+
+ dst = obufs.get_audio(1).data();
+ pbuf = buffers[1];
+
+ for (pframes_t n = 0; n < nframes; ++n) {
+ dst[n] += src[n] * pbuf[n];
+ }
+
+ /* XXX it would be nice to mark the buffer as written to */
+}
+
+
+Panner*
+Panner1in2out::factory (boost::shared_ptr<Pannable> p, Speakers& /* ignored */)
+{
+ return new Panner1in2out (p);
+}
+
+XMLNode&
+Panner1in2out::get_state (void)
+{
+ return state (true);
+}
+
+XMLNode&
+Panner1in2out::state (bool /*full_state*/)
+{
+ XMLNode& root (Panner::get_state ());
+ root.add_property (X_("type"), _descriptor.name);
+ return root;
+}
+
+int
+Panner1in2out::set_state (const XMLNode& node, int version)
+{
+ LocaleGuard lg (X_("POSIX"));
+ Panner::set_state (node, version);
+ return 0;
+}
+
+std::set<Evoral::Parameter>
+Panner1in2out::what_can_be_automated() const
+{
+ set<Evoral::Parameter> s;
+ s.insert (Evoral::Parameter (PanAzimuthAutomation));
+ return s;
+}
+
string
-Panner1in2out::describe_parameter (Evoral::Parameter param)
+Panner1in2out::describe_parameter (Evoral::Parameter p)
{
- switch (param.type()) {
- case PanWidthAutomation:
- return "Pan:width";
+ switch (p.type()) {
case PanAzimuthAutomation:
- return "Pan:position";
- case PanElevationAutomation:
- error << X_("stereo panner should not have elevation control") << endmsg;
- return "Pan:elevation";
- }
-
- return Automatable::describe_parameter (param);
+ return _("L/R");
+ default:
+ return _pannable->describe_parameter (p);
+ }
}
-
diff --git a/libs/panners/1in2out/panner_1in2out.h b/libs/panners/1in2out/panner_1in2out.h
index 152eb7156a..ced467c11b 100644
--- a/libs/panners/1in2out/panner_1in2out.h
+++ b/libs/panners/1in2out/panner_1in2out.h
@@ -31,40 +31,56 @@
#include "pbd/cartesian.h"
#include "ardour/types.h"
-#include "ardour/automation_control.h"
-#include "ardour/automatable.h"
+#include "ardour/panner.h"
namespace ARDOUR {
-class PannerStereoBase : public class Panner
+class Panner1in2out : public Panner
{
public:
- PannerStereoBase (Panner&);
- ~PannerStereoBase ();
+ Panner1in2out (boost::shared_ptr<Pannable>);
+ ~Panner1in2out ();
void set_position (double);
+ bool clamp_position (double&);
+
+ double position() const;
ChanCount in() const { return ChanCount (DataType::AUDIO, 1); }
ChanCount out() const { return ChanCount (DataType::AUDIO, 2); }
+ std::set<Evoral::Parameter> what_can_be_automated() const;
+
/* this class just leaves the pan law itself to be defined
by the update(), do_distribute_automated()
methods. derived classes also need a factory method
and a type name. See EqualPowerStereoPanner as an example.
*/
- void do_distribute (AudioBuffer& src, BufferSet& obufs, gain_t gain_coeff, pframes_t nframes);
+ static Panner* factory (boost::shared_ptr<Pannable>, Speakers&);
+
+ std::string describe_parameter (Evoral::Parameter);
+
+ XMLNode& state (bool full_state);
+ XMLNode& get_state (void);
+ int set_state (const XMLNode&, int version);
protected:
- boost::shared_ptr<AutomationControl> _position;
float left;
float right;
float desired_left;
float desired_right;
float left_interp;
float right_interp;
+
+ void distribute_one (AudioBuffer& src, BufferSet& obufs, gain_t gain_coeff, pframes_t nframes, uint32_t which);
+ void distribute_one_automated (AudioBuffer& srcbuf, BufferSet& obufs,
+ framepos_t start, framepos_t end, pframes_t nframes,
+ pan_t** buffers, uint32_t which);
+
+ void update ();
};
-}
+} // namespace
#endif /* __ardour_panner_1in2out_h__ */
diff --git a/libs/panners/1in2out/wscript b/libs/panners/1in2out/wscript
new file mode 100644
index 0000000000..e7e4d29928
--- /dev/null
+++ b/libs/panners/1in2out/wscript
@@ -0,0 +1,35 @@
+#!/usr/bin/env python
+import autowaf
+import os
+
+# Library version (UNIX style major, minor, micro)
+# major increment <=> incompatible changes
+# minor increment <=> compatible changes (additions)
+# micro increment <=> no interface changes
+LIBARDOUR_PAN1IN2OUT_LIB_VERSION = '1.0.0'
+
+# Mandatory variables
+srcdir = '.'
+blddir = 'build'
+
+def set_options(opt):
+ autowaf.set_options(opt)
+
+def configure(conf):
+ autowaf.configure(conf)
+
+def build(bld):
+ obj = bld.new_task_gen('cxx', 'shlib')
+ obj.source = [ 'panner_1in2out.cc' ]
+ obj.export_incdirs = ['.']
+ obj.cxxflags = '-DPACKAGE="libardour_pan1in2out"'
+ obj.includes = ['.']
+ obj.name = 'libardour_pan1in2out'
+ obj.target = 'pan1in2out'
+ obj.uselib_local = 'libardour libardour_cp libpbd'
+ obj.vnum = LIBARDOUR_PAN1IN2OUT_LIB_VERSION
+ obj.install_path = os.path.join(bld.env['LIBDIR'], 'ardour3', 'panners')
+
+def shutdown():
+ autowaf.shutdown()
+
diff --git a/libs/panners/2in2out/panner_2in2out.cc b/libs/panners/2in2out/panner_2in2out.cc
index 6bc0f93a8f..2c2856361c 100644
--- a/libs/panners/2in2out/panner_2in2out.cc
+++ b/libs/panners/2in2out/panner_2in2out.cc
@@ -71,21 +71,21 @@ extern "C" { PanPluginDescriptor* panner_descriptor () { return &_descriptor; }
Panner2in2out::Panner2in2out (boost::shared_ptr<Pannable> p)
: Panner (p)
{
- _pannable->pan_azimuth_control->set_value (0.5);
- _pannable->pan_width_control->set_value (1.0);
-
- /* LEFT SIGNAL, panned hard left */
- left[0] = 1.0;
- right[0] = 0.0;
- desired_left[0] = left_interp[0] = left[0];
- desired_right[0] = right_interp[0] = right[0];
-
- /* RIGHT SIGNAL, panned hard right */
- left[1] = 0;
- right[1] = 1.0;
- desired_left[1] = left_interp[1] = left[1];
- desired_right[1] = right_interp[1] = right[1];
-
+ if (!_pannable->has_state()) {
+ _pannable->pan_azimuth_control->set_value (0.5);
+ _pannable->pan_width_control->set_value (1.0);
+ }
+
+ update ();
+
+ /* LEFT SIGNAL */
+ left_interp[0] = left[0] = desired_left[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));
}
@@ -141,8 +141,6 @@ Panner2in2out::update ()
const double width = _pannable->pan_width_control->get_value();
const double direction_as_lr_fract = _pannable->pan_azimuth_control->get_value();
- cerr << "new pan values width=" << width << " LR = " << direction_as_lr_fract << endl;
-
if (width < 0.0) {
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
@@ -190,20 +188,14 @@ Panner2in2out::clamp_width (double& w)
bool
Panner2in2out::clamp_stereo_pan (double& direction_as_lr_fract, double& width)
{
- double r_pos = direction_as_lr_fract + (width/2.0);
- double l_pos = direction_as_lr_fract - (width/2.0);
- bool can_move_left = true;
- bool can_move_right = true;
-
- cerr << "Clamp pos = " << direction_as_lr_fract << " w = " << width << endl;
+ double r_pos;
+ double l_pos;
- if (width > 1.0 || width < 1.0) {
- return false;
- }
+ width = max (min (width, 1.0), -1.0);
+ direction_as_lr_fract = max (min (direction_as_lr_fract, 1.0), 0.0);
- if (direction_as_lr_fract > 1.0 || direction_as_lr_fract < 0.0) {
- return false;
- }
+ r_pos = direction_as_lr_fract + (width/2.0);
+ l_pos = direction_as_lr_fract - (width/2.0);
if (width < 0.0) {
swap (r_pos, l_pos);
@@ -213,19 +205,20 @@ Panner2in2out::clamp_stereo_pan (double& direction_as_lr_fract, double& width)
is already there, we're not moving the left signal.
*/
- if (l_pos <= 0.0 && desired_left[0] <= 0.0) {
- can_move_left = false;
+ 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.
*/
- if (r_pos >= 1.0 && desired_right[1] >= 1.0) {
- can_move_right = false;
+ if (r_pos > 1.0) {
+ return false;
+
}
- return can_move_left && can_move_right;
+ return true;
}
void
@@ -459,3 +452,24 @@ Panner2in2out::set_state (const XMLNode& node, int version)
return 0;
}
+std::set<Evoral::Parameter>
+Panner2in2out::what_can_be_automated() const
+{
+ set<Evoral::Parameter> s;
+ s.insert (Evoral::Parameter (PanAzimuthAutomation));
+ s.insert (Evoral::Parameter (PanWidthAutomation));
+ return s;
+}
+
+string
+Panner2in2out::describe_parameter (Evoral::Parameter p)
+{
+ switch (p.type()) {
+ case PanAzimuthAutomation:
+ return _("L/R");
+ case PanWidthAutomation:
+ return _("Width");
+ default:
+ return _pannable->describe_parameter (p);
+ }
+}
diff --git a/libs/panners/2in2out/panner_2in2out.h b/libs/panners/2in2out/panner_2in2out.h
index 8d8d57d709..0bb38fa27f 100644
--- a/libs/panners/2in2out/panner_2in2out.h
+++ b/libs/panners/2in2out/panner_2in2out.h
@@ -55,8 +55,12 @@ class Panner2in2out : public Panner
double position () const;
double width () const;
+ std::set<Evoral::Parameter> what_can_be_automated() const;
+
static Panner* factory (boost::shared_ptr<Pannable>, Speakers&);
+ std::string describe_parameter (Evoral::Parameter);
+
XMLNode& state (bool full_state);
XMLNode& get_state (void);
int set_state (const XMLNode&, int version);
diff --git a/libs/panners/2in2out/wscript b/libs/panners/2in2out/wscript
index 509848e5a6..22d5c03726 100644
--- a/libs/panners/2in2out/wscript
+++ b/libs/panners/2in2out/wscript
@@ -12,6 +12,12 @@ LIBARDOUR_PAN2IN2OUT_LIB_VERSION = '1.0.0'
srcdir = '.'
blddir = 'build'
+def set_options(opt):
+ autowaf.set_options(opt)
+
+def configure(conf):
+ autowaf.configure(conf)
+
def build(bld):
obj = bld.new_task_gen('cxx', 'shlib')
obj.source = [ 'panner_2in2out.cc' ]
diff --git a/libs/panners/vbap/vbap.cc b/libs/panners/vbap/vbap.cc
index 1876f4cf44..6088a2d0d6 100644
--- a/libs/panners/vbap/vbap.cc
+++ b/libs/panners/vbap/vbap.cc
@@ -10,27 +10,26 @@
#include "ardour/pannable.h"
#include "ardour/speakers.h"
-#include "ardour/vbap.h"
-#include "ardour/vbap_speakers.h"
#include "ardour/audio_buffer.h"
#include "ardour/buffer_set.h"
#include "ardour/pan_controllable.h"
+#include "vbap.h"
+#include "vbap_speakers.h"
+
using namespace PBD;
using namespace ARDOUR;
using namespace std;
static PanPluginDescriptor _descriptor = {
"VBAP 2D panner",
- 1, -1, 2, -1,
+ -1, -1,
VBAPanner::factory
};
extern "C" { PanPluginDescriptor* panner_descriptor () { return &_descriptor; } }
VBAPanner::Signal::Signal (Session& session, VBAPanner& p, uint32_t n)
- : azimuth_control (new PanControllable (session, string_compose (_("azimuth %1"), n+1), &p, Evoral::Parameter (PanAzimuthAutomation, 0, n)))
- , elevation_control (new PanControllable (session, string_compose (_("elevation %1"), n+1), &p, Evoral::Parameter (PanElevationAutomation, 0, n)))
{
gains[0] = gains[1] = gains[2] = 0;
desired_gains[0] = desired_gains[1] = desired_gains[2] = 0;
@@ -40,44 +39,86 @@ VBAPanner::Signal::Signal (Session& session, VBAPanner& p, uint32_t n)
VBAPanner::VBAPanner (boost::shared_ptr<Pannable> p, Speakers& s)
: Panner (p)
- , _dirty (true)
, _speakers (VBAPSpeakers::instance (s))
{
+ _pannable->pan_azimuth_control->Changed.connect_same_thread (*this, boost::bind (&VBAPanner::update, this));
+ _pannable->pan_width_control->Changed.connect_same_thread (*this, boost::bind (&VBAPanner::update, this));
+
+ update ();
}
VBAPanner::~VBAPanner ()
{
+ clear_signals ();
+}
+
+void
+VBAPanner::clear_signals ()
+{
for (vector<Signal*>::iterator i = _signals.begin(); i != _signals.end(); ++i) {
delete *i;
}
+ _signals.clear ();
}
void
-VBAPanner::configure_io (const ChanCount& in, const ChanCount& /* ignored - we use Speakers */)
+VBAPanner::configure_io (ChanCount in, ChanCount /* ignored - we use Speakers */)
{
uint32_t n = in.n_audio();
- /* 2d panning: spread signals equally around a circle */
-
- double degree_step = 360.0 / _speakers.n_speakers();
- double deg;
-
- /* even number of signals? make sure the top two are either side of "top".
- otherwise, just start at the "top" (90.0 degrees) and rotate around
- */
-
- if (n % 2) {
- deg = 90.0 - degree_step;
- } else {
- deg = 90.0;
- }
+ clear_signals ();
- _signals.clear ();
-
for (uint32_t i = 0; i < n; ++i) {
_signals.push_back (new Signal (_pannable->session(), *this, i));
- _signals[i]->direction = AngularVector (deg, 0.0);
- deg += degree_step;
+ }
+
+ update ();
+}
+
+void
+VBAPanner::update ()
+{
+ /* recompute signal directions based on panner azimuth and width (diffusion) parameters)
+ */
+
+ /* panner azimuth control is [0 .. 1.0] which we interpret as [0 .. 360] degrees
+ */
+
+ double center = _pannable->pan_azimuth_control->get_value() * 360.0;
+
+ /* panner width control is [-1.0 .. 1.0]; we ignore sign, and map to [0 .. 360] degrees
+ so that a width of 1 corresponds to a signal equally present from all directions,
+ and a width of zero corresponds to a point source from the "center" (above)
+ */
+
+ double w = fabs (_pannable->pan_width_control->get_value()) * 360.0;
+
+ double min_dir = center - w;
+ min_dir = max (min (min_dir, 360.0), 0.0);
+
+ double max_dir = center + w;
+ max_dir = max (min (max_dir, 360.0), 0.0);
+
+ double degree_step_per_signal = (max_dir - min_dir) / _signals.size();
+ double signal_direction = min_dir;
+
+ for (vector<Signal*>::iterator s = _signals.begin(); s != _signals.end(); ++s) {
+
+ Signal* signal = *s;
+
+ signal->direction = AngularVector (signal_direction, 0.0);
+
+ compute_gains (signal->desired_gains, signal->desired_outputs, signal->direction.azi, signal->direction.ele);
+ cerr << " @ " << signal->direction.azi << " /= " << signal->direction.ele
+ << " Outputs: "
+ << signal->desired_outputs[0] + 1 << ' '
+ << signal->desired_outputs[1] + 1 << ' '
+ << " Gains "
+ << signal->desired_gains[0] << ' '
+ << signal->desired_gains[1] << ' '
+ << endl;
+
+ signal_direction += degree_step_per_signal;
}
}
@@ -141,49 +182,29 @@ VBAPanner::compute_gains (double gains[3], int speaker_ids[3], int azi, int ele)
gains[1] /= power;
gains[2] /= power;
}
-
- _dirty = false;
}
void
-VBAPanner::do_distribute (BufferSet& inbufs, BufferSet& obufs, gain_t gain_coefficient, pframes_t nframes)
+VBAPanner::distribute (BufferSet& inbufs, BufferSet& obufs, gain_t gain_coefficient, pframes_t nframes)
{
- bool was_dirty = _dirty;
uint32_t n;
vector<Signal*>::iterator s;
assert (inbufs.count().n_audio() == _signals.size());
- /* XXX need to handle mono case */
-
for (s = _signals.begin(), n = 0; s != _signals.end(); ++s, ++n) {
Signal* signal (*s);
- if (was_dirty) {
- compute_gains (signal->desired_gains, signal->desired_outputs, signal->direction.azi, signal->direction.ele);
- cerr << " @ " << signal->direction.azi << " /= " << signal->direction.ele
- << " Outputs: "
- << signal->desired_outputs[0] + 1 << ' '
- << signal->desired_outputs[1] + 1 << ' '
- << " Gains "
- << signal->desired_gains[0] << ' '
- << signal->desired_gains[1] << ' '
- << endl;
- }
-
- do_distribute_one (inbufs.get_audio (n), obufs, gain_coefficient, nframes, n);
-
- if (was_dirty) {
- memcpy (signal->gains, signal->desired_gains, sizeof (signal->gains));
- memcpy (signal->outputs, signal->desired_outputs, sizeof (signal->outputs));
- }
+ distribute_one (inbufs.get_audio (n), obufs, gain_coefficient, nframes, n);
+
+ memcpy (signal->gains, signal->desired_gains, sizeof (signal->gains));
+ memcpy (signal->outputs, signal->desired_outputs, sizeof (signal->outputs));
}
}
-
void
-VBAPanner::do_distribute_one (AudioBuffer& srcbuf, BufferSet& obufs, gain_t gain_coefficient, pframes_t nframes, uint32_t which)
+VBAPanner::distribute_one (AudioBuffer& srcbuf, BufferSet& obufs, gain_t gain_coefficient, pframes_t nframes, uint32_t which)
{
Sample* const src = srcbuf.data();
Sample* dst;
@@ -228,8 +249,8 @@ VBAPanner::do_distribute_one (AudioBuffer& srcbuf, BufferSet& obufs, gain_t gain
}
void
-VBAPanner::do_distribute_one_automated (AudioBuffer& src, BufferSet& obufs,
- framepos_t start, framepos_t end, pframes_t nframes, pan_t** buffers, uint32_t which)
+VBAPanner::distribute_one_automated (AudioBuffer& src, BufferSet& obufs,
+ framepos_t start, framepos_t end, pframes_t nframes, pan_t** buffers, uint32_t which)
{
}
@@ -253,46 +274,12 @@ VBAPanner::set_state (const XMLNode& node, int /*version*/)
return 0;
}
-boost::shared_ptr<AutomationControl>
-VBAPanner::azimuth_control (uint32_t n)
-{
- if (n >= _signals.size()) {
- return boost::shared_ptr<AutomationControl>();
- }
- return _signals[n]->azimuth_control;
-}
-
-boost::shared_ptr<AutomationControl>
-VBAPanner::evelation_control (uint32_t n)
-{
- if (n >= _signals.size()) {
- return boost::shared_ptr<AutomationControl>();
- }
- return _signals[n]->elevation_control;
-}
-
Panner*
VBAPanner::factory (boost::shared_ptr<Pannable> p, Speakers& s)
{
return new VBAPanner (p, s);
}
-string
-VBAPanner::describe_parameter (Evoral::Parameter param)
-{
- stringstream ss;
- switch (param.type()) {
- case PanElevationAutomation:
- return string_compose ( _("Pan:elevation %1"), param.id() + 1);
- case PanWidthAutomation:
- return string_compose ( _("Pan:diffusion %1"), param.id() + 1);
- case PanAzimuthAutomation:
- return string_compose ( _("Pan:azimuth %1"), param.id() + 1);
- }
-
- return Automatable::describe_parameter (param);
-}
-
ChanCount
VBAPanner::in() const
{
@@ -304,3 +291,25 @@ VBAPanner::out() const
{
return ChanCount (DataType::AUDIO, _speakers.n_speakers());
}
+
+std::set<Evoral::Parameter>
+VBAPanner::what_can_be_automated() const
+{
+ set<Evoral::Parameter> s;
+ s.insert (Evoral::Parameter (PanAzimuthAutomation));
+ s.insert (Evoral::Parameter (PanWidthAutomation));
+ return s;
+}
+
+string
+VBAPanner::describe_parameter (Evoral::Parameter p)
+{
+ switch (p.type()) {
+ case PanAzimuthAutomation:
+ return _("Direction");
+ case PanWidthAutomation:
+ return _("Diffusion");
+ default:
+ return _pannable->describe_parameter (p);
+ }
+}
diff --git a/libs/panners/vbap/vbap.h b/libs/panners/vbap/vbap.h
index aacff8894c..2b80b032cb 100644
--- a/libs/panners/vbap/vbap.h
+++ b/libs/panners/vbap/vbap.h
@@ -26,7 +26,8 @@
#include "ardour/panner.h"
#include "ardour/panner_shell.h"
-#include "ardour/vbap_speakers.h"
+
+#include "vbap_speakers.h"
namespace ARDOUR {
@@ -39,26 +40,24 @@ public:
VBAPanner (boost::shared_ptr<Pannable>, Speakers& s);
~VBAPanner ();
- void configure_io (const ChanCount& in, const ChanCount& /* ignored - we use Speakers */);
+ void configure_io (ChanCount in, ChanCount /* ignored - we use Speakers */);
ChanCount in() const;
ChanCount out() const;
+ std::set<Evoral::Parameter> what_can_be_automated() const;
+
static Panner* factory (boost::shared_ptr<Pannable>, Speakers& s);
- void do_distribute (BufferSet& ibufs, BufferSet& obufs, gain_t gain_coeff, pframes_t nframes);
- void do_distribute_automated (BufferSet& ibufs, BufferSet& obufs,
- framepos_t start, framepos_t end, pframes_t nframes, pan_t** buffers);
+ void distribute (BufferSet& ibufs, BufferSet& obufs, gain_t gain_coeff, pframes_t nframes);
void set_azimuth_elevation (double azimuth, double elevation);
+ std::string describe_parameter (Evoral::Parameter);
+
XMLNode& state (bool full_state);
XMLNode& get_state ();
int set_state (const XMLNode&, int version);
- boost::shared_ptr<AutomationControl> azimuth_control (uint32_t signal);
- boost::shared_ptr<AutomationControl> evelation_control (uint32_t signal);
-
- std::string describe_parameter (Evoral::Parameter param);
private:
struct Signal {
@@ -67,20 +66,19 @@ private:
double desired_gains[3];
int outputs[3];
int desired_outputs[3];
- boost::shared_ptr<AutomationControl> azimuth_control;
- boost::shared_ptr<AutomationControl> elevation_control;
Signal (Session&, VBAPanner&, uint32_t which);
};
std::vector<Signal*> _signals;
- bool _dirty;
VBAPSpeakers& _speakers;
void compute_gains (double g[3], int ls[3], int azi, int ele);
+ void update ();
+ void clear_signals ();
- void do_distribute_one (AudioBuffer& src, BufferSet& obufs, gain_t gain_coeff, pframes_t nframes, uint32_t which);
- void do_distribute_one_automated (AudioBuffer& src, BufferSet& obufs,
+ void distribute_one (AudioBuffer& src, BufferSet& obufs, gain_t gain_coeff, pframes_t nframes, uint32_t which);
+ void distribute_one_automated (AudioBuffer& src, BufferSet& obufs,
framepos_t start, framepos_t end, pframes_t nframes,
pan_t** buffers, uint32_t which);
};
diff --git a/libs/panners/vbap/vbap_speakers.cc b/libs/panners/vbap/vbap_speakers.cc
index 9090ed65e1..7e70e5df66 100644
--- a/libs/panners/vbap/vbap_speakers.cc
+++ b/libs/panners/vbap/vbap_speakers.cc
@@ -36,7 +36,8 @@
#include <stdlib.h>
#include "pbd/cartesian.h"
-#include "ardour/vbap_speakers.h"
+
+#include "vbap_speakers.h"
using namespace ARDOUR;
using namespace PBD;
diff --git a/libs/panners/vbap/wscript b/libs/panners/vbap/wscript
new file mode 100644
index 0000000000..1399039483
--- /dev/null
+++ b/libs/panners/vbap/wscript
@@ -0,0 +1,35 @@
+#!/usr/bin/env python
+import autowaf
+import os
+
+# Library version (UNIX style major, minor, micro)
+# major increment <=> incompatible changes
+# minor increment <=> compatible changes (additions)
+# micro increment <=> no interface changes
+LIBARDOUR_PANVBAP_LIB_VERSION = '1.0.0'
+
+# Mandatory variables
+srcdir = '.'
+blddir = 'build'
+
+def set_options(opt):
+ autowaf.set_options(opt)
+
+def configure(conf):
+ autowaf.configure(conf)
+
+def build(bld):
+ obj = bld.new_task_gen('cxx', 'shlib')
+ obj.source = [ 'vbap_speakers.cc', 'vbap.cc' ]
+ obj.export_incdirs = ['.']
+ obj.cxxflags = '-DPACKAGE="libardour_panvbap"'
+ obj.includes = ['.']
+ obj.name = 'libardour_panvbap'
+ obj.target = 'panvbap'
+ obj.uselib_local = 'libardour libardour_cp libpbd'
+ obj.vnum = LIBARDOUR_PANVBAP_LIB_VERSION
+ obj.install_path = os.path.join(bld.env['LIBDIR'], 'ardour3', 'panners')
+
+def shutdown():
+ autowaf.shutdown()
+
diff --git a/libs/panners/wscript b/libs/panners/wscript
index 63b547d093..192f27b7db 100644
--- a/libs/panners/wscript
+++ b/libs/panners/wscript
@@ -6,13 +6,22 @@ import os
srcdir = '.'
blddir = 'build'
-#panners = [ '2in2out', 'vbap', '1in1out' ]
-panners = [ '2in2out' ]
+panners = [ '2in2out', '1in2out', 'vbap' ]
def set_options(opt):
autowaf.set_options(opt)
+def sub_config_and_use(conf, name, has_objects = True):
+ conf.sub_config(name)
+ autowaf.set_local_lib(conf, name, has_objects)
+
+def configure(conf):
+ autowaf.set_recursive()
+ autowaf.configure(conf)
+
+ for i in panners:
+ sub_config_and_use(conf, i)
+
def build(bld):
for i in panners:
bld.add_subdirs(i)
-
diff --git a/libs/pbd/pbd/controllable_descriptor.h b/libs/pbd/pbd/controllable_descriptor.h
index 1bf7705595..6b0d733656 100644
--- a/libs/pbd/pbd/controllable_descriptor.h
+++ b/libs/pbd/pbd/controllable_descriptor.h
@@ -39,6 +39,7 @@ public:
Recenable,
PanDirection,
PanWidth,
+ PanElevation,
Balance,
SendGain,
PluginParameter
diff --git a/libs/surfaces/mackie/mackie_control_protocol.cc b/libs/surfaces/mackie/mackie_control_protocol.cc
index ce2b2088d2..005d28f7aa 100644
--- a/libs/surfaces/mackie/mackie_control_protocol.cc
+++ b/libs/surfaces/mackie/mackie_control_protocol.cc
@@ -45,6 +45,7 @@
#include "ardour/location.h"
#include "ardour/midi_ui.h"
#include "ardour/panner.h"
+#include "ardour/panner_shell.h"
#include "ardour/route.h"
#include "ardour/session.h"
#include "ardour/tempo.h"
@@ -832,19 +833,17 @@ MackieControlProtocol::handle_control_event (SurfacePort & port, Control & contr
// pot (jog wheel, external control)
case Control::type_pot:
if (control.group().is_strip()) {
- if (route != 0 && route->panner())
- {
+ if (route) {
+ boost::shared_ptr<Panner> panner = route->panner_shell()->panner();
// pan for mono input routes, or stereo linked panners
- if (route->panner()->npanners() == 1 || (route->panner()->npanners() == 2 && route->panner()->linked()))
- {
- // assume pan for now
- AngularVector a = route->panner()->streampanner (0).get_effective_position ();
+ if (panner) {
+ double p = panner->position ();
// calculate new value, and adjust
- a.azi += 180.0 * state.delta * state.sign;
- a.azi = min (180.0, a.azi);
- a.azi = max (0.0, a.azi);
- route->panner()->streampanner (0).set_position (a);
+ p += state.delta * state.sign;
+ p = min (1.0, p);
+ p = max (0.0, p);
+ panner->set_position (p);
}
}
else
@@ -1000,15 +999,13 @@ MackieControlProtocol::notify_panner_changed (RouteSignal * route_signal, bool f
{
Pot & pot = route_signal->strip().vpot();
boost::shared_ptr<Panner> panner = route_signal->route()->panner();
- if ((panner && panner->npanners() == 1) || (panner->npanners() == 2 && panner->linked()))
- {
- AngularVector pos = route_signal->route()->panner()->streampanner(0).get_effective_position ();
- float fract = 1.0 - (pos.azi / 180.0); /* 1.0 = 0 degrees = right; 0.0 = 180 degrees = left */
+ if (panner) {
+ double pos = panner->position ();
// cache the MidiByteArray here, because the mackie led control is much lower
// resolution than the panner control. So we save lots of byte
// sends in spite of more work on the comparison
- MidiByteArray bytes = builder.build_led_ring (pot, ControlState (on, fract), MackieMidiBuilder::midi_pot_mode_dot);
+ MidiByteArray bytes = builder.build_led_ring (pot, ControlState (on, pos), MackieMidiBuilder::midi_pot_mode_dot);
// check that something has actually changed
if (force_update || bytes != route_signal->last_pan_written())
{
diff --git a/libs/surfaces/mackie/route_signal.cc b/libs/surfaces/mackie/route_signal.cc
index 2f6a6f7c5f..f6db125b8c 100644
--- a/libs/surfaces/mackie/route_signal.cc
+++ b/libs/surfaces/mackie/route_signal.cc
@@ -52,10 +52,6 @@ void RouteSignal::connect()
if (_route->panner()) {
_route->panner()->Changed.connect(connections, MISSING_INVALIDATOR, ui_bind (&MackieControlProtocol::notify_panner_changed, &_mcp, this, false), midi_ui_context());
-
- for ( unsigned int i = 0; i < _route->panner()->npanners(); ++i ) {
- _route->panner()->streampanner(i).Changed.connect (connections, MISSING_INVALIDATOR, ui_bind (&MackieControlProtocol::notify_panner_changed, &_mcp, this, false), midi_ui_context());
- }
}
boost::shared_ptr<Track> trk = boost::dynamic_pointer_cast<ARDOUR::Track>(_route);
diff --git a/libs/surfaces/osc/osc.cc b/libs/surfaces/osc/osc.cc
index 93b56a8a97..280959ae09 100644
--- a/libs/surfaces/osc/osc.cc
+++ b/libs/surfaces/osc/osc.cc
@@ -870,7 +870,7 @@ OSC::route_set_pan_stereo_position (int rid, float pos)
if (r) {
boost::shared_ptr<Panner> panner = r->panner();
if (panner) {
- panner->set_stereo_position (pos);
+ panner->set_position (pos);
}
}
@@ -888,7 +888,7 @@ OSC::route_set_pan_stereo_width (int rid, float pos)
if (r) {
boost::shared_ptr<Panner> panner = r->panner();
if (panner) {
- panner->set_stereo_width (pos);
+ panner->set_width (pos);
}
}