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"
56 #include "panner_1in2out.h"
58 #include "pbd/mathfix.h"
61 using namespace ARDOUR;
64 static PanPluginDescriptor _descriptor = {
65 "Mono to Stereo Panner",
67 Panner1in2out::factory
70 extern "C" { PanPluginDescriptor* panner_descriptor () { return &_descriptor; } }
72 Panner1in2out::Panner1in2out (boost::shared_ptr<Pannable> p)
75 if (!_pannable->has_state()) {
76 _pannable->pan_azimuth_control->set_value (0.5);
82 right = desired_right;
86 _pannable->pan_azimuth_control->Changed.connect_same_thread (*this, boost::bind (&Panner1in2out::update, this));
89 Panner1in2out::~Panner1in2out ()
94 Panner1in2out::update ()
97 float const pan_law_attenuation = -3.0f;
98 float const scale = 2.0f - 4.0f * powf (10.0f,pan_law_attenuation/20.0f);
100 panR = _pannable->pan_azimuth_control->get_value();
103 desired_left = panL * (scale * panL + 1.0f - scale);
104 desired_right = panR * (scale * panR + 1.0f - scale);
108 Panner1in2out::set_position (double p)
110 if (clamp_position (p)) {
111 _pannable->pan_azimuth_control->set_value (p);
116 Panner1in2out::clamp_position (double& p)
118 /* any position between 0.0 and 1.0 is legal */
119 DEBUG_TRACE (DEBUG::Panning, string_compose ("want to move panner to %1 - always allowed in 0.0-1.0 range\n", p));
120 p = max (min (p, 1.0), 0.0);
125 Panner1in2out::position_range () const
127 return make_pair (0, 1);
131 Panner1in2out::position () const
133 return _pannable->pan_azimuth_control->get_value ();
137 Panner1in2out::distribute_one (AudioBuffer& srcbuf, BufferSet& obufs, gain_t gain_coeff, pframes_t nframes, uint32_t /* not used */)
139 assert (obufs.count().n_audio() == 2);
145 Sample* const src = srcbuf.data();
149 dst = obufs.get_audio(0).data();
151 if (fabsf ((delta = (left - desired_left))) > 0.002) { // about 1 degree of arc
153 /* we've moving the pan by an appreciable amount, so we must
154 interpolate over 64 frames or nframes, whichever is smaller */
156 pframes_t const limit = min ((pframes_t) 64, nframes);
159 delta = -(delta / (float) (limit));
161 for (n = 0; n < limit; n++) {
162 left_interp = left_interp + delta;
163 left = left_interp + 0.9 * (left - left_interp);
164 dst[n] += src[n] * left * gain_coeff;
167 /* then pan the rest of the buffer; no need for interpolation for this bit */
169 pan = left * gain_coeff;
171 mix_buffers_with_gain (dst+n,src+n,nframes-n,pan);
178 if ((pan = (left * gain_coeff)) != 1.0f) {
182 /* pan is 1 but also not 0, so we must do it "properly" */
184 mix_buffers_with_gain(dst,src,nframes,pan);
186 /* mark that we wrote into the buffer */
194 /* pan is 1 so we can just copy the input samples straight in */
196 mix_buffers_no_gain(dst,src,nframes);
198 /* XXX it would be nice to mark that we wrote into the buffer */
204 dst = obufs.get_audio(1).data();
206 if (fabsf ((delta = (right - desired_right))) > 0.002) { // about 1 degree of arc
208 /* we're moving the pan by an appreciable amount, so we must
209 interpolate over 64 frames or nframes, whichever is smaller */
211 pframes_t const limit = min ((pframes_t) 64, nframes);
214 delta = -(delta / (float) (limit));
216 for (n = 0; n < limit; n++) {
217 right_interp = right_interp + delta;
218 right = right_interp + 0.9 * (right - right_interp);
219 dst[n] += src[n] * right * gain_coeff;
222 /* then pan the rest of the buffer, no need for interpolation for this bit */
224 pan = right * gain_coeff;
226 mix_buffers_with_gain(dst+n,src+n,nframes-n,pan);
228 /* XXX it would be nice to mark the buffer as written to */
232 right = desired_right;
233 right_interp = right;
235 if ((pan = (right * gain_coeff)) != 1.0f) {
239 /* pan is not 1 but also not 0, so we must do it "properly" */
241 mix_buffers_with_gain(dst,src,nframes,pan);
243 /* XXX it would be nice to mark the buffer as written to */
248 /* pan is 1 so we can just copy the input samples straight in */
250 mix_buffers_no_gain(dst,src,nframes);
252 /* XXX it would be nice to mark the buffer as written to */
259 Panner1in2out::distribute_one_automated (AudioBuffer& srcbuf, BufferSet& obufs,
260 framepos_t start, framepos_t end, pframes_t nframes,
261 pan_t** buffers, uint32_t which)
263 assert (obufs.count().n_audio() == 2);
267 Sample* const src = srcbuf.data();
268 pan_t* const position = buffers[0];
270 /* fetch positional data */
272 if (!_pannable->pan_azimuth_control->list()->curve().rt_safe_get_vector (start, end, position, nframes)) {
274 distribute_one (srcbuf, obufs, 1.0, nframes, which);
278 /* apply pan law to convert positional data into pan coefficients for
282 const float pan_law_attenuation = -3.0f;
283 const float scale = 2.0f - 4.0f * powf (10.0f,pan_law_attenuation/20.0f);
285 for (pframes_t n = 0; n < nframes; ++n) {
287 float panR = position[n];
288 const float panL = 1 - panR;
290 /* note that are overwriting buffers, but its OK
291 because we're finished with their old contents
292 (position automation data) and are
293 replacing it with panning/gain coefficients
294 that we need to actually process the data.
297 buffers[0][n] = panL * (scale * panL + 1.0f - scale);
298 buffers[1][n] = panR * (scale * panR + 1.0f - scale);
303 dst = obufs.get_audio(0).data();
306 for (pframes_t n = 0; n < nframes; ++n) {
307 dst[n] += src[n] * pbuf[n];
310 /* XXX it would be nice to mark the buffer as written to */
314 dst = obufs.get_audio(1).data();
317 for (pframes_t n = 0; n < nframes; ++n) {
318 dst[n] += src[n] * pbuf[n];
321 /* XXX it would be nice to mark the buffer as written to */
326 Panner1in2out::factory (boost::shared_ptr<Pannable> p, boost::shared_ptr<Speakers> /* ignored */)
328 return new Panner1in2out (p);
332 Panner1in2out::get_state ()
334 XMLNode& root (Panner::get_state ());
335 root.add_property (X_("type"), _descriptor.name);
340 std::set<Evoral::Parameter>
341 Panner1in2out::what_can_be_automated() const
343 set<Evoral::Parameter> s;
344 s.insert (Evoral::Parameter (PanAzimuthAutomation));
349 Panner1in2out::describe_parameter (Evoral::Parameter p)
352 case PanAzimuthAutomation:
355 return _pannable->describe_parameter (p);
360 Panner1in2out::value_as_string (boost::shared_ptr<AutomationControl> ac) const
362 /* DO NOT USE LocaleGuard HERE */
363 double val = ac->get_value();
365 switch (ac->parameter().type()) {
366 case PanAzimuthAutomation:
367 /* We show the position of the center of the image relative to the left & right.
368 This is expressed as a pair of percentage values that ranges from (100,0)
369 (hard left) through (50,50) (hard center) to (0,100) (hard right).
371 This is pretty wierd, but its the way audio engineers expect it. Just remember that
372 the center of the USA isn't Kansas, its (50LA, 50NY) and it will all make sense.
374 This is designed to be as narrow as possible. Dedicated
375 panner GUIs can do their own version of this if they need
376 something less compact.
379 return string_compose (_("L%1R%2"), (int) rint (100.0 * (1.0 - val)),
380 (int) rint (100.0 * val));
383 return _pannable->value_as_string (ac);
388 Panner1in2out::reset ()