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",
66 "http://ardour.org/plugin/panner_1in2out",
67 "http://ardour.org/plugin/panner_1in2out#ui",
70 Panner1in2out::factory
73 extern "C" ARDOURPANNER_API PanPluginDescriptor* panner_descriptor () { return &_descriptor; }
75 Panner1in2out::Panner1in2out (boost::shared_ptr<Pannable> p)
78 if (!_pannable->has_state()) {
79 _pannable->pan_azimuth_control->set_value (0.5);
85 right = desired_right;
89 _pannable->pan_azimuth_control->Changed.connect_same_thread (*this, boost::bind (&Panner1in2out::update, this));
92 Panner1in2out::~Panner1in2out ()
97 Panner1in2out::update ()
100 float const pan_law_attenuation = -3.0f;
101 float const scale = 2.0f - 4.0f * powf (10.0f,pan_law_attenuation/20.0f);
103 panR = _pannable->pan_azimuth_control->get_value();
106 desired_left = panL * (scale * panL + 1.0f - scale);
107 desired_right = panR * (scale * panR + 1.0f - scale);
111 Panner1in2out::set_position (double p)
113 if (clamp_position (p)) {
114 _pannable->pan_azimuth_control->set_value (p);
119 Panner1in2out::clamp_position (double& p)
121 /* any position between 0.0 and 1.0 is legal */
122 DEBUG_TRACE (DEBUG::Panning, string_compose ("want to move panner to %1 - always allowed in 0.0-1.0 range\n", p));
123 p = max (min (p, 1.0), 0.0);
128 Panner1in2out::position_range () const
130 return make_pair (0, 1);
134 Panner1in2out::position () const
136 return _pannable->pan_azimuth_control->get_value ();
140 Panner1in2out::distribute_one (AudioBuffer& srcbuf, BufferSet& obufs, gain_t gain_coeff, pframes_t nframes, uint32_t /* not used */)
142 assert (obufs.count().n_audio() == 2);
148 Sample* const src = srcbuf.data();
152 dst = obufs.get_audio(0).data();
154 if (fabsf ((delta = (left - desired_left))) > 0.002) { // about 1 degree of arc
156 /* we've moving the pan by an appreciable amount, so we must
157 interpolate over 64 frames or nframes, whichever is smaller */
159 pframes_t const limit = min ((pframes_t) 64, nframes);
162 delta = -(delta / (float) (limit));
164 for (n = 0; n < limit; n++) {
165 left_interp = left_interp + delta;
166 left = left_interp + 0.9 * (left - left_interp);
167 dst[n] += src[n] * left * gain_coeff;
170 /* then pan the rest of the buffer; no need for interpolation for this bit */
172 pan = left * gain_coeff;
174 mix_buffers_with_gain (dst+n,src+n,nframes-n,pan);
181 if ((pan = (left * gain_coeff)) != 1.0f) {
185 /* pan is 1 but also not 0, so we must do it "properly" */
187 mix_buffers_with_gain(dst,src,nframes,pan);
189 /* mark that we wrote into the buffer */
197 /* pan is 1 so we can just copy the input samples straight in */
199 mix_buffers_no_gain(dst,src,nframes);
201 /* XXX it would be nice to mark that we wrote into the buffer */
207 dst = obufs.get_audio(1).data();
209 if (fabsf ((delta = (right - desired_right))) > 0.002) { // about 1 degree of arc
211 /* we're moving the pan by an appreciable amount, so we must
212 interpolate over 64 frames or nframes, whichever is smaller */
214 pframes_t const limit = min ((pframes_t) 64, nframes);
217 delta = -(delta / (float) (limit));
219 for (n = 0; n < limit; n++) {
220 right_interp = right_interp + delta;
221 right = right_interp + 0.9 * (right - right_interp);
222 dst[n] += src[n] * right * gain_coeff;
225 /* then pan the rest of the buffer, no need for interpolation for this bit */
227 pan = right * gain_coeff;
229 mix_buffers_with_gain(dst+n,src+n,nframes-n,pan);
231 /* XXX it would be nice to mark the buffer as written to */
235 right = desired_right;
236 right_interp = right;
238 if ((pan = (right * gain_coeff)) != 1.0f) {
242 /* pan is not 1 but also not 0, so we must do it "properly" */
244 mix_buffers_with_gain(dst,src,nframes,pan);
246 /* XXX it would be nice to mark the buffer as written to */
251 /* pan is 1 so we can just copy the input samples straight in */
253 mix_buffers_no_gain(dst,src,nframes);
255 /* XXX it would be nice to mark the buffer as written to */
262 Panner1in2out::distribute_one_automated (AudioBuffer& srcbuf, BufferSet& obufs,
263 framepos_t start, framepos_t end, pframes_t nframes,
264 pan_t** buffers, uint32_t which)
266 assert (obufs.count().n_audio() == 2);
270 Sample* const src = srcbuf.data();
271 pan_t* const position = buffers[0];
273 /* fetch positional data */
275 if (!_pannable->pan_azimuth_control->list()->curve().rt_safe_get_vector (start, end, position, nframes)) {
277 distribute_one (srcbuf, obufs, 1.0, nframes, which);
281 /* apply pan law to convert positional data into pan coefficients for
285 const float pan_law_attenuation = -3.0f;
286 const float scale = 2.0f - 4.0f * powf (10.0f,pan_law_attenuation/20.0f);
288 for (pframes_t n = 0; n < nframes; ++n) {
290 float panR = position[n];
291 const float panL = 1 - panR;
293 /* note that are overwriting buffers, but its OK
294 because we're finished with their old contents
295 (position automation data) and are
296 replacing it with panning/gain coefficients
297 that we need to actually process the data.
300 buffers[0][n] = panL * (scale * panL + 1.0f - scale);
301 buffers[1][n] = panR * (scale * panR + 1.0f - scale);
306 dst = obufs.get_audio(0).data();
309 for (pframes_t n = 0; n < nframes; ++n) {
310 dst[n] += src[n] * pbuf[n];
313 /* XXX it would be nice to mark the buffer as written to */
317 dst = obufs.get_audio(1).data();
320 for (pframes_t n = 0; n < nframes; ++n) {
321 dst[n] += src[n] * pbuf[n];
324 /* XXX it would be nice to mark the buffer as written to */
329 Panner1in2out::factory (boost::shared_ptr<Pannable> p, boost::shared_ptr<Speakers> /* ignored */)
331 return new Panner1in2out (p);
335 Panner1in2out::get_state ()
337 XMLNode& root (Panner::get_state ());
338 root.add_property (X_("uri"), _descriptor.panner_uri);
339 /* this is needed to allow new sessions to load with old Ardour: */
340 root.add_property (X_("type"), _descriptor.name);
345 std::set<Evoral::Parameter>
346 Panner1in2out::what_can_be_automated() const
348 set<Evoral::Parameter> s;
349 s.insert (Evoral::Parameter (PanAzimuthAutomation));
354 Panner1in2out::describe_parameter (Evoral::Parameter p)
357 case PanAzimuthAutomation:
360 return _pannable->describe_parameter (p);
365 Panner1in2out::value_as_string (boost::shared_ptr<AutomationControl> ac) const
367 /* DO NOT USE LocaleGuard HERE */
368 double val = ac->get_value();
370 switch (ac->parameter().type()) {
371 case PanAzimuthAutomation:
372 /* We show the position of the center of the image relative to the left & right.
373 This is expressed as a pair of percentage values that ranges from (100,0)
374 (hard left) through (50,50) (hard center) to (0,100) (hard right).
376 This is pretty wierd, but its the way audio engineers expect it. Just remember that
377 the center of the USA isn't Kansas, its (50LA, 50NY) and it will all make sense.
379 This is designed to be as narrow as possible. Dedicated
380 panner GUIs can do their own version of this if they need
381 something less compact.
384 return string_compose (_("L%1R%2"), (int) rint (100.0 * (1.0 - val)),
385 (int) rint (100.0 * val));
388 return _pannable->value_as_string (ac);
393 Panner1in2out::reset ()