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;
127 for (vector<Signal*>::iterator s = _signals.begin(); s != _signals.end(); ++s) {
131 signal->direction = AngularVector (signal_direction, 0.0);
132 compute_gains (signal->desired_gains, signal->desired_outputs, signal->direction.azi, signal->direction.ele);
133 signal_direction += degree_step_per_signal;
136 } else if (_signals.size() == 1) {
138 /* width has no role to play if there is only 1 signal: VBAP does not do "diffusion" of a single channel */
140 Signal* s = _signals.front();
141 s->direction = AngularVector (center, 0);
142 compute_gains (s->desired_gains, s->desired_outputs, s->direction.azi, s->direction.ele);
147 VBAPanner::compute_gains (double gains[3], int speaker_ids[3], int azi, int ele)
149 /* calculates gain factors using loudspeaker setup and given direction */
154 double big_sm_g, gtmp[3];
156 azi_ele_to_cart (azi,ele, cartdir[0], cartdir[1], cartdir[2]);
157 big_sm_g = -100000.0;
159 gains[0] = gains[1] = gains[2] = 0;
160 speaker_ids[0] = speaker_ids[1] = speaker_ids[2] = 0;
162 for (i = 0; i < _speakers->n_tuples(); i++) {
164 small_g = 10000000.0;
166 for (j = 0; j < _speakers->dimension(); j++) {
170 for (k = 0; k < _speakers->dimension(); k++) {
171 gtmp[j] += cartdir[k] * _speakers->matrix(i)[j*_speakers->dimension()+k];
174 if (gtmp[j] < small_g) {
179 if (small_g > big_sm_g) {
186 speaker_ids[0] = _speakers->speaker_for_tuple (i, 0);
187 speaker_ids[1] = _speakers->speaker_for_tuple (i, 1);
189 if (_speakers->dimension() == 3) {
191 speaker_ids[2] = _speakers->speaker_for_tuple (i, 2);
199 power = sqrt (gains[0]*gains[0] + gains[1]*gains[1] + gains[2]*gains[2]);
209 VBAPanner::distribute (BufferSet& inbufs, BufferSet& obufs, gain_t gain_coefficient, pframes_t nframes)
212 vector<Signal*>::iterator s;
214 assert (inbufs.count().n_audio() == _signals.size());
216 for (s = _signals.begin(), n = 0; s != _signals.end(); ++s, ++n) {
220 distribute_one (inbufs.get_audio (n), obufs, gain_coefficient, nframes, n);
222 memcpy (signal->outputs, signal->desired_outputs, sizeof (signal->outputs));
227 VBAPanner::distribute_one (AudioBuffer& srcbuf, BufferSet& obufs, gain_t gain_coefficient, pframes_t nframes, uint32_t which)
229 Sample* const src = srcbuf.data();
230 Signal* signal (_signals[which]);
232 /* VBAP may distribute the signal across up to 3 speakers depending on
233 the configuration of the speakers.
235 But the set of speakers in use "this time" may be different from
236 the set of speakers "the last time". So we have up to 6 speakers
237 involved, and we have to interpolate so that those no longer
238 in use are rapidly faded to silence and those newly in use
239 are rapidly faded to their correct level. This prevents clicks
240 as we change the set of speakers used to put the signal in
243 However, the speakers are represented by output buffers, and other
244 speakers may write to the same buffers, so we cannot use
245 anything here that will simply assign new (sample) values
246 to the output buffers - everything must be done via mixing
247 functions and not assignment/copying.
250 vector<double>::size_type sz = signal->gains.size();
252 assert (sz == obufs.count().n_audio());
254 int8_t outputs[sz]; // on the stack, no malloc
256 /* set initial state of each output "record"
259 for (uint32_t o = 0; o < sz; ++o) {
263 /* for all outputs used this time and last time,
264 change the output record to show what has
269 for (int o = 0; o < 3; ++o) {
270 if (signal->outputs[o] != -1) {
272 outputs[signal->outputs[o]] |= 1;
275 if (signal->desired_outputs[o] != -1) {
277 outputs[signal->desired_outputs[o]] |= 1<<1;
281 /* at this point, we can test a speaker's status:
283 (outputs[o] & 1) <= in use before
284 (outputs[o] & 2) <= in use this time
285 (outputs[o] & 3) == 3 <= in use both times
286 outputs[o] == 0 <= not in use either time
290 for (int o = 0; o < 3; ++o) {
292 int output = signal->desired_outputs[o];
298 pan = gain_coefficient * signal->desired_gains[o];
300 if (pan == 0.0 && signal->gains[output] == 0.0) {
302 /* nothing deing delivered to this output */
304 signal->gains[output] = 0.0;
306 } else if (fabs (pan - signal->gains[output]) > 0.00001) {
308 /* signal to this output but the gain coefficient has changed, so
309 interpolate between them.
312 AudioBuffer& buf (obufs.get_audio (output));
313 buf.accumulate_with_ramped_gain_from (srcbuf.data(), nframes, signal->gains[output], pan, 0);
314 signal->gains[output] = pan;
318 /* signal to this output, same gain as before so just copy with gain
321 mix_buffers_with_gain (obufs.get_audio (output).data(),src,nframes,pan);
322 signal->gains[output] = pan;
326 /* clean up the outputs that were used last time but not this time
329 for (uint32_t o = 0; o < sz; ++o) {
330 if (outputs[o] == 1) {
331 /* take signal and deliver with a rapid fade out
333 AudioBuffer& buf (obufs.get_audio (o));
334 buf.accumulate_with_ramped_gain_from (srcbuf.data(), nframes, signal->gains[o], 0.0, 0);
335 signal->gains[o] = 0.0;
339 /* note that the output buffers were all silenced at some point
340 so anything we didn't write to with this signal (or any others)
341 is just as it should be.
346 VBAPanner::distribute_one_automated (AudioBuffer& src, BufferSet& obufs,
347 framepos_t start, framepos_t end, pframes_t nframes, pan_t** buffers, uint32_t which)
352 VBAPanner::get_state ()
358 VBAPanner::state (bool full_state)
360 XMLNode& node (Panner::get_state());
361 node.add_property (X_("type"), _descriptor.name);
366 VBAPanner::set_state (const XMLNode& node, int /*version*/)
372 VBAPanner::factory (boost::shared_ptr<Pannable> p, boost::shared_ptr<Speakers> s)
374 return new VBAPanner (p, s);
378 VBAPanner::in() const
380 return ChanCount (DataType::AUDIO, _signals.size());
384 VBAPanner::out() const
386 return ChanCount (DataType::AUDIO, _speakers->n_speakers());
389 std::set<Evoral::Parameter>
390 VBAPanner::what_can_be_automated() const
392 set<Evoral::Parameter> s;
393 s.insert (Evoral::Parameter (PanAzimuthAutomation));
394 if (_signals.size() > 1) {
395 s.insert (Evoral::Parameter (PanWidthAutomation));
401 VBAPanner::describe_parameter (Evoral::Parameter p)
404 case PanAzimuthAutomation:
405 return _("Direction");
406 case PanWidthAutomation:
407 return _("Diffusion");
409 return _pannable->describe_parameter (p);
414 VBAPanner::value_as_string (boost::shared_ptr<AutomationControl> ac) const
416 /* DO NOT USE LocaleGuard HERE */
417 double val = ac->get_value();
419 switch (ac->parameter().type()) {
420 case PanAzimuthAutomation: /* direction */
421 return string_compose (_("%1"), int (rint (val * 360.0)));
423 case PanWidthAutomation: /* diffusion */
424 return string_compose (_("%1%%"), (int) floor (100.0 * fabs(val)));
427 return _pannable->value_as_string (ac);
432 VBAPanner::signal_position (uint32_t n) const
434 if (n < _signals.size()) {
435 return _signals[n]->direction;
438 return AngularVector();
441 boost::shared_ptr<Speakers>
442 VBAPanner::get_speakers () const
444 return _speakers->parent();
448 VBAPanner::set_position (double p)
458 _pannable->pan_azimuth_control->set_value (p);
462 VBAPanner::set_width (double w)
464 _pannable->pan_width_control->set_value (min (1.0, max (-1.0, w)));