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 () const
127 return _pannable->pan_azimuth_control->get_value ();
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);
139 Sample* const src = srcbuf.data();
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);
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);
172 if ((pan = (left * gain_coeff)) != 1.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 */
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 */
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);
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 */
226 right = desired_right;
227 right_interp = right;
229 if ((pan = (right * gain_coeff)) != 1.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 */
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 */
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);
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)) {
268 distribute_one (srcbuf, obufs, 1.0, nframes, which);
272 /* apply pan law to convert positional data into pan coefficients for
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);
297 dst = obufs.get_audio(0).data();
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 */
308 dst = obufs.get_audio(1).data();
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 */
320 Panner1in2out::factory (boost::shared_ptr<Pannable> p, Speakers& /* ignored */)
322 return new Panner1in2out (p);
326 Panner1in2out::get_state (void)
332 Panner1in2out::state (bool /*full_state*/)
334 XMLNode& root (Panner::get_state ());
335 root.add_property (X_("type"), _descriptor.name);
340 Panner1in2out::set_state (const XMLNode& node, int version)
342 LocaleGuard lg (X_("POSIX"));
343 Panner::set_state (node, version);
347 std::set<Evoral::Parameter>
348 Panner1in2out::what_can_be_automated() const
350 set<Evoral::Parameter> s;
351 s.insert (Evoral::Parameter (PanAzimuthAutomation));
356 Panner1in2out::describe_parameter (Evoral::Parameter p)
359 case PanAzimuthAutomation:
362 return _pannable->describe_parameter (p);