diff options
author | Paul Davis <paul@linuxaudiosystems.com> | 2010-11-26 17:43:03 +0000 |
---|---|---|
committer | Paul Davis <paul@linuxaudiosystems.com> | 2010-11-26 17:43:03 +0000 |
commit | 553cf2982c4905c5a08f305ce2772beaa8c50324 (patch) | |
tree | d23a7bad787a2d5bc1a909a14e9869fcfb405ae8 /libs/ardour/panner.cc | |
parent | 1539ac1b9661f0c0bb313d8f0d9a72b6dc95aaf1 (diff) |
one step closer to working vbap panning
git-svn-id: svn://localhost/ardour2/branches/3.0@8091 d708f5d6-7413-0410-9779-e7cbd77b26cf
Diffstat (limited to 'libs/ardour/panner.cc')
-rw-r--r-- | libs/ardour/panner.cc | 569 |
1 files changed, 137 insertions, 432 deletions
diff --git a/libs/ardour/panner.cc b/libs/ardour/panner.cc index c44ab96a59..e438742266 100644 --- a/libs/ardour/panner.cc +++ b/libs/ardour/panner.cc @@ -61,15 +61,14 @@ using namespace PBD; float Panner::current_automation_version_number = 1.0; string EqualPowerStereoPanner::name = "Equal Power Stereo"; -string Multi2dPanner::name = "Multiple (2D)"; /* this is a default mapper of control values to a pan position others can be imagined. */ -static pan_t direct_control_to_pan (double fract) +static double direct_control_to_stereo_pan (double fract) { - return fract; + return BaseStereoPanner::lr_fract_to_azimuth (fract); } StreamPanner::StreamPanner (Panner& p, Evoral::Parameter param) @@ -82,10 +81,6 @@ StreamPanner::StreamPanner (Panner& p, Evoral::Parameter param) /* get our AutomationControl from our parent Panner, creating it if required */ _control = boost::dynamic_pointer_cast<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) -{ - if (!link_call && parent.linked()) { - parent.set_position (xpos, *this); - } - - if (_x != xpos) { - _x = xpos; - update (); - Changed (); - _control->Changed (); - } -} - -void -StreamPanner::set_position (float xpos, float ypos, bool link_call) +StreamPanner::set_position (const AngularVector& av, bool link_call) { if (!link_call && parent.linked()) { - parent.set_position (xpos, ypos, *this); + parent.set_position (av, *this); } - if (_x != xpos || _y != ypos) { - _x = xpos; - _y = ypos; - update (); - Changed (); - _control->Changed (); - } -} - -void -StreamPanner::set_position (float xpos, float ypos, float zpos, bool link_call) -{ - if (!link_call && parent.linked()) { - parent.set_position (xpos, ypos, zpos, *this); - } - - if (_x != xpos || _y != ypos || _z != zpos) { - _x = xpos; - _y = ypos; - _z = zpos; + if (_angles != av) { + _angles = av; update (); Changed (); _control->Changed (); @@ -431,10 +393,12 @@ EqualPowerStereoPanner::update () overhead. */ - /* x == 0 => hard left - x == 1 => hard right + /* x == 0 => hard left = 180.0 degrees + x == 1 => hard right = 0.0 degrees */ + double _x = BaseStereoPanner::azimuth_to_lr_fract (_angles.azi); + float const panR = _x; float const panL = 1 - panR; @@ -444,7 +408,7 @@ EqualPowerStereoPanner::update () desired_left = panL * (scale * panL + 1.0f - scale); desired_right = panR * (scale * panR + 1.0f - scale); - effective_x = _x; + _effective_angles = _angles; //_control->set_value(x); } @@ -472,7 +436,7 @@ EqualPowerStereoPanner::do_distribute_automated (AudioBuffer& srcbuf, BufferSet& /* store effective pan position. do this even if we are muted */ if (nframes > 0) { - effective_x = buffers[0][nframes-1]; + _effective_angles.azi = BaseStereoPanner::lr_fract_to_azimuth (buffers[0][nframes-1]); } if (_muted) { @@ -519,7 +483,7 @@ EqualPowerStereoPanner::do_distribute_automated (AudioBuffer& srcbuf, BufferSet& } StreamPanner* -EqualPowerStereoPanner::factory (Panner& parent, Evoral::Parameter param) +EqualPowerStereoPanner::factory (Panner& parent, Evoral::Parameter param, Speakers& /* ignored */) { return new EqualPowerStereoPanner (parent, param); } @@ -537,8 +501,8 @@ EqualPowerStereoPanner::state (bool /*full_state*/) char buf[64]; LocaleGuard lg (X_("POSIX")); - snprintf (buf, sizeof (buf), "%.12g", _x); - root->add_property (X_("x"), buf); + snprintf (buf, sizeof (buf), "%.12g", _angles.azi); + root->add_property (X_("azimuth"), buf); root->add_property (X_("type"), EqualPowerStereoPanner::name); // XXX: dont save automation here... its part of the automatable panner now. @@ -556,10 +520,15 @@ EqualPowerStereoPanner::set_state (const XMLNode& node, int version) const XMLProperty* prop; LocaleGuard lg (X_("POSIX")); - if ((prop = node.property (X_("x")))) { - const float pos = atof (prop->value().c_str()); - set_position (pos, true); - } + if ((prop = node.property (X_("azimuth")))) { + AngularVector a (atof (prop->value().c_str()), 0.0); + set_position (a, true); + } else if ((prop = node.property (X_("x")))) { + /* old school cartesian positioning */ + AngularVector a; + a.azi = BaseStereoPanner::lr_fract_to_azimuth (atof (prop->value().c_str())); + set_position (a, true); + } StreamPanner::set_state (node, version); @@ -575,7 +544,8 @@ EqualPowerStereoPanner::set_state (const XMLNode& node, int version) _control->alist()->set_state (*((*iter)->children().front()), version); if (_control->alist()->automation_state() != Off) { - set_position (_control->list()->eval (parent.session().transport_frame())); + double degrees = BaseStereoPanner::lr_fract_to_azimuth (_control->list()->eval (parent.session().transport_frame())); + set_position (AngularVector (degrees, 0.0)); } } } @@ -583,190 +553,6 @@ EqualPowerStereoPanner::set_state (const XMLNode& node, int version) return 0; } -/*----------------------------------------------------------------------*/ - -Multi2dPanner::Multi2dPanner (Panner& p, Evoral::Parameter param) - : StreamPanner (p, param) -{ - update (); -} - -Multi2dPanner::~Multi2dPanner () -{ -} - -void -Multi2dPanner::update () -{ - static const float BIAS = FLT_MIN; - uint32_t i; - uint32_t const nouts = parent.nouts (); - float dsq[nouts]; - float f, fr; - vector<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: - /* XXX horrible placement. FIXME */ - for (uint32_t n = 0; n < nouts; ++n) { - outputs.push_back (Output (0.1 * n, 0.1 * n)); + default: + { + double degree_step = 360.0 / nouts; + double deg; + uint32_t n; + + /* even number of speakers? make sure the top two are either side of "top". + otherwise, just start at the "top" (90.0 degrees) and rotate around + */ + + if (nouts % 2) { + deg = 90.0 - degree_step; + } else { + deg = 90.0; + } + for (n = 0; n < nouts; ++n, deg += degree_step) { + outputs.push_back (Output (AngularVector (deg, 0.0))); } } + } - VBAPSpeakers& speakers (_session.get_speakers()); - + Speakers& speakers (_session.get_speakers()); + speakers.clear_speakers (); for (vector<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); } } |