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