9 #include "pbd/cartesian.h"
11 #include "ardour/amp.h"
12 #include "ardour/audio_buffer.h"
13 #include "ardour/buffer_set.h"
14 #include "ardour/pan_controllable.h"
15 #include "ardour/pannable.h"
16 #include "ardour/speakers.h"
19 #include "vbap_speakers.h"
22 using namespace ARDOUR;
25 static PanPluginDescriptor _descriptor = {
31 extern "C" { PanPluginDescriptor* panner_descriptor () { return &_descriptor; } }
33 VBAPanner::Signal::Signal (Session& session, VBAPanner& p, uint32_t n, uint32_t n_speakers)
35 resize_gains (n_speakers);
37 desired_gains[0] = desired_gains[1] = desired_gains[2] = 0;
38 outputs[0] = outputs[1] = outputs[2] = -1;
39 desired_outputs[0] = desired_outputs[1] = desired_outputs[2] = -1;
43 VBAPanner::Signal::Signal::resize_gains (uint32_t n)
45 gains.assign (n, 0.0);
48 VBAPanner::VBAPanner (boost::shared_ptr<Pannable> p, boost::shared_ptr<Speakers> s)
50 , _speakers (new VBAPSpeakers (s))
52 _pannable->pan_azimuth_control->Changed.connect_same_thread (*this, boost::bind (&VBAPanner::update, this));
53 _pannable->pan_width_control->Changed.connect_same_thread (*this, boost::bind (&VBAPanner::update, this));
58 VBAPanner::~VBAPanner ()
64 VBAPanner::clear_signals ()
66 for (vector<Signal*>::iterator i = _signals.begin(); i != _signals.end(); ++i) {
73 VBAPanner::configure_io (ChanCount in, ChanCount /* ignored - we use Speakers */)
75 uint32_t n = in.n_audio();
79 for (uint32_t i = 0; i < n; ++i) {
80 Signal* s = new Signal (_pannable->session(), *this, i, _speakers->n_speakers());
81 _signals.push_back (s);
91 /* recompute signal directions based on panner azimuth and, if relevant, width (diffusion) parameters)
94 /* panner azimuth control is [0 .. 1.0] which we interpret as [0 .. 360] degrees
96 double center = _pannable->pan_azimuth_control->get_value() * 360.0;
98 if (_signals.size() > 1) {
100 /* panner width control is [-1.0 .. 1.0]; we ignore sign, and map to [0 .. 360] degrees
101 so that a width of 1 corresponds to a signal equally present from all directions,
102 and a width of zero corresponds to a point source from the "center" (above) point
103 on the perimeter of the speaker array.
106 double w = fabs (_pannable->pan_width_control->get_value()) * 360.0;
108 double min_dir = center - w;
110 min_dir = 360.0 + min_dir; // its already negative
112 min_dir = max (min (min_dir, 360.0), 0.0);
114 double max_dir = center + w;
115 if (max_dir > 360.0) {
116 max_dir = max_dir - 360.0;
118 max_dir = max (min (max_dir, 360.0), 0.0);
120 if (max_dir < min_dir) {
121 swap (max_dir, min_dir);
124 double degree_step_per_signal = (max_dir - min_dir) / (_signals.size() - 1);
125 double signal_direction = min_dir;
128 for (vector<Signal*>::iterator s = _signals.begin(); s != _signals.end(); ++s) {
132 signal->direction = AngularVector (signal_direction, 0.0);
133 compute_gains (signal->desired_gains, signal->desired_outputs, signal->direction.azi, signal->direction.ele);
134 signal_direction += degree_step_per_signal;
137 for (vector<Signal*>::reverse_iterator s = _signals.rbegin(); s != _signals.rend(); ++s) {
141 signal->direction = AngularVector (signal_direction, 0.0);
142 compute_gains (signal->desired_gains, signal->desired_outputs, signal->direction.azi, signal->direction.ele);
143 signal_direction += degree_step_per_signal;
147 } else if (_signals.size() == 1) {
149 /* width has no role to play if there is only 1 signal: VBAP does not do "diffusion" of a single channel */
151 Signal* s = _signals.front();
152 s->direction = AngularVector (center, 0);
153 compute_gains (s->desired_gains, s->desired_outputs, s->direction.azi, s->direction.ele);
158 VBAPanner::compute_gains (double gains[3], int speaker_ids[3], int azi, int ele)
160 /* calculates gain factors using loudspeaker setup and given direction */
165 double big_sm_g, gtmp[3];
167 spherical_to_cartesian (azi, ele, 1.0, cartdir[0], cartdir[1], cartdir[2]);
168 big_sm_g = -100000.0;
170 gains[0] = gains[1] = gains[2] = 0;
171 speaker_ids[0] = speaker_ids[1] = speaker_ids[2] = 0;
173 for (i = 0; i < _speakers->n_tuples(); i++) {
175 small_g = 10000000.0;
177 for (j = 0; j < _speakers->dimension(); j++) {
181 for (k = 0; k < _speakers->dimension(); k++) {
182 gtmp[j] += cartdir[k] * _speakers->matrix(i)[j*_speakers->dimension()+k];
185 if (gtmp[j] < small_g) {
190 if (small_g > big_sm_g) {
197 speaker_ids[0] = _speakers->speaker_for_tuple (i, 0);
198 speaker_ids[1] = _speakers->speaker_for_tuple (i, 1);
200 if (_speakers->dimension() == 3) {
202 speaker_ids[2] = _speakers->speaker_for_tuple (i, 2);
210 power = sqrt (gains[0]*gains[0] + gains[1]*gains[1] + gains[2]*gains[2]);
220 VBAPanner::distribute (BufferSet& inbufs, BufferSet& obufs, gain_t gain_coefficient, pframes_t nframes)
223 vector<Signal*>::iterator s;
225 assert (inbufs.count().n_audio() == _signals.size());
227 for (s = _signals.begin(), n = 0; s != _signals.end(); ++s, ++n) {
231 distribute_one (inbufs.get_audio (n), obufs, gain_coefficient, nframes, n);
233 memcpy (signal->outputs, signal->desired_outputs, sizeof (signal->outputs));
238 VBAPanner::distribute_one (AudioBuffer& srcbuf, BufferSet& obufs, gain_t gain_coefficient, pframes_t nframes, uint32_t which)
240 Sample* const src = srcbuf.data();
241 Signal* signal (_signals[which]);
243 /* VBAP may distribute the signal across up to 3 speakers depending on
244 the configuration of the speakers.
246 But the set of speakers in use "this time" may be different from
247 the set of speakers "the last time". So we have up to 6 speakers
248 involved, and we have to interpolate so that those no longer
249 in use are rapidly faded to silence and those newly in use
250 are rapidly faded to their correct level. This prevents clicks
251 as we change the set of speakers used to put the signal in
254 However, the speakers are represented by output buffers, and other
255 speakers may write to the same buffers, so we cannot use
256 anything here that will simply assign new (sample) values
257 to the output buffers - everything must be done via mixing
258 functions and not assignment/copying.
261 vector<double>::size_type sz = signal->gains.size();
263 assert (sz == obufs.count().n_audio());
265 int8_t outputs[sz]; // on the stack, no malloc
267 /* set initial state of each output "record"
270 for (uint32_t o = 0; o < sz; ++o) {
274 /* for all outputs used this time and last time,
275 change the output record to show what has
280 for (int o = 0; o < 3; ++o) {
281 if (signal->outputs[o] != -1) {
283 outputs[signal->outputs[o]] |= 1;
286 if (signal->desired_outputs[o] != -1) {
288 outputs[signal->desired_outputs[o]] |= 1<<1;
292 /* at this point, we can test a speaker's status:
294 (outputs[o] & 1) <= in use before
295 (outputs[o] & 2) <= in use this time
296 (outputs[o] & 3) == 3 <= in use both times
297 outputs[o] == 0 <= not in use either time
301 for (int o = 0; o < 3; ++o) {
303 int output = signal->desired_outputs[o];
309 pan = gain_coefficient * signal->desired_gains[o];
311 if (pan == 0.0 && signal->gains[output] == 0.0) {
313 /* nothing deing delivered to this output */
315 signal->gains[output] = 0.0;
317 } else if (fabs (pan - signal->gains[output]) > 0.00001) {
319 /* signal to this output but the gain coefficient has changed, so
320 interpolate between them.
323 AudioBuffer& buf (obufs.get_audio (output));
324 buf.accumulate_with_ramped_gain_from (srcbuf.data(), nframes, signal->gains[output], pan, 0);
325 signal->gains[output] = pan;
329 /* signal to this output, same gain as before so just copy with gain
332 mix_buffers_with_gain (obufs.get_audio (output).data(),src,nframes,pan);
333 signal->gains[output] = pan;
337 /* clean up the outputs that were used last time but not this time
340 for (uint32_t o = 0; o < sz; ++o) {
341 if (outputs[o] == 1) {
342 /* take signal and deliver with a rapid fade out
344 AudioBuffer& buf (obufs.get_audio (o));
345 buf.accumulate_with_ramped_gain_from (srcbuf.data(), nframes, signal->gains[o], 0.0, 0);
346 signal->gains[o] = 0.0;
350 /* note that the output buffers were all silenced at some point
351 so anything we didn't write to with this signal (or any others)
352 is just as it should be.
357 VBAPanner::distribute_one_automated (AudioBuffer& src, BufferSet& obufs,
358 framepos_t start, framepos_t end, pframes_t nframes, pan_t** buffers, uint32_t which)
363 VBAPanner::get_state ()
369 VBAPanner::state (bool full_state)
371 XMLNode& node (Panner::get_state());
372 node.add_property (X_("type"), _descriptor.name);
377 VBAPanner::set_state (const XMLNode& node, int /*version*/)
383 VBAPanner::factory (boost::shared_ptr<Pannable> p, boost::shared_ptr<Speakers> s)
385 return new VBAPanner (p, s);
389 VBAPanner::in() const
391 return ChanCount (DataType::AUDIO, _signals.size());
395 VBAPanner::out() const
397 return ChanCount (DataType::AUDIO, _speakers->n_speakers());
400 std::set<Evoral::Parameter>
401 VBAPanner::what_can_be_automated() const
403 set<Evoral::Parameter> s;
404 s.insert (Evoral::Parameter (PanAzimuthAutomation));
405 if (_signals.size() > 1) {
406 s.insert (Evoral::Parameter (PanWidthAutomation));
412 VBAPanner::describe_parameter (Evoral::Parameter p)
415 case PanAzimuthAutomation:
416 return _("Direction");
417 case PanWidthAutomation:
418 return _("Diffusion");
420 return _pannable->describe_parameter (p);
425 VBAPanner::value_as_string (boost::shared_ptr<AutomationControl> ac) const
427 /* DO NOT USE LocaleGuard HERE */
428 double val = ac->get_value();
430 switch (ac->parameter().type()) {
431 case PanAzimuthAutomation: /* direction */
432 return string_compose (_("%1"), int (rint (val * 360.0)));
434 case PanWidthAutomation: /* diffusion */
435 return string_compose (_("%1%%"), (int) floor (100.0 * fabs(val)));
438 return _pannable->value_as_string (ac);
443 VBAPanner::signal_position (uint32_t n) const
445 if (n < _signals.size()) {
446 return _signals[n]->direction;
449 return AngularVector();
452 boost::shared_ptr<Speakers>
453 VBAPanner::get_speakers () const
455 return _speakers->parent();
459 VBAPanner::set_position (double p)
469 _pannable->pan_azimuth_control->set_value (p);
473 VBAPanner::set_width (double w)
475 _pannable->pan_width_control->set_value (min (1.0, max (-1.0, w)));