ardour
panner_shell.cc
Go to the documentation of this file.
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/audioengine.h"
47 #include "ardour/buffer_set.h"
48 #include "ardour/debug.h"
49 #include "ardour/pannable.h"
50 #include "ardour/panner.h"
51 #include "ardour/panner_manager.h"
52 #include "ardour/panner_shell.h"
53 #include "ardour/session.h"
54 #include "ardour/speakers.h"
55 
56 #include "i18n.h"
57 
58 #include "pbd/mathfix.h"
59 
60 using namespace std;
61 using namespace ARDOUR;
62 using namespace PBD;
63 
64 PannerShell::PannerShell (string name, Session& s, boost::shared_ptr<Pannable> p, bool is_send)
65  : SessionObject (s, name)
66  , _pannable_route (p)
67  , _is_send (is_send)
68  , _panlinked (true)
69  , _bypassed (false)
70  , _current_panner_uri("")
71  , _user_selected_panner_uri("")
72  , _panner_gui_uri("")
73  , _force_reselect (false)
74 {
75  if (is_send) {
76  _pannable_internal.reset(new Pannable (s));
77  if (Config->get_link_send_and_route_panner()) {
78  _panlinked = true;
79  } else {
80  _panlinked = false;
81  }
82  }
83  set_name (name);
84 }
85 
87 {
88  DEBUG_TRACE(DEBUG::Destruction, string_compose ("panner shell %3 for %1 destructor, panner is %4, pannable is %2\n", _name, _pannable_route, this, _panner));
89 }
90 
91 void
93 {
94  uint32_t nouts = out.n_audio();
95  uint32_t nins = in.n_audio();
96 
97  /* if new and old config don't need panning, or if
98  the config hasn't changed, we're done.
99  */
100 
101  if (!_force_reselect && _panner && (_panner->in().n_audio() == nins) && (_panner->out().n_audio() == nouts)) {
102  return;
103  }
104  _force_reselect = false;
105 
106  if (nouts < 2 || nins == 0) {
107  /* no need for panning with less than 2 outputs or no inputs */
108  if (_panner) {
109  _panner.reset ();
110  _current_panner_uri = "";
111  _panner_gui_uri = "";
112  if (!_is_send || !_panlinked) {
113  pannable()->set_panner(_panner);
114  }
115  Changed (); /* EMIT SIGNAL */
116  }
117  return;
118  }
119 
121  if (!pi) {
122  fatal << _("No panner found: check that panners are being discovered correctly during startup.") << endmsg;
123  abort(); /*NOTREACHED*/
124  }
125 
126  DEBUG_TRACE (DEBUG::Panning, string_compose (_("select panner: %1\n"), pi->descriptor.name.c_str()));
127 
129 
130  if (nouts != speakers->size()) {
131  /* hmm, output count doesn't match session speaker count so
132  create a new speaker set.
133  */
134  Speakers* s = new Speakers ();
135  s->setup_default_speakers (nouts);
136  speakers.reset (s);
137  }
138 
139  /* TODO don't allow to link _is_send if internal & route panners are different types */
140  Panner* p = pi->descriptor.factory (pannable(), speakers);
141  // boost_debug_shared_ptr_mark_interesting (p, "Panner");
142  _panner.reset (p);
143  _panner->configure_io (in, out);
146 
147  if (!_is_send || !_panlinked) {
148  pannable()->set_panner(_panner);
149  }
150  Changed (); /* EMIT SIGNAL */
151 }
152 
153 XMLNode&
155 {
156  XMLNode* node = new XMLNode ("PannerShell");
157 
158  node->add_property (X_("bypassed"), _bypassed ? X_("yes") : X_("no"));
159  node->add_property (X_("user-panner"), _user_selected_panner_uri);
160  node->add_property (X_("linked-to-route"), _panlinked ? X_("yes") : X_("no"));
161 
162  if (_panner && _is_send) {
163  node->add_child_nocopy (_panner->get_state ());
164  }
165 
166  return *node;
167 }
168 
169 int
170 PannerShell::set_state (const XMLNode& node, int version)
171 {
172  XMLNodeList nlist = node.children ();
173  XMLNodeConstIterator niter;
174  const XMLProperty *prop;
175  LocaleGuard lg (X_("C"));
176 
177  if ((prop = node.property (X_("bypassed"))) != 0) {
179  }
180 
181  if ((prop = node.property (X_("linked-to-route"))) != 0) {
183  }
184 
185  if ((prop = node.property (X_("user-panner"))) != 0) {
186  _user_selected_panner_uri = prop->value ();
187  }
188 
189  _panner.reset ();
190 
191  for (niter = nlist.begin(); niter != nlist.end(); ++niter) {
192 
193  if ((*niter)->name() == X_("Panner")) {
194 
195  if ((prop = (*niter)->property (X_("uri")))) {
197  if (p) {
198  _panner.reset (p->descriptor.factory (
202  if (_is_send) {
203  if (!_panlinked) {
204  _pannable_internal->set_panner(_panner);
205  } else {
206  _force_reselect = true;
207  }
208  } else {
209  _pannable_route->set_panner(_panner);
210  }
211  if (_panner->set_state (**niter, version) == 0) {
212  return -1;
213  }
214  }
215  }
216 
217  else /* backwards compatibility */
218  if ((prop = (*niter)->property (X_("type")))) {
219 
220  list<PannerInfo*>::iterator p;
222 
223  for (p = pm.panner_info.begin(); p != pm.panner_info.end(); ++p) {
224  if (prop->value() == (*p)->descriptor.name) {
225 
226  /* note that we assume that all the stream panners
227  are of the same type. pretty good
228  assumption, but it's still an assumption.
229  */
230 
231  _panner.reset ((*p)->descriptor.factory (
233  _current_panner_uri = (*p)->descriptor.panner_uri;
234  _panner_gui_uri = (*p)->descriptor.gui_uri;
235 
236  if (_is_send) {
237  if (!_panlinked) {
238  _pannable_internal->set_panner(_panner);
239  } else {
240  _force_reselect = true;
241  }
242  } else {
243  _pannable_route->set_panner(_panner);
244  }
245 
246  if (_panner->set_state (**niter, version) == 0) {
247  return -1;
248  }
249 
250  break;
251  }
252  }
253 
254  if (p == pm.panner_info.end()) {
255  error << string_compose (_("Unknown panner plugin \"%1\" found in pan state - ignored"),
256  prop->value())
257  << endmsg;
258  }
259 
260  } else {
261  error << _("panner plugin node has no type information!")
262  << endmsg;
263  return -1;
264  }
265  }
266  }
267 
268  return 0;
269 }
270 
271 
272 void
274 {
275  if (outbufs.count().n_audio() == 0) {
276  // Don't want to lose audio...
277  assert(inbufs.count().n_audio() == 0);
278  return;
279  }
280 
281  if (outbufs.count().n_audio() == 1) {
282 
283  /* just one output: no real panning going on */
284 
285  AudioBuffer& dst = outbufs.get_audio(0);
286 
287  if (gain_coeff == GAIN_COEFF_ZERO) {
288 
289  /* gain was zero, so make it silent */
290 
291  dst.silence (nframes);
292 
293  } else if (gain_coeff == GAIN_COEFF_UNITY){
294 
295  /* mix all input buffers into the output */
296 
297  // copy the first
298  dst.read_from(inbufs.get_audio(0), nframes);
299 
300  // accumulate starting with the second
301  if (inbufs.count().n_audio() > 0) {
303  for (++i; i != inbufs.audio_end(); ++i) {
304  dst.merge_from(*i, nframes);
305  }
306  }
307 
308  } else {
309 
310  /* mix all buffers into the output, scaling them all by the gain */
311 
312  // copy the first
313  dst.read_from(inbufs.get_audio(0), nframes);
314 
315  // accumulate (with gain) starting with the second
316  if (inbufs.count().n_audio() > 0) {
318  for (++i; i != inbufs.audio_end(); ++i) {
319  dst.accumulate_with_gain_from(*i, nframes, gain_coeff);
320  }
321  }
322 
323  }
324 
325  return;
326  }
327 
328  /* multiple outputs ... we must have a panner */
329 
330  assert (_panner);
331 
332  /* setup silent buffers so that we can mix into the outbuffers (slightly suboptimal -
333  better to copy the first set of data then mix after that, but hey, its 2011)
334  */
335 
336  for (BufferSet::audio_iterator b = outbufs.audio_begin(); b != outbufs.audio_end(); ++b) {
337  (*b).silence (nframes);
338  }
339 
340  _panner->distribute (inbufs, outbufs, gain_coeff, nframes);
341 }
342 
343 void
344 PannerShell::run (BufferSet& inbufs, BufferSet& outbufs, framepos_t start_frame, framepos_t end_frame, pframes_t nframes)
345 {
346  if (inbufs.count().n_audio() == 0) {
347  /* Input has no audio buffers (e.g. Aux Send in a MIDI track at a
348  point with no audio because there is no preceding instrument)
349  */
350  outbufs.silence(nframes, 0);
351  return;
352  }
353 
354  if (outbufs.count().n_audio() == 0) {
355  // Failing to deliver audio we were asked to deliver is a bug
356  assert(inbufs.count().n_audio() == 0);
357  return;
358  }
359 
360  if (outbufs.count().n_audio() == 1) {
361 
362  /* one output only: no panner */
363 
364  AudioBuffer& dst = outbufs.get_audio(0);
365 
366  // FIXME: apply gain automation?
367 
368  // copy the first
369  dst.read_from (inbufs.get_audio(0), nframes);
370 
371  // accumulate starting with the second
373  for (++i; i != inbufs.audio_end(); ++i) {
374  dst.merge_from (*i, nframes);
375  }
376 
377  return;
378  }
379 
380  // More than 1 output
381 
382  AutoState as = _panner->automation_state ();
383 
384  // If we shouldn't play automation defer to distribute_no_automation
385 
386  if (!(as & Play || ((as & Touch) && !_panner->touching()))) {
387 
388  // Speed quietning
389  gain_t gain_coeff = GAIN_COEFF_UNITY;
390 
391  if (fabsf(_session.transport_speed()) > 1.5f && Config->get_quieten_at_speed ()) {
392  gain_coeff = speed_quietning;
393  }
394 
395  distribute_no_automation (inbufs, outbufs, nframes, gain_coeff);
396 
397  } else {
398 
399  /* setup the terrible silence so that we can mix into the outbuffers (slightly suboptimal -
400  better to copy the first set of data then mix after that, but hey, its 2011)
401  */
402  for (BufferSet::audio_iterator i = outbufs.audio_begin(); i != outbufs.audio_end(); ++i) {
403  i->silence(nframes);
404  }
405 
406  _panner->distribute_automated (inbufs, outbufs, start_frame, end_frame, nframes, _session.pan_automation_buffer());
407  }
408 }
409 
410 void
412 {
413  if (yn == _bypassed) {
414  return;
415  }
416 
417  _bypassed = yn;
418  _session.set_dirty ();
419  Changed (); /* EMIT SIGNAL */
420 }
421 
422 bool
424 {
425  return _bypassed;
426 }
427 
428 /* set custom-panner config
429  *
430  * This function is intended to be only called from
431  * Route::set_custom_panner()
432  * which will trigger IO-reconfigutaion if this fn return true
433  */
434 bool
436 {
437  if (uri == _user_selected_panner_uri) return false;
439  if (uri == _current_panner_uri) return false;
440  _force_reselect = true;
441  return true;
442 }
443 
444 bool
445 PannerShell::select_panner_by_uri (std::string const uri)
446 {
447  if (uri == _user_selected_panner_uri) return false;
449  if (uri == _current_panner_uri) return false;
450  _force_reselect = true;
451  if (_panner) {
452  Glib::Threads::Mutex::Lock lx (AudioEngine::instance()->process_lock ());
453  ChanCount in = _panner->in();
454  ChanCount out = _panner->out();
455  configure_io(in, out);
456  if (!_is_send || !_panlinked) {
457  pannable()->set_panner(_panner);
458  }
459  _session.set_dirty ();
460  }
461  return true;
462 }
463 
464 void
466 {
467  assert(_is_send);
468  if (onoff == _panlinked) {
469  return;
470  }
471 
472  /* set _pannable-_has_state = true
473  * this way the panners will pick it up
474  * when it is re-created
475  */
476  if (pannable()) {
477  XMLNode state = pannable()->get_state();
478  pannable()->set_state(state, 3000);
479  }
480 
481  _panlinked = onoff;
482 
483  _force_reselect = true;
484  if (_panner) {
485  Glib::Threads::Mutex::Lock lx (AudioEngine::instance()->process_lock ());
486  ChanCount in = _panner->in();
487  ChanCount out = _panner->out();
488  configure_io(in, out);
489  if (!_panlinked) {
490  pannable()->set_panner(_panner);
491  }
492  _session.set_dirty ();
493  }
494  PannableChanged();
495 }
ARDOUR::Session & _session
LIBPBD_API Transmitter fatal
void set_bypassed(bool)
PannerInfo * select_panner(ChanCount in, ChanCount out, std::string const uri="")
bool bypassed() const
const std::string & value() const
Definition: xml++.h:159
LIBARDOUR_API gain_t speed_quietning
double transport_speed() const
Definition: session.h:590
LIBARDOUR_API uint64_t Destruction
Definition: debug.cc:38
std::string panner_uri
Definition: panner.h:193
void distribute_no_automation(BufferSet &src, BufferSet &dest, pframes_t nframes, gain_t gain_coeff)
PBD::Signal0< void > PannableChanged
Definition: panner_shell.h:67
boost::shared_ptr< Panner > _panner
Definition: panner_shell.h:92
std::string _user_selected_panner_uri
Definition: panner_shell.h:101
uint32_t pframes_t
Definition: types.h:61
uint32_t n_audio() const
Definition: chan_count.h:63
tuple f
Definition: signals.py:35
Definition: Beats.hpp:239
LIBPBD_API Transmitter error
const XMLNodeList & children(const std::string &str=std::string()) const
Definition: xml++.cc:329
float gain_t
Definition: types.h:58
std::ostream & endmsg(std::ostream &ostr)
Definition: transmitter.h:71
static AudioEngine * instance()
Definition: audioengine.h:196
AudioBuffer & get_audio(size_t i)
Definition: buffer_set.h:100
#define GAIN_COEFF_ZERO
Definition: dB.h:26
std::list< XMLNode * > XMLNodeList
Definition: xml++.h:44
#define _(Text)
Definition: i18n.h:11
void accumulate_with_gain_from(const AudioBuffer &src, framecnt_t len, gain_t gain_coeff, framecnt_t dst_offset=0, framecnt_t src_offset=0)
Definition: audio_buffer.h:113
void silence(framecnt_t nframes, framecnt_t offset)
Definition: buffer_set.cc:463
std::string name
Definition: panner.h:192
#define X_(Text)
Definition: i18n.h:13
pan_t ** pan_automation_buffer() const
Definition: session.cc:4839
XMLProperty * property(const char *)
Definition: xml++.cc:413
LIBARDOUR_API RCConfiguration * Config
Definition: globals.cc:119
int set_state(const XMLNode &, int version)
boost::shared_ptr< Pannable > _pannable_internal
Definition: panner_shell.h:94
void silence(framecnt_t len, framecnt_t offset=0)
Definition: audio_buffer.cc:83
void set_linked_to_route(bool)
uint32_t size() const
Definition: speakers.h:46
bool string_is_affirmative(const std::string &str)
Definition: convert.cc:282
audio_iterator audio_begin()
Definition: buffer_set.h:173
Definition: amp.h:29
virtual bool set_name(const std::string &str)
PanPluginDescriptor descriptor
boost::shared_ptr< Speakers > get_speakers()
Definition: session.cc:5245
std::string gui_uri
Definition: panner.h:194
#define DEBUG_TRACE(bits, str)
Definition: debug.h:55
int64_t framepos_t
Definition: types.h:66
PannerInfo * get_by_uri(std::string uri) const
audio_iterator audio_end()
Definition: buffer_set.h:174
std::string _current_panner_uri
Definition: panner_shell.h:100
PBD::Property< std::string > _name
XMLProperty * add_property(const char *name, const std::string &value)
bool select_panner_by_uri(std::string const uri)
bool set_user_selected_panner_uri(std::string const uri)
const char * name
void add_child_nocopy(XMLNode &)
Definition: xml++.cc:357
void setup_default_speakers(uint32_t nspeakers)
Definition: speakers.cc:150
boost::shared_ptr< Pannable > _pannable_route
Definition: panner_shell.h:95
PBD::Signal0< void > Changed
Definition: panner_shell.h:68
Definition: xml++.h:95
const ChanCount & count() const
Definition: buffer_set.h:87
void run(BufferSet &src, BufferSet &dest, framepos_t start_frame, framepos_t end_frames, pframes_t nframes)
The fundamental Panner function.
ARDOUR::Panner *(* factory)(boost::shared_ptr< ARDOUR::Pannable >, boost::shared_ptr< ARDOUR::Speakers >)
Definition: panner.h:198
Definition: debug.h:30
LIBARDOUR_API uint64_t Panning
Definition: debug.cc:50
void configure_io(ChanCount in, ChanCount out)
Definition: panner_shell.cc:92
std::list< PannerInfo * > panner_info
virtual ~PannerShell()
Definition: panner_shell.cc:86
boost::shared_ptr< Pannable > pannable() const
Definition: panner_shell.h:71
void merge_from(const Buffer &src, framecnt_t len, framecnt_t dst_offset=0, framecnt_t src_offset=0)
Definition: audio_buffer.h:77
std::string _panner_gui_uri
Definition: panner_shell.h:102
static PannerManager & instance()
#define GAIN_COEFF_UNITY
Definition: dB.h:28
void read_from(const Sample *src, framecnt_t len, framecnt_t dst_offset=0, framecnt_t src_offset=0)
Definition: audio_buffer.h:39
XMLNodeList::const_iterator XMLNodeConstIterator
Definition: xml++.h:49
std::string string_compose(const std::string &fmt, const T1 &o1)
Definition: compose.h:208
XMLNode & get_state()
AutoState
Definition: types.h:145