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
, boost::shared_ptr
<Speakers
> /* ignored */)
322 return new Panner1in2out (p
);
326 Panner1in2out::get_state ()
328 XMLNode
& root (Panner::get_state ());
329 root
.add_property (X_("type"), _descriptor
.name
);
334 std::set
<Evoral::Parameter
>
335 Panner1in2out::what_can_be_automated() const
337 set
<Evoral::Parameter
> s
;
338 s
.insert (Evoral::Parameter (PanAzimuthAutomation
));
343 Panner1in2out::describe_parameter (Evoral::Parameter p
)
346 case PanAzimuthAutomation
:
349 return _pannable
->describe_parameter (p
);
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
));
373 return _pannable
->value_as_string (ac
);