Move panner bypass state up to the PannerShell so that it is preserved even when...
[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         XMLNode& node (Panner::get_state());
375         node.add_property (X_("type"), _descriptor.name);
376         return node;
377 }
378
379 Panner*
380 VBAPanner::factory (boost::shared_ptr<Pannable> p, boost::shared_ptr<Speakers> s)
381 {
382         return new VBAPanner (p, s);
383 }
384
385 ChanCount
386 VBAPanner::in() const
387 {
388         return ChanCount (DataType::AUDIO, _signals.size());
389 }
390
391 ChanCount
392 VBAPanner::out() const
393 {
394         return ChanCount (DataType::AUDIO, _speakers->n_speakers());
395 }
396
397 std::set<Evoral::Parameter> 
398 VBAPanner::what_can_be_automated() const
399 {
400         set<Evoral::Parameter> s;
401         s.insert (Evoral::Parameter (PanAzimuthAutomation));
402         if (_signals.size() > 1) {
403                 s.insert (Evoral::Parameter (PanWidthAutomation));
404         }
405         return s;
406 }
407         
408 string
409 VBAPanner::describe_parameter (Evoral::Parameter p)
410 {
411         switch (p.type()) {
412         case PanAzimuthAutomation:
413                 return _("Direction");
414         case PanWidthAutomation:
415                 return _("Diffusion");
416         default:
417                 return _pannable->describe_parameter (p);
418         }
419 }
420
421 string 
422 VBAPanner::value_as_string (boost::shared_ptr<AutomationControl> ac) const
423 {
424         /* DO NOT USE LocaleGuard HERE */
425         double val = ac->get_value();
426
427         switch (ac->parameter().type()) {
428         case PanAzimuthAutomation: /* direction */
429                 return string_compose (_("%1"), int (rint (val * 360.0)));
430                 
431         case PanWidthAutomation: /* diffusion */
432                 return string_compose (_("%1%%"), (int) floor (100.0 * fabs(val)));
433                 
434         default:
435                 return _pannable->value_as_string (ac);
436         }
437 }
438
439 AngularVector
440 VBAPanner::signal_position (uint32_t n) const
441 {
442         if (n < _signals.size()) {
443                 return _signals[n]->direction;
444         }
445
446         return AngularVector();
447 }
448
449 boost::shared_ptr<Speakers>
450 VBAPanner::get_speakers () const 
451 {
452         return _speakers->parent();
453 }
454
455 void
456 VBAPanner::set_position (double p)
457 {
458         if (p < 0.0) {
459                 p = 1.0 + p;
460         }
461
462         if (p > 1.0) {
463                 p = fmod (p, 1.0);
464         } 
465
466         _pannable->pan_azimuth_control->set_value (p);
467 }
468
469 void
470 VBAPanner::set_width (double w)
471 {
472         _pannable->pan_width_control->set_value (min (1.0, max (-1.0, w)));
473 }