summaryrefslogtreecommitdiff
path: root/libs/panners
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/panners
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/panners')
-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
11 files changed, 450 insertions, 194 deletions
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)
-