CSL  6.0
AmbisonicUtilities.cpp
Go to the documentation of this file.
1 //
2 // HOA_Utilities.cpp -- Higher Order Ambisonic utility classes
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 // Utility classes used by HOA_Encoder, MOA_Encoder, AmbisonicRotator, MOA_Rotator, HOA_Decoder
7 
8 #include "AmbisonicUtilities.h"
9 
10 #include <stdlib.h>
11 #include <stdio.h>
12 #include <math.h>
13 
14 using namespace csl;
15 
16 ////////////////////////////////////////////
17 /* AMBISONIC MIXER IMPLEMENTATION */
18 ////////////////////////////////////////////
19 
20 /*******************CONSTRUCTORS & DESTRUCTOR*******************/
21 
23  initialize();
24 }
25 
26 AmbisonicMixer::AmbisonicMixer(unsigned hOrder, unsigned vOrder) : AmbisonicUnitGenerator(hOrder, vOrder) {
27  initialize();
28 }
29 
31 
32 
33 /*******************INITIALIZE METHOD*******************/
34 
36 {
37  // allocate memory for source signal buffers according to number of channels required at this order
40 
41 #ifdef CSL_DEBUG
42  logMsg("Created an AmbisonicMixer with horizontal order = %d, vertical order = %d\n", mOrder.horizontalOrder, mOrder.verticalOrder);
43 #endif
44 
45 }
46 
48 
49  if (mNumChannels <= input.numChannels()) { // Make sure the input has at least the number of channels needed for the order specified
50 
51  mInputs.push_back(&input); // add the new input
52 
53  // calculate inverse of the number of input sound sources (used for normalization)
54  mInvNumInputs = 1.f/(float)mInputs.size();
55 
56 #ifdef CSL_DEBUG
57  logMsg("AmbisonicPanner: added %ith input\n", mInputs.size());
58 #endif
59  } else {
60  logMsg(kLogError, "AmbisonicMixer: cannot add input to a mixer of %ix%i order.\n", mOrder.horizontalOrder, mOrder.verticalOrder);
61  }
62  return;
63 }
64 
66 
67  AmbisonicOrder inputOrder = input.order();
68  if (inputOrder.horizontalOrder >= mOrder.horizontalOrder && inputOrder.verticalOrder >= mOrder.verticalOrder) {
69  mInputs.push_back(&input); // add the new input
70 
71  // calculate inverse of the number of input sound sources (used for normalization)
72  mInvNumInputs = 1.f/(float)mInputs.size();
73 
74 #ifdef CSL_DEBUG
75  logMsg("AmbisonicPanner: added %ith input\n", mInputs.size());
76 #endif
77 
78  } else {
79  logMsg(kLogError, "AmbisonicMixer: cannot add input of %ix%i order to a mixer of %ix%i order.\n", inputOrder.horizontalOrder, inputOrder.verticalOrder, mOrder.horizontalOrder, mOrder.verticalOrder);
80  }
81  return;
82 
83 }
84 
85 /*******************NEXT_BUFFER METHOD*******************/
86 
87 // To get my next buffer, get a buffer from the input, and then "process" it...
88 void AmbisonicMixer::nextBuffer(Buffer &outputBuffer, unsigned outBufNum) throw(CException) {
89  SampleBuffer outPtr, inPtr;
90  unsigned i, c, k, numFrames;
91  unsigned numInputs = mInputs.size();
92 
93  mInBuffer->mNumFrames = numFrames = outputBuffer.mNumFrames; // block size
94 
95  outputBuffer.zeroBuffers(); // initialize output buffer with zeros
96 
97 #ifdef CSL_DEBUG
98  logMsg("AmbisonicMixer::nextBuffer");
99 #endif
100 
101  if (numInputs) { // if there are no inputs, then the outputbuffer can remain as zeroes.
102 
103  if (outputBuffer.mNumChannels < mNumChannels)
104  logMsg(kLogError, "AmbisonicMixer requires %d output channels, found only %d ???\n", mNumChannels, outputBuffer.mNumChannels);
105 
106  // loop through each input in turn, adding it's next_buffer output onto the outputbuffer channel by channel
107  for (i = 0; i < numInputs; i++) {
108  // pointer to the current input framestream
109  UnitGenerator * input = mInputs[i];
110 
111  if (input->isActive()) { // if active input found, get a buffer and sum it into the output
112 
113  // fill up mInBuffer with the ambisonic encoded audio from the current input
114  input->nextBuffer(*mInBuffer);
115 
116  for (c = 0; c < mNumChannels; c++) { // loops through channels
117  outPtr = outputBuffer.buffer(c);
118  inPtr = mInBuffer->buffer(c);
119  for (k = 0; k < numFrames; k++) // loops through sample buffers
120  *outPtr++ += *inPtr++;
121  }
122  }
123  } // end of input loop
124 
125  // after mixing, scale down our encoded output by the number of encoded sources to avoid clipping:
126  // for each actual audio output channel (not each ambisonic channel of the greater order)...
127  for (c = 0; c < mNumChannels; c++) {
128  outPtr = outputBuffer.buffer(c); // reset the outPtr to the beginning of this channel in the outputbuffer
129 
130  for (i = 0; i < numFrames; i++) // ...scale this channels' output buffer down
131  *outPtr++ *= mInvNumInputs;
132  }
133  } // end of if (numInputs)
134 
135  return;
136 }
137 
138 
139 ////////////////////////////////////////////
140 /* AMBISONIC ROTATOR IMPLEMENTATION */
141 ////////////////////////////////////////////
142 
143 
144 /*******************CONSTRUCTORS & DESTRUCTOR*******************/
145 
147 
148  initialize(input);
149 }
150 
152 
153  initialize(input);
154 }
155 
156 AmbisonicRotator::AmbisonicRotator(UnitGenerator &input, unsigned horder, unsigned vorder) : AmbisonicUnitGenerator(horder, vorder) {
157 
158  initialize(input);
159 }
160 
162 
163  mInputPort->mUGen->removeOutput(this);
164  delete mInputPort;
165 
166  delete [] mSinAngle;
167  delete [] mCosAngle;
168 
169  delete [] mInputChannelIndex;
170  delete [] mChannelIndex;
171 
172  for (int i = 0; i < mNumChannels; i++) { // one sample pointer for each ambisonic output channel
173  if (mOutPtr[i])
174  delete mOutPtr[i];
175  if(mInPtr[i])
176  delete mInPtr[i];
177  }
178 
179  free(mOutPtr);
180  free(mInPtr);
181 
182 }
183 
184 
185 /*******************INITIALIZE METHOD*******************/
186 
188  mInputPort = new Port(&input);
189  input.addOutput(this); // be sure to add me as an output of the other guy
190 
191  // are there enough encoded channels in the input to rotate the specified order?
192  if (orderToChannels(mOrder) > input.numChannels()) {
193  // reduce order if not enough channels in the input ugen
194  while (orderToChannels(mOrder) > input.numChannels()) {
197  if (orderToChannels(mOrder) <= input.numChannels())
198  break;
199  }
200 
202  }
203 
204  logMsg(kLogWarning, "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",
206  }
207 
208  // recalculate the greater order & the uniformity (overriding values calculated in inherited HOA_AmbisonicFramestream constructor)
211  mNumChannels = orderToChannels(mOrder); // derive necessary output channels from ambisonic order
212  mNumChannelsGreaterOrder = orderToChannels(mGreaterOrder); // channels required for uniform greater order
213 
215  channelIndexer(mInputChannelIndex); // get a channel indexer based on the input order
216 
217  mChannelIndex = new unsigned[mNumChannelsGreaterOrder];
218  channelIndexer(mChannelIndex); // get a channel indexer based on the output order (after fixing the mOrder)
219 
220  mShouldRotate = mShouldTurn = mShouldTilt = false; // default initialise to no rotational activity
221 
222  mOutPtr = (sample**) malloc(mNumChannels * sizeof(sample*)); // Allocating memory for audio output pointers.
223  mInPtr = (sample**) malloc(mNumChannels * sizeof(sample*)); // Allocating memory for audio output pointers.
224  for (unsigned i = 0; i < mNumChannels; i++) { // one sample pointer for each ambisonic output channel
225  mOutPtr[i] = new sample;
226  mInPtr[i] = new sample;
227  }
228 
229  mSinAngle = new sample[mGreaterOrder]; // create arrays of samples to store sin & cosines of tilt, tumble & rotate angles
230  mCosAngle = new sample[mGreaterOrder]; // used in the rotator DSP loop
231 
232 #ifdef CSL_DEBUG
233  logMsg("Created AmbisonicRotator with horizontal order = %d, vertical order = %d\n", mOrder.horizontalOrder, mOrder.verticalOrder);
234 #endif
235 }
236 
237 
238 /*******************SET_N-TH_INPUT METHOD*******************/
239 
240 // function to set framestream sources for rotation, til tand tumble
241 void AmbisonicRotator::setNthInput(float amount, Axes axis)
242 {
243  // for example, use setNthInput(sine, kTILT);
244  switch (axis) {
245  case kTILT:
246  setTilt(amount);
247  break;
248  case kTUMBLE:
249  setTumble(amount);
250  break;
251  case kROTATE:
252  setRotate(amount);
253  break;
254  default:
255  logMsg(kLogError, "AmbisonicRotator; Attempted to add control signal to invalid axis channel ???\n");
256  }
257 }
258 
259 void AmbisonicRotator::setTilt(float amount) {
260  if (mOrder.isUniform) {
261  mTilt = amount;
262  if (amount)
263  mShouldTilt = true;
264  else
265  mShouldTilt = false; // if amount == 0 then don't even do the operations. Nothing to be changed!
266  } else {
267  logMsg(kLogWarning, "Attempt to set Tilt control for a hybrid order rotator failed.\n");
268  }
269 }
270 void AmbisonicRotator::setTumble(float amount) {
271  if (mOrder.isUniform) {
272  mTumble = amount;
273 
274  if (amount)
275  mShouldTurn = true;
276  else
277  mShouldTurn = false; // if amount == 0 then don't even do the operations. Nothing to be changed!
278  } else {
279  logMsg(kLogWarning, "Attempt to set Tumble control for a hybrid order rotator failed.\n");
280  }
281 }
282 void AmbisonicRotator::setRotate(float amount) {
283 
284  mRotate = amount;
285 
286  if (amount)
287  mShouldRotate = true;
288  else
289  mShouldRotate = false; // if amount == 0 then don't even do the operations. Nothing to be changed!
290 
291 }
292 
293 
294 /*******************NEXT_BUFFER METHOD*******************/
295 
296 // Overriding Framestream::nextBuffer(); To get my next buffer, get a buffer from the input, and then "process" it...
297 void AmbisonicRotator::nextBuffer(Buffer &outputBuffer, unsigned outBufNum) throw(CException) {
298  unsigned numFrames = mNumFrames = outputBuffer.mNumFrames; // block size
299  unsigned numChannels = mNumChannels;
300  unsigned greaterOrder = mGreaterOrder;
301  unsigned i; // to be used as the index in several for loops.
302  Buffer *inputBuffer;
303 
304 
305  if (outputBuffer.mNumChannels < numChannels) { // make sure that enough output channels were provided
306  logMsg(kLogError, "AmbisonicRotator needs a buffer with %d channels, found only %d\n", numChannels, outputBuffer.mNumChannels);
307  throw RunTimeError("Insufficient number of channels in buffer passed.");
308  }
309 
310 // inputBuffer = mInputPort->pullInput(numFrames);
311 // THE BLOCK BELOW WAS PULL_INPUT OF THE UGEN PORT...
312  inputBuffer = mInputPort->mBuffer; // else get the buffer
313  inputBuffer->mNumFrames = numFrames;
314  inputBuffer->mType = kSamples;
315  mInputPort->mUGen->nextBuffer(*inputBuffer); // and ask the UGen for nextBuffer()
316  inputBuffer->mIsPopulated = true;
317 
318  // Get a buffer from the input signal
319 // _input->nextBuffer(outputBuffer);
320 
321  // assign our in & out pointers to their respective channels of input & output buffers
322  for (i = 0; i < mNumChannelsGreaterOrder; i++) {
323  mOutPtr[i] = outputBuffer.buffer(mChannelIndex[i]);
324  }
325 
326  for (i = 0; i < numChannels; i++) {
327  mInPtr[i] = inputBuffer->buffer(mInputChannelIndex[i]);
328  }
329 
330  // First, copy the input encoded audio to the output, ready for rotations if required
331  for (i = 0; i < numFrames; i++) //
332  for (unsigned j = 0; j < numChannels; j++)
333  *mOutPtr[j]++ = *mInPtr[j]++;
334 
335  // do tilt processing if required
336  if (mShouldTilt) {
337  // reset outPtr to the begining of the buffer
338  for (i = 0; i < numChannels; i++)
339  mOutPtr[i] = outputBuffer.buffer(mChannelIndex[i]);
340 
341  // set the sin & cosines of i multiples of the tilt angles
342  for (i = 0; i < greaterOrder; i++) {
343  mSinAngle[i] = sinf((i+1) * mTilt);
344  mCosAngle[i] = cosf((i+1) * mTilt);
345  }
346 
347  // do the actual calculations
348  tiltFirstOrder();
349 
350  }
351 
352  // do tumble processing if required
353  if (mShouldTurn) {
354  // reset outPtr to the begining of the buffer
355  for (i = 0; i < numChannels; i++)
356  mOutPtr[i] = outputBuffer.buffer(mChannelIndex[i]);
357 
358  // set the sin & cosines of i multiples of the tilt angles
359  for (i = 0; i < greaterOrder; i++) {
360  mSinAngle[i] = sinf((i+1) * mTumble);
361  mCosAngle[i] = cosf((i+1) * mTumble);
362  }
363 
364  // do the actual calculations
365  tumbleFirstOrder();
366 
367  }
368 
369  // do the rotation processing if required
370  if (mShouldRotate) {
371  // reset outPtr to the begining of the buffer
372  for (i = 0; i < numChannels; i++)
373  mOutPtr[i] = outputBuffer.buffer(mChannelIndex[i]);
374 
375  // set the sin & cosines of i multiples of the tilt angles
376  for (i = 0; i < greaterOrder; i++) {
377  mSinAngle[i] = sinf((i+1) * mRotate);
378  mCosAngle[i] = cosf((i+1) * mRotate);
379  }
380 
381  // do the actual calculations - checking for hyrbid orders:
382  if (mOrder.horizontalOrder)
383  rotateFirstOrderHorizontal();
384  // note that there is no 1st order vertical calculation necessary for rotation
385  if (mOrder.verticalOrder > 1)
386  rotateSecondOrderVertical();
387 
388  }
389 
390  // done
391  return;
392 
393 }
394 
395 /*******************TILT, TUMBLE, ROTATE METHODS*******************/
396 
398  SampleBufferVector outPtr = mOutPtr; // create local copy of output pointer
399  sample temp1;
400 
401  for (unsigned i = 0; i < mNumFrames; i++) {
402  // W channel (0) remains unchanged
403  // X channel (1) remains unchanged
404  temp1 = *outPtr[2]; // needed to avoid recursive error
405  *outPtr[2]++ = mCosAngle[0] * (*outPtr[2]) - mSinAngle[0] * (*outPtr[3]);
406  *outPtr[3]++ = mSinAngle[0] * temp1 + mCosAngle[0] * (*outPtr[3]);
407 
408  }
409 
410 }
411 
413 {
414  SampleBufferVector outPtr = mOutPtr; // create local copy of output pointer
415  sample temp1, temp2, temp3; // needed to avoid recursive error
416 
417  for (unsigned i = 0; i < mNumFrames; i++) {
418  temp1 = *outPtr[4]; // needed to avoid recursive error
419  temp2 = *outPtr[5];
420  temp3 = *outPtr[7];
421 
422  *outPtr[4]++ = 0.5 * (mCosAngle[1] - 1) * (*outPtr[8]) + 0.5 * mSinAngle[1] * (*outPtr[7]) + 0.25 * (mCosAngle[1] + 3) * (*outPtr[4]);
423  *outPtr[5]++ = -mSinAngle[0] * (*outPtr[6]) + mCosAngle[0] * (*outPtr[5]);
424  *outPtr[6]++ = mCosAngle[0] * (*outPtr[6]) + mSinAngle[0] * temp2;
425  *outPtr[7]++ = -mSinAngle[1] * (*outPtr[8]) + mCosAngle[1] * (*outPtr[7]) - (0.5 * mSinAngle[1]) * temp1;
426  *outPtr[8]++ = 0.25 * (1 + 3 * mCosAngle[1]) * (*outPtr[8]) + 0.75 * mSinAngle[1] * temp3 + 0.375 * (mCosAngle[1] - 1) * temp1;
427 
428  }
429 
430  if (mGreaterOrder > 2) tiltThirdOrder();
431 
432 }
433 
435 {
436  SampleBufferVector outPtr = mOutPtr; // create local copy of output pointer
437  sample temp1, temp2, temp3, temp4, temp5;
438 
439  for (unsigned i = 0; i < mNumFrames; i++) {
440  // temporaries needed to avoid recursive error
441  temp1 = *outPtr[9];
442  temp2 = *outPtr[10];
443  temp3 = *outPtr[11];
444  temp4 = *outPtr[12];
445  temp5 = *outPtr[14];
446 
447  // The source for the 3rd order tilt matrix is Zmoelnig's thesis, taking into consideration his different channel naming convention!
448  *outPtr[9]++ = 0.125 * (3 * mCosAngle[1] + 5) * (*outPtr[9]) + 1.5 * mSinAngle[1] * (*outPtr[12]) + 0.515625 * (mCosAngle[1] - 1) * (*outPtr[13]); // P channel
449  *outPtr[10]++ = 0.0625 * (15 * mCosAngle[0] + mCosAngle[2]) * (*outPtr[10]) - 0.375 * (5 * mSinAngle[0] + mSinAngle[2]) * (*outPtr[11]) - 0.2578125 * (mCosAngle[0] - mCosAngle[2]) * (*outPtr[14]) + 0.25 * (3 * mSinAngle[0] - mSinAngle[2]) * (*outPtr[15]); // Q channel
450  *outPtr[11]++ = 0.0625 * (5 * mSinAngle[0] + mSinAngle[2]) * temp2 + 0.125 * (5 * mCosAngle[0] + 3 * mCosAngle[2]) * (*outPtr[11]) + 0.0859375 * (- mSinAngle[0] + 3 * mSinAngle[2]) * (*outPtr[14]) + 0.25 * (- mCosAngle[0] + mCosAngle[2]) * (*outPtr[15]); // N channel
451  *outPtr[12]++ = - 0.25 * mSinAngle[1] * temp1 + mCosAngle[1] * (*outPtr[12]) - 0.34375 * mSinAngle[1] * (*outPtr[13]); // O channel
452  *outPtr[13]++ = 0.454545454545 * (mCosAngle[1] - 1) * temp1 + + 1.818181818182 * mSinAngle[1] * temp4 + 0.125 * (3 + 5 * mCosAngle[1]) * (*outPtr[13]); // L channel
453  *outPtr[14]++ = 0.227272727273 * (- mCosAngle[0] + mCosAngle[2]) * temp2 + 0.454545454545 * (mSinAngle[0] - 3 * mSinAngle[2]) * temp3 + 0.0625 * (mCosAngle[0] + 15 * mCosAngle[2]) * (*outPtr[14]) - 0.181818181818 * (mSinAngle[0] + 5 * mSinAngle[2]) * (*outPtr[15]); // M channel
454  *outPtr[15]++ = 0.15625 * (- 3 * mSinAngle[0] + mSinAngle[2]) * temp2 + 0.9375 * (- mCosAngle[0] + mCosAngle[2]) * temp3 + 0.12890625 * (mSinAngle[0] + 5 * mSinAngle[2]) * temp5 + 0.125 * (3 * mCosAngle[0] + 5 * mCosAngle[2]) * (*outPtr[15]); // K channel
455 
456  }
457  // if (mOrder.greaterOrder > 3) tiltFourthOrder();
458 }
459 
460 
462  SampleBufferVector outPtr = mOutPtr; // create local copy of output pointer
463  sample temp1;
464 
465  for (unsigned i = 0; i < mNumFrames; i++) {
466  temp1 = *outPtr[1]; // needed to avoid recursive error
467  // W channel (0) remains unchanged
468  *outPtr[1]++ = mCosAngle[0] * (*outPtr[1]) - mSinAngle[0] * (*outPtr[3]);
469  // Y channel (2) remains unchanged
470  *outPtr[3]++ = mSinAngle[0] * temp1 + mCosAngle[0] * (*outPtr[3]);
471  }
472 }
473 
475 
476  SampleBufferVector outPtr = mOutPtr; // create local copy of output pointer
477  sample temp1, temp2, temp3;
478 
479  for (unsigned i = 0; i < mNumFrames; i++) {
480  // temporaries needed to avoid recursive error
481  temp1 = *outPtr[4];
482  temp2 = *outPtr[5];
483  temp3 = *outPtr[6];
484 
485  *outPtr[4]++ = 0.5 * (1 - mCosAngle[1]) * (*outPtr[8]) - 0.5 * mSinAngle[1] * (*outPtr[6]) + 0.25 * (3 + mCosAngle[1]) * (*outPtr[5]);
486  *outPtr[5]++ = -mSinAngle[0] * (*outPtr[7]) + mCosAngle[0] * (*outPtr[5]);
487  *outPtr[6]++ = -mSinAngle[1] * (*outPtr[8]) + mCosAngle[1] * (*outPtr[6]) + 0.5 * mSinAngle[1] * temp1;
488  *outPtr[7]++ = mCosAngle[0] * (*outPtr[7]) + mSinAngle[0] * temp2;
489  *outPtr[8]++ = 0.25 * (1 + 3 * mCosAngle[1]) * (*outPtr[8]) + 0.75 * mSinAngle[1] * temp3 + 0.375 * (1 - mCosAngle[1]) * temp1;
490  }
491  if (mGreaterOrder > 2) tumbleThirdOrder();
492 }
493 
495 {
496  SampleBufferVector outPtr = mOutPtr; // create local copy of output pointer
497  sample temp1, temp2, temp3, temp4, temp5;
498 
499  for (unsigned i = 0; i < mNumFrames; i++) {
500  // temporaries needed to avoid recursive error
501  temp1 = *outPtr[9];
502  temp2 = *outPtr[10];
503  temp3 = *outPtr[11];
504  temp4 = *outPtr[12];
505  temp5 = *outPtr[13];
506 
507  // The source for the 3rd order tumble matrix is Zmoelnig's thesis, taking into consideration his different channel naming convention!
508  *outPtr[9]++ = 0.0625 * (15 * mCosAngle[0] + mCosAngle[2]) * (*outPtr[9]) - 0.375 * (5 * mSinAngle[0] + mSinAngle[2]) * (*outPtr[11]) + 0.2578125 * (mCosAngle[0] - mCosAngle[2]) * (*outPtr[13]) + 0.25 * (- 3 * mSinAngle[0] + mSinAngle[2]) * (*outPtr[15]); // P channel
509  *outPtr[10]++ = 0.125 * (5 + 3 * mCosAngle[1]) * (*outPtr[10]) - 1.5 * mSinAngle[1] * (*outPtr[12]) + 0.515625 * (1 - mCosAngle[1]) * (*outPtr[14]); // Q channel
510  *outPtr[11]++ = 0.0625 * (5 * mSinAngle[0] + mSinAngle[2]) * temp1 + 0.125 * (5 * mCosAngle[0] + 3 * mCosAngle[2]) * (*outPtr[11])+ 0.0859375 * (mSinAngle[0] - 3 * mSinAngle[2]) * (*outPtr[13]) + 0.25 * (mCosAngle[0] - mCosAngle[2]) * (*outPtr[15]); // N channel
511  *outPtr[12]++ = 0.25 * mSinAngle[1] * temp2 + mCosAngle[1] * (*outPtr[12]) - 0.34375 * mSinAngle[1] * (*outPtr[14]); // O channel
512  *outPtr[13]++ = 0.227272727273 * (mCosAngle[0] - mCosAngle[2]) * temp1 + 0.454545454545 * (- mSinAngle[0] + 3 * mSinAngle[2]) * temp3 + 0.0625 * (mCosAngle[0] + 15 * mCosAngle[2]) * (*outPtr[13]) - 0.181818181818 * (mSinAngle[0] + 5 * mSinAngle[2]) * (*outPtr[15]); // L channel
513  *outPtr[14]++ = 0.454545454545 * (1 - mCosAngle[1]) * temp2 + 1.818181818182 * mSinAngle[1] * temp4 + 0.125 * (3 + 5 * mCosAngle[1]) * (*outPtr[14]); // M channel
514  *outPtr[15]++ = 0.15625 * (3 * mSinAngle[0] - mSinAngle[2]) * temp1 + 0.9375 * (mCosAngle[0] - mCosAngle[2]) * temp3 + 0.12890625 * (mSinAngle[0] + 5 * mSinAngle[2]) * temp5 + 0.125 * (3 * mCosAngle[0] + 5 * mCosAngle[2]) * (*outPtr[15]); // K channel
515 
516  }
517  //if (mOrder.greaterOrder > 3) tumbleFourthOrder();
518 }
519 
520 
521 
523 {
524  SampleBufferVector outPtr = mOutPtr; // create local copy of output pointer
525  sample temp1;
526 
527  for (unsigned i = 0; i < mNumFrames; i++) {
528  temp1 = *outPtr[1]; // needed to avoid recursive error
529  // W channel (0) remains unchanged
530  *outPtr[1]++ = mCosAngle[0] * (*outPtr[1]) - mSinAngle[0] * (*outPtr[2]);
531  *outPtr[2]++ = mSinAngle[0] * temp1 + mCosAngle[0] * (*outPtr[2]);
532  // Z channel (3) remains unchanged
533  }
534 
536 }
537 
538 // note that 1st order vertical for rotation is not required
539 
541 {
542  SampleBufferVector outPtr = mOutPtr; // create local copy of output pointer
543  sample temp1;
544 
545  for (unsigned i = 0; i < mNumFrames; i++) {
546  temp1 = *outPtr[4]; // needed to avoid recursive error
547  *outPtr[4]++ = mCosAngle[1] * (*outPtr[4]) - mSinAngle[1] * (*outPtr[5]); // U Channel
548  *outPtr[5]++ = mSinAngle[1] * temp1 + mCosAngle[1] * (*outPtr[5]); // V Channel
549  }
551 }
552 
554 {
555  SampleBufferVector outPtr = mOutPtr; // create local copy of output pointer
556  sample temp1;
557 
558  for (unsigned i = 0; i < mNumFrames; i++) {
559  temp1 = *outPtr[6]; // needed to avoid recursive error
560  *outPtr[6]++ = mCosAngle[0] * (*outPtr[6]) - mSinAngle[0] * (*outPtr[7]); // S channel
561  *outPtr[7]++ = mSinAngle[0] * temp1 + mCosAngle[0] * (*outPtr[7]); // T channel
562  // R channel (8) remains unchanged
563  }
565 }
566 
568 {
569  SampleBufferVector outPtr = mOutPtr; // create local copy of output pointer
570  sample temp1;
571 
572  for (unsigned i = 0; i < mNumFrames; i++) {
573  temp1 = *outPtr[9]; // needed to avoid recursive error
574 
575  // The source for the 3rd order tilt matrix is Zmoelnig's thesis, taking into consideration his different channel naming convention!
576  // This matrix seems to be different from the one that Malham specifies in his thesis!
577  *outPtr[9]++ = mCosAngle[2] * (*outPtr[9]) - mSinAngle[2] * (*outPtr[10]); // P channel
578  *outPtr[10]++ = mSinAngle[2] * temp1 + mCosAngle[2] * (*outPtr[10]); // Q channel
579  }
580  // if (mOrder.horizontalOrder > 3) rotateFourthOrderHorizontal();
581 }
582 
584 {
585  SampleBufferVector outPtr = mOutPtr; // create local copy of output pointer
586  sample temp2, temp3;
587 
588  for (unsigned i = 0; i < mNumFrames; i++) {
589  temp2 = *outPtr[11];// needed to avoid recursive error
590  temp3 = *outPtr[13];
591 
592  // The source for the 3rd order tilt matrix is Zmoelnig's thesis, taking into consideration his different channel naming convention!
593  // This matrix seems to be different from the one that Malham specifies in his thesis!
594  *outPtr[11]++ = mCosAngle[1] * (*outPtr[11]) - mSinAngle[1] * (*outPtr[12]); // N channel
595  *outPtr[12]++ = mSinAngle[1] * temp2 + mCosAngle[1] * (*mOutPtr[12]); // O channel
596  *outPtr[13]++ = mCosAngle[0] * (*outPtr[13]) - mSinAngle[0] * (*outPtr[14]); // L channel
597  *outPtr[14]++ = mSinAngle[0] * temp3 + mCosAngle[0] * (*outPtr[14]); // M channel
598  // K channel (15) remains unchanged
599  }
600  // if (mOrder.verticalOrder > 3) rotateFourthOrderVertical();
601 }
602 
603 
604 
605 /*******************MATRIX INVERSION UTILITY FUNCTION*******************/
606 
607 // code based on the matrix library found at: http://home1.gte.net/edwin2/Matrix/
608 
609 /*
610 PYTHAG computes sqrt(a^{2} + b^{2}) without destructive overflow or underflow.
611 */
612 static double at, bt, ct;
613 #define PYTHAG(a, b) ((at = fabs(a)) > (bt = fabs(b)) ? \
614 (ct = bt/at, at*sqrt(1.0+ct*ct)): (bt ? (ct = at/bt, bt*sqrt(1.0+ct*ct)): 0.0))
615 
616 static double maxarg1, maxarg2;
617 #define MAX(a, b) (maxarg1 = (a), maxarg2 = (b), (maxarg1) > (maxarg2) ? \
618 (maxarg1) : (maxarg2))
619 
620 #define SIGN(a, b) ((b) < 0.0 ? -fabs(a): fabs(a))
621 
622 /** \relates AmbisonicUnitGenerator
623 Given a matrix a[m][n], this routine computes its singular value
624 decomposition, A = U*W*V^{T}. The matrix U replaces a on output.
625 The diagonal matrix of singular values W is output as a vector w[n].
626 The matrix V (not the transpose V^{T}) is output as v[n][n].
627 m must be greater or equal to n; if it is smaller, then a should be
628 filled up to square with zero rows.
629 */
630 void csl::singularValueDecomposition(sample** a, int m, int n, sample* w, sample** v)
631 {
632  int flag, i, its, j, jj, k, l, nm;
633  double c, f, h, s, x, y, z;
634  double anorm = 0.0, g = 0.0, scale = 0.0;
635 
636  // if (m < n)
637 // error("SingularValueDecomposition: Matrix A must be augmented with extra rows of zeros.");
638  double* rv1 = new double [n];
639 
640  /* Householder reduction to bidiagonal form. */
641  for (i = 0; i < n; i++) {
642  l = i + 1;
643  rv1[i] = scale*g;
644  g = s = scale = 0.0;
645  if (i < m) {
646  for (k = i; k < m; k++)
647  scale += fabs(a[k][i]);
648  if (scale) {
649  for (k = i; k < m; k++) {
650  a[k][i] /= scale;
651  s += a[k][i]*a[k][i];
652  };
653  f = a[i][i];
654  g = -SIGN(sqrt(s), f);
655  h = f*g - s;
656  a[i][i] = f - g;
657  if (i != n - 1) {
658  for (j = l; j < n; j++) {
659  for (s = 0.0, k = i; k < m; k++)
660  s += a[k][i]*a[k][j];
661  f = s/h;
662  for (k = i; k < m; k++)
663  a[k][j] += f*a[k][i];
664  };
665  };
666  for (k = i; k < m; k++)
667  a[k][i] *= scale;
668  };
669  };
670  w[i] = scale*g;
671  g = s= scale = 0.0;
672  if (i < m && i != n - 1) {
673  for (k = l; k < n; k++)
674  scale += fabs(a[i][k]);
675  if (scale) {
676  for (k = l; k < n; k++) {
677  a[i][k] /= scale;
678  s += a[i][k]*a[i][k];
679  };
680  f = a[i][l];
681  g = -SIGN(sqrt(s), f);
682  h = f*g - s;
683  a[i][l] = f - g;
684  for (k = l; k < n; k++)
685  rv1[k] = a[i][k]/h;
686  if (i != m - 1) {
687  for (j = l; j < m; j++) {
688  for (s = 0.0, k = l; k < n; k++)
689  s += a[j][k]*a[i][k];
690  for (k = l; k < n; k++)
691  a[j][k] += s*rv1[k];
692  };
693  };
694  for (k = l; k < n; k++)
695  a[i][k] *= scale;
696  };
697  };
698  anorm = MAX(anorm, (fabs(w[i]) + fabs(rv1[i])));
699  };
700  /* Accumulation of right-hand transformations. */
701  for (i = n - 1; 0 <= i; i--)
702  {
703  if (i < n - 1)
704  {
705  if (g)
706  {
707  for (j = l; j < n; j++)
708  v[j][i] = (a[i][j]/a[i][l])/g;
709  /* Double division to avoid possible underflow: */
710  for (j = l; j < n; j++)
711  {
712  for (s = 0.0, k = l; k < n; k++)
713  s += a[i][k]*v[k][j];
714  for (k = l; k < n; k++)
715  v[k][j] += s*v[k][i];
716  };
717  };
718  for (j = l; j < n; j++)
719  v[i][j] = v[j][i] = 0.0;
720  };
721  v[i][i] = 1.0;
722  g = rv1[i];
723  l = i;
724  };
725  /* Accumulation of left-hand transformations. */
726  for (i = n - 1; 0 <= i; i--)
727  {
728  l = i + 1;
729  g = w[i];
730  if (i < n - 1)
731  for (j = l; j < n; j++)
732  a[i][j] = 0.0;
733  if (g)
734  {
735  g = 1.0/g;
736  if (i != n - 1)
737  {
738  for (j = l; j < n; j++)
739  {
740  for (s = 0.0, k = l; k < m; k++)
741  s += a[k][i]*a[k][j];
742  f = (s/a[i][i])*g;
743  for (k = i; k < m; k++)
744  a[k][j] += f*a[k][i];
745  };
746  };
747  for (j = i; j < m; j++)
748  a[j][i] *= g;
749  }
750  else
751  for (j = i; j < m; j++)
752  a[j][i] = 0.0;
753  ++a[i][i];
754  };
755  /* Diagonalization of the bidiagonal form. */
756  for (k = n - 1; 0 <= k; k--) /* Loop over singular values. */
757  {
758  for (its = 0; its < 30; its++) /* Loop over allowed iterations.*/
759  {
760  flag = 1;
761  for (l = k; 0 <= l; l--) /* Test for splitting: */
762  {
763  nm = l - 1; /* Note that rv1[0] is always zero.*/
764  if (fabs(rv1[l]) + anorm == anorm)
765  {
766  flag = 0;
767  break;
768  };
769  if (fabs(w[nm]) + anorm == anorm)
770  break;
771  };
772  if (flag)
773  {
774  c = 0.0; /* Cancellation of rv1[l], if l>0:*/
775  s = 1.0;
776  for (i = l; i <= k; i++) {
777  f = s*rv1[i];
778  if (fabs(f) + anorm != anorm)
779  {
780  g = w[i];
781  h = PYTHAG(f, g);
782  w[i] = h;
783  h = 1.0/h;
784  c = g*h;
785  s = (-f*h);
786  for (j = 0; j < m; j++)
787  {
788  y = a[j][nm];
789  z = a[j][i];
790  a[j][nm] = y*c + z*s;
791  a[j][i] = z*c - y*s;
792  };
793  };
794  };
795  };
796  z = w[k];
797  if (l == k) /* Convergence. */
798  {
799  if (z < 0.0) /* Singular value is made non-negative. */
800  {
801  w[k] = -z;
802  for (j = 0; j < n; j++)
803  v[j][k] = (-v[j][k]);
804  };
805  break;
806  };
807 // if (its == 29)
808 // error("No convergence in 30 SingularValueDecomposition iterations.");
809  x = w[l]; /* Shift from bottom 2-by-2 minor. */
810  nm = k - 1;
811  y = w[nm];
812  g = rv1[nm];
813  h = rv1[k];
814  f = ((y - z)*(y + z) + (g - h)*(g + h))/(2.0*h*y);
815  g = PYTHAG(f, 1.0);
816  f = ((x - z)*(x + z) + h*((y/(f + SIGN(g, f))) - h))/x;
817  /* Next QR transformation: */
818  c = s = 1.0;
819  for (j = l; j <= nm; j++)
820  {
821  i = j + 1;
822  g = rv1[i];
823  y = w[i];
824  h = s*g;
825  g = c*g;
826  z = PYTHAG(f, h);
827  rv1[j] = z;
828  c = f/z;
829  s = h/z;
830  f = x*c + g*s;
831  g = g*c - x*s;
832  h = y*s;
833  y = y*c;
834  for (jj = 0; jj < n; jj++)
835  {
836  x = v[jj][j];
837  z = v[jj][i];
838  v[jj][j] = x*c + z*s;
839  v[jj][i] = z*c - x*s;
840  };
841  z = PYTHAG(f, h);
842  w[j] = z; /* Rotation can be arbitrary if z = 0. */
843  if (z)
844  {
845  z = 1.0/z;
846  c = f*z;
847  s = h*z;
848  };
849  f = (c*g) + (s*y);
850  x = (c*y) - (s*g);
851  for (jj = 0; jj < m; jj++)
852  {
853  y = a[jj][j];
854  z = a[jj][i];
855  a[jj][j] = y*c + z*s;
856  a[jj][i] = z*c - y*s;
857  };
858  };
859  rv1[l] = 0.0;
860  rv1[k] = f;
861  w[k] = x;
862  };
863  };
864  delete [] rv1;
865  }
866 
867 /// \relates AmbisonicUnitGenerator
868 void csl::fumaEncodingWeights(SampleBuffer weights, const AmbisonicOrder &order, float azimuth, float elevation) {
869 
870  float x,y,z,x2,y2,z2;
871  // assume default location of 0,0, i.e directly in front on the plane
872  x= x2 = 1.f;
873  y = z = y2 = z2 = 0.f;
874 
875  unsigned h = order.horizontalOrder;
876  unsigned v = order.verticalOrder;
877  unsigned channel = 1;
878 
879  // skip this line, do it in the initialization of weights instead, because it never changes
880  //weights[0] = AMBI_INVSQRT2; // W channel, shouldn't it be already defined?
881 
882  if (h > 0) {
883  float cosel = cosf(elevation);
884  x = cosf(azimuth) * cosel;
885  y = sinf(azimuth) * cosel;
886 
887  weights[channel++] = x; // X = cos(A)cos(E)
888  weights[channel++] = y; // Y = sin(A)cos(E)
889 
890  }
891 
892  if (v > 0) {
893  z = sinf(elevation);
894  weights[channel++] = z; // Z = sin(E)
895  }
896 
897  if (h > 1) {
898  x2 = x*x;
899  y2 = y*y;
900 
901  weights[channel++] = x2 - y2; // U = cos(2A)cos2(E) = xx-yy
902  weights[channel++] = 2.f * x * y; // V = sin(2A)cos2(E) = 2xy
903 
904  }
905 
906  if (v > 1) {
907  z2 = z*z;
908 
909  weights[channel++] = 2.f * z * x; // S = cos(A)sin(2E) = 2zx
910  weights[channel++] = 2.f * z * y; // T = sin(A)sin(2E) = 2yz
911  weights[channel++] = (1.5f * z2) - 0.5f; // R = 1.5sin2(E)-0.5 = 1.5zz-0.5
912 
913  }
914 
915  if (h > 2) {
916  weights[channel++] = x * (x2 - 3.f*y2); // P = cos(3A)cos3(E) = X(X2-3Y2)
917  weights[channel++] = y * (y2 - 3.f*x2); // Q = sin(3A)cos3(E) = Y(3X2-Y2)
918  }
919 
920  if (v > 2) {
921  float pre = 8.f/11.f;
922 
923  weights[channel++] = z * (x2-y2) * 0.5f; // N = cos(2A)sin(E)cos2(E) = Z(X2-Y2)/2
924  weights[channel++] = x * y * z; // O = sin(2A)sin(E)cos2(E) = XYZ
925  weights[channel++] = pre * x * (5.f*z2 - 1.f); // L = 8cos(A)cos(E)(5sin2(E) - 1)/11 = 8X(5Z2-1)/11
926  weights[channel++] = pre * y * (5.f*z2 - 1.f); // M = 8sin(A)cos(E)(5sin2(E) - 1)/11 = 8Y(5Z2-1)/11
927  weights[channel++] = z * 0.5 * (5.f*z2 - 3.f); // K = sin(E)(5sin2(E) - 3)/2 = Z(5Z2-3)/2
928  }
929 
930 }
931 
932 /// \relates AmbisonicUnitGenerator
933 void csl::fumaIndexedEncodingWeights(SampleBuffer weights, const AmbisonicOrder &order, sample &azimuth, sample &elevation)
934 {
935  float x,y,z,x2,y2,z2;
936  // assume default location of 0,0, i.e directly in front on the plane
937  x= x2 = 1.f;
938  y= z = y2 = z2 = 0.f;
939 
940 
941  unsigned h = order.horizontalOrder;
942  unsigned v = order.verticalOrder;
943 
944  // skip this line, do it in the initialization of weights instead, because it never changes
945  //weights[0] = AMBI_INVSQRT2; // W channel, shouldn't it be already defined?
946 
947  if (h > 0) {
948  float cosel = cosf(elevation);
949  x = cosf(azimuth) * cosel;
950  y = sinf(azimuth) * cosel;
951 
952  weights[1] = x; // X = cos(A)cos(E)
953  weights[2] = y; // Y = sin(A)cos(E)
954 
955  }
956 
957  if (v > 0) {
958  z = sinf(elevation);
959  weights[3] = z; // Z = sin(E)
960  }
961 
962  if (h > 1) {
963  x2 = x*x;
964  y2 = y*y;
965 
966  weights[4] = x2 - y2; // U = cos(2A)cos2(E) = xx-yy
967  weights[5] = 2.f * x * y; // V = sin(2A)cos2(E) = 2xy
968 
969  if (h > 2) {
970  weights[9] = x * (x2 - 3.f*y2); // P = cos(3A)cos3(E) = X(X2-3Y2)
971  weights[10]= y * (y2 - 3.f*x2); // Q = sin(3A)cos3(E) = Y(3X2-Y2)
972  }
973  }
974 
975  if (v > 1) {
976  z2 = z*z;
977 
978  weights[6] = 2.f * z * x; // S = cos(A)sin(2E) = 2zx
979  weights[7] = 2.f * z * y; // T = sin(A)sin(2E) = 2yz
980  weights[8] = (1.5f * z2) - 0.5f; // R = 1.5sin2(E)-0.5 = 1.5zz-0.5
981 
982  if (v > 2) {
983  float pre = 8.f/11.f;
984 
985  weights[11] = z * (x2-y2) * 0.5f; // N = cos(2A)sin(E)cos2(E) = Z(X2-Y2)/2
986  weights[12] = x * y * z; // O = sin(2A)sin(E)cos2(E) = XYZ
987  weights[13] = pre * x * (5.f*z2 - 1.f); // L = 8cos(A)cos(E)(5sin2(E) - 1)/11 = 8X(5Z2-1)/11
988  weights[14] = pre * y * (5.f*z2 - 1.f); // M = 8sin(A)cos(E)(5sin2(E) - 1)/11 = 8Y(5Z2-1)/11
989  weights[15] = z * 0.5 * (5.f*z2 - 3.f); // K = sin(E)(5sin2(E) - 3)/2 = Z(5Z2-3)/2
990  }
991  }
992 }
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
#define PYTHAG(a, b)
unsigned mNumFrames
num frames used in each buffer
Definition: CSL_Core.h:113
SampleBufferVector mOutPtr
AdditiveInstrument.h – Sum-of-sines synthesis instrument class.
Definition: Accessor.h:17
void setNthInput(float amount, Axes axis)
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...
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
static double bt
AmbisonicMixer(unsigned order=1)
virtual void nextBuffer(Buffer &outputBuffer)
get a buffer of Frames – this is the core CSL "pull" function; the given buffer can be written into...
Definition: CSL_Core.cpp:726
Ambisonic order structure (separate definition for horizontal and vertical order): ...
Definition: Ambisonic.h:57
static double at
UGenVector mInputs
vector of pointers to the loudspeakers
void setRotate(float amount)
AmbisonicOrder order()
Definition: Ambisonic.h:78
static unsigned blockSize()
the default block size
Definition: CGestalt.cpp:57
bool isUniform
Returns true if horizontal and verical orders are identical.
Definition: Ambisonic.h:64
unsigned horizontalOrder
Definition: Ambisonic.h:60
virtual unsigned numChannels()
Definition: CSL_Core.h:252
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
BufferContentType mType
Data type flag set the internal size variables (no buffer allocation takes place) ...
Definition: CSL_Core.h:124
void addInput(AmbisonicUnitGenerator &input)
methods for adding/removing inputs to the mixer.
virtual void nextBuffer(Buffer &outputBuffer, unsigned outBufNum)
Number of active inputs.
virtual bool isActive()
query whether I'm currently active (Envelopes can go inactive)
Definition: CSL_Core.h:273
#define SIGN(a, b)
AmbisonicRotator(AmbisonicUnitGenerator &input)
initializes with no rotation
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
static double maxarg1
void initialize()
Initializing method called by constructors.
~AmbisonicMixer()
Destructor.
UnitGenerator * mUGen
my unit generator (pointer or NULL)
Definition: CSL_Core.h:320
void setTumble(float amount)
void setTilt(float amount)
static double ct
Buffer – the multi-channel sample buffer class (passed around between generators and IO guys)...
Definition: CSL_Core.h:106
Ambisonic Abstract Base Class.
Definition: Ambisonic.h:69
AmbisonicOrder mOrder
the order of the Unit Generator
Definition: Ambisonic.h:83
void allocateBuffers()
fcn to malloc storage buffers
Definition: CSL_Core.cpp:122
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
void removeOutput(UnitGenerator *ugen)
Definition: CSL_Core.cpp:680
forward declaration
Definition: CSL_Core.h:241
SampleBufferVector mInPtr
Regular audio samples.
Definition: CSL_Core.h:72
float mInvNumInputs
the inverse of the number of inputs (used for normalization)
Buffer * mInBuffer
buffer for the input framestream
static double maxarg2
void fumaIndexedEncodingWeights(SampleBuffer weights, const AmbisonicOrder &order, sample &azimuth, sample &elevation)
Utility function that calculates fuma encoding weights for a given order, azimuth and elevation...
void initialize(UnitGenerator &input)
#define MAX(a, b)
unsigned verticalOrder
Definition: Ambisonic.h:63
bool mIsPopulated
does the buffer have data?
Definition: CSL_Core.h:122
void singularValueDecomposition(sample **a, int m, int n, sample *w, sample **v)
Utility function used in calculating the inverse of a matrix, used in AmbisonicDecoder for the pseudo...
Base class of CSL exceptions (written upper-case). Has a string message.