enough with umpteen "i18n.h" files. Consolidate on pbd/i18n.h
[ardour.git] / libs / panners / 1in2out / panner_1in2out.cc
1 /*
2     Copyright (C) 2004-2011 Paul Davis
3
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.
8
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.
13
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.
17
18 */
19
20 #include <inttypes.h>
21
22 #include <cmath>
23 #include <cerrno>
24 #include <cstdlib>
25 #include <string>
26 #include <cstdio>
27 #include <locale.h>
28 #include <unistd.h>
29 #include <float.h>
30
31 #include <glibmm.h>
32
33 #include "pbd/cartesian.h"
34 #include "pbd/convert.h"
35 #include "pbd/error.h"
36 #include "pbd/failed_constructor.h"
37 #include "pbd/xml++.h"
38 #include "pbd/enumwriter.h"
39
40 #include "evoral/Curve.hpp"
41
42 #include "ardour/session.h"
43 #include "ardour/panner.h"
44 #include "ardour/utils.h"
45 #include "ardour/audio_buffer.h"
46
47 #include "ardour/debug.h"
48 #include "ardour/runtime_functions.h"
49 #include "ardour/buffer_set.h"
50 #include "ardour/audio_buffer.h"
51 #include "ardour/pannable.h"
52 #include "ardour/profile.h"
53
54 #include "pbd/i18n.h"
55 #include "panner_1in2out.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         "Mono to Stereo Panner",
65         "http://ardour.org/plugin/panner_1in2out",
66         "http://ardour.org/plugin/panner_1in2out#ui",
67         1, 2,
68         10000,
69         Panner1in2out::factory
70 };
71
72 extern "C" ARDOURPANNER_API PanPluginDescriptor*  panner_descriptor () { return &_descriptor; }
73
74 Panner1in2out::Panner1in2out (boost::shared_ptr<Pannable> p)
75         : Panner (p)
76 {
77         if (!Profile->get_trx () ) {
78             if (!_pannable->has_state ()) {
79                     _pannable->pan_azimuth_control->set_value (0.5, Controllable::NoGroup);
80             }
81         }
82
83         update ();
84
85         left = desired_left;
86         right = desired_right;
87         left_interp = left;
88         right_interp = right;
89
90         _pannable->pan_azimuth_control->Changed.connect_same_thread (*this, boost::bind (&Panner1in2out::update, this));
91 }
92
93 Panner1in2out::~Panner1in2out ()
94 {
95 }
96
97 void
98 Panner1in2out::update ()
99 {
100         float panR, panL;
101         float const pan_law_attenuation = -3.0f;
102         float const scale = 2.0f - 4.0f * powf (10.0f,pan_law_attenuation/20.0f);
103
104         panR = _pannable->pan_azimuth_control->get_value();
105         panL = 1 - panR;
106
107         desired_left = panL * (scale * panL + 1.0f - scale);
108         desired_right = panR * (scale * panR + 1.0f - scale);
109 }
110
111 void
112 Panner1in2out::set_position (double p)
113 {
114         if (clamp_position (p)) {
115                 _pannable->pan_azimuth_control->set_value (p, Controllable::NoGroup);
116         }
117 }
118
119 bool
120 Panner1in2out::clamp_position (double& p)
121 {
122         /* any position between 0.0 and 1.0 is legal */
123         DEBUG_TRACE (DEBUG::Panning, string_compose ("want to move panner to %1 - always allowed in 0.0-1.0 range\n", p));
124         p = max (min (p, 1.0), 0.0);
125         return true;
126 }
127
128 pair<double, double>
129 Panner1in2out::position_range () const
130 {
131         return make_pair (0, 1);
132 }
133
134 double
135 Panner1in2out::position () const
136 {
137         return _pannable->pan_azimuth_control->get_value ();
138 }
139
140 void
141 Panner1in2out::distribute_one (AudioBuffer& srcbuf, BufferSet& obufs, gain_t gain_coeff, pframes_t nframes, uint32_t /* not used */)
142 {
143         assert (obufs.count().n_audio() == 2);
144
145         pan_t delta;
146         Sample* dst;
147         pan_t pan;
148
149         Sample* const src = srcbuf.data();
150
151         /* LEFT OUTPUT */
152
153         dst = obufs.get_audio(0).data();
154
155         if (fabsf ((delta = (left - desired_left))) > 0.002) { // about 1 degree of arc
156
157                 /* we've moving the pan by an appreciable amount, so we must
158                    interpolate over 64 frames or nframes, whichever is smaller */
159
160                 pframes_t const limit = min ((pframes_t) 64, nframes);
161                 pframes_t n;
162
163                 delta = -(delta / (float) (limit));
164
165                 for (n = 0; n < limit; n++) {
166                         left_interp = left_interp + delta;
167                         left = left_interp + 0.9 * (left - left_interp);
168                         dst[n] += src[n] * left * gain_coeff;
169                 }
170
171                 /* then pan the rest of the buffer; no need for interpolation for this bit */
172
173                 pan = left * gain_coeff;
174
175                 mix_buffers_with_gain (dst+n,src+n,nframes-n,pan);
176
177         } else {
178
179                 left = desired_left;
180                 left_interp = left;
181
182                 if ((pan = (left * gain_coeff)) != 1.0f) {
183
184                         if (pan != 0.0f) {
185
186                                 /* pan is 1 but also not 0, so we must do it "properly" */
187
188                                 mix_buffers_with_gain(dst,src,nframes,pan);
189
190                                 /* mark that we wrote into the buffer */
191
192                                 // obufs[0] = 0;
193
194                         }
195
196                 } else {
197
198                         /* pan is 1 so we can just copy the input samples straight in */
199
200                         mix_buffers_no_gain(dst,src,nframes);
201
202                         /* XXX it would be nice to mark that we wrote into the buffer */
203                 }
204         }
205
206         /* RIGHT OUTPUT */
207
208         dst = obufs.get_audio(1).data();
209
210         if (fabsf ((delta = (right - desired_right))) > 0.002) { // about 1 degree of arc
211
212                 /* we're moving the pan by an appreciable amount, so we must
213                    interpolate over 64 frames or nframes, whichever is smaller */
214
215                 pframes_t const limit = min ((pframes_t) 64, nframes);
216                 pframes_t n;
217
218                 delta = -(delta / (float) (limit));
219
220                 for (n = 0; n < limit; n++) {
221                         right_interp = right_interp + delta;
222                         right = right_interp + 0.9 * (right - right_interp);
223                         dst[n] += src[n] * right * gain_coeff;
224                 }
225
226                 /* then pan the rest of the buffer, no need for interpolation for this bit */
227
228                 pan = right * gain_coeff;
229
230                 mix_buffers_with_gain(dst+n,src+n,nframes-n,pan);
231
232                 /* XXX it would be nice to mark the buffer as written to */
233
234         } else {
235
236                 right = desired_right;
237                 right_interp = right;
238
239                 if ((pan = (right * gain_coeff)) != 1.0f) {
240
241                         if (pan != 0.0f) {
242
243                                 /* pan is not 1 but also not 0, so we must do it "properly" */
244
245                                 mix_buffers_with_gain(dst,src,nframes,pan);
246
247                                 /* XXX it would be nice to mark the buffer as written to */
248                         }
249
250                 } else {
251
252                         /* pan is 1 so we can just copy the input samples straight in */
253
254                         mix_buffers_no_gain(dst,src,nframes);
255
256                         /* XXX it would be nice to mark the buffer as written to */
257                 }
258         }
259
260 }
261
262 void
263 Panner1in2out::distribute_one_automated (AudioBuffer& srcbuf, BufferSet& obufs,
264                                          framepos_t start, framepos_t end, pframes_t nframes,
265                                          pan_t** buffers, uint32_t which)
266 {
267         assert (obufs.count().n_audio() == 2);
268
269         Sample* dst;
270         pan_t* pbuf;
271         Sample* const src = srcbuf.data();
272         pan_t* const position = buffers[0];
273
274         /* fetch positional data */
275
276         if (!_pannable->pan_azimuth_control->list()->curve().rt_safe_get_vector (start, end, position, nframes)) {
277                 /* fallback */
278                 distribute_one (srcbuf, obufs, 1.0, nframes, which);
279                 return;
280         }
281
282         /* apply pan law to convert positional data into pan coefficients for
283            each buffer (output)
284         */
285
286         const float pan_law_attenuation = -3.0f;
287         const float scale = 2.0f - 4.0f * powf (10.0f,pan_law_attenuation/20.0f);
288
289         for (pframes_t n = 0; n < nframes; ++n) {
290
291                 float panR = position[n];
292                 const float panL = 1 - panR;
293
294                 /* note that are overwriting buffers, but its OK
295                    because we're finished with their old contents
296                    (position automation data) and are
297                    replacing it with panning/gain coefficients
298                    that we need to actually process the data.
299                 */
300
301                 buffers[0][n] = panL * (scale * panL + 1.0f - scale);
302                 buffers[1][n] = panR * (scale * panR + 1.0f - scale);
303         }
304
305         /* LEFT OUTPUT */
306
307         dst = obufs.get_audio(0).data();
308         pbuf = buffers[0];
309
310         for (pframes_t n = 0; n < nframes; ++n) {
311                 dst[n] += src[n] * pbuf[n];
312         }
313
314         /* XXX it would be nice to mark the buffer as written to */
315
316         /* RIGHT OUTPUT */
317
318         dst = obufs.get_audio(1).data();
319         pbuf = buffers[1];
320
321         for (pframes_t n = 0; n < nframes; ++n) {
322                 dst[n] += src[n] * pbuf[n];
323         }
324
325         /* XXX it would be nice to mark the buffer as written to */
326 }
327
328
329 Panner*
330 Panner1in2out::factory (boost::shared_ptr<Pannable> p, boost::shared_ptr<Speakers> /* ignored */)
331 {
332         return new Panner1in2out (p);
333 }
334
335 XMLNode&
336 Panner1in2out::get_state ()
337 {
338         XMLNode& root (Panner::get_state ());
339         root.add_property (X_("uri"), _descriptor.panner_uri);
340         /* this is needed to allow new sessions to load with old Ardour: */
341         root.add_property (X_("type"), _descriptor.name);
342         return root;
343 }
344
345
346 std::set<Evoral::Parameter>
347 Panner1in2out::what_can_be_automated() const
348 {
349         set<Evoral::Parameter> s;
350         s.insert (Evoral::Parameter (PanAzimuthAutomation));
351         return s;
352 }
353
354 string
355 Panner1in2out::describe_parameter (Evoral::Parameter p)
356 {
357         switch (p.type()) {
358         case PanAzimuthAutomation:
359                 return _("L/R");
360         default:
361                 return _pannable->describe_parameter (p);
362         }
363 }
364
365 string
366 Panner1in2out::value_as_string (boost::shared_ptr<const AutomationControl> ac) const
367 {
368         /* DO NOT USE LocaleGuard HERE */
369         double val = ac->get_value();
370
371         switch (ac->parameter().type()) {
372         case PanAzimuthAutomation:
373                 /* We show the position of the center of the image relative to the left & right.
374                    This is expressed as a pair of percentage values that ranges from (100,0)
375                    (hard left) through (50,50) (hard center) to (0,100) (hard right).
376
377                    This is pretty wierd, but its the way audio engineers expect it. Just remember that
378                    the center of the USA isn't Kansas, its (50LA, 50NY) and it will all make sense.
379
380                    This is designed to be as narrow as possible. Dedicated
381                    panner GUIs can do their own version of this if they need
382                    something less compact.
383                 */
384
385                 return string_compose (_("L%1R%2"), (int) rint (100.0 * (1.0 - val)),
386                                        (int) rint (100.0 * val));
387
388         default:
389                 return _("unused");
390         }
391 }
392
393 void
394 Panner1in2out::reset ()
395 {
396         set_position (0.5);
397         update ();
398 }