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