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