NO-OP: whitespace

This commit is contained in:
Robin Gareus 2020-03-23 16:46:03 +01:00
parent ac9e16f0b8
commit 4780a0fd60
Signed by: rgareus
GPG Key ID: A090BCE02CF57F04
10 changed files with 948 additions and 956 deletions

View File

@ -21,40 +21,38 @@
#include <inttypes.h>
#include <cmath>
#include <cerrno>
#include <cstdlib>
#include <string>
#include <cmath>
#include <cstdio>
#include <locale.h>
#include <unistd.h>
#include <cstdlib>
#include <float.h>
#include <locale.h>
#include <string>
#include <unistd.h>
#include <glibmm.h>
#include "pbd/cartesian.h"
#include "pbd/convert.h"
#include "pbd/enumwriter.h"
#include "pbd/error.h"
#include "pbd/failed_constructor.h"
#include "pbd/xml++.h"
#include "pbd/enumwriter.h"
#include "evoral/Curve.h"
#include "ardour/session.h"
#include "ardour/panner.h"
#include "ardour/utils.h"
#include "ardour/audio_buffer.h"
#include "ardour/debug.h"
#include "ardour/runtime_functions.h"
#include "ardour/buffer_set.h"
#include "ardour/audio_buffer.h"
#include "ardour/debug.h"
#include "ardour/pannable.h"
#include "ardour/panner.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 "pbd/i18n.h"
#include "pbd/mathfix.h"
@ -63,15 +61,19 @@ using namespace ARDOUR;
using namespace PBD;
static PanPluginDescriptor _descriptor = {
"Mono to Stereo Panner",
"http://ardour.org/plugin/panner_1in2out",
"http://ardour.org/plugin/panner_1in2out#ui",
1, 2,
20,
Panner1in2out::factory
"Mono to Stereo Panner",
"http://ardour.org/plugin/panner_1in2out",
"http://ardour.org/plugin/panner_1in2out#ui",
1, 2,
20,
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)
: Panner (p)
@ -80,16 +82,16 @@ Panner1in2out::Panner1in2out (boost::shared_ptr<Pannable> p)
_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;
right = desired_right;
left_interp = left;
right_interp = right;
left = desired_left;
right = desired_right;
left_interp = left;
right_interp = right;
_pannable->pan_azimuth_control->Changed.connect_same_thread (*this, boost::bind (&Panner1in2out::update, this));
_pannable->pan_azimuth_control->Changed.connect_same_thread (*this, boost::bind (&Panner1in2out::update, this));
}
Panner1in2out::~Panner1in2out ()
@ -99,32 +101,32 @@ Panner1in2out::~Panner1in2out ()
void
Panner1in2out::update ()
{
float panR, panL;
float const pan_law_attenuation = -3.0f;
float const scale = 2.0f - 4.0f * powf (10.0f,pan_law_attenuation/20.0f);
float panR, panL;
float const pan_law_attenuation = -3.0f;
float const scale = 2.0f - 4.0f * powf (10.0f, pan_law_attenuation / 20.0f);
panR = _pannable->pan_azimuth_control->get_value();
panL = 1 - panR;
panR = _pannable->pan_azimuth_control->get_value ();
panL = 1 - panR;
desired_left = panL * (scale * panL + 1.0f - scale);
desired_right = panR * (scale * panR + 1.0f - scale);
desired_left = panL * (scale * panL + 1.0f - scale);
desired_right = panR * (scale * panR + 1.0f - scale);
}
void
Panner1in2out::set_position (double p)
{
if (clamp_position (p)) {
_pannable->pan_azimuth_control->set_value (p, Controllable::NoGroup);
}
if (clamp_position (p)) {
_pannable->pan_azimuth_control->set_value (p, Controllable::NoGroup);
}
}
bool
Panner1in2out::clamp_position (double& p)
{
/* any position between 0.0 and 1.0 is legal */
DEBUG_TRACE (DEBUG::Panning, string_compose ("want to move panner to %1 - always allowed in 0.0-1.0 range\n", p));
p = max (min (p, 1.0), 0.0);
return true;
/* any position between 0.0 and 1.0 is legal */
DEBUG_TRACE (DEBUG::Panning, string_compose ("want to move panner to %1 - always allowed in 0.0-1.0 range\n", p));
p = max (min (p, 1.0), 0.0);
return true;
}
pair<double, double>
@ -136,37 +138,37 @@ Panner1in2out::position_range () const
double
Panner1in2out::position () const
{
return _pannable->pan_azimuth_control->get_value ();
return _pannable->pan_azimuth_control->get_value ();
}
void
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;
pan_t pan;
pan_t pan;
Sample* const src = srcbuf.data();
Sample* const src = srcbuf.data ();
/* 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
/* 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 n;
pframes_t const limit = min ((pframes_t)64, nframes);
pframes_t n;
delta = -(delta / (float) (limit));
delta = -(delta / (float)(limit));
for (n = 0; n < limit; n++) {
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;
}
@ -174,32 +176,25 @@ Panner1in2out::distribute_one (AudioBuffer& srcbuf, BufferSet& obufs, gain_t gai
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 {
left = desired_left;
left = desired_left;
left_interp = left;
if ((pan = (left * gain_coeff)) != 1.0f) {
if (pan != 0.0f) {
/* pan is 1 but also not 0, so we must do it "properly" */
mix_buffers_with_gain(dst,src,nframes,pan);
/* mark that we wrote into the buffer */
// obufs[0] = 0;
mix_buffers_with_gain (dst, src, nframes, pan);
/* XXX it would be nice to mark that we wrote into the buffer */
}
} else {
/* 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 */
}
@ -207,21 +202,21 @@ Panner1in2out::distribute_one (AudioBuffer& srcbuf, BufferSet& obufs, gain_t gai
/* 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
/* 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 n;
pframes_t const limit = min ((pframes_t)64, nframes);
pframes_t n;
delta = -(delta / (float) (limit));
delta = -(delta / (float)(limit));
for (n = 0; n < limit; n++) {
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;
}
@ -229,36 +224,31 @@ Panner1in2out::distribute_one (AudioBuffer& srcbuf, BufferSet& obufs, gain_t gai
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 */
} else {
right = desired_right;
right = desired_right;
right_interp = right;
if ((pan = (right * gain_coeff)) != 1.0f) {
if (pan != 0.0f) {
/* pan is not 1 but also not 0, so we must do it "properly" */
mix_buffers_with_gain(dst,src,nframes,pan);
mix_buffers_with_gain (dst, src, nframes, pan);
/* XXX it would be nice to mark the buffer as written to */
}
} else {
/* pan is 1 so we can just copy the input samples straight in */
mix_buffers_no_gain(dst,src,nframes);
mix_buffers_no_gain (dst, src, nframes);
/* XXX it would be nice to mark the buffer as written to */
}
}
}
void
@ -266,18 +256,18 @@ Panner1in2out::distribute_one_automated (AudioBuffer& srcbuf, BufferSet& obufs,
samplepos_t start, samplepos_t end, pframes_t nframes,
pan_t** buffers, uint32_t which)
{
assert (obufs.count().n_audio() == 2);
assert (obufs.count ().n_audio () == 2);
Sample* dst;
pan_t* pbuf;
Sample* const src = srcbuf.data();
pan_t* const position = buffers[0];
Sample* dst;
pan_t* pbuf;
Sample* const src = srcbuf.data ();
pan_t* const position = buffers[0];
/* fetch positional data */
if (!_pannable->pan_azimuth_control->list()->curve().rt_safe_get_vector (start, end, position, nframes)) {
if (!_pannable->pan_azimuth_control->list ()->curve ().rt_safe_get_vector (start, end, position, nframes)) {
/* fallback */
distribute_one (srcbuf, obufs, 1.0, nframes, which);
distribute_one (srcbuf, obufs, 1.0, nframes, which);
return;
}
@ -286,27 +276,26 @@ Panner1in2out::distribute_one_automated (AudioBuffer& srcbuf, BufferSet& obufs,
*/
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) {
float panR = position[n];
const float panL = 1 - panR;
float panR = position[n];
const float panL = 1 - panR;
/* note that are overwriting buffers, but its OK
* because we're finished with their old contents
* (position automation data) and are
* replacing it with panning/gain coefficients
* that we need to actually process the data.
*/
/* note that are overwriting buffers, but its OK
because we're finished with their old contents
(position automation data) and are
replacing it with panning/gain coefficients
that we need to actually process the data.
*/
buffers[0][n] = panL * (scale * panL + 1.0f - scale);
buffers[1][n] = panR * (scale * panR + 1.0f - scale);
}
buffers[0][n] = panL * (scale * panL + 1.0f - scale);
buffers[1][n] = panR * (scale * panR + 1.0f - scale);
}
/* LEFT OUTPUT */
dst = obufs.get_audio(0).data();
dst = obufs.get_audio (0).data ();
pbuf = buffers[0];
for (pframes_t n = 0; n < nframes; ++n) {
@ -317,7 +306,7 @@ Panner1in2out::distribute_one_automated (AudioBuffer& srcbuf, BufferSet& obufs,
/* RIGHT OUTPUT */
dst = obufs.get_audio(1).data();
dst = obufs.get_audio (1).data ();
pbuf = buffers[1];
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 */
}
Panner*
Panner1in2out::factory (boost::shared_ptr<Pannable> p, boost::shared_ptr<Speakers> /* ignored */)
{
@ -338,38 +326,37 @@ XMLNode&
Panner1in2out::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: */
root.set_property (X_("type"), _descriptor.name);
root.set_property (X_ ("type"), _descriptor.name);
return root;
}
string
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()) {
case PanAzimuthAutomation:
/* 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)
(hard left) through (50,50) (hard center) to (0,100) (hard right).
switch (ac->parameter ().type ()) {
case PanAzimuthAutomation:
/* 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)
* (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
the center of the USA isn't Kansas, its (50LA, 50NY) and it will all make sense.
return string_compose (_ ("L%1R%2"), (int)rint (100.0 * (1.0 - val)),
(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:
return _("unused");
}
default:
return _ ("unused");
}
}
void

View File

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

View File

@ -20,35 +20,34 @@
#include <inttypes.h>
#include <cmath>
#include <cerrno>
#include <cstdlib>
#include <string>
#include <cmath>
#include <cstdio>
#include <locale.h>
#include <unistd.h>
#include <cstdlib>
#include <float.h>
#include <locale.h>
#include <string>
#include <unistd.h>
#include <glibmm.h>
#include "pbd/cartesian.h"
#include "pbd/convert.h"
#include "pbd/enumwriter.h"
#include "pbd/error.h"
#include "pbd/failed_constructor.h"
#include "pbd/xml++.h"
#include "pbd/enumwriter.h"
#include "evoral/Curve.h"
#include "ardour/audio_buffer.h"
#include "ardour/audio_buffer.h"
#include "ardour/buffer_set.h"
#include "ardour/mix.h"
#include "ardour/pan_controllable.h"
#include "ardour/pannable.h"
#include "ardour/runtime_functions.h"
#include "ardour/session.h"
#include "ardour/utils.h"
#include "ardour/mix.h"
#include "panner_2in2out.h"
@ -61,45 +60,49 @@ using namespace ARDOUR;
using namespace PBD;
static PanPluginDescriptor _descriptor = {
"Equal Power Stereo",
"http://ardour.org/plugin/panner_2in2out",
"http://ardour.org/plugin/panner_2in2out#ui",
2, 2,
20,
Panner2in2out::factory
"Equal Power Stereo",
"http://ardour.org/plugin/panner_2in2out",
"http://ardour.org/plugin/panner_2in2out#ui",
2, 2,
20,
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)
: Panner (p)
{
if (!_pannable->has_state()) {
_pannable->pan_azimuth_control->set_value (0.5, Controllable::NoGroup);
_pannable->pan_width_control->set_value (1.0, Controllable::NoGroup);
}
if (!_pannable->has_state ()) {
_pannable->pan_azimuth_control->set_value (0.5, Controllable::NoGroup);
_pannable->pan_width_control->set_value (1.0, Controllable::NoGroup);
}
double const w = width();
double const wrange = min (position(), (1 - position())) * 2;
if (fabs(w) > wrange) {
set_width(w > 0 ? wrange : -wrange);
}
double const w = width ();
double const wrange = min (position (), (1 - position ())) * 2;
if (fabs (w) > wrange) {
set_width (w > 0 ? wrange : -wrange);
}
_can_automate_list.insert (Evoral::Parameter (PanAzimuthAutomation));
_can_automate_list.insert (Evoral::Parameter (PanWidthAutomation));
_can_automate_list.insert (Evoral::Parameter (PanAzimuthAutomation));
_can_automate_list.insert (Evoral::Parameter (PanWidthAutomation));
update ();
update ();
/* LEFT SIGNAL */
left_interp[0] = left[0] = desired_left[0];
right_interp[0] = right[0] = desired_right[0];
/* LEFT SIGNAL */
left_interp[0] = left[0] = desired_left[0];
right_interp[0] = right[0] = desired_right[0];
/* RIGHT SIGNAL */
left_interp[1] = left[1] = desired_left[1];
right_interp[1] = right[1] = desired_right[1];
/* RIGHT SIGNAL */
left_interp[1] = left[1] = desired_left[1];
right_interp[1] = right[1] = desired_right[1];
_pannable->pan_azimuth_control->Changed.connect_same_thread (*this, boost::bind (&Panner2in2out::update, this));
_pannable->pan_width_control->Changed.connect_same_thread (*this, boost::bind (&Panner2in2out::update, this));
_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));
}
Panner2in2out::~Panner2in2out ()
@ -109,29 +112,29 @@ Panner2in2out::~Panner2in2out ()
double
Panner2in2out::position () const
{
return _pannable->pan_azimuth_control->get_value();
return _pannable->pan_azimuth_control->get_value ();
}
double
Panner2in2out::width () const
{
return _pannable->pan_width_control->get_value();
return _pannable->pan_width_control->get_value ();
}
void
Panner2in2out::set_position (double p)
{
if (clamp_position (p)) {
_pannable->pan_azimuth_control->set_value (p, Controllable::NoGroup);
}
if (clamp_position (p)) {
_pannable->pan_azimuth_control->set_value (p, Controllable::NoGroup);
}
}
void
Panner2in2out::set_width (double p)
{
if (clamp_width (p)) {
_pannable->pan_width_control->set_value (p, Controllable::NoGroup);
}
if (clamp_width (p)) {
_pannable->pan_width_control->set_value (p, Controllable::NoGroup);
}
}
void
@ -150,149 +153,148 @@ Panner2in2out::update ()
return;
}
/* 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().
/* it would be very nice to split this out into a virtual function
* that can be accessed from BaseStereoPanner and used in do_distribute_automated().
*
* but the place where its used in do_distribute_automated() is a tight inner loop,
* and making "nframes" virtual function calls to compute values is an absurd
* overhead.
*/
but the place where its used in do_distribute_automated() is a tight inner loop,
and making "nframes" virtual function calls to compute values is an absurd
overhead.
*/
/* x == 0 => hard left = 180.0 degrees
* x == 1 => hard right = 0.0 degrees
*/
/* x == 0 => hard left = 180.0 degrees
x == 1 => hard right = 0.0 degrees
*/
float pos[2];
double width = this->width ();
const double direction_as_lr_fract = position ();
float pos[2];
double width = this->width ();
const double direction_as_lr_fract = position ();
double const wrange = min (position (), (1 - position ())) * 2;
if (fabs (width) > wrange) {
width = (width > 0 ? wrange : -wrange);
}
double const wrange = min (position(), (1 - position())) * 2;
if (fabs(width) > wrange) {
width = (width > 0 ? wrange : -wrange);
}
if (width < 0.0) {
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
}
if (width < 0.0) {
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 */
/* 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;
float const scale = 2.0f - 4.0f * powf (10.0f,pan_law_attenuation/20.0f);
float panR;
float panL;
/* left signal */
/* 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];
panL = 1 - panR;
desired_left[0] = panL * (scale * panL + 1.0f - scale);
desired_right[0] = panR * (scale * panR + 1.0f - scale);
/* right signal */
/* right signal */
panR = pos[1];
panL = 1 - panR;
desired_left[1] = panL * (scale * panL + 1.0f - scale);
desired_right[1] = panR * (scale * panR + 1.0f - scale);
panR = pos[1];
panL = 1 - panR;
desired_left[1] = panL * (scale * panL + 1.0f - scale);
desired_right[1] = panR * (scale * panR + 1.0f - scale);
}
bool
Panner2in2out::clamp_position (double& p)
{
double w = width ();
return clamp_stereo_pan (p, w);
double w = width ();
return clamp_stereo_pan (p, w);
}
bool
Panner2in2out::clamp_width (double& w)
{
double p = position ();
return clamp_stereo_pan (p, w);
double p = position ();
return clamp_stereo_pan (p, w);
}
pair<double, double>
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>
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);
}
bool
Panner2in2out::clamp_stereo_pan (double& direction_as_lr_fract, double& width)
{
double r_pos;
double l_pos;
double r_pos;
double l_pos;
width = max (min (width, 1.0), -1.0);
direction_as_lr_fract = max (min (direction_as_lr_fract, 1.0), 0.0);
width = max (min (width, 1.0), -1.0);
direction_as_lr_fract = max (min (direction_as_lr_fract, 1.0), 0.0);
r_pos = direction_as_lr_fract + (width/2.0);
l_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);
if (width < 0.0) {
swap (r_pos, l_pos);
}
if (width < 0.0) {
swap (r_pos, l_pos);
}
/* if the new left position is less than or equal to zero (hard left) and the left panner
is already there, we're not moving the left signal.
*/
/* if the new left position is less than or equal to zero (hard left)
* and the left panner is already there, we're not moving the left signal.
*/
if (l_pos < 0.0) {
return false;
}
if (l_pos < 0.0) {
return false;
}
/* if the new right position is less than or equal to 1.0 (hard right) and the right panner
is already there, we're not moving the right signal.
*/
/* if the new right position is less than or equal to 1.0 (hard right)
* and the right panner is already there, we're not moving the right signal.
*/
if (r_pos > 1.0) {
return false;
if (r_pos > 1.0) {
return false;
}
}
return true;
return true;
}
void
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;
pan_t pan;
pan_t pan;
Sample* const src = srcbuf.data();
Sample* const src = srcbuf.data ();
/* 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
/* 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 n;
pframes_t const limit = min ((pframes_t)64, nframes);
pframes_t n;
delta = -(delta / (float) (limit));
delta = -(delta / (float)(limit));
for (n = 0; n < limit; n++) {
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;
}
@ -300,33 +302,25 @@ Panner2in2out::distribute_one (AudioBuffer& srcbuf, BufferSet& obufs, gain_t gai
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 {
left[which] = desired_left[which];
left[which] = desired_left[which];
left_interp[which] = left[which];
if ((pan = (left[which] * gain_coeff)) != 1.0f) {
if (pan != 0.0f) {
/* 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);
/* mark that we wrote into the buffer */
// obufs[0] = 0;
mix_buffers_with_gain (dst, src, nframes, pan);
/* XXX it would be nice to mark that we wrote into the buffer */
}
} else {
/* 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 */
}
@ -334,21 +328,21 @@ Panner2in2out::distribute_one (AudioBuffer& srcbuf, BufferSet& obufs, gain_t gai
/* 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
/* 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 n;
pframes_t const limit = min ((pframes_t)64, nframes);
pframes_t n;
delta = -(delta / (float) (limit));
delta = -(delta / (float)(limit));
for (n = 0; n < limit; n++) {
right_interp[which] = right_interp[which] + delta;
right[which] = right_interp[which] + 0.9 * (right[which] - right_interp[which]);
right[which] = right_interp[which] + 0.9 * (right[which] - right_interp[which]);
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;
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 */
} else {
right[which] = desired_right[which];
right[which] = desired_right[which];
right_interp[which] = right[which];
if ((pan = (right[which] * gain_coeff)) != 1.0f) {
if (pan != 0.0f) {
/* pan is not 1 but also not 0, so we must do it "properly" */
mix_buffers_with_gain(dst,src,nframes,pan);
// obufs.get_audio(1).read_from (srcbuf, nframes);
mix_buffers_with_gain (dst, src, nframes, pan);
/* XXX it would be nice to mark the buffer as written to */
}
} else {
/* pan is 1 so we can just copy the input samples straight in */
mix_buffers_no_gain(dst,src,nframes);
mix_buffers_no_gain (dst, src, nframes);
/* 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,
pan_t** buffers, uint32_t which)
{
assert (obufs.count().n_audio() == 2);
assert (obufs.count ().n_audio () == 2);
Sample* dst;
pan_t* pbuf;
Sample* const src = srcbuf.data();
pan_t* const position = buffers[0];
pan_t* const width = buffers[1];
Sample* dst;
pan_t* pbuf;
Sample* const src = srcbuf.data ();
pan_t* const position = buffers[0];
pan_t* const width = buffers[1];
/* 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 */
distribute_one (srcbuf, obufs, 1.0, nframes, which);
distribute_one (srcbuf, obufs, 1.0, nframes, which);
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 */
distribute_one (srcbuf, obufs, 1.0, nframes, which);
distribute_one (srcbuf, obufs, 1.0, nframes, which);
return;
}
/* 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 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) {
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) {
// 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));
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
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.
*/
buffers[0][n] = panL * (scale * panL + 1.0f - scale);
buffers[1][n] = panR * (scale * panR + 1.0f - scale);
}
buffers[0][n] = panL * (scale * panL + 1.0f - scale);
buffers[1][n] = panR * (scale * panR + 1.0f - scale);
}
/* LEFT OUTPUT */
dst = obufs.get_audio(0).data();
dst = obufs.get_audio (0).data ();
pbuf = buffers[0];
for (pframes_t n = 0; n < nframes; ++n) {
@ -462,7 +450,7 @@ Panner2in2out::distribute_one_automated (AudioBuffer& srcbuf, BufferSet& obufs,
/* RIGHT OUTPUT */
dst = obufs.get_audio(1).data();
dst = obufs.get_audio (1).data ();
pbuf = buffers[1];
for (pframes_t n = 0; n < nframes; ++n) {
@ -482,41 +470,40 @@ XMLNode&
Panner2in2out::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: */
root.set_property (X_("type"), _descriptor.name);
root.set_property (X_ ("type"), _descriptor.name);
return root;
}
string
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()) {
case PanAzimuthAutomation:
/* 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)
(hard left) through (50,50) (hard center) to (0,100) (hard right).
switch (ac->parameter ().type ()) {
case PanAzimuthAutomation:
/* 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)
* (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
the center of the USA isn't Kansas, its (50LA, 50NY) and it will all make sense.
return string_compose (_ ("L%1R%2"), (int)rint (100.0 * (1.0 - val)),
(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.
*/
case PanWidthAutomation:
return string_compose (_ ("Width: %1%%"), (int)floor (100.0 * val));
return string_compose (_("L%1R%2"), (int) rint (100.0 * (1.0 - val)),
(int) rint (100.0 * val));
case PanWidthAutomation:
return string_compose (_("Width: %1%%"), (int) floor (100.0 * val));
default:
return _("unused");
}
default:
return _ ("unused");
}
}
void

View File

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

View File

@ -19,35 +19,34 @@
#include <inttypes.h>
#include <cmath>
#include <cerrno>
#include <cstdlib>
#include <string>
#include <cmath>
#include <cstdio>
#include <locale.h>
#include <unistd.h>
#include <cstdlib>
#include <float.h>
#include <locale.h>
#include <string>
#include <unistd.h>
#include <glibmm.h>
#include "pbd/cartesian.h"
#include "pbd/convert.h"
#include "pbd/enumwriter.h"
#include "pbd/error.h"
#include "pbd/failed_constructor.h"
#include "pbd/xml++.h"
#include "pbd/enumwriter.h"
#include "evoral/Curve.h"
#include "ardour/audio_buffer.h"
#include "ardour/audio_buffer.h"
#include "ardour/buffer_set.h"
#include "ardour/mix.h"
#include "ardour/pan_controllable.h"
#include "ardour/pannable.h"
#include "ardour/runtime_functions.h"
#include "ardour/session.h"
#include "ardour/utils.h"
#include "ardour/mix.h"
#include "panner_balance.h"
@ -68,12 +67,16 @@ static PanPluginDescriptor _descriptor = {
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)
: Panner (p)
{
if (!_pannable->has_state()) {
if (!_pannable->has_state ()) {
_pannable->pan_azimuth_control->set_value (0.5, Controllable::NoGroup);
}
@ -96,10 +99,10 @@ Pannerbalance::~Pannerbalance ()
double
Pannerbalance::position () const
{
return _pannable->pan_azimuth_control->get_value();
return _pannable->pan_azimuth_control->get_value ();
}
void
void
Pannerbalance::set_position (double p)
{
if (clamp_position (p)) {
@ -107,7 +110,7 @@ Pannerbalance::set_position (double p)
}
}
void
void
Pannerbalance::thaw ()
{
Panner::thaw ();
@ -123,7 +126,7 @@ Pannerbalance::update ()
return;
}
float const pos = _pannable->pan_azimuth_control->get_value();
float const pos = _pannable->pan_azimuth_control->get_value ();
if (pos == .5) {
desired_pos[0] = 1.0;
@ -153,29 +156,29 @@ Pannerbalance::position_range () const
void
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;
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
/* 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 n;
pframes_t const limit = min ((pframes_t)64, nframes);
pframes_t n;
delta = -(delta / (float) (limit));
delta = -(delta / (float)(limit));
for (n = 0; n < limit; n++) {
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;
}
@ -183,31 +186,24 @@ Pannerbalance::distribute_one (AudioBuffer& srcbuf, BufferSet& obufs, gain_t gai
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 {
pos[which] = desired_pos[which];
pos[which] = desired_pos[which];
pos_interp[which] = pos[which];
if ((pan = (pos[which] * gain_coeff)) != 1.0f) {
if (pan != 0.0f) {
/* 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);
/* mark that we wrote into the buffer */
// obufs[0] = 0;
mix_buffers_with_gain (dst, src, nframes, pan);
/* XXX it would be nice to mark the buffer as written to */
}
} else {
/* pan is 1 so we can just copy the input samples straight in */
mix_buffers_no_gain(dst,src,nframes);
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,
pan_t** buffers, uint32_t which)
{
assert (obufs.count().n_audio() == 2);
assert (obufs.count ().n_audio () == 2);
Sample* dst;
pan_t* pbuf;
Sample* const src = srcbuf.data();
pan_t* const position = buffers[0];
Sample* dst;
pan_t* pbuf;
Sample* const src = srcbuf.data ();
pan_t* const position = buffers[0];
/* fetch positional data */
if (!_pannable->pan_azimuth_control->list()->curve().rt_safe_get_vector (start, end, position, nframes)) {
if (!_pannable->pan_azimuth_control->list ()->curve ().rt_safe_get_vector (start, end, position, nframes)) {
/* fallback */
distribute_one (srcbuf, obufs, 1.0, nframes, which);
return;
}
for (pframes_t n = 0; n < nframes; ++n) {
float const pos = position[n];
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];
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);
}
XMLNode&
XMLNode&
Pannerbalance::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: */
root.set_property (X_("type"), _descriptor.name);
root.set_property (X_ ("type"), _descriptor.name);
return root;
}
string
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:
/* 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)
(hard left) through (50,50) (hard center) to (0,100) (hard right).
* 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).
*
* 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
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.
*/
return string_compose (_("L%1R%2"), (int) rint (100.0 * (1.0 - val)),
(int) rint (100.0 * val));
return string_compose (_ ("L%1R%2"), (int)rint (100.0 * (1.0 - val)),
(int)rint (100.0 * val));
default:
return _("unused");
return _ ("unused");
}
}

View File

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

View File

@ -20,8 +20,8 @@
*/
#include <cmath>
#include <cstdlib>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <iostream>
@ -51,131 +51,132 @@ using namespace ARDOUR;
using namespace std;
static PanPluginDescriptor _descriptor = {
"VBAP 2D panner",
"http://ardour.org/plugin/panner_vbap",
"http://ardour.org/plugin/panner_vbap#ui",
-1, -1,
10,
VBAPanner::factory
"VBAP 2D panner",
"http://ardour.org/plugin/panner_vbap",
"http://ardour.org/plugin/panner_vbap#ui",
-1, -1,
10,
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)
{
resize_gains (n_speakers);
resize_gains (n_speakers);
desired_gains[0] = desired_gains[1] = desired_gains[2] = 0;
outputs[0] = outputs[1] = outputs[2] = -1;
desired_outputs[0] = desired_outputs[1] = desired_outputs[2] = -1;
desired_gains[0] = desired_gains[1] = desired_gains[2] = 0;
outputs[0] = outputs[1] = outputs[2] = -1;
desired_outputs[0] = desired_outputs[1] = desired_outputs[2] = -1;
}
void
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)
: Panner (p)
, _speakers (new VBAPSpeakers (s))
{
_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_width_control->Changed.connect_same_thread (*this, boost::bind (&VBAPanner::update, this));
if (!_pannable->has_state()) {
reset();
}
_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_width_control->Changed.connect_same_thread (*this, boost::bind (&VBAPanner::update, this));
if (!_pannable->has_state ()) {
reset ();
}
update ();
update ();
}
VBAPanner::~VBAPanner ()
{
clear_signals ();
clear_signals ();
}
void
VBAPanner::clear_signals ()
{
for (vector<Signal*>::iterator i = _signals.begin(); i != _signals.end(); ++i) {
delete *i;
}
_signals.clear ();
for (vector<Signal*>::iterator i = _signals.begin (); i != _signals.end (); ++i) {
delete *i;
}
_signals.clear ();
}
void
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) {
Signal* s = new Signal (*this, i, _speakers->n_speakers());
_signals.push_back (s);
for (uint32_t i = 0; i < n; ++i) {
Signal* s = new Signal (*this, i, _speakers->n_speakers ());
_signals.push_back (s);
}
}
update ();
update ();
}
void
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 ();
_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));
}
/* recompute signal directions based on panner azimuth and, if relevant, width (diffusion) and elevation parameters */
double elevation = _pannable->pan_elevation_control->get_value () * 90.0;
/* recompute signal directions based on panner azimuth and, if relevant, width (diffusion) and elevation parameters */
double elevation = _pannable->pan_elevation_control->get_value() * 90.0;
if (_signals.size () > 1) {
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) {
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) {
int over = signal_direction;
over -= (signal_direction >= 0) ? 0 : 1;
signal_direction -= (double)over;
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;
over -= (signal_direction >= 0) ? 0 : 1;
signal_direction -= (double)over;
/* width has no role to play if there is only 1 signal: VBAP does not do "diffusion" of a single channel */
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;
Signal* s = _signals.front ();
s->direction = AngularVector (center, elevation);
compute_gains (s->desired_gains, s->desired_outputs, s->direction.azi, s->direction.ele);
}
/* width has no role to play if there is only 1 signal: VBAP does not do "diffusion" of a single channel */
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 */
SignalPositionChanged (); /* emit */
}
void
VBAPanner::compute_gains (double gains[3], int speaker_ids[3], int azi, int ele)
{
/* calculates gain factors using loudspeaker setup and given direction */
double cartdir[3];
double power;
int i,j,k;
double small_g;
double big_sm_g, gtmp[3];
const int dimension = _speakers->dimension();
assert(dimension == 2 || dimension == 3);
double cartdir[3];
double power;
int i, j, k;
double small_g;
double big_sm_g, gtmp[3];
const int dimension = _speakers->dimension ();
assert (dimension == 2 || dimension == 3);
spherical_to_cartesian (azi, ele, 1.0, cartdir[0], cartdir[1], cartdir[2]);
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;
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;
for (j = 0; j < dimension; j++) {
gtmp[j] = 0.0;
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) {
@ -201,7 +200,6 @@ VBAPanner::compute_gains (double gains[3], int speaker_ids[3], int azi, int ele)
}
if (small_g > big_sm_g) {
big_sm_g = small_g;
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[1] = _speakers->speaker_for_tuple (i, 1);
if (_speakers->dimension() == 3) {
gains[2] = gtmp[2];
if (_speakers->dimension () == 3) {
gains[2] = gtmp[2];
speaker_ids[2] = _speakers->speaker_for_tuple (i, 2);
} else {
gains[2] = 0.0;
gains[2] = 0.0;
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) {
gains[0] /= power;
@ -232,144 +230,136 @@ VBAPanner::compute_gains (double gains[3], int speaker_ids[3], int azi, int ele)
void
VBAPanner::distribute (BufferSet& inbufs, BufferSet& obufs, gain_t gain_coefficient, pframes_t nframes)
{
uint32_t n;
vector<Signal*>::iterator s;
uint32_t n;
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
VBAPanner::distribute_one (AudioBuffer& srcbuf, BufferSet& obufs, gain_t gain_coefficient, pframes_t nframes, uint32_t which)
{
Sample* const src = srcbuf.data();
Signal* signal (_signals[which]);
Sample* const src = srcbuf.data ();
Signal* signal (_signals[which]);
/* 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
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.
vector<double>::size_type sz = signal->gains.size ();
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.
*/
assert (sz == obufs.count ().n_audio ());
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());
int8_t *outputs = (int8_t*)alloca(sz); // on the stack, no malloc
/* set initial state of each output "record"
/* set initial state of each output "record"
*/
for (uint32_t o = 0; o < sz; ++o) {
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;
}
for (uint32_t o = 0; o < sz; ++o) {
outputs[o] = 0;
}
/* 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) {
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;
}
}
for (int o = 0; o < 3; ++o) {
if (signal->outputs[o] != -1) {
/* used last time */
outputs[signal->outputs[o]] |= 1;
}
/* 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.
*/
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 (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
VBAPanner::distribute_one_automated (AudioBuffer& /*src*/, BufferSet& /*obufs*/,
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 */
}
@ -377,10 +367,10 @@ VBAPanner::distribute_one_automated (AudioBuffer& /*src*/, BufferSet& /*obufs*/,
XMLNode&
VBAPanner::get_state ()
{
XMLNode& node (Panner::get_state());
node.set_property (X_("uri"), _descriptor.panner_uri);
XMLNode& node (Panner::get_state ());
node.set_property (X_ ("uri"), _descriptor.panner_uri);
/* 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;
}
@ -391,52 +381,51 @@ VBAPanner::factory (boost::shared_ptr<Pannable> p, boost::shared_ptr<Speakers> s
}
ChanCount
VBAPanner::in() const
VBAPanner::in () const
{
return ChanCount (DataType::AUDIO, _signals.size());
return ChanCount (DataType::AUDIO, _signals.size ());
}
ChanCount
VBAPanner::out() const
VBAPanner::out () const
{
return ChanCount (DataType::AUDIO, _speakers->n_speakers());
return ChanCount (DataType::AUDIO, _speakers->n_speakers ());
}
string
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()) {
case PanAzimuthAutomation: /* direction */
return string_compose (_("%1\u00B0"), (int (rint (val * 360.0))+180)%360);
switch (ac->parameter ().type ()) {
case PanAzimuthAutomation: /* direction */
return string_compose (_ ("%1\u00B0"), (int(rint (val * 360.0)) + 180) % 360);
case PanWidthAutomation: /* diffusion */
return string_compose (_("%1%%"), (int) floor (100.0 * fabs(val)));
case PanWidthAutomation: /* diffusion */
return string_compose (_ ("%1%%"), (int)floor (100.0 * fabs (val)));
case PanElevationAutomation: /* elevation */
return string_compose (_("%1\u00B0"), (int) floor (90.0 * fabs(val)));
case PanElevationAutomation: /* elevation */
return string_compose (_ ("%1\u00B0"), (int)floor (90.0 * fabs (val)));
default:
return _("unused");
}
default:
return _ ("unused");
}
}
AngularVector
VBAPanner::signal_position (uint32_t n) const
{
if (n < _signals.size()) {
return _signals[n]->direction;
}
if (n < _signals.size ()) {
return _signals[n]->direction;
}
return AngularVector();
return AngularVector ();
}
boost::shared_ptr<Speakers>
VBAPanner::get_speakers () const
{
return _speakers->parent();
return _speakers->parent ();
}
void
@ -465,11 +454,11 @@ void
VBAPanner::reset ()
{
set_position (.5);
if (_signals.size() > 1) {
set_width (1.0 - (1.0 / (double)_signals.size()));
} else {
set_width (1.0);
}
if (_signals.size () > 1) {
set_width (1.0 - (1.0 / (double)_signals.size ()));
} else {
set_width (1.0);
}
set_elevation (0);
update ();

View File

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

View File

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

View File

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