Merge branch 'master' into cairocanvas
[ardour.git] / libs / panners / stereobalance / panner_balance.cc
1 /*
2     Copyright (C) 2004-2011 Paul Davis
3     adopted from 2in2out panner by Robin Gareus <robin@gareus.org>
4
5     This program is free software; you can redistribute it and/or modify
6     it under the terms of the GNU General Public License as published by
7     the Free Software Foundation; either version 2 of the License, or
8     (at your option) any later version.
9
10     This program is distributed in the hope that it will be useful,
11     but WITHOUT ANY WARRANTY; without even the implied warranty of
12     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13     GNU General Public License for more details.
14
15     You should have received a copy of the GNU General Public License
16     along with this program; if not, write to the Free Software
17     Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
18
19 */
20
21 #include <inttypes.h>
22
23 #include <cmath>
24 #include <cerrno>
25 #include <fstream>
26 #include <cstdlib>
27 #include <string>
28 #include <cstdio>
29 #include <locale.h>
30 #include <unistd.h>
31 #include <float.h>
32 #include <iomanip>
33
34 #include <glibmm.h>
35
36 #include "pbd/cartesian.h"
37 #include "pbd/convert.h"
38 #include "pbd/error.h"
39 #include "pbd/failed_constructor.h"
40 #include "pbd/xml++.h"
41 #include "pbd/enumwriter.h"
42
43 #include "evoral/Curve.hpp"
44
45 #include "ardour/audio_buffer.h"
46 #include "ardour/audio_buffer.h"
47 #include "ardour/buffer_set.h"
48 #include "ardour/pan_controllable.h"
49 #include "ardour/pannable.h"
50 #include "ardour/runtime_functions.h"
51 #include "ardour/session.h"
52 #include "ardour/utils.h"
53 #include "ardour/mix.h"
54
55 #include "panner_balance.h"
56
57 #include "i18n.h"
58
59 #include "pbd/mathfix.h"
60
61 using namespace std;
62 using namespace ARDOUR;
63 using namespace PBD;
64
65 static PanPluginDescriptor _descriptor = {
66         "Stereo Balance",
67         "http://ardour.org/plugin/panner_balance",
68         "http://ardour.org/plugin/panner_balance#ui",
69         2, 2,
70         2000,
71         Pannerbalance::factory
72 };
73
74 extern "C" ARDOURPANNER_API PanPluginDescriptor* panner_descriptor () { return &_descriptor; } 
75
76 Pannerbalance::Pannerbalance (boost::shared_ptr<Pannable> p)
77         : Panner (p)
78 {
79         if (!_pannable->has_state()) {
80                 _pannable->pan_azimuth_control->set_value (0.5);
81         }
82
83         update ();
84
85         /* LEFT SIGNAL */
86         pos_interp[0] = pos[0] = desired_pos[0];
87         /* RIGHT SIGNAL */
88         pos_interp[1] = pos[1] = desired_pos[1];
89
90         _pannable->pan_azimuth_control->Changed.connect_same_thread (*this, boost::bind (&Pannerbalance::update, this));
91 }
92
93 Pannerbalance::~Pannerbalance ()
94 {
95 }
96
97 double
98 Pannerbalance::position () const
99 {
100         return _pannable->pan_azimuth_control->get_value();
101 }
102
103         void
104 Pannerbalance::set_position (double p)
105 {
106         if (clamp_position (p)) {
107                 _pannable->pan_azimuth_control->set_value (p);
108         }
109 }
110
111         void
112 Pannerbalance::thaw ()
113 {
114         Panner::thaw ();
115         if (_frozen == 0) {
116                 update ();
117         }
118 }
119
120 void
121 Pannerbalance::update ()
122 {
123         if (_frozen) {
124                 return;
125         }
126
127         float const pos = _pannable->pan_azimuth_control->get_value();
128
129         if (pos == .5) {
130                 desired_pos[0] = 1.0;
131                 desired_pos[1] = 1.0;
132         } else if (pos > .5) {
133                 desired_pos[0] = 2 - 2. * pos;
134                 desired_pos[1] = 1.0;
135         } else {
136                 desired_pos[0] = 1.0;
137                 desired_pos[1] = 2. * pos;
138         }
139 }
140
141 bool
142 Pannerbalance::clamp_position (double& p)
143 {
144         p = max (min (p, 1.0), 0.0);
145         return true;
146 }
147
148 pair<double, double>
149 Pannerbalance::position_range () const
150 {
151         return make_pair (0, 1);
152 }
153
154 void
155 Pannerbalance::distribute_one (AudioBuffer& srcbuf, BufferSet& obufs, gain_t gain_coeff, pframes_t nframes, uint32_t which)
156 {
157         assert (obufs.count().n_audio() == 2);
158
159         pan_t delta;
160         Sample* dst;
161         pan_t pan;
162
163         Sample* const src = srcbuf.data();
164
165         dst = obufs.get_audio(which).data();
166
167         if (fabsf ((delta = (pos[which] - desired_pos[which]))) > 0.002) { // about 1 degree of arc
168
169                 /* we've moving the pan by an appreciable amount, so we must
170                          interpolate over 64 frames or nframes, whichever is smaller */
171
172                 pframes_t const limit = min ((pframes_t) 64, nframes);
173                 pframes_t n;
174
175                 delta = -(delta / (float) (limit));
176
177                 for (n = 0; n < limit; n++) {
178                         pos_interp[which] = pos_interp[which] + delta;
179                         pos[which] = pos_interp[which] + 0.9 * (pos[which] - pos_interp[which]);
180                         dst[n] += src[n] * pos[which] * gain_coeff;
181                 }
182
183                 /* then pan the rest of the buffer; no need for interpolation for this bit */
184
185                 pan = pos[which] * gain_coeff;
186
187                 mix_buffers_with_gain (dst+n,src+n,nframes-n,pan);
188
189         } else {
190
191                 pos[which] = desired_pos[which];
192                 pos_interp[which] = pos[which];
193
194                 if ((pan = (pos[which] * gain_coeff)) != 1.0f) {
195
196                         if (pan != 0.0f) {
197
198                                 /* pan is 1 but also not 0, so we must do it "properly" */
199
200                                 //obufs.get_audio(1).read_from (srcbuf, nframes);
201                                 mix_buffers_with_gain(dst,src,nframes,pan);
202
203                                 /* mark that we wrote into the buffer */
204
205                                 // obufs[0] = 0;
206
207                         }
208
209                 } else {
210                         /* pan is 1 so we can just copy the input samples straight in */
211                         mix_buffers_no_gain(dst,src,nframes);
212                 }
213         }
214 }
215
216 void
217 Pannerbalance::distribute_one_automated (AudioBuffer& srcbuf, BufferSet& obufs,
218                                          framepos_t start, framepos_t end, pframes_t nframes,
219                                          pan_t** buffers, uint32_t which)
220 {
221         assert (obufs.count().n_audio() == 2);
222
223         Sample* dst;
224         pan_t* pbuf;
225         Sample* const src = srcbuf.data();
226         pan_t* const position = buffers[0];
227
228         /* fetch positional data */
229
230         if (!_pannable->pan_azimuth_control->list()->curve().rt_safe_get_vector (start, end, position, nframes)) {
231                 /* fallback */
232                 distribute_one (srcbuf, obufs, 1.0, nframes, which);
233                 return;
234         }
235
236         for (pframes_t n = 0; n < nframes; ++n) {
237
238                 float const pos = position[n];
239
240                 if (which == 0) { // Left
241                         if (pos > .5) {
242                                 buffers[which][n] = 2 - 2. * pos;
243                         } else {
244                                 buffers[which][n] = 1.0;
245                         }
246                 } else { // Right
247                         if (pos < .5) {
248                                 buffers[which][n] = 2. * pos;
249                         } else {
250                                 buffers[which][n] = 1.0;
251                         }
252                 }
253         }
254
255         dst = obufs.get_audio(which).data();
256         pbuf = buffers[which];
257
258         for (pframes_t n = 0; n < nframes; ++n) {
259                 dst[n] += src[n] * pbuf[n];
260         }
261
262         /* XXX it would be nice to mark the buffer as written to */
263 }
264
265 Panner*
266 Pannerbalance::factory (boost::shared_ptr<Pannable> p, boost::shared_ptr<Speakers> /* ignored */)
267 {
268         return new Pannerbalance (p);
269 }
270
271         XMLNode&
272 Pannerbalance::get_state ()
273 {
274         XMLNode& root (Panner::get_state ());
275         root.add_property (X_("uri"), _descriptor.panner_uri);
276         /* this is needed to allow new sessions to load with old Ardour: */
277         root.add_property (X_("type"), _descriptor.name);
278         return root;
279 }
280
281 std::set<Evoral::Parameter>
282 Pannerbalance::what_can_be_automated() const
283 {
284         set<Evoral::Parameter> s;
285         s.insert (Evoral::Parameter (PanAzimuthAutomation));
286         return s;
287 }
288
289 string
290 Pannerbalance::describe_parameter (Evoral::Parameter p)
291 {
292         switch (p.type()) {
293                 case PanAzimuthAutomation:
294                         return _("L/R");
295                 default:
296                         return _pannable->describe_parameter (p);
297         }
298 }
299
300 string
301 Pannerbalance::value_as_string (boost::shared_ptr<AutomationControl> ac) const
302 {
303         /* DO NOT USE LocaleGuard HERE */
304         double val = ac->get_value();
305
306         switch (ac->parameter().type()) {
307                 case PanAzimuthAutomation:
308                         /* We show the position of the center of the image relative to the left & right.
309                                  This is expressed as a pair of percentage values that ranges from (100,0)
310                                  (hard left) through (50,50) (hard center) to (0,100) (hard right).
311
312                                  This is pretty wierd, but its the way audio engineers expect it. Just remember that
313                                  the center of the USA isn't Kansas, its (50LA, 50NY) and it will all make sense.
314
315                                  This is designed to be as narrow as possible. Dedicated
316                                  panner GUIs can do their own version of this if they need
317                                  something less compact.
318                                  */
319
320                         return string_compose (_("L%1R%2"), (int) rint (100.0 * (1.0 - val)),
321                                         (int) rint (100.0 * val));
322
323                 default:
324                         return _("unused");
325         }
326 }
327
328 void
329 Pannerbalance::reset ()
330 {
331         set_position (0.5);
332         update ();
333 }