From 553cf2982c4905c5a08f305ce2772beaa8c50324 Mon Sep 17 00:00:00 2001 From: Paul Davis Date: Fri, 26 Nov 2010 17:43:03 +0000 Subject: one step closer to working vbap panning git-svn-id: svn://localhost/ardour2/branches/3.0@8091 d708f5d6-7413-0410-9779-e7cbd77b26cf --- libs/ardour/ardour/panner.h | 92 ++-- libs/ardour/ardour/session.h | 7 +- libs/ardour/ardour/speaker.h | 44 ++ libs/ardour/ardour/speakers.h | 55 +++ libs/ardour/ardour/vbap.h | 12 +- libs/ardour/ardour/vbap_speakers.h | 60 +-- libs/ardour/panner.cc | 569 ++++++------------------ libs/ardour/session.cc | 5 +- libs/ardour/speakers.cc | 108 +++++ libs/ardour/vbap.cc | 29 +- libs/ardour/vbap_speakers.cc | 202 +++------ libs/ardour/wscript | 1 + libs/pbd/cartesian.cc | 1 + libs/pbd/pbd/cartesian.h | 82 +++- libs/surfaces/mackie/mackie_control_protocol.cc | 24 +- 15 files changed, 585 insertions(+), 706 deletions(-) create mode 100644 libs/ardour/ardour/speaker.h create mode 100644 libs/ardour/ardour/speakers.h create mode 100644 libs/ardour/speakers.cc (limited to 'libs') 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 pan_control() { return _control; } - PBD::Signal0 Changed; /* for position */ + PBD::Signal0 Changed; /* for position or diffusion */ PBD::Signal0 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 @@ -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 +#include + +#include + +#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& speakers() { return _speakers; } + + void dump_speakers (std::ostream&); + + PBD::Signal0 Changed; + + protected: + std::vector _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 #include -#include - #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 -#include +#include + +#include #include #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 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 Changed; + ~VBAPSpeakers (); private: + static VBAPSpeakers* _instance; static const double MIN_VOL_P_SIDE_LGTH = 0.01; int _dimension; + std::vector& _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 _speakers; std::vector _matrices; /* holds matrices for a given speaker combinations */ std::vector _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&); - 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 (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 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::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::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::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::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::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::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::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::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::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::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::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::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::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::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::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::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::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::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,16 +97,8 @@ 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) { @@ -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 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 } 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 #include + #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 +#include + 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 = 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()) { -- cgit v1.2.3