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