monitor send gets access to the (shared) pannable of the track/bus, thus ensuring...
[ardour.git] / libs / panners / 2in2out / panner_2in2out.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/audio_buffer.h"
45 #include "ardour/audio_buffer.h"
46 #include "ardour/buffer_set.h"
47 #include "ardour/pan_controllable.h"
48 #include "ardour/pannable.h"
49 #include "ardour/runtime_functions.h"
50 #include "ardour/session.h"
51 #include "ardour/utils.h"
52 #include "ardour/mix.h"
53
54 #include "panner_2in2out.h"
55
56 #include "i18n.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         "Equal Power Stereo",
66         2, 2,
67         Panner2in2out::factory
68 };
69
70 extern "C" { PanPluginDescriptor* panner_descriptor () { return &_descriptor; } }
71
72 Panner2in2out::Panner2in2out (boost::shared_ptr<Pannable> p)
73         : Panner (p)
74 {
75         if (!_pannable->has_state()) {
76                 _pannable->pan_azimuth_control->set_value (0.5);
77                 _pannable->pan_width_control->set_value (1.0);
78         } 
79         
80         update ();
81         
82         /* LEFT SIGNAL */
83         left_interp[0] = left[0] = desired_left[0];
84         right_interp[0] = right[0] = desired_right[0]; 
85         
86         /* RIGHT SIGNAL */
87         left_interp[1] = left[1] = desired_left[1];
88         right_interp[1] = right[1] = desired_right[1];
89         
90         _pannable->pan_azimuth_control->Changed.connect_same_thread (*this, boost::bind (&Panner2in2out::update, this));
91         _pannable->pan_width_control->Changed.connect_same_thread (*this, boost::bind (&Panner2in2out::update, this));
92 }
93
94 Panner2in2out::~Panner2in2out ()
95 {
96 }
97
98 double 
99 Panner2in2out::position () const
100 {
101         return _pannable->pan_azimuth_control->get_value();
102 }
103
104 double 
105 Panner2in2out::width () const
106 {
107         return _pannable->pan_width_control->get_value();
108 }
109
110 void
111 Panner2in2out::set_position (double p)
112 {
113         if (clamp_position (p)) {
114                 _pannable->pan_azimuth_control->set_value (p);
115         }
116 }
117
118 void
119 Panner2in2out::set_width (double p)
120 {
121         if (clamp_width (p)) {
122                 _pannable->pan_width_control->set_value (p);
123         }
124 }
125
126 void
127 Panner2in2out::update ()
128 {
129         /* it would be very nice to split this out into a virtual function
130            that can be accessed from BaseStereoPanner and used in do_distribute_automated().
131            
132            but the place where its used in do_distribute_automated() is a tight inner loop,
133            and making "nframes" virtual function calls to compute values is an absurd
134            overhead.
135         */
136         
137         /* x == 0 => hard left = 180.0 degrees
138            x == 1 => hard right = 0.0 degrees
139         */
140         
141         float pos[2];
142         double width = _pannable->pan_width_control->get_value();
143         const double direction_as_lr_fract = _pannable->pan_azimuth_control->get_value();
144
145         if (width < 0.0) {
146                 width = -width;
147                 pos[0] = direction_as_lr_fract + (width/2.0); // left signal lr_fract
148                 pos[1] = direction_as_lr_fract - (width/2.0); // right signal lr_fract
149         } else {
150                 pos[1] = direction_as_lr_fract + (width/2.0); // right signal lr_fract
151                 pos[0] = direction_as_lr_fract - (width/2.0); // left signal lr_fract
152         }
153         
154         /* compute target gain coefficients for both input signals */
155         
156         float const pan_law_attenuation = -3.0f;
157         float const scale = 2.0f - 4.0f * powf (10.0f,pan_law_attenuation/20.0f);
158         float panR;
159         float panL;
160         
161         /* left signal */
162         
163         panR = pos[0];
164         panL = 1 - panR;
165         desired_left[0] = panL * (scale * panL + 1.0f - scale);
166         desired_right[0] = panR * (scale * panR + 1.0f - scale);
167         
168         /* right signal */
169         
170         panR = pos[1];
171         panL = 1 - panR;
172         desired_left[1] = panL * (scale * panL + 1.0f - scale);
173         desired_right[1] = panR * (scale * panR + 1.0f - scale);
174 }
175
176 bool
177 Panner2in2out::clamp_position (double& p)
178 {
179         double w = _pannable->pan_width_control->get_value();
180         return clamp_stereo_pan (p, w);
181 }
182
183 bool
184 Panner2in2out::clamp_width (double& w)
185 {
186         double p = _pannable->pan_azimuth_control->get_value();
187         return clamp_stereo_pan (p, w);
188 }
189
190 bool
191 Panner2in2out::clamp_stereo_pan (double& direction_as_lr_fract, double& width)
192 {
193         double r_pos;
194         double l_pos;
195
196         width = max (min (width, 1.0), -1.0);
197         direction_as_lr_fract = max (min (direction_as_lr_fract, 1.0), 0.0);
198
199         r_pos = direction_as_lr_fract + (width/2.0);
200         l_pos = direction_as_lr_fract - (width/2.0);
201
202         if (width < 0.0) {
203                 swap (r_pos, l_pos);
204         }
205
206         /* if the new left position is less than or equal to zero (hard left) and the left panner
207            is already there, we're not moving the left signal. 
208         */
209         
210         if (l_pos < 0.0) {
211                 return false;
212         }
213
214         /* if the new right position is less than or equal to 1.0 (hard right) and the right panner
215            is already there, we're not moving the right signal. 
216         */
217         
218         if (r_pos > 1.0) {
219                 return false;
220                 
221         }
222
223         return true;
224 }
225
226 void
227 Panner2in2out::distribute_one (AudioBuffer& srcbuf, BufferSet& obufs, gain_t gain_coeff, pframes_t nframes, uint32_t which)
228 {
229         assert (obufs.count().n_audio() == 2);
230
231         pan_t delta;
232         Sample* dst;
233         pan_t pan;
234
235         Sample* const src = srcbuf.data();
236         
237         /* LEFT OUTPUT */
238
239         dst = obufs.get_audio(0).data();
240
241         if (fabsf ((delta = (left[which] - desired_left[which]))) > 0.002) { // about 1 degree of arc
242
243                 /* we've moving the pan by an appreciable amount, so we must
244                    interpolate over 64 frames or nframes, whichever is smaller */
245
246                 pframes_t const limit = min ((pframes_t) 64, nframes);
247                 pframes_t n;
248
249                 delta = -(delta / (float) (limit));
250
251                 for (n = 0; n < limit; n++) {
252                         left_interp[which] = left_interp[which] + delta;
253                         left[which] = left_interp[which] + 0.9 * (left[which] - left_interp[which]);
254                         dst[n] += src[n] * left[which] * gain_coeff;
255                 }
256
257                 /* then pan the rest of the buffer; no need for interpolation for this bit */
258
259                 pan = left[which] * gain_coeff;
260
261                 mix_buffers_with_gain (dst+n,src+n,nframes-n,pan);
262
263         } else {
264
265                 left[which] = desired_left[which];
266                 left_interp[which] = left[which];
267
268                 if ((pan = (left[which] * gain_coeff)) != 1.0f) {
269
270                         if (pan != 0.0f) {
271
272                                 /* pan is 1 but also not 0, so we must do it "properly" */
273                                 
274                                 //obufs.get_audio(1).read_from (srcbuf, nframes);
275                                 mix_buffers_with_gain(dst,src,nframes,pan);
276
277                                 /* mark that we wrote into the buffer */
278
279                                 // obufs[0] = 0;
280
281                         }
282
283                 } else {
284
285                         /* pan is 1 so we can just copy the input samples straight in */
286
287                         mix_buffers_no_gain(dst,src,nframes);
288                         
289                         /* XXX it would be nice to mark that we wrote into the buffer */
290                 }
291         }
292
293         /* RIGHT OUTPUT */
294
295         dst = obufs.get_audio(1).data();
296
297         if (fabsf ((delta = (right[which] - desired_right[which]))) > 0.002) { // about 1 degree of arc
298
299                 /* we're moving the pan by an appreciable amount, so we must
300                    interpolate over 64 frames or nframes, whichever is smaller */
301
302                 pframes_t const limit = min ((pframes_t) 64, nframes);
303                 pframes_t n;
304
305                 delta = -(delta / (float) (limit));
306
307                 for (n = 0; n < limit; n++) {
308                         right_interp[which] = right_interp[which] + delta;
309                         right[which] = right_interp[which] + 0.9 * (right[which] - right_interp[which]);
310                         dst[n] += src[n] * right[which] * gain_coeff;
311                 }
312
313                 /* then pan the rest of the buffer, no need for interpolation for this bit */
314
315                 pan = right[which] * gain_coeff;
316
317                 mix_buffers_with_gain(dst+n,src+n,nframes-n,pan);
318
319                 /* XXX it would be nice to mark the buffer as written to */
320
321         } else {
322
323                 right[which] = desired_right[which];
324                 right_interp[which] = right[which];
325
326                 if ((pan = (right[which] * gain_coeff)) != 1.0f) {
327
328                         if (pan != 0.0f) {
329
330                                 /* pan is not 1 but also not 0, so we must do it "properly" */
331                                 
332                                 mix_buffers_with_gain(dst,src,nframes,pan);
333                                 // obufs.get_audio(1).read_from (srcbuf, nframes);
334                                 
335                                 /* XXX it would be nice to mark the buffer as written to */
336                         }
337
338                 } else {
339
340                         /* pan is 1 so we can just copy the input samples straight in */
341                         
342                         mix_buffers_no_gain(dst,src,nframes);
343
344                         /* XXX it would be nice to mark the buffer as written to */
345                 }
346         }
347 }
348
349 void
350 Panner2in2out::distribute_one_automated (AudioBuffer& srcbuf, BufferSet& obufs,
351                                          framepos_t start, framepos_t end, pframes_t nframes,
352                                          pan_t** buffers, uint32_t which)
353 {
354         assert (obufs.count().n_audio() == 2);
355
356         Sample* dst;
357         pan_t* pbuf;
358         Sample* const src = srcbuf.data();
359         pan_t* const position = buffers[0];
360         pan_t* const width = buffers[1];
361
362         /* fetch positional data */
363
364         if (!_pannable->pan_azimuth_control->list()->curve().rt_safe_get_vector (start, end, position, nframes)) {
365                 /* fallback */
366                 distribute_one (srcbuf, obufs, 1.0, nframes, which);
367                 return;
368         }
369
370         if (!_pannable->pan_width_control->list()->curve().rt_safe_get_vector (start, end, width, nframes)) {
371                 /* fallback */
372                 distribute_one (srcbuf, obufs, 1.0, nframes, which);
373                 return;
374         }
375
376         /* apply pan law to convert positional data into pan coefficients for
377            each buffer (output)
378         */
379
380         const float pan_law_attenuation = -3.0f;
381         const float scale = 2.0f - 4.0f * powf (10.0f,pan_law_attenuation/20.0f);
382
383         for (pframes_t n = 0; n < nframes; ++n) {
384
385                 float panR;
386
387                 if (which == 0) { 
388                         // panning left signal
389                         panR = position[n] - (width[n]/2.0f); // center - width/2
390                 } else {
391                         // panning right signal
392                         panR = position[n] + (width[n]/2.0f); // center - width/2
393                 }
394
395                 const float panL = 1 - panR;
396
397                 /* note that are overwriting buffers, but its OK
398                    because we're finished with their old contents
399                    (position/width automation data) and are
400                    replacing it with panning/gain coefficients 
401                    that we need to actually process the data.
402                 */
403                 
404                 buffers[0][n] = panL * (scale * panL + 1.0f - scale);
405                 buffers[1][n] = panR * (scale * panR + 1.0f - scale);
406         }
407
408         /* LEFT OUTPUT */
409
410         dst = obufs.get_audio(0).data();
411         pbuf = buffers[0];
412
413         for (pframes_t n = 0; n < nframes; ++n) {
414                 dst[n] += src[n] * pbuf[n];
415         }
416
417         /* XXX it would be nice to mark the buffer as written to */
418
419         /* RIGHT OUTPUT */
420
421         dst = obufs.get_audio(1).data();
422         pbuf = buffers[1];
423
424         for (pframes_t n = 0; n < nframes; ++n) {
425                 dst[n] += src[n] * pbuf[n];
426         }
427
428         /* XXX it would be nice to mark the buffer as written to */
429 }
430
431 Panner*
432 Panner2in2out::factory (boost::shared_ptr<Pannable> p, boost::shared_ptr<Speakers> /* ignored */)
433 {
434         return new Panner2in2out (p);
435 }
436
437 XMLNode&
438 Panner2in2out::get_state (void)
439 {
440         return state (true);
441 }
442
443 XMLNode&
444 Panner2in2out::state (bool /*full_state*/)
445 {
446         XMLNode& root (Panner::get_state ());
447         root.add_property (X_("type"), _descriptor.name);
448         return root;
449 }
450
451 int
452 Panner2in2out::set_state (const XMLNode& node, int version)
453 {
454         LocaleGuard lg (X_("POSIX"));
455         Panner::set_state (node, version);
456         return 0;
457 }
458
459 std::set<Evoral::Parameter> 
460 Panner2in2out::what_can_be_automated() const
461 {
462         set<Evoral::Parameter> s;
463         s.insert (Evoral::Parameter (PanAzimuthAutomation));
464         s.insert (Evoral::Parameter (PanWidthAutomation));
465         return s;
466 }
467
468 string
469 Panner2in2out::describe_parameter (Evoral::Parameter p)
470 {
471         switch (p.type()) {
472         case PanAzimuthAutomation:
473                 return _("L/R");
474         case PanWidthAutomation:
475                 return _("Width");
476         default:
477                 return _pannable->describe_parameter (p);
478         }
479 }
480
481 string 
482 Panner2in2out::value_as_string (boost::shared_ptr<AutomationControl> ac) const
483 {
484         /* DO NOT USE LocaleGuard HERE */
485         double val = ac->get_value();
486
487         switch (ac->parameter().type()) {
488         case PanAzimuthAutomation:
489                 /* We show the position of the center of the image relative to the left & right.
490                    This is expressed as a pair of percentage values that ranges from (100,0) 
491                    (hard left) through (50,50) (hard center) to (0,100) (hard right).
492                    
493                    This is pretty wierd, but its the way audio engineers expect it. Just remember that
494                    the center of the USA isn't Kansas, its (50LA, 50NY) and it will all make sense.
495                 */
496                 
497                 return string_compose (_("L:%1 R:%2"), (int) rint (100.0 * (1.0 - val)),
498                                        (int) rint (100.0 * val));
499                 
500         case PanWidthAutomation:
501                 return string_compose (_("Width: %1%%"), (int) floor (100.0 * val));
502                 
503         default:
504                 return _pannable->value_as_string (ac);
505         }
506 }