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