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/1in2out | |
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/1in2out')
-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 |
3 files changed, 235 insertions, 55 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() + |