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