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