2 Copyright (C) 2004-2011 Paul Davis
4 This program is free software; you can redistribute it and/or modify
5 it under the terms of the GNU General Public License as published by
6 the Free Software Foundation; either version 2 of the License, or
7 (at your option) any later version.
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 GNU General Public License for more details.
14 You should have received a copy of the GNU General Public License
15 along with this program; if not, write to the Free Software
16 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
35 #include "pbd/cartesian.h"
36 #include "pbd/convert.h"
37 #include "pbd/error.h"
38 #include "pbd/failed_constructor.h"
39 #include "pbd/xml++.h"
40 #include "pbd/enumwriter.h"
42 #include "evoral/Curve.hpp"
44 #include "ardour/session.h"
45 #include "ardour/panner.h"
46 #include "ardour/utils.h"
47 #include "ardour/audio_buffer.h"
49 #include "ardour/debug.h"
50 #include "ardour/runtime_functions.h"
51 #include "ardour/buffer_set.h"
52 #include "ardour/audio_buffer.h"
53 #include "ardour/pannable.h"
54 #include "ardour/visibility.h"
57 #include "panner_1in2out.h"
59 #include "pbd/mathfix.h"
62 using namespace ARDOUR;
65 static PanPluginDescriptor _descriptor = {
66 "Mono to Stereo Panner",
68 Panner1in2out::factory
71 extern "C" ARDOURPANNER_API PanPluginDescriptor* panner_descriptor () { return &_descriptor; }
73 Panner1in2out::Panner1in2out (boost::shared_ptr<Pannable> p)
76 if (!_pannable->has_state()) {
77 _pannable->pan_azimuth_control->set_value (0.5);
83 right = desired_right;
87 _pannable->pan_azimuth_control->Changed.connect_same_thread (*this, boost::bind (&Panner1in2out::update, this));
90 Panner1in2out::~Panner1in2out ()
95 Panner1in2out::update ()
98 float const pan_law_attenuation = -3.0f;
99 float const scale = 2.0f - 4.0f * powf (10.0f,pan_law_attenuation/20.0f);
101 panR = _pannable->pan_azimuth_control->get_value();
104 desired_left = panL * (scale * panL + 1.0f - scale);
105 desired_right = panR * (scale * panR + 1.0f - scale);
109 Panner1in2out::set_position (double p)
111 if (clamp_position (p)) {
112 _pannable->pan_azimuth_control->set_value (p);
117 Panner1in2out::clamp_position (double& p)
119 /* any position between 0.0 and 1.0 is legal */
120 DEBUG_TRACE (DEBUG::Panning, string_compose ("want to move panner to %1 - always allowed in 0.0-1.0 range\n", p));
121 p = max (min (p, 1.0), 0.0);
126 Panner1in2out::position_range () const
128 return make_pair (0, 1);
132 Panner1in2out::position () const
134 return _pannable->pan_azimuth_control->get_value ();
138 Panner1in2out::distribute_one (AudioBuffer& srcbuf, BufferSet& obufs, gain_t gain_coeff, pframes_t nframes, uint32_t /* not used */)
140 assert (obufs.count().n_audio() == 2);
146 Sample* const src = srcbuf.data();
150 dst = obufs.get_audio(0).data();
152 if (fabsf ((delta = (left - desired_left))) > 0.002) { // about 1 degree of arc
154 /* we've moving the pan by an appreciable amount, so we must
155 interpolate over 64 frames or nframes, whichever is smaller */
157 pframes_t const limit = min ((pframes_t) 64, nframes);
160 delta = -(delta / (float) (limit));
162 for (n = 0; n < limit; n++) {
163 left_interp = left_interp + delta;
164 left = left_interp + 0.9 * (left - left_interp);
165 dst[n] += src[n] * left * gain_coeff;
168 /* then pan the rest of the buffer; no need for interpolation for this bit */
170 pan = left * gain_coeff;
172 mix_buffers_with_gain (dst+n,src+n,nframes-n,pan);
179 if ((pan = (left * gain_coeff)) != 1.0f) {
183 /* pan is 1 but also not 0, so we must do it "properly" */
185 mix_buffers_with_gain(dst,src,nframes,pan);
187 /* mark that we wrote into the buffer */
195 /* pan is 1 so we can just copy the input samples straight in */
197 mix_buffers_no_gain(dst,src,nframes);
199 /* XXX it would be nice to mark that we wrote into the buffer */
205 dst = obufs.get_audio(1).data();
207 if (fabsf ((delta = (right - desired_right))) > 0.002) { // about 1 degree of arc
209 /* we're moving the pan by an appreciable amount, so we must
210 interpolate over 64 frames or nframes, whichever is smaller */
212 pframes_t const limit = min ((pframes_t) 64, nframes);
215 delta = -(delta / (float) (limit));
217 for (n = 0; n < limit; n++) {
218 right_interp = right_interp + delta;
219 right = right_interp + 0.9 * (right - right_interp);
220 dst[n] += src[n] * right * gain_coeff;
223 /* then pan the rest of the buffer, no need for interpolation for this bit */
225 pan = right * gain_coeff;
227 mix_buffers_with_gain(dst+n,src+n,nframes-n,pan);
229 /* XXX it would be nice to mark the buffer as written to */
233 right = desired_right;
234 right_interp = right;
236 if ((pan = (right * gain_coeff)) != 1.0f) {
240 /* pan is not 1 but also not 0, so we must do it "properly" */
242 mix_buffers_with_gain(dst,src,nframes,pan);
244 /* XXX it would be nice to mark the buffer as written to */
249 /* pan is 1 so we can just copy the input samples straight in */
251 mix_buffers_no_gain(dst,src,nframes);
253 /* XXX it would be nice to mark the buffer as written to */
260 Panner1in2out::distribute_one_automated (AudioBuffer& srcbuf, BufferSet& obufs,
261 framepos_t start, framepos_t end, pframes_t nframes,
262 pan_t** buffers, uint32_t which)
264 assert (obufs.count().n_audio() == 2);
268 Sample* const src = srcbuf.data();
269 pan_t* const position = buffers[0];
271 /* fetch positional data */
273 if (!_pannable->pan_azimuth_control->list()->curve().rt_safe_get_vector (start, end, position, nframes)) {
275 distribute_one (srcbuf, obufs, 1.0, nframes, which);
279 /* apply pan law to convert positional data into pan coefficients for
283 const float pan_law_attenuation = -3.0f;
284 const float scale = 2.0f - 4.0f * powf (10.0f,pan_law_attenuation/20.0f);
286 for (pframes_t n = 0; n < nframes; ++n) {
288 float panR = position[n];
289 const float panL = 1 - panR;
291 /* note that are overwriting buffers, but its OK
292 because we're finished with their old contents
293 (position automation data) and are
294 replacing it with panning/gain coefficients
295 that we need to actually process the data.
298 buffers[0][n] = panL * (scale * panL + 1.0f - scale);
299 buffers[1][n] = panR * (scale * panR + 1.0f - scale);
304 dst = obufs.get_audio(0).data();
307 for (pframes_t n = 0; n < nframes; ++n) {
308 dst[n] += src[n] * pbuf[n];
311 /* XXX it would be nice to mark the buffer as written to */
315 dst = obufs.get_audio(1).data();
318 for (pframes_t n = 0; n < nframes; ++n) {
319 dst[n] += src[n] * pbuf[n];
322 /* XXX it would be nice to mark the buffer as written to */
327 Panner1in2out::factory (boost::shared_ptr<Pannable> p, boost::shared_ptr<Speakers> /* ignored */)
329 return new Panner1in2out (p);
333 Panner1in2out::get_state ()
335 XMLNode& root (Panner::get_state ());
336 root.add_property (X_("type"), _descriptor.name);
341 std::set<Evoral::Parameter>
342 Panner1in2out::what_can_be_automated() const
344 set<Evoral::Parameter> s;
345 s.insert (Evoral::Parameter (PanAzimuthAutomation));
350 Panner1in2out::describe_parameter (Evoral::Parameter p)
353 case PanAzimuthAutomation:
356 return _pannable->describe_parameter (p);
361 Panner1in2out::value_as_string (boost::shared_ptr<AutomationControl> ac) const
363 /* DO NOT USE LocaleGuard HERE */
364 double val = ac->get_value();
366 switch (ac->parameter().type()) {
367 case PanAzimuthAutomation:
368 /* We show the position of the center of the image relative to the left & right.
369 This is expressed as a pair of percentage values that ranges from (100,0)
370 (hard left) through (50,50) (hard center) to (0,100) (hard right).
372 This is pretty wierd, but its the way audio engineers expect it. Just remember that
373 the center of the USA isn't Kansas, its (50LA, 50NY) and it will all make sense.
375 This is designed to be as narrow as possible. Dedicated
376 panner GUIs can do their own version of this if they need
377 something less compact.
380 return string_compose (_("L%1R%2"), (int) rint (100.0 * (1.0 - val)),
381 (int) rint (100.0 * val));
384 return _pannable->value_as_string (ac);
389 Panner1in2out::reset ()