1 /*
2 * Copyright (C) 2011-2017 Paul Davis <paul@linuxaudiosystems.com>
3 * Copyright (C) 2011 Carl Hetherington <carl@carlh.net>
4 * Copyright (C) 2013 John Emmas <john@creativepost.co.uk>
5 * Copyright (C) 2014 Robin Gareus <robin@gareus.org>
6 *
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 2 of the License, or
10 * (at your option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License along
18 * with this program; if not, write to the Free Software Foundation, Inc.,
19 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
20 */
21
22 #include <cmath>
23 #include <cstdio>
24 #include <cstdlib>
25 #include <cstring>
26
27 #include <iostream>
28 #include <string>
29
30 #ifdef COMPILER_MSVC
31 #include <malloc.h>
32 #endif
33
34 #include "pbd/cartesian.h"
35 #include "pbd/compose.h"
36
37 #include "ardour/amp.h"
38 #include "ardour/audio_buffer.h"
39 #include "ardour/buffer_set.h"
40 #include "ardour/pan_controllable.h"
41 #include "ardour/pannable.h"
42 #include "ardour/speakers.h"
43
44 #include "vbap.h"
45 #include "vbap_speakers.h"
46
47 #include "pbd/i18n.h"
48
49 using namespace PBD;
50 using namespace ARDOUR;
51 using namespace std;
52
53 static PanPluginDescriptor _descriptor = {
54 "VBAP 2D panner",
55 "http://ardour.org/plugin/panner_vbap",
56 "http://ardour.org/plugin/panner_vbap#ui",
57 -1, -1,
58 10,
59 VBAPanner::factory
60 };
61
62 extern "C" ARDOURPANNER_API PanPluginDescriptor*
panner_descriptor()63 panner_descriptor ()
64 {
65 return &_descriptor;
66 }
67
Signal(VBAPanner &,uint32_t,uint32_t n_speakers)68 VBAPanner::Signal::Signal (VBAPanner&, uint32_t, uint32_t n_speakers)
69 {
70 resize_gains (n_speakers);
71
72 desired_gains[0] = desired_gains[1] = desired_gains[2] = 0;
73 outputs[0] = outputs[1] = outputs[2] = -1;
74 desired_outputs[0] = desired_outputs[1] = desired_outputs[2] = -1;
75 }
76
77 void
resize_gains(uint32_t n)78 VBAPanner::Signal::resize_gains (uint32_t n)
79 {
80 gains.assign (n, 0.0);
81 }
82
VBAPanner(boost::shared_ptr<Pannable> p,boost::shared_ptr<Speakers> s)83 VBAPanner::VBAPanner (boost::shared_ptr<Pannable> p, boost::shared_ptr<Speakers> s)
84 : Panner (p)
85 , _speakers (new VBAPSpeakers (s))
86 {
87 _pannable->pan_azimuth_control->Changed.connect_same_thread (*this, boost::bind (&VBAPanner::update, this));
88 _pannable->pan_elevation_control->Changed.connect_same_thread (*this, boost::bind (&VBAPanner::update, this));
89 _pannable->pan_width_control->Changed.connect_same_thread (*this, boost::bind (&VBAPanner::update, this));
90 if (!_pannable->has_state ()) {
91 reset ();
92 }
93
94 update ();
95 }
96
~VBAPanner()97 VBAPanner::~VBAPanner ()
98 {
99 clear_signals ();
100 }
101
102 void
clear_signals()103 VBAPanner::clear_signals ()
104 {
105 for (vector<Signal*>::iterator i = _signals.begin (); i != _signals.end (); ++i) {
106 delete *i;
107 }
108 _signals.clear ();
109 }
110
111 void
configure_io(ChanCount in,ChanCount)112 VBAPanner::configure_io (ChanCount in, ChanCount /* ignored - we use Speakers */)
113 {
114 uint32_t n = in.n_audio ();
115
116 clear_signals ();
117
118 for (uint32_t i = 0; i < n; ++i) {
119 Signal* s = new Signal (*this, i, _speakers->n_speakers ());
120 _signals.push_back (s);
121 }
122
123 update ();
124 }
125
126 void
update()127 VBAPanner::update ()
128 {
129 _can_automate_list.clear ();
130 _can_automate_list.insert (Evoral::Parameter (PanAzimuthAutomation));
131 if (_signals.size () > 1) {
132 _can_automate_list.insert (Evoral::Parameter (PanWidthAutomation));
133 }
134 if (_speakers->dimension () == 3) {
135 _can_automate_list.insert (Evoral::Parameter (PanElevationAutomation));
136 }
137
138 /* recompute signal directions based on panner azimuth and, if relevant, width (diffusion) and elevation parameters */
139 double elevation = _pannable->pan_elevation_control->get_value () * 90.0;
140
141 if (_signals.size () > 1) {
142 double w = -(_pannable->pan_width_control->get_value ());
143 double signal_direction = 1.0 - (_pannable->pan_azimuth_control->get_value () + (w / 2));
144 double grd_step_per_signal = w / (_signals.size () - 1);
145 for (vector<Signal*>::iterator s = _signals.begin (); s != _signals.end (); ++s) {
146 Signal* signal = *s;
147
148 int over = signal_direction;
149 over -= (signal_direction >= 0) ? 0 : 1;
150 signal_direction -= (double)over;
151
152 signal->direction = AngularVector (signal_direction * 360.0, elevation);
153 compute_gains (signal->desired_gains, signal->desired_outputs, signal->direction.azi, signal->direction.ele);
154 signal_direction += grd_step_per_signal;
155 }
156 } else if (_signals.size () == 1) {
157 double center = (1.0 - _pannable->pan_azimuth_control->get_value ()) * 360.0;
158
159 /* width has no role to play if there is only 1 signal: VBAP does not do "diffusion" of a single channel */
160
161 Signal* s = _signals.front ();
162 s->direction = AngularVector (center, elevation);
163 compute_gains (s->desired_gains, s->desired_outputs, s->direction.azi, s->direction.ele);
164 }
165
166 SignalPositionChanged (); /* emit */
167 }
168
169 void
compute_gains(double gains[3],int speaker_ids[3],int azi,int ele)170 VBAPanner::compute_gains (double gains[3], int speaker_ids[3], int azi, int ele)
171 {
172 /* calculates gain factors using loudspeaker setup and given direction */
173 double cartdir[3];
174 double power;
175 int i, j, k;
176 double small_g;
177 double big_sm_g, gtmp[3];
178 const int dimension = _speakers->dimension ();
179 assert (dimension == 2 || dimension == 3);
180
181 spherical_to_cartesian (azi, ele, 1.0, cartdir[0], cartdir[1], cartdir[2]);
182 big_sm_g = -100000.0;
183
184 gains[0] = gains[1] = gains[2] = 0;
185 speaker_ids[0] = speaker_ids[1] = speaker_ids[2] = 0;
186
187 for (i = 0; i < _speakers->n_tuples (); i++) {
188 small_g = 10000000.0;
189
190 for (j = 0; j < dimension; j++) {
191 gtmp[j] = 0.0;
192
193 for (k = 0; k < dimension; k++) {
194 gtmp[j] += cartdir[k] * _speakers->matrix (i)[j * dimension + k];
195 }
196
197 if (gtmp[j] < small_g) {
198 small_g = gtmp[j];
199 }
200 }
201
202 if (small_g > big_sm_g) {
203 big_sm_g = small_g;
204
205 gains[0] = gtmp[0];
206 gains[1] = gtmp[1];
207
208 speaker_ids[0] = _speakers->speaker_for_tuple (i, 0);
209 speaker_ids[1] = _speakers->speaker_for_tuple (i, 1);
210
211 if (_speakers->dimension () == 3) {
212 gains[2] = gtmp[2];
213 speaker_ids[2] = _speakers->speaker_for_tuple (i, 2);
214 } else {
215 gains[2] = 0.0;
216 speaker_ids[2] = -1;
217 }
218 }
219 }
220
221 power = sqrt (gains[0] * gains[0] + gains[1] * gains[1] + gains[2] * gains[2]);
222
223 if (power > 0) {
224 gains[0] /= power;
225 gains[1] /= power;
226 gains[2] /= power;
227 }
228 }
229
230 void
distribute(BufferSet & inbufs,BufferSet & obufs,gain_t gain_coefficient,pframes_t nframes)231 VBAPanner::distribute (BufferSet& inbufs, BufferSet& obufs, gain_t gain_coefficient, pframes_t nframes)
232 {
233 uint32_t n;
234 vector<Signal*>::iterator s;
235
236 assert (inbufs.count ().n_audio () == _signals.size ());
237
238 for (s = _signals.begin (), n = 0; s != _signals.end (); ++s, ++n) {
239 Signal* signal (*s);
240
241 distribute_one (inbufs.get_audio (n), obufs, gain_coefficient, nframes, n);
242
243 memcpy (signal->outputs, signal->desired_outputs, sizeof (signal->outputs));
244 }
245 }
246
247 void
distribute_one(AudioBuffer & srcbuf,BufferSet & obufs,gain_t gain_coefficient,pframes_t nframes,uint32_t which)248 VBAPanner::distribute_one (AudioBuffer& srcbuf, BufferSet& obufs, gain_t gain_coefficient, pframes_t nframes, uint32_t which)
249 {
250 Sample* const src = srcbuf.data ();
251 Signal* signal (_signals[which]);
252
253 /* VBAP may distribute the signal across up to 3 speakers depending on
254 * the configuration of the speakers.
255 *
256 * But the set of speakers in use "this time" may be different from
257 * the set of speakers "the last time". So we have up to 6 speakers
258 * involved, and we have to interpolate so that those no longer
259 * in use are rapidly faded to silence and those newly in use
260 * are rapidly faded to their correct level. This prevents clicks
261 * as we change the set of speakers used to put the signal in
262 * a given position.
263 *
264 * However, the speakers are represented by output buffers, and other
265 * speakers may write to the same buffers, so we cannot use
266 * anything here that will simply assign new (sample) values
267 * to the output buffers - everything must be done via mixing
268 * functions and not assignment/copying.
269 */
270
271 vector<double>::size_type sz = signal->gains.size ();
272
273 assert (sz == obufs.count ().n_audio ());
274
275 int8_t* outputs = (int8_t*)alloca (sz); // on the stack, no malloc
276
277 /* set initial state of each output "record"
278 */
279
280 for (uint32_t o = 0; o < sz; ++o) {
281 outputs[o] = 0;
282 }
283
284 /* for all outputs used this time and last time,
285 * change the output record to show what has
286 * happened.
287 */
288
289 for (int o = 0; o < 3; ++o) {
290 if (signal->outputs[o] != -1) {
291 /* used last time */
292 outputs[signal->outputs[o]] |= 1;
293 }
294
295 if (signal->desired_outputs[o] != -1) {
296 /* used this time */
297 outputs[signal->desired_outputs[o]] |= 1 << 1;
298 }
299 }
300
301 /* at this point, we can test a speaker's status:
302 *
303 * (*outputs[o] & 1) <= in use before
304 * (*outputs[o] & 2) <= in use this time
305 * (*outputs[o] & 3) == 3 <= in use both times
306 * *outputs[o] == 0 <= not in use either time
307 *
308 */
309
310 for (int o = 0; o < 3; ++o) {
311 pan_t pan;
312 int output = signal->desired_outputs[o];
313
314 if (output == -1) {
315 continue;
316 }
317
318 pan = gain_coefficient * signal->desired_gains[o];
319
320 if (pan == 0.0 && signal->gains[output] == 0.0) {
321 /* nothing deing delivered to this output */
322
323 signal->gains[output] = 0.0;
324
325 } else if (fabs (pan - signal->gains[output]) > 0.00001) {
326 /* signal to this output but the gain coefficient has changed, so
327 * interpolate between them.
328 */
329
330 AudioBuffer& buf (obufs.get_audio (output));
331 buf.accumulate_with_ramped_gain_from (srcbuf.data (), nframes, signal->gains[output], pan, 0);
332 signal->gains[output] = pan;
333
334 } else {
335 /* signal to this output, same gain as before so just copy with gain */
336
337 mix_buffers_with_gain (obufs.get_audio (output).data (), src, nframes, pan);
338 signal->gains[output] = pan;
339 }
340 }
341
342 /* clean up the outputs that were used last time but not this time */
343
344 for (uint32_t o = 0; o < sz; ++o) {
345 if (outputs[o] == 1) {
346 /* take signal and deliver with a rapid fade out */
347 AudioBuffer& buf (obufs.get_audio (o));
348 buf.accumulate_with_ramped_gain_from (srcbuf.data (), nframes, signal->gains[o], 0.0, 0);
349 signal->gains[o] = 0.0;
350 }
351 }
352
353 /* note that the output buffers were all silenced at some point
354 * so anything we didn't write to with this signal (or any others)
355 * is just as it should be.
356 */
357 }
358
359 void
distribute_one_automated(AudioBuffer &,BufferSet &,samplepos_t,samplepos_t,pframes_t,pan_t **,uint32_t)360 VBAPanner::distribute_one_automated (AudioBuffer& /*src*/, BufferSet& /*obufs*/,
361 samplepos_t /*start*/, samplepos_t /*end*/,
362 pframes_t /*nframes*/, pan_t** /*buffers*/, uint32_t /*which*/)
363 {
364 /* XXX to be implemented */
365 }
366
367 XMLNode&
get_state()368 VBAPanner::get_state ()
369 {
370 XMLNode& node (Panner::get_state ());
371 node.set_property (X_ ("uri"), _descriptor.panner_uri);
372 /* this is needed to allow new sessions to load with old Ardour: */
373 node.set_property (X_ ("type"), _descriptor.name);
374 return node;
375 }
376
377 Panner*
factory(boost::shared_ptr<Pannable> p,boost::shared_ptr<Speakers> s)378 VBAPanner::factory (boost::shared_ptr<Pannable> p, boost::shared_ptr<Speakers> s)
379 {
380 return new VBAPanner (p, s);
381 }
382
383 ChanCount
in() const384 VBAPanner::in () const
385 {
386 return ChanCount (DataType::AUDIO, _signals.size ());
387 }
388
389 ChanCount
out() const390 VBAPanner::out () const
391 {
392 return ChanCount (DataType::AUDIO, _speakers->n_speakers ());
393 }
394
395 string
value_as_string(boost::shared_ptr<const AutomationControl> ac) const396 VBAPanner::value_as_string (boost::shared_ptr<const AutomationControl> ac) const
397 {
398 double val = ac->get_value ();
399
400 switch (ac->parameter ().type ()) {
401 case PanAzimuthAutomation: /* direction */
402 return string_compose (_ ("%1\u00B0"), (int(rint (val * 360.0)) + 180) % 360);
403
404 case PanWidthAutomation: /* diffusion */
405 return string_compose (_ ("%1%%"), (int)floor (100.0 * fabs (val)));
406
407 case PanElevationAutomation: /* elevation */
408 return string_compose (_ ("%1\u00B0"), (int)floor (90.0 * fabs (val)));
409
410 default:
411 return _ ("unused");
412 }
413 }
414
415 AngularVector
signal_position(uint32_t n) const416 VBAPanner::signal_position (uint32_t n) const
417 {
418 if (n < _signals.size ()) {
419 return _signals[n]->direction;
420 }
421
422 return AngularVector ();
423 }
424
425 boost::shared_ptr<Speakers>
get_speakers() const426 VBAPanner::get_speakers () const
427 {
428 return _speakers->parent ();
429 }
430
431 void
set_position(double p)432 VBAPanner::set_position (double p)
433 {
434 /* map into 0..1 range */
435 int over = p;
436 over -= (p >= 0) ? 0 : 1;
437 p -= (double)over;
438 _pannable->pan_azimuth_control->set_value (p, Controllable::NoGroup);
439 }
440
441 void
set_width(double w)442 VBAPanner::set_width (double w)
443 {
444 _pannable->pan_width_control->set_value (min (1.0, max (-1.0, w)), Controllable::NoGroup);
445 }
446
447 void
set_elevation(double e)448 VBAPanner::set_elevation (double e)
449 {
450 _pannable->pan_elevation_control->set_value (min (1.0, max (0.0, e)), Controllable::NoGroup);
451 }
452
453 void
reset()454 VBAPanner::reset ()
455 {
456 set_position (.5);
457 if (_signals.size () > 1) {
458 set_width (1.0 - (1.0 / (double)_signals.size ()));
459 } else {
460 set_width (1.0);
461 }
462 set_elevation (0);
463
464 update ();
465 }
466