1 /*
2  * Copyright (C) 2011-2017 Paul Davis <paul@linuxaudiosystems.com>
3  * Copyright (C) 2011 Carl Hetherington <carl@carlh.net>
4  * Copyright (C) 2013 John Emmas <john@creativepost.co.uk>
5  * Copyright (C) 2014 Robin Gareus <robin@gareus.org>
6  *
7  * This program is free software; you can redistribute it and/or modify
8  * it under the terms of the GNU General Public License as published by
9  * the Free Software Foundation; either version 2 of the License, or
10  * (at your option) any later version.
11  *
12  * This program is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15  * GNU General Public License for more details.
16  *
17  * You should have received a copy of the GNU General Public License along
18  * with this program; if not, write to the Free Software Foundation, Inc.,
19  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
20  */
21 
22 #include <cmath>
23 #include <cstdio>
24 #include <cstdlib>
25 #include <cstring>
26 
27 #include <iostream>
28 #include <string>
29 
30 #ifdef COMPILER_MSVC
31 #include <malloc.h>
32 #endif
33 
34 #include "pbd/cartesian.h"
35 #include "pbd/compose.h"
36 
37 #include "ardour/amp.h"
38 #include "ardour/audio_buffer.h"
39 #include "ardour/buffer_set.h"
40 #include "ardour/pan_controllable.h"
41 #include "ardour/pannable.h"
42 #include "ardour/speakers.h"
43 
44 #include "vbap.h"
45 #include "vbap_speakers.h"
46 
47 #include "pbd/i18n.h"
48 
49 using namespace PBD;
50 using namespace ARDOUR;
51 using namespace std;
52 
53 static PanPluginDescriptor _descriptor = {
54 	"VBAP 2D panner",
55 	"http://ardour.org/plugin/panner_vbap",
56 	"http://ardour.org/plugin/panner_vbap#ui",
57 	-1, -1,
58 	10,
59 	VBAPanner::factory
60 };
61 
62 extern "C" ARDOURPANNER_API PanPluginDescriptor*
panner_descriptor()63 panner_descriptor ()
64 {
65 	return &_descriptor;
66 }
67 
Signal(VBAPanner &,uint32_t,uint32_t n_speakers)68 VBAPanner::Signal::Signal (VBAPanner&, uint32_t, uint32_t n_speakers)
69 {
70 	resize_gains (n_speakers);
71 
72 	desired_gains[0] = desired_gains[1] = desired_gains[2] = 0;
73 	outputs[0] = outputs[1] = outputs[2] = -1;
74 	desired_outputs[0] = desired_outputs[1] = desired_outputs[2] = -1;
75 }
76 
77 void
resize_gains(uint32_t n)78 VBAPanner::Signal::resize_gains (uint32_t n)
79 {
80 	gains.assign (n, 0.0);
81 }
82 
VBAPanner(boost::shared_ptr<Pannable> p,boost::shared_ptr<Speakers> s)83 VBAPanner::VBAPanner (boost::shared_ptr<Pannable> p, boost::shared_ptr<Speakers> s)
84 	: Panner (p)
85 	, _speakers (new VBAPSpeakers (s))
86 {
87 	_pannable->pan_azimuth_control->Changed.connect_same_thread (*this, boost::bind (&VBAPanner::update, this));
88 	_pannable->pan_elevation_control->Changed.connect_same_thread (*this, boost::bind (&VBAPanner::update, this));
89 	_pannable->pan_width_control->Changed.connect_same_thread (*this, boost::bind (&VBAPanner::update, this));
90 	if (!_pannable->has_state ()) {
91 		reset ();
92 	}
93 
94 	update ();
95 }
96 
~VBAPanner()97 VBAPanner::~VBAPanner ()
98 {
99 	clear_signals ();
100 }
101 
102 void
clear_signals()103 VBAPanner::clear_signals ()
104 {
105 	for (vector<Signal*>::iterator i = _signals.begin (); i != _signals.end (); ++i) {
106 		delete *i;
107 	}
108 	_signals.clear ();
109 }
110 
111 void
configure_io(ChanCount in,ChanCount)112 VBAPanner::configure_io (ChanCount in, ChanCount /* ignored - we use Speakers */)
113 {
114 	uint32_t n = in.n_audio ();
115 
116 	clear_signals ();
117 
118 	for (uint32_t i = 0; i < n; ++i) {
119 		Signal* s = new Signal (*this, i, _speakers->n_speakers ());
120 		_signals.push_back (s);
121 	}
122 
123 	update ();
124 }
125 
126 void
update()127 VBAPanner::update ()
128 {
129 	_can_automate_list.clear ();
130 	_can_automate_list.insert (Evoral::Parameter (PanAzimuthAutomation));
131 	if (_signals.size () > 1) {
132 		_can_automate_list.insert (Evoral::Parameter (PanWidthAutomation));
133 	}
134 	if (_speakers->dimension () == 3) {
135 		_can_automate_list.insert (Evoral::Parameter (PanElevationAutomation));
136 	}
137 
138 	/* recompute signal directions based on panner azimuth and, if relevant, width (diffusion) and elevation parameters */
139 	double elevation = _pannable->pan_elevation_control->get_value () * 90.0;
140 
141 	if (_signals.size () > 1) {
142 		double w                   = -(_pannable->pan_width_control->get_value ());
143 		double signal_direction    = 1.0 - (_pannable->pan_azimuth_control->get_value () + (w / 2));
144 		double grd_step_per_signal = w / (_signals.size () - 1);
145 		for (vector<Signal*>::iterator s = _signals.begin (); s != _signals.end (); ++s) {
146 			Signal* signal = *s;
147 
148 			int over = signal_direction;
149 			over -= (signal_direction >= 0) ? 0 : 1;
150 			signal_direction -= (double)over;
151 
152 			signal->direction = AngularVector (signal_direction * 360.0, elevation);
153 			compute_gains (signal->desired_gains, signal->desired_outputs, signal->direction.azi, signal->direction.ele);
154 			signal_direction += grd_step_per_signal;
155 		}
156 	} else if (_signals.size () == 1) {
157 		double center = (1.0 - _pannable->pan_azimuth_control->get_value ()) * 360.0;
158 
159 		/* width has no role to play if there is only 1 signal: VBAP does not do "diffusion" of a single channel */
160 
161 		Signal* s    = _signals.front ();
162 		s->direction = AngularVector (center, elevation);
163 		compute_gains (s->desired_gains, s->desired_outputs, s->direction.azi, s->direction.ele);
164 	}
165 
166 	SignalPositionChanged (); /* emit */
167 }
168 
169 void
compute_gains(double gains[3],int speaker_ids[3],int azi,int ele)170 VBAPanner::compute_gains (double gains[3], int speaker_ids[3], int azi, int ele)
171 {
172 	/* calculates gain factors using loudspeaker setup and given direction */
173 	double    cartdir[3];
174 	double    power;
175 	int       i, j, k;
176 	double    small_g;
177 	double    big_sm_g, gtmp[3];
178 	const int dimension = _speakers->dimension ();
179 	assert (dimension == 2 || dimension == 3);
180 
181 	spherical_to_cartesian (azi, ele, 1.0, cartdir[0], cartdir[1], cartdir[2]);
182 	big_sm_g = -100000.0;
183 
184 	gains[0] = gains[1] = gains[2] = 0;
185 	speaker_ids[0] = speaker_ids[1] = speaker_ids[2] = 0;
186 
187 	for (i = 0; i < _speakers->n_tuples (); i++) {
188 		small_g = 10000000.0;
189 
190 		for (j = 0; j < dimension; j++) {
191 			gtmp[j] = 0.0;
192 
193 			for (k = 0; k < dimension; k++) {
194 				gtmp[j] += cartdir[k] * _speakers->matrix (i)[j * dimension + k];
195 			}
196 
197 			if (gtmp[j] < small_g) {
198 				small_g = gtmp[j];
199 			}
200 		}
201 
202 		if (small_g > big_sm_g) {
203 			big_sm_g = small_g;
204 
205 			gains[0] = gtmp[0];
206 			gains[1] = gtmp[1];
207 
208 			speaker_ids[0] = _speakers->speaker_for_tuple (i, 0);
209 			speaker_ids[1] = _speakers->speaker_for_tuple (i, 1);
210 
211 			if (_speakers->dimension () == 3) {
212 				gains[2]       = gtmp[2];
213 				speaker_ids[2] = _speakers->speaker_for_tuple (i, 2);
214 			} else {
215 				gains[2]       = 0.0;
216 				speaker_ids[2] = -1;
217 			}
218 		}
219 	}
220 
221 	power = sqrt (gains[0] * gains[0] + gains[1] * gains[1] + gains[2] * gains[2]);
222 
223 	if (power > 0) {
224 		gains[0] /= power;
225 		gains[1] /= power;
226 		gains[2] /= power;
227 	}
228 }
229 
230 void
distribute(BufferSet & inbufs,BufferSet & obufs,gain_t gain_coefficient,pframes_t nframes)231 VBAPanner::distribute (BufferSet& inbufs, BufferSet& obufs, gain_t gain_coefficient, pframes_t nframes)
232 {
233 	uint32_t                  n;
234 	vector<Signal*>::iterator s;
235 
236 	assert (inbufs.count ().n_audio () == _signals.size ());
237 
238 	for (s = _signals.begin (), n = 0; s != _signals.end (); ++s, ++n) {
239 		Signal* signal (*s);
240 
241 		distribute_one (inbufs.get_audio (n), obufs, gain_coefficient, nframes, n);
242 
243 		memcpy (signal->outputs, signal->desired_outputs, sizeof (signal->outputs));
244 	}
245 }
246 
247 void
distribute_one(AudioBuffer & srcbuf,BufferSet & obufs,gain_t gain_coefficient,pframes_t nframes,uint32_t which)248 VBAPanner::distribute_one (AudioBuffer& srcbuf, BufferSet& obufs, gain_t gain_coefficient, pframes_t nframes, uint32_t which)
249 {
250 	Sample* const src = srcbuf.data ();
251 	Signal*       signal (_signals[which]);
252 
253 	/* VBAP may distribute the signal across up to 3 speakers depending on
254 	 * the configuration of the speakers.
255 	 *
256 	 * But the set of speakers in use "this time" may be different from
257 	 * the set of speakers "the last time". So we have up to 6 speakers
258 	 * involved, and we have to interpolate so that those no longer
259 	 * in use are rapidly faded to silence and those newly in use
260 	 * are rapidly faded to their correct level. This prevents clicks
261 	 * as we change the set of speakers used to put the signal in
262 	 * a given position.
263 	 *
264 	 * However, the speakers are represented by output buffers, and other
265 	 * speakers may write to the same buffers, so we cannot use
266 	 * anything here that will simply assign new (sample) values
267 	 * to the output buffers - everything must be done via mixing
268 	 * functions and not assignment/copying.
269 	 */
270 
271 	vector<double>::size_type sz = signal->gains.size ();
272 
273 	assert (sz == obufs.count ().n_audio ());
274 
275 	int8_t* outputs = (int8_t*)alloca (sz); // on the stack, no malloc
276 
277 	/* set initial state of each output "record"
278          */
279 
280 	for (uint32_t o = 0; o < sz; ++o) {
281 		outputs[o] = 0;
282 	}
283 
284 	/* for all outputs used this time and last time,
285 				 * change the output record to show what has
286 				 * happened.
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 			/* nothing deing delivered to this output */
322 
323 			signal->gains[output] = 0.0;
324 
325 		} else if (fabs (pan - signal->gains[output]) > 0.00001) {
326 			/* signal to this output but the gain coefficient has changed, so
327 			 * interpolate between them.
328 			 */
329 
330 			AudioBuffer& buf (obufs.get_audio (output));
331 			buf.accumulate_with_ramped_gain_from (srcbuf.data (), nframes, signal->gains[output], pan, 0);
332 			signal->gains[output] = pan;
333 
334 		} else {
335 			/* signal to this output, same gain as before so just copy with gain */
336 
337 			mix_buffers_with_gain (obufs.get_audio (output).data (), src, nframes, pan);
338 			signal->gains[output] = pan;
339 		}
340 	}
341 
342 	/* clean up the outputs that were used last time but not this time */
343 
344 	for (uint32_t o = 0; o < sz; ++o) {
345 		if (outputs[o] == 1) {
346 			/* take signal and deliver with a rapid fade out */
347 			AudioBuffer& buf (obufs.get_audio (o));
348 			buf.accumulate_with_ramped_gain_from (srcbuf.data (), nframes, signal->gains[o], 0.0, 0);
349 			signal->gains[o] = 0.0;
350 		}
351 	}
352 
353 	/* note that the output buffers were all silenced at some point
354 	 * so anything we didn't write to with this signal (or any others)
355 	 * is just as it should be.
356 	 */
357 }
358 
359 void
distribute_one_automated(AudioBuffer &,BufferSet &,samplepos_t,samplepos_t,pframes_t,pan_t **,uint32_t)360 VBAPanner::distribute_one_automated (AudioBuffer& /*src*/, BufferSet& /*obufs*/,
361                                      samplepos_t /*start*/, samplepos_t /*end*/,
362                                      pframes_t /*nframes*/, pan_t** /*buffers*/, uint32_t /*which*/)
363 {
364 	/* XXX to be implemented */
365 }
366 
367 XMLNode&
get_state()368 VBAPanner::get_state ()
369 {
370 	XMLNode& node (Panner::get_state ());
371 	node.set_property (X_ ("uri"), _descriptor.panner_uri);
372 	/* this is needed to allow new sessions to load with old Ardour: */
373 	node.set_property (X_ ("type"), _descriptor.name);
374 	return node;
375 }
376 
377 Panner*
factory(boost::shared_ptr<Pannable> p,boost::shared_ptr<Speakers> s)378 VBAPanner::factory (boost::shared_ptr<Pannable> p, boost::shared_ptr<Speakers> s)
379 {
380 	return new VBAPanner (p, s);
381 }
382 
383 ChanCount
in() const384 VBAPanner::in () const
385 {
386 	return ChanCount (DataType::AUDIO, _signals.size ());
387 }
388 
389 ChanCount
out() const390 VBAPanner::out () const
391 {
392 	return ChanCount (DataType::AUDIO, _speakers->n_speakers ());
393 }
394 
395 string
value_as_string(boost::shared_ptr<const AutomationControl> ac) const396 VBAPanner::value_as_string (boost::shared_ptr<const AutomationControl> ac) const
397 {
398 	double val = ac->get_value ();
399 
400 	switch (ac->parameter ().type ()) {
401 		case PanAzimuthAutomation: /* direction */
402 			return string_compose (_ ("%1\u00B0"), (int(rint (val * 360.0)) + 180) % 360);
403 
404 		case PanWidthAutomation: /* diffusion */
405 			return string_compose (_ ("%1%%"), (int)floor (100.0 * fabs (val)));
406 
407 		case PanElevationAutomation: /* elevation */
408 			return string_compose (_ ("%1\u00B0"), (int)floor (90.0 * fabs (val)));
409 
410 		default:
411 			return _ ("unused");
412 	}
413 }
414 
415 AngularVector
signal_position(uint32_t n) const416 VBAPanner::signal_position (uint32_t n) const
417 {
418 	if (n < _signals.size ()) {
419 		return _signals[n]->direction;
420 	}
421 
422 	return AngularVector ();
423 }
424 
425 boost::shared_ptr<Speakers>
get_speakers() const426 VBAPanner::get_speakers () const
427 {
428 	return _speakers->parent ();
429 }
430 
431 void
set_position(double p)432 VBAPanner::set_position (double p)
433 {
434 	/* map into 0..1 range */
435 	int over = p;
436 	over -= (p >= 0) ? 0 : 1;
437 	p -= (double)over;
438 	_pannable->pan_azimuth_control->set_value (p, Controllable::NoGroup);
439 }
440 
441 void
set_width(double w)442 VBAPanner::set_width (double w)
443 {
444 	_pannable->pan_width_control->set_value (min (1.0, max (-1.0, w)), Controllable::NoGroup);
445 }
446 
447 void
set_elevation(double e)448 VBAPanner::set_elevation (double e)
449 {
450 	_pannable->pan_elevation_control->set_value (min (1.0, max (0.0, e)), Controllable::NoGroup);
451 }
452 
453 void
reset()454 VBAPanner::reset ()
455 {
456 	set_position (.5);
457 	if (_signals.size () > 1) {
458 		set_width (1.0 - (1.0 / (double)_signals.size ()));
459 	} else {
460 		set_width (1.0);
461 	}
462 	set_elevation (0);
463 
464 	update ();
465 }
466