summaryrefslogtreecommitdiff
path: root/libs
diff options
context:
space:
mode:
authorPaul Davis <paul@linuxaudiosystems.com>2010-11-26 17:43:03 +0000
committerPaul Davis <paul@linuxaudiosystems.com>2010-11-26 17:43:03 +0000
commit553cf2982c4905c5a08f305ce2772beaa8c50324 (patch)
treed23a7bad787a2d5bc1a909a14e9869fcfb405ae8 /libs
parent1539ac1b9661f0c0bb313d8f0d9a72b6dc95aaf1 (diff)
one step closer to working vbap panning
git-svn-id: svn://localhost/ardour2/branches/3.0@8091 d708f5d6-7413-0410-9779-e7cbd77b26cf
Diffstat (limited to 'libs')
-rw-r--r--libs/ardour/ardour/panner.h92
-rw-r--r--libs/ardour/ardour/session.h7
-rw-r--r--libs/ardour/ardour/speaker.h44
-rw-r--r--libs/ardour/ardour/speakers.h55
-rw-r--r--libs/ardour/ardour/vbap.h12
-rw-r--r--libs/ardour/ardour/vbap_speakers.h60
-rw-r--r--libs/ardour/panner.cc569
-rw-r--r--libs/ardour/session.cc5
-rw-r--r--libs/ardour/speakers.cc108
-rw-r--r--libs/ardour/vbap.cc29
-rw-r--r--libs/ardour/vbap_speakers.cc202
-rw-r--r--libs/ardour/wscript1
-rw-r--r--libs/pbd/cartesian.cc1
-rw-r--r--libs/pbd/pbd/cartesian.h82
-rw-r--r--libs/surfaces/mackie/mackie_control_protocol.cc24
15 files changed, 585 insertions, 706 deletions
diff --git a/libs/ardour/ardour/panner.h b/libs/ardour/ardour/panner.h
index da68ed8578..a4e063884e 100644
--- a/libs/ardour/ardour/panner.h
+++ b/libs/ardour/ardour/panner.h
@@ -28,6 +28,7 @@
#include "pbd/stateful.h"
#include "pbd/controllable.h"
+#include "pbd/cartesian.h"
#include "ardour/types.h"
#include "ardour/automation_control.h"
@@ -39,6 +40,7 @@ class Session;
class Panner;
class BufferSet;
class AudioBuffer;
+class Speakers;
class StreamPanner : public PBD::Stateful
{
@@ -49,17 +51,10 @@ class StreamPanner : public PBD::Stateful
void set_muted (bool yn);
bool muted() const { return _muted; }
- void set_position (float x, bool link_call = false);
- void set_position (float x, float y, bool link_call = false);
- void set_position (float x, float y, float z, bool link_call = false);
-
- void get_position (float& xpos) const { xpos = _x; }
- void get_position (float& xpos, float& ypos) const { xpos = _x; ypos = _y; }
- void get_position (float& xpos, float& ypos, float& zpos) const { xpos = _x; ypos = _y; zpos = _z; }
-
- void get_effective_position (float& xpos) const { xpos = effective_x; }
- void get_effective_position (float& xpos, float& ypos) const { xpos = effective_x; ypos = effective_y; }
- void get_effective_position (float& xpos, float& ypos, float& zpos) const { xpos = effective_x; ypos = effective_y; zpos = effective_z; }
+ 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, nframes_t);
void distribute_automated (AudioBuffer &, BufferSet &, nframes_t, nframes_t, nframes_t, pan_t **);
@@ -80,7 +75,7 @@ class StreamPanner : public PBD::Stateful
boost::shared_ptr<AutomationControl> pan_control() { return _control; }
- PBD::Signal0<void> Changed; /* for position */
+ PBD::Signal0<void> Changed; /* for position or diffusion */
PBD::Signal0<void> StateChanged; /* for mute, mono */
int set_state (const XMLNode&, int version);
@@ -97,17 +92,9 @@ class StreamPanner : public PBD::Stateful
void set_mono (bool);
- float _x;
- float _y;
- float _z;
-
- /* these are for automation. they store the last value
- used by the most recent process() cycle.
- */
-
- float effective_x;
- float effective_y;
- float effective_z;
+ PBD::AngularVector _angles;
+ PBD::AngularVector _effective_angles;
+ double _diffusion;
bool _muted;
bool _mono;
@@ -116,7 +103,7 @@ class StreamPanner : public PBD::Stateful
void add_state (XMLNode&);
- /* Update internal parameters based on _x, _y and _z */
+ /* Update internal parameters based on this.angles */
virtual void update () = 0;
};
@@ -134,6 +121,19 @@ class BaseStereoPanner : public StreamPanner
void do_distribute (AudioBuffer& src, BufferSet& obufs, gain_t gain_coeff, nframes_t nframes);
+ static double azimuth_to_lr_fract (double azi) {
+ /* 180.0 degrees=> left => 0.0 */
+ /* 0.0 degrees => right => 1.0 */
+ return 1.0 - (azi/180.0);
+ }
+
+ static double lr_fract_to_azimuth (double fract) {
+ /* fract = 0.0 => degrees = 180.0 => left */
+ /* fract = 1.0 => degrees = 0.0 => right */
+ return 180.0 - (fract * 180.0);
+ }
+
+
/* old school automation loading */
int load (std::istream&, std::string path, uint32_t&);
@@ -159,7 +159,7 @@ class EqualPowerStereoPanner : public BaseStereoPanner
void get_current_coefficients (pan_t*) const;
void get_desired_coefficients (pan_t*) const;
- static StreamPanner* factory (Panner&, Evoral::Parameter param);
+ static StreamPanner* factory (Panner&, Evoral::Parameter param, Speakers&);
static std::string name;
XMLNode& state (bool full_state);
@@ -170,32 +170,6 @@ class EqualPowerStereoPanner : public BaseStereoPanner
void update ();
};
-class Multi2dPanner : public StreamPanner
-{
- public:
- Multi2dPanner (Panner& parent, Evoral::Parameter);
- ~Multi2dPanner ();
-
- void do_distribute (AudioBuffer& src, BufferSet& obufs, gain_t gain_coeff, nframes_t nframes);
- void do_distribute_automated (AudioBuffer& src, BufferSet& obufs,
- nframes_t start, nframes_t end, nframes_t nframes, pan_t** buffers);
-
- static StreamPanner* factory (Panner&, Evoral::Parameter);
- static std::string name;
-
- XMLNode& state (bool full_state);
- XMLNode& get_state (void);
- int set_state (const XMLNode&, int version);
-
- /* old school automation loading */
-
- int load (std::istream&, std::string path, uint32_t&);
-
- 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.
*/
@@ -203,14 +177,12 @@ class Panner : public SessionObject, public Automatable
{
public:
struct Output {
- float x;
- float y;
- float z;
+ PBD::AngularVector position;
pan_t current_pan;
pan_t desired_pan;
- Output (float xp, float yp, float zp = 0.0)
- : x (xp), y (yp), z (zp), current_pan (0), desired_pan (0) {}
+ Output (const PBD::AngularVector& a)
+ : position (a), current_pan (0), desired_pan (0) {}
};
@@ -249,6 +221,10 @@ public:
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(); }
@@ -274,9 +250,7 @@ public:
/* only StreamPanner should call these */
- void set_position (float x, StreamPanner& orig);
- void set_position (float x, float y, StreamPanner& orig);
- void set_position (float x, float y, float z, StreamPanner& orig);
+ void set_position (const PBD::AngularVector&, StreamPanner& orig);
/* old school automation */
diff --git a/libs/ardour/ardour/session.h b/libs/ardour/ardour/session.h
index ff69a9ebdf..db334f6f19 100644
--- a/libs/ardour/ardour/session.h
+++ b/libs/ardour/ardour/session.h
@@ -52,7 +52,7 @@
#include "ardour/location.h"
#include "ardour/timecode.h"
#include "ardour/interpolation.h"
-#include "ardour/vbap_speakers.h"
+#include "ardour/speakers.h"
#ifdef HAVE_JACK_SESSION
#include <jack/session.h>
@@ -118,6 +118,7 @@ class SessionMetadata;
class SessionPlaylists;
class Slave;
class Source;
+class Speakers;
class TempoMap;
class VSTPlugin;
class Graph;
@@ -728,7 +729,7 @@ class Session : public PBD::StatefulDestructible, public PBD::ScopedConnectionLi
/* Speakers */
- VBAPSpeakers& get_speakers ();
+ Speakers& get_speakers ();
/* Controllables */
@@ -1470,7 +1471,7 @@ class Session : public PBD::StatefulDestructible, public PBD::ScopedConnectionLi
void start_time_changed (framepos_t);
void end_time_changed (framepos_t);
- VBAPSpeakers* _speakers;
+ Speakers* _speakers;
};
} // namespace ARDOUR
diff --git a/libs/ardour/ardour/speaker.h b/libs/ardour/ardour/speaker.h
new file mode 100644
index 0000000000..8e9e9116ae
--- /dev/null
+++ b/libs/ardour/ardour/speaker.h
@@ -0,0 +1,44 @@
+/*
+ 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_speaker_h__
+#define __libardour_speaker_h__
+
+#include "pbd/cartesian.h"
+
+namespace ARDOUR {
+
+class Speaker {
+ public:
+ Speaker (int, const PBD::AngularVector& position);
+
+ void move (const PBD::AngularVector& new_position);
+
+ const PBD::CartesianVector& coords() const { return _coords; }
+ const PBD::AngularVector& angles() const { return _angles; }
+
+ int id;
+
+ private:
+ PBD::CartesianVector _coords;
+ PBD::AngularVector _angles;
+};
+
+} /* namespace */
+
+#endif /* __libardour_speaker_h__ */
diff --git a/libs/ardour/ardour/speakers.h b/libs/ardour/ardour/speakers.h
new file mode 100644
index 0000000000..ee8d752ed9
--- /dev/null
+++ b/libs/ardour/ardour/speakers.h
@@ -0,0 +1,55 @@
+/*
+ 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_speakers_h__
+#define __libardour_speakers_h__
+
+#include <vector>
+#include <iostream>
+
+#include <pbd/signals.h>
+
+#include "ardour/speaker.h"
+
+namespace ARDOUR {
+
+class Speakers {
+ public:
+ Speakers ();
+ virtual ~Speakers ();
+
+ virtual int add_speaker (const PBD::AngularVector&);
+ virtual void remove_speaker (int id);
+ virtual void move_speaker (int id, const PBD::AngularVector& new_position);
+ virtual void clear_speakers ();
+
+ std::vector<Speaker>& speakers() { return _speakers; }
+
+ void dump_speakers (std::ostream&);
+
+ PBD::Signal0<void> Changed;
+
+ protected:
+ std::vector<Speaker> _speakers;
+
+ virtual void update () {}
+};
+
+} /* namespace */
+
+#endif /* __libardour_speakers_h__ */
diff --git a/libs/ardour/ardour/vbap.h b/libs/ardour/ardour/vbap.h
index b6c2b7a710..620bd9d297 100644
--- a/libs/ardour/ardour/vbap.h
+++ b/libs/ardour/ardour/vbap.h
@@ -22,19 +22,21 @@
#include <string>
#include <map>
-#include <pbd/signals.h>
-
#include "ardour/panner.h"
+#include "ardour/vbap_speakers.h"
namespace ARDOUR {
-class VBAPSpeakers;
+class Speakers;
class VBAPanner : public StreamPanner {
public:
- VBAPanner (Panner& parent, Evoral::Parameter param, VBAPSpeakers& s);
+ 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, nframes_t nframes);
void do_distribute_automated (AudioBuffer& src, BufferSet& obufs,
nframes_t start, nframes_t end, nframes_t nframes, pan_t** buffers);
@@ -58,8 +60,6 @@ class VBAPanner : public StreamPanner {
int outputs[3];
int desired_outputs[3];
- PBD::ScopedConnection speaker_connection;
-
VBAPSpeakers& _speakers;
void compute_gains (double g[3], int ls[3], int azi, int ele);
diff --git a/libs/ardour/ardour/vbap_speakers.h b/libs/ardour/ardour/vbap_speakers.h
index 51430398c0..19dc56c988 100644
--- a/libs/ardour/ardour/vbap_speakers.h
+++ b/libs/ardour/ardour/vbap_speakers.h
@@ -20,67 +20,45 @@
#define __libardour_vbap_speakers_h__
#include <string>
-#include <map>
+#include <vector>
+
+#include <boost/utility.hpp>
#include <pbd/signals.h>
#include "ardour/panner.h"
+#include "ardour/speakers.h"
namespace ARDOUR {
-class VBAPSpeakers {
- public:
- struct cart_vec {
- double x;
- double y;
- double z;
- };
-
- struct ang_vec {
- double azi;
- double ele;
- double length;
- };
+class Speakers;
- static const int MAX_TRIPLET_AMOUNT = 60;
+class VBAPSpeakers : public boost::noncopyable {
+ public:
typedef std::vector<double> dvector;
- VBAPSpeakers ();
- ~VBAPSpeakers ();
-
- int add_speaker (double direction, double elevation = 0.0);
- void remove_speaker (int id);
- void move_speaker (int id, double direction, double elevation = 0.0);
- void clear_speakers ();
-
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 void angle_to_cart(ang_vec *from, cart_vec *to);
+ static VBAPSpeakers& instance (Speakers&);
- PBD::Signal0<void> Changed;
+ ~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;
- /* A struct for a loudspeaker instance */
- struct Speaker {
- int id;
- cart_vec coords;
- ang_vec angles;
-
- Speaker (int, double azimuth, double elevation);
-
- void move (double azimuth, double elevation);
- };
+ VBAPSpeakers (Speakers&);
struct azimuth_sorter {
bool operator() (const Speaker& s1, const Speaker& s2) {
- return s1.angles.azi < s2.angles.azi;
+ return s1.angles().azi < s2.angles().azi;
}
};
@@ -96,7 +74,6 @@ class VBAPSpeakers {
tmatrix() : dvector (3, 0.0) {}
};
- std::vector<Speaker> _speakers;
std::vector<dvector> _matrices; /* holds matrices for a given speaker combinations */
std::vector<tmatrix> _speaker_tuples; /* holds speakers IDs for a given combination */
@@ -107,11 +84,11 @@ class VBAPSpeakers {
struct ls_triplet_chain *next;
};
- static float vec_angle(cart_vec v1, cart_vec v2);
- static float vec_length(cart_vec v1);
- static float vec_prod(cart_vec v1, cart_vec v2);
+ 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(cart_vec v1,cart_vec v2, cart_vec *res);
+ 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);
@@ -123,7 +100,6 @@ class VBAPSpeakers {
void sort_2D_lss (int* sorted_lss);
int calc_2D_inv_tmatrix (double azi1,double azi2, double* inv_mat);
- void dump_speakers (std::ostream&);
};
} /* namespace */
diff --git a/libs/ardour/panner.cc b/libs/ardour/panner.cc
index c44ab96a59..e438742266 100644
--- a/libs/ardour/panner.cc
+++ b/libs/ardour/panner.cc
@@ -61,15 +61,14 @@ using namespace PBD;
float Panner::current_automation_version_number = 1.0;
string EqualPowerStereoPanner::name = "Equal Power Stereo";
-string Multi2dPanner::name = "Multiple (2D)";
/* this is a default mapper of control values to a pan position
others can be imagined.
*/
-static pan_t direct_control_to_pan (double fract)
+static double direct_control_to_stereo_pan (double fract)
{
- return fract;
+ return BaseStereoPanner::lr_fract_to_azimuth (fract);
}
StreamPanner::StreamPanner (Panner& p, Evoral::Parameter param)
@@ -82,10 +81,6 @@ StreamPanner::StreamPanner (Panner& p, Evoral::Parameter param)
/* get our AutomationControl from our parent Panner, creating it if required */
_control = boost::dynamic_pointer_cast<AutomationControl> (parent.control (param, true));
-
- _x = 0.5;
- _y = 0.5;
- _z = 0.5;
}
StreamPanner::~StreamPanner ()
@@ -104,7 +99,7 @@ StreamPanner::set_mono (bool yn)
void
Panner::PanControllable::set_value (double val)
{
- panner.streampanner (parameter().id()).set_position (direct_control_to_pan (val));
+ panner.streampanner (parameter().id()).set_position (AngularVector (direct_control_to_stereo_pan (val), 0.0));
AutomationControl::set_value(val);
}
@@ -124,47 +119,14 @@ StreamPanner::set_muted (bool yn)
}
void
-StreamPanner::set_position (float xpos, bool link_call)
-{
- if (!link_call && parent.linked()) {
- parent.set_position (xpos, *this);
- }
-
- if (_x != xpos) {
- _x = xpos;
- update ();
- Changed ();
- _control->Changed ();
- }
-}
-
-void
-StreamPanner::set_position (float xpos, float ypos, bool link_call)
+StreamPanner::set_position (const AngularVector& av, bool link_call)
{
if (!link_call && parent.linked()) {
- parent.set_position (xpos, ypos, *this);
+ parent.set_position (av, *this);
}
- if (_x != xpos || _y != ypos) {
- _x = xpos;
- _y = ypos;
- update ();
- Changed ();
- _control->Changed ();
- }
-}
-
-void
-StreamPanner::set_position (float xpos, float ypos, float zpos, bool link_call)
-{
- if (!link_call && parent.linked()) {
- parent.set_position (xpos, ypos, zpos, *this);
- }
-
- if (_x != xpos || _y != ypos || _z != zpos) {
- _x = xpos;
- _y = ypos;
- _z = zpos;
+ if (_angles != av) {
+ _angles = av;
update ();
Changed ();
_control->Changed ();
@@ -431,10 +393,12 @@ EqualPowerStereoPanner::update ()
overhead.
*/
- /* x == 0 => hard left
- x == 1 => hard right
+ /* 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;
@@ -444,7 +408,7 @@ EqualPowerStereoPanner::update ()
desired_left = panL * (scale * panL + 1.0f - scale);
desired_right = panR * (scale * panR + 1.0f - scale);
- effective_x = _x;
+ _effective_angles = _angles;
//_control->set_value(x);
}
@@ -472,7 +436,7 @@ EqualPowerStereoPanner::do_distribute_automated (AudioBuffer& srcbuf, BufferSet&
/* store effective pan position. do this even if we are muted */
if (nframes > 0) {
- effective_x = buffers[0][nframes-1];
+ _effective_angles.azi = BaseStereoPanner::lr_fract_to_azimuth (buffers[0][nframes-1]);
}
if (_muted) {
@@ -519,7 +483,7 @@ EqualPowerStereoPanner::do_distribute_automated (AudioBuffer& srcbuf, BufferSet&
}
StreamPanner*
-EqualPowerStereoPanner::factory (Panner& parent, Evoral::Parameter param)
+EqualPowerStereoPanner::factory (Panner& parent, Evoral::Parameter param, Speakers& /* ignored */)
{
return new EqualPowerStereoPanner (parent, param);
}
@@ -537,8 +501,8 @@ EqualPowerStereoPanner::state (bool /*full_state*/)
char buf[64];
LocaleGuard lg (X_("POSIX"));
- snprintf (buf, sizeof (buf), "%.12g", _x);
- root->add_property (X_("x"), buf);
+ snprintf (buf, sizeof (buf), "%.12g", _angles.azi);
+ root->add_property (X_("azimuth"), buf);
root->add_property (X_("type"), EqualPowerStereoPanner::name);
// XXX: dont save automation here... its part of the automatable panner now.
@@ -556,10 +520,15 @@ EqualPowerStereoPanner::set_state (const XMLNode& node, int version)
const XMLProperty* prop;
LocaleGuard lg (X_("POSIX"));
- if ((prop = node.property (X_("x")))) {
- const float pos = atof (prop->value().c_str());
- set_position (pos, true);
- }
+ if ((prop = node.property (X_("azimuth")))) {
+ AngularVector a (atof (prop->value().c_str()), 0.0);
+ set_position (a, true);
+ } else if ((prop = node.property (X_("x")))) {
+ /* old school cartesian positioning */
+ AngularVector a;
+ a.azi = BaseStereoPanner::lr_fract_to_azimuth (atof (prop->value().c_str()));
+ set_position (a, true);
+ }
StreamPanner::set_state (node, version);
@@ -575,7 +544,8 @@ EqualPowerStereoPanner::set_state (const XMLNode& node, int version)
_control->alist()->set_state (*((*iter)->children().front()), version);
if (_control->alist()->automation_state() != Off) {
- set_position (_control->list()->eval (parent.session().transport_frame()));
+ double degrees = BaseStereoPanner::lr_fract_to_azimuth (_control->list()->eval (parent.session().transport_frame()));
+ set_position (AngularVector (degrees, 0.0));
}
}
}
@@ -583,190 +553,6 @@ EqualPowerStereoPanner::set_state (const XMLNode& node, int version)
return 0;
}
-/*----------------------------------------------------------------------*/
-
-Multi2dPanner::Multi2dPanner (Panner& p, Evoral::Parameter param)
- : StreamPanner (p, param)
-{
- update ();
-}
-
-Multi2dPanner::~Multi2dPanner ()
-{
-}
-
-void
-Multi2dPanner::update ()
-{
- static const float BIAS = FLT_MIN;
- uint32_t i;
- uint32_t const nouts = parent.nouts ();
- float dsq[nouts];
- float f, fr;
- vector<pan_t> pans;
-
- f = 0.0f;
-
- for (i = 0; i < nouts; i++) {
- dsq[i] = ((_x - parent.output(i).x) * (_x - parent.output(i).x) + (_y - parent.output(i).y) * (_y - parent.output(i).y) + BIAS);
- if (dsq[i] < 0.0) {
- dsq[i] = 0.0;
- }
- f += dsq[i] * dsq[i];
- }
-#ifdef __APPLE__
- // terrible hack to support OSX < 10.3.9 builds
- fr = (float) (1.0 / sqrt((double)f));
-#else
- fr = 1.0 / sqrtf(f);
-#endif
- for (i = 0; i < nouts; ++i) {
- parent.output(i).desired_pan = 1.0f - (dsq[i] * fr);
- }
-
- effective_x = _x;
-}
-
-void
-Multi2dPanner::do_distribute (AudioBuffer& srcbuf, BufferSet& obufs, gain_t gain_coeff, nframes_t nframes)
-{
- Sample* dst;
- pan_t pan;
-
- if (_muted) {
- return;
- }
-
- Sample* const src = srcbuf.data();
-
- uint32_t const N = parent.nouts ();
- for (uint32_t n = 0; n < N; ++n) {
- Panner::Output& o = parent.output (n);
-
- dst = obufs.get_audio(n).data();
-
-#ifdef CAN_INTERP
- if (fabsf ((delta = (left_interp - desired_left))) > 0.002) { // about 1 degree of arc
-
- /* interpolate over 64 frames or nframes, whichever is smaller */
-
- nframes_t limit = min ((nframes_t)64, nframes);
- nframes_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;
- }
-
- pan = left * gain_coeff;
- mix_buffers_with_gain(dst+n,src+n,nframes-n,pan);
-
- } else {
-
-#else
- pan = o.desired_pan;
-
- if ((pan *= gain_coeff) != 1.0f) {
-
- if (pan != 0.0f) {
- mix_buffers_with_gain(dst,src,nframes,pan);
- }
-
- } else {
- mix_buffers_no_gain(dst,src,nframes);
- }
-#endif
-#ifdef CAN_INTERP
- }
-#endif
- }
-
- return;
-}
-
-void
-Multi2dPanner::do_distribute_automated (AudioBuffer& /*src*/, BufferSet& /*obufs*/,
- nframes_t /*start*/, nframes_t /*end*/, nframes_t /*nframes*/,
- pan_t** /*buffers*/)
-{
- if (_muted) {
- return;
- }
-
- /* what ? */
-
- return;
-}
-
-StreamPanner*
-Multi2dPanner::factory (Panner& p, Evoral::Parameter param)
-{
- return new Multi2dPanner (p, param);
-}
-
-int
-Multi2dPanner::load (istream& /*in*/, string /*path*/, uint32_t& /*linecnt*/)
-{
- return 0;
-}
-
-XMLNode&
-Multi2dPanner::get_state (void)
-{
- return state (true);
-}
-
-XMLNode&
-Multi2dPanner::state (bool /*full_state*/)
-{
- XMLNode* root = new XMLNode ("StreamPanner");
- char buf[64];
- LocaleGuard lg (X_("POSIX"));
-
- snprintf (buf, sizeof (buf), "%.12g", _x);
- root->add_property (X_("x"), buf);
- snprintf (buf, sizeof (buf), "%.12g", _y);
- root->add_property (X_("y"), buf);
- root->add_property (X_("type"), Multi2dPanner::name);
-
- /* XXX no meaningful automation yet */
-
- return *root;
-}
-
-int
-Multi2dPanner::set_state (const XMLNode& node, int /*version*/)
-{
- const XMLProperty* prop;
- float newx,newy;
- LocaleGuard lg (X_("POSIX"));
-
- newx = -1;
- newy = -1;
-
- if ((prop = node.property (X_("x")))) {
- newx = atof (prop->value().c_str());
- }
-
- if ((prop = node.property (X_("y")))) {
- newy = atof (prop->value().c_str());
- }
-
- if (_x < 0 || _y < 0) {
- error << _("badly-formed positional data for Multi2dPanner - ignored")
- << endmsg;
- return -1;
- }
-
- set_position (newx, newy);
- return 0;
-}
-
-/*---------------------------------------------------------------------- */
-
Panner::Panner (string name, Session& s)
: SessionObject (s, name)
, Automatable (s)
@@ -827,16 +613,20 @@ Panner::reset_to_default ()
}
if (outputs.size() == 2) {
+ AngularVector a;
switch (_streampanners.size()) {
case 1:
- _streampanners.front()->set_position (0.5);
+ 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:
- _streampanners.front()->set_position (0.0);
+ a.azi = 180.0; /* "left", in degrees */
+ _streampanners.front()->set_position (a);
_streampanners.front()->pan_control()->list()->reset_default (0.0);
- _streampanners.back()->set_position (1.0);
+ a.azi = 0.0; /* "right", in degrees */
+ _streampanners.back()->set_position (a);
_streampanners.back()->pan_control()->list()->reset_default (1.0);
return;
default:
@@ -848,13 +638,15 @@ Panner::reset_to_default ()
vector<StreamPanner*>::iterator p;
for (o = outputs.begin(), p = _streampanners.begin(); o != outputs.end() && p != _streampanners.end(); ++o, ++p) {
- (*p)->set_position ((*o).x, (*o).y);
+ (*p)->set_position ((*o).position);
}
}
void
Panner::reset_streampanner (uint32_t which)
{
+ AngularVector a;
+
if (which >= _streampanners.size() || which >= outputs.size()) {
return;
}
@@ -868,24 +660,27 @@ Panner::reset_streampanner (uint32_t which)
switch (_streampanners.size()) {
case 1:
/* stereo out, 1 stream, default = middle */
- _streampanners.front()->set_position (0.5);
+ 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) {
- _streampanners.front()->set_position (0.0);
- _streampanners.front()->pan_control()->list()->reset_default (0.0);
- } else {
- _streampanners.back()->set_position (1.0);
- _streampanners.back()->pan_control()->list()->reset_default (1.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].x, outputs[which].y);
+ _streampanners[which]->set_position (outputs[which].position);
}
}
@@ -947,16 +742,13 @@ Panner::reset (uint32_t nouts, uint32_t npans)
break;
case 2: // line
- outputs.push_back (Output (0, 0));
- outputs.push_back (Output (1.0, 0));
+ 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;
- case 3:
- case 4:
- case 5:
default:
setup_speakers (nouts);
for (n = 0; n < npans; ++n) {
@@ -983,24 +775,46 @@ Panner::reset (uint32_t nouts, uint32_t npans)
if (npans == 2 && outputs.size() == 2) {
/* Do this only if we changed configuration, or our configuration
- appears to be the default set up (center).
+ appears to be the default set up (zero degrees)
*/
- float left;
- float right;
+ AngularVector left;
+ AngularVector right;
- _streampanners.front()->get_position (left);
- _streampanners.back()->get_position (right);
+ left = _streampanners.front()->get_position ();
+ right = _streampanners.back()->get_position ();
- if (changed || ((left == 0.5) && (right == 0.5))) {
+ if (changed || ((left.azi == 0.0) && (right.azi == 0.0))) {
- _streampanners.front()->set_position (0.0);
+ _streampanners.front()->set_position (AngularVector (180.0, 0.0));
_streampanners.front()->pan_control()->list()->reset_default (0.0);
- _streampanners.back()->set_position (1.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;
+ }
+
+ for (std::vector<StreamPanner*>::iterator x = _streampanners.begin(); x != _streampanners.end(); ++x) {
+ (*x)->set_position (AngularVector (deg, 0.0));
+ deg += degree_step;
+ }
+ }
}
void
@@ -1076,12 +890,12 @@ Panner::automation_style () const
struct PanPlugins {
string name;
uint32_t nouts;
- StreamPanner* (*factory)(Panner&, Evoral::Parameter);
+ StreamPanner* (*factory)(Panner&, Evoral::Parameter, Speakers&);
};
PanPlugins pan_plugins[] = {
{ EqualPowerStereoPanner::name, 2, EqualPowerStereoPanner::factory },
- { Multi2dPanner::name, 3, Multi2dPanner::factory },
+ { VBAPanner::name, 3, VBAPanner::factory },
{ string (""), 0, 0 }
};
@@ -1104,10 +918,10 @@ Panner::state (bool full)
for (vector<Panner::Output>::iterator o = outputs.begin(); o != outputs.end(); ++o) {
XMLNode* onode = new XMLNode (X_("Output"));
- snprintf (buf, sizeof (buf), "%.12g", (*o).x);
- onode->add_property (X_("x"), buf);
- snprintf (buf, sizeof (buf), "%.12g", (*o).y);
- onode->add_property (X_("y"), buf);
+ snprintf (buf, sizeof (buf), "%.12g", (*o).position.azi);
+ onode->add_property (X_("azimuth"), buf);
+ snprintf (buf, sizeof (buf), "%.12g", (*o).position.ele);
+ onode->add_property (X_("elevation"), buf);
node->add_child_nocopy (*onode);
}
@@ -1143,7 +957,6 @@ Panner::set_state (const XMLNode& node, int version)
set_linked (string_is_affirmative (prop->value()));
}
-
if ((prop = node.property (X_("bypassed"))) != 0) {
set_bypassed (string_is_affirmative (prop->value()));
}
@@ -1158,15 +971,20 @@ Panner::set_state (const XMLNode& node, int version)
for (niter = nlist.begin(); niter != nlist.end(); ++niter) {
if ((*niter)->name() == X_("Output")) {
- float x, y;
+ AngularVector a;
- prop = (*niter)->property (X_("x"));
- sscanf (prop->value().c_str(), "%g", &x);
+ 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()));
+ }
- prop = (*niter)->property (X_("y"));
- sscanf (prop->value().c_str(), "%g", &y);
+ if ((prop = (*niter)->property (X_("elevation")))) {
+ sscanf (prop->value().c_str(), "%lg", &a.ele);
+ }
- outputs.push_back (Output (x, y));
+ outputs.push_back (Output (a));
}
}
@@ -1185,7 +1003,7 @@ Panner::set_state (const XMLNode& node, int version)
assumption, but it's still an assumption.
*/
- sp = pan_plugins[i].factory (*this, Evoral::Parameter(PanAutomation, 0, num_panners));
+ sp = pan_plugins[i].factory (*this, Evoral::Parameter(PanAutomation, 0, num_panners), _session.get_speakers ());
num_panners++;
if (sp->set_state (**niter, version) == 0) {
@@ -1244,25 +1062,21 @@ Panner::touching () const
}
void
-Panner::set_position (float xpos, StreamPanner& orig)
+Panner::set_position (const AngularVector& a, StreamPanner& orig)
{
- float xnow;
- float xdelta ;
- float xnew;
+ AngularVector delta;
+ AngularVector new_position;
- orig.get_position (xnow);
- xdelta = xpos - xnow;
+ 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 (xpos, true);
+ (*i)->set_position (a, true);
} else {
- (*i)->get_position (xnow);
- xnew = min (1.0f, xnow + xdelta);
- xnew = max (0.0f, xnew);
- (*i)->set_position (xnew, true);
+ new_position = (*i)->get_position() + delta;
+ (*i)->set_position (new_position, true);
}
}
@@ -1270,117 +1084,10 @@ Panner::set_position (float xpos, StreamPanner& orig)
for (vector<StreamPanner*>::iterator i = _streampanners.begin(); i != _streampanners.end(); ++i) {
if (*i == &orig) {
- (*i)->set_position (xpos, true);
+ (*i)->set_position (a, true);
} else {
- (*i)->get_position (xnow);
- xnew = min (1.0f, xnow - xdelta);
- xnew = max (0.0f, xnew);
- (*i)->set_position (xnew, true);
- }
- }
- }
-}
-
-void
-Panner::set_position (float xpos, float ypos, StreamPanner& orig)
-{
- float xnow, ynow;
- float xdelta, ydelta;
- float xnew, ynew;
-
- orig.get_position (xnow, ynow);
- xdelta = xpos - xnow;
- ydelta = ypos - ynow;
-
- if (_link_direction == SameDirection) {
-
- for (vector<StreamPanner*>::iterator i = _streampanners.begin(); i != _streampanners.end(); ++i) {
- if (*i == &orig) {
- (*i)->set_position (xpos, ypos, true);
- } else {
- (*i)->get_position (xnow, ynow);
-
- xnew = min (1.0f, xnow + xdelta);
- xnew = max (0.0f, xnew);
-
- ynew = min (1.0f, ynow + ydelta);
- ynew = max (0.0f, ynew);
-
- (*i)->set_position (xnew, ynew, true);
- }
- }
-
- } else {
-
- for (vector<StreamPanner*>::iterator i = _streampanners.begin(); i != _streampanners.end(); ++i) {
- if (*i == &orig) {
- (*i)->set_position (xpos, ypos, true);
- } else {
- (*i)->get_position (xnow, ynow);
-
- xnew = min (1.0f, xnow - xdelta);
- xnew = max (0.0f, xnew);
-
- ynew = min (1.0f, ynow - ydelta);
- ynew = max (0.0f, ynew);
-
- (*i)->set_position (xnew, ynew, true);
- }
- }
- }
-}
-
-void
-Panner::set_position (float xpos, float ypos, float zpos, StreamPanner& orig)
-{
- float xnow, ynow, znow;
- float xdelta, ydelta, zdelta;
- float xnew, ynew, znew;
-
- orig.get_position (xnow, ynow, znow);
- xdelta = xpos - xnow;
- ydelta = ypos - ynow;
- zdelta = zpos - znow;
-
- if (_link_direction == SameDirection) {
-
- for (vector<StreamPanner*>::iterator i = _streampanners.begin(); i != _streampanners.end(); ++i) {
- if (*i == &orig) {
- (*i)->set_position (xpos, ypos, zpos, true);
- } else {
- (*i)->get_position (xnow, ynow, znow);
-
- xnew = min (1.0f, xnow + xdelta);
- xnew = max (0.0f, xnew);
-
- ynew = min (1.0f, ynow + ydelta);
- ynew = max (0.0f, ynew);
-
- znew = min (1.0f, znow + zdelta);
- znew = max (0.0f, znew);
-
- (*i)->set_position (xnew, ynew, znew, true);
- }
- }
-
- } else {
-
- for (vector<StreamPanner*>::iterator i = _streampanners.begin(); i != _streampanners.end(); ++i) {
- if (*i == &orig) {
- (*i)->set_position (xpos, ypos, true);
- } else {
- (*i)->get_position (xnow, ynow, znow);
-
- xnew = min (1.0f, xnow - xdelta);
- xnew = max (0.0f, xnew);
-
- ynew = min (1.0f, ynow - ydelta);
- ynew = max (0.0f, ynew);
-
- znew = min (1.0f, znow + zdelta);
- znew = max (0.0f, znew);
-
- (*i)->set_position (xnew, ynew, znew, true);
+ new_position = (*i)->get_position() - delta;
+ (*i)->set_position (new_position, true);
}
}
}
@@ -1638,47 +1345,45 @@ Panner::setup_speakers (uint32_t nouts)
{
switch (nouts) {
case 3:
- outputs.push_back (Output (0.5, 0));
- outputs.push_back (Output (0, 1.0));
- outputs.push_back (Output (1.0, 1.0));
+ /* 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 (Output (0, 1.0));
- outputs.push_back (Output (1.0, 1.0));
- outputs.push_back (Output (1.0, 0));
- outputs.push_back (Output (0, 0));
+ 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;
- case 5: //square+offcenter center
- outputs.push_back (Output (0, 0));
- outputs.push_back (Output (1.0, 0));
- outputs.push_back (Output (1.0, 1.0));
- outputs.push_back (Output (0, 1.0));
- outputs.push_back (Output (0.5, 0.75));
- break;
-
- default:
- /* XXX horrible placement. FIXME */
- for (uint32_t n = 0; n < nouts; ++n) {
- outputs.push_back (Output (0.1 * n, 0.1 * n));
+ 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)));
}
}
+ }
- VBAPSpeakers& speakers (_session.get_speakers());
-
+ Speakers& speakers (_session.get_speakers());
+
speakers.clear_speakers ();
for (vector<Output>::iterator o = outputs.begin(); o != outputs.end(); ++o) {
- double azimuth;
- double elevation;
- double tx, ty, tz;
-
- tx = (*o).x - 0.5;
- ty = (*o).y - 0.5;
- tz = 0.0; // XXX change this if we ever do actual 3D
-
- cart_to_azi_ele (tx, ty, tz, azimuth, elevation);
- speakers.add_speaker (azimuth, elevation);
+ speakers.add_speaker ((*o).position);
}
}
diff --git a/libs/ardour/session.cc b/libs/ardour/session.cc
index 64c5a173c4..06fd2ec41d 100644
--- a/libs/ardour/session.cc
+++ b/libs/ardour/session.cc
@@ -4202,11 +4202,12 @@ Session::ensure_search_path_includes (const string& path, DataType type)
}
}
-VBAPSpeakers&
+Speakers&
Session::get_speakers()
{
if (!_speakers) {
- _speakers = new VBAPSpeakers;
+ _speakers = new Speakers;
}
+
return *_speakers;
}
diff --git a/libs/ardour/speakers.cc b/libs/ardour/speakers.cc
new file mode 100644
index 0000000000..c5495f204d
--- /dev/null
+++ b/libs/ardour/speakers.cc
@@ -0,0 +1,108 @@
+/*
+ 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.
+*/
+
+#include "ardour/speaker.h"
+#include "ardour/speakers.h"
+
+using namespace ARDOUR;
+using namespace PBD;
+using namespace std;
+
+Speaker::Speaker (int i, const AngularVector& position)
+ : id (i)
+{
+ move (position);
+}
+
+void
+Speaker::move (const AngularVector& new_position)
+{
+ _angles = new_position;
+ _angles.cartesian (_coords);
+}
+
+Speakers::Speakers ()
+{
+}
+
+Speakers::~Speakers ()
+{
+}
+
+void
+Speakers::dump_speakers (ostream& o)
+{
+ for (vector<Speaker>::iterator i = _speakers.begin(); i != _speakers.end(); ++i) {
+ o << "Speaker " << (*i).id << " @ "
+ << (*i).coords().x << ", " << (*i).coords().y << ", " << (*i).coords().z
+ << " azimuth " << (*i).angles().azi
+ << " elevation " << (*i).angles().ele
+ << " distance " << (*i).angles().length
+ << endl;
+ }
+}
+
+void
+Speakers::clear_speakers ()
+{
+ _speakers.clear ();
+ update ();
+}
+
+int
+Speakers::add_speaker (const AngularVector& position)
+{
+ int id = _speakers.size();
+
+ cerr << "Added speaker " << id << " at " << position.azi << " /= " << position.ele << endl;
+
+ _speakers.push_back (Speaker (id, position));
+ update ();
+
+ dump_speakers (cerr);
+ Changed ();
+
+ return id;
+}
+
+void
+Speakers::remove_speaker (int id)
+{
+ for (vector<Speaker>::iterator i = _speakers.begin(); i != _speakers.end(); ) {
+ if ((*i).id == id) {
+ i = _speakers.erase (i);
+ update ();
+ break;
+ }
+ }
+}
+
+void
+Speakers::move_speaker (int id, const AngularVector& new_position)
+{
+ for (vector<Speaker>::iterator i = _speakers.begin(); i != _speakers.end(); ++i) {
+ if ((*i).id == id) {
+ (*i).move (new_position);
+ update ();
+ break;
+ }
+ }
+}
+
+
+
diff --git a/libs/ardour/vbap.cc b/libs/ardour/vbap.cc
index bb5bed4963..31a8e6c134 100644
--- a/libs/ardour/vbap.cc
+++ b/libs/ardour/vbap.cc
@@ -41,6 +41,7 @@
#include "pbd/cartesian.h"
+#include "ardour/speakers.h"
#include "ardour/vbap.h"
#include "ardour/vbap_speakers.h"
#include "ardour/audio_buffer.h"
@@ -50,13 +51,13 @@ using namespace PBD;
using namespace ARDOUR;
using namespace std;
-VBAPanner::VBAPanner (Panner& parent, Evoral::Parameter param, VBAPSpeakers& s)
+string VBAPanner::name = X_("VBAP");
+
+VBAPanner::VBAPanner (Panner& parent, Evoral::Parameter param, Speakers& s)
: StreamPanner (parent, param)
, _dirty (false)
- , _speakers (s)
-
+ , _speakers (VBAPSpeakers::instance (s))
{
- _speakers.Changed.connect_same_thread (speaker_connection, boost::bind (&VBAPanner::mark_dirty, this));
}
VBAPanner::~VBAPanner ()
@@ -72,16 +73,11 @@ VBAPanner::mark_dirty ()
void
VBAPanner::update ()
{
- /* convert from coordinate space with (0,0) at upper left to (0,0) at center and dimensions of 1 unit */
- _x -= 0.5;
- _y -= 0.5;
-
-
- /* we're 2D for now */
- _z = 0.0;
-
- cart_to_azi_ele (_x, _y, _z, _azimuth, _elevation);
+ /* force 2D for now */
+ _angles.ele = 0.0;
_dirty = true;
+
+ Changed ();
}
void
@@ -234,3 +230,10 @@ 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
index 3881c971d8..308f1beafb 100644
--- a/libs/ardour/vbap_speakers.cc
+++ b/libs/ardour/vbap_speakers.cc
@@ -39,100 +39,40 @@
#include "ardour/vbap_speakers.h"
using namespace ARDOUR;
+using namespace PBD;
using namespace std;
-VBAPSpeakers::Speaker::Speaker (int i, double azimuth, double elevation)
- : id (i)
-{
- move (azimuth, elevation);
- cerr << setprecision (5) << "%%%%%%%%%% New speaker @ " << angles.azi << ", " << angles.ele << endl;
-}
-
-void
-VBAPSpeakers::Speaker::move (double azimuth, double elevation)
-{
- angles.azi = azimuth;
- angles.ele = elevation;
- angles.length = 1.0;
- angle_to_cart (&angles, &coords);
-}
+VBAPSpeakers* VBAPSpeakers::_instance = 0;
-VBAPSpeakers::VBAPSpeakers ()
- : _dimension (2)
-{
-}
-
-VBAPSpeakers::~VBAPSpeakers ()
+VBAPSpeakers&
+VBAPSpeakers::instance (Speakers& s)
{
-}
-
-void
-VBAPSpeakers::dump_speakers (ostream& o)
-{
- for (vector<Speaker>::iterator i = _speakers.begin(); i != _speakers.end(); ++i) {
- o << "Speaker " << (*i).id << " @ "
- << (*i).coords.x << ", " << (*i).coords.y << ", " << (*i).coords.z
- << " azimuth " << (*i).angles.azi
- << " elevation " << (*i).angles.ele
- << " distance " << (*i).angles.length
- << endl;
+ if (_instance == 0) {
+ _instance = new VBAPSpeakers (s);
}
-}
-void
-VBAPSpeakers::clear_speakers ()
-{
- _speakers.clear ();
- update ();
+ return *_instance;
}
-int
-VBAPSpeakers::add_speaker (double azimuth, double elevation)
-{
- int id = _speakers.size();
-
- cerr << "Added speaker " << id << " at " << azimuth << " /= " << elevation << endl;
-
- _speakers.push_back (Speaker (id, azimuth, elevation));
- update ();
-
- dump_speakers (cerr);
-
- return id;
-}
-
-void
-VBAPSpeakers::remove_speaker (int id)
+VBAPSpeakers::VBAPSpeakers (Speakers& s)
+ : _dimension (2)
+ , _speakers (s.speakers())
{
- for (vector<Speaker>::iterator i = _speakers.begin(); i != _speakers.end(); ) {
- if ((*i).id == id) {
- i = _speakers.erase (i);
- update ();
- break;
- }
- }
+ s.Changed.connect_same_thread (speaker_connection, boost::bind (&VBAPSpeakers::update, this));
}
-void
-VBAPSpeakers::move_speaker (int id, double direction, double elevation)
+VBAPSpeakers::~VBAPSpeakers ()
{
- for (vector<Speaker>::iterator i = _speakers.begin(); i != _speakers.end(); ++i) {
- if ((*i).id == id) {
- (*i).move (direction, elevation);
- update ();
- break;
- }
- }
}
void
VBAPSpeakers::update ()
{
int dim = 2;
-
- for (vector<Speaker>::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";
+
+ 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;
}
@@ -157,17 +97,9 @@ VBAPSpeakers::update ()
} else {
choose_speaker_pairs ();
}
-
- Changed (); /* EMIT SIGNAL */
}
void
-VBAPSpeakers::angle_to_cart(ang_vec *from, cart_vec *to)
-{
- PBD::azi_ele_to_cart (from->azi, from->ele, to->x, to->y, to->z);
-}
-
-void
VBAPSpeakers::choose_speaker_triplets(struct ls_triplet_chain **ls_triplets)
{
/* Selects the loudspeaker triplets, and
@@ -217,7 +149,7 @@ VBAPSpeakers::choose_speaker_triplets(struct ls_triplet_chain **ls_triplets)
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));
+ distance = fabs(vec_angle(_speakers[i].coords(),_speakers[j].coords()));
k=0;
while(distance_table[k] < distance) {
k++;
@@ -291,7 +223,9 @@ VBAPSpeakers::any_ls_inside_triplet(int a, int b, int c)
{
/* returns 1 if there is loudspeaker(s) inside given ls triplet */
float invdet;
- cart_vec *lp1, *lp2, *lp3;
+ const CartesianVector* lp1;
+ const CartesianVector* lp2;
+ const CartesianVector* lp3;
float invmx[9];
int i,j;
float tmp;
@@ -299,9 +233,9 @@ VBAPSpeakers::any_ls_inside_triplet(int a, int b, int c)
bool this_inside;
int n_speakers = _speakers.size();
- lp1 = &(_speakers[a].coords);
- lp2 = &(_speakers[b].coords);
- lp3 = &(_speakers[c].coords);
+ 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))
@@ -323,9 +257,9 @@ VBAPSpeakers::any_ls_inside_triplet(int a, int b, int c)
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];
+ 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;
}
@@ -369,7 +303,7 @@ VBAPSpeakers::add_ldsp_triplet(int i, int j, int k, struct ls_triplet_chain **ls
}
float
-VBAPSpeakers::vec_angle(cart_vec v1, cart_vec v2)
+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)));
@@ -386,13 +320,13 @@ VBAPSpeakers::vec_angle(cart_vec v1, cart_vec v2)
}
float
-VBAPSpeakers::vec_length(cart_vec v1)
+VBAPSpeakers::vec_length(CartesianVector v1)
{
return (sqrt(v1.x*v1.x + v1.y*v1.y + v1.z*v1.z));
}
float
-VBAPSpeakers::vec_prod(cart_vec v1, cart_vec v2)
+VBAPSpeakers::vec_prod(CartesianVector v1, CartesianVector v2)
{
return (v1.x*v2.x + v1.y*v2.y + v1.z*v2.z);
}
@@ -405,13 +339,13 @@ VBAPSpeakers::vol_p_side_lgth(int i, int j,int k, const vector<Speaker>& speaker
This is used when removing too narrow triangles. */
float volper, lgth;
- cart_vec xprod;
+ 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)));
+ 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;
@@ -421,7 +355,7 @@ VBAPSpeakers::vol_p_side_lgth(int i, int j,int k, const vector<Speaker>& speaker
}
void
-VBAPSpeakers::cross_prod(cart_vec v1,cart_vec v2, cart_vec *res)
+VBAPSpeakers::cross_prod(CartesianVector v1,CartesianVector v2, CartesianVector *res)
{
float length;
@@ -429,7 +363,7 @@ VBAPSpeakers::cross_prod(cart_vec v1,cart_vec v2, cart_vec *res)
res->y = (v1.z * v2.x ) - (v1.x * v2.z);
res->z = (v1.x * v2.y ) - (v1.y * v2.x);
- length= vec_length(*res);
+ length = vec_length(*res);
res->x /= length;
res->y /= length;
res->z /= length;
@@ -446,30 +380,30 @@ VBAPSpeakers::lines_intersect (int i, int j, int k, int l)
if you want to have that paper.
*/
- cart_vec v1;
- cart_vec v2;
- cart_vec v3, neg_v3;
+ 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(_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));
+ 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*/
@@ -496,7 +430,9 @@ VBAPSpeakers::calculate_3x3_matrixes(struct ls_triplet_chain *ls_triplets)
{
/* Calculates the inverse matrices for 3D */
float invdet;
- cart_vec *lp1, *lp2, *lp3;
+ const CartesianVector* lp1;
+ const CartesianVector* lp2;
+ const CartesianVector* lp3;
float *invmx;
struct ls_triplet_chain *tr_ptr = ls_triplets;
int triplet_count = 0;
@@ -524,9 +460,9 @@ VBAPSpeakers::calculate_3x3_matrixes(struct ls_triplet_chain *ls_triplets)
}
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);
+ 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;
@@ -603,17 +539,17 @@ VBAPSpeakers::choose_speaker_pairs (){
for (speaker = 0; speaker < n_speakers-1; speaker++) {
cerr << "Looking at "
- << _speakers[sorted_speakers[speaker]].id << " @ " << _speakers[sorted_speakers[speaker]].angles.azi
+ << _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
+ << _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
+ << _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,
+ 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++;
@@ -621,10 +557,10 @@ VBAPSpeakers::choose_speaker_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,
+ 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++;
diff --git a/libs/ardour/wscript b/libs/ardour/wscript
index e6417d7f52..3ece2d0784 100644
--- a/libs/ardour/wscript
+++ b/libs/ardour/wscript
@@ -189,6 +189,7 @@ libardour_sources = [
'sndfilesource.cc',
'source.cc',
'source_factory.cc',
+ 'speakers.cc',
'strip_silence.cc',
'svn_revision.cc',
'tape_file_matcher.cc',
diff --git a/libs/pbd/cartesian.cc b/libs/pbd/cartesian.cc
index fe7bf29acf..8bece8e005 100644
--- a/libs/pbd/cartesian.cc
+++ b/libs/pbd/cartesian.cc
@@ -18,6 +18,7 @@
#include <iostream>
#include <math.h>
+
#include "pbd/cartesian.h"
using namespace std;
diff --git a/libs/pbd/pbd/cartesian.h b/libs/pbd/pbd/cartesian.h
index 67f8f0629c..b44a12cd3d 100644
--- a/libs/pbd/pbd/cartesian.h
+++ b/libs/pbd/pbd/cartesian.h
@@ -19,9 +19,87 @@
#ifndef __libpbd_cartesian_h__
#define __libpbd_cartesian_h__
+#include <cfloat>
+#include <cmath>
+
namespace PBD {
- void azi_ele_to_cart (double azi, double ele, double& x, double& y, double& z);
- void cart_to_azi_ele (double x, double y, double z, double& azi, double& ele);
+
+void azi_ele_to_cart (double azi, double ele, double& x, double& y, double& z);
+void cart_to_azi_ele (double x, double y, double z, double& azi, double& ele);
+
+struct AngularVector;
+
+struct CartesianVector {
+ double x;
+ double y;
+ double z;
+
+ CartesianVector () : x(0.0), y(0.0), z(0.0) {}
+ CartesianVector (double xp, double yp, double zp = 0.0) : x(xp), y(yp), z(zp) {}
+
+ CartesianVector& translate (CartesianVector& other, double xtranslate, double ytranslate, double ztranslate = 0.0) {
+ other.x += xtranslate;
+ other.y += ytranslate;
+ other.z += ztranslate;
+ return other;
+ }
+
+ CartesianVector& scale (CartesianVector& other, double xscale, double yscale, double zscale = 1.0) {
+ other.x *= xscale;
+ other.y *= yscale;
+ other.z *= zscale;
+ return other;
+ }
+
+ void angular (AngularVector&) const;
+};
+
+struct AngularVector {
+ double azi;
+ double ele;
+ double length;
+
+ AngularVector () : azi(0.0), ele(0.0), length (0.0) {}
+ AngularVector (double a, double e, double l = 1.0) : azi(a), ele(e), length (l) {}
+
+ AngularVector operator- (const AngularVector& other) const {
+ AngularVector r;
+ r.azi = azi - other.azi;
+ r.ele = ele - other.ele;
+ r.length = length - other.length;
+ return r;
+ }
+
+ AngularVector operator+ (const AngularVector& other) const {
+ AngularVector r;
+ r.azi = azi + other.azi;
+ r.ele = ele + other.ele;
+ r.length = length + other.length;
+ return r;
+ }
+
+ bool operator== (const AngularVector& other) const {
+ return fabs (azi - other.azi) <= FLT_EPSILON &&
+ fabs (ele - other.ele) <= FLT_EPSILON &&
+ fabs (length - other.length) <= FLT_EPSILON;
+ }
+
+ bool operator!= (const AngularVector& other) const {
+ return fabs (azi - other.azi) > FLT_EPSILON ||
+ fabs (ele - other.ele) > FLT_EPSILON ||
+ fabs (length - other.length) > FLT_EPSILON;
+ }
+
+ void cartesian (CartesianVector& c) const {
+ azi_ele_to_cart (azi, ele, c.x, c.y, c.z);
+ }
+};
+
+inline
+void CartesianVector::angular (AngularVector& a) const {
+ cart_to_azi_ele (x, y, z, a.azi, a.ele);
+}
+
}
#endif /* __libpbd_cartesian_h__ */
diff --git a/libs/surfaces/mackie/mackie_control_protocol.cc b/libs/surfaces/mackie/mackie_control_protocol.cc
index 6dda801ad6..c9c62dcb21 100644
--- a/libs/surfaces/mackie/mackie_control_protocol.cc
+++ b/libs/surfaces/mackie/mackie_control_protocol.cc
@@ -840,17 +840,13 @@ MackieControlProtocol::handle_control_event (SurfacePort & port, Control & contr
if (route->panner()->npanners() == 1 || (route->panner()->npanners() == 2 && route->panner()->linked()))
{
// assume pan for now
- float xpos;
- route->panner()->streampanner (0).get_effective_position (xpos);
-
- // calculate new value, and trim
- xpos += state.delta * state.sign;
- if (xpos > 1.0)
- xpos = 1.0;
- else if (xpos < 0.0)
- xpos = 0.0;
-
- route->panner()->streampanner (0).set_position (xpos);
+ AngularVector a = route->panner()->streampanner (0).get_effective_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);
}
}
else
@@ -1008,13 +1004,13 @@ MackieControlProtocol::notify_panner_changed (RouteSignal * route_signal, bool f
boost::shared_ptr<Panner> panner = route_signal->route()->panner();
if ((panner && panner->npanners() == 1) || (panner->npanners() == 2 && panner->linked()))
{
- float pos;
- route_signal->route()->panner()->streampanner(0).get_effective_position (pos);
+ 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 */
// 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, pos), MackieMidiBuilder::midi_pot_mode_dot);
+ MidiByteArray bytes = builder.build_led_ring (pot, ControlState (on, fract), MackieMidiBuilder::midi_pot_mode_dot);
// check that something has actually changed
if (force_update || bytes != route_signal->last_pan_written())
{