rework panning -- Squashed commit of the following:
[ardour.git] / libs / panners / stereobalance / panner_balance.cc
1 /*
2     Copyright (C) 2004-2011 Paul Davis
3     adopted from 2in2out panner by Robin Gareus <robin@gareus.org>
4
5     This program is free software; you can redistribute it and/or modify
6     it under the terms of the GNU General Public License as published by
7     the Free Software Foundation; either version 2 of the License, or
8     (at your option) any later version.
9
10     This program is distributed in the hope that it will be useful,
11     but WITHOUT ANY WARRANTY; without even the implied warranty of
12     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13     GNU General Public License for more details.
14
15     You should have received a copy of the GNU General Public License
16     along with this program; if not, write to the Free Software
17     Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
18
19 */
20
21 #include <inttypes.h>
22
23 #include <cmath>
24 #include <cerrno>
25 #include <fstream>
26 #include <cstdlib>
27 #include <string>
28 #include <cstdio>
29 #include <locale.h>
30 #include <unistd.h>
31 #include <float.h>
32 #include <iomanip>
33
34 #include <glibmm.h>
35
36 #include "pbd/cartesian.h"
37 #include "pbd/convert.h"
38 #include "pbd/error.h"
39 #include "pbd/failed_constructor.h"
40 #include "pbd/xml++.h"
41 #include "pbd/enumwriter.h"
42
43 #include "evoral/Curve.hpp"
44
45 #include "ardour/audio_buffer.h"
46 #include "ardour/audio_buffer.h"
47 #include "ardour/buffer_set.h"
48 #include "ardour/pan_controllable.h"
49 #include "ardour/pannable.h"
50 #include "ardour/runtime_functions.h"
51 #include "ardour/session.h"
52 #include "ardour/utils.h"
53 #include "ardour/mix.h"
54
55 #include "panner_balance.h"
56
57 #include "i18n.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         "Stereo Balance",
67         "http://ardour.org/plugin/panner_balance",
68         "http://ardour.org/plugin/panner_balance#ui",
69         2, 2,
70         Pannerbalance::factory
71 };
72
73 extern "C" { PanPluginDescriptor* panner_descriptor () { return &_descriptor; } }
74
75 Pannerbalance::Pannerbalance (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 SIGNAL */
85         pos_interp[0] = pos[0] = desired_pos[0];
86         /* RIGHT SIGNAL */
87         pos_interp[1] = pos[1] = desired_pos[1];
88
89         _pannable->pan_azimuth_control->Changed.connect_same_thread (*this, boost::bind (&Pannerbalance::update, this));
90 }
91
92 Pannerbalance::~Pannerbalance ()
93 {
94 }
95
96 double
97 Pannerbalance::position () const
98 {
99         return _pannable->pan_azimuth_control->get_value();
100 }
101
102         void
103 Pannerbalance::set_position (double p)
104 {
105         if (clamp_position (p)) {
106                 _pannable->pan_azimuth_control->set_value (p);
107         }
108 }
109
110         void
111 Pannerbalance::thaw ()
112 {
113         Panner::thaw ();
114         if (_frozen == 0) {
115                 update ();
116         }
117 }
118
119 void
120 Pannerbalance::update ()
121 {
122         if (_frozen) {
123                 return;
124         }
125
126         float const pos = _pannable->pan_azimuth_control->get_value();
127
128         if (pos == .5) {
129                 desired_pos[0] = 1.0;
130                 desired_pos[1] = 1.0;
131         } else if (pos > .5) {
132                 desired_pos[0] = 2 - 2. * pos;
133                 desired_pos[1] = 1.0;
134         } else {
135                 desired_pos[0] = 1.0;
136                 desired_pos[1] = 2. * pos;
137         }
138 }
139
140 bool
141 Pannerbalance::clamp_position (double& p)
142 {
143         p = max (min (p, 1.0), 0.0);
144         return true;
145 }
146
147 pair<double, double>
148 Pannerbalance::position_range () const
149 {
150         return make_pair (0, 1);
151 }
152
153 void
154 Pannerbalance::distribute_one (AudioBuffer& srcbuf, BufferSet& obufs, gain_t gain_coeff, pframes_t nframes, uint32_t which)
155 {
156         assert (obufs.count().n_audio() == 2);
157
158         pan_t delta;
159         Sample* dst;
160         pan_t pan;
161
162         Sample* const src = srcbuf.data();
163
164         dst = obufs.get_audio(which).data();
165
166         if (fabsf ((delta = (pos[which] - desired_pos[which]))) > 0.002) { // about 1 degree of arc
167
168                 /* we've moving the pan by an appreciable amount, so we must
169                          interpolate over 64 frames or nframes, whichever is smaller */
170
171                 pframes_t const limit = min ((pframes_t) 64, nframes);
172                 pframes_t n;
173
174                 delta = -(delta / (float) (limit));
175
176                 for (n = 0; n < limit; n++) {
177                         pos_interp[which] = pos_interp[which] + delta;
178                         pos[which] = pos_interp[which] + 0.9 * (pos[which] - pos_interp[which]);
179                         dst[n] += src[n] * pos[which] * gain_coeff;
180                 }
181
182                 /* then pan the rest of the buffer; no need for interpolation for this bit */
183
184                 pan = pos[which] * gain_coeff;
185
186                 mix_buffers_with_gain (dst+n,src+n,nframes-n,pan);
187
188         } else {
189
190                 pos[which] = desired_pos[which];
191                 pos_interp[which] = pos[which];
192
193                 if ((pan = (pos[which] * gain_coeff)) != 1.0f) {
194
195                         if (pan != 0.0f) {
196
197                                 /* pan is 1 but also not 0, so we must do it "properly" */
198
199                                 //obufs.get_audio(1).read_from (srcbuf, nframes);
200                                 mix_buffers_with_gain(dst,src,nframes,pan);
201
202                                 /* mark that we wrote into the buffer */
203
204                                 // obufs[0] = 0;
205
206                         }
207
208                 } else {
209                         /* pan is 1 so we can just copy the input samples straight in */
210                         mix_buffers_no_gain(dst,src,nframes);
211                 }
212         }
213 }
214
215 void
216 Pannerbalance::distribute_one_automated (AudioBuffer& srcbuf, BufferSet& obufs,
217                                          framepos_t start, framepos_t end, pframes_t nframes,
218                                          pan_t** buffers, uint32_t which)
219 {
220         assert (obufs.count().n_audio() == 2);
221
222         Sample* dst;
223         pan_t* pbuf;
224         Sample* const src = srcbuf.data();
225         pan_t* const position = buffers[0];
226
227         /* fetch positional data */
228
229         if (!_pannable->pan_azimuth_control->list()->curve().rt_safe_get_vector (start, end, position, nframes)) {
230                 /* fallback */
231                 distribute_one (srcbuf, obufs, 1.0, nframes, which);
232                 return;
233         }
234
235         for (pframes_t n = 0; n < nframes; ++n) {
236
237                 float const pos = position[n];
238
239                 if (which == 0) { // Left
240                         if (pos > .5) {
241                                 buffers[which][n] = 2 - 2. * pos;
242                         } else {
243                                 buffers[which][n] = 1.0;
244                         }
245                 } else { // Right
246                         if (pos < .5) {
247                                 buffers[which][n] = 2. * pos;
248                         } else {
249                                 buffers[which][n] = 1.0;
250                         }
251                 }
252         }
253
254         dst = obufs.get_audio(which).data();
255         pbuf = buffers[which];
256
257         for (pframes_t n = 0; n < nframes; ++n) {
258                 dst[n] += src[n] * pbuf[n];
259         }
260
261         /* XXX it would be nice to mark the buffer as written to */
262 }
263
264 Panner*
265 Pannerbalance::factory (boost::shared_ptr<Pannable> p, boost::shared_ptr<Speakers> /* ignored */)
266 {
267         return new Pannerbalance (p);
268 }
269
270         XMLNode&
271 Pannerbalance::get_state ()
272 {
273         XMLNode& root (Panner::get_state ());
274         root.add_property (X_("uri"), _descriptor.panner_uri);
275         /* this is needed to allow new sessions to load with old Ardour: */
276         root.add_property (X_("type"), _descriptor.name);
277         return root;
278 }
279
280 std::set<Evoral::Parameter>
281 Pannerbalance::what_can_be_automated() const
282 {
283         set<Evoral::Parameter> s;
284         s.insert (Evoral::Parameter (PanAzimuthAutomation));
285         return s;
286 }
287
288 string
289 Pannerbalance::describe_parameter (Evoral::Parameter p)
290 {
291         switch (p.type()) {
292                 case PanAzimuthAutomation:
293                         return _("L/R");
294                 default:
295                         return _pannable->describe_parameter (p);
296         }
297 }
298
299 string
300 Pannerbalance::value_as_string (boost::shared_ptr<AutomationControl> ac) const
301 {
302         /* DO NOT USE LocaleGuard HERE */
303         double val = ac->get_value();
304
305         switch (ac->parameter().type()) {
306                 case PanAzimuthAutomation:
307                         /* We show the position of the center of the image relative to the left & right.
308                                  This is expressed as a pair of percentage values that ranges from (100,0)
309                                  (hard left) through (50,50) (hard center) to (0,100) (hard right).
310
311                                  This is pretty wierd, but its the way audio engineers expect it. Just remember that
312                                  the center of the USA isn't Kansas, its (50LA, 50NY) and it will all make sense.
313
314                                  This is designed to be as narrow as possible. Dedicated
315                                  panner GUIs can do their own version of this if they need
316                                  something less compact.
317                                  */
318
319                         return string_compose (_("L%1R%2"), (int) rint (100.0 * (1.0 - val)),
320                                         (int) rint (100.0 * val));
321
322                 default:
323                         return _pannable->value_as_string (ac);
324         }
325 }
326
327 void
328 Pannerbalance::reset ()
329 {
330         set_position (0.5);
331         update ();
332 }