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/profile.h"
57 #include "panner_1in2out.h"
59 #include "pbd/mathfix.h"
62 using namespace ARDOUR;
65 static PanPluginDescriptor _descriptor = {
66 "Mono to Stereo Panner",
67 "http://ardour.org/plugin/panner_1in2out",
68 "http://ardour.org/plugin/panner_1in2out#ui",
71 Panner1in2out::factory
74 extern "C" ARDOURPANNER_API PanPluginDescriptor* panner_descriptor () { return &_descriptor; }
76 Panner1in2out::Panner1in2out (boost::shared_ptr<Pannable> p)
79 if (!Profile->get_trx () ) {
80 if (!_pannable->has_state ()) {
81 _pannable->pan_azimuth_control->set_value (0.5);
88 right = desired_right;
92 _pannable->pan_azimuth_control->Changed.connect_same_thread (*this, boost::bind (&Panner1in2out::update, this));
95 Panner1in2out::~Panner1in2out ()
100 Panner1in2out::update ()
103 float const pan_law_attenuation = -3.0f;
104 float const scale = 2.0f - 4.0f * powf (10.0f,pan_law_attenuation/20.0f);
106 panR = _pannable->pan_azimuth_control->get_value();
109 desired_left = panL * (scale * panL + 1.0f - scale);
110 desired_right = panR * (scale * panR + 1.0f - scale);
114 Panner1in2out::set_position (double p)
116 if (clamp_position (p)) {
117 _pannable->pan_azimuth_control->set_value (p);
122 Panner1in2out::clamp_position (double& p)
124 /* any position between 0.0 and 1.0 is legal */
125 DEBUG_TRACE (DEBUG::Panning, string_compose ("want to move panner to %1 - always allowed in 0.0-1.0 range\n", p));
126 p = max (min (p, 1.0), 0.0);
131 Panner1in2out::position_range () const
133 return make_pair (0, 1);
137 Panner1in2out::position () const
139 return _pannable->pan_azimuth_control->get_value ();
143 Panner1in2out::distribute_one (AudioBuffer& srcbuf, BufferSet& obufs, gain_t gain_coeff, pframes_t nframes, uint32_t /* not used */)
145 assert (obufs.count().n_audio() == 2);
151 Sample* const src = srcbuf.data();
155 dst = obufs.get_audio(0).data();
157 if (fabsf ((delta = (left - desired_left))) > 0.002) { // about 1 degree of arc
159 /* we've moving the pan by an appreciable amount, so we must
160 interpolate over 64 frames or nframes, whichever is smaller */
162 pframes_t const limit = min ((pframes_t) 64, nframes);
165 delta = -(delta / (float) (limit));
167 for (n = 0; n < limit; n++) {
168 left_interp = left_interp + delta;
169 left = left_interp + 0.9 * (left - left_interp);
170 dst[n] += src[n] * left * gain_coeff;
173 /* then pan the rest of the buffer; no need for interpolation for this bit */
175 pan = left * gain_coeff;
177 mix_buffers_with_gain (dst+n,src+n,nframes-n,pan);
184 if ((pan = (left * gain_coeff)) != 1.0f) {
188 /* pan is 1 but also not 0, so we must do it "properly" */
190 mix_buffers_with_gain(dst,src,nframes,pan);
192 /* mark that we wrote into the buffer */
200 /* pan is 1 so we can just copy the input samples straight in */
202 mix_buffers_no_gain(dst,src,nframes);
204 /* XXX it would be nice to mark that we wrote into the buffer */
210 dst = obufs.get_audio(1).data();
212 if (fabsf ((delta = (right - desired_right))) > 0.002) { // about 1 degree of arc
214 /* we're moving the pan by an appreciable amount, so we must
215 interpolate over 64 frames or nframes, whichever is smaller */
217 pframes_t const limit = min ((pframes_t) 64, nframes);
220 delta = -(delta / (float) (limit));
222 for (n = 0; n < limit; n++) {
223 right_interp = right_interp + delta;
224 right = right_interp + 0.9 * (right - right_interp);
225 dst[n] += src[n] * right * gain_coeff;
228 /* then pan the rest of the buffer, no need for interpolation for this bit */
230 pan = right * gain_coeff;
232 mix_buffers_with_gain(dst+n,src+n,nframes-n,pan);
234 /* XXX it would be nice to mark the buffer as written to */
238 right = desired_right;
239 right_interp = right;
241 if ((pan = (right * gain_coeff)) != 1.0f) {
245 /* pan is not 1 but also not 0, so we must do it "properly" */
247 mix_buffers_with_gain(dst,src,nframes,pan);
249 /* XXX it would be nice to mark the buffer as written to */
254 /* pan is 1 so we can just copy the input samples straight in */
256 mix_buffers_no_gain(dst,src,nframes);
258 /* XXX it would be nice to mark the buffer as written to */
265 Panner1in2out::distribute_one_automated (AudioBuffer& srcbuf, BufferSet& obufs,
266 framepos_t start, framepos_t end, pframes_t nframes,
267 pan_t** buffers, uint32_t which)
269 assert (obufs.count().n_audio() == 2);
273 Sample* const src = srcbuf.data();
274 pan_t* const position = buffers[0];
276 /* fetch positional data */
278 if (!_pannable->pan_azimuth_control->list()->curve().rt_safe_get_vector (start, end, position, nframes)) {
280 distribute_one (srcbuf, obufs, 1.0, nframes, which);
284 /* apply pan law to convert positional data into pan coefficients for
288 const float pan_law_attenuation = -3.0f;
289 const float scale = 2.0f - 4.0f * powf (10.0f,pan_law_attenuation/20.0f);
291 for (pframes_t n = 0; n < nframes; ++n) {
293 float panR = position[n];
294 const float panL = 1 - panR;
296 /* note that are overwriting buffers, but its OK
297 because we're finished with their old contents
298 (position automation data) and are
299 replacing it with panning/gain coefficients
300 that we need to actually process the data.
303 buffers[0][n] = panL * (scale * panL + 1.0f - scale);
304 buffers[1][n] = panR * (scale * panR + 1.0f - scale);
309 dst = obufs.get_audio(0).data();
312 for (pframes_t n = 0; n < nframes; ++n) {
313 dst[n] += src[n] * pbuf[n];
316 /* XXX it would be nice to mark the buffer as written to */
320 dst = obufs.get_audio(1).data();
323 for (pframes_t n = 0; n < nframes; ++n) {
324 dst[n] += src[n] * pbuf[n];
327 /* XXX it would be nice to mark the buffer as written to */
332 Panner1in2out::factory (boost::shared_ptr<Pannable> p, boost::shared_ptr<Speakers> /* ignored */)
334 return new Panner1in2out (p);
338 Panner1in2out::get_state ()
340 XMLNode& root (Panner::get_state ());
341 root.add_property (X_("uri"), _descriptor.panner_uri);
342 /* this is needed to allow new sessions to load with old Ardour: */
343 root.add_property (X_("type"), _descriptor.name);
348 std::set<Evoral::Parameter>
349 Panner1in2out::what_can_be_automated() const
351 set<Evoral::Parameter> s;
352 s.insert (Evoral::Parameter (PanAzimuthAutomation));
357 Panner1in2out::describe_parameter (Evoral::Parameter p)
360 case PanAzimuthAutomation:
363 return _pannable->describe_parameter (p);
368 Panner1in2out::value_as_string (boost::shared_ptr<AutomationControl> ac) const
370 /* DO NOT USE LocaleGuard HERE */
371 double val = ac->get_value();
373 switch (ac->parameter().type()) {
374 case PanAzimuthAutomation:
375 /* We show the position of the center of the image relative to the left & right.
376 This is expressed as a pair of percentage values that ranges from (100,0)
377 (hard left) through (50,50) (hard center) to (0,100) (hard right).
379 This is pretty wierd, but its the way audio engineers expect it. Just remember that
380 the center of the USA isn't Kansas, its (50LA, 50NY) and it will all make sense.
382 This is designed to be as narrow as possible. Dedicated
383 panner GUIs can do their own version of this if they need
384 something less compact.
387 return string_compose (_("L%1R%2"), (int) rint (100.0 * (1.0 - val)),
388 (int) rint (100.0 * val));
396 Panner1in2out::reset ()