NO-OP: whitespace

This commit is contained in:
Robin Gareus 2020-03-23 16:46:03 +01:00
parent ac9e16f0b8
commit 4780a0fd60
No known key found for this signature in database
GPG key ID: A090BCE02CF57F04
10 changed files with 948 additions and 956 deletions

View file

@ -21,40 +21,38 @@
#include <inttypes.h> #include <inttypes.h>
#include <cmath>
#include <cerrno> #include <cerrno>
#include <cstdlib> #include <cmath>
#include <string>
#include <cstdio> #include <cstdio>
#include <locale.h> #include <cstdlib>
#include <unistd.h>
#include <float.h> #include <float.h>
#include <locale.h>
#include <string>
#include <unistd.h>
#include <glibmm.h> #include <glibmm.h>
#include "pbd/cartesian.h" #include "pbd/cartesian.h"
#include "pbd/convert.h" #include "pbd/convert.h"
#include "pbd/enumwriter.h"
#include "pbd/error.h" #include "pbd/error.h"
#include "pbd/failed_constructor.h" #include "pbd/failed_constructor.h"
#include "pbd/xml++.h" #include "pbd/xml++.h"
#include "pbd/enumwriter.h"
#include "evoral/Curve.h" #include "evoral/Curve.h"
#include "ardour/session.h"
#include "ardour/panner.h"
#include "ardour/utils.h"
#include "ardour/audio_buffer.h" #include "ardour/audio_buffer.h"
#include "ardour/debug.h"
#include "ardour/runtime_functions.h"
#include "ardour/buffer_set.h" #include "ardour/buffer_set.h"
#include "ardour/audio_buffer.h" #include "ardour/debug.h"
#include "ardour/pannable.h" #include "ardour/pannable.h"
#include "ardour/panner.h"
#include "ardour/profile.h" #include "ardour/profile.h"
#include "ardour/runtime_functions.h"
#include "ardour/session.h"
#include "ardour/utils.h"
#include "pbd/i18n.h"
#include "panner_1in2out.h" #include "panner_1in2out.h"
#include "pbd/i18n.h"
#include "pbd/mathfix.h" #include "pbd/mathfix.h"
@ -63,15 +61,19 @@ using namespace ARDOUR;
using namespace PBD; using namespace PBD;
static PanPluginDescriptor _descriptor = { static PanPluginDescriptor _descriptor = {
"Mono to Stereo Panner", "Mono to Stereo Panner",
"http://ardour.org/plugin/panner_1in2out", "http://ardour.org/plugin/panner_1in2out",
"http://ardour.org/plugin/panner_1in2out#ui", "http://ardour.org/plugin/panner_1in2out#ui",
1, 2, 1, 2,
20, 20,
Panner1in2out::factory Panner1in2out::factory
}; };
extern "C" ARDOURPANNER_API PanPluginDescriptor* panner_descriptor () { return &_descriptor; } extern "C" ARDOURPANNER_API PanPluginDescriptor*
panner_descriptor ()
{
return &_descriptor;
}
Panner1in2out::Panner1in2out (boost::shared_ptr<Pannable> p) Panner1in2out::Panner1in2out (boost::shared_ptr<Pannable> p)
: Panner (p) : Panner (p)
@ -80,16 +82,16 @@ Panner1in2out::Panner1in2out (boost::shared_ptr<Pannable> p)
_pannable->pan_azimuth_control->set_value (0.5, Controllable::NoGroup); _pannable->pan_azimuth_control->set_value (0.5, Controllable::NoGroup);
} }
_can_automate_list.insert (Evoral::Parameter (PanAzimuthAutomation)); _can_automate_list.insert (Evoral::Parameter (PanAzimuthAutomation));
update (); update ();
left = desired_left; left = desired_left;
right = desired_right; right = desired_right;
left_interp = left; left_interp = left;
right_interp = right; right_interp = right;
_pannable->pan_azimuth_control->Changed.connect_same_thread (*this, boost::bind (&Panner1in2out::update, this)); _pannable->pan_azimuth_control->Changed.connect_same_thread (*this, boost::bind (&Panner1in2out::update, this));
} }
Panner1in2out::~Panner1in2out () Panner1in2out::~Panner1in2out ()
@ -99,32 +101,32 @@ Panner1in2out::~Panner1in2out ()
void void
Panner1in2out::update () Panner1in2out::update ()
{ {
float panR, panL; float panR, panL;
float const pan_law_attenuation = -3.0f; float const pan_law_attenuation = -3.0f;
float const scale = 2.0f - 4.0f * powf (10.0f,pan_law_attenuation/20.0f); float const scale = 2.0f - 4.0f * powf (10.0f, pan_law_attenuation / 20.0f);
panR = _pannable->pan_azimuth_control->get_value(); panR = _pannable->pan_azimuth_control->get_value ();
panL = 1 - panR; panL = 1 - panR;
desired_left = panL * (scale * panL + 1.0f - scale); desired_left = panL * (scale * panL + 1.0f - scale);
desired_right = panR * (scale * panR + 1.0f - scale); desired_right = panR * (scale * panR + 1.0f - scale);
} }
void void
Panner1in2out::set_position (double p) Panner1in2out::set_position (double p)
{ {
if (clamp_position (p)) { if (clamp_position (p)) {
_pannable->pan_azimuth_control->set_value (p, Controllable::NoGroup); _pannable->pan_azimuth_control->set_value (p, Controllable::NoGroup);
} }
} }
bool bool
Panner1in2out::clamp_position (double& p) Panner1in2out::clamp_position (double& p)
{ {
/* any position between 0.0 and 1.0 is legal */ /* any position between 0.0 and 1.0 is legal */
DEBUG_TRACE (DEBUG::Panning, string_compose ("want to move panner to %1 - always allowed in 0.0-1.0 range\n", p)); DEBUG_TRACE (DEBUG::Panning, string_compose ("want to move panner to %1 - always allowed in 0.0-1.0 range\n", p));
p = max (min (p, 1.0), 0.0); p = max (min (p, 1.0), 0.0);
return true; return true;
} }
pair<double, double> pair<double, double>
@ -136,37 +138,37 @@ Panner1in2out::position_range () const
double double
Panner1in2out::position () const Panner1in2out::position () const
{ {
return _pannable->pan_azimuth_control->get_value (); return _pannable->pan_azimuth_control->get_value ();
} }
void void
Panner1in2out::distribute_one (AudioBuffer& srcbuf, BufferSet& obufs, gain_t gain_coeff, pframes_t nframes, uint32_t /* not used */) Panner1in2out::distribute_one (AudioBuffer& srcbuf, BufferSet& obufs, gain_t gain_coeff, pframes_t nframes, uint32_t /* not used */)
{ {
assert (obufs.count().n_audio() == 2); assert (obufs.count ().n_audio () == 2);
pan_t delta; pan_t delta;
Sample* dst; Sample* dst;
pan_t pan; pan_t pan;
Sample* const src = srcbuf.data(); Sample* const src = srcbuf.data ();
/* LEFT OUTPUT */ /* LEFT OUTPUT */
dst = obufs.get_audio(0).data(); dst = obufs.get_audio (0).data ();
if (fabsf ((delta = (left - desired_left))) > 0.002) { // about 1 degree of arc if (fabsf ((delta = (left - desired_left))) > 0.002) { // about 1 degree of arc
/* we've moving the pan by an appreciable amount, so we must /* we've moving the pan by an appreciable amount, so we must
interpolate over 64 samples or nframes, whichever is smaller */ * interpolate over 64 samples or nframes, whichever is smaller */
pframes_t const limit = min ((pframes_t) 64, nframes); pframes_t const limit = min ((pframes_t)64, nframes);
pframes_t n; pframes_t n;
delta = -(delta / (float) (limit)); delta = -(delta / (float)(limit));
for (n = 0; n < limit; n++) { for (n = 0; n < limit; n++) {
left_interp = left_interp + delta; left_interp = left_interp + delta;
left = left_interp + 0.9 * (left - left_interp); left = left_interp + 0.9 * (left - left_interp);
dst[n] += src[n] * left * gain_coeff; dst[n] += src[n] * left * gain_coeff;
} }
@ -174,32 +176,25 @@ Panner1in2out::distribute_one (AudioBuffer& srcbuf, BufferSet& obufs, gain_t gai
pan = left * gain_coeff; pan = left * gain_coeff;
mix_buffers_with_gain (dst+n,src+n,nframes-n,pan); mix_buffers_with_gain (dst + n, src + n, nframes - n, pan);
} else { } else {
left = desired_left;
left = desired_left;
left_interp = left; left_interp = left;
if ((pan = (left * gain_coeff)) != 1.0f) { if ((pan = (left * gain_coeff)) != 1.0f) {
if (pan != 0.0f) { if (pan != 0.0f) {
/* pan is 1 but also not 0, so we must do it "properly" */ /* pan is 1 but also not 0, so we must do it "properly" */
mix_buffers_with_gain(dst,src,nframes,pan); mix_buffers_with_gain (dst, src, nframes, pan);
/* mark that we wrote into the buffer */
// obufs[0] = 0;
/* XXX it would be nice to mark that we wrote into the buffer */
} }
} else { } else {
/* pan is 1 so we can just copy the input samples straight in */ /* pan is 1 so we can just copy the input samples straight in */
mix_buffers_no_gain(dst,src,nframes); mix_buffers_no_gain (dst, src, nframes);
/* XXX it would be nice to mark that we wrote into the buffer */ /* XXX it would be nice to mark that we wrote into the buffer */
} }
@ -207,21 +202,21 @@ Panner1in2out::distribute_one (AudioBuffer& srcbuf, BufferSet& obufs, gain_t gai
/* RIGHT OUTPUT */ /* RIGHT OUTPUT */
dst = obufs.get_audio(1).data(); dst = obufs.get_audio (1).data ();
if (fabsf ((delta = (right - desired_right))) > 0.002) { // about 1 degree of arc if (fabsf ((delta = (right - desired_right))) > 0.002) { // about 1 degree of arc
/* we're moving the pan by an appreciable amount, so we must /* we're moving the pan by an appreciable amount, so we must
interpolate over 64 samples or nframes, whichever is smaller */ * interpolate over 64 samples or nframes, whichever is smaller */
pframes_t const limit = min ((pframes_t) 64, nframes); pframes_t const limit = min ((pframes_t)64, nframes);
pframes_t n; pframes_t n;
delta = -(delta / (float) (limit)); delta = -(delta / (float)(limit));
for (n = 0; n < limit; n++) { for (n = 0; n < limit; n++) {
right_interp = right_interp + delta; right_interp = right_interp + delta;
right = right_interp + 0.9 * (right - right_interp); right = right_interp + 0.9 * (right - right_interp);
dst[n] += src[n] * right * gain_coeff; dst[n] += src[n] * right * gain_coeff;
} }
@ -229,36 +224,31 @@ Panner1in2out::distribute_one (AudioBuffer& srcbuf, BufferSet& obufs, gain_t gai
pan = right * gain_coeff; pan = right * gain_coeff;
mix_buffers_with_gain(dst+n,src+n,nframes-n,pan); mix_buffers_with_gain (dst + n, src + n, nframes - n, pan);
/* XXX it would be nice to mark the buffer as written to */ /* XXX it would be nice to mark the buffer as written to */
} else { } else {
right = desired_right;
right = desired_right;
right_interp = right; right_interp = right;
if ((pan = (right * gain_coeff)) != 1.0f) { if ((pan = (right * gain_coeff)) != 1.0f) {
if (pan != 0.0f) { if (pan != 0.0f) {
/* pan is not 1 but also not 0, so we must do it "properly" */ /* pan is not 1 but also not 0, so we must do it "properly" */
mix_buffers_with_gain(dst,src,nframes,pan); mix_buffers_with_gain (dst, src, nframes, pan);
/* XXX it would be nice to mark the buffer as written to */ /* XXX it would be nice to mark the buffer as written to */
} }
} else { } else {
/* pan is 1 so we can just copy the input samples straight in */ /* pan is 1 so we can just copy the input samples straight in */
mix_buffers_no_gain(dst,src,nframes); mix_buffers_no_gain (dst, src, nframes);
/* XXX it would be nice to mark the buffer as written to */ /* XXX it would be nice to mark the buffer as written to */
} }
} }
} }
void void
@ -266,18 +256,18 @@ Panner1in2out::distribute_one_automated (AudioBuffer& srcbuf, BufferSet& obufs,
samplepos_t start, samplepos_t end, pframes_t nframes, samplepos_t start, samplepos_t end, pframes_t nframes,
pan_t** buffers, uint32_t which) pan_t** buffers, uint32_t which)
{ {
assert (obufs.count().n_audio() == 2); assert (obufs.count ().n_audio () == 2);
Sample* dst; Sample* dst;
pan_t* pbuf; pan_t* pbuf;
Sample* const src = srcbuf.data(); Sample* const src = srcbuf.data ();
pan_t* const position = buffers[0]; pan_t* const position = buffers[0];
/* fetch positional data */ /* fetch positional data */
if (!_pannable->pan_azimuth_control->list()->curve().rt_safe_get_vector (start, end, position, nframes)) { if (!_pannable->pan_azimuth_control->list ()->curve ().rt_safe_get_vector (start, end, position, nframes)) {
/* fallback */ /* fallback */
distribute_one (srcbuf, obufs, 1.0, nframes, which); distribute_one (srcbuf, obufs, 1.0, nframes, which);
return; return;
} }
@ -286,27 +276,26 @@ Panner1in2out::distribute_one_automated (AudioBuffer& srcbuf, BufferSet& obufs,
*/ */
const float pan_law_attenuation = -3.0f; const float pan_law_attenuation = -3.0f;
const float scale = 2.0f - 4.0f * powf (10.0f,pan_law_attenuation/20.0f); const float scale = 2.0f - 4.0f * powf (10.0f, pan_law_attenuation / 20.0f);
for (pframes_t n = 0; n < nframes; ++n) { for (pframes_t n = 0; n < nframes; ++n) {
float panR = position[n];
const float panL = 1 - panR;
float panR = position[n]; /* note that are overwriting buffers, but its OK
const float panL = 1 - panR; * because we're finished with their old contents
* (position automation data) and are
* replacing it with panning/gain coefficients
* that we need to actually process the data.
*/
/* note that are overwriting buffers, but its OK buffers[0][n] = panL * (scale * panL + 1.0f - scale);
because we're finished with their old contents buffers[1][n] = panR * (scale * panR + 1.0f - scale);
(position automation data) and are }
replacing it with panning/gain coefficients
that we need to actually process the data.
*/
buffers[0][n] = panL * (scale * panL + 1.0f - scale);
buffers[1][n] = panR * (scale * panR + 1.0f - scale);
}
/* LEFT OUTPUT */ /* LEFT OUTPUT */
dst = obufs.get_audio(0).data(); dst = obufs.get_audio (0).data ();
pbuf = buffers[0]; pbuf = buffers[0];
for (pframes_t n = 0; n < nframes; ++n) { for (pframes_t n = 0; n < nframes; ++n) {
@ -317,7 +306,7 @@ Panner1in2out::distribute_one_automated (AudioBuffer& srcbuf, BufferSet& obufs,
/* RIGHT OUTPUT */ /* RIGHT OUTPUT */
dst = obufs.get_audio(1).data(); dst = obufs.get_audio (1).data ();
pbuf = buffers[1]; pbuf = buffers[1];
for (pframes_t n = 0; n < nframes; ++n) { for (pframes_t n = 0; n < nframes; ++n) {
@ -327,7 +316,6 @@ Panner1in2out::distribute_one_automated (AudioBuffer& srcbuf, BufferSet& obufs,
/* XXX it would be nice to mark the buffer as written to */ /* XXX it would be nice to mark the buffer as written to */
} }
Panner* Panner*
Panner1in2out::factory (boost::shared_ptr<Pannable> p, boost::shared_ptr<Speakers> /* ignored */) Panner1in2out::factory (boost::shared_ptr<Pannable> p, boost::shared_ptr<Speakers> /* ignored */)
{ {
@ -338,38 +326,37 @@ XMLNode&
Panner1in2out::get_state () Panner1in2out::get_state ()
{ {
XMLNode& root (Panner::get_state ()); XMLNode& root (Panner::get_state ());
root.set_property (X_("uri"), _descriptor.panner_uri); root.set_property (X_ ("uri"), _descriptor.panner_uri);
/* this is needed to allow new sessions to load with old Ardour: */ /* this is needed to allow new sessions to load with old Ardour: */
root.set_property (X_("type"), _descriptor.name); root.set_property (X_ ("type"), _descriptor.name);
return root; return root;
} }
string string
Panner1in2out::value_as_string (boost::shared_ptr<const AutomationControl> ac) const Panner1in2out::value_as_string (boost::shared_ptr<const AutomationControl> ac) const
{ {
/* DO NOT USE LocaleGuard HERE */ double val = ac->get_value ();
double val = ac->get_value();
switch (ac->parameter().type()) { switch (ac->parameter ().type ()) {
case PanAzimuthAutomation: case PanAzimuthAutomation:
/* We show the position of the center of the image relative to the left & right. /* We show the position of the center of the image relative to the left & right.
This is expressed as a pair of percentage values that ranges from (100,0) * This is expressed as a pair of percentage values that ranges from (100,0)
(hard left) through (50,50) (hard center) to (0,100) (hard right). * (hard left) through (50,50) (hard center) to (0,100) (hard right).
*
* This is pretty wierd, but its the way audio engineers expect it. Just remember that
* the center of the USA isn't Kansas, its (50LA, 50NY) and it will all make sense.
*
* This is designed to be as narrow as possible. Dedicated
* panner GUIs can do their own version of this if they need
* something less compact.
*/
This is pretty wierd, but its the way audio engineers expect it. Just remember that return string_compose (_ ("L%1R%2"), (int)rint (100.0 * (1.0 - val)),
the center of the USA isn't Kansas, its (50LA, 50NY) and it will all make sense. (int)rint (100.0 * val));
This is designed to be as narrow as possible. Dedicated default:
panner GUIs can do their own version of this if they need return _ ("unused");
something less compact. }
*/
return string_compose (_("L%1R%2"), (int) rint (100.0 * (1.0 - val)),
(int) rint (100.0 * val));
default:
return _("unused");
}
} }
void void

View file

@ -21,36 +21,43 @@
#ifndef __ardour_panner_1in2out_h__ #ifndef __ardour_panner_1in2out_h__
#define __ardour_panner_1in2out_h__ #define __ardour_panner_1in2out_h__
#include <cmath>
#include <cassert> #include <cassert>
#include <vector> #include <cmath>
#include <string>
#include <iostream> #include <iostream>
#include <string>
#include <vector>
#include "pbd/stateful.h"
#include "pbd/controllable.h"
#include "pbd/cartesian.h" #include "pbd/cartesian.h"
#include "pbd/controllable.h"
#include "pbd/stateful.h"
#include "ardour/types.h"
#include "ardour/panner.h" #include "ardour/panner.h"
#include "ardour/types.h"
namespace ARDOUR
namespace ARDOUR { {
class Panner1in2out : public Panner class Panner1in2out : public Panner
{ {
public: public:
Panner1in2out (boost::shared_ptr<Pannable>); Panner1in2out (boost::shared_ptr<Pannable>);
~Panner1in2out (); ~Panner1in2out ();
void set_position (double); void set_position (double);
bool clamp_position (double&); bool clamp_position (double&);
std::pair<double, double> position_range () const; std::pair<double, double> position_range () const;
double position() const; double position () const;
ChanCount in() const { return ChanCount (DataType::AUDIO, 1); } ChanCount in () const
ChanCount out() const { return ChanCount (DataType::AUDIO, 2); } {
return ChanCount (DataType::AUDIO, 1);
}
ChanCount out () const
{
return ChanCount (DataType::AUDIO, 2);
}
static Panner* factory (boost::shared_ptr<Pannable>, boost::shared_ptr<Speakers>); static Panner* factory (boost::shared_ptr<Pannable>, boost::shared_ptr<Speakers>);
@ -60,7 +67,7 @@ class Panner1in2out : public Panner
void reset (); void reset ();
protected: protected:
float left; float left;
float right; float right;
float desired_left; float desired_left;
@ -76,6 +83,6 @@ class Panner1in2out : public Panner
void update (); void update ();
}; };
} // namespace } // namespace ARDOUR
#endif /* __ardour_panner_1in2out_h__ */ #endif /* __ardour_panner_1in2out_h__ */

View file

@ -20,35 +20,34 @@
#include <inttypes.h> #include <inttypes.h>
#include <cmath>
#include <cerrno> #include <cerrno>
#include <cstdlib> #include <cmath>
#include <string>
#include <cstdio> #include <cstdio>
#include <locale.h> #include <cstdlib>
#include <unistd.h>
#include <float.h> #include <float.h>
#include <locale.h>
#include <string>
#include <unistd.h>
#include <glibmm.h> #include <glibmm.h>
#include "pbd/cartesian.h" #include "pbd/cartesian.h"
#include "pbd/convert.h" #include "pbd/convert.h"
#include "pbd/enumwriter.h"
#include "pbd/error.h" #include "pbd/error.h"
#include "pbd/failed_constructor.h" #include "pbd/failed_constructor.h"
#include "pbd/xml++.h" #include "pbd/xml++.h"
#include "pbd/enumwriter.h"
#include "evoral/Curve.h" #include "evoral/Curve.h"
#include "ardour/audio_buffer.h"
#include "ardour/audio_buffer.h" #include "ardour/audio_buffer.h"
#include "ardour/buffer_set.h" #include "ardour/buffer_set.h"
#include "ardour/mix.h"
#include "ardour/pan_controllable.h" #include "ardour/pan_controllable.h"
#include "ardour/pannable.h" #include "ardour/pannable.h"
#include "ardour/runtime_functions.h" #include "ardour/runtime_functions.h"
#include "ardour/session.h" #include "ardour/session.h"
#include "ardour/utils.h" #include "ardour/utils.h"
#include "ardour/mix.h"
#include "panner_2in2out.h" #include "panner_2in2out.h"
@ -61,45 +60,49 @@ using namespace ARDOUR;
using namespace PBD; using namespace PBD;
static PanPluginDescriptor _descriptor = { static PanPluginDescriptor _descriptor = {
"Equal Power Stereo", "Equal Power Stereo",
"http://ardour.org/plugin/panner_2in2out", "http://ardour.org/plugin/panner_2in2out",
"http://ardour.org/plugin/panner_2in2out#ui", "http://ardour.org/plugin/panner_2in2out#ui",
2, 2, 2, 2,
20, 20,
Panner2in2out::factory Panner2in2out::factory
}; };
extern "C" ARDOURPANNER_API PanPluginDescriptor* panner_descriptor () { return &_descriptor; } extern "C" ARDOURPANNER_API PanPluginDescriptor*
panner_descriptor ()
{
return &_descriptor;
}
Panner2in2out::Panner2in2out (boost::shared_ptr<Pannable> p) Panner2in2out::Panner2in2out (boost::shared_ptr<Pannable> p)
: Panner (p) : Panner (p)
{ {
if (!_pannable->has_state()) { if (!_pannable->has_state ()) {
_pannable->pan_azimuth_control->set_value (0.5, Controllable::NoGroup); _pannable->pan_azimuth_control->set_value (0.5, Controllable::NoGroup);
_pannable->pan_width_control->set_value (1.0, Controllable::NoGroup); _pannable->pan_width_control->set_value (1.0, Controllable::NoGroup);
} }
double const w = width(); double const w = width ();
double const wrange = min (position(), (1 - position())) * 2; double const wrange = min (position (), (1 - position ())) * 2;
if (fabs(w) > wrange) { if (fabs (w) > wrange) {
set_width(w > 0 ? wrange : -wrange); set_width (w > 0 ? wrange : -wrange);
} }
_can_automate_list.insert (Evoral::Parameter (PanAzimuthAutomation)); _can_automate_list.insert (Evoral::Parameter (PanAzimuthAutomation));
_can_automate_list.insert (Evoral::Parameter (PanWidthAutomation)); _can_automate_list.insert (Evoral::Parameter (PanWidthAutomation));
update (); update ();
/* LEFT SIGNAL */ /* LEFT SIGNAL */
left_interp[0] = left[0] = desired_left[0]; left_interp[0] = left[0] = desired_left[0];
right_interp[0] = right[0] = desired_right[0]; right_interp[0] = right[0] = desired_right[0];
/* RIGHT SIGNAL */ /* RIGHT SIGNAL */
left_interp[1] = left[1] = desired_left[1]; left_interp[1] = left[1] = desired_left[1];
right_interp[1] = right[1] = desired_right[1]; right_interp[1] = right[1] = desired_right[1];
_pannable->pan_azimuth_control->Changed.connect_same_thread (*this, boost::bind (&Panner2in2out::update, this)); _pannable->pan_azimuth_control->Changed.connect_same_thread (*this, boost::bind (&Panner2in2out::update, this));
_pannable->pan_width_control->Changed.connect_same_thread (*this, boost::bind (&Panner2in2out::update, this)); _pannable->pan_width_control->Changed.connect_same_thread (*this, boost::bind (&Panner2in2out::update, this));
} }
Panner2in2out::~Panner2in2out () Panner2in2out::~Panner2in2out ()
@ -109,29 +112,29 @@ Panner2in2out::~Panner2in2out ()
double double
Panner2in2out::position () const Panner2in2out::position () const
{ {
return _pannable->pan_azimuth_control->get_value(); return _pannable->pan_azimuth_control->get_value ();
} }
double double
Panner2in2out::width () const Panner2in2out::width () const
{ {
return _pannable->pan_width_control->get_value(); return _pannable->pan_width_control->get_value ();
} }
void void
Panner2in2out::set_position (double p) Panner2in2out::set_position (double p)
{ {
if (clamp_position (p)) { if (clamp_position (p)) {
_pannable->pan_azimuth_control->set_value (p, Controllable::NoGroup); _pannable->pan_azimuth_control->set_value (p, Controllable::NoGroup);
} }
} }
void void
Panner2in2out::set_width (double p) Panner2in2out::set_width (double p)
{ {
if (clamp_width (p)) { if (clamp_width (p)) {
_pannable->pan_width_control->set_value (p, Controllable::NoGroup); _pannable->pan_width_control->set_value (p, Controllable::NoGroup);
} }
} }
void void
@ -150,149 +153,148 @@ Panner2in2out::update ()
return; return;
} }
/* it would be very nice to split this out into a virtual function /* it would be very nice to split this out into a virtual function
that can be accessed from BaseStereoPanner and used in do_distribute_automated(). * that can be accessed from BaseStereoPanner and used in do_distribute_automated().
*
* but the place where its used in do_distribute_automated() is a tight inner loop,
* and making "nframes" virtual function calls to compute values is an absurd
* overhead.
*/
but the place where its used in do_distribute_automated() is a tight inner loop, /* x == 0 => hard left = 180.0 degrees
and making "nframes" virtual function calls to compute values is an absurd * x == 1 => hard right = 0.0 degrees
overhead. */
*/
/* x == 0 => hard left = 180.0 degrees float pos[2];
x == 1 => hard right = 0.0 degrees double width = this->width ();
*/ const double direction_as_lr_fract = position ();
float pos[2]; double const wrange = min (position (), (1 - position ())) * 2;
double width = this->width (); if (fabs (width) > wrange) {
const double direction_as_lr_fract = position (); width = (width > 0 ? wrange : -wrange);
}
double const wrange = min (position(), (1 - position())) * 2; if (width < 0.0) {
if (fabs(width) > wrange) { width = -width;
width = (width > 0 ? wrange : -wrange); pos[0] = direction_as_lr_fract + (width / 2.0); // left signal lr_fract
} pos[1] = direction_as_lr_fract - (width / 2.0); // right signal lr_fract
} else {
pos[1] = direction_as_lr_fract + (width / 2.0); // right signal lr_fract
pos[0] = direction_as_lr_fract - (width / 2.0); // left signal lr_fract
}
if (width < 0.0) { /* compute target gain coefficients for both input signals */
width = -width;
pos[0] = direction_as_lr_fract + (width/2.0); // left signal lr_fract
pos[1] = direction_as_lr_fract - (width/2.0); // right signal lr_fract
} else {
pos[1] = direction_as_lr_fract + (width/2.0); // right signal lr_fract
pos[0] = direction_as_lr_fract - (width/2.0); // left signal lr_fract
}
/* compute target gain coefficients for both input signals */ float const pan_law_attenuation = -3.0f;
float const scale = 2.0f - 4.0f * powf (10.0f, pan_law_attenuation / 20.0f);
float panR;
float panL;
float const pan_law_attenuation = -3.0f; /* left signal */
float const scale = 2.0f - 4.0f * powf (10.0f,pan_law_attenuation/20.0f);
float panR;
float panL;
/* left signal */ panR = pos[0];
panL = 1 - panR;
desired_left[0] = panL * (scale * panL + 1.0f - scale);
desired_right[0] = panR * (scale * panR + 1.0f - scale);
panR = pos[0]; /* right signal */
panL = 1 - panR;
desired_left[0] = panL * (scale * panL + 1.0f - scale);
desired_right[0] = panR * (scale * panR + 1.0f - scale);
/* right signal */ panR = pos[1];
panL = 1 - panR;
panR = pos[1]; desired_left[1] = panL * (scale * panL + 1.0f - scale);
panL = 1 - panR; desired_right[1] = panR * (scale * panR + 1.0f - scale);
desired_left[1] = panL * (scale * panL + 1.0f - scale);
desired_right[1] = panR * (scale * panR + 1.0f - scale);
} }
bool bool
Panner2in2out::clamp_position (double& p) Panner2in2out::clamp_position (double& p)
{ {
double w = width (); double w = width ();
return clamp_stereo_pan (p, w); return clamp_stereo_pan (p, w);
} }
bool bool
Panner2in2out::clamp_width (double& w) Panner2in2out::clamp_width (double& w)
{ {
double p = position (); double p = position ();
return clamp_stereo_pan (p, w); return clamp_stereo_pan (p, w);
} }
pair<double, double> pair<double, double>
Panner2in2out::position_range () const Panner2in2out::position_range () const
{ {
return make_pair (0.5 - (1 - width()) / 2, 0.5 + (1 - width()) / 2); return make_pair (0.5 - (1 - width ()) / 2, 0.5 + (1 - width ()) / 2);
} }
pair<double, double> pair<double, double>
Panner2in2out::width_range () const Panner2in2out::width_range () const
{ {
double const w = min (position(), (1 - position())) * 2; double const w = min (position (), (1 - position ())) * 2;
return make_pair (-w, w); return make_pair (-w, w);
} }
bool bool
Panner2in2out::clamp_stereo_pan (double& direction_as_lr_fract, double& width) Panner2in2out::clamp_stereo_pan (double& direction_as_lr_fract, double& width)
{ {
double r_pos; double r_pos;
double l_pos; double l_pos;
width = max (min (width, 1.0), -1.0); width = max (min (width, 1.0), -1.0);
direction_as_lr_fract = max (min (direction_as_lr_fract, 1.0), 0.0); direction_as_lr_fract = max (min (direction_as_lr_fract, 1.0), 0.0);
r_pos = direction_as_lr_fract + (width/2.0); r_pos = direction_as_lr_fract + (width / 2.0);
l_pos = direction_as_lr_fract - (width/2.0); l_pos = direction_as_lr_fract - (width / 2.0);
if (width < 0.0) { if (width < 0.0) {
swap (r_pos, l_pos); swap (r_pos, l_pos);
} }
/* if the new left position is less than or equal to zero (hard left) and the left panner /* if the new left position is less than or equal to zero (hard left)
is already there, we're not moving the left signal. * and the left panner is already there, we're not moving the left signal.
*/ */
if (l_pos < 0.0) { if (l_pos < 0.0) {
return false; return false;
} }
/* if the new right position is less than or equal to 1.0 (hard right) and the right panner /* if the new right position is less than or equal to 1.0 (hard right)
is already there, we're not moving the right signal. * and the right panner is already there, we're not moving the right signal.
*/ */
if (r_pos > 1.0) { if (r_pos > 1.0) {
return false; return false;
}
} return true;
return true;
} }
void void
Panner2in2out::distribute_one (AudioBuffer& srcbuf, BufferSet& obufs, gain_t gain_coeff, pframes_t nframes, uint32_t which) Panner2in2out::distribute_one (AudioBuffer& srcbuf, BufferSet& obufs, gain_t gain_coeff, pframes_t nframes, uint32_t which)
{ {
assert (obufs.count().n_audio() == 2); assert (obufs.count ().n_audio () == 2);
pan_t delta; pan_t delta;
Sample* dst; Sample* dst;
pan_t pan; pan_t pan;
Sample* const src = srcbuf.data(); Sample* const src = srcbuf.data ();
/* LEFT OUTPUT */ /* LEFT OUTPUT */
dst = obufs.get_audio(0).data(); dst = obufs.get_audio (0).data ();
if (fabsf ((delta = (left[which] - desired_left[which]))) > 0.002) { // about 1 degree of arc if (fabsf ((delta = (left[which] - desired_left[which]))) > 0.002) { // about 1 degree of arc
/* we've moving the pan by an appreciable amount, so we must /* we've moving the pan by an appreciable amount, so we must
interpolate over 64 samples or nframes, whichever is smaller */ * interpolate over 64 samples or nframes, whichever is smaller */
pframes_t const limit = min ((pframes_t) 64, nframes); pframes_t const limit = min ((pframes_t)64, nframes);
pframes_t n; pframes_t n;
delta = -(delta / (float) (limit)); delta = -(delta / (float)(limit));
for (n = 0; n < limit; n++) { for (n = 0; n < limit; n++) {
left_interp[which] = left_interp[which] + delta; left_interp[which] = left_interp[which] + delta;
left[which] = left_interp[which] + 0.9 * (left[which] - left_interp[which]); left[which] = left_interp[which] + 0.9 * (left[which] - left_interp[which]);
dst[n] += src[n] * left[which] * gain_coeff; dst[n] += src[n] * left[which] * gain_coeff;
} }
@ -300,33 +302,25 @@ Panner2in2out::distribute_one (AudioBuffer& srcbuf, BufferSet& obufs, gain_t gai
pan = left[which] * gain_coeff; pan = left[which] * gain_coeff;
mix_buffers_with_gain (dst+n,src+n,nframes-n,pan); mix_buffers_with_gain (dst + n, src + n, nframes - n, pan);
} else { } else {
left[which] = desired_left[which];
left[which] = desired_left[which];
left_interp[which] = left[which]; left_interp[which] = left[which];
if ((pan = (left[which] * gain_coeff)) != 1.0f) { if ((pan = (left[which] * gain_coeff)) != 1.0f) {
if (pan != 0.0f) { if (pan != 0.0f) {
/* pan is 1 but also not 0, so we must do it "properly" */ /* pan is 1 but also not 0, so we must do it "properly" */
//obufs.get_audio(1).read_from (srcbuf, nframes); mix_buffers_with_gain (dst, src, nframes, pan);
mix_buffers_with_gain(dst,src,nframes,pan);
/* mark that we wrote into the buffer */
// obufs[0] = 0;
/* XXX it would be nice to mark that we wrote into the buffer */
} }
} else { } else {
/* pan is 1 so we can just copy the input samples straight in */ /* pan is 1 so we can just copy the input samples straight in */
mix_buffers_no_gain(dst,src,nframes); mix_buffers_no_gain (dst, src, nframes);
/* XXX it would be nice to mark that we wrote into the buffer */ /* XXX it would be nice to mark that we wrote into the buffer */
} }
@ -334,21 +328,21 @@ Panner2in2out::distribute_one (AudioBuffer& srcbuf, BufferSet& obufs, gain_t gai
/* RIGHT OUTPUT */ /* RIGHT OUTPUT */
dst = obufs.get_audio(1).data(); dst = obufs.get_audio (1).data ();
if (fabsf ((delta = (right[which] - desired_right[which]))) > 0.002) { // about 1 degree of arc if (fabsf ((delta = (right[which] - desired_right[which]))) > 0.002) { // about 1 degree of arc
/* we're moving the pan by an appreciable amount, so we must /* we're moving the pan by an appreciable amount, so we must
interpolate over 64 samples or nframes, whichever is smaller */ * interpolate over 64 samples or nframes, whichever is smaller */
pframes_t const limit = min ((pframes_t) 64, nframes); pframes_t const limit = min ((pframes_t)64, nframes);
pframes_t n; pframes_t n;
delta = -(delta / (float) (limit)); delta = -(delta / (float)(limit));
for (n = 0; n < limit; n++) { for (n = 0; n < limit; n++) {
right_interp[which] = right_interp[which] + delta; right_interp[which] = right_interp[which] + delta;
right[which] = right_interp[which] + 0.9 * (right[which] - right_interp[which]); right[which] = right_interp[which] + 0.9 * (right[which] - right_interp[which]);
dst[n] += src[n] * right[which] * gain_coeff; dst[n] += src[n] * right[which] * gain_coeff;
} }
@ -356,32 +350,27 @@ Panner2in2out::distribute_one (AudioBuffer& srcbuf, BufferSet& obufs, gain_t gai
pan = right[which] * gain_coeff; pan = right[which] * gain_coeff;
mix_buffers_with_gain(dst+n,src+n,nframes-n,pan); mix_buffers_with_gain (dst + n, src + n, nframes - n, pan);
/* XXX it would be nice to mark the buffer as written to */ /* XXX it would be nice to mark the buffer as written to */
} else { } else {
right[which] = desired_right[which];
right[which] = desired_right[which];
right_interp[which] = right[which]; right_interp[which] = right[which];
if ((pan = (right[which] * gain_coeff)) != 1.0f) { if ((pan = (right[which] * gain_coeff)) != 1.0f) {
if (pan != 0.0f) { if (pan != 0.0f) {
/* pan is not 1 but also not 0, so we must do it "properly" */ /* pan is not 1 but also not 0, so we must do it "properly" */
mix_buffers_with_gain(dst,src,nframes,pan); mix_buffers_with_gain (dst, src, nframes, pan);
// obufs.get_audio(1).read_from (srcbuf, nframes);
/* XXX it would be nice to mark the buffer as written to */ /* XXX it would be nice to mark the buffer as written to */
} }
} else { } else {
/* pan is 1 so we can just copy the input samples straight in */ /* pan is 1 so we can just copy the input samples straight in */
mix_buffers_no_gain(dst,src,nframes); mix_buffers_no_gain (dst, src, nframes);
/* XXX it would be nice to mark the buffer as written to */ /* XXX it would be nice to mark the buffer as written to */
} }
@ -393,65 +382,64 @@ Panner2in2out::distribute_one_automated (AudioBuffer& srcbuf, BufferSet& obufs,
samplepos_t start, samplepos_t end, pframes_t nframes, samplepos_t start, samplepos_t end, pframes_t nframes,
pan_t** buffers, uint32_t which) pan_t** buffers, uint32_t which)
{ {
assert (obufs.count().n_audio() == 2); assert (obufs.count ().n_audio () == 2);
Sample* dst; Sample* dst;
pan_t* pbuf; pan_t* pbuf;
Sample* const src = srcbuf.data(); Sample* const src = srcbuf.data ();
pan_t* const position = buffers[0]; pan_t* const position = buffers[0];
pan_t* const width = buffers[1]; pan_t* const width = buffers[1];
/* fetch positional data */ /* fetch positional data */
if (!_pannable->pan_azimuth_control->list()->curve().rt_safe_get_vector (start, end, position, nframes)) { if (!_pannable->pan_azimuth_control->list ()->curve ().rt_safe_get_vector (start, end, position, nframes)) {
/* fallback */ /* fallback */
distribute_one (srcbuf, obufs, 1.0, nframes, which); distribute_one (srcbuf, obufs, 1.0, nframes, which);
return; return;
} }
if (!_pannable->pan_width_control->list()->curve().rt_safe_get_vector (start, end, width, nframes)) { if (!_pannable->pan_width_control->list ()->curve ().rt_safe_get_vector (start, end, width, nframes)) {
/* fallback */ /* fallback */
distribute_one (srcbuf, obufs, 1.0, nframes, which); distribute_one (srcbuf, obufs, 1.0, nframes, which);
return; return;
} }
/* apply pan law to convert positional data into pan coefficients for /* apply pan law to convert positional data into pan coefficients for
each buffer (output) * each buffer (output)
*/ */
const float pan_law_attenuation = -3.0f; const float pan_law_attenuation = -3.0f;
const float scale = 2.0f - 4.0f * powf (10.0f,pan_law_attenuation/20.0f); const float scale = 2.0f - 4.0f * powf (10.0f, pan_law_attenuation / 20.0f);
for (pframes_t n = 0; n < nframes; ++n) { for (pframes_t n = 0; n < nframes; ++n) {
float panR;
float panR; if (which == 0) {
/* panning left signal */
panR = position[n] - (width[n] / 2.0f); // center - width/2
} else {
/* panning right signal */
panR = position[n] + (width[n] / 2.0f); // center - width/2
}
if (which == 0) { panR = max (0.f, min (1.f, panR));
// panning left signal
panR = position[n] - (width[n]/2.0f); // center - width/2
} else {
// panning right signal
panR = position[n] + (width[n]/2.0f); // center - width/2
}
panR = max(0.f, min(1.f, panR)); const float panL = 1 - panR;
const float panL = 1 - panR; /* note that are overwriting buffers, but its OK
* because we're finished with their old contents
* (position/width automation data) and are
* replacing it with panning/gain coefficients
* that we need to actually process the data.
*/
/* note that are overwriting buffers, but its OK buffers[0][n] = panL * (scale * panL + 1.0f - scale);
because we're finished with their old contents buffers[1][n] = panR * (scale * panR + 1.0f - scale);
(position/width automation data) and are }
replacing it with panning/gain coefficients
that we need to actually process the data.
*/
buffers[0][n] = panL * (scale * panL + 1.0f - scale);
buffers[1][n] = panR * (scale * panR + 1.0f - scale);
}
/* LEFT OUTPUT */ /* LEFT OUTPUT */
dst = obufs.get_audio(0).data(); dst = obufs.get_audio (0).data ();
pbuf = buffers[0]; pbuf = buffers[0];
for (pframes_t n = 0; n < nframes; ++n) { for (pframes_t n = 0; n < nframes; ++n) {
@ -462,7 +450,7 @@ Panner2in2out::distribute_one_automated (AudioBuffer& srcbuf, BufferSet& obufs,
/* RIGHT OUTPUT */ /* RIGHT OUTPUT */
dst = obufs.get_audio(1).data(); dst = obufs.get_audio (1).data ();
pbuf = buffers[1]; pbuf = buffers[1];
for (pframes_t n = 0; n < nframes; ++n) { for (pframes_t n = 0; n < nframes; ++n) {
@ -482,41 +470,40 @@ XMLNode&
Panner2in2out::get_state () Panner2in2out::get_state ()
{ {
XMLNode& root (Panner::get_state ()); XMLNode& root (Panner::get_state ());
root.set_property (X_("uri"), _descriptor.panner_uri); root.set_property (X_ ("uri"), _descriptor.panner_uri);
/* this is needed to allow new sessions to load with old Ardour: */ /* this is needed to allow new sessions to load with old Ardour: */
root.set_property (X_("type"), _descriptor.name); root.set_property (X_ ("type"), _descriptor.name);
return root; return root;
} }
string string
Panner2in2out::value_as_string (boost::shared_ptr<const AutomationControl> ac) const Panner2in2out::value_as_string (boost::shared_ptr<const AutomationControl> ac) const
{ {
/* DO NOT USE LocaleGuard HERE */ double val = ac->get_value ();
double val = ac->get_value();
switch (ac->parameter().type()) { switch (ac->parameter ().type ()) {
case PanAzimuthAutomation: case PanAzimuthAutomation:
/* We show the position of the center of the image relative to the left & right. /* We show the position of the center of the image relative to the left & right.
This is expressed as a pair of percentage values that ranges from (100,0) * This is expressed as a pair of percentage values that ranges from (100,0)
(hard left) through (50,50) (hard center) to (0,100) (hard right). * (hard left) through (50,50) (hard center) to (0,100) (hard right).
*
* This is pretty wierd, but its the way audio engineers expect it. Just remember that
* the center of the USA isn't Kansas, its (50LA, 50NY) and it will all make sense.
*
* This is designed to be as narrow as possible. Dedicated
* panner GUIs can do their own version of this if they need
* something less compact.
*/
This is pretty wierd, but its the way audio engineers expect it. Just remember that return string_compose (_ ("L%1R%2"), (int)rint (100.0 * (1.0 - val)),
the center of the USA isn't Kansas, its (50LA, 50NY) and it will all make sense. (int)rint (100.0 * val));
This is designed to be as narrow as possible. Dedicated case PanWidthAutomation:
panner GUIs can do their own version of this if they need return string_compose (_ ("Width: %1%%"), (int)floor (100.0 * val));
something less compact.
*/
return string_compose (_("L%1R%2"), (int) rint (100.0 * (1.0 - val)), default:
(int) rint (100.0 * val)); return _ ("unused");
}
case PanWidthAutomation:
return string_compose (_("Width: %1%%"), (int) floor (100.0 * val));
default:
return _("unused");
}
} }
void void

View file

@ -20,31 +20,37 @@
#ifndef __ardour_panner_2in2out_h__ #ifndef __ardour_panner_2in2out_h__
#define __ardour_panner_2in2out_h__ #define __ardour_panner_2in2out_h__
#include <cmath>
#include <cassert> #include <cassert>
#include <vector> #include <cmath>
#include <string>
#include <iostream> #include <iostream>
#include <string>
#include <vector>
#include "pbd/stateful.h"
#include "pbd/controllable.h"
#include "pbd/cartesian.h" #include "pbd/cartesian.h"
#include "pbd/controllable.h"
#include "pbd/stateful.h"
#include "ardour/automation_control.h"
#include "ardour/automatable.h" #include "ardour/automatable.h"
#include "ardour/automation_control.h"
#include "ardour/panner.h" #include "ardour/panner.h"
#include "ardour/types.h" #include "ardour/types.h"
namespace ARDOUR { namespace ARDOUR
{
class Panner2in2out : public Panner class Panner2in2out : public Panner
{ {
public: public:
Panner2in2out (boost::shared_ptr<Pannable>); Panner2in2out (boost::shared_ptr<Pannable>);
~Panner2in2out (); ~Panner2in2out ();
ChanCount in() const { return ChanCount (DataType::AUDIO, 2); } ChanCount in () const
ChanCount out() const { return ChanCount (DataType::AUDIO, 2); } {
return ChanCount (DataType::AUDIO, 2);
}
ChanCount out () const
{
return ChanCount (DataType::AUDIO, 2);
}
bool clamp_position (double&); bool clamp_position (double&);
bool clamp_width (double&); bool clamp_width (double&);
@ -69,7 +75,7 @@ class Panner2in2out : public Panner
void reset (); void reset ();
void thaw (); void thaw ();
protected: protected:
float left[2]; float left[2];
float right[2]; float right[2];
float desired_left[2]; float desired_left[2];
@ -77,7 +83,7 @@ class Panner2in2out : public Panner
float left_interp[2]; float left_interp[2];
float right_interp[2]; float right_interp[2];
private: private:
bool clamp_stereo_pan (double& direction_as_lr_fract, double& width); bool clamp_stereo_pan (double& direction_as_lr_fract, double& width);
void distribute_one (AudioBuffer& srcbuf, BufferSet& obufs, gain_t gain_coeff, pframes_t nframes, uint32_t which); void distribute_one (AudioBuffer& srcbuf, BufferSet& obufs, gain_t gain_coeff, pframes_t nframes, uint32_t which);
@ -86,6 +92,6 @@ class Panner2in2out : public Panner
pan_t** buffers, uint32_t which); pan_t** buffers, uint32_t which);
}; };
} // namespace } // namespace ARDOUR
#endif /* __ardour_panner_2in2out_h__ */ #endif /* __ardour_panner_2in2out_h__ */

View file

@ -19,35 +19,34 @@
#include <inttypes.h> #include <inttypes.h>
#include <cmath>
#include <cerrno> #include <cerrno>
#include <cstdlib> #include <cmath>
#include <string>
#include <cstdio> #include <cstdio>
#include <locale.h> #include <cstdlib>
#include <unistd.h>
#include <float.h> #include <float.h>
#include <locale.h>
#include <string>
#include <unistd.h>
#include <glibmm.h> #include <glibmm.h>
#include "pbd/cartesian.h" #include "pbd/cartesian.h"
#include "pbd/convert.h" #include "pbd/convert.h"
#include "pbd/enumwriter.h"
#include "pbd/error.h" #include "pbd/error.h"
#include "pbd/failed_constructor.h" #include "pbd/failed_constructor.h"
#include "pbd/xml++.h" #include "pbd/xml++.h"
#include "pbd/enumwriter.h"
#include "evoral/Curve.h" #include "evoral/Curve.h"
#include "ardour/audio_buffer.h"
#include "ardour/audio_buffer.h" #include "ardour/audio_buffer.h"
#include "ardour/buffer_set.h" #include "ardour/buffer_set.h"
#include "ardour/mix.h"
#include "ardour/pan_controllable.h" #include "ardour/pan_controllable.h"
#include "ardour/pannable.h" #include "ardour/pannable.h"
#include "ardour/runtime_functions.h" #include "ardour/runtime_functions.h"
#include "ardour/session.h" #include "ardour/session.h"
#include "ardour/utils.h" #include "ardour/utils.h"
#include "ardour/mix.h"
#include "panner_balance.h" #include "panner_balance.h"
@ -68,12 +67,16 @@ static PanPluginDescriptor _descriptor = {
Pannerbalance::factory Pannerbalance::factory
}; };
extern "C" ARDOURPANNER_API PanPluginDescriptor* panner_descriptor () { return &_descriptor; } extern "C" ARDOURPANNER_API PanPluginDescriptor*
panner_descriptor ()
{
return &_descriptor;
}
Pannerbalance::Pannerbalance (boost::shared_ptr<Pannable> p) Pannerbalance::Pannerbalance (boost::shared_ptr<Pannable> p)
: Panner (p) : Panner (p)
{ {
if (!_pannable->has_state()) { if (!_pannable->has_state ()) {
_pannable->pan_azimuth_control->set_value (0.5, Controllable::NoGroup); _pannable->pan_azimuth_control->set_value (0.5, Controllable::NoGroup);
} }
@ -96,10 +99,10 @@ Pannerbalance::~Pannerbalance ()
double double
Pannerbalance::position () const Pannerbalance::position () const
{ {
return _pannable->pan_azimuth_control->get_value(); return _pannable->pan_azimuth_control->get_value ();
} }
void void
Pannerbalance::set_position (double p) Pannerbalance::set_position (double p)
{ {
if (clamp_position (p)) { if (clamp_position (p)) {
@ -107,7 +110,7 @@ Pannerbalance::set_position (double p)
} }
} }
void void
Pannerbalance::thaw () Pannerbalance::thaw ()
{ {
Panner::thaw (); Panner::thaw ();
@ -123,7 +126,7 @@ Pannerbalance::update ()
return; return;
} }
float const pos = _pannable->pan_azimuth_control->get_value(); float const pos = _pannable->pan_azimuth_control->get_value ();
if (pos == .5) { if (pos == .5) {
desired_pos[0] = 1.0; desired_pos[0] = 1.0;
@ -153,29 +156,29 @@ Pannerbalance::position_range () const
void void
Pannerbalance::distribute_one (AudioBuffer& srcbuf, BufferSet& obufs, gain_t gain_coeff, pframes_t nframes, uint32_t which) Pannerbalance::distribute_one (AudioBuffer& srcbuf, BufferSet& obufs, gain_t gain_coeff, pframes_t nframes, uint32_t which)
{ {
assert (obufs.count().n_audio() == 2); assert (obufs.count ().n_audio () == 2);
pan_t delta; pan_t delta;
Sample* dst; Sample* dst;
pan_t pan; pan_t pan;
Sample* const src = srcbuf.data(); Sample* const src = srcbuf.data ();
dst = obufs.get_audio(which).data(); dst = obufs.get_audio (which).data ();
if (fabsf ((delta = (pos[which] - desired_pos[which]))) > 0.002) { // about 1 degree of arc if (fabsf ((delta = (pos[which] - desired_pos[which]))) > 0.002) { // about 1 degree of arc
/* we've moving the pan by an appreciable amount, so we must /* we've moving the pan by an appreciable amount, so we must
interpolate over 64 samples or nframes, whichever is smaller */ * interpolate over 64 samples or nframes, whichever is smaller */
pframes_t const limit = min ((pframes_t) 64, nframes); pframes_t const limit = min ((pframes_t)64, nframes);
pframes_t n; pframes_t n;
delta = -(delta / (float) (limit)); delta = -(delta / (float)(limit));
for (n = 0; n < limit; n++) { for (n = 0; n < limit; n++) {
pos_interp[which] = pos_interp[which] + delta; pos_interp[which] = pos_interp[which] + delta;
pos[which] = pos_interp[which] + 0.9 * (pos[which] - pos_interp[which]); pos[which] = pos_interp[which] + 0.9 * (pos[which] - pos_interp[which]);
dst[n] += src[n] * pos[which] * gain_coeff; dst[n] += src[n] * pos[which] * gain_coeff;
} }
@ -183,31 +186,24 @@ Pannerbalance::distribute_one (AudioBuffer& srcbuf, BufferSet& obufs, gain_t gai
pan = pos[which] * gain_coeff; pan = pos[which] * gain_coeff;
mix_buffers_with_gain (dst+n,src+n,nframes-n,pan); mix_buffers_with_gain (dst + n, src + n, nframes - n, pan);
} else { } else {
pos[which] = desired_pos[which];
pos[which] = desired_pos[which];
pos_interp[which] = pos[which]; pos_interp[which] = pos[which];
if ((pan = (pos[which] * gain_coeff)) != 1.0f) { if ((pan = (pos[which] * gain_coeff)) != 1.0f) {
if (pan != 0.0f) { if (pan != 0.0f) {
/* pan is 1 but also not 0, so we must do it "properly" */ /* pan is 1 but also not 0, so we must do it "properly" */
//obufs.get_audio(1).read_from (srcbuf, nframes); mix_buffers_with_gain (dst, src, nframes, pan);
mix_buffers_with_gain(dst,src,nframes,pan);
/* mark that we wrote into the buffer */
// obufs[0] = 0;
/* XXX it would be nice to mark the buffer as written to */
} }
} else { } else {
/* pan is 1 so we can just copy the input samples straight in */ /* pan is 1 so we can just copy the input samples straight in */
mix_buffers_no_gain(dst,src,nframes); mix_buffers_no_gain (dst, src, nframes);
} }
} }
} }
@ -217,23 +213,22 @@ Pannerbalance::distribute_one_automated (AudioBuffer& srcbuf, BufferSet& obufs,
samplepos_t start, samplepos_t end, pframes_t nframes, samplepos_t start, samplepos_t end, pframes_t nframes,
pan_t** buffers, uint32_t which) pan_t** buffers, uint32_t which)
{ {
assert (obufs.count().n_audio() == 2); assert (obufs.count ().n_audio () == 2);
Sample* dst; Sample* dst;
pan_t* pbuf; pan_t* pbuf;
Sample* const src = srcbuf.data(); Sample* const src = srcbuf.data ();
pan_t* const position = buffers[0]; pan_t* const position = buffers[0];
/* fetch positional data */ /* fetch positional data */
if (!_pannable->pan_azimuth_control->list()->curve().rt_safe_get_vector (start, end, position, nframes)) { if (!_pannable->pan_azimuth_control->list ()->curve ().rt_safe_get_vector (start, end, position, nframes)) {
/* fallback */ /* fallback */
distribute_one (srcbuf, obufs, 1.0, nframes, which); distribute_one (srcbuf, obufs, 1.0, nframes, which);
return; return;
} }
for (pframes_t n = 0; n < nframes; ++n) { for (pframes_t n = 0; n < nframes; ++n) {
float const pos = position[n]; float const pos = position[n];
if (which == 0) { // Left if (which == 0) { // Left
@ -251,7 +246,7 @@ Pannerbalance::distribute_one_automated (AudioBuffer& srcbuf, BufferSet& obufs,
} }
} }
dst = obufs.get_audio(which).data(); dst = obufs.get_audio (which).data ();
pbuf = buffers[which]; pbuf = buffers[which];
for (pframes_t n = 0; n < nframes; ++n) { for (pframes_t n = 0; n < nframes; ++n) {
@ -267,41 +262,40 @@ Pannerbalance::factory (boost::shared_ptr<Pannable> p, boost::shared_ptr<Speaker
return new Pannerbalance (p); return new Pannerbalance (p);
} }
XMLNode& XMLNode&
Pannerbalance::get_state () Pannerbalance::get_state ()
{ {
XMLNode& root (Panner::get_state ()); XMLNode& root (Panner::get_state ());
root.set_property (X_("uri"), _descriptor.panner_uri); root.set_property (X_ ("uri"), _descriptor.panner_uri);
/* this is needed to allow new sessions to load with old Ardour: */ /* this is needed to allow new sessions to load with old Ardour: */
root.set_property (X_("type"), _descriptor.name); root.set_property (X_ ("type"), _descriptor.name);
return root; return root;
} }
string string
Pannerbalance::value_as_string (boost::shared_ptr<const AutomationControl> ac) const Pannerbalance::value_as_string (boost::shared_ptr<const AutomationControl> ac) const
{ {
/* DO NOT USE LocaleGuard HERE */ double val = ac->get_value ();
double val = ac->get_value();
switch (ac->parameter().type()) { switch (ac->parameter ().type ()) {
case PanAzimuthAutomation: case PanAzimuthAutomation:
/* We show the position of the center of the image relative to the left & right. /* We show the position of the center of the image relative to the left & right.
This is expressed as a pair of percentage values that ranges from (100,0) * This is expressed as a pair of percentage values that ranges from (100,0)
(hard left) through (50,50) (hard center) to (0,100) (hard right). * (hard left) through (50,50) (hard center) to (0,100) (hard right).
*
* This is pretty wierd, but its the way audio engineers expect it. Just remember that
* the center of the USA isn't Kansas, its (50LA, 50NY) and it will all make sense.
*
* This is designed to be as narrow as possible. Dedicated
* panner GUIs can do their own version of this if they need
* something less compact.
*/
This is pretty wierd, but its the way audio engineers expect it. Just remember that return string_compose (_ ("L%1R%2"), (int)rint (100.0 * (1.0 - val)),
the center of the USA isn't Kansas, its (50LA, 50NY) and it will all make sense. (int)rint (100.0 * val));
This is designed to be as narrow as possible. Dedicated
panner GUIs can do their own version of this if they need
something less compact.
*/
return string_compose (_("L%1R%2"), (int) rint (100.0 * (1.0 - val)),
(int) rint (100.0 * val));
default: default:
return _("unused"); return _ ("unused");
} }
} }

View file

@ -19,36 +19,42 @@
#ifndef __ardour_panner_balance_h__ #ifndef __ardour_panner_balance_h__
#define __ardour_panner_balance_h__ #define __ardour_panner_balance_h__
#include <cmath>
#include <cassert> #include <cassert>
#include <vector> #include <cmath>
#include <string>
#include <iostream> #include <iostream>
#include <string>
#include <vector>
#include "pbd/stateful.h"
#include "pbd/controllable.h"
#include "pbd/cartesian.h" #include "pbd/cartesian.h"
#include "pbd/controllable.h"
#include "pbd/stateful.h"
#include "ardour/automation_control.h"
#include "ardour/automatable.h" #include "ardour/automatable.h"
#include "ardour/automation_control.h"
#include "ardour/panner.h" #include "ardour/panner.h"
#include "ardour/types.h" #include "ardour/types.h"
namespace ARDOUR { namespace ARDOUR
{
class Pannerbalance : public Panner class Pannerbalance : public Panner
{ {
public: public:
Pannerbalance (boost::shared_ptr<Pannable>); Pannerbalance (boost::shared_ptr<Pannable>);
~Pannerbalance (); ~Pannerbalance ();
ChanCount in() const { return ChanCount (DataType::AUDIO, 2); } ChanCount in () const
ChanCount out() const { return ChanCount (DataType::AUDIO, 2); } {
return ChanCount (DataType::AUDIO, 2);
}
ChanCount out () const
{
return ChanCount (DataType::AUDIO, 2);
}
void set_position (double); void set_position (double);
bool clamp_position (double&); bool clamp_position (double&);
std::pair<double, double> position_range () const; std::pair<double, double> position_range () const;
double position () const; double position () const;
static Panner* factory (boost::shared_ptr<Pannable>, boost::shared_ptr<Speakers>); static Panner* factory (boost::shared_ptr<Pannable>, boost::shared_ptr<Speakers>);
@ -59,20 +65,20 @@ class Pannerbalance : public Panner
void reset (); void reset ();
void thaw (); void thaw ();
protected: protected:
float pos[2]; float pos[2];
float desired_pos[2]; float desired_pos[2];
float pos_interp[2]; float pos_interp[2];
void update (); void update ();
private: private:
void distribute_one (AudioBuffer& srcbuf, BufferSet& obufs, gain_t gain_coeff, pframes_t nframes, uint32_t which); void distribute_one (AudioBuffer& srcbuf, BufferSet& obufs, gain_t gain_coeff, pframes_t nframes, uint32_t which);
void distribute_one_automated (AudioBuffer& srcbuf, BufferSet& obufs, void distribute_one_automated (AudioBuffer& srcbuf, BufferSet& obufs,
samplepos_t start, samplepos_t end, pframes_t nframes, samplepos_t start, samplepos_t end, pframes_t nframes,
pan_t** buffers, uint32_t which); pan_t** buffers, uint32_t which);
}; };
} // namespace } // namespace ARDOUR
#endif /* __ardour_panner_balance_h__ */ #endif /* __ardour_panner_balance_h__ */

View file

@ -20,8 +20,8 @@
*/ */
#include <cmath> #include <cmath>
#include <cstdlib>
#include <cstdio> #include <cstdio>
#include <cstdlib>
#include <cstring> #include <cstring>
#include <iostream> #include <iostream>
@ -51,131 +51,132 @@ using namespace ARDOUR;
using namespace std; using namespace std;
static PanPluginDescriptor _descriptor = { static PanPluginDescriptor _descriptor = {
"VBAP 2D panner", "VBAP 2D panner",
"http://ardour.org/plugin/panner_vbap", "http://ardour.org/plugin/panner_vbap",
"http://ardour.org/plugin/panner_vbap#ui", "http://ardour.org/plugin/panner_vbap#ui",
-1, -1, -1, -1,
10, 10,
VBAPanner::factory VBAPanner::factory
}; };
extern "C" ARDOURPANNER_API PanPluginDescriptor* panner_descriptor () { return &_descriptor; } extern "C" ARDOURPANNER_API PanPluginDescriptor*
panner_descriptor ()
{
return &_descriptor;
}
VBAPanner::Signal::Signal (VBAPanner&, uint32_t, uint32_t n_speakers) VBAPanner::Signal::Signal (VBAPanner&, uint32_t, uint32_t n_speakers)
{ {
resize_gains (n_speakers); resize_gains (n_speakers);
desired_gains[0] = desired_gains[1] = desired_gains[2] = 0; desired_gains[0] = desired_gains[1] = desired_gains[2] = 0;
outputs[0] = outputs[1] = outputs[2] = -1; outputs[0] = outputs[1] = outputs[2] = -1;
desired_outputs[0] = desired_outputs[1] = desired_outputs[2] = -1; desired_outputs[0] = desired_outputs[1] = desired_outputs[2] = -1;
} }
void void
VBAPanner::Signal::resize_gains (uint32_t n) VBAPanner::Signal::resize_gains (uint32_t n)
{ {
gains.assign (n, 0.0); gains.assign (n, 0.0);
} }
VBAPanner::VBAPanner (boost::shared_ptr<Pannable> p, boost::shared_ptr<Speakers> s) VBAPanner::VBAPanner (boost::shared_ptr<Pannable> p, boost::shared_ptr<Speakers> s)
: Panner (p) : Panner (p)
, _speakers (new VBAPSpeakers (s)) , _speakers (new VBAPSpeakers (s))
{ {
_pannable->pan_azimuth_control->Changed.connect_same_thread (*this, boost::bind (&VBAPanner::update, this)); _pannable->pan_azimuth_control->Changed.connect_same_thread (*this, boost::bind (&VBAPanner::update, this));
_pannable->pan_elevation_control->Changed.connect_same_thread (*this, boost::bind (&VBAPanner::update, this)); _pannable->pan_elevation_control->Changed.connect_same_thread (*this, boost::bind (&VBAPanner::update, this));
_pannable->pan_width_control->Changed.connect_same_thread (*this, boost::bind (&VBAPanner::update, this)); _pannable->pan_width_control->Changed.connect_same_thread (*this, boost::bind (&VBAPanner::update, this));
if (!_pannable->has_state()) { if (!_pannable->has_state ()) {
reset(); reset ();
} }
update (); update ();
} }
VBAPanner::~VBAPanner () VBAPanner::~VBAPanner ()
{ {
clear_signals (); clear_signals ();
} }
void void
VBAPanner::clear_signals () VBAPanner::clear_signals ()
{ {
for (vector<Signal*>::iterator i = _signals.begin(); i != _signals.end(); ++i) { for (vector<Signal*>::iterator i = _signals.begin (); i != _signals.end (); ++i) {
delete *i; delete *i;
} }
_signals.clear (); _signals.clear ();
} }
void void
VBAPanner::configure_io (ChanCount in, ChanCount /* ignored - we use Speakers */) VBAPanner::configure_io (ChanCount in, ChanCount /* ignored - we use Speakers */)
{ {
uint32_t n = in.n_audio(); uint32_t n = in.n_audio ();
clear_signals (); clear_signals ();
for (uint32_t i = 0; i < n; ++i) { for (uint32_t i = 0; i < n; ++i) {
Signal* s = new Signal (*this, i, _speakers->n_speakers()); Signal* s = new Signal (*this, i, _speakers->n_speakers ());
_signals.push_back (s); _signals.push_back (s);
}
} update ();
update ();
} }
void void
VBAPanner::update () VBAPanner::update ()
{ {
_can_automate_list.clear ();
_can_automate_list.insert (Evoral::Parameter (PanAzimuthAutomation));
if (_signals.size () > 1) {
_can_automate_list.insert (Evoral::Parameter (PanWidthAutomation));
}
if (_speakers->dimension () == 3) {
_can_automate_list.insert (Evoral::Parameter (PanElevationAutomation));
}
_can_automate_list.clear (); /* recompute signal directions based on panner azimuth and, if relevant, width (diffusion) and elevation parameters */
_can_automate_list.insert (Evoral::Parameter (PanAzimuthAutomation)); double elevation = _pannable->pan_elevation_control->get_value () * 90.0;
if (_signals.size() > 1) {
_can_automate_list.insert (Evoral::Parameter (PanWidthAutomation));
}
if (_speakers->dimension() == 3) {
_can_automate_list.insert (Evoral::Parameter (PanElevationAutomation));
}
/* recompute signal directions based on panner azimuth and, if relevant, width (diffusion) and elevation parameters */ if (_signals.size () > 1) {
double elevation = _pannable->pan_elevation_control->get_value() * 90.0; double w = -(_pannable->pan_width_control->get_value ());
double signal_direction = 1.0 - (_pannable->pan_azimuth_control->get_value () + (w / 2));
double grd_step_per_signal = w / (_signals.size () - 1);
for (vector<Signal*>::iterator s = _signals.begin (); s != _signals.end (); ++s) {
Signal* signal = *s;
if (_signals.size() > 1) { int over = signal_direction;
double w = - (_pannable->pan_width_control->get_value()); over -= (signal_direction >= 0) ? 0 : 1;
double signal_direction = 1.0 - (_pannable->pan_azimuth_control->get_value() + (w/2)); signal_direction -= (double)over;
double grd_step_per_signal = w / (_signals.size() - 1);
for (vector<Signal*>::iterator s = _signals.begin(); s != _signals.end(); ++s) {
Signal* signal = *s; signal->direction = AngularVector (signal_direction * 360.0, elevation);
compute_gains (signal->desired_gains, signal->desired_outputs, signal->direction.azi, signal->direction.ele);
signal_direction += grd_step_per_signal;
}
} else if (_signals.size () == 1) {
double center = (1.0 - _pannable->pan_azimuth_control->get_value ()) * 360.0;
int over = signal_direction; /* width has no role to play if there is only 1 signal: VBAP does not do "diffusion" of a single channel */
over -= (signal_direction >= 0) ? 0 : 1;
signal_direction -= (double)over;
signal->direction = AngularVector (signal_direction * 360.0, elevation); Signal* s = _signals.front ();
compute_gains (signal->desired_gains, signal->desired_outputs, signal->direction.azi, signal->direction.ele); s->direction = AngularVector (center, elevation);
signal_direction += grd_step_per_signal; compute_gains (s->desired_gains, s->desired_outputs, s->direction.azi, s->direction.ele);
} }
} else if (_signals.size() == 1) {
double center = (1.0 - _pannable->pan_azimuth_control->get_value()) * 360.0;
/* width has no role to play if there is only 1 signal: VBAP does not do "diffusion" of a single channel */ SignalPositionChanged (); /* emit */
Signal* s = _signals.front();
s->direction = AngularVector (center, elevation);
compute_gains (s->desired_gains, s->desired_outputs, s->direction.azi, s->direction.ele);
}
SignalPositionChanged(); /* emit */
} }
void void
VBAPanner::compute_gains (double gains[3], int speaker_ids[3], int azi, int ele) VBAPanner::compute_gains (double gains[3], int speaker_ids[3], int azi, int ele)
{ {
/* calculates gain factors using loudspeaker setup and given direction */ /* calculates gain factors using loudspeaker setup and given direction */
double cartdir[3]; double cartdir[3];
double power; double power;
int i,j,k; int i, j, k;
double small_g; double small_g;
double big_sm_g, gtmp[3]; double big_sm_g, gtmp[3];
const int dimension = _speakers->dimension(); const int dimension = _speakers->dimension ();
assert(dimension == 2 || dimension == 3); assert (dimension == 2 || dimension == 3);
spherical_to_cartesian (azi, ele, 1.0, cartdir[0], cartdir[1], cartdir[2]); spherical_to_cartesian (azi, ele, 1.0, cartdir[0], cartdir[1], cartdir[2]);
big_sm_g = -100000.0; big_sm_g = -100000.0;
@ -183,16 +184,14 @@ VBAPanner::compute_gains (double gains[3], int speaker_ids[3], int azi, int ele)
gains[0] = gains[1] = gains[2] = 0; gains[0] = gains[1] = gains[2] = 0;
speaker_ids[0] = speaker_ids[1] = speaker_ids[2] = 0; speaker_ids[0] = speaker_ids[1] = speaker_ids[2] = 0;
for (i = 0; i < _speakers->n_tuples(); i++) { for (i = 0; i < _speakers->n_tuples (); i++) {
small_g = 10000000.0; small_g = 10000000.0;
for (j = 0; j < dimension; j++) { for (j = 0; j < dimension; j++) {
gtmp[j] = 0.0; gtmp[j] = 0.0;
for (k = 0; k < dimension; k++) { for (k = 0; k < dimension; k++) {
gtmp[j] += cartdir[k] * _speakers->matrix(i)[j * dimension + k]; gtmp[j] += cartdir[k] * _speakers->matrix (i)[j * dimension + k];
} }
if (gtmp[j] < small_g) { if (gtmp[j] < small_g) {
@ -201,7 +200,6 @@ VBAPanner::compute_gains (double gains[3], int speaker_ids[3], int azi, int ele)
} }
if (small_g > big_sm_g) { if (small_g > big_sm_g) {
big_sm_g = small_g; big_sm_g = small_g;
gains[0] = gtmp[0]; gains[0] = gtmp[0];
@ -210,17 +208,17 @@ VBAPanner::compute_gains (double gains[3], int speaker_ids[3], int azi, int ele)
speaker_ids[0] = _speakers->speaker_for_tuple (i, 0); speaker_ids[0] = _speakers->speaker_for_tuple (i, 0);
speaker_ids[1] = _speakers->speaker_for_tuple (i, 1); speaker_ids[1] = _speakers->speaker_for_tuple (i, 1);
if (_speakers->dimension() == 3) { if (_speakers->dimension () == 3) {
gains[2] = gtmp[2]; gains[2] = gtmp[2];
speaker_ids[2] = _speakers->speaker_for_tuple (i, 2); speaker_ids[2] = _speakers->speaker_for_tuple (i, 2);
} else { } else {
gains[2] = 0.0; gains[2] = 0.0;
speaker_ids[2] = -1; speaker_ids[2] = -1;
} }
} }
} }
power = sqrt (gains[0]*gains[0] + gains[1]*gains[1] + gains[2]*gains[2]); power = sqrt (gains[0] * gains[0] + gains[1] * gains[1] + gains[2] * gains[2]);
if (power > 0) { if (power > 0) {
gains[0] /= power; gains[0] /= power;
@ -232,144 +230,136 @@ VBAPanner::compute_gains (double gains[3], int speaker_ids[3], int azi, int ele)
void void
VBAPanner::distribute (BufferSet& inbufs, BufferSet& obufs, gain_t gain_coefficient, pframes_t nframes) VBAPanner::distribute (BufferSet& inbufs, BufferSet& obufs, gain_t gain_coefficient, pframes_t nframes)
{ {
uint32_t n; uint32_t n;
vector<Signal*>::iterator s; vector<Signal*>::iterator s;
assert (inbufs.count().n_audio() == _signals.size()); assert (inbufs.count ().n_audio () == _signals.size ());
for (s = _signals.begin(), n = 0; s != _signals.end(); ++s, ++n) { for (s = _signals.begin (), n = 0; s != _signals.end (); ++s, ++n) {
Signal* signal (*s);
Signal* signal (*s); distribute_one (inbufs.get_audio (n), obufs, gain_coefficient, nframes, n);
distribute_one (inbufs.get_audio (n), obufs, gain_coefficient, nframes, n); memcpy (signal->outputs, signal->desired_outputs, sizeof (signal->outputs));
}
memcpy (signal->outputs, signal->desired_outputs, sizeof (signal->outputs));
}
} }
void void
VBAPanner::distribute_one (AudioBuffer& srcbuf, BufferSet& obufs, gain_t gain_coefficient, pframes_t nframes, uint32_t which) VBAPanner::distribute_one (AudioBuffer& srcbuf, BufferSet& obufs, gain_t gain_coefficient, pframes_t nframes, uint32_t which)
{ {
Sample* const src = srcbuf.data(); Sample* const src = srcbuf.data ();
Signal* signal (_signals[which]); Signal* signal (_signals[which]);
/* VBAP may distribute the signal across up to 3 speakers depending on /* VBAP may distribute the signal across up to 3 speakers depending on
the configuration of the speakers. * the configuration of the speakers.
*
* But the set of speakers in use "this time" may be different from
* the set of speakers "the last time". So we have up to 6 speakers
* involved, and we have to interpolate so that those no longer
* in use are rapidly faded to silence and those newly in use
* are rapidly faded to their correct level. This prevents clicks
* as we change the set of speakers used to put the signal in
* a given position.
*
* However, the speakers are represented by output buffers, and other
* speakers may write to the same buffers, so we cannot use
* anything here that will simply assign new (sample) values
* to the output buffers - everything must be done via mixing
* functions and not assignment/copying.
*/
But the set of speakers in use "this time" may be different from vector<double>::size_type sz = signal->gains.size ();
the set of speakers "the last time". So we have up to 6 speakers
involved, and we have to interpolate so that those no longer
in use are rapidly faded to silence and those newly in use
are rapidly faded to their correct level. This prevents clicks
as we change the set of speakers used to put the signal in
a given position.
However, the speakers are represented by output buffers, and other assert (sz == obufs.count ().n_audio ());
speakers may write to the same buffers, so we cannot use
anything here that will simply assign new (sample) values
to the output buffers - everything must be done via mixing
functions and not assignment/copying.
*/
vector<double>::size_type sz = signal->gains.size(); int8_t* outputs = (int8_t*)alloca (sz); // on the stack, no malloc
assert (sz == obufs.count().n_audio()); /* set initial state of each output "record"
int8_t *outputs = (int8_t*)alloca(sz); // on the stack, no malloc
/* set initial state of each output "record"
*/ */
for (uint32_t o = 0; o < sz; ++o) { for (uint32_t o = 0; o < sz; ++o) {
outputs[o] = 0; outputs[o] = 0;
}
/* for all outputs used this time and last time,
change the output record to show what has
happened.
*/
for (int o = 0; o < 3; ++o) {
if (signal->outputs[o] != -1) {
/* used last time */
outputs[signal->outputs[o]] |= 1;
}
if (signal->desired_outputs[o] != -1) {
/* used this time */
outputs[signal->desired_outputs[o]] |= 1<<1;
}
}
/* at this point, we can test a speaker's status:
(*outputs[o] & 1) <= in use before
(*outputs[o] & 2) <= in use this time
(*outputs[o] & 3) == 3 <= in use both times
*outputs[o] == 0 <= not in use either time
*/
for (int o = 0; o < 3; ++o) {
pan_t pan;
int output = signal->desired_outputs[o];
if (output == -1) {
continue;
}
pan = gain_coefficient * signal->desired_gains[o];
if (pan == 0.0 && signal->gains[output] == 0.0) {
/* nothing deing delivered to this output */
signal->gains[output] = 0.0;
} else if (fabs (pan - signal->gains[output]) > 0.00001) {
/* signal to this output but the gain coefficient has changed, so
interpolate between them.
*/
AudioBuffer& buf (obufs.get_audio (output));
buf.accumulate_with_ramped_gain_from (srcbuf.data(), nframes, signal->gains[output], pan, 0);
signal->gains[output] = pan;
} else {
/* signal to this output, same gain as before so just copy with gain
*/
mix_buffers_with_gain (obufs.get_audio (output).data(),src,nframes,pan);
signal->gains[output] = pan;
}
} }
/* clean up the outputs that were used last time but not this time /* for all outputs used this time and last time,
*/ * change the output record to show what has
* happened.
*/
for (uint32_t o = 0; o < sz; ++o) { for (int o = 0; o < 3; ++o) {
if (outputs[o] == 1) { if (signal->outputs[o] != -1) {
/* take signal and deliver with a rapid fade out /* used last time */
*/ outputs[signal->outputs[o]] |= 1;
AudioBuffer& buf (obufs.get_audio (o)); }
buf.accumulate_with_ramped_gain_from (srcbuf.data(), nframes, signal->gains[o], 0.0, 0);
signal->gains[o] = 0.0;
}
}
/* note that the output buffers were all silenced at some point if (signal->desired_outputs[o] != -1) {
so anything we didn't write to with this signal (or any others) /* used this time */
is just as it should be. outputs[signal->desired_outputs[o]] |= 1 << 1;
*/ }
}
/* at this point, we can test a speaker's status:
*
* (*outputs[o] & 1) <= in use before
* (*outputs[o] & 2) <= in use this time
* (*outputs[o] & 3) == 3 <= in use both times
* *outputs[o] == 0 <= not in use either time
*
*/
for (int o = 0; o < 3; ++o) {
pan_t pan;
int output = signal->desired_outputs[o];
if (output == -1) {
continue;
}
pan = gain_coefficient * signal->desired_gains[o];
if (pan == 0.0 && signal->gains[output] == 0.0) {
/* nothing deing delivered to this output */
signal->gains[output] = 0.0;
} else if (fabs (pan - signal->gains[output]) > 0.00001) {
/* signal to this output but the gain coefficient has changed, so
* interpolate between them.
*/
AudioBuffer& buf (obufs.get_audio (output));
buf.accumulate_with_ramped_gain_from (srcbuf.data (), nframes, signal->gains[output], pan, 0);
signal->gains[output] = pan;
} else {
/* signal to this output, same gain as before so just copy with gain */
mix_buffers_with_gain (obufs.get_audio (output).data (), src, nframes, pan);
signal->gains[output] = pan;
}
}
/* clean up the outputs that were used last time but not this time */
for (uint32_t o = 0; o < sz; ++o) {
if (outputs[o] == 1) {
/* take signal and deliver with a rapid fade out */
AudioBuffer& buf (obufs.get_audio (o));
buf.accumulate_with_ramped_gain_from (srcbuf.data (), nframes, signal->gains[o], 0.0, 0);
signal->gains[o] = 0.0;
}
}
/* note that the output buffers were all silenced at some point
* so anything we didn't write to with this signal (or any others)
* is just as it should be.
*/
} }
void void
VBAPanner::distribute_one_automated (AudioBuffer& /*src*/, BufferSet& /*obufs*/, VBAPanner::distribute_one_automated (AudioBuffer& /*src*/, BufferSet& /*obufs*/,
samplepos_t /*start*/, samplepos_t /*end*/, samplepos_t /*start*/, samplepos_t /*end*/,
pframes_t /*nframes*/, pan_t** /*buffers*/, uint32_t /*which*/) pframes_t /*nframes*/, pan_t** /*buffers*/, uint32_t /*which*/)
{ {
/* XXX to be implemented */ /* XXX to be implemented */
} }
@ -377,10 +367,10 @@ VBAPanner::distribute_one_automated (AudioBuffer& /*src*/, BufferSet& /*obufs*/,
XMLNode& XMLNode&
VBAPanner::get_state () VBAPanner::get_state ()
{ {
XMLNode& node (Panner::get_state()); XMLNode& node (Panner::get_state ());
node.set_property (X_("uri"), _descriptor.panner_uri); node.set_property (X_ ("uri"), _descriptor.panner_uri);
/* this is needed to allow new sessions to load with old Ardour: */ /* this is needed to allow new sessions to load with old Ardour: */
node.set_property (X_("type"), _descriptor.name); node.set_property (X_ ("type"), _descriptor.name);
return node; return node;
} }
@ -391,52 +381,51 @@ VBAPanner::factory (boost::shared_ptr<Pannable> p, boost::shared_ptr<Speakers> s
} }
ChanCount ChanCount
VBAPanner::in() const VBAPanner::in () const
{ {
return ChanCount (DataType::AUDIO, _signals.size()); return ChanCount (DataType::AUDIO, _signals.size ());
} }
ChanCount ChanCount
VBAPanner::out() const VBAPanner::out () const
{ {
return ChanCount (DataType::AUDIO, _speakers->n_speakers()); return ChanCount (DataType::AUDIO, _speakers->n_speakers ());
} }
string string
VBAPanner::value_as_string (boost::shared_ptr<const AutomationControl> ac) const VBAPanner::value_as_string (boost::shared_ptr<const AutomationControl> ac) const
{ {
/* DO NOT USE LocaleGuard HERE */ double val = ac->get_value ();
double val = ac->get_value();
switch (ac->parameter().type()) { switch (ac->parameter ().type ()) {
case PanAzimuthAutomation: /* direction */ case PanAzimuthAutomation: /* direction */
return string_compose (_("%1\u00B0"), (int (rint (val * 360.0))+180)%360); return string_compose (_ ("%1\u00B0"), (int(rint (val * 360.0)) + 180) % 360);
case PanWidthAutomation: /* diffusion */ case PanWidthAutomation: /* diffusion */
return string_compose (_("%1%%"), (int) floor (100.0 * fabs(val))); return string_compose (_ ("%1%%"), (int)floor (100.0 * fabs (val)));
case PanElevationAutomation: /* elevation */ case PanElevationAutomation: /* elevation */
return string_compose (_("%1\u00B0"), (int) floor (90.0 * fabs(val))); return string_compose (_ ("%1\u00B0"), (int)floor (90.0 * fabs (val)));
default: default:
return _("unused"); return _ ("unused");
} }
} }
AngularVector AngularVector
VBAPanner::signal_position (uint32_t n) const VBAPanner::signal_position (uint32_t n) const
{ {
if (n < _signals.size()) { if (n < _signals.size ()) {
return _signals[n]->direction; return _signals[n]->direction;
} }
return AngularVector(); return AngularVector ();
} }
boost::shared_ptr<Speakers> boost::shared_ptr<Speakers>
VBAPanner::get_speakers () const VBAPanner::get_speakers () const
{ {
return _speakers->parent(); return _speakers->parent ();
} }
void void
@ -465,11 +454,11 @@ void
VBAPanner::reset () VBAPanner::reset ()
{ {
set_position (.5); set_position (.5);
if (_signals.size() > 1) { if (_signals.size () > 1) {
set_width (1.0 - (1.0 / (double)_signals.size())); set_width (1.0 - (1.0 / (double)_signals.size ()));
} else { } else {
set_width (1.0); set_width (1.0);
} }
set_elevation (0); set_elevation (0);
update (); update ();

View file

@ -20,8 +20,8 @@
#ifndef __libardour_vbap_h__ #ifndef __libardour_vbap_h__
#define __libardour_vbap_h__ #define __libardour_vbap_h__
#include <string>
#include <map> #include <map>
#include <string>
#include "pbd/cartesian.h" #include "pbd/cartesian.h"
@ -30,8 +30,8 @@
#include "vbap_speakers.h" #include "vbap_speakers.h"
namespace ARDOUR { namespace ARDOUR
{
class Speakers; class Speakers;
class Pannable; class Pannable;
@ -41,13 +41,13 @@ public:
VBAPanner (boost::shared_ptr<Pannable>, boost::shared_ptr<Speakers>); VBAPanner (boost::shared_ptr<Pannable>, boost::shared_ptr<Speakers>);
~VBAPanner (); ~VBAPanner ();
void configure_io (ChanCount in, ChanCount /* ignored - we use Speakers */); void configure_io (ChanCount in, ChanCount /* ignored - we use Speakers */);
ChanCount in() const; ChanCount in () const;
ChanCount out() const; ChanCount out () const;
void set_position (double); void set_position (double);
void set_width (double); void set_width (double);
void set_elevation (double); void set_elevation (double);
static Panner* factory (boost::shared_ptr<Pannable>, boost::shared_ptr<Speakers>); static Panner* factory (boost::shared_ptr<Pannable>, boost::shared_ptr<Speakers>);
@ -55,41 +55,41 @@ public:
void set_azimuth_elevation (double azimuth, double elevation); void set_azimuth_elevation (double azimuth, double elevation);
std::string value_as_string (boost::shared_ptr<const AutomationControl>) const; std::string value_as_string (boost::shared_ptr<const AutomationControl>) const;
XMLNode& get_state (); XMLNode& get_state ();
PBD::AngularVector signal_position (uint32_t n) const; PBD::AngularVector signal_position (uint32_t n) const;
boost::shared_ptr<Speakers> get_speakers() const; boost::shared_ptr<Speakers> get_speakers () const;
void reset (); void reset ();
private: private:
struct Signal { struct Signal {
PBD::AngularVector direction; PBD::AngularVector direction;
std::vector<double> gains; /* most recently used gain for all speakers */ std::vector<double> gains; /* most recently used gain for all speakers */
int outputs[3]; /* most recent set of outputs used (2 or 3, depending on dimension) */ int outputs[3]; /* most recent set of outputs used (2 or 3, depending on dimension) */
int desired_outputs[3]; /* outputs to use the next time we distribute */ int desired_outputs[3]; /* outputs to use the next time we distribute */
double desired_gains[3]; /* target gains for desired_outputs */ double desired_gains[3]; /* target gains for desired_outputs */
Signal (VBAPanner&, uint32_t which, uint32_t n_speakers); Signal (VBAPanner&, uint32_t which, uint32_t n_speakers);
void resize_gains (uint32_t n_speakers); void resize_gains (uint32_t n_speakers);
}; };
std::vector<Signal*> _signals; std::vector<Signal*> _signals;
boost::shared_ptr<VBAPSpeakers> _speakers; boost::shared_ptr<VBAPSpeakers> _speakers;
void compute_gains (double g[3], int ls[3], int azi, int ele); void compute_gains (double g[3], int ls[3], int azi, int ele);
void update (); void update ();
void clear_signals (); void clear_signals ();
void distribute_one (AudioBuffer& src, BufferSet& obufs, gain_t gain_coeff, pframes_t nframes, uint32_t which); void distribute_one (AudioBuffer& src, BufferSet& obufs, gain_t gain_coeff, pframes_t nframes, uint32_t which);
void distribute_one_automated (AudioBuffer& src, BufferSet& obufs, void distribute_one_automated (AudioBuffer& src, BufferSet& obufs,
samplepos_t start, samplepos_t end, pframes_t nframes, samplepos_t start, samplepos_t end, pframes_t nframes,
pan_t** buffers, uint32_t which); pan_t** buffers, uint32_t which);
}; };
} /* namespace */ } // namespace ARDOUR
#endif /* __libardour_vbap_h__ */ #endif /* __libardour_vbap_h__ */

View file

@ -31,8 +31,8 @@
of the software. of the software.
*/ */
#include <cmath>
#include <algorithm> #include <algorithm>
#include <cmath>
#include <stdlib.h> #include <stdlib.h>
#include "pbd/cartesian.h" #include "pbd/cartesian.h"
@ -47,10 +47,10 @@ const double VBAPSpeakers::MIN_VOL_P_SIDE_LGTH = 0.01;
VBAPSpeakers::VBAPSpeakers (boost::shared_ptr<Speakers> s) VBAPSpeakers::VBAPSpeakers (boost::shared_ptr<Speakers> s)
: _dimension (2) : _dimension (2)
, _parent (s) , _parent (s)
{ {
_parent->Changed.connect_same_thread (speaker_connection, boost::bind (&VBAPSpeakers::update, this)); _parent->Changed.connect_same_thread (speaker_connection, boost::bind (&VBAPSpeakers::update, this));
update (); update ();
} }
VBAPSpeakers::~VBAPSpeakers () VBAPSpeakers::~VBAPSpeakers ()
@ -62,10 +62,10 @@ VBAPSpeakers::update ()
{ {
int dim = 2; int dim = 2;
_speakers = _parent->speakers(); _speakers = _parent->speakers ();
for (vector<Speaker>::const_iterator i = _speakers.begin(); i != _speakers.end(); ++i) { for (vector<Speaker>::const_iterator i = _speakers.begin (); i != _speakers.end (); ++i) {
if ((*i).angles().ele != 0.0) { if ((*i).angles ().ele != 0.0) {
dim = 3; dim = 3;
break; break;
} }
@ -73,13 +73,13 @@ VBAPSpeakers::update ()
_dimension = dim; _dimension = dim;
if (_speakers.size() < 2) { if (_speakers.size () < 2) {
/* nothing to be done with less than two speakers */ /* nothing to be done with less than two speakers */
return; return;
} }
if (_dimension == 3) { if (_dimension == 3) {
ls_triplet_chain *ls_triplets = 0; ls_triplet_chain* ls_triplets = 0;
choose_speaker_triplets (&ls_triplets); choose_speaker_triplets (&ls_triplets);
if (ls_triplets) { if (ls_triplets) {
calculate_3x3_matrixes (ls_triplets); calculate_3x3_matrixes (ls_triplets);
@ -91,16 +91,16 @@ VBAPSpeakers::update ()
} }
void void
VBAPSpeakers::choose_speaker_triplets(struct ls_triplet_chain **ls_triplets) VBAPSpeakers::choose_speaker_triplets (struct ls_triplet_chain** ls_triplets)
{ {
/* Selects the loudspeaker triplets, and /* Selects the loudspeaker triplets, and
calculates the inversion matrices for each selected triplet. * calculates the inversion matrices for each selected triplet.
A line (connection) is drawn between each loudspeaker. The lines * A line (connection) is drawn between each loudspeaker. The lines
denote the sides of the triangles. The triangles should not be * denote the sides of the triangles. The triangles should not be
intersecting. All crossing connections are searched and the * intersecting. All crossing connections are searched and the
longer connection is erased. This yields non-intesecting triangles, * longer connection is erased. This yields non-intesecting triangles,
which can be used in panning. * which can be used in panning.
*/ */
#if 0 // DEVEL/DEBUG #if 0 // DEVEL/DEBUG
for (vector<Speaker>::iterator i = _speakers.begin(); i != _speakers.end(); ++i) { for (vector<Speaker>::iterator i = _speakers.begin(); i != _speakers.end(); ++i) {
@ -113,23 +113,24 @@ VBAPSpeakers::choose_speaker_triplets(struct ls_triplet_chain **ls_triplets)
} }
#endif #endif
int i,j,k,l,table_size; int i, j, k, l, table_size;
int n_speakers = _speakers.size (); int n_speakers = _speakers.size ();
if (n_speakers < 3) { if (n_speakers < 3) {
fprintf(stderr, "VBAP: at least 3 speakers need to be defined."); fprintf (stderr, "VBAP: at least 3 speakers need to be defined.");
return; return;
} }
/* variable length arrays arrived in C99, became optional in C11, and /* variable length arrays arrived in C99, became optional in C11, and
are only planned for C++14. Use alloca which is functionally * are only planned for C++14. Use alloca which is functionally
identical (but uglier to read). * identical (but uglier to read).
*/ */
int* connections = (int*) alloca (sizeof (int) * n_speakers * n_speakers); int* connections = (int*)alloca (sizeof (int) * n_speakers * n_speakers);
float* distance_table = (float *) alloca (sizeof (float) * ((n_speakers * (n_speakers - 1)) / 2)); float* distance_table = (float*)alloca (sizeof (float) * ((n_speakers * (n_speakers - 1)) / 2));
int* distance_table_i = (int *) alloca (sizeof (int) * ((n_speakers * (n_speakers - 1)) / 2)); int* distance_table_i = (int*)alloca (sizeof (int) * ((n_speakers * (n_speakers - 1)) / 2));
int* distance_table_j = (int *) alloca (sizeof (int) * ((n_speakers * (n_speakers - 1)) / 2)); int* distance_table_j = (int*)alloca (sizeof (int) * ((n_speakers * (n_speakers - 1)) / 2));
float distance; float distance;
struct ls_triplet_chain *trip_ptr, *prev, *tmp_ptr; struct ls_triplet_chain *trip_ptr, *prev, *tmp_ptr;
for (i = 0; i < n_speakers * n_speakers; i++) { for (i = 0; i < n_speakers * n_speakers; i++) {
@ -137,41 +138,41 @@ VBAPSpeakers::choose_speaker_triplets(struct ls_triplet_chain **ls_triplets)
} }
for (i = 0; i < n_speakers; i++) { for (i = 0; i < n_speakers; i++) {
for (j = i+1; j < n_speakers; j++) { for (j = i + 1; j < n_speakers; j++) {
for(k = j+1; k < n_speakers; k++) { for (k = j + 1; k < n_speakers; k++) {
if (vol_p_side_lgth(i, j, k, _speakers) > MIN_VOL_P_SIDE_LGTH) { if (vol_p_side_lgth (i, j, k, _speakers) > MIN_VOL_P_SIDE_LGTH) {
connections[(i*n_speakers)+j]=1; connections[(i * n_speakers) + j] = 1;
connections[(j*n_speakers)+i]=1; connections[(j * n_speakers) + i] = 1;
connections[(i*n_speakers)+k]=1; connections[(i * n_speakers) + k] = 1;
connections[(k*n_speakers)+i]=1; connections[(k * n_speakers) + i] = 1;
connections[(j*n_speakers)+k]=1; connections[(j * n_speakers) + k] = 1;
connections[(k*n_speakers)+j]=1; connections[(k * n_speakers) + j] = 1;
add_ldsp_triplet(i,j,k,ls_triplets); add_ldsp_triplet (i, j, k, ls_triplets);
} }
} }
} }
} }
/*calculate distancies between all speakers and sorting them*/ /*calculate distancies between all speakers and sorting them*/
table_size =(((n_speakers - 1) * (n_speakers)) / 2); table_size = (((n_speakers - 1) * (n_speakers)) / 2);
for (i = 0; i < table_size; i++) { for (i = 0; i < table_size; i++) {
distance_table[i] = 100000.0; distance_table[i] = 100000.0;
} }
for (i = 0;i < n_speakers; i++) { for (i = 0; i < n_speakers; i++) {
for (j = i+1; j < n_speakers; j++) { for (j = i + 1; j < n_speakers; j++) {
if (connections[(i*n_speakers)+j] == 1) { if (connections[(i * n_speakers) + j] == 1) {
distance = fabs(vec_angle(_speakers[i].coords(),_speakers[j].coords())); distance = fabs (vec_angle (_speakers[i].coords (), _speakers[j].coords ()));
k=0; k = 0;
while(distance_table[k] < distance) { while (distance_table[k] < distance) {
k++; k++;
} }
for (l = table_size - 1; l > k ; l--) { for (l = table_size - 1; l > k; l--) {
distance_table[l] = distance_table[l-1]; distance_table[l] = distance_table[l - 1];
distance_table_i[l] = distance_table_i[l-1]; distance_table_i[l] = distance_table_i[l - 1];
distance_table_j[l] = distance_table_j[l-1]; distance_table_j[l] = distance_table_j[l - 1];
} }
distance_table[k] = distance; distance_table[k] = distance;
distance_table_i[k] = i; distance_table_i[k] = i;
distance_table_j[k] = j; distance_table_j[k] = j;
} else } else
@ -180,18 +181,18 @@ VBAPSpeakers::choose_speaker_triplets(struct ls_triplet_chain **ls_triplets)
} }
/* disconnecting connections which are crossing shorter ones, /* disconnecting connections which are crossing shorter ones,
starting from shortest one and removing all that cross it, * starting from shortest one and removing all that cross it,
and proceeding to next shortest */ * and proceeding to next shortest */
for (i = 0; i < table_size; i++) { for (i = 0; i < table_size; i++) {
int fst_ls = distance_table_i[i]; int fst_ls = distance_table_i[i];
int sec_ls = distance_table_j[i]; int sec_ls = distance_table_j[i];
if (connections[(fst_ls*n_speakers)+sec_ls] == 1) { if (connections[(fst_ls * n_speakers) + sec_ls] == 1) {
for (j = 0; j < n_speakers; j++) { for (j = 0; j < n_speakers; j++) {
for (k = j+1; k < n_speakers; k++) { for (k = j + 1; k < n_speakers; k++) {
if ((j != fst_ls) && (k != sec_ls) && (k != fst_ls) && (j != sec_ls)) { if ((j != fst_ls) && (k != sec_ls) && (k != fst_ls) && (j != sec_ls)) {
if (lines_intersect(fst_ls, sec_ls, j, k) == 1){ if (lines_intersect (fst_ls, sec_ls, j, k) == 1) {
connections[(j*n_speakers)+k] = 0; connections[(j * n_speakers) + k] = 0;
connections[(k*n_speakers)+j] = 0; connections[(k * n_speakers) + j] = 0;
} }
} }
} }
@ -200,59 +201,58 @@ VBAPSpeakers::choose_speaker_triplets(struct ls_triplet_chain **ls_triplets)
} }
/* remove triangles which had crossing sides /* remove triangles which had crossing sides
with smaller triangles or include loudspeakers*/ * with smaller triangles or include loudspeakers*/
trip_ptr = *ls_triplets; trip_ptr = *ls_triplets;
prev = 0; prev = 0;
while (trip_ptr != 0){ while (trip_ptr != 0) {
i = trip_ptr->ls_nos[0]; i = trip_ptr->ls_nos[0];
j = trip_ptr->ls_nos[1]; j = trip_ptr->ls_nos[1];
k = trip_ptr->ls_nos[2]; k = trip_ptr->ls_nos[2];
if (connections[(i*n_speakers)+j] == 0 || if (connections[(i * n_speakers) + j] == 0 ||
connections[(i*n_speakers)+k] == 0 || connections[(i * n_speakers) + k] == 0 ||
connections[(j*n_speakers)+k] == 0 || connections[(j * n_speakers) + k] == 0 ||
any_ls_inside_triplet(i,j,k) == 1 ){ any_ls_inside_triplet (i, j, k) == 1) {
if (prev != 0) { if (prev != 0) {
prev->next = trip_ptr->next; prev->next = trip_ptr->next;
tmp_ptr = trip_ptr; tmp_ptr = trip_ptr;
trip_ptr = trip_ptr->next; trip_ptr = trip_ptr->next;
free(tmp_ptr); free (tmp_ptr);
} else { } else {
*ls_triplets = trip_ptr->next; *ls_triplets = trip_ptr->next;
tmp_ptr = trip_ptr; tmp_ptr = trip_ptr;
trip_ptr = trip_ptr->next; trip_ptr = trip_ptr->next;
free(tmp_ptr); free (tmp_ptr);
} }
} else { } else {
prev = trip_ptr; prev = trip_ptr;
trip_ptr = trip_ptr->next; trip_ptr = trip_ptr->next;
} }
} }
} }
int int
VBAPSpeakers::any_ls_inside_triplet(int a, int b, int c) VBAPSpeakers::any_ls_inside_triplet (int a, int b, int c)
{ {
/* returns 1 if there is loudspeaker(s) inside given ls triplet */ /* returns 1 if there is loudspeaker(s) inside given ls triplet */
float invdet; float invdet;
const CartesianVector* lp1; const CartesianVector* lp1;
const CartesianVector* lp2; const CartesianVector* lp2;
const CartesianVector* lp3; const CartesianVector* lp3;
float invmx[9]; float invmx[9];
int i,j; int i, j;
float tmp; float tmp;
bool any_ls_inside; bool any_ls_inside;
bool this_inside; bool this_inside;
int n_speakers = _speakers.size(); int n_speakers = _speakers.size ();
lp1 = &(_speakers[a].coords()); lp1 = &(_speakers[a].coords ());
lp2 = &(_speakers[b].coords()); lp2 = &(_speakers[b].coords ());
lp3 = &(_speakers[c].coords()); lp3 = &(_speakers[c].coords ());
/* matrix inversion */ /* matrix inversion */
invdet = 1.0 / ( lp1->x * ((lp2->y * lp3->z) - (lp2->z * lp3->y)) invdet = 1.0 / ( lp1->x * ((lp2->y * lp3->z) - (lp2->z * lp3->y))
- lp1->y * ((lp2->x * lp3->z) - (lp2->z * lp3->x)) - lp1->y * ((lp2->x * lp3->z) - (lp2->z * lp3->x))
+ lp1->z * ((lp2->x * lp3->y) - (lp2->y * lp3->x))); + lp1->z * ((lp2->x * lp3->y) - (lp2->y * lp3->x)));
invmx[0] = ((lp2->y * lp3->z) - (lp2->z * lp3->y)) * invdet; invmx[0] = ((lp2->y * lp3->z) - (lp2->z * lp3->y)) * invdet;
invmx[3] = ((lp1->y * lp3->z) - (lp1->z * lp3->y)) * -invdet; invmx[3] = ((lp1->y * lp3->z) - (lp1->z * lp3->y)) * -invdet;
@ -266,12 +266,12 @@ VBAPSpeakers::any_ls_inside_triplet(int a, int b, int c)
any_ls_inside = false; any_ls_inside = false;
for (i = 0; i < n_speakers; i++) { for (i = 0; i < n_speakers; i++) {
if (i != a && i!=b && i != c) { if (i != a && i != b && i != c) {
this_inside = true; this_inside = true;
for (j = 0; j < 3; j++) { for (j = 0; j < 3; j++) {
tmp = _speakers[i].coords().x * invmx[0 + j*3]; tmp = _speakers[i].coords ().x * invmx[0 + j * 3];
tmp += _speakers[i].coords().y * invmx[1 + j*3]; tmp += _speakers[i].coords ().y * invmx[1 + j * 3];
tmp += _speakers[i].coords().z * invmx[2 + j*3]; tmp += _speakers[i].coords ().z * invmx[2 + j * 3];
if (tmp < -0.001) { if (tmp < -0.001) {
this_inside = false; this_inside = false;
} }
@ -285,22 +285,21 @@ VBAPSpeakers::any_ls_inside_triplet(int a, int b, int c)
return any_ls_inside; return any_ls_inside;
} }
void void
VBAPSpeakers::add_ldsp_triplet(int i, int j, int k, struct ls_triplet_chain **ls_triplets) VBAPSpeakers::add_ldsp_triplet (int i, int j, int k, struct ls_triplet_chain** ls_triplets)
{ {
/* adds i,j,k triplet to triplet chain*/ /* adds i,j,k triplet to triplet chain*/
struct ls_triplet_chain *trip_ptr, *prev; struct ls_triplet_chain *trip_ptr, *prev;
trip_ptr = *ls_triplets; trip_ptr = *ls_triplets;
prev = 0; prev = 0;
while (trip_ptr != 0){ while (trip_ptr != 0) {
prev = trip_ptr; prev = trip_ptr;
trip_ptr = trip_ptr->next; trip_ptr = trip_ptr->next;
} }
trip_ptr = (struct ls_triplet_chain*) malloc (sizeof (struct ls_triplet_chain)); trip_ptr = (struct ls_triplet_chain*)malloc (sizeof (struct ls_triplet_chain));
if (prev == 0) { if (prev == 0) {
*ls_triplets = trip_ptr; *ls_triplets = trip_ptr;
@ -308,17 +307,17 @@ VBAPSpeakers::add_ldsp_triplet(int i, int j, int k, struct ls_triplet_chain **ls
prev->next = trip_ptr; prev->next = trip_ptr;
} }
trip_ptr->next = 0; trip_ptr->next = 0;
trip_ptr->ls_nos[0] = i; trip_ptr->ls_nos[0] = i;
trip_ptr->ls_nos[1] = j; trip_ptr->ls_nos[1] = j;
trip_ptr->ls_nos[2] = k; trip_ptr->ls_nos[2] = k;
} }
double double
VBAPSpeakers::vec_angle(CartesianVector v1, CartesianVector v2) VBAPSpeakers::vec_angle (CartesianVector v1, CartesianVector v2)
{ {
double inner= ((v1.x*v2.x + v1.y*v2.y + v1.z*v2.z)/ double inner = ((v1.x * v2.x + v1.y * v2.y + v1.z * v2.z) /
(vec_length(v1) * vec_length(v2))); (vec_length (v1) * vec_length (v2)));
if (inner > 1.0) { if (inner > 1.0) {
inner = 1.0; inner = 1.0;
@ -328,37 +327,36 @@ VBAPSpeakers::vec_angle(CartesianVector v1, CartesianVector v2)
inner = -1.0; inner = -1.0;
} }
return fabs(acos(inner)); return fabs (acos (inner));
} }
double double
VBAPSpeakers::vec_length(CartesianVector v1) VBAPSpeakers::vec_length (CartesianVector v1)
{ {
double rv = sqrt(v1.x*v1.x + v1.y*v1.y + v1.z*v1.z); double rv = sqrt (v1.x * v1.x + v1.y * v1.y + v1.z * v1.z);
if (rv > 1e-14) return rv; if (rv > 1e-14)
return rv;
return 0; return 0;
} }
double double
VBAPSpeakers::vec_prod(CartesianVector v1, CartesianVector v2) VBAPSpeakers::vec_prod (CartesianVector v1, CartesianVector v2)
{ {
return (v1.x*v2.x + v1.y*v2.y + v1.z*v2.z); return (v1.x * v2.x + v1.y * v2.y + v1.z * v2.z);
} }
double double
VBAPSpeakers::vol_p_side_lgth(int i, int j, int k, const vector<Speaker>& speakers) VBAPSpeakers::vol_p_side_lgth (int i, int j, int k, const vector<Speaker>& speakers)
{ {
/* calculate volume of the parallelepiped defined by the loudspeaker /* calculate volume of the parallelepiped defined by the loudspeaker
direction vectors and divide it with total length of the triangle sides. * direction vectors and divide it with total length of the triangle sides.
This is used when removing too narrow triangles. */ * This is used when removing too narrow triangles. */
double volper, lgth; double volper, lgth;
CartesianVector xprod; CartesianVector xprod;
cross_prod (speakers[i].coords(), speakers[j].coords(), &xprod); cross_prod (speakers[i].coords (), speakers[j].coords (), &xprod);
volper = fabs (vec_prod(xprod, speakers[k].coords())); volper = fabs (vec_prod (xprod, speakers[k].coords ()));
lgth = ( fabs (vec_angle(speakers[i].coords(), speakers[j].coords())) lgth = (fabs (vec_angle (speakers[i].coords (), speakers[j].coords ())) + fabs (vec_angle (speakers[i].coords (), speakers[k].coords ())) + fabs (vec_angle (speakers[j].coords (), speakers[k].coords ())));
+ fabs (vec_angle(speakers[i].coords(), speakers[k].coords()))
+ fabs (vec_angle(speakers[j].coords(), speakers[k].coords())));
if (lgth > 0.00001) { if (lgth > 0.00001) {
return volper / lgth; return volper / lgth;
} else { } else {
@ -367,7 +365,7 @@ VBAPSpeakers::vol_p_side_lgth(int i, int j, int k, const vector<Speaker>& speake
} }
void void
VBAPSpeakers::cross_prod(CartesianVector v1,CartesianVector v2, CartesianVector *res) VBAPSpeakers::cross_prod (CartesianVector v1, CartesianVector v2, CartesianVector* res)
{ {
double length; double length;
@ -375,7 +373,7 @@ VBAPSpeakers::cross_prod(CartesianVector v1,CartesianVector v2, CartesianVector
res->y = (v1.z * v2.x) - (v1.x * v2.z); res->y = (v1.z * v2.x) - (v1.x * v2.z);
res->z = (v1.x * v2.y) - (v1.y * v2.x); res->z = (v1.x * v2.y) - (v1.y * v2.x);
length = vec_length(*res); length = vec_length (*res);
if (length > 0) { if (length > 0) {
res->x /= length; res->x /= length;
res->y /= length; res->y /= length;
@ -391,51 +389,52 @@ int
VBAPSpeakers::lines_intersect (int i, int j, int k, int l) VBAPSpeakers::lines_intersect (int i, int j, int k, int l)
{ {
/* checks if two lines intersect on 3D sphere /* checks if two lines intersect on 3D sphere
see theory in paper Pulkki, V. Lokki, T. "Creating Auditory Displays * see theory in paper Pulkki, V. Lokki, T. "Creating Auditory Displays
with Multiple Loudspeakers Using VBAP: A Case Study with * with Multiple Loudspeakers Using VBAP: A Case Study with
DIVA Project" in International Conference on * DIVA Project" in International Conference on
Auditory Displays -98. E-mail Ville.Pulkki@hut.fi * Auditory Displays -98. E-mail Ville.Pulkki@hut.fi
if you want to have that paper. * if you want to have that paper.
*/ */
CartesianVector v1; CartesianVector v1;
CartesianVector v2; CartesianVector v2;
CartesianVector v3, neg_v3; 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); float dist_ij, dist_kl, dist_iv3, dist_jv3, dist_inv3, dist_jnv3;
cross_prod(_speakers[k].coords(),_speakers[l].coords(),&v2); float dist_kv3, dist_lv3, dist_knv3, dist_lnv3;
cross_prod(v1,v2,&v3);
neg_v3.x= 0.0 - v3.x; cross_prod (_speakers[i].coords (), _speakers[j].coords (), &v1);
neg_v3.y= 0.0 - v3.y; cross_prod (_speakers[k].coords (), _speakers[l].coords (), &v2);
neg_v3.z= 0.0 - v3.z; cross_prod (v1, v2, &v3);
dist_ij = (vec_angle(_speakers[i].coords(),_speakers[j].coords())); neg_v3.x = 0.0 - v3.x;
dist_kl = (vec_angle(_speakers[k].coords(),_speakers[l].coords())); neg_v3.y = 0.0 - v3.y;
dist_iv3 = (vec_angle(_speakers[i].coords(),v3)); neg_v3.z = 0.0 - v3.z;
dist_jv3 = (vec_angle(v3,_speakers[j].coords()));
dist_inv3 = (vec_angle(_speakers[i].coords(),neg_v3)); dist_ij = (vec_angle (_speakers[i].coords (), _speakers[j].coords ()));
dist_jnv3 = (vec_angle(neg_v3,_speakers[j].coords())); dist_kl = (vec_angle (_speakers[k].coords (), _speakers[l].coords ()));
dist_kv3 = (vec_angle(_speakers[k].coords(),v3)); dist_iv3 = (vec_angle (_speakers[i].coords (), v3));
dist_lv3 = (vec_angle(v3,_speakers[l].coords())); dist_jv3 = (vec_angle (v3, _speakers[j].coords ()));
dist_knv3 = (vec_angle(_speakers[k].coords(),neg_v3)); dist_inv3 = (vec_angle (_speakers[i].coords (), neg_v3));
dist_lnv3 = (vec_angle(neg_v3,_speakers[l].coords())); dist_jnv3 = (vec_angle (neg_v3, _speakers[j].coords ()));
dist_kv3 = (vec_angle (_speakers[k].coords (), v3));
dist_lv3 = (vec_angle (v3, _speakers[l].coords ()));
dist_knv3 = (vec_angle (_speakers[k].coords (), neg_v3));
dist_lnv3 = (vec_angle (neg_v3, _speakers[l].coords ()));
/* if one of loudspeakers is close to crossing point, don't do anything*/ /* if one of loudspeakers is close to crossing point, don't do anything*/
if(fabsf(dist_iv3) <= 0.01 || fabsf(dist_jv3) <= 0.01 || if (fabsf (dist_iv3) <= 0.01 || fabsf (dist_jv3) <= 0.01 ||
fabsf(dist_kv3) <= 0.01 || fabsf(dist_lv3) <= 0.01 || fabsf (dist_kv3) <= 0.01 || fabsf (dist_lv3) <= 0.01 ||
fabsf(dist_inv3) <= 0.01 || fabsf(dist_jnv3) <= 0.01 || fabsf (dist_inv3) <= 0.01 || fabsf (dist_jnv3) <= 0.01 ||
fabsf(dist_knv3) <= 0.01 || fabsf(dist_lnv3) <= 0.01 ) { fabsf (dist_knv3) <= 0.01 || fabsf (dist_lnv3) <= 0.01) {
return(0); return (0);
} }
/* if crossing point is on line between both loudspeakers return 1 */ /* if crossing point is on line between both loudspeakers return 1 */
if (((fabsf(dist_ij - (dist_iv3 + dist_jv3)) <= 0.01 ) && if (((fabsf (dist_ij - (dist_iv3 + dist_jv3)) <= 0.01) &&
(fabsf(dist_kl - (dist_kv3 + dist_lv3)) <= 0.01)) || (fabsf (dist_kl - (dist_kv3 + dist_lv3)) <= 0.01)) ||
((fabsf(dist_ij - (dist_inv3 + dist_jnv3)) <= 0.01) && ((fabsf (dist_ij - (dist_inv3 + dist_jnv3)) <= 0.01) &&
(fabsf(dist_kl - (dist_knv3 + dist_lnv3)) <= 0.01 ))) { (fabsf (dist_kl - (dist_knv3 + dist_lnv3)) <= 0.01))) {
return (1); return (1);
} else { } else {
return (0); return (0);
@ -443,17 +442,17 @@ VBAPSpeakers::lines_intersect (int i, int j, int k, int l)
} }
void void
VBAPSpeakers::calculate_3x3_matrixes(struct ls_triplet_chain *ls_triplets) VBAPSpeakers::calculate_3x3_matrixes (struct ls_triplet_chain* ls_triplets)
{ {
/* Calculates the inverse matrices for 3D */ /* Calculates the inverse matrices for 3D */
float invdet; float invdet;
const CartesianVector* lp1; const CartesianVector* lp1;
const CartesianVector* lp2; const CartesianVector* lp2;
const CartesianVector* lp3; const CartesianVector* lp3;
float *invmx; float* invmx;
struct ls_triplet_chain *tr_ptr = ls_triplets; struct ls_triplet_chain* tr_ptr = ls_triplets;
int triplet_count = 0; int triplet_count = 0;
int triplet; int triplet;
assert (tr_ptr); assert (tr_ptr);
@ -474,21 +473,21 @@ VBAPSpeakers::calculate_3x3_matrixes(struct ls_triplet_chain *ls_triplets)
_speaker_tuples.clear (); _speaker_tuples.clear ();
for (int n = 0; n < triplet_count; ++n) { for (int n = 0; n < triplet_count; ++n) {
_matrices.push_back (threeDmatrix()); _matrices.push_back (threeDmatrix ());
_speaker_tuples.push_back (tmatrix()); _speaker_tuples.push_back (tmatrix ());
} }
tr_ptr = ls_triplets; tr_ptr = ls_triplets;
while (tr_ptr != 0) { while (tr_ptr != 0) {
lp1 = &(_speakers[tr_ptr->ls_nos[0]].coords()); lp1 = &(_speakers[tr_ptr->ls_nos[0]].coords ());
lp2 = &(_speakers[tr_ptr->ls_nos[1]].coords()); lp2 = &(_speakers[tr_ptr->ls_nos[1]].coords ());
lp3 = &(_speakers[tr_ptr->ls_nos[2]].coords()); lp3 = &(_speakers[tr_ptr->ls_nos[2]].coords ());
/* matrix inversion */ /* matrix inversion */
invmx = tr_ptr->inv_mx; invmx = tr_ptr->inv_mx;
invdet = 1.0 / ( lp1->x * ((lp2->y * lp3->z) - (lp2->z * lp3->y)) invdet = 1.0 / ( lp1->x * ((lp2->y * lp3->z) - (lp2->z * lp3->y))
- lp1->y * ((lp2->x * lp3->z) - (lp2->z * lp3->x)) - lp1->y * ((lp2->x * lp3->z) - (lp2->z * lp3->x))
+ lp1->z * ((lp2->x * lp3->y) - (lp2->y * lp3->x))); + lp1->z * ((lp2->x * lp3->y) - (lp2->y * lp3->x)));
invmx[0] = ((lp2->y * lp3->z) - (lp2->z * lp3->y)) * invdet; invmx[0] = ((lp2->y * lp3->z) - (lp2->z * lp3->y)) * invdet;
invmx[3] = ((lp1->y * lp3->z) - (lp1->z * lp3->y)) * -invdet; invmx[3] = ((lp1->y * lp3->z) - (lp1->z * lp3->y)) * -invdet;
@ -530,29 +529,29 @@ VBAPSpeakers::calculate_3x3_matrixes(struct ls_triplet_chain *ls_triplets)
} }
void void
VBAPSpeakers::choose_speaker_pairs (){ VBAPSpeakers::choose_speaker_pairs ()
{
/* selects the loudspeaker pairs, calculates the inversion /* selects the loudspeaker pairs, calculates the inversion
matrices and stores the data to a global array * matrices and stores the data to a global array
*/ */
const int n_speakers = _speakers.size(); const int n_speakers = _speakers.size ();
if (n_speakers < 2) { if (n_speakers < 2) {
fprintf(stderr, "VBAP: at least 2 speakers need to be defined."); fprintf (stderr, "VBAP: at least 2 speakers need to be defined.");
return; return;
} }
const double AZIMUTH_DELTA_THRESHOLD_DEGREES = (180.0/M_PI) * (M_PI - 0.175); const double AZIMUTH_DELTA_THRESHOLD_DEGREES = (180.0 / M_PI) * (M_PI - 0.175);
/* variable length arrays arrived in C99, became optional in C11, and /* variable length arrays arrived in C99, became optional in C11, and
are only planned for C++14. Use alloca which is functionally * are only planned for C++14. Use alloca which is functionally
identical (but uglier to read). * identical (but uglier to read).
*/ */
int* sorted_speakers = (int*) alloca (sizeof (int) * n_speakers); int* sorted_speakers = (int*)alloca (sizeof (int) * n_speakers);
bool* exists = (bool*) alloca (sizeof(bool) * n_speakers); bool* exists = (bool*)alloca (sizeof (bool) * n_speakers);
double* inverse_matrix = (double*) alloca (sizeof (double) * n_speakers * 4); double* inverse_matrix = (double*)alloca (sizeof (double) * n_speakers * 4);
int expected_pairs = 0; int expected_pairs = 0;
int pair; int pair;
int speaker; int speaker;
for (speaker = 0; speaker < n_speakers; ++speaker) { for (speaker = 0; speaker < n_speakers; ++speaker) {
exists[speaker] = false; exists[speaker] = false;
@ -562,30 +561,28 @@ VBAPSpeakers::choose_speaker_pairs (){
#ifdef __clang_analyzer__ #ifdef __clang_analyzer__
// sort_2D_lss() assigns values to all of sorted_speakers // sort_2D_lss() assigns values to all of sorted_speakers
// "uninitialized value" // "uninitialized value"
memset(sorted_speakers, 0, sizeof(*sorted_speakers)); memset (sorted_speakers, 0, sizeof (*sorted_speakers));
#endif #endif
sort_2D_lss (sorted_speakers); sort_2D_lss (sorted_speakers);
/* adjacent loudspeakers are the loudspeaker pairs to be used.*/ /* adjacent loudspeakers are the loudspeaker pairs to be used.*/
for (speaker = 0; speaker < n_speakers-1; speaker++) { for (speaker = 0; speaker < n_speakers - 1; speaker++) {
if ((_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) {
_speakers[sorted_speakers[speaker]].angles().azi) <= AZIMUTH_DELTA_THRESHOLD_DEGREES) { if (calc_2D_inv_tmatrix (_speakers[sorted_speakers[speaker]].angles ().azi,
if (calc_2D_inv_tmatrix( _speakers[sorted_speakers[speaker]].angles().azi, _speakers[sorted_speakers[speaker + 1]].angles ().azi,
_speakers[sorted_speakers[speaker+1]].angles().azi, &inverse_matrix[4 * speaker]) != 0) {
&inverse_matrix[4 * speaker]) != 0){
exists[speaker] = true; exists[speaker] = true;
expected_pairs++; expected_pairs++;
} }
} }
} }
if (((6.283 - _speakers[sorted_speakers[n_speakers-1]].angles().azi) if (((6.283 - _speakers[sorted_speakers[n_speakers - 1]].angles ().azi) + _speakers[sorted_speakers[0]].angles ().azi) <= AZIMUTH_DELTA_THRESHOLD_DEGREES) {
+_speakers[sorted_speakers[0]].angles().azi) <= AZIMUTH_DELTA_THRESHOLD_DEGREES) { if (calc_2D_inv_tmatrix (_speakers[sorted_speakers[n_speakers - 1]].angles ().azi,
if (calc_2D_inv_tmatrix(_speakers[sorted_speakers[n_speakers-1]].angles().azi, _speakers[sorted_speakers[0]].angles ().azi,
_speakers[sorted_speakers[0]].angles().azi, &inverse_matrix[4 * (n_speakers - 1)]) != 0) {
&inverse_matrix[4*(n_speakers-1)]) != 0) { exists[n_speakers - 1] = true;
exists[n_speakers-1] = true;
expected_pairs++; expected_pairs++;
} }
} }
@ -596,31 +593,31 @@ VBAPSpeakers::choose_speaker_pairs (){
_speaker_tuples.clear (); _speaker_tuples.clear ();
for (int n = 0; n < expected_pairs; ++n) { for (int n = 0; n < expected_pairs; ++n) {
_matrices.push_back (twoDmatrix()); _matrices.push_back (twoDmatrix ());
_speaker_tuples.push_back (tmatrix()); _speaker_tuples.push_back (tmatrix ());
} }
for (speaker = 0; speaker < n_speakers - 1; speaker++) { for (speaker = 0; speaker < n_speakers - 1; speaker++) {
if (exists[speaker]) { if (exists[speaker]) {
_matrices[pair][0] = inverse_matrix[(speaker*4)+0]; _matrices[pair][0] = inverse_matrix[(speaker * 4) + 0];
_matrices[pair][1] = inverse_matrix[(speaker*4)+1]; _matrices[pair][1] = inverse_matrix[(speaker * 4) + 1];
_matrices[pair][2] = inverse_matrix[(speaker*4)+2]; _matrices[pair][2] = inverse_matrix[(speaker * 4) + 2];
_matrices[pair][3] = inverse_matrix[(speaker*4)+3]; _matrices[pair][3] = inverse_matrix[(speaker * 4) + 3];
_speaker_tuples[pair][0] = sorted_speakers[speaker]; _speaker_tuples[pair][0] = sorted_speakers[speaker];
_speaker_tuples[pair][1] = sorted_speakers[speaker+1]; _speaker_tuples[pair][1] = sorted_speakers[speaker + 1];
pair++; pair++;
} }
} }
if (exists[n_speakers-1]) { if (exists[n_speakers - 1]) {
_matrices[pair][0] = inverse_matrix[(speaker*4)+0]; _matrices[pair][0] = inverse_matrix[(speaker * 4) + 0];
_matrices[pair][1] = inverse_matrix[(speaker*4)+1]; _matrices[pair][1] = inverse_matrix[(speaker * 4) + 1];
_matrices[pair][2] = inverse_matrix[(speaker*4)+2]; _matrices[pair][2] = inverse_matrix[(speaker * 4) + 2];
_matrices[pair][3] = inverse_matrix[(speaker*4)+3]; _matrices[pair][3] = inverse_matrix[(speaker * 4) + 3];
_speaker_tuples[pair][0] = sorted_speakers[n_speakers-1]; _speaker_tuples[pair][0] = sorted_speakers[n_speakers - 1];
_speaker_tuples[pair][1] = sorted_speakers[0]; _speaker_tuples[pair][1] = sorted_speakers[0];
} }
} }
@ -628,33 +625,32 @@ VBAPSpeakers::choose_speaker_pairs (){
void void
VBAPSpeakers::sort_2D_lss (int* sorted_speakers) VBAPSpeakers::sort_2D_lss (int* sorted_speakers)
{ {
vector<Speaker> tmp = _speakers; vector<Speaker> tmp = _speakers;
vector<Speaker>::iterator s; vector<Speaker>::iterator s;
azimuth_sorter sorter; azimuth_sorter sorter;
unsigned int n; unsigned int n;
sort (tmp.begin(), tmp.end(), sorter); sort (tmp.begin (), tmp.end (), sorter);
for (n = 0, s = tmp.begin(); s != tmp.end(); ++s, ++n) { for (n = 0, s = tmp.begin (); s != tmp.end (); ++s, ++n) {
sorted_speakers[n] = (*s).id; sorted_speakers[n] = (*s).id;
} }
assert(n == _speakers.size ()); assert (n == _speakers.size ());
} }
int int
VBAPSpeakers::calc_2D_inv_tmatrix (double azi1, double azi2, double* inverse_matrix) VBAPSpeakers::calc_2D_inv_tmatrix (double azi1, double azi2, double* inverse_matrix)
{ {
double x1,x2,x3,x4; double x1, x2, x3, x4;
double det; double det;
x1 = cos (azi1 * (M_PI/180.0)); x1 = cos (azi1 * (M_PI / 180.0));
x2 = sin (azi1 * (M_PI/180.0)); x2 = sin (azi1 * (M_PI / 180.0));
x3 = cos (azi2 * (M_PI/180.0)); x3 = cos (azi2 * (M_PI / 180.0));
x4 = sin (azi2 * (M_PI/180.0)); x4 = sin (azi2 * (M_PI / 180.0));
det = (x1 * x4) - ( x3 * x2 ); det = (x1 * x4) - (x3 * x2);
if (fabs(det) <= 0.001) {
if (fabs (det) <= 0.001) {
inverse_matrix[0] = 0.0; inverse_matrix[0] = 0.0;
inverse_matrix[1] = 0.0; inverse_matrix[1] = 0.0;
inverse_matrix[2] = 0.0; inverse_matrix[2] = 0.0;
@ -663,7 +659,6 @@ VBAPSpeakers::calc_2D_inv_tmatrix (double azi1, double azi2, double* inverse_mat
return 0; return 0;
} else { } else {
inverse_matrix[0] = x4 / det; inverse_matrix[0] = x4 / det;
inverse_matrix[1] = -x3 / det; inverse_matrix[1] = -x3 / det;
inverse_matrix[2] = -x2 / det; inverse_matrix[2] = -x2 / det;
@ -672,5 +667,3 @@ VBAPSpeakers::calc_2D_inv_tmatrix (double azi1, double azi2, double* inverse_mat
return 1; return 1;
} }
} }

View file

@ -29,79 +29,102 @@
#include "ardour/panner.h" #include "ardour/panner.h"
#include "ardour/speakers.h" #include "ardour/speakers.h"
namespace ARDOUR { namespace ARDOUR
{
class Speakers; class Speakers;
class VBAPSpeakers : public boost::noncopyable { class VBAPSpeakers : public boost::noncopyable
{
public: public:
VBAPSpeakers (boost::shared_ptr<Speakers>); VBAPSpeakers (boost::shared_ptr<Speakers>);
typedef std::vector<double> dvector; typedef std::vector<double> dvector;
const dvector matrix (int tuple) const { return _matrices[tuple]; }
int speaker_for_tuple (int tuple, int which) const { return _speaker_tuples[tuple][which]; }
int n_tuples () const { return _matrices.size(); } const dvector matrix (int tuple) const
int dimension() const { return _dimension; } {
return _matrices[tuple];
}
uint32_t n_speakers() const { return _speakers.size(); } int speaker_for_tuple (int tuple, int which) const
boost::shared_ptr<Speakers> parent() const { return _parent; } {
return _speaker_tuples[tuple][which];
}
int n_tuples () const
{
return _matrices.size ();
}
int dimension () const
{
return _dimension;
}
uint32_t n_speakers () const
{
return _speakers.size ();
}
boost::shared_ptr<Speakers> parent () const
{
return _parent;
}
~VBAPSpeakers (); ~VBAPSpeakers ();
private: private:
static const double MIN_VOL_P_SIDE_LGTH; static const double MIN_VOL_P_SIDE_LGTH;
int _dimension; int _dimension;
boost::shared_ptr<Speakers> _parent; boost::shared_ptr<Speakers> _parent;
std::vector<Speaker> _speakers; std::vector<Speaker> _speakers;
PBD::ScopedConnection speaker_connection; PBD::ScopedConnection speaker_connection;
struct azimuth_sorter { struct azimuth_sorter {
bool operator() (const Speaker& s1, const Speaker& s2) { bool operator() (const Speaker& s1, const Speaker& s2)
return s1.angles().azi < s2.angles().azi; {
return s1.angles ().azi < s2.angles ().azi;
} }
}; };
struct twoDmatrix : public dvector { struct twoDmatrix : public dvector {
twoDmatrix() : dvector (4, 0.0) {} twoDmatrix () : dvector (4, 0.0) { }
}; };
struct threeDmatrix : public dvector { struct threeDmatrix : public dvector {
threeDmatrix() : dvector (9, 0.0) {} threeDmatrix () : dvector (9, 0.0) { }
}; };
struct tmatrix : public dvector { struct tmatrix : public dvector {
tmatrix() : dvector (3, 0.0) {} tmatrix () : dvector (3, 0.0) { }
}; };
std::vector<dvector> _matrices; /* holds matrices for a given speaker combinations */ std::vector<dvector> _matrices; /* holds matrices for a given speaker combinations */
std::vector<tmatrix> _speaker_tuples; /* holds speakers IDs for a given combination */ std::vector<tmatrix> _speaker_tuples; /* holds speakers IDs for a given combination */
/* A struct for all loudspeakers */ /* A struct for all loudspeakers */
struct ls_triplet_chain { struct ls_triplet_chain {
int ls_nos[3]; int ls_nos[3];
float inv_mx[9]; float inv_mx[9];
struct ls_triplet_chain *next; struct ls_triplet_chain* next;
}; };
static double vec_angle(PBD::CartesianVector v1, PBD::CartesianVector v2); static double vec_angle (PBD::CartesianVector v1, PBD::CartesianVector v2);
static double vec_length(PBD::CartesianVector v1); static double vec_length (PBD::CartesianVector v1);
static double vec_prod(PBD::CartesianVector v1, PBD::CartesianVector v2); static double vec_prod (PBD::CartesianVector v1, PBD::CartesianVector v2);
static double vol_p_side_lgth(int i, int j,int k, const std::vector<Speaker>&); static double vol_p_side_lgth (int i, int j, int k, const std::vector<Speaker>&);
static void cross_prod(PBD::CartesianVector v1,PBD::CartesianVector v2, PBD::CartesianVector *res); static void cross_prod (PBD::CartesianVector v1, PBD::CartesianVector v2, PBD::CartesianVector* res);
void update (); void update ();
int any_ls_inside_triplet (int a, int b, int c); int any_ls_inside_triplet (int a, int b, int c);
void add_ldsp_triplet (int i, int j, int k, struct ls_triplet_chain **ls_triplets); void add_ldsp_triplet (int i, int j, int k, struct ls_triplet_chain** ls_triplets);
int lines_intersect (int i,int j,int k,int l); int lines_intersect (int i, int j, int k, int l);
void calculate_3x3_matrixes (struct ls_triplet_chain *ls_triplets); void calculate_3x3_matrixes (struct ls_triplet_chain* ls_triplets);
void choose_speaker_triplets (struct ls_triplet_chain **ls_triplets); void choose_speaker_triplets (struct ls_triplet_chain** ls_triplets);
void choose_speaker_pairs (); void choose_speaker_pairs ();
void sort_2D_lss (int* sorted_lss); void sort_2D_lss (int* sorted_lss);
int calc_2D_inv_tmatrix (double azi1,double azi2, double* inv_mat); int calc_2D_inv_tmatrix (double azi1, double azi2, double* inv_mat);
}; };
} /* namespace */ } // namespace ARDOUR
#endif /* __libardour_vbap_speakers_h__ */ #endif /* __libardour_vbap_speakers_h__ */