CSL  5.2
Binaural.cpp
Go to the documentation of this file.
1 ///
2 /// Binaural.cpp -- HRTF-based binaural panner/spatializers
3 ///
4 /// Classes
5 /// HRTF: holds the data that corresponds to an HRTF for a single position.
6 /// HRTFDatabase: vector of HRTFs; implemented as a Singleton because it's large.
7 /// BinauralPanner: place sources in 3D using block-wise convolution with an HRTF.
8 /// BinauralSourceCache: used for caching previous state of spatial sources.
9 ///
10 /// See the copyright notice and acknowledgment of authors in the file COPYRIGHT
11 /// Created by Jorge Castellanos on 7/19/06.
12 /// Rewritten for FFT wrappers and pluggable sound file APIs in 8/09 by STP.
13 /// Inspired by and partially based on the VST HRTF Plug-in written by Ryan Avery.
14 ///
15 
16 #include "Binaural.h"
17 
18 using namespace csl;
19 using namespace std;
20 
21 //
22 // BinauralPanner constructor (blockSize typ. 512, FFT 1024, spect[513])
23 //
24 
25 BinauralPanner::BinauralPanner(unsigned blockSize)
26  : SpatialPanner(), // inherited constructor
27  // set up FFT wrappers
28  mInFFT(blockSize * FFT_DOWNS, CSL_FFT_COMPLEX, CSL_FFT_FORWARD), // forward
29  mOutFFT(blockSize * FFT_DOWNS, CSL_FFT_COMPLEX, CSL_FFT_INVERSE), // inverse
30  mInBuf(1, blockSize * 2), // dbl length
31  mTmpBuf(1, blockSize * 2 + 8), // complex #s
32  mOutBuf(2, blockSize * 2), // dbl length
33  mBlockInd(0) { // starting index
34  unsigned hrtfLength, winSize; // set up panner variables
35  mFramesPerBlock = blockSize; // 512
37 #ifdef IPHONE
38  mNumBlocksToSum = mNumBlocks / SUM_DOWNS; // sum part of the HRTF
39 #else
40  mNumBlocksToSum = mNumBlocks; // sum the whole HRTF
41 #endif
42  hrtfLength = HRTFDatabase::Database()->hrtfLength(); // 512
43  winSize = HRTFDatabase::Database()->windowSize(); // 1024
44  mTmpBuf.mType = kSpectra; // temp buffer is complex spectral data
45  setCopyPolicy(kIgnore); // so the CopyPolicy doesn't overide panning
46  setNumChannels(2); // be stereo
47 
48  SAFE_MALLOC(mIFFTOutL, sample, winSize); // allocate real sample buffers
49  SAFE_MALLOC(mIFFTOutR, sample, winSize);
50  SAFE_MALLOC(mHOutL, SampleComplex, hrtfLength + 1); // and complex spectral sums
51  SAFE_MALLOC(mHOutR, SampleComplex, hrtfLength + 1);
52  mInBuf.allocateBuffers(); // this holds the input samples;
53  // the other buffers are ptrs only
54 }
55 
56 // Destructor frees space
57 
63 }
64 
65 // Returns a pointer to an alocated cache data for its own use.
66 
68  return (void *)(new BinauralSourceCache(this));
69 }
70 
71 //
72 // BinauralPanner::nextBuffer() does the heavy lifting of the block-wise convolution
73 //
74 
75 void BinauralPanner::nextBuffer(Buffer & outputBuffer) throw (CException) {
76 
77  HRTFDatabase * hrtfDatabase = HRTFDatabase::Database();
78  unsigned numFrames = outputBuffer.mNumFrames;
79  unsigned tmpBufPtr, tmpHRTFPtr = 0;
80  unsigned hrtfLength = hrtfDatabase->hrtfLength();
81  unsigned hrtf2Use = hrtfLength / LEN_DOWNS;
82  unsigned winSize = hrtfDatabase->windowSize();
83 #ifdef CSL_DEBUG
84  logMsg("BinauralPanner::nextBuffer");
85 #endif
86  if (outputBuffer.mNumChannels != 2) {
87  logMsg(kLogError, "BinauralPanner needs a stereo output buffer");
88  return;
89  }
90  if (numFrames != mFramesPerBlock) {
92  "BinauralPanner::nextBuffer # frames %d wrong for HRTF size %d (use a block resizer).",
93  numFrames, mFramesPerBlock);
94  return;
95  }
96  outputBuffer.zeroBuffers(); // clear output
97 
98  for (unsigned i = 0; i < mSources.size(); i++) { // i loops through sources
99 
100  SpatialSource * source = (SpatialSource *) mSources[i]; // Get the sound source
101 
102  if (source->isActive()) { // if source is active
103  mInBuf.mNumFrames = winSize; // set in # frames
104  mInBuf.zeroBuffers(); // clear buffer
105  mInBuf.mNumFrames = numFrames; // set in # frames
106  bool samePos = ! source->positionChanged(); // check this before we grab the input
107 
108  source->nextBuffer(mInBuf); // get input from the source
109 
110 //////// // debugging: copy input to output
111 // memcpy(outputBuffer.buffer(0), mInBuf.buffer(0), numFrames * sizeof(sample));
112 // memcpy(outputBuffer.buffer(1), mInBuf.buffer(0), numFrames * sizeof(sample));
113 // return;
114 //////// // Get the cached data
115  BinauralSourceCache * tcache = (BinauralSourceCache *) mCache[i];
116  // set input complex fft pointer
117  mTmpBuf.setBuffer(0, (SampleBuffer) tcache->mInSpect[mBlockInd]);
118  mInBuf.mNumFrames = winSize; // dbl length
119 
120  mInFFT.nextBuffer(mInBuf, mTmpBuf); // Calculate input FFT into cache->mInSpect
121 
122  if (samePos) { // if the position of the source is the same.
123  if (mBlockInd > 0) // assign the same HRTF you had the previous round
124  tcache->mHRTF[mBlockInd] = tcache->mHRTF[mBlockInd - 1];
125  else
126  tcache->mHRTF[mBlockInd] = tcache->mHRTF[mNumBlocks - 1];
127 
128  } else { // if the position changed, then find the new HRTF
129  unsigned hrtfIndex = hrtfDatabase->hrtfAt(*source->position());
130  tcache->mHRTF[mBlockInd] = hrtfIndex;
131 // CPoint hrtfPosition = hrtfDatabase->hrtfAt(localHRTF)->mPosition;
132 // logMsg("\t\t\t\tHRTF: %5.1f @ %5.1f \t Source: %5.1f @ %5.1f",
133 // hrtfPosition.theta() * CSL_DEGS_PER_RAD, hrtfPosition.ele() * CSL_DEGS_PER_RAD,
134 // source->position()->theta() * CSL_DEGS_PER_RAD, source->position()->ele() * CSL_DEGS_PER_RAD);
135  }
136  // zero the complex spectrum buffers before summing loop
137  memset(mHOutL, 0, hrtf2Use * sizeof(SampleComplex));
138  memset(mHOutR, 0, hrtf2Use * sizeof(SampleComplex));
139 
140  // Do the convolution of HRTF with the input spectra
141  tmpBufPtr = mBlockInd; // set the block counter to the current block
142  do { // Go thru previous HRTFs doing complex multiply/accumulate
143  HRTF * tempHRTF = hrtfDatabase->hrtfAt(tcache->mHRTF[tmpBufPtr]);
144  for (unsigned j = 0; j < hrtf2Use; j++) { // loop over buffers of past FFT out
145  ComplexPtr in1j = tcache->mInSpect[tmpBufPtr][j]; // input spectrum
146  ComplexPtr inLj = tempHRTF->mHrtfL[tmpHRTFPtr][j]; // L/R HRTF
147  ComplexPtr inRj = tempHRTF->mHrtfR[tmpHRTFPtr][j];
148  ComplexPtr outLj = mHOutL[j]; // L/R summation buffers
149  ComplexPtr outRj = mHOutR[j];
150  cmac(in1j, inLj, outLj); // complex MAC macro
151  cmac(in1j, inRj, outRj);
152  // old-way: complex MAC loop with 2D arrays
153 // cmac(cache->mInSpect[tmpBufPtr][j], tempHRTF->mHrtfL[tmpHRTFPtr][j], mHOutL[j]);
154 // cmac(cache->mInSpect[tmpBufPtr][j], tempHRTF->mHrtfR[tmpHRTFPtr][j], mHOutR[j]);
155  }
156  if (++tmpBufPtr >= mNumBlocksToSum)
157  tmpBufPtr = 0;
158  if (++tmpHRTFPtr >= mNumBlocksToSum)
159  tmpHRTFPtr = 0;
160  } while (tmpBufPtr != mBlockInd); // end of loop through prior inputs and HRTF spectra
161 
162  // debugging: overwrite spectral sum with input
163 // memcpy(mHOutL, cache->mInSpect[mBlockInd], hrtfLength * sizeof(SampleComplex));
164 // memcpy(mHOutR, cache->mInSpect[mBlockInd], hrtfLength * sizeof(SampleComplex));
165  // Output IFFT stage
166  mTmpBuf.mNumFrames = winSize; // set up output buffer
167  mTmpBuf.setBuffer(0, (SampleBuffer) mHOutL); // IFFT input = summation buffer
168  mOutBuf.setBuffer(0, mIFFTOutL); // IFFT out = sample buffer
169 
170  mOutFFT.nextBuffer(mTmpBuf, mOutBuf); // do left IFFT
171 
172  mTmpBuf.setBuffer(0, (SampleBuffer) mHOutR);
173  mOutBuf.setBuffer(0, mIFFTOutR);
174 
175  mOutFFT.nextBuffer(mTmpBuf, mOutBuf); // do right IFFT
176 
177  SampleBuffer outL = outputBuffer.buffer(0); // Get pointers to output buffers (stereo)
178  SampleBuffer outR = outputBuffer.buffer(1);
179  SampleBuffer ifftL = mIFFTOutL; // IFFT real out data ptrs
180  SampleBuffer ifftR = mIFFTOutR;
181  SampleBuffer cacheL = tcache->mPrevOutL; // previous real output caches
182  SampleBuffer cacheR = tcache->mPrevOutR;
183 #ifdef USE_FFTW
184  sample scale = 1.0f / (float) numFrames; // window size scale
185 #else
186  sample scale = 1.0f ; // / sqrtf(numFrames);
187 // sample scale = 1.0f / sqrtf(numFrames);
188 #endif
189  for (unsigned j = 0; j < numFrames; j++) { // Overlap and add loop
190  *outL++ += ((*ifftL++ + *cacheL++) * scale); // sum/scale IFFT & cache
191  *outR++ += ((*ifftR++ + *cacheR++) * scale);
192  }
193  // copy top-half of output to cache
194  memcpy(tcache->mPrevOutL, mIFFTOutL + numFrames, numFrames * sizeof(sample));
195  memcpy(tcache->mPrevOutR, mIFFTOutR + numFrames, numFrames * sizeof(sample));
196  } // end if source active
197  } // end source loop
198 
199  if (mBlockInd == 0) // test/decrement panner's block counter
200  mBlockInd = mNumBlocksToSum; // for next call
201  mBlockInd--;
202 }
203 
204 
205 // BinauralSourceCache constructor
206 
208  mNumBlocks = parent->mNumBlocks;
209  unsigned framesPerBlock = parent->mFramesPerBlock;
210  unsigned hrtfLength = HRTFDatabase::Database()->hrtfLength() + 1;
211 // unsigned windowSize = HRTFDatabase::Database()->windowSize();
212 
213  // allocate buffers
214  SAFE_MALLOC(mPrevOutL, sample, framesPerBlock);
215  SAFE_MALLOC(mPrevOutR, sample, framesPerBlock);
216  SAFE_MALLOC(mHRTF, unsigned, mNumBlocks);
217 
218  // create/zero the complex arrays for past inp FFTs
220  for (unsigned i = 0; i < mNumBlocks; i++) {
221  SAFE_MALLOC(mInSpect[i], SampleComplex, hrtfLength);
222  }
223 }
224 
226  for (unsigned i = 0; i < mNumBlocks; i++) {
227  SAFE_FREE(mInSpect[i]);
228  }
232  SAFE_FREE(mHRTF);
233 }