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