Move panner bypass state up to the PannerShell so that it is preserved even when...
[ardour2.git] / libs / panners / 1in2out / panner_1in2out.cc
blob19b8dadd6de75ecfd264dbf1c22ae75c0211e4d5
1 /*
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.
20 #include <inttypes.h>
22 #include <cmath>
23 #include <cerrno>
24 #include <fstream>
25 #include <cstdlib>
26 #include <string>
27 #include <cstdio>
28 #include <locale.h>
29 #include <unistd.h>
30 #include <float.h>
31 #include <iomanip>
33 #include <glibmm.h>
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"
55 #include "i18n.h"
56 #include "panner_1in2out.h"
58 #include "pbd/mathfix.h"
60 using namespace std;
61 using namespace ARDOUR;
62 using namespace PBD;
64 static PanPluginDescriptor _descriptor = {
65 "Mono to Stereo Panner",
66 1, 2,
67 Panner1in2out::factory
70 extern "C" { PanPluginDescriptor* panner_descriptor () { return &_descriptor; } }
72 Panner1in2out::Panner1in2out (boost::shared_ptr<Pannable> p)
73 : Panner (p)
75 if (!_pannable->has_state()) {
76 _pannable->pan_azimuth_control->set_value (0.5);
79 update ();
81 left = desired_left;
82 right = desired_right;
83 left_interp = left;
84 right_interp = right;
86 _pannable->pan_azimuth_control->Changed.connect_same_thread (*this, boost::bind (&Panner1in2out::update, this));
89 Panner1in2out::~Panner1in2out ()
93 void
94 Panner1in2out::update ()
96 float panR, panL;
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();
101 panL = 1 - panR;
103 desired_left = panL * (scale * panL + 1.0f - scale);
104 desired_right = panR * (scale * panR + 1.0f - scale);
107 void
108 Panner1in2out::set_position (double p)
110 if (clamp_position (p)) {
111 _pannable->pan_azimuth_control->set_value (p);
115 bool
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);
121 return true;
124 double
125 Panner1in2out::position () const
127 return _pannable->pan_azimuth_control->get_value ();
130 void
131 Panner1in2out::distribute_one (AudioBuffer& srcbuf, BufferSet& obufs, gain_t gain_coeff, pframes_t nframes, uint32_t /* not used */)
133 assert (obufs.count().n_audio() == 2);
135 pan_t delta;
136 Sample* dst;
137 pan_t pan;
139 Sample* const src = srcbuf.data();
141 /* LEFT OUTPUT */
143 dst = obufs.get_audio(0).data();
145 if (fabsf ((delta = (left - desired_left))) > 0.002) { // about 1 degree of arc
147 /* we've moving the pan by an appreciable amount, so we must
148 interpolate over 64 frames or nframes, whichever is smaller */
150 pframes_t const limit = min ((pframes_t) 64, nframes);
151 pframes_t n;
153 delta = -(delta / (float) (limit));
155 for (n = 0; n < limit; n++) {
156 left_interp = left_interp + delta;
157 left = left_interp + 0.9 * (left - left_interp);
158 dst[n] += src[n] * left * gain_coeff;
161 /* then pan the rest of the buffer; no need for interpolation for this bit */
163 pan = left * gain_coeff;
165 mix_buffers_with_gain (dst+n,src+n,nframes-n,pan);
167 } else {
169 left = desired_left;
170 left_interp = left;
172 if ((pan = (left * gain_coeff)) != 1.0f) {
174 if (pan != 0.0f) {
176 /* pan is 1 but also not 0, so we must do it "properly" */
178 mix_buffers_with_gain(dst,src,nframes,pan);
180 /* mark that we wrote into the buffer */
182 // obufs[0] = 0;
186 } else {
188 /* pan is 1 so we can just copy the input samples straight in */
190 mix_buffers_no_gain(dst,src,nframes);
192 /* XXX it would be nice to mark that we wrote into the buffer */
196 /* RIGHT OUTPUT */
198 dst = obufs.get_audio(1).data();
200 if (fabsf ((delta = (right - desired_right))) > 0.002) { // about 1 degree of arc
202 /* we're moving the pan by an appreciable amount, so we must
203 interpolate over 64 frames or nframes, whichever is smaller */
205 pframes_t const limit = min ((pframes_t) 64, nframes);
206 pframes_t n;
208 delta = -(delta / (float) (limit));
210 for (n = 0; n < limit; n++) {
211 right_interp = right_interp + delta;
212 right = right_interp + 0.9 * (right - right_interp);
213 dst[n] += src[n] * right * gain_coeff;
216 /* then pan the rest of the buffer, no need for interpolation for this bit */
218 pan = right * gain_coeff;
220 mix_buffers_with_gain(dst+n,src+n,nframes-n,pan);
222 /* XXX it would be nice to mark the buffer as written to */
224 } else {
226 right = desired_right;
227 right_interp = right;
229 if ((pan = (right * gain_coeff)) != 1.0f) {
231 if (pan != 0.0f) {
233 /* pan is not 1 but also not 0, so we must do it "properly" */
235 mix_buffers_with_gain(dst,src,nframes,pan);
237 /* XXX it would be nice to mark the buffer as written to */
240 } else {
242 /* pan is 1 so we can just copy the input samples straight in */
244 mix_buffers_no_gain(dst,src,nframes);
246 /* XXX it would be nice to mark the buffer as written to */
252 void
253 Panner1in2out::distribute_one_automated (AudioBuffer& srcbuf, BufferSet& obufs,
254 framepos_t start, framepos_t end, pframes_t nframes,
255 pan_t** buffers, uint32_t which)
257 assert (obufs.count().n_audio() == 2);
259 Sample* dst;
260 pan_t* pbuf;
261 Sample* const src = srcbuf.data();
262 pan_t* const position = buffers[0];
264 /* fetch positional data */
266 if (!_pannable->pan_azimuth_control->list()->curve().rt_safe_get_vector (start, end, position, nframes)) {
267 /* fallback */
268 distribute_one (srcbuf, obufs, 1.0, nframes, which);
269 return;
272 /* apply pan law to convert positional data into pan coefficients for
273 each buffer (output)
276 const float pan_law_attenuation = -3.0f;
277 const float scale = 2.0f - 4.0f * powf (10.0f,pan_law_attenuation/20.0f);
279 for (pframes_t n = 0; n < nframes; ++n) {
281 float panR = position[n];
282 const float panL = 1 - panR;
284 /* note that are overwriting buffers, but its OK
285 because we're finished with their old contents
286 (position automation data) and are
287 replacing it with panning/gain coefficients
288 that we need to actually process the data.
291 buffers[0][n] = panL * (scale * panL + 1.0f - scale);
292 buffers[1][n] = panR * (scale * panR + 1.0f - scale);
295 /* LEFT OUTPUT */
297 dst = obufs.get_audio(0).data();
298 pbuf = buffers[0];
300 for (pframes_t n = 0; n < nframes; ++n) {
301 dst[n] += src[n] * pbuf[n];
304 /* XXX it would be nice to mark the buffer as written to */
306 /* RIGHT OUTPUT */
308 dst = obufs.get_audio(1).data();
309 pbuf = buffers[1];
311 for (pframes_t n = 0; n < nframes; ++n) {
312 dst[n] += src[n] * pbuf[n];
315 /* XXX it would be nice to mark the buffer as written to */
319 Panner*
320 Panner1in2out::factory (boost::shared_ptr<Pannable> p, boost::shared_ptr<Speakers> /* ignored */)
322 return new Panner1in2out (p);
325 XMLNode&
326 Panner1in2out::get_state ()
328 XMLNode& root (Panner::get_state ());
329 root.add_property (X_("type"), _descriptor.name);
330 return root;
334 std::set<Evoral::Parameter>
335 Panner1in2out::what_can_be_automated() const
337 set<Evoral::Parameter> s;
338 s.insert (Evoral::Parameter (PanAzimuthAutomation));
339 return s;
342 string
343 Panner1in2out::describe_parameter (Evoral::Parameter p)
345 switch (p.type()) {
346 case PanAzimuthAutomation:
347 return _("L/R");
348 default:
349 return _pannable->describe_parameter (p);
353 string
354 Panner1in2out::value_as_string (boost::shared_ptr<AutomationControl> ac) const
356 /* DO NOT USE LocaleGuard HERE */
357 double val = ac->get_value();
359 switch (ac->parameter().type()) {
360 case PanAzimuthAutomation:
361 /* We show the position of the center of the image relative to the left & right.
362 This is expressed as a pair of percentage values that ranges from (100,0)
363 (hard left) through (50,50) (hard center) to (0,100) (hard right).
365 This is pretty wierd, but its the way audio engineers expect it. Just remember that
366 the center of the USA isn't Kansas, its (50LA, 50NY) and it will all make sense.
369 return string_compose (_("L:%1 R:%2"), (int) rint (100.0 * (1.0 - val)),
370 (int) rint (100.0 * val));
372 default:
373 return _pannable->value_as_string (ac);