CSL  6.0
VBAP.cpp
Go to the documentation of this file.
1 ///
2 /// VBAP.h -- Vector Base Amplitude Panning class (v2 - Nov 2006)
3 /// See the copyright notice and acknowledgment of authors in the file COPYRIGHT
4 /// Doug McCoy, 2004.
5 ///
6 
7 #include "VBAP.h"
8 
9 #define MAX_NUM_VBAP_TRIPLETS 512
10 #define MIN_VOLUME_TO_LENGTH_RATIO 0.01
11 
12 using namespace csl;
13 
14 typedef struct {
15  int firstNode;
17 } Connection;
18 
19 
20 double vecAngle(CPoint v1, CPoint v2) {
21  double inner = v1*v2/(v1.len() * v2.len());
22  if (inner > 1.0)
23  inner = 1.0;
24  else if (inner < -1.0)
25  inner = -1.0;
26  return fabs(acos(inner) );
27 }
28 
30  unsigned dimensions = m.RowNo();
31  double temp[3] = {0};
32  for (unsigned i = 0; i < dimensions; i++) // for each node, insert speaker coordinates in matrix
33  for (unsigned j = 0; j < dimensions; j++) // for each node, insert speaker coordinates in matrix
34  temp[i] += v(j) * m(j,i);
35 
36  CPoint result(temp[0], temp[1], temp[2]);
37  return result;
38 }
39 
41  nodes = new unsigned[size];
42 
43 }
44 
46  delete [] nodes;
47 
48 }
49 
51  cout << nodes[0] << "\t" << nodes[1] << "\t" << nodes[2] << endl;
52 }
53 
54 ///////////////////////////////////////////////////
55 /* SPEAKER TRIPLET LAYOUT IMPLEMENTATION */
56 ///////////////////////////////////////////////////
57 
59  mSpeakerLayout = aLayout;
60 
61 // mSpeakerLayout->attachObserver(this); // Tell the speaker layout to inform you of any changes
62  mMode = mode;
63  // Be smart about setting the mode.
64  if (mMode == kAuto) {
67  else
69  }
70 
71 // if (mSpeakerLayout->isPeriphonic()) {
72 // if (mMode == 0)
73 // mMode = kPeriphonic;
74 // } else
75 // mMode = kPantophonic;
76 
77  mTriplets = (SpeakerSet **) malloc(MAX_NUM_VBAP_TRIPLETS * sizeof(SpeakerSet*));
78  mNumTriplets = 0;
79  if (mMode == kPantophonic)
81  else
83 
84  dump();
85 }
86 
88 
89 // mSpeakerLayout->detachObserver(this); // Remove yourself from the list of observers.
90 
91  unsigned i = 0;
92  for (i = 0; i < mNumTriplets; i++)
93  delete mTriplets[i];
94 
95  free(mTriplets);
96 }
97 
100  printf("Max number of VBAP triplets exceeded. Triplet not added\n");
101  return;
102  }
103  mTriplets[mNumTriplets++] = lst;
104  return;
105 }
106 
108 
109  // locate the item
110  for (unsigned i = 0; ((i < mNumTriplets)); i++) {
111  if (mTriplets[i] == s) {
112  delete mTriplets[i];
113  --mNumTriplets;
114 
115  // shift the upper items down 1 in the list
116  for (; i < mNumTriplets; i++) {
117  mTriplets[i] = mTriplets[i+1];
118  }
119  break;
120  }
121  }
122 
123  return;
124 }
125 
127  cout << "Number of SpeakerSets: " << mNumTriplets << endl;
128  for (unsigned i = 0; i < mNumTriplets; i++) {
129  cout << "Triple #" << i << ": ";
130  mTriplets[i]->dump();
131  }
132 }
133 
135  unsigned dimensions = mMode;
136  CSLMatrix L(dimensions,dimensions);
137  for (unsigned i = 0; i < dimensions; i++) { // for each node, insert speaker coordinates in matrix
138  CPoint temp = mSpeakerLayout->speakerAtIndex(lst->nodes[i])->position();
139  for (unsigned j = 0; j < dimensions; j++) // for each node, insert speaker coordinates in matrix
140  L(i,j) = temp(j);
141  }
142  lst->invL = new CSLMatrix (L.Inv()); // invert matrix and populate SpeakerSet member
143  return;
144 }
145 
147  CPoint cross((li^lj) ^ (ln^lm));
148  float ang_ci = vecAngle(li, cross);
149  float ang_cj = vecAngle(lj, cross);
150  float ang_ij = vecAngle(li, lj);
151  float ang_cn = vecAngle(ln, cross);
152  float ang_cm = vecAngle(lm, cross);
153  float ang_nm = vecAngle(ln, ln);
154 
155  if (((ang_ci + ang_cj) == ang_ij) && ((ang_cn + ang_cm) == ang_nm) )
156  return true;
157  else {
158  ang_ci = vecAngle(li, -cross);
159  ang_cj = vecAngle(lj, -cross);
160  ang_cn = vecAngle(ln, -cross);
161  ang_cm = vecAngle(lm, -cross);
162 
163  return (((ang_ci + ang_cj) == ang_ij) && ((ang_cn + ang_cm) == ang_nm) );
164  }
165 }
166 
167 // If 2D VBAP, then find the pair of speakers.
169 
170  unsigned numSpeakers = mSpeakerLayout->numSpeakers();
171  unsigned j, index;
172  unsigned speakerMapping[numSpeakers]; // To map unordered speakers into an ordered set.
173  float speakerAngles[numSpeakers];
174  float indexAngle;
175 
176  logMsg("VBAP::finding loudspeaker pairs");
177 
178  // Build a map to the speakers, that points to speaker indexes.
179  for (unsigned i = 0; i < numSpeakers; i++) {
180  speakerAngles[i] = mSpeakerLayout->speakerAtIndex(i)->azimuth();
181  speakerMapping[i] = i;
182  }
183 
184  // Sort speakers into the map
185  for (unsigned i = 1; i < numSpeakers; i++) {
186  // Only sort speakers that have elevation == 0. Ignore all other.
187  if (mSpeakerLayout->speakerAtIndex(i)->elevation() == 0) {
188 
189  indexAngle = speakerAngles[i];
190  index = speakerMapping[i];
191  j = i;
192 
193  while ((j > 0) && (speakerAngles[j-1] > indexAngle)) {
194  speakerAngles[j] = speakerAngles[j-1];
195  speakerMapping[j] = speakerMapping[j-1];
196  j = j - 1;
197 
198  }
199  speakerAngles[j] = indexAngle;
200  speakerMapping[j] = index;
201  }
202  }
203 
204 //#ifdef CSL_DEBUG
205  for (unsigned i = 0; i < numSpeakers; i++)
206  mSpeakerLayout->speakerAtIndex(speakerMapping[i])->dump();
207 //#endif
208 
209  for (unsigned i = 1; i < numSpeakers; i++){
210  SpeakerSet *sPair = new SpeakerSet(2);
211  sPair->nodes[0] = speakerMapping[i-1];
212  sPair->nodes[1] = speakerMapping[i];
213  invertTripleMatrix(sPair);
214  addTriple(sPair);
215  }
216  // Add the last speaker
217  SpeakerSet *sPair = new SpeakerSet(2);
218  sPair->nodes[0] = speakerMapping[numSpeakers-1];
219  sPair->nodes[1] = speakerMapping[0];
220  invertTripleMatrix(sPair);
221  addTriple(sPair);
222 
223 
224 }
225 
227  unsigned numSpeakers = mSpeakerLayout->numSpeakers();
228  // form all possible triples
229  for (unsigned i = 0; i < numSpeakers; i++){
230  for (unsigned j = i+1; j < numSpeakers; j++){
231  for (unsigned k = j+1; k < numSpeakers; k++){
232  SpeakerSet *triplet = new SpeakerSet;
233  triplet->nodes[0] = i;
234  triplet->nodes[1] = j;
235  triplet->nodes[2] = k;
236  addTriple(triplet);
237  }
238  }
239  }
240 
241  // remove too narrow triples
242  for (unsigned i = 0; i < mNumTriplets; i++) {
243  SpeakerSet *trip = mTriplets[i];
244  CPoint p0, p1, p2;
245  p0 = mSpeakerLayout->speakerAtIndex(trip->nodes[0])->position();
246  p1 = mSpeakerLayout->speakerAtIndex(trip->nodes[1])->position();
247  p2 = mSpeakerLayout->speakerAtIndex(trip->nodes[2])->position();
248 
249  CPoint xprod = p0 ^ p1; // ^ is cross product operator
250  float volume = fabsf(xprod * p2);
251  float length = fabsf(vecAngle(p0, p1) ) + fabsf(vecAngle(p0, p2) ) + fabsf(vecAngle(p1, p2) );
252  float ratio;
253  if (length > 0.00001)
254  ratio = volume / length;
255  else
256  ratio = 0.0;
257 
258  if (ratio < MIN_VOLUME_TO_LENGTH_RATIO) {
259  removeTriple(trip);
260  i--;
261  }
262  }
263 
264  // resolve sides that cross
265  for (unsigned i = 0; i < mNumTriplets; i++) {
266  // Holders to the triplets being tested, so they can be removed if needed.
267  SpeakerSet *currentOutterTriple, *currentInnerTriple;
268  Connection connections[6];
269  connections[0].firstNode = mTriplets[i]->nodes[0];
270  connections[0].secondNode = mTriplets[i]->nodes[1];
271 
272  connections[1].firstNode = mTriplets[i]->nodes[1];
273  connections[1].secondNode = mTriplets[i]->nodes[2];
274 
275  connections[2].firstNode = mTriplets[i]->nodes[0];
276  connections[2].secondNode = mTriplets[i]->nodes[2];
277 
278  currentOutterTriple = mTriplets[i];
279  for (unsigned n = i+1; n < mNumTriplets; n++) {
280  connections[3].firstNode = mTriplets[n]->nodes[0];
281  connections[3].secondNode = mTriplets[n]->nodes[1];
282 
283  connections[4].firstNode = mTriplets[n]->nodes[1];
284  connections[4].secondNode = mTriplets[n]->nodes[2];
285 
286  connections[5].firstNode = mTriplets[n]->nodes[0];
287  connections[5].secondNode = mTriplets[n]->nodes[2];
288 
289  currentInnerTriple = mTriplets[n];
290  bool removed = false;
291  bool startOver = false;
292  for (unsigned j = 0; j < 3; j++) {
293  CPoint v1 = mSpeakerLayout->speakerAtIndex(connections[j].firstNode)->position();
294  CPoint v2 = mSpeakerLayout->speakerAtIndex(connections[j].secondNode)->position();
295  for (unsigned k = 3; k < 6; k++) {
296  CPoint v3 = mSpeakerLayout->speakerAtIndex(connections[k].firstNode)->position();
297  CPoint v4 = mSpeakerLayout->speakerAtIndex(connections[k].secondNode)->position();
298 
299  if (evaluateCrossing(v1, v2, v3, v4 ) ) { // if connections intersect
300  float length1 = v1.distance(v2);
301  float length2 = v3.distance(v4);
302  if (length1 > length2) { // remove triple with longer connection
303  removeTriple(currentOutterTriple );
304  i--; // ith triple deleted so don't increment i on next loop
305  startOver = true;
306  }
307  else {
308  removeTriple(currentInnerTriple );
309  n--; // nth triple deleted so don't increment n on next loop
310  }
311  removed = true;
312  }
313  if (removed) break; // break k loop
314  }
315  if (removed) break; // break j loop i.e. start a new n loop
316  }
317  if (startOver) break; // break n loop i.e. removed ith triple
318  }
319  }
320 
321  // remove triangles that contain other Speakers
322  for (unsigned ii = 0; ii < mNumTriplets; ii++) {
324 // cout << "outside invert: " << *(mTriplets[ii]->invL) << endl;
325 
326  for (unsigned jj = 0; jj < numSpeakers; jj++) {
327  // check to see if the current speaker is one of the nodes of the triple
328  if ((jj == mTriplets[ii]->nodes[0]) || (jj == mTriplets[ii]->nodes[1]) || (jj == mTriplets[ii]->nodes[2]) )
329  continue;
331  CPoint v = vectorMultiply(p, (*(mTriplets[ii]->invL)) );
332  bool x_inside, y_inside, z_inside;
333  x_inside = (v.x >= -1e-4); // inside if positive or negative near zero, -1e-4 is a magic number
334  y_inside = (v.y >= -1e-4);
335  z_inside = (v.z >= -1e-4);
336 
337  if ((x_inside) && (y_inside) && ((mMode == 2) || (z_inside) ) ) {
338  removeTriple(mTriplets[ii] ); // if LSpeaker is within triangle, delete triple
339  ii--;
340  break;
341  }
342  }
343  }
344  if (mNumTriplets == 0 )
345  throw LogicError("No SpeakerSets found. Check mode setting or speaker layout.");
346 
347  return;
348 } // end find_triples()
349 
350 //////////////////////////////////////
351 /* VBAP IMPLEMENTATION */
352 /////////////////////////////////////
353 
354 /// The two parameters are optional. \n The mode defines wether to consider elevation (full 3D) positions, or horizontal only (kPantophonic).
355 /// If not specified, the mode is chosen automatically. If the SpeakerLayout contains speakers at different elevations, then it sets the mode to 3D.
356 /// Otherwise, it is set to 2D. \n The layout parameter allows to specify a different layout than the default. The recomended use is to create a layout
357 /// and set it as default. Then any Panner can make use of it. \n This parameter overrides the use of the default layout, using the layout passed here.
358 VBAP::VBAP(VBAPMode mode, SpeakerLayout *layout) : SpatialPanner(layout), mMode(mode) {
359 
360  setCopyPolicy(kIgnore); // This is needed so the default kCopy doesn't overide the multiple channel panning done here.
361 
362  // Be smart about setting the mode.
363  if (mMode == kAuto) {
365  mMode = kPeriphonic;
366  else
368  }
369  setNumChannels(mSpeakerLayout->numSpeakers()); // Set the number of (output) channels to the number of loudspeakers in the layout
371 
372 }
373 
375  delete mSpeakerSetLayout;
376 }
377 
379 
380  setNumChannels(mSpeakerLayout->numSpeakers()); // Set the number of (output) channels to the number of loudspeakers in the layout
381  delete mSpeakerSetLayout; // Delete the old triplets
382  mSpeakerSetLayout = new SpeakerSetLayout(mSpeakerLayout); // create a new triplet layout
383 
384 }
385 
386 void *VBAP::cache() {
387  return (void *)new VBAPSourceCache(); // Returns a pointer to an alocated cache data for its own use.
388 }
389 
390 void VBAP::nextBuffer(Buffer &outputBuffer) throw(CException) {
391  this->nextBuffer(outputBuffer, 0);
392 }
393 
394 void VBAP::nextBuffer(Buffer &outputBuffer, unsigned outBufNum) throw(CException) {
395  unsigned numFrames = outputBuffer.mNumFrames;
396  unsigned numTriplets = mSpeakerSetLayout->mNumTriplets;
397  outputBuffer.zeroBuffers(); // clear output buffer
398 
399 #ifdef CSL_DEBUG
400  logMsg("VBAP::nextBuffer");
401 #endif
402 
403  for (unsigned i = 0; i < mSources.size(); i++) { // i loops through inputs
404  SpatialSource *source = (SpatialSource*) mSources[i]; // Get the sound source
405  VBAPSourceCache *tcache = (VBAPSourceCache *) mCache[i]; // Get any cached data belonging to the sound source.
406 
407  if (source->isActive()) { // if source is active
408  if (source->positionChanged()) { // if the position of the source has changed, recompute gains.
409  CPoint currentGains;
410  CPoint sourcePosition = *source->position(); // Get the source position.
411  sourcePosition.normalize(); // to keep calculations on sphere of Speakers
412 
413  unsigned currentTripletIndex = tcache->tripletIndex; // Cached source placement, so it starts searching from there.
414 
415  // Search thru the triplets array in search of a match for the source position.
416  for (unsigned count = 0; count < numTriplets; ++count) {
417  currentGains = vectorMultiply(sourcePosition, (*(mSpeakerSetLayout->mTriplets[currentTripletIndex]->invL)) );
418  if ((currentGains.x >= 0) && (currentGains.y >= 0) && ((mMode == 2) || (currentGains.z >= 0)) ) // need more sophisticated condition eventually
419  break;
420 
421  ++currentTripletIndex;
422  if (currentTripletIndex >= numTriplets)
423  currentTripletIndex = 0;
424  }
425 
426  tcache->tripletIndex = currentTripletIndex; // Store the new index
427 
428  double soundPowerConstant = 1;
429  double gainScale = sqrt(soundPowerConstant) / currentGains.len();
430  currentGains *= gainScale;
431 
432  // Store the gains for possible future use.
433  tcache->gains[0] = currentGains.x;
434  tcache->gains[1] = currentGains.y;
435  tcache->gains[2] = currentGains.z;
436  } // end of if (positionChanged)
437 
438  SpeakerSet *currentTriplet = mSpeakerSetLayout->mTriplets[tcache->tripletIndex];
439 
440  source->nextBuffer(mTempBuffer); // Ask the source to fill the buffer with the data to be processed
441 
442  for (unsigned j = 0; j < mMode; j++) {
443  SampleBuffer out1 = outputBuffer.buffer(currentTriplet->nodes[j]); // get a pointer to the buffer of the speaker that needds some gain applied.
444  SampleBuffer opp1 = mTempBuffer.buffer(0); // this buffer has the data of the input source. Only mono for now. TO BE FIXED AND EXTENDED TO ...
445 
446  for (unsigned k = 0; k < numFrames; k++) { // k loops through sample buffers
447  *out1++ += tcache->gains[j] * (*opp1++);
448  }
449  }
450 
451 #ifdef CSL_DEBUG
452  logMsg("Speaker Gains: %.2f, \t %.2f, \t %.2f", tcache->gains[0], tcache->gains[1], tcache->gains[2]);
453 #endif
454 
455  } // end if active
456  } // end for loop
457  return;
458 }
459 
460 
461 //////////////////////////////////////
462 /* STEREO PANNER IMPLEMENTATION */
463 /////////////////////////////////////
464 
466 
468  delete mSpeakerLayout; // This time I allocated the memory, so I have to get rid of it.
469 }
470 
471 ////////////////////////////////////////
472 /* SURRORUND PANNER IMPLEMENTATION */
473 ///////////////////////////////////////
474 
475 SurroundPanner::SurroundPanner(unsigned numSpeakers, bool useSubwoofer) : VBAP(kPantophonic, new StereoSpeakerLayout) { }
476 
478  delete mSpeakerLayout; // This time I allocated the memory, so I have to get rid of it.
479 }
480 
sample * SampleBuffer
1-channel buffer data type, vector of (sample)
Definition: CSL_Types.h:194
void logMsg(const char *format,...)
These are the public logging messages.
Definition: CGestalt.cpp:292
float elevation()
float azimuth()
AdditiveInstrument.h – Sum-of-sines synthesis instrument class.
Definition: Accessor.h:17
double vecAngle(CPoint v1, CPoint v2)
Definition: VBAP.cpp:20
~SpeakerSetLayout()
destructor
Definition: VBAP.cpp:87
unsigned mMode
Definition: VBAP.h:169
Speaker * speakerAtIndex(unsigned speakerIndex) const
Returns the speaker at the specified index.
Definition: SpeakerLayout.h:65
COORD_TYPE len()
Definition: CPoint.h:143
bool evaluateCrossing(CPoint &li, CPoint &lj, CPoint &ln, CPoint &lm)
Definition: VBAP.cpp:146
CSLMatrix * invL
pre-computed inverse matrix for this triplet
Definition: VBAP.h:123
void nextBuffer(Buffer &outputBuffer)
Just as any Effect in CSL, this method gets called at runtime by the audio driver. Here is where the actual processing happens.
Definition: VBAP.cpp:390
SpeakerSet(unsigned size=3)
Definition: VBAP.cpp:40
int secondNode
Definition: VBAP.cpp:16
matrixT Inv() _THROW_MATRIX_ERROR
VBAPMode mMode
Prints useful information about this VBAP instance.
Definition: VBAP.h:73
Impossible operation.
void normalize()
Definition: CPoint.cpp:423
virtual ~StereoPanner()
Definition: VBAP.cpp:467
COORD_TYPE x
Definition: CPoint.h:41
Full 3D VBAP.
Definition: VBAP.h:49
virtual ~VBAP()
Definition: VBAP.cpp:374
virtual void nextBuffer(Buffer &outputBuffer, unsigned outBufNum)
really compute the next buffer given an offset base channel; this is called by nextBuffer, possibly multiple times
CPoint * position(unsigned channelNum=0)
Returns the distance from the center.
Base class for all panners. Handles source management and holds a speaker layout. ...
Definition: SpatialPanner.h:22
matrix< double > CSLMatrix
Definition: VBAP.h:37
void addTriple(SpeakerSet *lst)
Definition: VBAP.cpp:98
#define MAX_NUM_VBAP_TRIPLETS
VBAP.h – Vector Base Amplitude Panning class (v2 - Nov 2006) See the copyright notice and acknowledg...
Definition: VBAP.cpp:9
#define MIN_VOLUME_TO_LENGTH_RATIO
Definition: VBAP.cpp:10
int firstNode
Definition: VBAP.cpp:15
COORD_TYPE z
Definition: CPoint.h:41
COORD_TYPE distance(CPoint *)
Definition: CPoint.cpp:353
Temp Spatial Sound Source.
Definition: SpatialSource.h:29
void dump()
Print speaker information.
SpeakerLayout * mSpeakerLayout
If null, it will use the default layout by calling SpeakerLayout::defaultSpeakerLayout();.
Definition: SpatialPanner.h:52
virtual void * cache()
Returns an instance of it's cache data per sound source.
Definition: VBAP.cpp:386
static size_t size
Definition: fft_N.c:40
virtual bool isActive()
query whether I'm currently active (Envelopes can go inactive)
Definition: CSL_Core.h:273
virtual void speakerLayoutChanged()
called when the speaker layout changes, so panners update precalculated data
Definition: VBAP.cpp:378
void findSpeakerPairs()
Definition: VBAP.cpp:168
virtual bool positionChanged()
Returns whether the sound source position changed since last block call.
Definition: SpatialSource.h:63
void dump()
just print friendly info about the triplet
Definition: VBAP.cpp:50
COORD_TYPE y
Definition: CPoint.h:41
SpeakerSetLayout(SpeakerLayout *aLayout, VBAPMode mode=kAuto)
Constructors & destructor:
Definition: VBAP.cpp:58
float gains[3]
Definition: VBAP.h:110
SpeakerSet ** mTriplets
Definition: VBAP.h:166
void invertTripleMatrix(SpeakerSet *lst)
Definition: VBAP.cpp:134
SpeakerSetLayout * mSpeakerSetLayout
A reference to the layout that contains the speaker pairs or triplets.
Definition: VBAP.h:77
void setCopyPolicy(BufferCopyPolicy ch)
get/set the receiver's buffer copy policy
Definition: CSL_Core.h:256
unsigned * nodes
the index for each of the three speakers that represent the triplet
Definition: VBAP.h:122
Vector Base Amplitude Panning.
Definition: VBAP.h:61
Buffer – the multi-channel sample buffer class (passed around between generators and IO guys)...
Definition: CSL_Core.h:106
SurroundPanner(unsigned numSpeakers=5, bool useSubwoofer=true)
The constructor defaults to a 5.1 setup.
Definition: VBAP.cpp:475
unsigned mNumTriplets
Definition: VBAP.h:168
unsigned numSpeakers() const
Definition: SpeakerLayout.h:58
SpeakerLayout * mSpeakerLayout
Definition: VBAP.h:167
ignore extra buffer channels
Definition: CSL_Core.h:213
CPoint position()
Groups two or three loudspeakers, and their inverse. Used for VBAP computations.
Definition: VBAP.h:117
void findSpeakerTriplets()
Definition: VBAP.cpp:226
size_t RowNo() const
Definition: matrix.h:236
void removeTriple(SpeakerSet *lst)
Definition: VBAP.cpp:107
void setNumChannels(unsigned ch)
get/set the receiver's number of outputs
Definition: CSL_Core.h:253
virtual ~SurroundPanner()
Definition: VBAP.cpp:477
Only uses the horizontally placed speakers (zero elevation)
Definition: VBAP.h:48
VBAPMode
VBAP types.
Definition: VBAP.h:46
unsigned tripletIndex
Definition: VBAP.h:113
Base class of CSL exceptions (written upper-case). Has a string message.
CPoint vectorMultiply(CPoint &v, CSLMatrix &m)
Definition: VBAP.cpp:29
Standard "Stereo Speaker Layout", where two speakers are positioned 30¼ left, 30¼ right and no elevat...
Definition: SpeakerLayout.h:86
VBAP(VBAPMode mode=kAuto, SpeakerLayout *layout=SpeakerLayout::defaultSpeakerLayout())
Initializer for a VBAP Panner. Optionally a speaker layout can be specified. Defaults to auto...
Definition: VBAP.cpp:358