CSL  6.0
Ambisonic.cpp
Go to the documentation of this file.
1 //
2 // Ambisonic.cpp -- Higher Order Ambisonic abstract base class for all Ambisonic effects and panners.
3 // See the copyright notice and acknowledgment of authors in the file COPYRIGHT
4 // Higher Order Ambisonic classes written by Jorge Castellanos, Graham Wakefield, Florian Hollerweger, 2005
5 //
6 // AmbisonicUnitGenerator.cpp -- Higher Order Ambisonic superclass for all Ambisonic encoded framestreams
7 // See the copyright notice and acknowledgment of authors in the file COPYRIGHT
8 // Higher Order Ambisonic classes written by Jorge Castellanos, Graham Wakefield, Florian Hollerweger, 2005
9 //
10 // Mixin superclass for the ambisonic framestream classes (e.g. HOA_Encoder, HOA_Rotator, AmbisonicDecoder).
11 // Encapsulates parameters regarding the ambisonic order of the class (including hybrid orders)
12 //
13 // See HOA_AmbisonicFramestream.h for descriptions of the the class members.
14 
15 #include "Ambisonic.h"
16 #include "AmbisonicUtilities.h"
17 
18 #include <stdlib.h>
19 #include <math.h>
20 #include <string.h>
21 #include <iostream>
22 
23 #define AMBI_INVSQRT2 (1/1.414213562)
24 #define INV_SQRT2 (1/1.414213562)
25 #define AMBI_INVSQRT2 (1/1.414213562)
26 
27 using std::cout;
28 using std::endl;
29 
30 using namespace csl;
31 
32 /*******************CONSTRUCTORS & DESTRUCTOR*******************/
33 
34 AmbisonicUnitGenerator::AmbisonicUnitGenerator(unsigned torder) : mOrder(torder, torder) {
35  initOrder(); // initialize
36 }
37 
38 AmbisonicUnitGenerator::AmbisonicUnitGenerator(unsigned hOrder, unsigned vOrder) : mOrder(hOrder, vOrder) {
39  mOrder.verticalOrder = vOrder;
40  initOrder();
41 }
42 
44  initOrder(); // initialize
45 }
46 
48 
49 
51 
52  setCopyPolicy(kIgnore); // This is needed so the default kCopy doesn't overide the multiple channel panning done here.
53 
54  mOrder.isUniform = (mOrder.horizontalOrder == mOrder.verticalOrder); // false for hybrid orders
55  mNumChannels = orderToChannels(mOrder); // derive necessary output channels from ambisonic order
56 
57 }
58 
60  mOrder = torder;
61  initOrder();
62 }
63 
64 /*******************UTILITY FUNCTIONS*******************/
65 
66 unsigned AmbisonicUnitGenerator::channelsToUniformOrder(const unsigned channels) {
67  // M = floor(sqrt(N) - 1)
68  return (unsigned)floor(sqrt((double)channels)-1);
69 }
70 
72  return torder.horizontalOrder > torder.verticalOrder ? torder.horizontalOrder : torder.verticalOrder;
73 }
74 
76  // hybrid system version: N = 2*M_h + 1 + (M_v + 1)^2 - (2*M_v + 1)
77  return (2 * torder.horizontalOrder + 1) + ((unsigned)pow(torder.verticalOrder + 1.f, 2) - (2 * torder.verticalOrder + 1));
78 }
79 
80 unsigned AmbisonicUnitGenerator::orderToChannels(unsigned torder) {
81  // uniform system version: N = (M+1)^2
82  return (unsigned)pow(torder + 1.f, 2);
83 }
84 
86  // N_h = 2*M_h + 1
87  return (2 * torder.horizontalOrder + 1);
88 }
89 
91  // N_v = (M_v + 1)^2 - (2*M_v + 1)
92  return ((unsigned)pow(torder.verticalOrder + 1.f, 2) - (2 * torder.verticalOrder + 1));
93 }
94 
95 
96 /*******************CHANNEL INDEXER FUNCTIONS*******************/
97 
98 void AmbisonicUnitGenerator::channelIndexer(unsigned *indexArray) {
99  AmbisonicOrder torder = mOrder;
100  unsigned *channelIndex = indexArray;
101  // how many channels it would need *if* it was a uniform order (hOrder == vOrder)
102  unsigned numChannelsGreaterOrder = orderToChannels(greaterOrder(torder));
103 
104  // how many channels it actually needs for the hybrid order
105  unsigned nnumChannels = orderToChannels(torder);
106 
107  unsigned n = 1;
108  unsigned currentOrder = 1;
109  unsigned hoaChannel = 1;
110  unsigned numChannelsCurrentOrder;
111 
112  // fill the channel indexer with zeroes
113  for (unsigned i = 0; i < numChannelsGreaterOrder; i++)
114  channelIndex[i] = 0;
115 
116  // work our way up the orders until we have exhausted all the required channels
117  while (n < nnumChannels) {
118  // horizontal indexing required?
119  if (currentOrder <= torder.horizontalOrder) {
120  // there are always 2 channels for horizontal encoding at each order
121  channelIndex[hoaChannel++] = n++;
122  channelIndex[hoaChannel++] = n++;
123  } else {
124  hoaChannel += 2; // skip them
125  }
126 
127  // how many channels are required up to the current order?
128  numChannelsCurrentOrder = orderToChannels(currentOrder);
129 
130  // vertical indexing required?
131  if (currentOrder <= torder.verticalOrder) {
132  // do until all required vertical channels up to the current order are exhausted
133  while(hoaChannel < numChannelsCurrentOrder) {
134  channelIndex[hoaChannel++] = n++; // set and increment
135  }
136  } else { // skip them
137  hoaChannel = numChannelsCurrentOrder;
138  }
139  // finished with the current order, increment and do again
140  currentOrder++;
141  }
142  return;
143 }
144 
145 
146 void AmbisonicUnitGenerator::invChannelIndexer(unsigned *indexArray) {
147 
148  AmbisonicOrder torder = mOrder;
149  unsigned *channelIndex = indexArray;
150 
151  // how many channels it actually needs for the hybrid order
152  unsigned nnumChannels = orderToChannels(torder);
153  channelIndex[0] = 0; // w channel
154 
155  unsigned n = 1;
156  unsigned currentOrder = 1;
157  unsigned numChannelsCurrentOrder;
158 
159  // work our way up the orders until we have exhausted all the required channels
160  while (n < nnumChannels) {
161  // horizontal indexing required?
162  if (currentOrder <= torder.horizontalOrder) {
163  // there are always 2 channels for horizontal encoding at each order
164  channelIndex[n] = n++;
165  channelIndex[n] = n++;
166  } else {
167  n += 2; // skip them
168  }
169  // how many channels are required up to the current order?
170  numChannelsCurrentOrder = orderToChannels(currentOrder);
171  // vertical indexing required?
172  if (currentOrder <= torder.verticalOrder) {
173  // do until all required vertical channels up to the current order are exhausted
174  while(n < numChannelsCurrentOrder) {
175  channelIndex[n] = n++; // set and increment
176  }
177  } else {
178  n = numChannelsCurrentOrder; // skip them
179  } // finished with the current order, increment and do again
180  currentOrder++;
181  }
182  return;
183 }
184 
185 
186 //////////////////////////////////////////////
187 /* AMBISONIC ENCODER IMPLEMENTATION */
188 //////////////////////////////////////////////
189 
190 /*******************CONSTRUCTORS & DESTRUCTOR*******************/
192 
193 AmbisonicEncoder::AmbisonicEncoder(SpatialSource &input, unsigned order) : AmbisonicUnitGenerator(order), mInputPort(NULL) {
194  setInput(input);
195  initialize();
196 }
197 
198 AmbisonicEncoder::AmbisonicEncoder(SpatialSource &input, unsigned horder, unsigned vorder) : AmbisonicUnitGenerator(horder, vorder), mInputPort(NULL) {
199  setInput(input);
200  initialize();
201 }
202 
204  delete [] mWeights; // de-allocate memory
205 
206  if (mInputPort != NULL) {
207  delete mInputPort;
208  }
209 }
210 
211 
212 /*******************INITIALIZE METHOD*******************/
213 
215 
216  // allocate memory for encoding weights
218  // initialize them
219  mWeights[0] = AMBI_INVSQRT2; // W channel weight is constant
220  for (unsigned i = 1; i < mNumChannels; i++) {
221  mWeights[i] = 0.f;
222  }
223 }
224 
225 /// Use to set the input to be encoded.
227 
228  if (mInputPort == NULL)
229  mInputPort = new Port(&input);
230 
231  if (mInputPort->mUGen != &input) { // Make sure no setting the same input.
232  mInputPort->mUGen->removeOutput(this); // Remove the encoder (slef) from the previous input ugen.
233  mInputPort->mUGen = &input; // mInputPort->setInput(input);
234 
235  input.addOutput(this); // be sure to add me as an output of the other guy
236  }
237 #ifdef CSL_DEBUG
238  logMsg("AmbisonicEncoder::setInput");
239 #endif
240 }
241 
242 /*******************NEXT_BUFFER METHOD*******************/
243 
244 // To get my next buffer, get a buffer from the input, and then "process" it...
245 void AmbisonicEncoder::nextBuffer(Buffer &outputBuffer, unsigned outBufNum) throw(CException) {
246 
247  unsigned numFrames = outputBuffer.mNumFrames; // Block size
248  Buffer *inputBuffer;
249  outputBuffer.zeroBuffers(); // Initialize output buffer with zeros
250 #ifdef CSL_DEBUG
251  logMsg("AmbisonicEncoder::nextBuffer");
252 #endif
253  if (outputBuffer.mNumChannels < mNumChannels) // Make sure the buffer passed has enough channels
254  logMsg(kLogError, "The AmbisonicEncoder requires %d output channels, found only %d ???\n", mNumChannels, outputBuffer.mNumChannels);
255 
256  // Get a pointer to the buffer of the sound source to be encoded
257 // inputBuffer = mInputPort->pullInput(numFrames);
258 // THE BLOCK BELOW WAS PULL_INPUT OF THE UGEN PORT...
259  inputBuffer = mInputPort->mBuffer; // else get the buffer
260  inputBuffer->mNumFrames = numFrames;
261  inputBuffer->mType = kSamples;
262  mInputPort->mUGen->nextBuffer(*inputBuffer); // and ask the UGen for nextBuffer()
263  inputBuffer->mIsPopulated = true;
264 
265  SpatialSource *input = (SpatialSource *)mInputPort->mUGen;
266 
267 // if (input->positionChanged()) {
268  // Get the encoding weights for this encoder
269  fumaEncodingWeights(mWeights, mOrder, input->azimuth(), input->elevation());
270 // }
271  // Start encoding audio from zeroth order; higher orders cascade from this function
272  for (unsigned i = 0; i < mNumChannels; i++) {
273  SampleBuffer inPtr = inputBuffer->buffer(0); // Reset the input pointer
274  SampleBuffer outPtr = outputBuffer.buffer(i); // Set a pointer to the output buffer
275 
276  for (unsigned j = 0; j < numFrames; j++) {
277  // Actual audio processing: encode sound sources following the Furse-Malham encoding convention
278  *outPtr++ += (*inPtr++) * mWeights[i]; // encoding W channel
279  }
280  }
281 }
282 
283 
284 ////////////////////////////////////////////
285 /* AMBISONIC DECODER IMPLEMENTATION */
286 ////////////////////////////////////////////
287 
288 
289 /*******************CONSTRUCTORS & DESTRUCTOR*******************/
290 
293  : AmbisonicUnitGenerator(input.order()), mInputPort(NULL), mSpeakerLayout(layout),
294  mDecodingMethod(method), mDecoderFlavour(flavour) {
295  initialize(input, method, flavour);
296 }
297 
300  : AmbisonicUnitGenerator(order), mInputPort(NULL),
301  mDecodingMethod(method), mDecoderFlavour(flavour) {
302  initialize(input, method, flavour);
303 }
304 
305 AmbisonicDecoder::AmbisonicDecoder(UnitGenerator &input, unsigned hOrder, unsigned vOrder,
307  : AmbisonicUnitGenerator(hOrder, vOrder), mInputPort(NULL),
308  mDecodingMethod(method), mDecoderFlavour(flavour) {
309  initialize(input, method, flavour);
310 }
311 
313  mInputPort->mUGen->removeOutput(this);
314  delete mInputPort;
315  delete [] mIOChannelMap;
316 
317  for (unsigned s = 0; s < mSpeakerLayout->numSpeakers(); s++)
318  delete [] mDecodingMatrix[s];
319  free(mDecodingMatrix);
320 }
321 
322 
323 /*******************INITIALIZE METHOD*******************/
324 
326 
327 // if (mSpeakerLayout == NULL) // if the user didn't set a layout then ask for the default layout.
328 // mSpeakerLayout = SpeakerLayout::defaultSpeakerLayout();
329 
330  mInputPort = new Port(&input);
331  input.addOutput(this); // be sure to add me as an output of the other guy
332 
333  unsigned numSpeakers = mSpeakerLayout->numSpeakers(); // get a local copy of the number of speakers.
334  AmbisonicOrder decodingOrder = mOrder;
335 
336  // are there enough speakers to decode this order?
337 // if (orderToChannels(decodingOrder) > numSpeakers) {
338 // // less speakers than necessary, so reduce decoding order by successively decrementing the vertical,
339 // // then horizontal order, until L >= N criterium applies
340 // while (orderToChannels(decodingOrder) > numSpeakers) {
341 // if (decodingOrder.verticalOrder >= decodingOrder.horizontalOrder) {
342 // --decodingOrder.verticalOrder;
343 // if (orderToChannels(decodingOrder) <= numSpeakers)
344 // break;
345 // }
346 // --decodingOrder.horizontalOrder;
347 // }
348 //
349 // logMsg(kLogWarning,
350 // "Reducing decoding order because of available number of speakers (%d): horizontal order = %i, vertical order = %i\n",
351 // numSpeakers, decodingOrder.horizontalOrder, decodingOrder.verticalOrder);
352 // }
353 
354  // are there enough encoded channels in the input to decode the specified order?
355  if (orderToChannels(decodingOrder) > input.numChannels()) {
356  // reduce order if not enough channels in the input ugen
357  while (orderToChannels(decodingOrder) > input.numChannels()) {
358  if (decodingOrder.verticalOrder >= decodingOrder.horizontalOrder) {
359  --decodingOrder.verticalOrder;
360  if (orderToChannels(decodingOrder) <= input.numChannels())
361  break;
362  }
363  --decodingOrder.horizontalOrder;
364  }
366  "Reducing decoding order because encoded input does not contain enough channels for specified order.\n Input channels:(%d): horizontal order = %i, vertical order = %i\n",
367  input.numChannels(), decodingOrder.horizontalOrder, decodingOrder.verticalOrder);
368  }
369 
370 
371  // recalculate the greater order & the uniformity
372  decodingOrder.isUniform = (decodingOrder.horizontalOrder == decodingOrder.verticalOrder);
373  mNumChannels = orderToChannels(decodingOrder); // how many input ambisonic channels needed at this order
374  // how many channels it would need *if* it was a uniform order (hOrder == vOrder)
375  unsigned numChannelsGreaterOrder = orderToChannels(greaterOrder(decodingOrder));
376  unsigned channelIndex[numChannelsGreaterOrder];
377  unsigned invChannelIndex[numChannelsGreaterOrder];
378 
379  // The invChannelIndexer doesn't go thru all channels, so make sure to set others to zero.
380  memset(invChannelIndex , 0, numChannelsGreaterOrder * sizeof(unsigned));
381 
382  channelIndexer(channelIndex); // get a channel indexer based on the input order
383  invChannelIndexer(invChannelIndex); // get an indexer from output framestream channel to 'ideal' ambisonic channel (after fixing the mOrder)
384 
385  mIOChannelMap = new int[mNumChannels];
386  for (unsigned n = 0; n < mNumChannels; n++) {
387 // cout << n << ": " << invChannelIndex[n] << endl;
388  mIOChannelMap[n] = channelIndex[invChannelIndex[n]];
389  }
390 
391  mDecodingMatrix = (sample**) malloc(numSpeakers * sizeof(sample*));
392  for (unsigned s = 0; s < numSpeakers; s++)
393  mDecodingMatrix[s] = new sample[mNumChannels];
394 
396  asPseudoInverse(); // build the Decoding matrix D using the pseudo inverse method
397  else
398  asProjection(); // build the Decoding matrix using the projection method
399 
400 
401  // process the decoding matrix according to the desired decoder flavour
402  if (mDecoderFlavour == kINPHASE) {
403  if ( ! decodingOrder.isUniform)
404  logMsg(kLogWarning, "AmbisonicDecoder: the in-phase decoding method is only well defined for non-hybrid systems - you have asked for an in phase decoder for a hybrid ambisonic decoding.\n");
405  makeInPhase(greaterOrder(decodingOrder));
406  }
407  else if (mDecoderFlavour == kMAXRE) {
408  if ( ! decodingOrder.isUniform)
409  logMsg(kLogWarning, "AmbisonicDecoder: the max-rE decoding method is only well defined for non-hybrid systems - you have asked for a max rE decoder for a hybrid ambisonic decoding.\n");
410  makeMaxRE(greaterOrder(decodingOrder));
411  }
412 
413 #ifdef CSL_DEBUG
414 
415  printf("Decoding from %i ambisonic channels to %i speakers", mNumChannels, numSpeakers);
416  printf(mDecodingMethod == kPSEUDOINVERSE ? ", using pseudo inverse method" : ", using projection method");
417  switch(mDecoderFlavour) {
418  case 0: printf(" of basic flavour\n"); break;
419  case 1: printf(" of in-phase flavour\n"); break;
420  case 2: printf(" of max rE flavour\n"); break;
421  }
422 
423  printf("Decoding Matrix:\n");
424  for (unsigned s=0; s< numSpeakers; s++ ) {
425  printf("speaker %d\t",s);
426  for (unsigned n=0; n < mNumChannels; n++) {
427  printf("%f\t\t", mDecodingMatrix[s][n]);
428  }
429  printf("\n");
430  }
431  printf("\n");
432 
433  printf("Created AmbisonicDecoder with horizontal order = %d, vertical order = %d\n", mOrder.horizontalOrder, mOrder.verticalOrder);
434 
435 #endif
436 
437 }
438 
439 
440 /*******************DECODING MATRIX METHODS*******************/
441 /*
442 B ... column vector of encoded Ambisonic channels, dimension N x 1
443 C ... re-encoding matrix, dimension N x L
444 C' ... transposed re-encoding matrix, dimension L x N
445 pinv(C) ... pseudoinverse of the re-encoding matrix C, dimension L x N
446 C * C' ... matrix product of the matrices C and C', dimension N x N
447 inv (C * C') ... inverse of C * C', dimension N x N
448 D ... decoding matrix, dimension L x N
449 p ... column vector of decoded loudspeaker driving signals, dimension L x 1
450 
451 The matching condition (reference soundfield = synthesized soundfield): B = C * p
452 gives the decoding equation: p = D * B
453 Where D can be calculated by the projection method, or by the pseudoinverse method
454 */
455 
456 /// build the Decoding matrix D using the projection method D = (1/L) * C'
458 
459  makeTransposedReEncodingMatrix(mDecodingMatrix); // get C' before scaling it down by 1/L
460 
461  unsigned numSpeakers = mSpeakerLayout->numSpeakers();
462  sample invNumSpeakers = 1.f/((float)numSpeakers); // inverse of number of speakers (1/L)
463 
464  // for each element in the matrix...
465  for (unsigned s = 0; s < numSpeakers; s++) {
466  for (unsigned i = 0; i < mNumChannels; i++) {
467  mDecodingMatrix[s][i] *= invNumSpeakers; // ...scale down matrix element by 1/L:
468  }
469  }
470 }
471 
472 /// Build the Decoding matrix D using the pseudo inverse method \n D = pinv(C) = C' * inv(C * C'). \n
473 /// Pseudo inverse code based on the matrix library found at: http://home1.gte.net/edwin2/Matrix/
475  makeTransposedReEncodingMatrix(mDecodingMatrix); // get C' before scaling it down by 1/L
476 
477  unsigned nnumChannels = mNumChannels;
478  unsigned numSpeakers = mSpeakerLayout->numSpeakers();
479 
480 
481  // build temporary 2D array of dimension N x N to be used in calculating the psuedo-inverse: (C * C') and inv(C * C')
482  SampleBufferVector squareMatrix = (sample**) malloc(nnumChannels * sizeof(sample*));
483  for (unsigned n = 0; n < nnumChannels; n++) {
484  squareMatrix[n] = new sample[nnumChannels];
485  }
486 
487  SampleBufferVector invSquareMatrix = (sample**) malloc(nnumChannels * sizeof(sample*));
488  for (unsigned n = 0; n < nnumChannels; n++) {
489  invSquareMatrix[n] = new sample[nnumChannels];
490  }
491 
492  // create temporary matrices
493  sample** transposedReEncodingMatrix = (sample**) malloc(numSpeakers * sizeof(sample*));
494  for (unsigned s = 0; s < numSpeakers; s++)
495  transposedReEncodingMatrix[s] = new sample[nnumChannels];
496 
497  makeTransposedReEncodingMatrix(transposedReEncodingMatrix);
498 
499  // calculate (C * C'):
500  for (unsigned n = 0; n < nnumChannels; n++) { // for each row of (C * C')...
501 
502  for (unsigned j = 0; j < nnumChannels; j++ ) { // for each column of (C * C')...
503  squareMatrix[n][j] = 0.f; // ... zero the new cell
504  // matrix multiplication: stepping along the row of C and down the column of C', summing as it goes
505  for (unsigned s = 0; s < numSpeakers; s++ ) {
506  // (C * C') - we read C' as C by swapping the rows and columns of C' in the first term
507  squareMatrix[n][j] += transposedReEncodingMatrix[s][n] * transposedReEncodingMatrix[s][j];
508  }
509  }
510  }
511 
512  // ***start of matrix inversion: inv(C * C')***
513 
514  SampleBuffer uu = new sample [nnumChannels * nnumChannels]; // more temporary variables
515  SampleBuffer vv = new sample [nnumChannels * nnumChannels];
516  SampleBuffer w = new sample [nnumChannels];
517  SampleBufferVector u = new sample*[nnumChannels];
518  SampleBufferVector v = new sample*[nnumChannels];
519 
520  for (unsigned i = 0; i < nnumChannels; i++) {
521  u[i] = &(uu[nnumChannels*i]);
522  v[i] = &(vv[nnumChannels*i]);
523  for (unsigned j = 0; j < nnumChannels; j++)
524  u[i][j] = squareMatrix[i][j];
525  }
526 
527  singularValueDecomposition(u, nnumChannels, nnumChannels, w, v); // singular value decomposition
528  sample wmax = 0.0; // maximum singular value.
529 
530  for (unsigned j = 0; j < nnumChannels; j++) {
531  if (w[j] > wmax)
532  wmax = w[j];
533  }
534 
535  sample wmin = wmax*1e-12; // epsilon
536  for (unsigned k = 0; k < nnumChannels; k++)
537  if (w[k] < wmin)
538  w[k] = 0.0;
539  else
540  w[k] = 1.0/w[k];
541 
542  for (unsigned i = 0; i < nnumChannels; i++) {
543  for (unsigned j = 0; j < nnumChannels; j++) {
544  invSquareMatrix[i][j] = 0.0;
545  for (unsigned k = 0; k < nnumChannels; k++)
546  invSquareMatrix[i][j] += v[i][k]*w[k]*u[j][k];
547  }
548  }
549  // clean up
550  delete [] w;
551  delete [] u;
552  delete [] v;
553  delete [] uu;
554  delete [] vv;
555 
556  // ***end of matrix inversion***
557 
558  // get decoding matrix C by pseudoinverting it, i.e. multiply transposed re-encoding matrix by matrix
559  // we just inverted: D = C' * inv(C * C')
560 
561  for (unsigned s = 0; s < numSpeakers; s++ ) { // for each row of mDecodingMatrix...
562  for (unsigned n = 0; n < nnumChannels; n++) { // for each column of mDecodingMatrix...
563  mDecodingMatrix[s][n] = 0.f; // ...zero the new cell
564  for (unsigned j = 0; j < nnumChannels; j++) { // for each column of C'...
565  // ... do operations to obtainD = C' * inv(C * C')
566  mDecodingMatrix[s][n] += transposedReEncodingMatrix[s][j] * invSquareMatrix[j][n];
567  }
568  }
569  }
570 
571  // Cleanup
572  for (unsigned n = 0; n < nnumChannels; n++) {
573  delete [] squareMatrix[n];
574  delete [] invSquareMatrix[n];
575  }
576  for (unsigned s = 0; s < numSpeakers; s++)
577  delete [] transposedReEncodingMatrix[s];
578 
579  free(squareMatrix);
580  free(invSquareMatrix);
581  free(transposedReEncodingMatrix);
582 }
583 
584 
585 /*******************TRANSPOSED RE-ENCODING MATRIX GENERATION METHOD*******************/
586 
588 {
589  unsigned numSpeakers = mSpeakerLayout->numSpeakers();
590  Speaker *speaker;
592  float **transposedReEncodingMatrix = transposeMatrix;
593 
594  // for each speaker
595  for (unsigned s = 0; s < numSpeakers; s++) {
596  speaker = mSpeakerLayout->speakerAtIndex(s); // get the details of this speaker
597 
598  // create a row in the LxN matrix containing the encoding weights (FuMa) for this speaker
599  transposedReEncodingMatrix[s][0] = AMBI_INVSQRT2; // W channel is always the same
600  fumaEncodingWeights(transposedReEncodingMatrix[s], order, speaker->azimuth(), speaker->elevation());
601  }
602 
603  return;
604 }
605 
606 
607 /*******************DECODING MATRIX FLAVOUR ADOPTION METHODS*******************/
608 
609 /// Scales the decoding matrix according to the factors for max rE decoding
610 void AmbisonicDecoder::makeMaxRE(unsigned greaterOrder) {
611  unsigned numSpeakers = mSpeakerLayout->numSpeakers();
612 
613  // hard-coded gain coefficients for in max-rE decoding (parameters specified up to 4th order):
614  sample maxREParameters[][5] = { {1.0, 1.0, 1.0, 1.0, 1.0},
615  // n = 0, M = 0, 1, 2, 3, 4
616  {0.f, 0.577, 0.775, 0.861, 0.906},
617  // n = 1, M = 0, 1, 2, 3, 4
618  {0.f, 0.f, 0.4, 0.612, 0.732},
619  // n = 2, M = 0, 1, 2, 3, 4
620  {0.f, 0.f, 0.f, 0.305, 0.501},
621  // n = 3, M = 0, 1, 2, 3, 4
622  {0.f, 0.f, 0.f, 0.f, 0.246},
623  // n = 4, M = 0, 1, 2, 3, 4
624  };
625 
626  // calculate gain factors for each order
627  unsigned M = greaterOrder; // the order we are assuming as the context
628 
629  for (unsigned i = 1; i < mNumChannels; i++) { // skip W channel as it's factor is always 1
630  for (unsigned l = 0; l < numSpeakers; l++) {
631  // channelsToUniformOrder(i)+1 is a convenient way to get n (the order of each ambisonic channel), but it is not defined for W
632  mDecodingMatrix[l][i] *= maxREParameters[channelsToUniformOrder(i)+1][M];
633  }
634  }
635 }
636 
637 /// Scales the decoding matrix according to the factors for in-phase decoding
638 void AmbisonicDecoder::makeInPhase(unsigned tgreaterOrder)
639 {
640  unsigned numSpeakers = mSpeakerLayout->numSpeakers();
641 
642  // hard-coded gain coefficients for in phase decoding (parameters specified up to 4th order)
643 
644  sample inPhaseParameters[][5] = { // n = 0, 1, 2, 3, 4, M = 0, 1, 2, 3, 4
645  {1.0, 1.0, 1.0, 1.0, 1.0},
646  {0.f, 0.333, 0.5, 0.6, 0.667},
647  {0.f, 0.f, 0.1, 0.2, 0.286},
648  {0.f, 0.f, 0.f, 0.029, 0.071},
649  {0.f, 0.f, 0.f, 0.f, 0.008},
650  };
651 
652  // calculate gain factors for each order
653  unsigned M = tgreaterOrder; // the order we are assuming as the context
654 
655  for (unsigned i = 1; i < mNumChannels; i++) { // skip W channel as its factor is always 1
656  for (unsigned l = 0; l < numSpeakers; l++) {
657  // channelsToUniformOrder(i)+1 is a convenient way to get n (the order of each ambisonic channel), but it is not defined for W
658  mDecodingMatrix[l][i] *= inPhaseParameters[channelsToUniformOrder(i)+1][M];
659  }
660  }
661 }
662 
663 
664 /*******************NEXT_BUFFER METHOD*******************/
665 
666 // To get my next buffer, get a buffer from the input, and then "process" it...
667 void AmbisonicDecoder::nextBuffer(Buffer &outputBuffer, unsigned outBufNum) throw(CException) {
668 
669  unsigned numChannels = mNumChannels;
670  unsigned numSpeakers = mSpeakerLayout->numSpeakers();
671  unsigned numFrames = outputBuffer.mNumFrames; // block size
672  Buffer *inputBuffer;
673 
674  if (outputBuffer.mNumChannels < numSpeakers) { // USED TO BE numChannels. I think it makes more sense to be numSpeakers.
675  logMsg(kLogError, "AmbisonicDecoder needs a buffer with %d channels, found only %d\n", numChannels, outputBuffer.mNumChannels);
676  throw RunTimeError("Insufficient number of channels in buffer passed.");
677  }
678 
679  outputBuffer.zeroBuffers(); // fill the output buffers with silence
680 
681 // inputBuffer = mInputPort->pullInput(numFrames);
682 // THE BLOCK BELOW WAS PULL_INPUT OF THE UGEN PORT...
683  inputBuffer = mInputPort->mBuffer; // else get the buffer
684  inputBuffer->mNumFrames = numFrames;
685  inputBuffer->mType = kSamples;
686  mInputPort->mUGen->nextBuffer(*inputBuffer); // and ask the UGen for nextBuffer()
687  inputBuffer->mIsPopulated = true;
688 
689 
690  // since all equations for all speakers & encoded channels are the same, they can be merged into a single loop:
691  for (unsigned n = 0; n < numChannels; n++) { // for each input ambisonic channel to be used
692  // get pointers to the encoded input channels (according to the channel indexer to accomodate hybrid orders)
693  SampleBuffer inPtr = inputBuffer->buffer(mIOChannelMap[n]);
694 
695  // for each speaker, for each order, for each sample & encoded channel
696  for (unsigned l = 0; l < numSpeakers; l++) {
697  SampleBuffer outPtr = outputBuffer.buffer(l); // get a pointer to the output channel
698 
699  // for each frame of the buffer
700  for (unsigned i = 0; i < numFrames; i++) {
701 
702  *outPtr++ += mDecodingMatrix[l][n] * inPtr[i];
703  }
704  }
705  }
706 
707  return;
708 }
sample * SampleBuffer
1-channel buffer data type, vector of (sample)
Definition: CSL_Types.h:194
AmbisonicDecoder(AmbisonicUnitGenerator &input, SpeakerLayout *layout=SpeakerLayout::defaultSpeakerLayout(), AmbisonicDecoderMethod method=kPROJECTION, AmbisonicDecoderFlavour flavour=kBASIC)
Defaults to standard speaker layout as defined in "HOA_SpeakerLayout" class and to Ambisonic order of...
Definition: Ambisonic.cpp:291
void logMsg(const char *format,...)
These are the public logging messages.
Definition: CGestalt.cpp:292
void setOrder(AmbisonicOrder order)
Returns the Ambisonic order.
Definition: Ambisonic.cpp:59
void singularValueDecomposition(sample **a, int m, int n, sample *w, sample **v)
unsigned mNumFrames
num frames used in each buffer
Definition: CSL_Core.h:113
void makeMaxRE(unsigned greaterOrder)
Adjusts the decoding matrix D for Max rE flavour.
Definition: Ambisonic.cpp:610
float elevation()
Returns the horizontal angle.
Definition: SpatialSource.h:57
void asProjection()
Create the decoding matrix "D" using the projection method.
Definition: Ambisonic.cpp:457
float elevation()
float azimuth()
AdditiveInstrument.h – Sum-of-sines synthesis instrument class.
Definition: Accessor.h:17
Speaker * speakerAtIndex(unsigned speakerIndex) const
Returns the speaker at the specified index.
Definition: SpeakerLayout.h:65
void channelIndexer(unsigned *indexArray)
Calculates a lookup table to map Ambisonic channel index to actually used UnitGenerator channel...
Definition: Ambisonic.cpp:98
unsigned orderToChannels(const AmbisonicOrder order)
Returns the number of Ambisonic channels from a hybrid Ambisonic order: N = 2*M_h + 1 + (M_v + 1)^2 -...
Definition: Ambisonic.cpp:75
Illegal operation at run time.
virtual SampleBuffer buffer(unsigned bufNum)
convenience accessors for sample buffers
Definition: CSL_Core.cpp:66
void fumaEncodingWeights(SampleBuffer weights, const AmbisonicOrder &order, sample azimuth, sample elevation)
Utility function that calculates fuma encoding weights for a given order, azimuth and elevation...
SampleBuffer mWeights
Encoding weights for each order (per source)
Definition: Ambisonic.h:139
unsigned greaterOrder(const AmbisonicOrder order)
Compares the horizontal and vertical Ambisonic order of a hybrid order and returns the largest...
Definition: Ambisonic.cpp:71
void addOutput(UnitGenerator *ugen)
add to or return the UGen vector of outputs
Definition: CSL_Core.cpp:670
AmbisonicDecoderFlavour mDecoderFlavour
Definition: Ambisonic.h:204
#define AMBI_INVSQRT2
Definition: Ambisonic.cpp:25
AmbisonicUnitGenerator(unsigned order=0)
Initialize with uniform Ambisonic order. Defaults to zeroth order.
Definition: Ambisonic.cpp:34
void zeroBuffers()
fill all data with 0
Definition: CSL_Core.cpp:173
Ambisonic order structure (separate definition for horizontal and vertical order): ...
Definition: Ambisonic.h:57
virtual void nextBuffer(Buffer &outputBuffer, unsigned outBufNum)
Does the DSP processing for the Ambisonic Decoder.
Definition: Ambisonic.cpp:667
Represents a speaker as a position relative to the center of a space.
unsigned orderToHorizontalChannels(const AmbisonicOrder order)
Returns the number of horizontal Ambisonic channels from a hybrid Ambisonic order: N_h = 2*M_h + 1...
Definition: Ambisonic.cpp:85
AmbisonicOrder order()
Definition: Ambisonic.h:78
void asPseudoInverse()
create the decoding matrix "D" using the pseudoinverse method
Definition: Ambisonic.cpp:474
bool isUniform
Returns true if horizontal and verical orders are identical.
Definition: Ambisonic.h:64
void initialize(UnitGenerator &input, AmbisonicDecoderMethod method, AmbisonicDecoderFlavour flavour)
initializing method called by constructors
Definition: Ambisonic.cpp:325
unsigned horizontalOrder
Definition: Ambisonic.h:60
virtual unsigned numChannels()
Definition: CSL_Core.h:252
SpatialSource * input()
Definition: Ambisonic.h:133
void makeTransposedReEncodingMatrix(float **transposeMatrix)
Utility method that creates the transposed re-encoding matrix C'.
Definition: Ambisonic.cpp:587
Temp Spatial Sound Source.
Definition: SpatialSource.h:29
float sample
(could be changed to int, or double)
Definition: CSL_Types.h:191
unsigned mNumChannels
my "expected" number of output channels
Definition: CSL_Core.h:292
~AmbisonicDecoder()
Destructor.
Definition: Ambisonic.cpp:312
void makeInPhase(unsigned greaterOrder)
Adjusts the decoding matrix D for in-phase flavour.
Definition: Ambisonic.cpp:638
BufferContentType mType
Data type flag set the internal size variables (no buffer allocation takes place) ...
Definition: CSL_Core.h:124
virtual ~AmbisonicEncoder()
Destructor.
Definition: Ambisonic.cpp:203
void initialize()
Initializing method called by constructors.
Definition: Ambisonic.cpp:214
float azimuth()
Sets the distance from the center.
Definition: SpatialSource.h:56
void fumaEncodingWeights(SampleBuffer weights, const AmbisonicOrder &order, float azimuth, float elevation)
AmbisonicDecoderFlavour
Flag for the decoder flavour.
Definition: Ambisonic.h:153
unsigned orderToVerticalChannels(const AmbisonicOrder order)
Returns the number of vertical Ambisonic channels from a hybrid Ambisonic order: N_v = (M_v + 1)^2 - ...
Definition: Ambisonic.cpp:90
void invChannelIndexer(unsigned *indexArray)
Calculates a lookup table to map actually used UnitGenerator channel to Ambisonic channel index...
Definition: Ambisonic.cpp:146
SpeakerLayout * mSpeakerLayout
Definition: Ambisonic.h:202
void setCopyPolicy(BufferCopyPolicy ch)
get/set the receiver's buffer copy policy
Definition: CSL_Core.h:256
UnitGenerator * mUGen
my unit generator (pointer or NULL)
Definition: CSL_Core.h:320
void setInput(SpatialSource &input)
Set my input.
Definition: Ambisonic.cpp:226
Buffer – the multi-channel sample buffer class (passed around between generators and IO guys)...
Definition: CSL_Core.h:106
unsigned numSpeakers() const
Definition: SpeakerLayout.h:58
ignore extra buffer channels
Definition: CSL_Core.h:213
AmbisonicEncoder()
Default constructor.
Definition: Ambisonic.cpp:191
Ambisonic Abstract Base Class.
Definition: Ambisonic.h:69
AmbisonicOrder mOrder
the order of the Unit Generator
Definition: Ambisonic.h:83
AmbisonicDecoderMethod
Flag for the decoding method.
Definition: Ambisonic.h:146
SampleBuffer * SampleBufferVector
Multi-channel buffer data type, vector of (SampleBuffer)
Definition: CSL_Types.h:195
Port – used to represent constant, control-rate or signal inputs and outputs in named maps; holds a ...
Definition: CSL_Core.h:312
AmbisonicDecoderMethod mDecodingMethod
Definition: Ambisonic.h:203
void removeOutput(UnitGenerator *ugen)
Definition: CSL_Core.cpp:680
virtual void nextBuffer(Buffer &outputBuffer, unsigned outBufNum)
Does the DSP processing for the Ambisonic Encoder.
Definition: Ambisonic.cpp:245
forward declaration
Definition: CSL_Core.h:241
Regular audio samples.
Definition: CSL_Core.h:72
unsigned channelsToUniformOrder(const unsigned channels)
Definition: Ambisonic.cpp:66
Port * mInputPort
Holds the input to be encoded.
Definition: Ambisonic.h:140
SampleBufferVector mDecodingMatrix
Definition: Ambisonic.h:201
unsigned verticalOrder
Definition: Ambisonic.h:63
bool mIsPopulated
does the buffer have data?
Definition: CSL_Core.h:122
Base class of CSL exceptions (written upper-case). Has a string message.