Comment fixes.
[ardour.git] / libs / ardour / panner_shell.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/boost_debug.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/automatable.h"
48 #include "ardour/buffer_set.h"
49 #include "ardour/debug.h"
50 #include "ardour/pannable.h"
51 #include "ardour/panner.h"
52 #include "ardour/panner_manager.h"
53 #include "ardour/panner_shell.h"
54 #include "ardour/runtime_functions.h"
55 #include "ardour/session.h"
56 #include "ardour/speakers.h"
57 #include "ardour/utils.h"
58
59 #include "i18n.h"
60
61 #include "pbd/mathfix.h"
62
63 using namespace std;
64 using namespace ARDOUR;
65 using namespace PBD;
66
67 PannerShell::PannerShell (string name, Session& s, boost::shared_ptr<Pannable> p)
68         : SessionObject (s, name)
69         , _pannable (p)
70         , _bypassed (false)
71 {
72         set_name (name);
73 }
74
75 PannerShell::~PannerShell ()
76 {
77         DEBUG_TRACE(DEBUG::Destruction, string_compose ("panner shell %3 for %1 destructor, panner is %4, pannable is %2\n", _name, _pannable, this, _panner));
78 }
79
80 void
81 PannerShell::configure_io (ChanCount in, ChanCount out)
82 {
83         uint32_t nouts = out.n_audio();
84         uint32_t nins = in.n_audio();
85
86         /* if new and old config don't need panning, or if
87            the config hasn't changed, we're done.
88         */
89
90         if (_panner && (_panner->in().n_audio() == nins) && (_panner->out().n_audio() == nouts)) {
91                 return;
92         }
93
94         if (nouts < 2 || nins == 0) {
95                 /* no need for panning with less than 2 outputs or no inputs */
96                 if (_panner) {
97                         _panner.reset ();
98                         Changed (); /* EMIT SIGNAL */
99                 }
100                 return;
101         }
102
103         PannerInfo* pi = PannerManager::instance().select_panner (in, out);
104         if (!pi) {
105                 cerr << "No panner found: check that panners are being discovered correctly during startup.\n";
106                 assert (pi);
107         }
108
109         boost::shared_ptr<Speakers> speakers = _session.get_speakers ();
110
111         if (nouts != speakers->size()) {
112                 /* hmm, output count doesn't match session speaker count so
113                    create a new speaker set.
114                 */
115                 Speakers* s = new Speakers ();
116                 s->setup_default_speakers (nouts);
117                 speakers.reset (s);
118         }
119
120         Panner* p = pi->descriptor.factory (_pannable, speakers);
121         // boost_debug_shared_ptr_mark_interesting (p, "Panner");
122         _panner.reset (p);
123         _panner->configure_io (in, out);
124
125         Changed (); /* EMIT SIGNAL */
126 }
127
128 XMLNode&
129 PannerShell::get_state ()
130 {
131         XMLNode* node = new XMLNode ("PannerShell");
132
133         node->add_property (X_("bypassed"), _bypassed ? X_("yes") : X_("no"));
134
135         if (_panner) {
136                 node->add_child_nocopy (_panner->get_state ());
137         }
138
139         return *node;
140 }
141
142 int
143 PannerShell::set_state (const XMLNode& node, int version)
144 {
145         XMLNodeList nlist = node.children ();
146         XMLNodeConstIterator niter;
147         const XMLProperty *prop;
148         LocaleGuard lg (X_("POSIX"));
149
150         if ((prop = node.property (X_("bypassed"))) != 0) {
151                 set_bypassed (string_is_affirmative (prop->value ()));
152         }
153
154         _panner.reset ();
155         
156         for (niter = nlist.begin(); niter != nlist.end(); ++niter) {
157
158                 if ((*niter)->name() == X_("Panner")) {
159
160                         if ((prop = (*niter)->property (X_("type")))) {
161
162                                 list<PannerInfo*>::iterator p;
163                                 PannerManager& pm (PannerManager::instance());
164
165                                 for (p = pm.panner_info.begin(); p != pm.panner_info.end(); ++p) {
166                                         if (prop->value() == (*p)->descriptor.name) {
167
168                                                 /* note that we assume that all the stream panners
169                                                    are of the same type. pretty good
170                                                    assumption, but it's still an assumption.
171                                                 */
172
173                                                 _panner.reset ((*p)->descriptor.factory (_pannable, _session.get_speakers ()));
174
175                                                 if (_panner->set_state (**niter, version) == 0) {
176                                                         return -1;
177                                                 }
178
179                                                 break;
180                                         }
181                                 }
182
183                                 if (p == pm.panner_info.end()) {
184                                         error << string_compose (_("Unknown panner plugin \"%1\" found in pan state - ignored"),
185                                                                  prop->value())
186                                               << endmsg;
187                                 }
188
189                         } else {
190                                 error << _("panner plugin node has no type information!")
191                                       << endmsg;
192                                 return -1;
193                         }
194                 }
195         }
196
197         return 0;
198 }
199
200
201 void
202 PannerShell::distribute_no_automation (BufferSet& inbufs, BufferSet& outbufs, pframes_t nframes, gain_t gain_coeff)
203 {
204         if (outbufs.count().n_audio() == 0) {
205                 // Don't want to lose audio...
206                 assert(inbufs.count().n_audio() == 0);
207                 return;
208         }
209
210         if (outbufs.count().n_audio() == 1) {
211
212                 /* just one output: no real panning going on */
213
214                 AudioBuffer& dst = outbufs.get_audio(0);
215
216                 if (gain_coeff == 0.0f) {
217
218                         /* gain was zero, so make it silent */
219
220                         dst.silence (nframes);
221
222                 } else if (gain_coeff == 1.0f){
223
224                         /* mix all input buffers into the output */
225
226                         // copy the first
227                         dst.read_from(inbufs.get_audio(0), nframes);
228
229                         // accumulate starting with the second
230                         if (inbufs.count().n_audio() > 0) {
231                                 BufferSet::audio_iterator i = inbufs.audio_begin();
232                                 for (++i; i != inbufs.audio_end(); ++i) {
233                                         dst.merge_from(*i, nframes);
234                                 }
235                         }
236
237                 } else {
238
239                         /* mix all buffers into the output, scaling them all by the gain */
240
241                         // copy the first
242                         dst.read_from(inbufs.get_audio(0), nframes);
243
244                         // accumulate (with gain) starting with the second
245                         if (inbufs.count().n_audio() > 0) {
246                                 BufferSet::audio_iterator i = inbufs.audio_begin();
247                                 for (++i; i != inbufs.audio_end(); ++i) {
248                                         dst.accumulate_with_gain_from(*i, nframes, gain_coeff);
249                                 }
250                         }
251
252                 }
253
254                 return;
255         }
256
257         /* multiple outputs ... we must have a panner */
258
259         assert (_panner);
260
261         /* setup silent buffers so that we can mix into the outbuffers (slightly suboptimal -
262            better to copy the first set of data then mix after that, but hey, its 2011)
263         */
264
265         for (BufferSet::audio_iterator b = outbufs.audio_begin(); b != outbufs.audio_end(); ++b) {
266                 (*b).silence (nframes);
267         }
268
269         _panner->distribute (inbufs, outbufs, gain_coeff, nframes);
270 }
271
272 void
273 PannerShell::run (BufferSet& inbufs, BufferSet& outbufs, framepos_t start_frame, framepos_t end_frame, pframes_t nframes)
274 {
275         if (inbufs.count().n_audio() == 0) {
276                 /* Input has no audio buffers (e.g. Aux Send in a MIDI track at a
277                    point with no audio because there is no preceding instrument)
278                 */
279                 outbufs.silence(nframes, 0);
280                 return;
281         }
282
283         if (outbufs.count().n_audio() == 0) {
284                 // Failing to deliver audio we were asked to deliver is a bug
285                 assert(inbufs.count().n_audio() == 0);
286                 return;
287         }
288
289         if (outbufs.count().n_audio() == 1) {
290
291                 /* one output only: no panner */
292
293                 AudioBuffer& dst = outbufs.get_audio(0);
294
295                 // FIXME: apply gain automation?
296
297                 // copy the first
298                 dst.read_from (inbufs.get_audio(0), nframes);
299
300                 // accumulate starting with the second
301                 BufferSet::audio_iterator i = inbufs.audio_begin();
302                 for (++i; i != inbufs.audio_end(); ++i) {
303                         dst.merge_from (*i, nframes);
304                 }
305
306                 return;
307         }
308
309         // More than 1 output
310
311         AutoState as = _panner->automation_state ();
312
313         // If we shouldn't play automation defer to distribute_no_automation
314
315         if (!(as & Play || ((as & Touch) && !_panner->touching()))) {
316
317                 // Speed quietning
318                 gain_t gain_coeff = 1.0;
319
320                 if (fabsf(_session.transport_speed()) > 1.5f && Config->get_quieten_at_speed ()) {
321                         gain_coeff = speed_quietning;
322                 }
323
324                 distribute_no_automation (inbufs, outbufs, nframes, gain_coeff);
325
326         } else {
327
328                 /* setup the terrible silence so that we can mix into the outbuffers (slightly suboptimal -
329                    better to copy the first set of data then mix after that, but hey, its 2011)
330                 */
331                 for (BufferSet::audio_iterator i = outbufs.audio_begin(); i != outbufs.audio_end(); ++i) {
332                         i->silence(nframes);
333                 }
334
335                 _panner->distribute_automated (inbufs, outbufs, start_frame, end_frame, nframes, _session.pan_automation_buffer());
336         }
337 }
338
339 void
340 PannerShell::set_bypassed (bool yn)
341 {
342         if (yn == _bypassed) {
343                 return;
344         }
345         
346         _bypassed = yn;
347         Changed (); /* EMIT SIGNAL */
348 }
349
350 bool
351 PannerShell::bypassed () const
352 {
353         return _bypassed;
354 }