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