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