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