tentative commit of new panners subtree
[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/panner_1in2out.h"
47 #include "ardour/utils.h"
48 #include "ardour/audio_buffer.h"
49
50 #include "ardour/runtime_functions.h"
51 #include "ardour/buffer_set.h"
52 #include "ardour/audio_buffer.h"
53 #include "ardour/vbap.h"
54
55 #include "i18n.h"
56
57 #include "pbd/mathfix.h"
58
59 using namespace std;
60 using namespace ARDOUR;
61 using namespace PBD;
62
63 static PanPluginDescriptor _descriptor = {
64         "Mono to Stereo Panner",
65         1, 1, 2, 2,
66         Panner1in2out::factory
67 };
68
69 extern "C" { PanPluginDescriptor* panner_descriptor () { return &_descriptor; }
70
71 Panner1in2out::Panner1in2out (PannerShell& p)
72         : Panner (p)
73         , _position (new PanControllable (parent.session(), _("position"), this, Evoral::Parameter(PanAzimuthAutomation, 0, 0)))
74         , left (0.5)
75         , right (0.5)
76         , left_interp (left)
77         , right_interp (right)
78 {
79         desired_left = left;
80         desired_right = right;
81 }
82
83 Panner1in2out::~Panner1in2out ()
84 {
85 }
86
87 void
88 Panner1in2out::set_position (double p)
89 {
90         _desired_right = p;
91         _desired_left = 1 - p;
92 }
93
94 void
95 Panner1in2out::do_distribute_one (AudioBuffer& srcbuf, BufferSet& obufs, gain_t gain_coeff, pframes_t nframes, uint32_t /* not used */)
96 {
97         assert (obufs.count().n_audio() == 2);
98
99         pan_t delta;
100         Sample* dst;
101         pan_t pan;
102
103         if (_muted) {
104                 return;
105         }
106
107         Sample* const src = srcbuf.data();
108         
109         /* LEFT OUTPUT */
110
111         dst = obufs.get_audio(0).data();
112
113         if (fabsf ((delta = (left[which] - desired_left[which]))) > 0.002) { // about 1 degree of arc
114
115                 /* we've moving the pan by an appreciable amount, so we must
116                    interpolate over 64 frames or nframes, whichever is smaller */
117
118                 pframes_t const limit = min ((pframes_t) 64, nframes);
119                 pframes_t n;
120
121                 delta = -(delta / (float) (limit));
122
123                 for (n = 0; n < limit; n++) {
124                         left_interp[which] = left_interp[which] + delta;
125                         left = left_interp[which] + 0.9 * (left[which] - left_interp[which]);
126                         dst[n] += src[n] * left[which] * gain_coeff;
127                 }
128
129                 /* then pan the rest of the buffer; no need for interpolation for this bit */
130
131                 pan = left[which] * gain_coeff;
132
133                 mix_buffers_with_gain (dst+n,src+n,nframes-n,pan);
134
135         } else {
136
137                 left[which] = desired_left[which];
138                 left_interp[which] = left[which];
139
140                 if ((pan = (left[which] * gain_coeff)) != 1.0f) {
141
142                         if (pan != 0.0f) {
143
144                                 /* pan is 1 but also not 0, so we must do it "properly" */
145
146                                 mix_buffers_with_gain(dst,src,nframes,pan);
147
148                                 /* mark that we wrote into the buffer */
149
150                                 // obufs[0] = 0;
151
152                         }
153
154                 } else {
155
156                         /* pan is 1 so we can just copy the input samples straight in */
157
158                         mix_buffers_no_gain(dst,src,nframes);
159                         
160                         /* XXX it would be nice to mark that we wrote into the buffer */
161                 }
162         }
163
164         /* RIGHT OUTPUT */
165
166         dst = obufs.get_audio(1).data();
167
168         if (fabsf ((delta = (right[which] - desired_right[which]))) > 0.002) { // about 1 degree of arc
169
170                 /* we're moving the pan by an appreciable amount, so we must
171                    interpolate over 64 frames or nframes, whichever is smaller */
172
173                 pframes_t const limit = min ((pframes_t) 64, nframes);
174                 pframes_t n;
175
176                 delta = -(delta / (float) (limit));
177
178                 for (n = 0; n < limit; n++) {
179                         right_interp[which] = right_interp[which] + delta;
180                         right[which] = right_interp[which] + 0.9 * (right[which] - right_interp[which]);
181                         dst[n] += src[n] * right[which] * gain_coeff;
182                 }
183
184                 /* then pan the rest of the buffer, no need for interpolation for this bit */
185
186                 pan = right[which] * gain_coeff;
187
188                 mix_buffers_with_gain(dst+n,src+n,nframes-n,pan);
189
190                 /* XXX it would be nice to mark the buffer as written to */
191
192         } else {
193
194                 right[which] = desired_right[which];
195                 right_interp[which] = right[which];
196
197                 if ((pan = (right[which] * gain_coeff)) != 1.0f) {
198
199                         if (pan != 0.0f) {
200
201                                 /* pan is not 1 but also not 0, so we must do it "properly" */
202                                 
203                                 mix_buffers_with_gain(dst,src,nframes,pan);
204
205                                 /* XXX it would be nice to mark the buffer as written to */
206                         }
207
208                 } else {
209
210                         /* pan is 1 so we can just copy the input samples straight in */
211                         
212                         mix_buffers_no_gain(dst,src,nframes);
213
214                         /* XXX it would be nice to mark the buffer as written to */
215                 }
216         }
217
218 }
219
220 string
221 Panner1in2out::describe_parameter (Evoral::Parameter param)
222 {
223         switch (param.type()) {
224         case PanWidthAutomation:
225                 return "Pan:width";
226         case PanAzimuthAutomation:
227                 return "Pan:position";
228         case PanElevationAutomation: 
229                 error << X_("stereo panner should not have elevation control") << endmsg;
230                 return "Pan:elevation";
231         } 
232         
233         return Automatable::describe_parameter (param);
234 }
235