X-Git-Url: https://main.carlh.net/gitweb/?a=blobdiff_plain;f=libs%2Fpanners%2Fvbap%2Fvbap.cc;h=e6cbd3655ff741be3e69946cafa3664768eafd54;hb=9192a2e96947e8ebb5e4bff2ad502222d9db65d4;hp=6d7496729c87abf938b07e0b5dacb4b9c94f9be7;hpb=fb313fb1741a04f270fff3703967df572ac4fc45;p=ardour.git diff --git a/libs/panners/vbap/vbap.cc b/libs/panners/vbap/vbap.cc index 6d7496729c..e6cbd3655f 100644 --- a/libs/panners/vbap/vbap.cc +++ b/libs/panners/vbap/vbap.cc @@ -1,5 +1,5 @@ /* - Copyright (C) 2012 Paul Davis + Copyright (C) 2012 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 @@ -42,7 +42,7 @@ #include "vbap.h" #include "vbap_speakers.h" -#include "i18n.h" +#include "pbd/i18n.h" using namespace PBD; using namespace ARDOUR; @@ -50,7 +50,10 @@ using namespace std; static PanPluginDescriptor _descriptor = { "VBAP 2D panner", + "http://ardour.org/plugin/panner_vbap", + "http://ardour.org/plugin/panner_vbap#ui", -1, -1, + 1000, VBAPanner::factory }; @@ -69,14 +72,18 @@ void VBAPanner::Signal::resize_gains (uint32_t n) { gains.assign (n, 0.0); -} +} VBAPanner::VBAPanner (boost::shared_ptr p, boost::shared_ptr s) : Panner (p) , _speakers (new VBAPSpeakers (s)) { _pannable->pan_azimuth_control->Changed.connect_same_thread (*this, boost::bind (&VBAPanner::update, this)); + _pannable->pan_elevation_control->Changed.connect_same_thread (*this, boost::bind (&VBAPanner::update, this)); _pannable->pan_width_control->Changed.connect_same_thread (*this, boost::bind (&VBAPanner::update, this)); + if (!_pannable->has_state()) { + reset(); + } update (); } @@ -105,7 +112,7 @@ VBAPanner::configure_io (ChanCount in, ChanCount /* ignored - we use Speakers */ for (uint32_t i = 0; i < n; ++i) { Signal* s = new Signal (_pannable->session(), *this, i, _speakers->n_speakers()); _signals.push_back (s); - + } update (); @@ -114,80 +121,40 @@ VBAPanner::configure_io (ChanCount in, ChanCount /* ignored - we use Speakers */ void VBAPanner::update () { - /* recompute signal directions based on panner azimuth and, if relevant, width (diffusion) parameters) - */ - - /* panner azimuth control is [0 .. 1.0] which we interpret as [0 .. 360] degrees - */ - double center = _pannable->pan_azimuth_control->get_value() * 360.0; + /* recompute signal directions based on panner azimuth and, if relevant, width (diffusion) and elevation parameters */ + double elevation = _pannable->pan_elevation_control->get_value() * 90.0; if (_signals.size() > 1) { + double w = - (_pannable->pan_width_control->get_value()); + double signal_direction = 1.0 - (_pannable->pan_azimuth_control->get_value() + (w/2)); + double grd_step_per_signal = w / (_signals.size() - 1); + for (vector::iterator s = _signals.begin(); s != _signals.end(); ++s) { - /* panner width control is [-1.0 .. 1.0]; we ignore sign, and map to [0 .. 360] degrees - so that a width of 1 corresponds to a signal equally present from all directions, - and a width of zero corresponds to a point source from the "center" (above) point - on the perimeter of the speaker array. - */ - - double w = fabs (_pannable->pan_width_control->get_value()) * 360.0; - - double min_dir = center - (w/2.0); - if (min_dir < 0) { - min_dir = 360.0 + min_dir; // its already negative - } - min_dir = max (min (min_dir, 360.0), 0.0); - - double max_dir = center + (w/2.0); - if (max_dir > 360.0) { - max_dir = max_dir - 360.0; - } - max_dir = max (min (max_dir, 360.0), 0.0); - - if (max_dir < min_dir) { - swap (max_dir, min_dir); - } - - double degree_step_per_signal = (max_dir - min_dir) / (_signals.size() - 1); - double signal_direction = min_dir; - - if (w >= 0.0) { + Signal* signal = *s; - /* positive width - normal order of signal spread */ + int over = signal_direction; + over -= (signal_direction >= 0) ? 0 : 1; + signal_direction -= (double)over; - for (vector::iterator s = _signals.begin(); s != _signals.end(); ++s) { - - Signal* signal = *s; - - signal->direction = AngularVector (signal_direction, 0.0); - compute_gains (signal->desired_gains, signal->desired_outputs, signal->direction.azi, signal->direction.ele); - signal_direction += degree_step_per_signal; - } - } else { - - /* inverted width - reverse order of signal spread */ - - for (vector::reverse_iterator s = _signals.rbegin(); s != _signals.rend(); ++s) { - - Signal* signal = *s; - - signal->direction = AngularVector (signal_direction, 0.0); - compute_gains (signal->desired_gains, signal->desired_outputs, signal->direction.azi, signal->direction.ele); - signal_direction += degree_step_per_signal; - } + signal->direction = AngularVector (signal_direction * 360.0, elevation); + compute_gains (signal->desired_gains, signal->desired_outputs, signal->direction.azi, signal->direction.ele); + signal_direction += grd_step_per_signal; } - } else if (_signals.size() == 1) { + double center = (1.0 - _pannable->pan_azimuth_control->get_value()) * 360.0; /* width has no role to play if there is only 1 signal: VBAP does not do "diffusion" of a single channel */ Signal* s = _signals.front(); - s->direction = AngularVector (center, 0); + s->direction = AngularVector (center, elevation); compute_gains (s->desired_gains, s->desired_outputs, s->direction.azi, s->direction.ele); } + + SignalPositionChanged(); /* emit */ } -void -VBAPanner::compute_gains (double gains[3], int speaker_ids[3], int azi, int ele) +void +VBAPanner::compute_gains (double gains[3], int speaker_ids[3], int azi, int ele) { /* calculates gain factors using loudspeaker setup and given direction */ double cartdir[3]; @@ -195,8 +162,10 @@ VBAPanner::compute_gains (double gains[3], int speaker_ids[3], int azi, int ele) int i,j,k; double small_g; double big_sm_g, gtmp[3]; + const int dimension = _speakers->dimension(); + assert(dimension == 2 || dimension == 3); - spherical_to_cartesian (azi, ele, 1.0, cartdir[0], cartdir[1], cartdir[2]); + spherical_to_cartesian (azi, ele, 1.0, cartdir[0], cartdir[1], cartdir[2]); big_sm_g = -100000.0; gains[0] = gains[1] = gains[2] = 0; @@ -206,12 +175,12 @@ VBAPanner::compute_gains (double gains[3], int speaker_ids[3], int azi, int ele) small_g = 10000000.0; - for (j = 0; j < _speakers->dimension(); j++) { + for (j = 0; j < dimension; j++) { gtmp[j] = 0.0; - for (k = 0; k < _speakers->dimension(); k++) { - gtmp[j] += cartdir[k] * _speakers->matrix(i)[j*_speakers->dimension()+k]; + for (k = 0; k < dimension; k++) { + gtmp[j] += cartdir[k] * _speakers->matrix(i)[j * dimension + k]; } if (gtmp[j] < small_g) { @@ -223,8 +192,8 @@ VBAPanner::compute_gains (double gains[3], int speaker_ids[3], int azi, int ele) big_sm_g = small_g; - gains[0] = gtmp[0]; - gains[1] = gtmp[1]; + gains[0] = gtmp[0]; + gains[1] = gtmp[1]; speaker_ids[0] = _speakers->speaker_for_tuple (i, 0); speaker_ids[1] = _speakers->speaker_for_tuple (i, 1); @@ -238,11 +207,11 @@ VBAPanner::compute_gains (double gains[3], int speaker_ids[3], int azi, int ele) } } } - + power = sqrt (gains[0]*gains[0] + gains[1]*gains[1] + gains[2]*gains[2]); if (power > 0) { - gains[0] /= power; + gains[0] /= power; gains[1] /= power; gains[2] /= power; } @@ -295,7 +264,7 @@ VBAPanner::distribute_one (AudioBuffer& srcbuf, BufferSet& obufs, gain_t gain_co assert (sz == obufs.count().n_audio()); int8_t *outputs = (int8_t*)alloca(sz); // on the stack, no malloc - + /* set initial state of each output "record" */ @@ -313,12 +282,12 @@ VBAPanner::distribute_one (AudioBuffer& srcbuf, BufferSet& obufs, gain_t gain_co if (signal->outputs[o] != -1) { /* used last time */ outputs[signal->outputs[o]] |= 1; - } + } if (signal->desired_outputs[o] != -1) { /* used this time */ outputs[signal->desired_outputs[o]] |= 1<<1; - } + } } /* at this point, we can test a speaker's status: @@ -327,7 +296,7 @@ VBAPanner::distribute_one (AudioBuffer& srcbuf, BufferSet& obufs, gain_t gain_co (*outputs[o] & 2) <= in use this time (*outputs[o] & 3) == 3 <= in use both times *outputs[o] == 0 <= not in use either time - + */ for (int o = 0; o < 3; ++o) { @@ -341,14 +310,14 @@ VBAPanner::distribute_one (AudioBuffer& srcbuf, BufferSet& obufs, gain_t gain_co pan = gain_coefficient * signal->desired_gains[o]; if (pan == 0.0 && signal->gains[output] == 0.0) { - + /* nothing deing delivered to this output */ signal->gains[output] = 0.0; - + } else if (fabs (pan - signal->gains[output]) > 0.00001) { - - /* signal to this output but the gain coefficient has changed, so + + /* signal to this output but the gain coefficient has changed, so interpolate between them. */ @@ -357,10 +326,10 @@ VBAPanner::distribute_one (AudioBuffer& srcbuf, BufferSet& obufs, gain_t gain_co signal->gains[output] = pan; } else { - + /* signal to this output, same gain as before so just copy with gain */ - + mix_buffers_with_gain (obufs.get_audio (output).data(),src,nframes,pan); signal->gains[output] = pan; } @@ -385,9 +354,9 @@ VBAPanner::distribute_one (AudioBuffer& srcbuf, BufferSet& obufs, gain_t gain_co */ } -void +void VBAPanner::distribute_one_automated (AudioBuffer& /*src*/, BufferSet& /*obufs*/, - framepos_t /*start*/, framepos_t /*end*/, + framepos_t /*start*/, framepos_t /*end*/, pframes_t /*nframes*/, pan_t** /*buffers*/, uint32_t /*which*/) { /* XXX to be implemented */ @@ -396,7 +365,9 @@ VBAPanner::distribute_one_automated (AudioBuffer& /*src*/, BufferSet& /*obufs*/, XMLNode& VBAPanner::get_state () { - XMLNode& node (Panner::get_state()); + XMLNode& node (Panner::get_state()); + node.add_property (X_("uri"), _descriptor.panner_uri); + /* this is needed to allow new sessions to load with old Ardour: */ node.add_property (X_("type"), _descriptor.name); return node; } @@ -419,7 +390,7 @@ VBAPanner::out() const return ChanCount (DataType::AUDIO, _speakers->n_speakers()); } -std::set +std::set VBAPanner::what_can_be_automated() const { set s; @@ -427,37 +398,45 @@ VBAPanner::what_can_be_automated() const if (_signals.size() > 1) { s.insert (Evoral::Parameter (PanWidthAutomation)); } + if (_speakers->dimension() == 3) { + s.insert (Evoral::Parameter (PanElevationAutomation)); + } return s; } - + string VBAPanner::describe_parameter (Evoral::Parameter p) { switch (p.type()) { case PanAzimuthAutomation: - return _("Direction"); + return _("Azimuth"); case PanWidthAutomation: - return _("Diffusion"); + return _("Width"); + case PanElevationAutomation: + return _("Elevation"); default: return _pannable->describe_parameter (p); } } -string -VBAPanner::value_as_string (boost::shared_ptr ac) const +string +VBAPanner::value_as_string (boost::shared_ptr ac) const { /* DO NOT USE LocaleGuard HERE */ double val = ac->get_value(); switch (ac->parameter().type()) { case PanAzimuthAutomation: /* direction */ - return string_compose (_("%1"), int (rint (val * 360.0))); - + return string_compose (_("%1\u00B0"), (int (rint (val * 360.0))+180)%360); + case PanWidthAutomation: /* diffusion */ return string_compose (_("%1%%"), (int) floor (100.0 * fabs(val))); - + + case PanElevationAutomation: /* elevation */ + return string_compose (_("%1\u00B0"), (int) floor (90.0 * fabs(val))); + default: - return _pannable->value_as_string (ac); + return _("unused"); } } @@ -472,7 +451,7 @@ VBAPanner::signal_position (uint32_t n) const } boost::shared_ptr -VBAPanner::get_speakers () const +VBAPanner::get_speakers () const { return _speakers->parent(); } @@ -480,28 +459,35 @@ VBAPanner::get_speakers () const void VBAPanner::set_position (double p) { - if (p < 0.0) { - p = 1.0 + p; - } - - if (p > 1.0) { - p = fmod (p, 1.0); - } - - _pannable->pan_azimuth_control->set_value (p); + /* map into 0..1 range */ + int over = p; + over -= (p >= 0) ? 0 : 1; + p -= (double)over; + _pannable->pan_azimuth_control->set_value (p, Controllable::NoGroup); } void VBAPanner::set_width (double w) { - _pannable->pan_width_control->set_value (min (1.0, max (-1.0, w))); + _pannable->pan_width_control->set_value (min (1.0, max (-1.0, w)), Controllable::NoGroup); +} + +void +VBAPanner::set_elevation (double e) +{ + _pannable->pan_elevation_control->set_value (min (1.0, max (0.0, e)), Controllable::NoGroup); } void VBAPanner::reset () { - set_position (0); - set_width (1); + set_position (.5); + if (_signals.size() > 1) { + set_width (1.0 - (1.0 / (double)_signals.size())); + } else { + set_width (1.0); + } + set_elevation (0); update (); }