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)
105 double w = fabs (_pannable->pan_width_control->get_value()) * 360.0;
107 double min_dir = center - w;
109 min_dir = 360.0 + min_dir; // its already negative
111 min_dir = max (min (min_dir, 360.0), 0.0);
113 double max_dir = center + w;
114 if (max_dir > 360.0) {
115 max_dir = max_dir - 360.0;
117 max_dir = max (min (max_dir, 360.0), 0.0);
119 if (max_dir < min_dir) {
120 swap (max_dir, min_dir);
123 double degree_step_per_signal = (max_dir - min_dir) / (_signals.size() - 1);
124 double signal_direction = min_dir;
126 for (vector<Signal*>::iterator s = _signals.begin(); s != _signals.end(); ++s) {
130 signal->direction = AngularVector (signal_direction, 0.0);
131 compute_gains (signal->desired_gains, signal->desired_outputs, signal->direction.azi, signal->direction.ele);
132 signal_direction += degree_step_per_signal;
135 } else if (_signals.size() == 1) {
137 /* width has no role to play if there is only 1 signal: VBAP does not do "diffusion" of a single channel */
139 Signal* s = _signals.front();
140 s->direction = AngularVector (center, 0);
141 compute_gains (s->desired_gains, s->desired_outputs, s->direction.azi, s->direction.ele);
146 VBAPanner::compute_gains (double gains[3], int speaker_ids[3], int azi, int ele)
148 /* calculates gain factors using loudspeaker setup and given direction */
153 double big_sm_g, gtmp[3];
155 spherical_to_cartesian (azi, ele, 1.0, cartdir[0], cartdir[1], cartdir[2]);
156 big_sm_g = -100000.0;
158 gains[0] = gains[1] = gains[2] = 0;
159 speaker_ids[0] = speaker_ids[1] = speaker_ids[2] = 0;
161 for (i = 0; i < _speakers->n_tuples(); i++) {
163 small_g = 10000000.0;
165 for (j = 0; j < _speakers->dimension(); j++) {
169 for (k = 0; k < _speakers->dimension(); k++) {
170 gtmp[j] += cartdir[k] * _speakers->matrix(i)[j*_speakers->dimension()+k];
173 if (gtmp[j] < small_g) {
178 if (small_g > big_sm_g) {
185 speaker_ids[0] = _speakers->speaker_for_tuple (i, 0);
186 speaker_ids[1] = _speakers->speaker_for_tuple (i, 1);
188 if (_speakers->dimension() == 3) {
190 speaker_ids[2] = _speakers->speaker_for_tuple (i, 2);
198 power = sqrt (gains[0]*gains[0] + gains[1]*gains[1] + gains[2]*gains[2]);
208 VBAPanner::distribute (BufferSet& inbufs, BufferSet& obufs, gain_t gain_coefficient, pframes_t nframes)
211 vector<Signal*>::iterator s;
213 assert (inbufs.count().n_audio() == _signals.size());
215 for (s = _signals.begin(), n = 0; s != _signals.end(); ++s, ++n) {
219 distribute_one (inbufs.get_audio (n), obufs, gain_coefficient, nframes, n);
221 memcpy (signal->outputs, signal->desired_outputs, sizeof (signal->outputs));
226 VBAPanner::distribute_one (AudioBuffer& srcbuf, BufferSet& obufs, gain_t gain_coefficient, pframes_t nframes, uint32_t which)
228 Sample* const src = srcbuf.data();
229 Signal* signal (_signals[which]);
231 /* VBAP may distribute the signal across up to 3 speakers depending on
232 the configuration of the speakers.
234 But the set of speakers in use "this time" may be different from
235 the set of speakers "the last time". So we have up to 6 speakers
236 involved, and we have to interpolate so that those no longer
237 in use are rapidly faded to silence and those newly in use
238 are rapidly faded to their correct level. This prevents clicks
239 as we change the set of speakers used to put the signal in
242 However, the speakers are represented by output buffers, and other
243 speakers may write to the same buffers, so we cannot use
244 anything here that will simply assign new (sample) values
245 to the output buffers - everything must be done via mixing
246 functions and not assignment/copying.
249 vector<double>::size_type sz = signal->gains.size();
251 assert (sz == obufs.count().n_audio());
253 int8_t outputs[sz]; // on the stack, no malloc
255 /* set initial state of each output "record"
258 for (uint32_t o = 0; o < sz; ++o) {
262 /* for all outputs used this time and last time,
263 change the output record to show what has
268 for (int o = 0; o < 3; ++o) {
269 if (signal->outputs[o] != -1) {
271 outputs[signal->outputs[o]] |= 1;
274 if (signal->desired_outputs[o] != -1) {
276 outputs[signal->desired_outputs[o]] |= 1<<1;
280 /* at this point, we can test a speaker's status:
282 (outputs[o] & 1) <= in use before
283 (outputs[o] & 2) <= in use this time
284 (outputs[o] & 3) == 3 <= in use both times
285 outputs[o] == 0 <= not in use either time
289 for (int o = 0; o < 3; ++o) {
291 int output = signal->desired_outputs[o];
297 pan = gain_coefficient * signal->desired_gains[o];
299 if (pan == 0.0 && signal->gains[output] == 0.0) {
301 /* nothing deing delivered to this output */
303 signal->gains[output] = 0.0;
305 } else if (fabs (pan - signal->gains[output]) > 0.00001) {
307 /* signal to this output but the gain coefficient has changed, so
308 interpolate between them.
311 AudioBuffer& buf (obufs.get_audio (output));
312 buf.accumulate_with_ramped_gain_from (srcbuf.data(), nframes, signal->gains[output], pan, 0);
313 signal->gains[output] = pan;
317 /* signal to this output, same gain as before so just copy with gain
320 mix_buffers_with_gain (obufs.get_audio (output).data(),src,nframes,pan);
321 signal->gains[output] = pan;
325 /* clean up the outputs that were used last time but not this time
328 for (uint32_t o = 0; o < sz; ++o) {
329 if (outputs[o] == 1) {
330 /* take signal and deliver with a rapid fade out
332 AudioBuffer& buf (obufs.get_audio (o));
333 buf.accumulate_with_ramped_gain_from (srcbuf.data(), nframes, signal->gains[o], 0.0, 0);
334 signal->gains[o] = 0.0;
338 /* note that the output buffers were all silenced at some point
339 so anything we didn't write to with this signal (or any others)
340 is just as it should be.
345 VBAPanner::distribute_one_automated (AudioBuffer& src, BufferSet& obufs,
346 framepos_t start, framepos_t end, pframes_t nframes, pan_t** buffers, uint32_t which)
351 VBAPanner::get_state ()
357 VBAPanner::state (bool full_state)
359 XMLNode& node (Panner::get_state());
360 node.add_property (X_("type"), _descriptor.name);
365 VBAPanner::set_state (const XMLNode& node, int /*version*/)
371 VBAPanner::factory (boost::shared_ptr<Pannable> p, boost::shared_ptr<Speakers> s)
373 return new VBAPanner (p, s);
377 VBAPanner::in() const
379 return ChanCount (DataType::AUDIO, _signals.size());
383 VBAPanner::out() const
385 return ChanCount (DataType::AUDIO, _speakers->n_speakers());
388 std::set<Evoral::Parameter>
389 VBAPanner::what_can_be_automated() const
391 set<Evoral::Parameter> s;
392 s.insert (Evoral::Parameter (PanAzimuthAutomation));
393 if (_signals.size() > 1) {
394 s.insert (Evoral::Parameter (PanWidthAutomation));
400 VBAPanner::describe_parameter (Evoral::Parameter p)
403 case PanAzimuthAutomation:
404 return _("Direction");
405 case PanWidthAutomation:
406 return _("Diffusion");
408 return _pannable->describe_parameter (p);
413 VBAPanner::value_as_string (boost::shared_ptr<AutomationControl> ac) const
415 /* DO NOT USE LocaleGuard HERE */
416 double val = ac->get_value();
418 switch (ac->parameter().type()) {
419 case PanAzimuthAutomation: /* direction */
420 return string_compose (_("%1"), int (rint (val * 360.0)));
422 case PanWidthAutomation: /* diffusion */
423 return string_compose (_("%1%%"), (int) floor (100.0 * fabs(val)));
426 return _pannable->value_as_string (ac);
431 VBAPanner::signal_position (uint32_t n) const
433 if (n < _signals.size()) {
434 return _signals[n]->direction;
437 return AngularVector();
440 boost::shared_ptr<Speakers>
441 VBAPanner::get_speakers () const
443 return _speakers->parent();
447 VBAPanner::set_position (double p)
457 _pannable->pan_azimuth_control->set_value (p);
461 VBAPanner::set_width (double w)
463 _pannable->pan_width_control->set_value (min (1.0, max (-1.0, w)));