one step closer to working vbap panning
git-svn-id: svn://localhost/ardour2/branches/3.0@8091 d708f5d6-7413-0410-9779-e7cbd77b26cf
This commit is contained in:
parent
1539ac1b96
commit
553cf2982c
|
@ -41,14 +41,11 @@ using namespace ARDOUR;
|
|||
using namespace PBD;
|
||||
using Gtkmm2ext::Keyboard;
|
||||
|
||||
Panner2d::Target::Target (float xa, float ya, const char *txt)
|
||||
: x (xa, 0.0, 1.0, 0.01, 0.1)
|
||||
, y (ya, 0.0, 1.0, 0.01, 0.1)
|
||||
, azimuth (M_PI/2.0, 0.0, 2.0 * M_PI, 0.1, 0.5)
|
||||
Panner2d::Target::Target (const AngularVector& a, const char *txt)
|
||||
: position (a)
|
||||
, text (txt)
|
||||
, _selected (false)
|
||||
{
|
||||
azimuth.set_value ((random() / (double) INT_MAX) * (2.0 * M_PI));
|
||||
}
|
||||
|
||||
Panner2d::Target::~Target ()
|
||||
|
@ -64,10 +61,6 @@ Panner2d::Target::set_text (const char* txt)
|
|||
Panner2d::Panner2d (boost::shared_ptr<Panner> p, int32_t h)
|
||||
: panner (p), width (0), height (h)
|
||||
{
|
||||
allow_x = false;
|
||||
allow_y = false;
|
||||
allow_target = false;
|
||||
|
||||
panner->StateChanged.connect (state_connection, invalidator (*this), boost::bind (&Panner2d::handle_state_change, this), gui_context());
|
||||
panner->Changed.connect (change_connection, invalidator (*this), boost::bind (&Panner2d::handle_position_change, this), gui_context());
|
||||
|
||||
|
@ -90,7 +83,7 @@ Panner2d::reset (uint32_t n_inputs)
|
|||
/* pucks */
|
||||
|
||||
while (pucks.size() < n_inputs) {
|
||||
add_puck ("", 0.0, 0.0);
|
||||
add_puck ("", AngularVector());
|
||||
}
|
||||
|
||||
if (pucks.size() > n_inputs) {
|
||||
|
@ -128,25 +121,14 @@ Panner2d::reset (uint32_t n_inputs)
|
|||
}
|
||||
|
||||
for (uint32_t i = existing_pucks; i < n_inputs; ++i) {
|
||||
float x, y;
|
||||
double dx, dy;
|
||||
|
||||
panner->streampanner (i).get_position (x, y);
|
||||
|
||||
dx = x;
|
||||
dy = y;
|
||||
clamp_to_circle (dx, dy);
|
||||
|
||||
pucks[i]->x.set_value (dx);
|
||||
pucks[i]->y.set_value (dy);
|
||||
|
||||
pucks[i]->position = panner->streampanner (i).get_position ();
|
||||
pucks[i]->visible = true;
|
||||
}
|
||||
|
||||
/* add all outputs */
|
||||
|
||||
while (targets.size() < panner->nouts()) {
|
||||
add_target (0.0, 0.0);
|
||||
add_target (AngularVector());
|
||||
}
|
||||
|
||||
if (targets.size() > panner->nouts()) {
|
||||
|
@ -166,25 +148,13 @@ Panner2d::reset (uint32_t n_inputs)
|
|||
|
||||
snprintf (buf, sizeof (buf), "%d", n+1);
|
||||
targets[n]->set_text (buf);
|
||||
targets[n]->x.set_value (panner->output(n).x);
|
||||
targets[n]->y.set_value (panner->output(n).y);
|
||||
targets[n]->position = panner->output(n).position;
|
||||
targets[n]->visible = true;
|
||||
}
|
||||
|
||||
allow_x_motion (true);
|
||||
allow_y_motion (true);
|
||||
allow_target_motion (true);
|
||||
|
||||
queue_draw ();
|
||||
}
|
||||
|
||||
Gtk::Adjustment&
|
||||
Panner2d::azimuth (uint32_t which)
|
||||
{
|
||||
assert (which < pucks.size());
|
||||
return pucks[which]->azimuth;
|
||||
}
|
||||
|
||||
void
|
||||
Panner2d::on_size_allocate (Gtk::Allocation& alloc)
|
||||
{
|
||||
|
@ -200,16 +170,9 @@ Panner2d::on_size_allocate (Gtk::Allocation& alloc)
|
|||
}
|
||||
|
||||
int
|
||||
Panner2d::add_puck (const char* text, float x, float y)
|
||||
Panner2d::add_puck (const char* text, const AngularVector& a)
|
||||
{
|
||||
double dx, dy;
|
||||
|
||||
dx = x;
|
||||
dy = y;
|
||||
|
||||
clamp_to_circle (dx, dy);
|
||||
|
||||
Target* puck = new Target (dx, dy, text);
|
||||
Target* puck = new Target (a, text);
|
||||
pucks.push_back (puck);
|
||||
puck->visible = true;
|
||||
|
||||
|
@ -217,9 +180,9 @@ Panner2d::add_puck (const char* text, float x, float y)
|
|||
}
|
||||
|
||||
int
|
||||
Panner2d::add_target (float x, float y)
|
||||
Panner2d::add_target (const AngularVector& a)
|
||||
{
|
||||
Target* target = new Target (x, y, "");
|
||||
Target* target = new Target (a, "");
|
||||
targets.push_back (target);
|
||||
target->visible = true;
|
||||
queue_draw ();
|
||||
|
@ -242,57 +205,53 @@ Panner2d::handle_position_change ()
|
|||
ENSURE_GUI_THREAD (*this, &Panner2d::handle_position_change)
|
||||
|
||||
for (n = 0; n < pucks.size(); ++n) {
|
||||
float x, y;
|
||||
panner->streampanner(n).get_position (x, y);
|
||||
pucks[n]->x.set_value (x);
|
||||
pucks[n]->y.set_value (y);
|
||||
pucks[n]->position = panner->streampanner(n).get_position ();
|
||||
}
|
||||
|
||||
for (n = 0; n < targets.size(); ++n) {
|
||||
targets[n]->x.set_value (panner->output(n).x);
|
||||
targets[n]->y.set_value (panner->output(n).y);
|
||||
targets[n]->position = panner->output(n).position;
|
||||
}
|
||||
|
||||
queue_draw ();
|
||||
}
|
||||
|
||||
void
|
||||
Panner2d::move_puck (int which, float x, float y)
|
||||
Panner2d::move_puck (int which, const AngularVector& a)
|
||||
{
|
||||
if (which >= int (targets.size())) {
|
||||
return;
|
||||
}
|
||||
|
||||
targets[which]->x.set_value (x);
|
||||
targets[which]->y.set_value (y);
|
||||
targets[which]->position = a;
|
||||
queue_draw ();
|
||||
}
|
||||
|
||||
Panner2d::Target *
|
||||
Panner2d::find_closest_object (gdouble x, gdouble y, int& which) const
|
||||
{
|
||||
gdouble efx, efy;
|
||||
gdouble cx, cy;
|
||||
Target *closest = 0;
|
||||
Target *candidate;
|
||||
float distance;
|
||||
float best_distance = FLT_MAX;
|
||||
int pwhich;
|
||||
|
||||
efx = x/(width-1.0);
|
||||
efy = 1.0 - (y/(height-1.0)); /* convert from X Window origin */
|
||||
|
||||
which = 0;
|
||||
pwhich = 0;
|
||||
|
||||
cerr << "@ " << x << ", " << y << endl;
|
||||
|
||||
for (Targets::const_iterator i = pucks.begin(); i != pucks.end(); ++i, ++pwhich) {
|
||||
candidate = *i;
|
||||
|
||||
cx = candidate->x.get_value();
|
||||
cy = candidate->y.get_value();
|
||||
CartesianVector c;
|
||||
|
||||
distance = sqrt ((cx - efx) * (cx - efx) +
|
||||
(cy - efy) * (cy - efy));
|
||||
candidate->position.cartesian (c);
|
||||
cart_to_gtk (c);
|
||||
|
||||
distance = sqrt ((c.x - x) * (c.x - x) +
|
||||
(c.y - y) * (c.y - y));
|
||||
|
||||
cerr << "\tConsider candiate " << candidate->text << " @ " << c.x << ", " << c.y << ", " << c.z << " distance = " << distance << endl;
|
||||
|
||||
if (distance < best_distance) {
|
||||
closest = candidate;
|
||||
|
@ -301,10 +260,13 @@ Panner2d::find_closest_object (gdouble x, gdouble y, int& which) const
|
|||
}
|
||||
}
|
||||
|
||||
if (best_distance > 0.05) { // arbitrary
|
||||
|
||||
if (best_distance > 20) { // arbitrary
|
||||
return 0;
|
||||
}
|
||||
|
||||
cerr << "the winner is " << closest->text << endl;
|
||||
|
||||
return closest;
|
||||
}
|
||||
|
||||
|
@ -328,7 +290,6 @@ bool
|
|||
Panner2d::on_expose_event (GdkEventExpose *event)
|
||||
{
|
||||
gint x, y;
|
||||
float fx, fy;
|
||||
cairo_t* cr;
|
||||
|
||||
cr = gdk_cairo_create (get_window()->gobj());
|
||||
|
@ -386,25 +347,26 @@ Panner2d::on_expose_event (GdkEventExpose *event)
|
|||
if (puck->visible) {
|
||||
/* redraw puck */
|
||||
|
||||
fx = min (puck->x.get_value(), 1.0);
|
||||
fx = max (fx, -1.0f);
|
||||
x = (gint) floor (width * fx - 4);
|
||||
|
||||
fy = min (fy, 1.0f);
|
||||
fy = max (fy, -1.0f);
|
||||
|
||||
/* translate back to X Window abomination coordinates */
|
||||
fy = -(puck->y.get_value() - 1.0);
|
||||
|
||||
y = (gint) floor (height * fy - 4);
|
||||
CartesianVector c;
|
||||
|
||||
puck->position.cartesian (c);
|
||||
cart_to_gtk (c);
|
||||
|
||||
x = (gint) floor (c.x);
|
||||
y = (gint) floor (c.y);
|
||||
|
||||
/* XXX need to shift circles so that they are centered on the circle */
|
||||
|
||||
cairo_arc (cr, x, y, arc_radius, 0, 2.0 * M_PI);
|
||||
cairo_set_source_rgb (cr, 0.8, 0.2, 0.1);
|
||||
cairo_close_path (cr);
|
||||
cairo_fill (cr);
|
||||
|
||||
cairo_move_to (cr, x + 6, y + 6);
|
||||
cairo_show_text (cr, puck->text.c_str());
|
||||
|
||||
char buf[256];
|
||||
snprintf (buf, sizeof (buf), "%s:%d", puck->text.c_str(), (int) lrint (puck->position.azi));
|
||||
cairo_show_text (cr, buf);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -419,14 +381,14 @@ Panner2d::on_expose_event (GdkEventExpose *event)
|
|||
|
||||
if (target->visible) {
|
||||
|
||||
fx = min (target->x.get_value(), 1.0);
|
||||
fx = max (fx, -1.0f);
|
||||
x = (gint) floor (width * fx);
|
||||
|
||||
fy = min (target->y.get_value(), 1.0);
|
||||
fy = max (fy, -1.0f);
|
||||
y = (gint) floor (height * fy);
|
||||
CartesianVector c;
|
||||
|
||||
target->position.cartesian (c);
|
||||
cart_to_gtk (c);
|
||||
|
||||
x = (int) floor (c.x);
|
||||
y = (int) floor (c.y);
|
||||
|
||||
snprintf (buf, sizeof (buf), "%d", n);
|
||||
|
||||
cairo_set_source_rgb (cr, 0.0, 0.8, 0.1);
|
||||
|
@ -434,6 +396,7 @@ Panner2d::on_expose_event (GdkEventExpose *event)
|
|||
cairo_fill (cr);
|
||||
cairo_move_to (cr, x+6, y+6);
|
||||
cairo_show_text (cr, buf);
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -541,88 +504,80 @@ Panner2d::handle_motion (gint evx, gint evy, GdkModifierType state)
|
|||
|
||||
if (state & GDK_BUTTON1_MASK && !(state & GDK_BUTTON2_MASK)) {
|
||||
|
||||
double fx = evx;
|
||||
double fy = evy;
|
||||
CartesianVector c;
|
||||
bool need_move = false;
|
||||
|
||||
drag_target->position.cartesian (c);
|
||||
cart_to_gtk (c);
|
||||
|
||||
clamp_to_circle (fx, fy);
|
||||
|
||||
if ((fx != drag_target->x.get_value()) || (fy != drag_target->y.get_value())) {
|
||||
if ((evx != c.x) || (evy != c.y)) {
|
||||
need_move = true;
|
||||
}
|
||||
|
||||
if (need_move) {
|
||||
drag_target->x.set_value (fx);
|
||||
drag_target->y.set_value (fy);
|
||||
CartesianVector cp (evx, evy, 0.0);
|
||||
|
||||
/* canonicalize position */
|
||||
|
||||
gtk_to_cart (cp);
|
||||
|
||||
/* position actual signal on circle */
|
||||
|
||||
clamp_to_circle (cp.x, cp.y);
|
||||
|
||||
/* generate an angular representation and set drag target (GUI) position */
|
||||
|
||||
cp.angular (drag_target->position); /* sets drag target position */
|
||||
|
||||
panner->streampanner (drag_index).set_position (drag_target->position);
|
||||
|
||||
panner->streampanner (drag_index).set_position (drag_target->x.get_value(), drag_target->y.get_value(), false);
|
||||
queue_draw ();
|
||||
}
|
||||
|
||||
} else if ((state & GDK_BUTTON2_MASK) && !(state & GDK_BUTTON1_MASK)) {
|
||||
|
||||
int xdelta = drag_x - evx;
|
||||
int ydelta = drag_x - evy;
|
||||
|
||||
drag_target->azimuth.set_value (drag_target->azimuth.get_value() + (2 * M_PI) * ((float)ydelta)/height * ((float) -xdelta)/height);
|
||||
queue_draw ();
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void
|
||||
Panner2d::cart_to_azi_ele (double x, double y, double& azi, double& ele)
|
||||
Panner2d::cart_to_gtk (CartesianVector& c) const
|
||||
{
|
||||
x = min (x, (double) width);
|
||||
x = max (x, 0.0);
|
||||
x = x / (width-1.0);
|
||||
/* "c" uses a coordinate space that is:
|
||||
|
||||
center = 0.0
|
||||
dimension = 2.0 * 2.0
|
||||
so max values along each axis are -1..+1
|
||||
|
||||
y = min (y, (double) height);
|
||||
y = max (y, 0.0);
|
||||
y = y / (height-1.0);
|
||||
GTK uses a coordinate space that is:
|
||||
|
||||
/* at this point, new_x and new_y are in the range [ 0.0 .. 1.0 ], with
|
||||
(0,0) at the upper left corner (thank you, X Window)
|
||||
|
||||
we need to translate to (0,0) at center
|
||||
top left = 0.0
|
||||
dimension = width * height
|
||||
so max values along each axis are 0,width and
|
||||
0,height
|
||||
*/
|
||||
|
||||
x -= 0.5;
|
||||
y = (1.0 - y) - 0.5;
|
||||
|
||||
PBD::cart_to_azi_ele (x, y, 0.0, azi, ele);
|
||||
c.x = (width / 2) * (c.x + 1);
|
||||
c.y = (height / 2) * (1 - c.y);
|
||||
|
||||
/* XXX z-axis not handled - 2D for now */
|
||||
}
|
||||
|
||||
void
|
||||
Panner2d::azi_ele_to_cart (double azi, double ele, double& x, double& y)
|
||||
Panner2d::gtk_to_cart (CartesianVector& c) const
|
||||
{
|
||||
double z;
|
||||
c.x = (c.x / (width / 2.0)) - 1.0;
|
||||
c.y = -((c.y / (height / 2.0)) - 1.0);
|
||||
|
||||
PBD::azi_ele_to_cart (azi, ele, x, y, z);
|
||||
|
||||
/* xp,yp,zp use a (0,0) == center and 2.0 unit dimension. so convert
|
||||
back to (0,0) and 1.0 unit dimension
|
||||
*/
|
||||
|
||||
x /= 2.0;
|
||||
y /= 2.0;
|
||||
z /= 2.0;
|
||||
|
||||
/* and now convert back to (0,0) == upper left corner */
|
||||
|
||||
x += 0.5;
|
||||
y += 0.5;
|
||||
z += 0.5;
|
||||
/* XXX z-axis not handled - 2D for now */
|
||||
}
|
||||
|
||||
void
|
||||
Panner2d::clamp_to_circle (double& x, double& y)
|
||||
{
|
||||
double azi, ele;
|
||||
|
||||
cart_to_azi_ele (x, y, azi, ele);
|
||||
azi_ele_to_cart (azi, ele, x, y);
|
||||
double z = 0.0;
|
||||
|
||||
PBD::cart_to_azi_ele (x, y, z, azi, ele);
|
||||
PBD::azi_ele_to_cart (azi, ele, x, y, z);
|
||||
}
|
||||
|
||||
void
|
||||
|
@ -631,24 +586,6 @@ Panner2d::toggle_bypass ()
|
|||
panner->set_bypassed (!panner->bypassed());
|
||||
}
|
||||
|
||||
void
|
||||
Panner2d::allow_x_motion (bool yn)
|
||||
{
|
||||
allow_x = yn;
|
||||
}
|
||||
|
||||
void
|
||||
Panner2d::allow_target_motion (bool yn)
|
||||
{
|
||||
allow_target = yn;
|
||||
}
|
||||
|
||||
void
|
||||
Panner2d::allow_y_motion (bool yn)
|
||||
{
|
||||
allow_y = yn;
|
||||
}
|
||||
|
||||
Panner2dWindow::Panner2dWindow (boost::shared_ptr<Panner> p, int32_t h, uint32_t inputs)
|
||||
: widget (p, h)
|
||||
, reset_button (_("Reset"))
|
||||
|
@ -694,10 +631,11 @@ Panner2dWindow::reset (uint32_t n_inputs)
|
|||
{
|
||||
widget.reset (n_inputs);
|
||||
|
||||
#if 0
|
||||
while (spinners.size() < n_inputs) {
|
||||
spinners.push_back (new Gtk::SpinButton (widget.azimuth (spinners.size())));
|
||||
spinner_box.pack_start (*spinners.back(), false, false);
|
||||
spinners.back()->set_digits (4);
|
||||
// spinners.push_back (new Gtk::SpinButton (widget.azimuth (spinners.size())));
|
||||
//spinner_box.pack_start (*spinners.back(), false, false);
|
||||
//spinners.back()->set_digits (4);
|
||||
spinners.back()->show ();
|
||||
}
|
||||
|
||||
|
@ -706,4 +644,5 @@ Panner2dWindow::reset (uint32_t n_inputs)
|
|||
delete spinners.back();
|
||||
spinners.erase (--spinners.end());
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
|
|
@ -32,6 +32,8 @@
|
|||
#include <gtkmm/spinbutton.h>
|
||||
#include <gtkmm/adjustment.h>
|
||||
|
||||
#include "pbd/cartesian.h"
|
||||
|
||||
namespace ARDOUR {
|
||||
class Panner;
|
||||
}
|
||||
|
@ -53,22 +55,21 @@ class Panner2d : public Gtk::DrawingArea
|
|||
Panner2d (boost::shared_ptr<ARDOUR::Panner>, int32_t height);
|
||||
~Panner2d ();
|
||||
|
||||
void allow_x_motion(bool);
|
||||
void allow_y_motion(bool);
|
||||
void allow_target_motion (bool);
|
||||
|
||||
int add_target (float x, float y);
|
||||
int add_puck (const char* text, float x, float y);
|
||||
void move_puck (int, float x, float y);
|
||||
int add_target (const PBD::AngularVector&);
|
||||
int add_puck (const char* text, const PBD::AngularVector&);
|
||||
void move_puck (int which, const PBD::AngularVector&);
|
||||
void reset (uint32_t n_inputs);
|
||||
|
||||
Gtk::Adjustment& azimuth (uint32_t which);
|
||||
|
||||
boost::shared_ptr<ARDOUR::Panner> get_panner() const { return panner; }
|
||||
|
||||
sigc::signal<void,int> PuckMoved;
|
||||
sigc::signal<void,int> TargetMoved;
|
||||
|
||||
void cart_to_gtk (PBD::CartesianVector&) const;
|
||||
void gtk_to_cart (PBD::CartesianVector&) const;
|
||||
|
||||
protected:
|
||||
bool on_expose_event (GdkEventExpose *);
|
||||
bool on_button_press_event (GdkEventButton *);
|
||||
|
@ -79,25 +80,23 @@ class Panner2d : public Gtk::DrawingArea
|
|||
private:
|
||||
class Target {
|
||||
public:
|
||||
Gtk::Adjustment x;
|
||||
Gtk::Adjustment y;
|
||||
Gtk::Adjustment azimuth;
|
||||
bool visible;
|
||||
std::string text;
|
||||
|
||||
Target (float xa, float ya, const char* txt = 0);
|
||||
~Target ();
|
||||
|
||||
void set_text (const char*);
|
||||
void set_selected (bool yn) {
|
||||
_selected = yn;
|
||||
}
|
||||
bool selected() const {
|
||||
return _selected;
|
||||
}
|
||||
|
||||
PBD::AngularVector position;
|
||||
bool visible;
|
||||
std::string text;
|
||||
|
||||
Target (const PBD::AngularVector&, const char* txt = 0);
|
||||
~Target ();
|
||||
|
||||
void set_text (const char*);
|
||||
void set_selected (bool yn) {
|
||||
_selected = yn;
|
||||
}
|
||||
bool selected() const {
|
||||
return _selected;
|
||||
}
|
||||
|
||||
private:
|
||||
bool _selected;
|
||||
bool _selected;
|
||||
};
|
||||
|
||||
boost::shared_ptr<ARDOUR::Panner> panner;
|
||||
|
@ -108,14 +107,12 @@ class Panner2d : public Gtk::DrawingArea
|
|||
Targets pucks;
|
||||
|
||||
Target *drag_target;
|
||||
int drag_x;
|
||||
int drag_y;
|
||||
int drag_x;
|
||||
int drag_y;
|
||||
int drag_index;
|
||||
bool allow_x;
|
||||
bool allow_y;
|
||||
bool allow_target;
|
||||
int width;
|
||||
int height;
|
||||
bool allow_target;
|
||||
int width;
|
||||
int height;
|
||||
|
||||
bool bypassflag;
|
||||
|
||||
|
@ -133,12 +130,6 @@ class Panner2d : public Gtk::DrawingArea
|
|||
PBD::ScopedConnection state_connection;
|
||||
PBD::ScopedConnection change_connection;
|
||||
|
||||
/* cartesian coordinates in GTK units ; return azimuth & elevation in degrees */
|
||||
void cart_to_azi_ele (double x, double y, double& azi, double& eli);
|
||||
|
||||
/* azimuth & elevation in degrees; return cartesian coordinates in GTK units */
|
||||
void azi_ele_to_cart (double azi, double eli, double& x, double& y);
|
||||
|
||||
/* cartesian coordinates in GTK units ; adjust to same but on a circle of radius 1.0
|
||||
and centered in the middle of our area
|
||||
*/
|
||||
|
|
|
@ -666,23 +666,18 @@ PannerUI::pan_value_changed (uint32_t which)
|
|||
|
||||
if (twod_panner) {
|
||||
|
||||
float x;
|
||||
float y;
|
||||
_panner->streampanner(which).get_position (x, y);
|
||||
|
||||
in_pan_update = true;
|
||||
twod_panner->move_puck (which, x, y);
|
||||
twod_panner->move_puck (which, _panner->streampanner(which).get_position());
|
||||
in_pan_update = false;
|
||||
|
||||
} else if (_panner->npanners() > 0 && which < _panner->npanners()) {
|
||||
float xpos;
|
||||
float val = pan_adjustments[which]->get_value ();
|
||||
AngularVector model = _panner->streampanner(which).get_position();
|
||||
double fract = pan_adjustments[which]->get_value();
|
||||
AngularVector view (BaseStereoPanner::lr_fract_to_azimuth (fract), 0.0);
|
||||
|
||||
_panner->streampanner(which).get_position (xpos);
|
||||
|
||||
if (!Panner::equivalent (val, xpos)) {
|
||||
if (!Panner::equivalent (model, view)) {
|
||||
in_pan_update = true;
|
||||
pan_adjustments[which]->set_value (xpos);
|
||||
pan_adjustments[which]->set_value (BaseStereoPanner::azimuth_to_lr_fract (model.azi));
|
||||
in_pan_update = false;
|
||||
}
|
||||
}
|
||||
|
@ -701,7 +696,6 @@ PannerUI::update_pan_bars (bool only_if_aplay)
|
|||
*/
|
||||
|
||||
for (i = pan_adjustments.begin(), n = 0; i != pan_adjustments.end(); ++i, ++n) {
|
||||
float xpos, val;
|
||||
|
||||
if (only_if_aplay) {
|
||||
boost::shared_ptr<AutomationList> alist (_panner->streampanner(n).pan_control()->alist());
|
||||
|
@ -711,11 +705,12 @@ PannerUI::update_pan_bars (bool only_if_aplay)
|
|||
}
|
||||
}
|
||||
|
||||
_panner->streampanner(n).get_effective_position (xpos);
|
||||
val = (*i)->get_value ();
|
||||
AngularVector model = _panner->streampanner(n).get_effective_position();
|
||||
double fract = (*i)->get_value();
|
||||
AngularVector view (BaseStereoPanner::lr_fract_to_azimuth (fract), 0.0);
|
||||
|
||||
if (!Panner::equivalent (val, xpos)) {
|
||||
(*i)->set_value (xpos);
|
||||
if (!Panner::equivalent (model, view)) {
|
||||
(*i)->set_value (BaseStereoPanner::azimuth_to_lr_fract (model.azi));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -28,6 +28,7 @@
|
|||
|
||||
#include "pbd/stateful.h"
|
||||
#include "pbd/controllable.h"
|
||||
#include "pbd/cartesian.h"
|
||||
|
||||
#include "ardour/types.h"
|
||||
#include "ardour/automation_control.h"
|
||||
|
@ -39,6 +40,7 @@ class Session;
|
|||
class Panner;
|
||||
class BufferSet;
|
||||
class AudioBuffer;
|
||||
class Speakers;
|
||||
|
||||
class StreamPanner : public PBD::Stateful
|
||||
{
|
||||
|
@ -49,17 +51,10 @@ class StreamPanner : public PBD::Stateful
|
|||
void set_muted (bool yn);
|
||||
bool muted() const { return _muted; }
|
||||
|
||||
void set_position (float x, bool link_call = false);
|
||||
void set_position (float x, float y, bool link_call = false);
|
||||
void set_position (float x, float y, float z, bool link_call = false);
|
||||
|
||||
void get_position (float& xpos) const { xpos = _x; }
|
||||
void get_position (float& xpos, float& ypos) const { xpos = _x; ypos = _y; }
|
||||
void get_position (float& xpos, float& ypos, float& zpos) const { xpos = _x; ypos = _y; zpos = _z; }
|
||||
|
||||
void get_effective_position (float& xpos) const { xpos = effective_x; }
|
||||
void get_effective_position (float& xpos, float& ypos) const { xpos = effective_x; ypos = effective_y; }
|
||||
void get_effective_position (float& xpos, float& ypos, float& zpos) const { xpos = effective_x; ypos = effective_y; zpos = effective_z; }
|
||||
const PBD::AngularVector& get_position() const { return _angles; }
|
||||
const PBD::AngularVector& get_effective_position() const { return _effective_angles; }
|
||||
void set_position (const PBD::AngularVector&, bool link_call = false);
|
||||
void set_diffusion (double);
|
||||
|
||||
void distribute (AudioBuffer &, BufferSet &, gain_t, nframes_t);
|
||||
void distribute_automated (AudioBuffer &, BufferSet &, nframes_t, nframes_t, nframes_t, pan_t **);
|
||||
|
@ -80,7 +75,7 @@ class StreamPanner : public PBD::Stateful
|
|||
|
||||
boost::shared_ptr<AutomationControl> pan_control() { return _control; }
|
||||
|
||||
PBD::Signal0<void> Changed; /* for position */
|
||||
PBD::Signal0<void> Changed; /* for position or diffusion */
|
||||
PBD::Signal0<void> StateChanged; /* for mute, mono */
|
||||
|
||||
int set_state (const XMLNode&, int version);
|
||||
|
@ -97,17 +92,9 @@ class StreamPanner : public PBD::Stateful
|
|||
|
||||
void set_mono (bool);
|
||||
|
||||
float _x;
|
||||
float _y;
|
||||
float _z;
|
||||
|
||||
/* these are for automation. they store the last value
|
||||
used by the most recent process() cycle.
|
||||
*/
|
||||
|
||||
float effective_x;
|
||||
float effective_y;
|
||||
float effective_z;
|
||||
PBD::AngularVector _angles;
|
||||
PBD::AngularVector _effective_angles;
|
||||
double _diffusion;
|
||||
|
||||
bool _muted;
|
||||
bool _mono;
|
||||
|
@ -116,7 +103,7 @@ class StreamPanner : public PBD::Stateful
|
|||
|
||||
void add_state (XMLNode&);
|
||||
|
||||
/* Update internal parameters based on _x, _y and _z */
|
||||
/* Update internal parameters based on this.angles */
|
||||
virtual void update () = 0;
|
||||
};
|
||||
|
||||
|
@ -134,6 +121,19 @@ class BaseStereoPanner : public StreamPanner
|
|||
|
||||
void do_distribute (AudioBuffer& src, BufferSet& obufs, gain_t gain_coeff, nframes_t nframes);
|
||||
|
||||
static double azimuth_to_lr_fract (double azi) {
|
||||
/* 180.0 degrees=> left => 0.0 */
|
||||
/* 0.0 degrees => right => 1.0 */
|
||||
return 1.0 - (azi/180.0);
|
||||
}
|
||||
|
||||
static double lr_fract_to_azimuth (double fract) {
|
||||
/* fract = 0.0 => degrees = 180.0 => left */
|
||||
/* fract = 1.0 => degrees = 0.0 => right */
|
||||
return 180.0 - (fract * 180.0);
|
||||
}
|
||||
|
||||
|
||||
/* old school automation loading */
|
||||
|
||||
int load (std::istream&, std::string path, uint32_t&);
|
||||
|
@ -159,7 +159,7 @@ class EqualPowerStereoPanner : public BaseStereoPanner
|
|||
void get_current_coefficients (pan_t*) const;
|
||||
void get_desired_coefficients (pan_t*) const;
|
||||
|
||||
static StreamPanner* factory (Panner&, Evoral::Parameter param);
|
||||
static StreamPanner* factory (Panner&, Evoral::Parameter param, Speakers&);
|
||||
static std::string name;
|
||||
|
||||
XMLNode& state (bool full_state);
|
||||
|
@ -170,32 +170,6 @@ class EqualPowerStereoPanner : public BaseStereoPanner
|
|||
void update ();
|
||||
};
|
||||
|
||||
class Multi2dPanner : public StreamPanner
|
||||
{
|
||||
public:
|
||||
Multi2dPanner (Panner& parent, Evoral::Parameter);
|
||||
~Multi2dPanner ();
|
||||
|
||||
void do_distribute (AudioBuffer& src, BufferSet& obufs, gain_t gain_coeff, nframes_t nframes);
|
||||
void do_distribute_automated (AudioBuffer& src, BufferSet& obufs,
|
||||
nframes_t start, nframes_t end, nframes_t nframes, pan_t** buffers);
|
||||
|
||||
static StreamPanner* factory (Panner&, Evoral::Parameter);
|
||||
static std::string name;
|
||||
|
||||
XMLNode& state (bool full_state);
|
||||
XMLNode& get_state (void);
|
||||
int set_state (const XMLNode&, int version);
|
||||
|
||||
/* old school automation loading */
|
||||
|
||||
int load (std::istream&, std::string path, uint32_t&);
|
||||
|
||||
private:
|
||||
void update ();
|
||||
};
|
||||
|
||||
|
||||
/** Class to pan from some number of inputs to some number of outputs.
|
||||
* This class has a number of StreamPanners, one for each input.
|
||||
*/
|
||||
|
@ -203,14 +177,12 @@ class Panner : public SessionObject, public Automatable
|
|||
{
|
||||
public:
|
||||
struct Output {
|
||||
float x;
|
||||
float y;
|
||||
float z;
|
||||
PBD::AngularVector position;
|
||||
pan_t current_pan;
|
||||
pan_t desired_pan;
|
||||
|
||||
Output (float xp, float yp, float zp = 0.0)
|
||||
: x (xp), y (yp), z (zp), current_pan (0), desired_pan (0) {}
|
||||
Output (const PBD::AngularVector& a)
|
||||
: position (a), current_pan (0), desired_pan (0) {}
|
||||
|
||||
};
|
||||
|
||||
|
@ -249,6 +221,10 @@ public:
|
|||
static bool equivalent (pan_t a, pan_t b) {
|
||||
return fabsf (a - b) < 0.002; // about 1 degree of arc for a stereo panner
|
||||
}
|
||||
static bool equivalent (const PBD::AngularVector& a, const PBD::AngularVector& b) {
|
||||
/* XXX azimuth only, at present */
|
||||
return fabs (a.azi - b.azi) < 1.0;
|
||||
}
|
||||
|
||||
void move_output (uint32_t, float x, float y);
|
||||
uint32_t nouts() const { return outputs.size(); }
|
||||
|
@ -274,9 +250,7 @@ public:
|
|||
|
||||
/* only StreamPanner should call these */
|
||||
|
||||
void set_position (float x, StreamPanner& orig);
|
||||
void set_position (float x, float y, StreamPanner& orig);
|
||||
void set_position (float x, float y, float z, StreamPanner& orig);
|
||||
void set_position (const PBD::AngularVector&, StreamPanner& orig);
|
||||
|
||||
/* old school automation */
|
||||
|
||||
|
|
|
@ -52,7 +52,7 @@
|
|||
#include "ardour/location.h"
|
||||
#include "ardour/timecode.h"
|
||||
#include "ardour/interpolation.h"
|
||||
#include "ardour/vbap_speakers.h"
|
||||
#include "ardour/speakers.h"
|
||||
|
||||
#ifdef HAVE_JACK_SESSION
|
||||
#include <jack/session.h>
|
||||
|
@ -118,6 +118,7 @@ class SessionMetadata;
|
|||
class SessionPlaylists;
|
||||
class Slave;
|
||||
class Source;
|
||||
class Speakers;
|
||||
class TempoMap;
|
||||
class VSTPlugin;
|
||||
class Graph;
|
||||
|
@ -728,7 +729,7 @@ class Session : public PBD::StatefulDestructible, public PBD::ScopedConnectionLi
|
|||
|
||||
/* Speakers */
|
||||
|
||||
VBAPSpeakers& get_speakers ();
|
||||
Speakers& get_speakers ();
|
||||
|
||||
/* Controllables */
|
||||
|
||||
|
@ -1470,7 +1471,7 @@ class Session : public PBD::StatefulDestructible, public PBD::ScopedConnectionLi
|
|||
void start_time_changed (framepos_t);
|
||||
void end_time_changed (framepos_t);
|
||||
|
||||
VBAPSpeakers* _speakers;
|
||||
Speakers* _speakers;
|
||||
};
|
||||
|
||||
} // namespace ARDOUR
|
||||
|
|
|
@ -0,0 +1,44 @@
|
|||
/*
|
||||
Copyright (C) 2010 Paul Davis
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*/
|
||||
|
||||
#ifndef __libardour_speaker_h__
|
||||
#define __libardour_speaker_h__
|
||||
|
||||
#include "pbd/cartesian.h"
|
||||
|
||||
namespace ARDOUR {
|
||||
|
||||
class Speaker {
|
||||
public:
|
||||
Speaker (int, const PBD::AngularVector& position);
|
||||
|
||||
void move (const PBD::AngularVector& new_position);
|
||||
|
||||
const PBD::CartesianVector& coords() const { return _coords; }
|
||||
const PBD::AngularVector& angles() const { return _angles; }
|
||||
|
||||
int id;
|
||||
|
||||
private:
|
||||
PBD::CartesianVector _coords;
|
||||
PBD::AngularVector _angles;
|
||||
};
|
||||
|
||||
} /* namespace */
|
||||
|
||||
#endif /* __libardour_speaker_h__ */
|
|
@ -0,0 +1,55 @@
|
|||
/*
|
||||
Copyright (C) 2010 Paul Davis
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*/
|
||||
|
||||
#ifndef __libardour_speakers_h__
|
||||
#define __libardour_speakers_h__
|
||||
|
||||
#include <vector>
|
||||
#include <iostream>
|
||||
|
||||
#include <pbd/signals.h>
|
||||
|
||||
#include "ardour/speaker.h"
|
||||
|
||||
namespace ARDOUR {
|
||||
|
||||
class Speakers {
|
||||
public:
|
||||
Speakers ();
|
||||
virtual ~Speakers ();
|
||||
|
||||
virtual int add_speaker (const PBD::AngularVector&);
|
||||
virtual void remove_speaker (int id);
|
||||
virtual void move_speaker (int id, const PBD::AngularVector& new_position);
|
||||
virtual void clear_speakers ();
|
||||
|
||||
std::vector<Speaker>& speakers() { return _speakers; }
|
||||
|
||||
void dump_speakers (std::ostream&);
|
||||
|
||||
PBD::Signal0<void> Changed;
|
||||
|
||||
protected:
|
||||
std::vector<Speaker> _speakers;
|
||||
|
||||
virtual void update () {}
|
||||
};
|
||||
|
||||
} /* namespace */
|
||||
|
||||
#endif /* __libardour_speakers_h__ */
|
|
@ -22,19 +22,21 @@
|
|||
#include <string>
|
||||
#include <map>
|
||||
|
||||
#include <pbd/signals.h>
|
||||
|
||||
#include "ardour/panner.h"
|
||||
#include "ardour/vbap_speakers.h"
|
||||
|
||||
namespace ARDOUR {
|
||||
|
||||
class VBAPSpeakers;
|
||||
class Speakers;
|
||||
|
||||
class VBAPanner : public StreamPanner {
|
||||
public:
|
||||
VBAPanner (Panner& parent, Evoral::Parameter param, VBAPSpeakers& s);
|
||||
VBAPanner (Panner& parent, Evoral::Parameter param, Speakers& s);
|
||||
~VBAPanner ();
|
||||
|
||||
static StreamPanner* factory (Panner& parent, Evoral::Parameter param, Speakers& s);
|
||||
static std::string name;
|
||||
|
||||
void do_distribute (AudioBuffer&, BufferSet& obufs, gain_t gain_coeff, nframes_t nframes);
|
||||
void do_distribute_automated (AudioBuffer& src, BufferSet& obufs,
|
||||
nframes_t start, nframes_t end, nframes_t nframes, pan_t** buffers);
|
||||
|
@ -58,8 +60,6 @@ class VBAPanner : public StreamPanner {
|
|||
int outputs[3];
|
||||
int desired_outputs[3];
|
||||
|
||||
PBD::ScopedConnection speaker_connection;
|
||||
|
||||
VBAPSpeakers& _speakers;
|
||||
|
||||
void compute_gains (double g[3], int ls[3], int azi, int ele);
|
||||
|
|
|
@ -20,67 +20,45 @@
|
|||
#define __libardour_vbap_speakers_h__
|
||||
|
||||
#include <string>
|
||||
#include <map>
|
||||
#include <vector>
|
||||
|
||||
#include <boost/utility.hpp>
|
||||
|
||||
#include <pbd/signals.h>
|
||||
|
||||
#include "ardour/panner.h"
|
||||
#include "ardour/speakers.h"
|
||||
|
||||
namespace ARDOUR {
|
||||
|
||||
class VBAPSpeakers {
|
||||
class Speakers;
|
||||
|
||||
class VBAPSpeakers : public boost::noncopyable {
|
||||
public:
|
||||
struct cart_vec {
|
||||
double x;
|
||||
double y;
|
||||
double z;
|
||||
};
|
||||
|
||||
struct ang_vec {
|
||||
double azi;
|
||||
double ele;
|
||||
double length;
|
||||
};
|
||||
|
||||
static const int MAX_TRIPLET_AMOUNT = 60;
|
||||
typedef std::vector<double> dvector;
|
||||
|
||||
VBAPSpeakers ();
|
||||
~VBAPSpeakers ();
|
||||
|
||||
int add_speaker (double direction, double elevation = 0.0);
|
||||
void remove_speaker (int id);
|
||||
void move_speaker (int id, double direction, double elevation = 0.0);
|
||||
void clear_speakers ();
|
||||
|
||||
const dvector matrix (int tuple) const { return _matrices[tuple]; }
|
||||
int speaker_for_tuple (int tuple, int which) const { return _speaker_tuples[tuple][which]; }
|
||||
|
||||
int n_tuples () const { return _matrices.size(); }
|
||||
int dimension() const { return _dimension; }
|
||||
|
||||
static void angle_to_cart(ang_vec *from, cart_vec *to);
|
||||
static VBAPSpeakers& instance (Speakers&);
|
||||
|
||||
PBD::Signal0<void> Changed;
|
||||
~VBAPSpeakers ();
|
||||
|
||||
private:
|
||||
static VBAPSpeakers* _instance;
|
||||
static const double MIN_VOL_P_SIDE_LGTH = 0.01;
|
||||
int _dimension;
|
||||
std::vector<Speaker>& _speakers;
|
||||
PBD::ScopedConnection speaker_connection;
|
||||
|
||||
/* A struct for a loudspeaker instance */
|
||||
struct Speaker {
|
||||
int id;
|
||||
cart_vec coords;
|
||||
ang_vec angles;
|
||||
|
||||
Speaker (int, double azimuth, double elevation);
|
||||
|
||||
void move (double azimuth, double elevation);
|
||||
};
|
||||
VBAPSpeakers (Speakers&);
|
||||
|
||||
struct azimuth_sorter {
|
||||
bool operator() (const Speaker& s1, const Speaker& s2) {
|
||||
return s1.angles.azi < s2.angles.azi;
|
||||
return s1.angles().azi < s2.angles().azi;
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -96,7 +74,6 @@ class VBAPSpeakers {
|
|||
tmatrix() : dvector (3, 0.0) {}
|
||||
};
|
||||
|
||||
std::vector<Speaker> _speakers;
|
||||
std::vector<dvector> _matrices; /* holds matrices for a given speaker combinations */
|
||||
std::vector<tmatrix> _speaker_tuples; /* holds speakers IDs for a given combination */
|
||||
|
||||
|
@ -107,11 +84,11 @@ class VBAPSpeakers {
|
|||
struct ls_triplet_chain *next;
|
||||
};
|
||||
|
||||
static float vec_angle(cart_vec v1, cart_vec v2);
|
||||
static float vec_length(cart_vec v1);
|
||||
static float vec_prod(cart_vec v1, cart_vec v2);
|
||||
static float vec_angle(PBD::CartesianVector v1, PBD::CartesianVector v2);
|
||||
static float vec_length(PBD::CartesianVector v1);
|
||||
static float vec_prod(PBD::CartesianVector v1, PBD::CartesianVector v2);
|
||||
static float vol_p_side_lgth(int i, int j,int k, const std::vector<Speaker>&);
|
||||
static void cross_prod(cart_vec v1,cart_vec v2, cart_vec *res);
|
||||
static void cross_prod(PBD::CartesianVector v1,PBD::CartesianVector v2, PBD::CartesianVector *res);
|
||||
|
||||
void update ();
|
||||
int any_ls_inside_triplet (int a, int b, int c);
|
||||
|
@ -123,7 +100,6 @@ class VBAPSpeakers {
|
|||
void sort_2D_lss (int* sorted_lss);
|
||||
int calc_2D_inv_tmatrix (double azi1,double azi2, double* inv_mat);
|
||||
|
||||
void dump_speakers (std::ostream&);
|
||||
};
|
||||
|
||||
} /* namespace */
|
||||
|
|
|
@ -61,15 +61,14 @@ using namespace PBD;
|
|||
float Panner::current_automation_version_number = 1.0;
|
||||
|
||||
string EqualPowerStereoPanner::name = "Equal Power Stereo";
|
||||
string Multi2dPanner::name = "Multiple (2D)";
|
||||
|
||||
/* this is a default mapper of control values to a pan position
|
||||
others can be imagined.
|
||||
*/
|
||||
|
||||
static pan_t direct_control_to_pan (double fract)
|
||||
static double direct_control_to_stereo_pan (double fract)
|
||||
{
|
||||
return fract;
|
||||
return BaseStereoPanner::lr_fract_to_azimuth (fract);
|
||||
}
|
||||
|
||||
StreamPanner::StreamPanner (Panner& p, Evoral::Parameter param)
|
||||
|
@ -82,10 +81,6 @@ StreamPanner::StreamPanner (Panner& p, Evoral::Parameter param)
|
|||
|
||||
/* get our AutomationControl from our parent Panner, creating it if required */
|
||||
_control = boost::dynamic_pointer_cast<AutomationControl> (parent.control (param, true));
|
||||
|
||||
_x = 0.5;
|
||||
_y = 0.5;
|
||||
_z = 0.5;
|
||||
}
|
||||
|
||||
StreamPanner::~StreamPanner ()
|
||||
|
@ -104,7 +99,7 @@ StreamPanner::set_mono (bool yn)
|
|||
void
|
||||
Panner::PanControllable::set_value (double val)
|
||||
{
|
||||
panner.streampanner (parameter().id()).set_position (direct_control_to_pan (val));
|
||||
panner.streampanner (parameter().id()).set_position (AngularVector (direct_control_to_stereo_pan (val), 0.0));
|
||||
AutomationControl::set_value(val);
|
||||
}
|
||||
|
||||
|
@ -124,47 +119,14 @@ StreamPanner::set_muted (bool yn)
|
|||
}
|
||||
|
||||
void
|
||||
StreamPanner::set_position (float xpos, bool link_call)
|
||||
StreamPanner::set_position (const AngularVector& av, bool link_call)
|
||||
{
|
||||
if (!link_call && parent.linked()) {
|
||||
parent.set_position (xpos, *this);
|
||||
parent.set_position (av, *this);
|
||||
}
|
||||
|
||||
if (_x != xpos) {
|
||||
_x = xpos;
|
||||
update ();
|
||||
Changed ();
|
||||
_control->Changed ();
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
StreamPanner::set_position (float xpos, float ypos, bool link_call)
|
||||
{
|
||||
if (!link_call && parent.linked()) {
|
||||
parent.set_position (xpos, ypos, *this);
|
||||
}
|
||||
|
||||
if (_x != xpos || _y != ypos) {
|
||||
_x = xpos;
|
||||
_y = ypos;
|
||||
update ();
|
||||
Changed ();
|
||||
_control->Changed ();
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
StreamPanner::set_position (float xpos, float ypos, float zpos, bool link_call)
|
||||
{
|
||||
if (!link_call && parent.linked()) {
|
||||
parent.set_position (xpos, ypos, zpos, *this);
|
||||
}
|
||||
|
||||
if (_x != xpos || _y != ypos || _z != zpos) {
|
||||
_x = xpos;
|
||||
_y = ypos;
|
||||
_z = zpos;
|
||||
if (_angles != av) {
|
||||
_angles = av;
|
||||
update ();
|
||||
Changed ();
|
||||
_control->Changed ();
|
||||
|
@ -431,10 +393,12 @@ EqualPowerStereoPanner::update ()
|
|||
overhead.
|
||||
*/
|
||||
|
||||
/* x == 0 => hard left
|
||||
x == 1 => hard right
|
||||
/* x == 0 => hard left = 180.0 degrees
|
||||
x == 1 => hard right = 0.0 degrees
|
||||
*/
|
||||
|
||||
double _x = BaseStereoPanner::azimuth_to_lr_fract (_angles.azi);
|
||||
|
||||
float const panR = _x;
|
||||
float const panL = 1 - panR;
|
||||
|
||||
|
@ -444,7 +408,7 @@ EqualPowerStereoPanner::update ()
|
|||
desired_left = panL * (scale * panL + 1.0f - scale);
|
||||
desired_right = panR * (scale * panR + 1.0f - scale);
|
||||
|
||||
effective_x = _x;
|
||||
_effective_angles = _angles;
|
||||
//_control->set_value(x);
|
||||
}
|
||||
|
||||
|
@ -472,7 +436,7 @@ EqualPowerStereoPanner::do_distribute_automated (AudioBuffer& srcbuf, BufferSet&
|
|||
/* store effective pan position. do this even if we are muted */
|
||||
|
||||
if (nframes > 0) {
|
||||
effective_x = buffers[0][nframes-1];
|
||||
_effective_angles.azi = BaseStereoPanner::lr_fract_to_azimuth (buffers[0][nframes-1]);
|
||||
}
|
||||
|
||||
if (_muted) {
|
||||
|
@ -519,7 +483,7 @@ EqualPowerStereoPanner::do_distribute_automated (AudioBuffer& srcbuf, BufferSet&
|
|||
}
|
||||
|
||||
StreamPanner*
|
||||
EqualPowerStereoPanner::factory (Panner& parent, Evoral::Parameter param)
|
||||
EqualPowerStereoPanner::factory (Panner& parent, Evoral::Parameter param, Speakers& /* ignored */)
|
||||
{
|
||||
return new EqualPowerStereoPanner (parent, param);
|
||||
}
|
||||
|
@ -537,8 +501,8 @@ EqualPowerStereoPanner::state (bool /*full_state*/)
|
|||
char buf[64];
|
||||
LocaleGuard lg (X_("POSIX"));
|
||||
|
||||
snprintf (buf, sizeof (buf), "%.12g", _x);
|
||||
root->add_property (X_("x"), buf);
|
||||
snprintf (buf, sizeof (buf), "%.12g", _angles.azi);
|
||||
root->add_property (X_("azimuth"), buf);
|
||||
root->add_property (X_("type"), EqualPowerStereoPanner::name);
|
||||
|
||||
// XXX: dont save automation here... its part of the automatable panner now.
|
||||
|
@ -556,10 +520,15 @@ EqualPowerStereoPanner::set_state (const XMLNode& node, int version)
|
|||
const XMLProperty* prop;
|
||||
LocaleGuard lg (X_("POSIX"));
|
||||
|
||||
if ((prop = node.property (X_("x")))) {
|
||||
const float pos = atof (prop->value().c_str());
|
||||
set_position (pos, true);
|
||||
}
|
||||
if ((prop = node.property (X_("azimuth")))) {
|
||||
AngularVector a (atof (prop->value().c_str()), 0.0);
|
||||
set_position (a, true);
|
||||
} else if ((prop = node.property (X_("x")))) {
|
||||
/* old school cartesian positioning */
|
||||
AngularVector a;
|
||||
a.azi = BaseStereoPanner::lr_fract_to_azimuth (atof (prop->value().c_str()));
|
||||
set_position (a, true);
|
||||
}
|
||||
|
||||
StreamPanner::set_state (node, version);
|
||||
|
||||
|
@ -575,7 +544,8 @@ EqualPowerStereoPanner::set_state (const XMLNode& node, int version)
|
|||
_control->alist()->set_state (*((*iter)->children().front()), version);
|
||||
|
||||
if (_control->alist()->automation_state() != Off) {
|
||||
set_position (_control->list()->eval (parent.session().transport_frame()));
|
||||
double degrees = BaseStereoPanner::lr_fract_to_azimuth (_control->list()->eval (parent.session().transport_frame()));
|
||||
set_position (AngularVector (degrees, 0.0));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -583,190 +553,6 @@ EqualPowerStereoPanner::set_state (const XMLNode& node, int version)
|
|||
return 0;
|
||||
}
|
||||
|
||||
/*----------------------------------------------------------------------*/
|
||||
|
||||
Multi2dPanner::Multi2dPanner (Panner& p, Evoral::Parameter param)
|
||||
: StreamPanner (p, param)
|
||||
{
|
||||
update ();
|
||||
}
|
||||
|
||||
Multi2dPanner::~Multi2dPanner ()
|
||||
{
|
||||
}
|
||||
|
||||
void
|
||||
Multi2dPanner::update ()
|
||||
{
|
||||
static const float BIAS = FLT_MIN;
|
||||
uint32_t i;
|
||||
uint32_t const nouts = parent.nouts ();
|
||||
float dsq[nouts];
|
||||
float f, fr;
|
||||
vector<pan_t> pans;
|
||||
|
||||
f = 0.0f;
|
||||
|
||||
for (i = 0; i < nouts; i++) {
|
||||
dsq[i] = ((_x - parent.output(i).x) * (_x - parent.output(i).x) + (_y - parent.output(i).y) * (_y - parent.output(i).y) + BIAS);
|
||||
if (dsq[i] < 0.0) {
|
||||
dsq[i] = 0.0;
|
||||
}
|
||||
f += dsq[i] * dsq[i];
|
||||
}
|
||||
#ifdef __APPLE__
|
||||
// terrible hack to support OSX < 10.3.9 builds
|
||||
fr = (float) (1.0 / sqrt((double)f));
|
||||
#else
|
||||
fr = 1.0 / sqrtf(f);
|
||||
#endif
|
||||
for (i = 0; i < nouts; ++i) {
|
||||
parent.output(i).desired_pan = 1.0f - (dsq[i] * fr);
|
||||
}
|
||||
|
||||
effective_x = _x;
|
||||
}
|
||||
|
||||
void
|
||||
Multi2dPanner::do_distribute (AudioBuffer& srcbuf, BufferSet& obufs, gain_t gain_coeff, nframes_t nframes)
|
||||
{
|
||||
Sample* dst;
|
||||
pan_t pan;
|
||||
|
||||
if (_muted) {
|
||||
return;
|
||||
}
|
||||
|
||||
Sample* const src = srcbuf.data();
|
||||
|
||||
uint32_t const N = parent.nouts ();
|
||||
for (uint32_t n = 0; n < N; ++n) {
|
||||
Panner::Output& o = parent.output (n);
|
||||
|
||||
dst = obufs.get_audio(n).data();
|
||||
|
||||
#ifdef CAN_INTERP
|
||||
if (fabsf ((delta = (left_interp - desired_left))) > 0.002) { // about 1 degree of arc
|
||||
|
||||
/* interpolate over 64 frames or nframes, whichever is smaller */
|
||||
|
||||
nframes_t limit = min ((nframes_t)64, nframes);
|
||||
nframes_t n;
|
||||
|
||||
delta = -(delta / (float) (limit));
|
||||
|
||||
for (n = 0; n < limit; n++) {
|
||||
left_interp = left_interp + delta;
|
||||
left = left_interp + 0.9 * (left - left_interp);
|
||||
dst[n] += src[n] * left * gain_coeff;
|
||||
}
|
||||
|
||||
pan = left * gain_coeff;
|
||||
mix_buffers_with_gain(dst+n,src+n,nframes-n,pan);
|
||||
|
||||
} else {
|
||||
|
||||
#else
|
||||
pan = o.desired_pan;
|
||||
|
||||
if ((pan *= gain_coeff) != 1.0f) {
|
||||
|
||||
if (pan != 0.0f) {
|
||||
mix_buffers_with_gain(dst,src,nframes,pan);
|
||||
}
|
||||
|
||||
} else {
|
||||
mix_buffers_no_gain(dst,src,nframes);
|
||||
}
|
||||
#endif
|
||||
#ifdef CAN_INTERP
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
void
|
||||
Multi2dPanner::do_distribute_automated (AudioBuffer& /*src*/, BufferSet& /*obufs*/,
|
||||
nframes_t /*start*/, nframes_t /*end*/, nframes_t /*nframes*/,
|
||||
pan_t** /*buffers*/)
|
||||
{
|
||||
if (_muted) {
|
||||
return;
|
||||
}
|
||||
|
||||
/* what ? */
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
StreamPanner*
|
||||
Multi2dPanner::factory (Panner& p, Evoral::Parameter param)
|
||||
{
|
||||
return new Multi2dPanner (p, param);
|
||||
}
|
||||
|
||||
int
|
||||
Multi2dPanner::load (istream& /*in*/, string /*path*/, uint32_t& /*linecnt*/)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
XMLNode&
|
||||
Multi2dPanner::get_state (void)
|
||||
{
|
||||
return state (true);
|
||||
}
|
||||
|
||||
XMLNode&
|
||||
Multi2dPanner::state (bool /*full_state*/)
|
||||
{
|
||||
XMLNode* root = new XMLNode ("StreamPanner");
|
||||
char buf[64];
|
||||
LocaleGuard lg (X_("POSIX"));
|
||||
|
||||
snprintf (buf, sizeof (buf), "%.12g", _x);
|
||||
root->add_property (X_("x"), buf);
|
||||
snprintf (buf, sizeof (buf), "%.12g", _y);
|
||||
root->add_property (X_("y"), buf);
|
||||
root->add_property (X_("type"), Multi2dPanner::name);
|
||||
|
||||
/* XXX no meaningful automation yet */
|
||||
|
||||
return *root;
|
||||
}
|
||||
|
||||
int
|
||||
Multi2dPanner::set_state (const XMLNode& node, int /*version*/)
|
||||
{
|
||||
const XMLProperty* prop;
|
||||
float newx,newy;
|
||||
LocaleGuard lg (X_("POSIX"));
|
||||
|
||||
newx = -1;
|
||||
newy = -1;
|
||||
|
||||
if ((prop = node.property (X_("x")))) {
|
||||
newx = atof (prop->value().c_str());
|
||||
}
|
||||
|
||||
if ((prop = node.property (X_("y")))) {
|
||||
newy = atof (prop->value().c_str());
|
||||
}
|
||||
|
||||
if (_x < 0 || _y < 0) {
|
||||
error << _("badly-formed positional data for Multi2dPanner - ignored")
|
||||
<< endmsg;
|
||||
return -1;
|
||||
}
|
||||
|
||||
set_position (newx, newy);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*---------------------------------------------------------------------- */
|
||||
|
||||
Panner::Panner (string name, Session& s)
|
||||
: SessionObject (s, name)
|
||||
, Automatable (s)
|
||||
|
@ -827,16 +613,20 @@ Panner::reset_to_default ()
|
|||
}
|
||||
|
||||
if (outputs.size() == 2) {
|
||||
AngularVector a;
|
||||
switch (_streampanners.size()) {
|
||||
case 1:
|
||||
_streampanners.front()->set_position (0.5);
|
||||
a.azi = 90.0; /* "front" or "top", in degrees */
|
||||
_streampanners.front()->set_position (a);
|
||||
_streampanners.front()->pan_control()->list()->reset_default (0.5);
|
||||
return;
|
||||
break;
|
||||
case 2:
|
||||
_streampanners.front()->set_position (0.0);
|
||||
a.azi = 180.0; /* "left", in degrees */
|
||||
_streampanners.front()->set_position (a);
|
||||
_streampanners.front()->pan_control()->list()->reset_default (0.0);
|
||||
_streampanners.back()->set_position (1.0);
|
||||
a.azi = 0.0; /* "right", in degrees */
|
||||
_streampanners.back()->set_position (a);
|
||||
_streampanners.back()->pan_control()->list()->reset_default (1.0);
|
||||
return;
|
||||
default:
|
||||
|
@ -848,13 +638,15 @@ Panner::reset_to_default ()
|
|||
vector<StreamPanner*>::iterator p;
|
||||
|
||||
for (o = outputs.begin(), p = _streampanners.begin(); o != outputs.end() && p != _streampanners.end(); ++o, ++p) {
|
||||
(*p)->set_position ((*o).x, (*o).y);
|
||||
(*p)->set_position ((*o).position);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
Panner::reset_streampanner (uint32_t which)
|
||||
{
|
||||
AngularVector a;
|
||||
|
||||
if (which >= _streampanners.size() || which >= outputs.size()) {
|
||||
return;
|
||||
}
|
||||
|
@ -868,24 +660,27 @@ Panner::reset_streampanner (uint32_t which)
|
|||
switch (_streampanners.size()) {
|
||||
case 1:
|
||||
/* stereo out, 1 stream, default = middle */
|
||||
_streampanners.front()->set_position (0.5);
|
||||
a.azi = 90.0; /* "front" or "top", in degrees */
|
||||
_streampanners.front()->set_position (a);
|
||||
_streampanners.front()->pan_control()->list()->reset_default (0.5);
|
||||
break;
|
||||
case 2:
|
||||
/* stereo out, 2 streams, default = hard left/right */
|
||||
if (which == 0) {
|
||||
_streampanners.front()->set_position (0.0);
|
||||
_streampanners.front()->pan_control()->list()->reset_default (0.0);
|
||||
} else {
|
||||
_streampanners.back()->set_position (1.0);
|
||||
_streampanners.back()->pan_control()->list()->reset_default (1.0);
|
||||
a.azi = 180.0; /* "left", in degrees */
|
||||
_streampanners.front()->set_position (a);
|
||||
_streampanners.front()->pan_control()->list()->reset_default (0.0);
|
||||
} else {
|
||||
a.azi = 0.0; /* "right", in degrees */
|
||||
_streampanners.back()->set_position (a);
|
||||
_streampanners.back()->pan_control()->list()->reset_default (1.0);
|
||||
}
|
||||
break;
|
||||
}
|
||||
return;
|
||||
|
||||
default:
|
||||
_streampanners[which]->set_position (outputs[which].x, outputs[which].y);
|
||||
_streampanners[which]->set_position (outputs[which].position);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -947,16 +742,13 @@ Panner::reset (uint32_t nouts, uint32_t npans)
|
|||
break;
|
||||
|
||||
case 2: // line
|
||||
outputs.push_back (Output (0, 0));
|
||||
outputs.push_back (Output (1.0, 0));
|
||||
outputs.push_back (Output (AngularVector (180.0, 0.0)));
|
||||
outputs.push_back (Output (AngularVector (0.0, 0,0)));
|
||||
for (n = 0; n < npans; ++n) {
|
||||
_streampanners.push_back (new EqualPowerStereoPanner (*this, Evoral::Parameter(PanAutomation, 0, n)));
|
||||
}
|
||||
break;
|
||||
|
||||
case 3:
|
||||
case 4:
|
||||
case 5:
|
||||
default:
|
||||
setup_speakers (nouts);
|
||||
for (n = 0; n < npans; ++n) {
|
||||
|
@ -983,24 +775,46 @@ Panner::reset (uint32_t nouts, uint32_t npans)
|
|||
if (npans == 2 && outputs.size() == 2) {
|
||||
|
||||
/* Do this only if we changed configuration, or our configuration
|
||||
appears to be the default set up (center).
|
||||
appears to be the default set up (zero degrees)
|
||||
*/
|
||||
|
||||
float left;
|
||||
float right;
|
||||
AngularVector left;
|
||||
AngularVector right;
|
||||
|
||||
_streampanners.front()->get_position (left);
|
||||
_streampanners.back()->get_position (right);
|
||||
left = _streampanners.front()->get_position ();
|
||||
right = _streampanners.back()->get_position ();
|
||||
|
||||
if (changed || ((left == 0.5) && (right == 0.5))) {
|
||||
if (changed || ((left.azi == 0.0) && (right.azi == 0.0))) {
|
||||
|
||||
_streampanners.front()->set_position (0.0);
|
||||
_streampanners.front()->set_position (AngularVector (180.0, 0.0));
|
||||
_streampanners.front()->pan_control()->list()->reset_default (0.0);
|
||||
|
||||
_streampanners.back()->set_position (1.0);
|
||||
_streampanners.back()->set_position (AngularVector (0.0, 0.0));
|
||||
_streampanners.back()->pan_control()->list()->reset_default (1.0);
|
||||
}
|
||||
}
|
||||
|
||||
} else if (npans > 1 && outputs.size() > 2) {
|
||||
|
||||
/* 2d panning: spread signals equally around a circle */
|
||||
|
||||
double degree_step = 360.0 / nouts;
|
||||
double deg;
|
||||
|
||||
/* even number of signals? make sure the top two are either side of "top".
|
||||
otherwise, just start at the "top" (90.0 degrees) and rotate around
|
||||
*/
|
||||
|
||||
if (npans % 2) {
|
||||
deg = 90.0 - degree_step;
|
||||
} else {
|
||||
deg = 90.0;
|
||||
}
|
||||
|
||||
for (std::vector<StreamPanner*>::iterator x = _streampanners.begin(); x != _streampanners.end(); ++x) {
|
||||
(*x)->set_position (AngularVector (deg, 0.0));
|
||||
deg += degree_step;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
|
@ -1076,12 +890,12 @@ Panner::automation_style () const
|
|||
struct PanPlugins {
|
||||
string name;
|
||||
uint32_t nouts;
|
||||
StreamPanner* (*factory)(Panner&, Evoral::Parameter);
|
||||
StreamPanner* (*factory)(Panner&, Evoral::Parameter, Speakers&);
|
||||
};
|
||||
|
||||
PanPlugins pan_plugins[] = {
|
||||
{ EqualPowerStereoPanner::name, 2, EqualPowerStereoPanner::factory },
|
||||
{ Multi2dPanner::name, 3, Multi2dPanner::factory },
|
||||
{ VBAPanner::name, 3, VBAPanner::factory },
|
||||
{ string (""), 0, 0 }
|
||||
};
|
||||
|
||||
|
@ -1104,10 +918,10 @@ Panner::state (bool full)
|
|||
|
||||
for (vector<Panner::Output>::iterator o = outputs.begin(); o != outputs.end(); ++o) {
|
||||
XMLNode* onode = new XMLNode (X_("Output"));
|
||||
snprintf (buf, sizeof (buf), "%.12g", (*o).x);
|
||||
onode->add_property (X_("x"), buf);
|
||||
snprintf (buf, sizeof (buf), "%.12g", (*o).y);
|
||||
onode->add_property (X_("y"), buf);
|
||||
snprintf (buf, sizeof (buf), "%.12g", (*o).position.azi);
|
||||
onode->add_property (X_("azimuth"), buf);
|
||||
snprintf (buf, sizeof (buf), "%.12g", (*o).position.ele);
|
||||
onode->add_property (X_("elevation"), buf);
|
||||
node->add_child_nocopy (*onode);
|
||||
}
|
||||
|
||||
|
@ -1143,7 +957,6 @@ Panner::set_state (const XMLNode& node, int version)
|
|||
set_linked (string_is_affirmative (prop->value()));
|
||||
}
|
||||
|
||||
|
||||
if ((prop = node.property (X_("bypassed"))) != 0) {
|
||||
set_bypassed (string_is_affirmative (prop->value()));
|
||||
}
|
||||
|
@ -1158,15 +971,20 @@ Panner::set_state (const XMLNode& node, int version)
|
|||
for (niter = nlist.begin(); niter != nlist.end(); ++niter) {
|
||||
if ((*niter)->name() == X_("Output")) {
|
||||
|
||||
float x, y;
|
||||
AngularVector a;
|
||||
|
||||
prop = (*niter)->property (X_("x"));
|
||||
sscanf (prop->value().c_str(), "%g", &x);
|
||||
if ((prop = (*niter)->property (X_("azimuth")))) {
|
||||
sscanf (prop->value().c_str(), "%lg", &a.azi);
|
||||
} else if ((prop = (*niter)->property (X_("x")))) {
|
||||
/* old school cartesian */
|
||||
a.azi = BaseStereoPanner::lr_fract_to_azimuth (atof (prop->value().c_str()));
|
||||
}
|
||||
|
||||
prop = (*niter)->property (X_("y"));
|
||||
sscanf (prop->value().c_str(), "%g", &y);
|
||||
if ((prop = (*niter)->property (X_("elevation")))) {
|
||||
sscanf (prop->value().c_str(), "%lg", &a.ele);
|
||||
}
|
||||
|
||||
outputs.push_back (Output (x, y));
|
||||
outputs.push_back (Output (a));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1185,7 +1003,7 @@ Panner::set_state (const XMLNode& node, int version)
|
|||
assumption, but it's still an assumption.
|
||||
*/
|
||||
|
||||
sp = pan_plugins[i].factory (*this, Evoral::Parameter(PanAutomation, 0, num_panners));
|
||||
sp = pan_plugins[i].factory (*this, Evoral::Parameter(PanAutomation, 0, num_panners), _session.get_speakers ());
|
||||
num_panners++;
|
||||
|
||||
if (sp->set_state (**niter, version) == 0) {
|
||||
|
@ -1244,25 +1062,21 @@ Panner::touching () const
|
|||
}
|
||||
|
||||
void
|
||||
Panner::set_position (float xpos, StreamPanner& orig)
|
||||
Panner::set_position (const AngularVector& a, StreamPanner& orig)
|
||||
{
|
||||
float xnow;
|
||||
float xdelta ;
|
||||
float xnew;
|
||||
AngularVector delta;
|
||||
AngularVector new_position;
|
||||
|
||||
orig.get_position (xnow);
|
||||
xdelta = xpos - xnow;
|
||||
delta = orig.get_position() - a;
|
||||
|
||||
if (_link_direction == SameDirection) {
|
||||
|
||||
for (vector<StreamPanner*>::iterator i = _streampanners.begin(); i != _streampanners.end(); ++i) {
|
||||
if (*i == &orig) {
|
||||
(*i)->set_position (xpos, true);
|
||||
(*i)->set_position (a, true);
|
||||
} else {
|
||||
(*i)->get_position (xnow);
|
||||
xnew = min (1.0f, xnow + xdelta);
|
||||
xnew = max (0.0f, xnew);
|
||||
(*i)->set_position (xnew, true);
|
||||
new_position = (*i)->get_position() + delta;
|
||||
(*i)->set_position (new_position, true);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1270,117 +1084,10 @@ Panner::set_position (float xpos, StreamPanner& orig)
|
|||
|
||||
for (vector<StreamPanner*>::iterator i = _streampanners.begin(); i != _streampanners.end(); ++i) {
|
||||
if (*i == &orig) {
|
||||
(*i)->set_position (xpos, true);
|
||||
(*i)->set_position (a, true);
|
||||
} else {
|
||||
(*i)->get_position (xnow);
|
||||
xnew = min (1.0f, xnow - xdelta);
|
||||
xnew = max (0.0f, xnew);
|
||||
(*i)->set_position (xnew, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
Panner::set_position (float xpos, float ypos, StreamPanner& orig)
|
||||
{
|
||||
float xnow, ynow;
|
||||
float xdelta, ydelta;
|
||||
float xnew, ynew;
|
||||
|
||||
orig.get_position (xnow, ynow);
|
||||
xdelta = xpos - xnow;
|
||||
ydelta = ypos - ynow;
|
||||
|
||||
if (_link_direction == SameDirection) {
|
||||
|
||||
for (vector<StreamPanner*>::iterator i = _streampanners.begin(); i != _streampanners.end(); ++i) {
|
||||
if (*i == &orig) {
|
||||
(*i)->set_position (xpos, ypos, true);
|
||||
} else {
|
||||
(*i)->get_position (xnow, ynow);
|
||||
|
||||
xnew = min (1.0f, xnow + xdelta);
|
||||
xnew = max (0.0f, xnew);
|
||||
|
||||
ynew = min (1.0f, ynow + ydelta);
|
||||
ynew = max (0.0f, ynew);
|
||||
|
||||
(*i)->set_position (xnew, ynew, true);
|
||||
}
|
||||
}
|
||||
|
||||
} else {
|
||||
|
||||
for (vector<StreamPanner*>::iterator i = _streampanners.begin(); i != _streampanners.end(); ++i) {
|
||||
if (*i == &orig) {
|
||||
(*i)->set_position (xpos, ypos, true);
|
||||
} else {
|
||||
(*i)->get_position (xnow, ynow);
|
||||
|
||||
xnew = min (1.0f, xnow - xdelta);
|
||||
xnew = max (0.0f, xnew);
|
||||
|
||||
ynew = min (1.0f, ynow - ydelta);
|
||||
ynew = max (0.0f, ynew);
|
||||
|
||||
(*i)->set_position (xnew, ynew, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
Panner::set_position (float xpos, float ypos, float zpos, StreamPanner& orig)
|
||||
{
|
||||
float xnow, ynow, znow;
|
||||
float xdelta, ydelta, zdelta;
|
||||
float xnew, ynew, znew;
|
||||
|
||||
orig.get_position (xnow, ynow, znow);
|
||||
xdelta = xpos - xnow;
|
||||
ydelta = ypos - ynow;
|
||||
zdelta = zpos - znow;
|
||||
|
||||
if (_link_direction == SameDirection) {
|
||||
|
||||
for (vector<StreamPanner*>::iterator i = _streampanners.begin(); i != _streampanners.end(); ++i) {
|
||||
if (*i == &orig) {
|
||||
(*i)->set_position (xpos, ypos, zpos, true);
|
||||
} else {
|
||||
(*i)->get_position (xnow, ynow, znow);
|
||||
|
||||
xnew = min (1.0f, xnow + xdelta);
|
||||
xnew = max (0.0f, xnew);
|
||||
|
||||
ynew = min (1.0f, ynow + ydelta);
|
||||
ynew = max (0.0f, ynew);
|
||||
|
||||
znew = min (1.0f, znow + zdelta);
|
||||
znew = max (0.0f, znew);
|
||||
|
||||
(*i)->set_position (xnew, ynew, znew, true);
|
||||
}
|
||||
}
|
||||
|
||||
} else {
|
||||
|
||||
for (vector<StreamPanner*>::iterator i = _streampanners.begin(); i != _streampanners.end(); ++i) {
|
||||
if (*i == &orig) {
|
||||
(*i)->set_position (xpos, ypos, true);
|
||||
} else {
|
||||
(*i)->get_position (xnow, ynow, znow);
|
||||
|
||||
xnew = min (1.0f, xnow - xdelta);
|
||||
xnew = max (0.0f, xnew);
|
||||
|
||||
ynew = min (1.0f, ynow - ydelta);
|
||||
ynew = max (0.0f, ynew);
|
||||
|
||||
znew = min (1.0f, znow + zdelta);
|
||||
znew = max (0.0f, znew);
|
||||
|
||||
(*i)->set_position (xnew, ynew, znew, true);
|
||||
new_position = (*i)->get_position() - delta;
|
||||
(*i)->set_position (new_position, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1638,47 +1345,45 @@ Panner::setup_speakers (uint32_t nouts)
|
|||
{
|
||||
switch (nouts) {
|
||||
case 3:
|
||||
outputs.push_back (Output (0.5, 0));
|
||||
outputs.push_back (Output (0, 1.0));
|
||||
outputs.push_back (Output (1.0, 1.0));
|
||||
/* top, bottom kind-of-left & bottom kind-of-right */
|
||||
outputs.push_back (AngularVector (90.0, 0.0));
|
||||
outputs.push_back (AngularVector (215.0, 0,0));
|
||||
outputs.push_back (AngularVector (335.0, 0,0));
|
||||
break;
|
||||
case 4:
|
||||
/* clockwise from top left */
|
||||
outputs.push_back (Output (0, 1.0));
|
||||
outputs.push_back (Output (1.0, 1.0));
|
||||
outputs.push_back (Output (1.0, 0));
|
||||
outputs.push_back (Output (0, 0));
|
||||
outputs.push_back (AngularVector (135.0, 0.0));
|
||||
outputs.push_back (AngularVector (45.0, 0.0));
|
||||
outputs.push_back (AngularVector (335.0, 0.0));
|
||||
outputs.push_back (AngularVector (215.0, 0.0));
|
||||
break;
|
||||
|
||||
case 5: //square+offcenter center
|
||||
outputs.push_back (Output (0, 0));
|
||||
outputs.push_back (Output (1.0, 0));
|
||||
outputs.push_back (Output (1.0, 1.0));
|
||||
outputs.push_back (Output (0, 1.0));
|
||||
outputs.push_back (Output (0.5, 0.75));
|
||||
break;
|
||||
default:
|
||||
{
|
||||
double degree_step = 360.0 / nouts;
|
||||
double deg;
|
||||
uint32_t n;
|
||||
|
||||
default:
|
||||
/* XXX horrible placement. FIXME */
|
||||
for (uint32_t n = 0; n < nouts; ++n) {
|
||||
outputs.push_back (Output (0.1 * n, 0.1 * n));
|
||||
/* even number of speakers? make sure the top two are either side of "top".
|
||||
otherwise, just start at the "top" (90.0 degrees) and rotate around
|
||||
*/
|
||||
|
||||
if (nouts % 2) {
|
||||
deg = 90.0 - degree_step;
|
||||
} else {
|
||||
deg = 90.0;
|
||||
}
|
||||
for (n = 0; n < nouts; ++n, deg += degree_step) {
|
||||
outputs.push_back (Output (AngularVector (deg, 0.0)));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
VBAPSpeakers& speakers (_session.get_speakers());
|
||||
|
||||
Speakers& speakers (_session.get_speakers());
|
||||
|
||||
speakers.clear_speakers ();
|
||||
|
||||
for (vector<Output>::iterator o = outputs.begin(); o != outputs.end(); ++o) {
|
||||
double azimuth;
|
||||
double elevation;
|
||||
double tx, ty, tz;
|
||||
|
||||
tx = (*o).x - 0.5;
|
||||
ty = (*o).y - 0.5;
|
||||
tz = 0.0; // XXX change this if we ever do actual 3D
|
||||
|
||||
cart_to_azi_ele (tx, ty, tz, azimuth, elevation);
|
||||
speakers.add_speaker (azimuth, elevation);
|
||||
speakers.add_speaker ((*o).position);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -4202,11 +4202,12 @@ Session::ensure_search_path_includes (const string& path, DataType type)
|
|||
}
|
||||
}
|
||||
|
||||
VBAPSpeakers&
|
||||
Speakers&
|
||||
Session::get_speakers()
|
||||
{
|
||||
if (!_speakers) {
|
||||
_speakers = new VBAPSpeakers;
|
||||
_speakers = new Speakers;
|
||||
}
|
||||
|
||||
return *_speakers;
|
||||
}
|
||||
|
|
|
@ -0,0 +1,108 @@
|
|||
/*
|
||||
Copyright (C) 2010 Paul Davis
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*/
|
||||
|
||||
#include "ardour/speaker.h"
|
||||
#include "ardour/speakers.h"
|
||||
|
||||
using namespace ARDOUR;
|
||||
using namespace PBD;
|
||||
using namespace std;
|
||||
|
||||
Speaker::Speaker (int i, const AngularVector& position)
|
||||
: id (i)
|
||||
{
|
||||
move (position);
|
||||
}
|
||||
|
||||
void
|
||||
Speaker::move (const AngularVector& new_position)
|
||||
{
|
||||
_angles = new_position;
|
||||
_angles.cartesian (_coords);
|
||||
}
|
||||
|
||||
Speakers::Speakers ()
|
||||
{
|
||||
}
|
||||
|
||||
Speakers::~Speakers ()
|
||||
{
|
||||
}
|
||||
|
||||
void
|
||||
Speakers::dump_speakers (ostream& o)
|
||||
{
|
||||
for (vector<Speaker>::iterator i = _speakers.begin(); i != _speakers.end(); ++i) {
|
||||
o << "Speaker " << (*i).id << " @ "
|
||||
<< (*i).coords().x << ", " << (*i).coords().y << ", " << (*i).coords().z
|
||||
<< " azimuth " << (*i).angles().azi
|
||||
<< " elevation " << (*i).angles().ele
|
||||
<< " distance " << (*i).angles().length
|
||||
<< endl;
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
Speakers::clear_speakers ()
|
||||
{
|
||||
_speakers.clear ();
|
||||
update ();
|
||||
}
|
||||
|
||||
int
|
||||
Speakers::add_speaker (const AngularVector& position)
|
||||
{
|
||||
int id = _speakers.size();
|
||||
|
||||
cerr << "Added speaker " << id << " at " << position.azi << " /= " << position.ele << endl;
|
||||
|
||||
_speakers.push_back (Speaker (id, position));
|
||||
update ();
|
||||
|
||||
dump_speakers (cerr);
|
||||
Changed ();
|
||||
|
||||
return id;
|
||||
}
|
||||
|
||||
void
|
||||
Speakers::remove_speaker (int id)
|
||||
{
|
||||
for (vector<Speaker>::iterator i = _speakers.begin(); i != _speakers.end(); ) {
|
||||
if ((*i).id == id) {
|
||||
i = _speakers.erase (i);
|
||||
update ();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
Speakers::move_speaker (int id, const AngularVector& new_position)
|
||||
{
|
||||
for (vector<Speaker>::iterator i = _speakers.begin(); i != _speakers.end(); ++i) {
|
||||
if ((*i).id == id) {
|
||||
(*i).move (new_position);
|
||||
update ();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -41,6 +41,7 @@
|
|||
|
||||
#include "pbd/cartesian.h"
|
||||
|
||||
#include "ardour/speakers.h"
|
||||
#include "ardour/vbap.h"
|
||||
#include "ardour/vbap_speakers.h"
|
||||
#include "ardour/audio_buffer.h"
|
||||
|
@ -50,13 +51,13 @@ using namespace PBD;
|
|||
using namespace ARDOUR;
|
||||
using namespace std;
|
||||
|
||||
VBAPanner::VBAPanner (Panner& parent, Evoral::Parameter param, VBAPSpeakers& s)
|
||||
string VBAPanner::name = X_("VBAP");
|
||||
|
||||
VBAPanner::VBAPanner (Panner& parent, Evoral::Parameter param, Speakers& s)
|
||||
: StreamPanner (parent, param)
|
||||
, _dirty (false)
|
||||
, _speakers (s)
|
||||
|
||||
, _speakers (VBAPSpeakers::instance (s))
|
||||
{
|
||||
_speakers.Changed.connect_same_thread (speaker_connection, boost::bind (&VBAPanner::mark_dirty, this));
|
||||
}
|
||||
|
||||
VBAPanner::~VBAPanner ()
|
||||
|
@ -72,16 +73,11 @@ VBAPanner::mark_dirty ()
|
|||
void
|
||||
VBAPanner::update ()
|
||||
{
|
||||
/* convert from coordinate space with (0,0) at upper left to (0,0) at center and dimensions of 1 unit */
|
||||
_x -= 0.5;
|
||||
_y -= 0.5;
|
||||
|
||||
|
||||
/* we're 2D for now */
|
||||
_z = 0.0;
|
||||
|
||||
cart_to_azi_ele (_x, _y, _z, _azimuth, _elevation);
|
||||
/* force 2D for now */
|
||||
_angles.ele = 0.0;
|
||||
_dirty = true;
|
||||
|
||||
Changed ();
|
||||
}
|
||||
|
||||
void
|
||||
|
@ -234,3 +230,10 @@ VBAPanner::set_state (const XMLNode& node, int /*version*/)
|
|||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
StreamPanner*
|
||||
VBAPanner::factory (Panner& parent, Evoral::Parameter param, Speakers& s)
|
||||
{
|
||||
return new VBAPanner (parent, param, s);
|
||||
}
|
||||
|
||||
|
|
|
@ -39,100 +39,40 @@
|
|||
#include "ardour/vbap_speakers.h"
|
||||
|
||||
using namespace ARDOUR;
|
||||
using namespace PBD;
|
||||
using namespace std;
|
||||
|
||||
VBAPSpeakers::Speaker::Speaker (int i, double azimuth, double elevation)
|
||||
: id (i)
|
||||
VBAPSpeakers* VBAPSpeakers::_instance = 0;
|
||||
|
||||
VBAPSpeakers&
|
||||
VBAPSpeakers::instance (Speakers& s)
|
||||
{
|
||||
move (azimuth, elevation);
|
||||
cerr << setprecision (5) << "%%%%%%%%%% New speaker @ " << angles.azi << ", " << angles.ele << endl;
|
||||
if (_instance == 0) {
|
||||
_instance = new VBAPSpeakers (s);
|
||||
}
|
||||
|
||||
return *_instance;
|
||||
}
|
||||
|
||||
void
|
||||
VBAPSpeakers::Speaker::move (double azimuth, double elevation)
|
||||
{
|
||||
angles.azi = azimuth;
|
||||
angles.ele = elevation;
|
||||
angles.length = 1.0;
|
||||
angle_to_cart (&angles, &coords);
|
||||
}
|
||||
|
||||
VBAPSpeakers::VBAPSpeakers ()
|
||||
VBAPSpeakers::VBAPSpeakers (Speakers& s)
|
||||
: _dimension (2)
|
||||
, _speakers (s.speakers())
|
||||
{
|
||||
s.Changed.connect_same_thread (speaker_connection, boost::bind (&VBAPSpeakers::update, this));
|
||||
}
|
||||
|
||||
VBAPSpeakers::~VBAPSpeakers ()
|
||||
{
|
||||
}
|
||||
|
||||
void
|
||||
VBAPSpeakers::dump_speakers (ostream& o)
|
||||
{
|
||||
for (vector<Speaker>::iterator i = _speakers.begin(); i != _speakers.end(); ++i) {
|
||||
o << "Speaker " << (*i).id << " @ "
|
||||
<< (*i).coords.x << ", " << (*i).coords.y << ", " << (*i).coords.z
|
||||
<< " azimuth " << (*i).angles.azi
|
||||
<< " elevation " << (*i).angles.ele
|
||||
<< " distance " << (*i).angles.length
|
||||
<< endl;
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
VBAPSpeakers::clear_speakers ()
|
||||
{
|
||||
_speakers.clear ();
|
||||
update ();
|
||||
}
|
||||
|
||||
int
|
||||
VBAPSpeakers::add_speaker (double azimuth, double elevation)
|
||||
{
|
||||
int id = _speakers.size();
|
||||
|
||||
cerr << "Added speaker " << id << " at " << azimuth << " /= " << elevation << endl;
|
||||
|
||||
_speakers.push_back (Speaker (id, azimuth, elevation));
|
||||
update ();
|
||||
|
||||
dump_speakers (cerr);
|
||||
|
||||
return id;
|
||||
}
|
||||
|
||||
void
|
||||
VBAPSpeakers::remove_speaker (int id)
|
||||
{
|
||||
for (vector<Speaker>::iterator i = _speakers.begin(); i != _speakers.end(); ) {
|
||||
if ((*i).id == id) {
|
||||
i = _speakers.erase (i);
|
||||
update ();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
VBAPSpeakers::move_speaker (int id, double direction, double elevation)
|
||||
{
|
||||
for (vector<Speaker>::iterator i = _speakers.begin(); i != _speakers.end(); ++i) {
|
||||
if ((*i).id == id) {
|
||||
(*i).move (direction, elevation);
|
||||
update ();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
VBAPSpeakers::update ()
|
||||
{
|
||||
int dim = 2;
|
||||
|
||||
for (vector<Speaker>::iterator i = _speakers.begin(); i != _speakers.end(); ++i) {
|
||||
if ((*i).angles.ele != 0.0) {
|
||||
cerr << "\n\n\nSPEAKER " << (*i).id << " has ele = " << (*i).angles.ele << "\n\n\n\n";
|
||||
|
||||
for (vector<Speaker>::const_iterator i = _speakers.begin(); i != _speakers.end(); ++i) {
|
||||
if ((*i).angles().ele != 0.0) {
|
||||
cerr << "\n\n\nSPEAKER " << (*i).id << " has ele = " << (*i).angles().ele << "\n\n\n\n";
|
||||
dim = 3;
|
||||
break;
|
||||
}
|
||||
|
@ -157,16 +97,8 @@ VBAPSpeakers::update ()
|
|||
} else {
|
||||
choose_speaker_pairs ();
|
||||
}
|
||||
|
||||
Changed (); /* EMIT SIGNAL */
|
||||
}
|
||||
|
||||
void
|
||||
VBAPSpeakers::angle_to_cart(ang_vec *from, cart_vec *to)
|
||||
{
|
||||
PBD::azi_ele_to_cart (from->azi, from->ele, to->x, to->y, to->z);
|
||||
}
|
||||
|
||||
void
|
||||
VBAPSpeakers::choose_speaker_triplets(struct ls_triplet_chain **ls_triplets)
|
||||
{
|
||||
|
@ -217,7 +149,7 @@ VBAPSpeakers::choose_speaker_triplets(struct ls_triplet_chain **ls_triplets)
|
|||
for (i = 0;i < n_speakers; i++) {
|
||||
for (j = i+1; j < n_speakers; j++) {
|
||||
if (connections[i][j] == 1) {
|
||||
distance = fabs(vec_angle(_speakers[i].coords,_speakers[j].coords));
|
||||
distance = fabs(vec_angle(_speakers[i].coords(),_speakers[j].coords()));
|
||||
k=0;
|
||||
while(distance_table[k] < distance) {
|
||||
k++;
|
||||
|
@ -291,7 +223,9 @@ VBAPSpeakers::any_ls_inside_triplet(int a, int b, int c)
|
|||
{
|
||||
/* returns 1 if there is loudspeaker(s) inside given ls triplet */
|
||||
float invdet;
|
||||
cart_vec *lp1, *lp2, *lp3;
|
||||
const CartesianVector* lp1;
|
||||
const CartesianVector* lp2;
|
||||
const CartesianVector* lp3;
|
||||
float invmx[9];
|
||||
int i,j;
|
||||
float tmp;
|
||||
|
@ -299,9 +233,9 @@ VBAPSpeakers::any_ls_inside_triplet(int a, int b, int c)
|
|||
bool this_inside;
|
||||
int n_speakers = _speakers.size();
|
||||
|
||||
lp1 = &(_speakers[a].coords);
|
||||
lp2 = &(_speakers[b].coords);
|
||||
lp3 = &(_speakers[c].coords);
|
||||
lp1 = &(_speakers[a].coords());
|
||||
lp2 = &(_speakers[b].coords());
|
||||
lp3 = &(_speakers[c].coords());
|
||||
|
||||
/* matrix inversion */
|
||||
invdet = 1.0 / ( lp1->x * ((lp2->y * lp3->z) - (lp2->z * lp3->y))
|
||||
|
@ -323,9 +257,9 @@ VBAPSpeakers::any_ls_inside_triplet(int a, int b, int c)
|
|||
if (i != a && i!=b && i != c) {
|
||||
this_inside = true;
|
||||
for (j = 0; j < 3; j++) {
|
||||
tmp = _speakers[i].coords.x * invmx[0 + j*3];
|
||||
tmp += _speakers[i].coords.y * invmx[1 + j*3];
|
||||
tmp += _speakers[i].coords.z * invmx[2 + j*3];
|
||||
tmp = _speakers[i].coords().x * invmx[0 + j*3];
|
||||
tmp += _speakers[i].coords().y * invmx[1 + j*3];
|
||||
tmp += _speakers[i].coords().z * invmx[2 + j*3];
|
||||
if (tmp < -0.001) {
|
||||
this_inside = false;
|
||||
}
|
||||
|
@ -369,7 +303,7 @@ VBAPSpeakers::add_ldsp_triplet(int i, int j, int k, struct ls_triplet_chain **ls
|
|||
}
|
||||
|
||||
float
|
||||
VBAPSpeakers::vec_angle(cart_vec v1, cart_vec v2)
|
||||
VBAPSpeakers::vec_angle(CartesianVector v1, CartesianVector v2)
|
||||
{
|
||||
float inner= ((v1.x*v2.x + v1.y*v2.y + v1.z*v2.z)/
|
||||
(vec_length(v1) * vec_length(v2)));
|
||||
|
@ -386,13 +320,13 @@ VBAPSpeakers::vec_angle(cart_vec v1, cart_vec v2)
|
|||
}
|
||||
|
||||
float
|
||||
VBAPSpeakers::vec_length(cart_vec v1)
|
||||
VBAPSpeakers::vec_length(CartesianVector v1)
|
||||
{
|
||||
return (sqrt(v1.x*v1.x + v1.y*v1.y + v1.z*v1.z));
|
||||
}
|
||||
|
||||
float
|
||||
VBAPSpeakers::vec_prod(cart_vec v1, cart_vec v2)
|
||||
VBAPSpeakers::vec_prod(CartesianVector v1, CartesianVector v2)
|
||||
{
|
||||
return (v1.x*v2.x + v1.y*v2.y + v1.z*v2.z);
|
||||
}
|
||||
|
@ -405,13 +339,13 @@ VBAPSpeakers::vol_p_side_lgth(int i, int j,int k, const vector<Speaker>& speaker
|
|||
This is used when removing too narrow triangles. */
|
||||
|
||||
float volper, lgth;
|
||||
cart_vec xprod;
|
||||
CartesianVector xprod;
|
||||
|
||||
cross_prod (speakers[i].coords, speakers[j].coords, &xprod);
|
||||
volper = fabsf (vec_prod(xprod, speakers[k].coords));
|
||||
lgth = (fabsf (vec_angle(speakers[i].coords, speakers[j].coords))
|
||||
+ fabsf (vec_angle(speakers[i].coords, speakers[k].coords))
|
||||
+ fabsf (vec_angle(speakers[j].coords, speakers[k].coords)));
|
||||
cross_prod (speakers[i].coords(), speakers[j].coords(), &xprod);
|
||||
volper = fabsf (vec_prod(xprod, speakers[k].coords()));
|
||||
lgth = (fabsf (vec_angle(speakers[i].coords(), speakers[j].coords()))
|
||||
+ fabsf (vec_angle(speakers[i].coords(), speakers[k].coords()))
|
||||
+ fabsf (vec_angle(speakers[j].coords(), speakers[k].coords())));
|
||||
|
||||
if (lgth > 0.00001) {
|
||||
return volper / lgth;
|
||||
|
@ -421,7 +355,7 @@ VBAPSpeakers::vol_p_side_lgth(int i, int j,int k, const vector<Speaker>& speaker
|
|||
}
|
||||
|
||||
void
|
||||
VBAPSpeakers::cross_prod(cart_vec v1,cart_vec v2, cart_vec *res)
|
||||
VBAPSpeakers::cross_prod(CartesianVector v1,CartesianVector v2, CartesianVector *res)
|
||||
{
|
||||
float length;
|
||||
|
||||
|
@ -429,7 +363,7 @@ VBAPSpeakers::cross_prod(cart_vec v1,cart_vec v2, cart_vec *res)
|
|||
res->y = (v1.z * v2.x ) - (v1.x * v2.z);
|
||||
res->z = (v1.x * v2.y ) - (v1.y * v2.x);
|
||||
|
||||
length= vec_length(*res);
|
||||
length = vec_length(*res);
|
||||
res->x /= length;
|
||||
res->y /= length;
|
||||
res->z /= length;
|
||||
|
@ -446,30 +380,30 @@ VBAPSpeakers::lines_intersect (int i, int j, int k, int l)
|
|||
if you want to have that paper.
|
||||
*/
|
||||
|
||||
cart_vec v1;
|
||||
cart_vec v2;
|
||||
cart_vec v3, neg_v3;
|
||||
CartesianVector v1;
|
||||
CartesianVector v2;
|
||||
CartesianVector v3, neg_v3;
|
||||
float dist_ij,dist_kl,dist_iv3,dist_jv3,dist_inv3,dist_jnv3;
|
||||
float dist_kv3,dist_lv3,dist_knv3,dist_lnv3;
|
||||
|
||||
cross_prod(_speakers[i].coords,_speakers[j].coords,&v1);
|
||||
cross_prod(_speakers[k].coords,_speakers[l].coords,&v2);
|
||||
cross_prod(_speakers[i].coords(),_speakers[j].coords(),&v1);
|
||||
cross_prod(_speakers[k].coords(),_speakers[l].coords(),&v2);
|
||||
cross_prod(v1,v2,&v3);
|
||||
|
||||
neg_v3.x= 0.0 - v3.x;
|
||||
neg_v3.y= 0.0 - v3.y;
|
||||
neg_v3.z= 0.0 - v3.z;
|
||||
|
||||
dist_ij = (vec_angle(_speakers[i].coords,_speakers[j].coords));
|
||||
dist_kl = (vec_angle(_speakers[k].coords,_speakers[l].coords));
|
||||
dist_iv3 = (vec_angle(_speakers[i].coords,v3));
|
||||
dist_jv3 = (vec_angle(v3,_speakers[j].coords));
|
||||
dist_inv3 = (vec_angle(_speakers[i].coords,neg_v3));
|
||||
dist_jnv3 = (vec_angle(neg_v3,_speakers[j].coords));
|
||||
dist_kv3 = (vec_angle(_speakers[k].coords,v3));
|
||||
dist_lv3 = (vec_angle(v3,_speakers[l].coords));
|
||||
dist_knv3 = (vec_angle(_speakers[k].coords,neg_v3));
|
||||
dist_lnv3 = (vec_angle(neg_v3,_speakers[l].coords));
|
||||
dist_ij = (vec_angle(_speakers[i].coords(),_speakers[j].coords()));
|
||||
dist_kl = (vec_angle(_speakers[k].coords(),_speakers[l].coords()));
|
||||
dist_iv3 = (vec_angle(_speakers[i].coords(),v3));
|
||||
dist_jv3 = (vec_angle(v3,_speakers[j].coords()));
|
||||
dist_inv3 = (vec_angle(_speakers[i].coords(),neg_v3));
|
||||
dist_jnv3 = (vec_angle(neg_v3,_speakers[j].coords()));
|
||||
dist_kv3 = (vec_angle(_speakers[k].coords(),v3));
|
||||
dist_lv3 = (vec_angle(v3,_speakers[l].coords()));
|
||||
dist_knv3 = (vec_angle(_speakers[k].coords(),neg_v3));
|
||||
dist_lnv3 = (vec_angle(neg_v3,_speakers[l].coords()));
|
||||
|
||||
/* if one of loudspeakers is close to crossing point, don't do anything*/
|
||||
|
||||
|
@ -496,7 +430,9 @@ VBAPSpeakers::calculate_3x3_matrixes(struct ls_triplet_chain *ls_triplets)
|
|||
{
|
||||
/* Calculates the inverse matrices for 3D */
|
||||
float invdet;
|
||||
cart_vec *lp1, *lp2, *lp3;
|
||||
const CartesianVector* lp1;
|
||||
const CartesianVector* lp2;
|
||||
const CartesianVector* lp3;
|
||||
float *invmx;
|
||||
struct ls_triplet_chain *tr_ptr = ls_triplets;
|
||||
int triplet_count = 0;
|
||||
|
@ -524,9 +460,9 @@ VBAPSpeakers::calculate_3x3_matrixes(struct ls_triplet_chain *ls_triplets)
|
|||
}
|
||||
|
||||
while (tr_ptr != 0) {
|
||||
lp1 = &(_speakers[tr_ptr->ls_nos[0]].coords);
|
||||
lp2 = &(_speakers[tr_ptr->ls_nos[1]].coords);
|
||||
lp3 = &(_speakers[tr_ptr->ls_nos[2]].coords);
|
||||
lp1 = &(_speakers[tr_ptr->ls_nos[0]].coords());
|
||||
lp2 = &(_speakers[tr_ptr->ls_nos[1]].coords());
|
||||
lp3 = &(_speakers[tr_ptr->ls_nos[2]].coords());
|
||||
|
||||
/* matrix inversion */
|
||||
invmx = tr_ptr->inv_mx;
|
||||
|
@ -603,17 +539,17 @@ VBAPSpeakers::choose_speaker_pairs (){
|
|||
for (speaker = 0; speaker < n_speakers-1; speaker++) {
|
||||
|
||||
cerr << "Looking at "
|
||||
<< _speakers[sorted_speakers[speaker]].id << " @ " << _speakers[sorted_speakers[speaker]].angles.azi
|
||||
<< _speakers[sorted_speakers[speaker]].id << " @ " << _speakers[sorted_speakers[speaker]].angles().azi
|
||||
<< " and "
|
||||
<< _speakers[sorted_speakers[speaker+1]].id << " @ " << _speakers[sorted_speakers[speaker+1]].angles.azi
|
||||
<< _speakers[sorted_speakers[speaker+1]].id << " @ " << _speakers[sorted_speakers[speaker+1]].angles().azi
|
||||
<< " delta = "
|
||||
<< _speakers[sorted_speakers[speaker+1]].angles.azi - _speakers[sorted_speakers[speaker]].angles.azi
|
||||
<< _speakers[sorted_speakers[speaker+1]].angles().azi - _speakers[sorted_speakers[speaker]].angles().azi
|
||||
<< endl;
|
||||
|
||||
if ((_speakers[sorted_speakers[speaker+1]].angles.azi -
|
||||
_speakers[sorted_speakers[speaker]].angles.azi) <= AZIMUTH_DELTA_THRESHOLD_DEGREES) {
|
||||
if (calc_2D_inv_tmatrix( _speakers[sorted_speakers[speaker]].angles.azi,
|
||||
_speakers[sorted_speakers[speaker+1]].angles.azi,
|
||||
if ((_speakers[sorted_speakers[speaker+1]].angles().azi -
|
||||
_speakers[sorted_speakers[speaker]].angles().azi) <= AZIMUTH_DELTA_THRESHOLD_DEGREES) {
|
||||
if (calc_2D_inv_tmatrix( _speakers[sorted_speakers[speaker]].angles().azi,
|
||||
_speakers[sorted_speakers[speaker+1]].angles().azi,
|
||||
inverse_matrix[speaker]) != 0){
|
||||
exists[speaker] = true;
|
||||
expected_pairs++;
|
||||
|
@ -621,10 +557,10 @@ VBAPSpeakers::choose_speaker_pairs (){
|
|||
}
|
||||
}
|
||||
|
||||
if (((6.283 - _speakers[sorted_speakers[n_speakers-1]].angles.azi)
|
||||
+_speakers[sorted_speakers[0]].angles.azi) <= AZIMUTH_DELTA_THRESHOLD_DEGREES) {
|
||||
if (calc_2D_inv_tmatrix(_speakers[sorted_speakers[n_speakers-1]].angles.azi,
|
||||
_speakers[sorted_speakers[0]].angles.azi,
|
||||
if (((6.283 - _speakers[sorted_speakers[n_speakers-1]].angles().azi)
|
||||
+_speakers[sorted_speakers[0]].angles().azi) <= AZIMUTH_DELTA_THRESHOLD_DEGREES) {
|
||||
if (calc_2D_inv_tmatrix(_speakers[sorted_speakers[n_speakers-1]].angles().azi,
|
||||
_speakers[sorted_speakers[0]].angles().azi,
|
||||
inverse_matrix[n_speakers-1]) != 0) {
|
||||
exists[n_speakers-1] = true;
|
||||
expected_pairs++;
|
||||
|
|
|
@ -189,6 +189,7 @@ libardour_sources = [
|
|||
'sndfilesource.cc',
|
||||
'source.cc',
|
||||
'source_factory.cc',
|
||||
'speakers.cc',
|
||||
'strip_silence.cc',
|
||||
'svn_revision.cc',
|
||||
'tape_file_matcher.cc',
|
||||
|
|
|
@ -18,6 +18,7 @@
|
|||
|
||||
#include <iostream>
|
||||
#include <math.h>
|
||||
|
||||
#include "pbd/cartesian.h"
|
||||
|
||||
using namespace std;
|
||||
|
|
|
@ -19,9 +19,87 @@
|
|||
#ifndef __libpbd_cartesian_h__
|
||||
#define __libpbd_cartesian_h__
|
||||
|
||||
#include <cfloat>
|
||||
#include <cmath>
|
||||
|
||||
namespace PBD {
|
||||
void azi_ele_to_cart (double azi, double ele, double& x, double& y, double& z);
|
||||
void cart_to_azi_ele (double x, double y, double z, double& azi, double& ele);
|
||||
|
||||
void azi_ele_to_cart (double azi, double ele, double& x, double& y, double& z);
|
||||
void cart_to_azi_ele (double x, double y, double z, double& azi, double& ele);
|
||||
|
||||
struct AngularVector;
|
||||
|
||||
struct CartesianVector {
|
||||
double x;
|
||||
double y;
|
||||
double z;
|
||||
|
||||
CartesianVector () : x(0.0), y(0.0), z(0.0) {}
|
||||
CartesianVector (double xp, double yp, double zp = 0.0) : x(xp), y(yp), z(zp) {}
|
||||
|
||||
CartesianVector& translate (CartesianVector& other, double xtranslate, double ytranslate, double ztranslate = 0.0) {
|
||||
other.x += xtranslate;
|
||||
other.y += ytranslate;
|
||||
other.z += ztranslate;
|
||||
return other;
|
||||
}
|
||||
|
||||
CartesianVector& scale (CartesianVector& other, double xscale, double yscale, double zscale = 1.0) {
|
||||
other.x *= xscale;
|
||||
other.y *= yscale;
|
||||
other.z *= zscale;
|
||||
return other;
|
||||
}
|
||||
|
||||
void angular (AngularVector&) const;
|
||||
};
|
||||
|
||||
struct AngularVector {
|
||||
double azi;
|
||||
double ele;
|
||||
double length;
|
||||
|
||||
AngularVector () : azi(0.0), ele(0.0), length (0.0) {}
|
||||
AngularVector (double a, double e, double l = 1.0) : azi(a), ele(e), length (l) {}
|
||||
|
||||
AngularVector operator- (const AngularVector& other) const {
|
||||
AngularVector r;
|
||||
r.azi = azi - other.azi;
|
||||
r.ele = ele - other.ele;
|
||||
r.length = length - other.length;
|
||||
return r;
|
||||
}
|
||||
|
||||
AngularVector operator+ (const AngularVector& other) const {
|
||||
AngularVector r;
|
||||
r.azi = azi + other.azi;
|
||||
r.ele = ele + other.ele;
|
||||
r.length = length + other.length;
|
||||
return r;
|
||||
}
|
||||
|
||||
bool operator== (const AngularVector& other) const {
|
||||
return fabs (azi - other.azi) <= FLT_EPSILON &&
|
||||
fabs (ele - other.ele) <= FLT_EPSILON &&
|
||||
fabs (length - other.length) <= FLT_EPSILON;
|
||||
}
|
||||
|
||||
bool operator!= (const AngularVector& other) const {
|
||||
return fabs (azi - other.azi) > FLT_EPSILON ||
|
||||
fabs (ele - other.ele) > FLT_EPSILON ||
|
||||
fabs (length - other.length) > FLT_EPSILON;
|
||||
}
|
||||
|
||||
void cartesian (CartesianVector& c) const {
|
||||
azi_ele_to_cart (azi, ele, c.x, c.y, c.z);
|
||||
}
|
||||
};
|
||||
|
||||
inline
|
||||
void CartesianVector::angular (AngularVector& a) const {
|
||||
cart_to_azi_ele (x, y, z, a.azi, a.ele);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#endif /* __libpbd_cartesian_h__ */
|
||||
|
|
|
@ -840,17 +840,13 @@ MackieControlProtocol::handle_control_event (SurfacePort & port, Control & contr
|
|||
if (route->panner()->npanners() == 1 || (route->panner()->npanners() == 2 && route->panner()->linked()))
|
||||
{
|
||||
// assume pan for now
|
||||
float xpos;
|
||||
route->panner()->streampanner (0).get_effective_position (xpos);
|
||||
|
||||
// calculate new value, and trim
|
||||
xpos += state.delta * state.sign;
|
||||
if (xpos > 1.0)
|
||||
xpos = 1.0;
|
||||
else if (xpos < 0.0)
|
||||
xpos = 0.0;
|
||||
|
||||
route->panner()->streampanner (0).set_position (xpos);
|
||||
AngularVector a = route->panner()->streampanner (0).get_effective_position ();
|
||||
|
||||
// calculate new value, and adjust
|
||||
a.azi += 180.0 * state.delta * state.sign;
|
||||
a.azi = min (180.0, a.azi);
|
||||
a.azi = max (0.0, a.azi);
|
||||
route->panner()->streampanner (0).set_position (a);
|
||||
}
|
||||
}
|
||||
else
|
||||
|
@ -1008,13 +1004,13 @@ MackieControlProtocol::notify_panner_changed (RouteSignal * route_signal, bool f
|
|||
boost::shared_ptr<Panner> panner = route_signal->route()->panner();
|
||||
if ((panner && panner->npanners() == 1) || (panner->npanners() == 2 && panner->linked()))
|
||||
{
|
||||
float pos;
|
||||
route_signal->route()->panner()->streampanner(0).get_effective_position (pos);
|
||||
AngularVector pos = route_signal->route()->panner()->streampanner(0).get_effective_position ();
|
||||
float fract = 1.0 - (pos.azi / 180.0); /* 1.0 = 0 degrees = right; 0.0 = 180 degrees = left */
|
||||
|
||||
// cache the MidiByteArray here, because the mackie led control is much lower
|
||||
// resolution than the panner control. So we save lots of byte
|
||||
// sends in spite of more work on the comparison
|
||||
MidiByteArray bytes = builder.build_led_ring (pot, ControlState (on, pos), MackieMidiBuilder::midi_pot_mode_dot);
|
||||
MidiByteArray bytes = builder.build_led_ring (pot, ControlState (on, fract), MackieMidiBuilder::midi_pot_mode_dot);
|
||||
// check that something has actually changed
|
||||
if (force_update || bytes != route_signal->last_pan_written())
|
||||
{
|
||||
|
|
Loading…
Reference in New Issue