99a83d4106da01839316eda49ed7aefe39d0fa57
[ardour.git] / libs / panners / vbap / vbap.cc
1 #include <cmath>
2 #include <cstdlib>
3 #include <cstdio>
4 #include <cstring>
5
6 #include <iostream>
7 #include <string>
8
9 #include "pbd/cartesian.h"
10
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"
17
18 #include "vbap.h"
19 #include "vbap_speakers.h"
20
21 using namespace PBD;
22 using namespace ARDOUR;
23 using namespace std;
24
25 static PanPluginDescriptor _descriptor = {
26         "VBAP 2D panner",
27         -1, -1,
28         VBAPanner::factory
29 };
30
31 extern "C" { PanPluginDescriptor* panner_descriptor () { return &_descriptor; } }
32
33 VBAPanner::Signal::Signal (Session& session, VBAPanner& p, uint32_t n, uint32_t n_speakers)
34 {
35         resize_gains (n_speakers);
36
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;
40 }
41
42 void
43 VBAPanner::Signal::Signal::resize_gains (uint32_t n)
44 {
45         gains.assign (n, 0.0);
46 }        
47
48 VBAPanner::VBAPanner (boost::shared_ptr<Pannable> p, boost::shared_ptr<Speakers> s)
49         : Panner (p)
50         , _speakers (new VBAPSpeakers (s))
51 {
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));
54
55         update ();
56 }
57
58 VBAPanner::~VBAPanner ()
59 {
60         clear_signals ();
61 }
62
63 void
64 VBAPanner::clear_signals ()
65 {
66         for (vector<Signal*>::iterator i = _signals.begin(); i != _signals.end(); ++i) {
67                 delete *i;
68         }
69         _signals.clear ();
70 }
71
72 void
73 VBAPanner::configure_io (ChanCount in, ChanCount /* ignored - we use Speakers */)
74 {
75         uint32_t n = in.n_audio();
76
77         clear_signals ();
78
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);
82                 
83         }
84
85         update ();
86 }
87
88 void
89 VBAPanner::update ()
90 {
91         /* recompute signal directions based on panner azimuth and, if relevant, width (diffusion) parameters)
92          */
93
94         /* panner azimuth control is [0 .. 1.0] which we interpret as [0 .. 360] degrees
95          */
96         double center = _pannable->pan_azimuth_control->get_value() * 360.0;
97
98         if (_signals.size() > 1) {
99
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)
103                 */
104
105                 double w = fabs (_pannable->pan_width_control->get_value()) * 360.0;
106                 
107                 double min_dir = center - w;
108                 min_dir = max (min (min_dir, 360.0), 0.0);
109                 
110                 double max_dir = center + w;
111                 max_dir = max (min (max_dir, 360.0), 0.0);
112                 
113                 double degree_step_per_signal = (max_dir - min_dir) / _signals.size();
114                 double signal_direction = min_dir;
115                 
116                 for (vector<Signal*>::iterator s = _signals.begin(); s != _signals.end(); ++s) {
117                         
118                         Signal* signal = *s;
119                         
120                         signal->direction = AngularVector (signal_direction, 0.0);
121                         compute_gains (signal->desired_gains, signal->desired_outputs, signal->direction.azi, signal->direction.ele);
122                         signal_direction += degree_step_per_signal;
123                 }
124
125         } else if (_signals.size() == 1) {
126
127                 /* width has no role to play if there is only 1 signal: VBAP does not do "diffusion" of a single channel */
128
129                 Signal* s = _signals.front();
130                 s->direction = AngularVector (center, 0);
131                 compute_gains (s->desired_gains, s->desired_outputs, s->direction.azi, s->direction.ele);
132         }
133 }
134
135 void 
136 VBAPanner::compute_gains (double gains[3], int speaker_ids[3], int azi, int ele) 
137 {
138         /* calculates gain factors using loudspeaker setup and given direction */
139         double cartdir[3];
140         double power;
141         int i,j,k;
142         double small_g;
143         double big_sm_g, gtmp[3];
144
145         azi_ele_to_cart (azi,ele, cartdir[0], cartdir[1], cartdir[2]);  
146         big_sm_g = -100000.0;
147
148         gains[0] = gains[1] = gains[2] = 0;
149         speaker_ids[0] = speaker_ids[1] = speaker_ids[2] = 0;
150
151         for (i = 0; i < _speakers->n_tuples(); i++) {
152
153                 small_g = 10000000.0;
154
155                 for (j = 0; j < _speakers->dimension(); j++) {
156
157                         gtmp[j] = 0.0;
158
159                         for (k = 0; k < _speakers->dimension(); k++) {
160                                 gtmp[j] += cartdir[k] * _speakers->matrix(i)[j*_speakers->dimension()+k]; 
161                         }
162
163                         if (gtmp[j] < small_g) {
164                                 small_g = gtmp[j];
165                         }
166                 }
167
168                 if (small_g > big_sm_g) {
169
170                         big_sm_g = small_g;
171
172                         gains[0] = gtmp[0]; 
173                         gains[1] = gtmp[1]; 
174
175                         speaker_ids[0] = _speakers->speaker_for_tuple (i, 0);
176                         speaker_ids[1] = _speakers->speaker_for_tuple (i, 1);
177
178                         if (_speakers->dimension() == 3) {
179                                 gains[2] = gtmp[2];
180                                 speaker_ids[2] = _speakers->speaker_for_tuple (i, 2);
181                         } else {
182                                 gains[2] = 0.0;
183                                 speaker_ids[2] = -1;
184                         }
185                 }
186         }
187         
188         power = sqrt (gains[0]*gains[0] + gains[1]*gains[1] + gains[2]*gains[2]);
189
190         if (power > 0) {
191                 gains[0] /= power; 
192                 gains[1] /= power;
193                 gains[2] /= power;
194         }
195 }
196
197 void
198 VBAPanner::distribute (BufferSet& inbufs, BufferSet& obufs, gain_t gain_coefficient, pframes_t nframes)
199 {
200         uint32_t n;
201         vector<Signal*>::iterator s;
202
203         assert (inbufs.count().n_audio() == _signals.size());
204
205         for (s = _signals.begin(), n = 0; s != _signals.end(); ++s, ++n) {
206
207                 Signal* signal (*s);
208
209                 distribute_one (inbufs.get_audio (n), obufs, gain_coefficient, nframes, n);
210
211                 memcpy (signal->outputs, signal->desired_outputs, sizeof (signal->outputs));
212         }
213 }
214
215 void
216 VBAPanner::distribute_one (AudioBuffer& srcbuf, BufferSet& obufs, gain_t gain_coefficient, pframes_t nframes, uint32_t which)
217 {
218         Sample* const src = srcbuf.data();
219         Signal* signal (_signals[which]);
220
221         /* VBAP may distribute the signal across up to 3 speakers depending on
222            the configuration of the speakers.
223
224            But the set of speakers in use "this time" may be different from
225            the set of speakers "the last time". So we have up to 6 speakers
226            involved, and we have to interpolate so that those no longer
227            in use are rapidly faded to silence and those newly in use
228            are rapidly faded to their correct level. This prevents clicks
229            as we change the set of speakers used to put the signal in
230            a given position.
231
232            However, the speakers are represented by output buffers, and other
233            speakers may write to the same buffers, so we cannot use
234            anything here that will simply assign new (sample) values
235            to the output buffers - everything must be done via mixing
236            functions and not assignment/copying.
237         */
238
239         vector<double>::size_type sz = signal->gains.size();
240
241         assert (sz == obufs.count().n_audio());
242
243         int8_t outputs[sz]; // on the stack, no malloc
244         
245         /* set initial state of each output "record"
246          */
247
248         for (uint32_t o = 0; o < sz; ++o) {
249                 outputs[o] = 0;
250         }
251
252         /* for all outputs used this time and last time,
253            change the output record to show what has
254            happened.
255         */
256
257
258         for (int o = 0; o < 3; ++o) {
259                 if (signal->outputs[o] != -1) {
260                         /* used last time */
261                         outputs[signal->outputs[o]] |= 1;
262                 } 
263
264                 if (signal->desired_outputs[o] != -1) {
265                         /* used this time */
266                         outputs[signal->desired_outputs[o]] |= 1<<1;
267                 } 
268         }
269
270         /* at this point, we can test a speaker's status:
271
272            (outputs[o] & 1)      <= in use before
273            (outputs[o] & 2)      <= in use this time
274            (outputs[o] & 3) == 3 <= in use both times
275             outputs[o] == 0      <= not in use either time
276            
277         */
278
279         for (int o = 0; o < 3; ++o) {
280                 pan_t pan;
281                 int output = signal->desired_outputs[o];
282
283                 if (output == -1) {
284                         continue;
285                 }
286
287                 pan = gain_coefficient * signal->desired_gains[o];
288
289                 if (pan == 0.0 && signal->gains[output] == 0.0) {
290                         
291                         /* nothing deing delivered to this output */
292
293                         signal->gains[output] = 0.0;
294                         
295                 } else if (fabs (pan - signal->gains[output]) > 0.00001) {
296                         
297                         /* signal to this output but the gain coefficient has changed, so 
298                            interpolate between them.
299                         */
300
301                         AudioBuffer& buf (obufs.get_audio (output));
302                         buf.accumulate_with_ramped_gain_from (srcbuf.data(), nframes, signal->gains[output], pan, 0);
303                         signal->gains[output] = pan;
304
305                 } else {
306                         
307                         /* signal to this output, same gain as before so just copy with gain
308                          */
309                            
310                         mix_buffers_with_gain (obufs.get_audio (output).data(),src,nframes,pan);
311                         signal->gains[output] = pan;
312                 }
313         }
314
315         /* clean up the outputs that were used last time but not this time
316          */
317
318         for (uint32_t o = 0; o < sz; ++o) {
319                 if (outputs[o] == 1) {
320                         /* take signal and deliver with a rapid fade out
321                          */
322                         AudioBuffer& buf (obufs.get_audio (o));
323                         buf.accumulate_with_ramped_gain_from (srcbuf.data(), nframes, signal->gains[o], 0.0, 0);
324                         signal->gains[o] = 0.0;
325                 }
326         }
327
328         /* note that the output buffers were all silenced at some point
329            so anything we didn't write to with this signal (or any others)
330            is just as it should be.
331         */
332 }
333
334 void 
335 VBAPanner::distribute_one_automated (AudioBuffer& src, BufferSet& obufs,
336                                      framepos_t start, framepos_t end, pframes_t nframes, pan_t** buffers, uint32_t which)
337 {
338 }
339
340 XMLNode&
341 VBAPanner::get_state ()
342 {
343         return state (true);
344 }
345
346 XMLNode&
347 VBAPanner::state (bool full_state)
348 {
349         XMLNode& node (Panner::get_state());
350         node.add_property (X_("type"), _descriptor.name);
351         return node;
352 }
353
354 int
355 VBAPanner::set_state (const XMLNode& node, int /*version*/)
356 {
357         return 0;
358 }
359
360 Panner*
361 VBAPanner::factory (boost::shared_ptr<Pannable> p, boost::shared_ptr<Speakers> s)
362 {
363         return new VBAPanner (p, s);
364 }
365
366 ChanCount
367 VBAPanner::in() const
368 {
369         return ChanCount (DataType::AUDIO, _signals.size());
370 }
371
372 ChanCount
373 VBAPanner::out() const
374 {
375         return ChanCount (DataType::AUDIO, _speakers->n_speakers());
376 }
377
378 std::set<Evoral::Parameter> 
379 VBAPanner::what_can_be_automated() const
380 {
381         set<Evoral::Parameter> s;
382         s.insert (Evoral::Parameter (PanAzimuthAutomation));
383         if (_signals.size() > 1) {
384                 s.insert (Evoral::Parameter (PanWidthAutomation));
385         }
386         return s;
387 }
388         
389 string
390 VBAPanner::describe_parameter (Evoral::Parameter p)
391 {
392         switch (p.type()) {
393         case PanAzimuthAutomation:
394                 return _("Direction");
395         case PanWidthAutomation:
396                 return _("Diffusion");
397         default:
398                 return _pannable->describe_parameter (p);
399         }
400 }
401
402 string 
403 VBAPanner::value_as_string (boost::shared_ptr<AutomationControl> ac) const
404 {
405         /* DO NOT USE LocaleGuard HERE */
406         double val = ac->get_value();
407
408         switch (ac->parameter().type()) {
409         case PanAzimuthAutomation: /* direction */
410                 return string_compose (_("%1"), int (rint (val * 360.0)));
411                 
412         case PanWidthAutomation: /* diffusion */
413                 return string_compose (_("%1%%"), (int) floor (100.0 * fabs(val)));
414                 
415         default:
416                 return _pannable->value_as_string (ac);
417         }
418 }
419
420 AngularVector
421 VBAPanner::signal_position (uint32_t n) const
422 {
423         if (n < _signals.size()) {
424                 return _signals[n]->direction;
425         }
426
427         return AngularVector();
428 }
429
430 boost::shared_ptr<Speakers>
431 VBAPanner::get_speakers () const 
432 {
433         return _speakers->parent();
434 }
435
436 void
437 VBAPanner::set_position (double p)
438 {
439         if (p < 0.0) {
440                 p = 1.0 + p;
441         }
442
443         if (p > 1.0) {
444                 p = fmod (p, 1.0);
445         } 
446
447         _pannable->pan_azimuth_control->set_value (p);
448 }
449
450 void
451 VBAPanner::set_width (double w)
452 {
453         _pannable->pan_width_control->set_value (min (1.0, max (-1.0, w)));
454 }