NO-OP: whitespace
This commit is contained in:
parent
ac9e16f0b8
commit
4780a0fd60
|
@ -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
|
||||
|
|
|
@ -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__ */
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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__ */
|
||||
|
|
|
@ -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");
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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__ */
|
||||
|
|
|
@ -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 ();
|
||||
|
|
|
@ -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__ */
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -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__ */
|
||||
|
|
Loading…
Reference in New Issue
Block a user