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