diff options
author | Paul Davis <paul@linuxaudiosystems.com> | 2011-01-27 01:31:03 +0000 |
---|---|---|
committer | Paul Davis <paul@linuxaudiosystems.com> | 2011-01-27 01:31:03 +0000 |
commit | 15b5fce90480490455237da917167b0bcb5ce946 (patch) | |
tree | 5c1c5929a83c05db1a901e775fefe4f6cf8dc1b7 /libs/panners | |
parent | 1385643131a2b2231bbbc0c584c76883fcfb580a (diff) |
merge 3.0-panexp (pan experiments) branch, revisions 8534-8585 into 3.0, thus ending 3.0-panexp. THIS COMMIT WILL BREAK ALL EXISTING 3.0 SESSIONS IN SOME WAY (possibly not fatally).
git-svn-id: svn://localhost/ardour2/branches/3.0@8586 d708f5d6-7413-0410-9779-e7cbd77b26cf
Diffstat (limited to 'libs/panners')
-rw-r--r-- | libs/panners/1in2out/panner_1in2out.cc | 223 | ||||
-rw-r--r-- | libs/panners/1in2out/panner_1in2out.h | 32 | ||||
-rw-r--r-- | libs/panners/1in2out/wscript | 35 | ||||
-rw-r--r-- | libs/panners/2in2out/panner_2in2out.cc | 82 | ||||
-rw-r--r-- | libs/panners/2in2out/panner_2in2out.h | 4 | ||||
-rw-r--r-- | libs/panners/2in2out/wscript | 6 | ||||
-rw-r--r-- | libs/panners/vbap/vbap.cc | 183 | ||||
-rw-r--r-- | libs/panners/vbap/vbap.h | 26 | ||||
-rw-r--r-- | libs/panners/vbap/vbap_speakers.cc | 3 | ||||
-rw-r--r-- | libs/panners/vbap/wscript | 35 | ||||
-rw-r--r-- | libs/panners/wscript | 15 |
11 files changed, 450 insertions, 194 deletions
diff --git a/libs/panners/1in2out/panner_1in2out.cc b/libs/panners/1in2out/panner_1in2out.cc index 2851aec095..7a59aa04cc 100644 --- a/libs/panners/1in2out/panner_1in2out.cc +++ b/libs/panners/1in2out/panner_1in2out.cc @@ -43,16 +43,17 @@ #include "ardour/session.h" #include "ardour/panner.h" -#include "ardour/panner_1in2out.h" #include "ardour/utils.h" #include "ardour/audio_buffer.h" +#include "ardour/debug.h" #include "ardour/runtime_functions.h" #include "ardour/buffer_set.h" #include "ardour/audio_buffer.h" -#include "ardour/vbap.h" +#include "ardour/pannable.h" #include "i18n.h" +#include "panner_1in2out.h" #include "pbd/mathfix.h" @@ -62,22 +63,27 @@ using namespace PBD; static PanPluginDescriptor _descriptor = { "Mono to Stereo Panner", - 1, 1, 2, 2, + 1, 2, Panner1in2out::factory }; -extern "C" { PanPluginDescriptor* panner_descriptor () { return &_descriptor; } +extern "C" { PanPluginDescriptor* panner_descriptor () { return &_descriptor; } } -Panner1in2out::Panner1in2out (PannerShell& p) +Panner1in2out::Panner1in2out (boost::shared_ptr<Pannable> p) : Panner (p) - , _position (new PanControllable (parent.session(), _("position"), this, Evoral::Parameter(PanAzimuthAutomation, 0, 0))) - , left (0.5) - , right (0.5) - , left_interp (left) - , right_interp (right) { - desired_left = left; - desired_right = right; + if (!_pannable->has_state()) { + _pannable->pan_azimuth_control->set_value (0.5); + } + + update (); + + left = desired_left; + right = desired_right; + left_interp = left; + right_interp = right; + + _pannable->pan_azimuth_control->Changed.connect_same_thread (*this, boost::bind (&Panner1in2out::update, this)); } Panner1in2out::~Panner1in2out () @@ -85,14 +91,44 @@ Panner1in2out::~Panner1in2out () } void +Panner1in2out::update () +{ + float panR, panL; + float const pan_law_attenuation = -3.0f; + float const scale = 2.0f - 4.0f * powf (10.0f,pan_law_attenuation/20.0f); + + panR = _pannable->pan_azimuth_control->get_value(); + panL = 1 - panR; + + desired_left = panL * (scale * panL + 1.0f - scale); + desired_right = panR * (scale * panR + 1.0f - scale); +} + +void Panner1in2out::set_position (double p) { - _desired_right = p; - _desired_left = 1 - p; + if (clamp_position (p)) { + _pannable->pan_azimuth_control->set_value (p); + } +} + +bool +Panner1in2out::clamp_position (double& p) +{ + /* any position between 0.0 and 1.0 is legal */ + DEBUG_TRACE (DEBUG::Panning, string_compose ("want to move panner to %1 - always allowed in 0.0-1.0 range\n", p)); + p = max (min (p, 1.0), 0.0); + return true; +} + +double +Panner1in2out::position () const +{ + return _pannable->pan_azimuth_control->get_value (); } void -Panner1in2out::do_distribute_one (AudioBuffer& srcbuf, BufferSet& obufs, gain_t gain_coeff, pframes_t nframes, uint32_t /* not used */) +Panner1in2out::distribute_one (AudioBuffer& srcbuf, BufferSet& obufs, gain_t gain_coeff, pframes_t nframes, uint32_t /* not used */) { assert (obufs.count().n_audio() == 2); @@ -100,17 +136,13 @@ Panner1in2out::do_distribute_one (AudioBuffer& srcbuf, BufferSet& obufs, gain_t Sample* dst; pan_t pan; - if (_muted) { - return; - } - Sample* const src = srcbuf.data(); /* LEFT OUTPUT */ dst = obufs.get_audio(0).data(); - if (fabsf ((delta = (left[which] - desired_left[which]))) > 0.002) { // about 1 degree of arc + if (fabsf ((delta = (left - desired_left))) > 0.002) { // about 1 degree of arc /* we've moving the pan by an appreciable amount, so we must interpolate over 64 frames or nframes, whichever is smaller */ @@ -121,23 +153,23 @@ Panner1in2out::do_distribute_one (AudioBuffer& srcbuf, BufferSet& obufs, gain_t delta = -(delta / (float) (limit)); for (n = 0; n < limit; n++) { - left_interp[which] = left_interp[which] + delta; - left = left_interp[which] + 0.9 * (left[which] - left_interp[which]); - dst[n] += src[n] * left[which] * gain_coeff; + left_interp = left_interp + delta; + left = left_interp + 0.9 * (left - left_interp); + dst[n] += src[n] * left * gain_coeff; } /* then pan the rest of the buffer; no need for interpolation for this bit */ - pan = left[which] * gain_coeff; + pan = left * gain_coeff; mix_buffers_with_gain (dst+n,src+n,nframes-n,pan); } else { - left[which] = desired_left[which]; - left_interp[which] = left[which]; + left = desired_left; + left_interp = left; - if ((pan = (left[which] * gain_coeff)) != 1.0f) { + if ((pan = (left * gain_coeff)) != 1.0f) { if (pan != 0.0f) { @@ -165,7 +197,7 @@ Panner1in2out::do_distribute_one (AudioBuffer& srcbuf, BufferSet& obufs, gain_t dst = obufs.get_audio(1).data(); - if (fabsf ((delta = (right[which] - desired_right[which]))) > 0.002) { // about 1 degree of arc + if (fabsf ((delta = (right - desired_right))) > 0.002) { // about 1 degree of arc /* we're moving the pan by an appreciable amount, so we must interpolate over 64 frames or nframes, whichever is smaller */ @@ -176,14 +208,14 @@ Panner1in2out::do_distribute_one (AudioBuffer& srcbuf, BufferSet& obufs, gain_t delta = -(delta / (float) (limit)); for (n = 0; n < limit; n++) { - right_interp[which] = right_interp[which] + delta; - right[which] = right_interp[which] + 0.9 * (right[which] - right_interp[which]); - dst[n] += src[n] * right[which] * gain_coeff; + right_interp = right_interp + delta; + right = right_interp + 0.9 * (right - right_interp); + dst[n] += src[n] * right * gain_coeff; } /* then pan the rest of the buffer, no need for interpolation for this bit */ - pan = right[which] * gain_coeff; + pan = right * gain_coeff; mix_buffers_with_gain(dst+n,src+n,nframes-n,pan); @@ -191,10 +223,10 @@ Panner1in2out::do_distribute_one (AudioBuffer& srcbuf, BufferSet& obufs, gain_t } else { - right[which] = desired_right[which]; - right_interp[which] = right[which]; + right = desired_right; + right_interp = right; - if ((pan = (right[which] * gain_coeff)) != 1.0f) { + if ((pan = (right * gain_coeff)) != 1.0f) { if (pan != 0.0f) { @@ -217,19 +249,116 @@ Panner1in2out::do_distribute_one (AudioBuffer& srcbuf, BufferSet& obufs, gain_t } +void +Panner1in2out::distribute_one_automated (AudioBuffer& srcbuf, BufferSet& obufs, + framepos_t start, framepos_t end, pframes_t nframes, + pan_t** buffers, uint32_t which) +{ + assert (obufs.count().n_audio() == 2); + + Sample* dst; + pan_t* pbuf; + Sample* const src = srcbuf.data(); + pan_t* const position = buffers[0]; + + /* fetch positional data */ + + if (!_pannable->pan_azimuth_control->list()->curve().rt_safe_get_vector (start, end, position, nframes)) { + /* fallback */ + distribute_one (srcbuf, obufs, 1.0, nframes, which); + return; + } + + /* apply pan law to convert positional data into pan coefficients for + each buffer (output) + */ + + const float pan_law_attenuation = -3.0f; + const float scale = 2.0f - 4.0f * powf (10.0f,pan_law_attenuation/20.0f); + + for (pframes_t n = 0; n < nframes; ++n) { + + float panR = position[n]; + const float panL = 1 - panR; + + /* note that are overwriting buffers, but its OK + because we're finished with their old contents + (position automation data) and are + replacing it with panning/gain coefficients + that we need to actually process the data. + */ + + buffers[0][n] = panL * (scale * panL + 1.0f - scale); + buffers[1][n] = panR * (scale * panR + 1.0f - scale); + } + + /* LEFT OUTPUT */ + + dst = obufs.get_audio(0).data(); + pbuf = buffers[0]; + + for (pframes_t n = 0; n < nframes; ++n) { + dst[n] += src[n] * pbuf[n]; + } + + /* XXX it would be nice to mark the buffer as written to */ + + /* RIGHT OUTPUT */ + + dst = obufs.get_audio(1).data(); + pbuf = buffers[1]; + + for (pframes_t n = 0; n < nframes; ++n) { + dst[n] += src[n] * pbuf[n]; + } + + /* XXX it would be nice to mark the buffer as written to */ +} + + +Panner* +Panner1in2out::factory (boost::shared_ptr<Pannable> p, Speakers& /* ignored */) +{ + return new Panner1in2out (p); +} + +XMLNode& +Panner1in2out::get_state (void) +{ + return state (true); +} + +XMLNode& +Panner1in2out::state (bool /*full_state*/) +{ + XMLNode& root (Panner::get_state ()); + root.add_property (X_("type"), _descriptor.name); + return root; +} + +int +Panner1in2out::set_state (const XMLNode& node, int version) +{ + LocaleGuard lg (X_("POSIX")); + Panner::set_state (node, version); + return 0; +} + +std::set<Evoral::Parameter> +Panner1in2out::what_can_be_automated() const +{ + set<Evoral::Parameter> s; + s.insert (Evoral::Parameter (PanAzimuthAutomation)); + return s; +} + string -Panner1in2out::describe_parameter (Evoral::Parameter param) +Panner1in2out::describe_parameter (Evoral::Parameter p) { - switch (param.type()) { - case PanWidthAutomation: - return "Pan:width"; + switch (p.type()) { case PanAzimuthAutomation: - return "Pan:position"; - case PanElevationAutomation: - error << X_("stereo panner should not have elevation control") << endmsg; - return "Pan:elevation"; - } - - return Automatable::describe_parameter (param); + return _("L/R"); + default: + return _pannable->describe_parameter (p); + } } - diff --git a/libs/panners/1in2out/panner_1in2out.h b/libs/panners/1in2out/panner_1in2out.h index 152eb7156a..ced467c11b 100644 --- a/libs/panners/1in2out/panner_1in2out.h +++ b/libs/panners/1in2out/panner_1in2out.h @@ -31,40 +31,56 @@ #include "pbd/cartesian.h" #include "ardour/types.h" -#include "ardour/automation_control.h" -#include "ardour/automatable.h" +#include "ardour/panner.h" namespace ARDOUR { -class PannerStereoBase : public class Panner +class Panner1in2out : public Panner { public: - PannerStereoBase (Panner&); - ~PannerStereoBase (); + Panner1in2out (boost::shared_ptr<Pannable>); + ~Panner1in2out (); void set_position (double); + bool clamp_position (double&); + + double position() const; ChanCount in() const { return ChanCount (DataType::AUDIO, 1); } ChanCount out() const { return ChanCount (DataType::AUDIO, 2); } + std::set<Evoral::Parameter> what_can_be_automated() const; + /* this class just leaves the pan law itself to be defined by the update(), do_distribute_automated() methods. derived classes also need a factory method and a type name. See EqualPowerStereoPanner as an example. */ - void do_distribute (AudioBuffer& src, BufferSet& obufs, gain_t gain_coeff, pframes_t nframes); + static Panner* factory (boost::shared_ptr<Pannable>, Speakers&); + + std::string describe_parameter (Evoral::Parameter); + + XMLNode& state (bool full_state); + XMLNode& get_state (void); + int set_state (const XMLNode&, int version); protected: - boost::shared_ptr<AutomationControl> _position; float left; float right; float desired_left; float desired_right; float left_interp; float right_interp; + + void distribute_one (AudioBuffer& src, BufferSet& obufs, gain_t gain_coeff, pframes_t nframes, uint32_t which); + void distribute_one_automated (AudioBuffer& srcbuf, BufferSet& obufs, + framepos_t start, framepos_t end, pframes_t nframes, + pan_t** buffers, uint32_t which); + + void update (); }; -} +} // namespace #endif /* __ardour_panner_1in2out_h__ */ diff --git a/libs/panners/1in2out/wscript b/libs/panners/1in2out/wscript new file mode 100644 index 0000000000..e7e4d29928 --- /dev/null +++ b/libs/panners/1in2out/wscript @@ -0,0 +1,35 @@ +#!/usr/bin/env python +import autowaf +import os + +# Library version (UNIX style major, minor, micro) +# major increment <=> incompatible changes +# minor increment <=> compatible changes (additions) +# micro increment <=> no interface changes +LIBARDOUR_PAN1IN2OUT_LIB_VERSION = '1.0.0' + +# Mandatory variables +srcdir = '.' +blddir = 'build' + +def set_options(opt): + autowaf.set_options(opt) + +def configure(conf): + autowaf.configure(conf) + +def build(bld): + obj = bld.new_task_gen('cxx', 'shlib') + obj.source = [ 'panner_1in2out.cc' ] + obj.export_incdirs = ['.'] + obj.cxxflags = '-DPACKAGE="libardour_pan1in2out"' + obj.includes = ['.'] + obj.name = 'libardour_pan1in2out' + obj.target = 'pan1in2out' + obj.uselib_local = 'libardour libardour_cp libpbd' + obj.vnum = LIBARDOUR_PAN1IN2OUT_LIB_VERSION + obj.install_path = os.path.join(bld.env['LIBDIR'], 'ardour3', 'panners') + +def shutdown(): + autowaf.shutdown() + diff --git a/libs/panners/2in2out/panner_2in2out.cc b/libs/panners/2in2out/panner_2in2out.cc index 6bc0f93a8f..2c2856361c 100644 --- a/libs/panners/2in2out/panner_2in2out.cc +++ b/libs/panners/2in2out/panner_2in2out.cc @@ -71,21 +71,21 @@ extern "C" { PanPluginDescriptor* panner_descriptor () { return &_descriptor; } Panner2in2out::Panner2in2out (boost::shared_ptr<Pannable> p) : Panner (p) { - _pannable->pan_azimuth_control->set_value (0.5); - _pannable->pan_width_control->set_value (1.0); - - /* LEFT SIGNAL, panned hard left */ - left[0] = 1.0; - right[0] = 0.0; - desired_left[0] = left_interp[0] = left[0]; - desired_right[0] = right_interp[0] = right[0]; - - /* RIGHT SIGNAL, panned hard right */ - left[1] = 0; - right[1] = 1.0; - desired_left[1] = left_interp[1] = left[1]; - desired_right[1] = right_interp[1] = right[1]; - + if (!_pannable->has_state()) { + _pannable->pan_azimuth_control->set_value (0.5); + _pannable->pan_width_control->set_value (1.0); + } + + update (); + + /* LEFT SIGNAL */ + left_interp[0] = left[0] = desired_left[0]; + right_interp[0] = right[0] = desired_right[0]; + + /* RIGHT SIGNAL */ + left_interp[1] = left[1] = desired_left[1]; + right_interp[1] = right[1] = desired_right[1]; + _pannable->pan_azimuth_control->Changed.connect_same_thread (*this, boost::bind (&Panner2in2out::update, this)); _pannable->pan_width_control->Changed.connect_same_thread (*this, boost::bind (&Panner2in2out::update, this)); } @@ -141,8 +141,6 @@ Panner2in2out::update () const double width = _pannable->pan_width_control->get_value(); const double direction_as_lr_fract = _pannable->pan_azimuth_control->get_value(); - cerr << "new pan values width=" << width << " LR = " << direction_as_lr_fract << endl; - if (width < 0.0) { pos[0] = direction_as_lr_fract + (width/2.0); // left signal lr_fract pos[1] = direction_as_lr_fract - (width/2.0); // right signal lr_fract @@ -190,20 +188,14 @@ Panner2in2out::clamp_width (double& w) bool Panner2in2out::clamp_stereo_pan (double& direction_as_lr_fract, double& width) { - double r_pos = direction_as_lr_fract + (width/2.0); - double l_pos = direction_as_lr_fract - (width/2.0); - bool can_move_left = true; - bool can_move_right = true; - - cerr << "Clamp pos = " << direction_as_lr_fract << " w = " << width << endl; + double r_pos; + double l_pos; - if (width > 1.0 || width < 1.0) { - return false; - } + width = max (min (width, 1.0), -1.0); + direction_as_lr_fract = max (min (direction_as_lr_fract, 1.0), 0.0); - if (direction_as_lr_fract > 1.0 || direction_as_lr_fract < 0.0) { - return false; - } + r_pos = direction_as_lr_fract + (width/2.0); + l_pos = direction_as_lr_fract - (width/2.0); if (width < 0.0) { swap (r_pos, l_pos); @@ -213,19 +205,20 @@ Panner2in2out::clamp_stereo_pan (double& direction_as_lr_fract, double& width) is already there, we're not moving the left signal. */ - if (l_pos <= 0.0 && desired_left[0] <= 0.0) { - can_move_left = false; + if (l_pos < 0.0) { + return false; } /* if the new right position is less than or equal to 1.0 (hard right) and the right panner is already there, we're not moving the right signal. */ - if (r_pos >= 1.0 && desired_right[1] >= 1.0) { - can_move_right = false; + if (r_pos > 1.0) { + return false; + } - return can_move_left && can_move_right; + return true; } void @@ -459,3 +452,24 @@ Panner2in2out::set_state (const XMLNode& node, int version) return 0; } +std::set<Evoral::Parameter> +Panner2in2out::what_can_be_automated() const +{ + set<Evoral::Parameter> s; + s.insert (Evoral::Parameter (PanAzimuthAutomation)); + s.insert (Evoral::Parameter (PanWidthAutomation)); + return s; +} + +string +Panner2in2out::describe_parameter (Evoral::Parameter p) +{ + switch (p.type()) { + case PanAzimuthAutomation: + return _("L/R"); + case PanWidthAutomation: + return _("Width"); + default: + return _pannable->describe_parameter (p); + } +} diff --git a/libs/panners/2in2out/panner_2in2out.h b/libs/panners/2in2out/panner_2in2out.h index 8d8d57d709..0bb38fa27f 100644 --- a/libs/panners/2in2out/panner_2in2out.h +++ b/libs/panners/2in2out/panner_2in2out.h @@ -55,8 +55,12 @@ class Panner2in2out : public Panner double position () const; double width () const; + std::set<Evoral::Parameter> what_can_be_automated() const; + static Panner* factory (boost::shared_ptr<Pannable>, Speakers&); + std::string describe_parameter (Evoral::Parameter); + XMLNode& state (bool full_state); XMLNode& get_state (void); int set_state (const XMLNode&, int version); diff --git a/libs/panners/2in2out/wscript b/libs/panners/2in2out/wscript index 509848e5a6..22d5c03726 100644 --- a/libs/panners/2in2out/wscript +++ b/libs/panners/2in2out/wscript @@ -12,6 +12,12 @@ LIBARDOUR_PAN2IN2OUT_LIB_VERSION = '1.0.0' srcdir = '.' blddir = 'build' +def set_options(opt): + autowaf.set_options(opt) + +def configure(conf): + autowaf.configure(conf) + def build(bld): obj = bld.new_task_gen('cxx', 'shlib') obj.source = [ 'panner_2in2out.cc' ] diff --git a/libs/panners/vbap/vbap.cc b/libs/panners/vbap/vbap.cc index 1876f4cf44..6088a2d0d6 100644 --- a/libs/panners/vbap/vbap.cc +++ b/libs/panners/vbap/vbap.cc @@ -10,27 +10,26 @@ #include "ardour/pannable.h" #include "ardour/speakers.h" -#include "ardour/vbap.h" -#include "ardour/vbap_speakers.h" #include "ardour/audio_buffer.h" #include "ardour/buffer_set.h" #include "ardour/pan_controllable.h" +#include "vbap.h" +#include "vbap_speakers.h" + using namespace PBD; using namespace ARDOUR; using namespace std; static PanPluginDescriptor _descriptor = { "VBAP 2D panner", - 1, -1, 2, -1, + -1, -1, VBAPanner::factory }; extern "C" { PanPluginDescriptor* panner_descriptor () { return &_descriptor; } } VBAPanner::Signal::Signal (Session& session, VBAPanner& p, uint32_t n) - : azimuth_control (new PanControllable (session, string_compose (_("azimuth %1"), n+1), &p, Evoral::Parameter (PanAzimuthAutomation, 0, n))) - , elevation_control (new PanControllable (session, string_compose (_("elevation %1"), n+1), &p, Evoral::Parameter (PanElevationAutomation, 0, n))) { gains[0] = gains[1] = gains[2] = 0; desired_gains[0] = desired_gains[1] = desired_gains[2] = 0; @@ -40,44 +39,86 @@ VBAPanner::Signal::Signal (Session& session, VBAPanner& p, uint32_t n) VBAPanner::VBAPanner (boost::shared_ptr<Pannable> p, Speakers& s) : Panner (p) - , _dirty (true) , _speakers (VBAPSpeakers::instance (s)) { + _pannable->pan_azimuth_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)); + + update (); } VBAPanner::~VBAPanner () { + clear_signals (); +} + +void +VBAPanner::clear_signals () +{ for (vector<Signal*>::iterator i = _signals.begin(); i != _signals.end(); ++i) { delete *i; } + _signals.clear (); } void -VBAPanner::configure_io (const ChanCount& in, const ChanCount& /* ignored - we use Speakers */) +VBAPanner::configure_io (ChanCount in, ChanCount /* ignored - we use Speakers */) { uint32_t n = in.n_audio(); - /* 2d panning: spread signals equally around a circle */ - - double degree_step = 360.0 / _speakers.n_speakers(); - 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 (n % 2) { - deg = 90.0 - degree_step; - } else { - deg = 90.0; - } + clear_signals (); - _signals.clear (); - for (uint32_t i = 0; i < n; ++i) { _signals.push_back (new Signal (_pannable->session(), *this, i)); - _signals[i]->direction = AngularVector (deg, 0.0); - deg += degree_step; + } + + update (); +} + +void +VBAPanner::update () +{ + /* recompute signal directions based on panner azimuth and 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; + + /* 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) + */ + + double w = fabs (_pannable->pan_width_control->get_value()) * 360.0; + + double min_dir = center - w; + min_dir = max (min (min_dir, 360.0), 0.0); + + double max_dir = center + w; + max_dir = max (min (max_dir, 360.0), 0.0); + + double degree_step_per_signal = (max_dir - min_dir) / _signals.size(); + double signal_direction = min_dir; + + for (vector<Signal*>::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); + cerr << " @ " << signal->direction.azi << " /= " << signal->direction.ele + << " Outputs: " + << signal->desired_outputs[0] + 1 << ' ' + << signal->desired_outputs[1] + 1 << ' ' + << " Gains " + << signal->desired_gains[0] << ' ' + << signal->desired_gains[1] << ' ' + << endl; + + signal_direction += degree_step_per_signal; } } @@ -141,49 +182,29 @@ VBAPanner::compute_gains (double gains[3], int speaker_ids[3], int azi, int ele) gains[1] /= power; gains[2] /= power; } - - _dirty = false; } void -VBAPanner::do_distribute (BufferSet& inbufs, BufferSet& obufs, gain_t gain_coefficient, pframes_t nframes) +VBAPanner::distribute (BufferSet& inbufs, BufferSet& obufs, gain_t gain_coefficient, pframes_t nframes) { - bool was_dirty = _dirty; uint32_t n; vector<Signal*>::iterator s; assert (inbufs.count().n_audio() == _signals.size()); - /* XXX need to handle mono case */ - for (s = _signals.begin(), n = 0; s != _signals.end(); ++s, ++n) { Signal* signal (*s); - if (was_dirty) { - compute_gains (signal->desired_gains, signal->desired_outputs, signal->direction.azi, signal->direction.ele); - cerr << " @ " << signal->direction.azi << " /= " << signal->direction.ele - << " Outputs: " - << signal->desired_outputs[0] + 1 << ' ' - << signal->desired_outputs[1] + 1 << ' ' - << " Gains " - << signal->desired_gains[0] << ' ' - << signal->desired_gains[1] << ' ' - << endl; - } - - do_distribute_one (inbufs.get_audio (n), obufs, gain_coefficient, nframes, n); - - if (was_dirty) { - memcpy (signal->gains, signal->desired_gains, sizeof (signal->gains)); - memcpy (signal->outputs, signal->desired_outputs, sizeof (signal->outputs)); - } + distribute_one (inbufs.get_audio (n), obufs, gain_coefficient, nframes, n); + + memcpy (signal->gains, signal->desired_gains, sizeof (signal->gains)); + memcpy (signal->outputs, signal->desired_outputs, sizeof (signal->outputs)); } } - void -VBAPanner::do_distribute_one (AudioBuffer& srcbuf, BufferSet& obufs, gain_t gain_coefficient, pframes_t nframes, uint32_t which) +VBAPanner::distribute_one (AudioBuffer& srcbuf, BufferSet& obufs, gain_t gain_coefficient, pframes_t nframes, uint32_t which) { Sample* const src = srcbuf.data(); Sample* dst; @@ -228,8 +249,8 @@ VBAPanner::do_distribute_one (AudioBuffer& srcbuf, BufferSet& obufs, gain_t gain } void -VBAPanner::do_distribute_one_automated (AudioBuffer& src, BufferSet& obufs, - framepos_t start, framepos_t end, pframes_t nframes, pan_t** buffers, uint32_t which) +VBAPanner::distribute_one_automated (AudioBuffer& src, BufferSet& obufs, + framepos_t start, framepos_t end, pframes_t nframes, pan_t** buffers, uint32_t which) { } @@ -253,46 +274,12 @@ VBAPanner::set_state (const XMLNode& node, int /*version*/) return 0; } -boost::shared_ptr<AutomationControl> -VBAPanner::azimuth_control (uint32_t n) -{ - if (n >= _signals.size()) { - return boost::shared_ptr<AutomationControl>(); - } - return _signals[n]->azimuth_control; -} - -boost::shared_ptr<AutomationControl> -VBAPanner::evelation_control (uint32_t n) -{ - if (n >= _signals.size()) { - return boost::shared_ptr<AutomationControl>(); - } - return _signals[n]->elevation_control; -} - Panner* VBAPanner::factory (boost::shared_ptr<Pannable> p, Speakers& s) { return new VBAPanner (p, s); } -string -VBAPanner::describe_parameter (Evoral::Parameter param) -{ - stringstream ss; - switch (param.type()) { - case PanElevationAutomation: - return string_compose ( _("Pan:elevation %1"), param.id() + 1); - case PanWidthAutomation: - return string_compose ( _("Pan:diffusion %1"), param.id() + 1); - case PanAzimuthAutomation: - return string_compose ( _("Pan:azimuth %1"), param.id() + 1); - } - - return Automatable::describe_parameter (param); -} - ChanCount VBAPanner::in() const { @@ -304,3 +291,25 @@ VBAPanner::out() const { return ChanCount (DataType::AUDIO, _speakers.n_speakers()); } + +std::set<Evoral::Parameter> +VBAPanner::what_can_be_automated() const +{ + set<Evoral::Parameter> s; + s.insert (Evoral::Parameter (PanAzimuthAutomation)); + s.insert (Evoral::Parameter (PanWidthAutomation)); + return s; +} + +string +VBAPanner::describe_parameter (Evoral::Parameter p) +{ + switch (p.type()) { + case PanAzimuthAutomation: + return _("Direction"); + case PanWidthAutomation: + return _("Diffusion"); + default: + return _pannable->describe_parameter (p); + } +} diff --git a/libs/panners/vbap/vbap.h b/libs/panners/vbap/vbap.h index aacff8894c..2b80b032cb 100644 --- a/libs/panners/vbap/vbap.h +++ b/libs/panners/vbap/vbap.h @@ -26,7 +26,8 @@ #include "ardour/panner.h" #include "ardour/panner_shell.h" -#include "ardour/vbap_speakers.h" + +#include "vbap_speakers.h" namespace ARDOUR { @@ -39,26 +40,24 @@ public: VBAPanner (boost::shared_ptr<Pannable>, Speakers& s); ~VBAPanner (); - void configure_io (const ChanCount& in, const ChanCount& /* ignored - we use Speakers */); + void configure_io (ChanCount in, ChanCount /* ignored - we use Speakers */); ChanCount in() const; ChanCount out() const; + std::set<Evoral::Parameter> what_can_be_automated() const; + static Panner* factory (boost::shared_ptr<Pannable>, Speakers& s); - void do_distribute (BufferSet& ibufs, BufferSet& obufs, gain_t gain_coeff, pframes_t nframes); - void do_distribute_automated (BufferSet& ibufs, BufferSet& obufs, - framepos_t start, framepos_t end, pframes_t nframes, pan_t** buffers); + void distribute (BufferSet& ibufs, BufferSet& obufs, gain_t gain_coeff, pframes_t nframes); void set_azimuth_elevation (double azimuth, double elevation); + std::string describe_parameter (Evoral::Parameter); + XMLNode& state (bool full_state); XMLNode& get_state (); int set_state (const XMLNode&, int version); - boost::shared_ptr<AutomationControl> azimuth_control (uint32_t signal); - boost::shared_ptr<AutomationControl> evelation_control (uint32_t signal); - - std::string describe_parameter (Evoral::Parameter param); private: struct Signal { @@ -67,20 +66,19 @@ private: double desired_gains[3]; int outputs[3]; int desired_outputs[3]; - boost::shared_ptr<AutomationControl> azimuth_control; - boost::shared_ptr<AutomationControl> elevation_control; Signal (Session&, VBAPanner&, uint32_t which); }; std::vector<Signal*> _signals; - bool _dirty; VBAPSpeakers& _speakers; void compute_gains (double g[3], int ls[3], int azi, int ele); + void update (); + void clear_signals (); - void do_distribute_one (AudioBuffer& src, BufferSet& obufs, gain_t gain_coeff, pframes_t nframes, uint32_t which); - void do_distribute_one_automated (AudioBuffer& src, BufferSet& obufs, + void distribute_one (AudioBuffer& src, BufferSet& obufs, gain_t gain_coeff, pframes_t nframes, uint32_t which); + void distribute_one_automated (AudioBuffer& src, BufferSet& obufs, framepos_t start, framepos_t end, pframes_t nframes, pan_t** buffers, uint32_t which); }; diff --git a/libs/panners/vbap/vbap_speakers.cc b/libs/panners/vbap/vbap_speakers.cc index 9090ed65e1..7e70e5df66 100644 --- a/libs/panners/vbap/vbap_speakers.cc +++ b/libs/panners/vbap/vbap_speakers.cc @@ -36,7 +36,8 @@ #include <stdlib.h> #include "pbd/cartesian.h" -#include "ardour/vbap_speakers.h" + +#include "vbap_speakers.h" using namespace ARDOUR; using namespace PBD; diff --git a/libs/panners/vbap/wscript b/libs/panners/vbap/wscript new file mode 100644 index 0000000000..1399039483 --- /dev/null +++ b/libs/panners/vbap/wscript @@ -0,0 +1,35 @@ +#!/usr/bin/env python +import autowaf +import os + +# Library version (UNIX style major, minor, micro) +# major increment <=> incompatible changes +# minor increment <=> compatible changes (additions) +# micro increment <=> no interface changes +LIBARDOUR_PANVBAP_LIB_VERSION = '1.0.0' + +# Mandatory variables +srcdir = '.' +blddir = 'build' + +def set_options(opt): + autowaf.set_options(opt) + +def configure(conf): + autowaf.configure(conf) + +def build(bld): + obj = bld.new_task_gen('cxx', 'shlib') + obj.source = [ 'vbap_speakers.cc', 'vbap.cc' ] + obj.export_incdirs = ['.'] + obj.cxxflags = '-DPACKAGE="libardour_panvbap"' + obj.includes = ['.'] + obj.name = 'libardour_panvbap' + obj.target = 'panvbap' + obj.uselib_local = 'libardour libardour_cp libpbd' + obj.vnum = LIBARDOUR_PANVBAP_LIB_VERSION + obj.install_path = os.path.join(bld.env['LIBDIR'], 'ardour3', 'panners') + +def shutdown(): + autowaf.shutdown() + diff --git a/libs/panners/wscript b/libs/panners/wscript index 63b547d093..192f27b7db 100644 --- a/libs/panners/wscript +++ b/libs/panners/wscript @@ -6,13 +6,22 @@ import os srcdir = '.' blddir = 'build' -#panners = [ '2in2out', 'vbap', '1in1out' ] -panners = [ '2in2out' ] +panners = [ '2in2out', '1in2out', 'vbap' ] def set_options(opt): autowaf.set_options(opt) +def sub_config_and_use(conf, name, has_objects = True): + conf.sub_config(name) + autowaf.set_local_lib(conf, name, has_objects) + +def configure(conf): + autowaf.set_recursive() + autowaf.configure(conf) + + for i in panners: + sub_config_and_use(conf, i) + def build(bld): for i in panners: bld.add_subdirs(i) - |