9 #include "pbd/cartesian.h"
11 #include "ardour/pannable.h"
12 #include "ardour/speakers.h"
13 #include "ardour/audio_buffer.h"
14 #include "ardour/buffer_set.h"
15 #include "ardour/pan_controllable.h"
18 #include "vbap_speakers.h"
21 using namespace ARDOUR;
24 static PanPluginDescriptor _descriptor = {
30 extern "C" { PanPluginDescriptor* panner_descriptor () { return &_descriptor; } }
32 VBAPanner::Signal::Signal (Session& session, VBAPanner& p, uint32_t n)
34 gains[0] = gains[1] = gains[2] = 0;
35 desired_gains[0] = desired_gains[1] = desired_gains[2] = 0;
36 outputs[0] = outputs[1] = outputs[2] = -1;
37 desired_outputs[0] = desired_outputs[1] = desired_outputs[2] = -1;
40 VBAPanner::VBAPanner (boost::shared_ptr<Pannable> p, boost::shared_ptr<Speakers> s)
42 , _speakers (new VBAPSpeakers (s))
44 _pannable->pan_azimuth_control->Changed.connect_same_thread (*this, boost::bind (&VBAPanner::update, this));
45 _pannable->pan_width_control->Changed.connect_same_thread (*this, boost::bind (&VBAPanner::update, this));
50 VBAPanner::~VBAPanner ()
56 VBAPanner::clear_signals ()
58 for (vector<Signal*>::iterator i = _signals.begin(); i != _signals.end(); ++i) {
65 VBAPanner::configure_io (ChanCount in, ChanCount /* ignored - we use Speakers */)
67 uint32_t n = in.n_audio();
71 for (uint32_t i = 0; i < n; ++i) {
72 _signals.push_back (new Signal (_pannable->session(), *this, i));
81 /* recompute signal directions based on panner azimuth and width (diffusion) parameters)
84 /* panner azimuth control is [0 .. 1.0] which we interpret as [0 .. 360] degrees
87 double center = _pannable->pan_azimuth_control->get_value() * 360.0;
89 /* panner width control is [-1.0 .. 1.0]; we ignore sign, and map to [0 .. 360] degrees
90 so that a width of 1 corresponds to a signal equally present from all directions,
91 and a width of zero corresponds to a point source from the "center" (above)
94 double w = fabs (_pannable->pan_width_control->get_value()) * 360.0;
96 double min_dir = center - w;
97 min_dir = max (min (min_dir, 360.0), 0.0);
99 double max_dir = center + w;
100 max_dir = max (min (max_dir, 360.0), 0.0);
102 double degree_step_per_signal = (max_dir - min_dir) / _signals.size();
103 double signal_direction = min_dir;
105 for (vector<Signal*>::iterator s = _signals.begin(); s != _signals.end(); ++s) {
109 signal->direction = AngularVector (signal_direction, 0.0);
111 compute_gains (signal->desired_gains, signal->desired_outputs, signal->direction.azi, signal->direction.ele);
112 cerr << " @ " << signal->direction.azi << " /= " << signal->direction.ele
114 << signal->desired_outputs[0] + 1 << ' '
115 << signal->desired_outputs[1] + 1 << ' '
117 << signal->desired_gains[0] << ' '
118 << signal->desired_gains[1] << ' '
121 signal_direction += degree_step_per_signal;
126 VBAPanner::compute_gains (double gains[3], int speaker_ids[3], int azi, int ele)
128 /* calculates gain factors using loudspeaker setup and given direction */
133 double big_sm_g, gtmp[3];
135 azi_ele_to_cart (azi,ele, cartdir[0], cartdir[1], cartdir[2]);
136 big_sm_g = -100000.0;
138 gains[0] = gains[1] = gains[2] = 0;
139 speaker_ids[0] = speaker_ids[1] = speaker_ids[2] = 0;
141 for (i = 0; i < _speakers->n_tuples(); i++) {
143 small_g = 10000000.0;
145 for (j = 0; j < _speakers->dimension(); j++) {
149 for (k = 0; k < _speakers->dimension(); k++) {
150 gtmp[j] += cartdir[k] * _speakers->matrix(i)[j*_speakers->dimension()+k];
153 if (gtmp[j] < small_g) {
158 if (small_g > big_sm_g) {
165 speaker_ids[0] = _speakers->speaker_for_tuple (i, 0);
166 speaker_ids[1] = _speakers->speaker_for_tuple (i, 1);
168 if (_speakers->dimension() == 3) {
170 speaker_ids[2] = _speakers->speaker_for_tuple (i, 2);
178 power = sqrt (gains[0]*gains[0] + gains[1]*gains[1] + gains[2]*gains[2]);
188 VBAPanner::distribute (BufferSet& inbufs, BufferSet& obufs, gain_t gain_coefficient, pframes_t nframes)
191 vector<Signal*>::iterator s;
193 assert (inbufs.count().n_audio() == _signals.size());
195 for (s = _signals.begin(), n = 0; s != _signals.end(); ++s, ++n) {
199 distribute_one (inbufs.get_audio (n), obufs, gain_coefficient, nframes, n);
201 memcpy (signal->gains, signal->desired_gains, sizeof (signal->gains));
202 memcpy (signal->outputs, signal->desired_outputs, sizeof (signal->outputs));
207 VBAPanner::distribute_one (AudioBuffer& srcbuf, BufferSet& obufs, gain_t gain_coefficient, pframes_t nframes, uint32_t which)
209 Sample* const src = srcbuf.data();
212 uint32_t n_audio = obufs.count().n_audio();
214 Signal* signal (_signals[which]);
216 for (uint32_t o = 0; o < n_audio; ++o) {
220 /* VBAP may distribute the signal across up to 3 speakers depending on
221 the configuration of the speakers.
224 for (int o = 0; o < 3; ++o) {
225 if (signal->desired_outputs[o] != -1) {
229 /* XXX TODO: interpolate across changes in gain and/or outputs
232 dst = obufs.get_audio (signal->desired_outputs[o]).data();
234 pan = gain_coefficient * signal->desired_gains[o];
235 mix_buffers_with_gain (dst+n,src+n,nframes-n,pan);
241 for (uint32_t o = 0; o < n_audio; ++o) {
243 /* VBAP decided not to deliver any audio to this output, so we write silence */
244 dst = obufs.get_audio(o).data();
245 memset (dst, 0, sizeof (Sample) * nframes);
252 VBAPanner::distribute_one_automated (AudioBuffer& src, BufferSet& obufs,
253 framepos_t start, framepos_t end, pframes_t nframes, pan_t** buffers, uint32_t which)
258 VBAPanner::get_state ()
264 VBAPanner::state (bool full_state)
266 XMLNode& node (Panner::get_state());
267 node.add_property (X_("type"), _descriptor.name);
272 VBAPanner::set_state (const XMLNode& node, int /*version*/)
278 VBAPanner::factory (boost::shared_ptr<Pannable> p, boost::shared_ptr<Speakers> s)
280 return new VBAPanner (p, s);
284 VBAPanner::in() const
286 return ChanCount (DataType::AUDIO, _signals.size());
290 VBAPanner::out() const
292 return ChanCount (DataType::AUDIO, _speakers->n_speakers());
295 std::set<Evoral::Parameter>
296 VBAPanner::what_can_be_automated() const
298 set<Evoral::Parameter> s;
299 s.insert (Evoral::Parameter (PanAzimuthAutomation));
300 s.insert (Evoral::Parameter (PanWidthAutomation));
305 VBAPanner::describe_parameter (Evoral::Parameter p)
308 case PanAzimuthAutomation:
309 return _("Direction");
310 case PanWidthAutomation:
311 return _("Diffusion");
313 return _pannable->describe_parameter (p);
318 VBAPanner::value_as_string (boost::shared_ptr<AutomationControl> ac) const
320 /* DO NOT USE LocaleGuard HERE */
321 double val = ac->get_value();
323 switch (ac->parameter().type()) {
324 case PanAzimuthAutomation: /* direction */
325 return string_compose (_("%1"), val * 360.0);
327 case PanWidthAutomation: /* diffusion */
328 return string_compose (_("%1%%"), (int) floor (100.0 * fabs(val)));
331 return _pannable->value_as_string (ac);
336 VBAPanner::signal_position (uint32_t n) const
338 if (n < _signals.size()) {
339 return _signals[n]->direction;
342 return AngularVector();