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