CSL  6.0
Binaural.cpp
Go to the documentation of this file.
1 ///
2 /// Binaural.cpp -- HRTF-based binaural panner/spatializers
3 ///
4 /// Classes
5 /// BinauralPanner: place sources in 3D using block-wise convolution with an HRTF.
6 /// BinauralSourceCache: used for caching previous state of spatial sources.
7 ///
8 /// See the copyright notice and acknowledgment of authors in the file COPYRIGHT
9 /// Created by Jorge Castellanos on 7/19/06.
10 /// Rewritten for FFT wrappers and pluggable sound file APIs in 8/09 by STP.
11 /// Inspired by and partially based on the VST HRTF Plug-in written by Ryan Avery.
12 ///
13 
14 #include "Binaural.h"
15 
16 using namespace csl;
17 using namespace std;
18 
19 //
20 // BinauralPanner constructor (blockSize typ. 512, FFT 1024, spect[513])
21 //
22 
23 BinauralPanner::BinauralPanner(unsigned blockSize)
24  : SpatialPanner(), // inherited constructor
25  // set up FFT wrappers
26  mInFFT(blockSize * FFT_DOWNS, CSL_FFT_COMPLEX, CSL_FFT_FORWARD), // forward
27  mOutFFT(blockSize * FFT_DOWNS, CSL_FFT_COMPLEX, CSL_FFT_INVERSE), // inverse
28  mInBuf(1, blockSize * 2), // dbl length
29  mTmpBuf(1, blockSize * 2 + 8), // complex #s
30  mOutBuf(2, blockSize * 2), // dbl length
31  mBlockInd(0) { // starting index
32  unsigned hrtfLength, winSize; // set up panner variables
33  mFramesPerBlock = blockSize; // 512
35 #ifdef IPHONE
36  mNumBlocksToSum = mNumBlocks / SUM_DOWNS; // sum part of the HRTF
37 #else
38  mNumBlocksToSum = mNumBlocks; // sum the whole HRTF
39 #endif
40  hrtfLength = HRTFDatabase::Database()->hrtfLength(); // 512
41  winSize = HRTFDatabase::Database()->windowSize(); // 1024
42  mTmpBuf.mType = kSpectra; // temp buffer is complex spectral data
43  setCopyPolicy(kIgnore); // so the CopyPolicy doesn't overide panning
44  setNumChannels(2); // be stereo
45 
46  SAFE_MALLOC(mIFFTOutL, sample, winSize); // allocate real sample buffers
47  SAFE_MALLOC(mIFFTOutR, sample, winSize);
48  SAFE_MALLOC(mHOutL, SampleComplex, hrtfLength + 1); // and complex spectral sums
49  SAFE_MALLOC(mHOutR, SampleComplex, hrtfLength + 1);
50  mInBuf.allocateBuffers(); // this holds the input samples;
51  // the other buffers are ptrs only
52 }
53 
54 // Destructor frees space
55 
61 }
62 
63 // Returns a pointer to an alocated cache data for its own use.
64 
66  return (void *)(new BinauralSourceCache(this));
67 }
68 
69 //
70 // BinauralPanner::nextBuffer() does the heavy lifting of the block-wise convolution
71 //
72 
73 void BinauralPanner::nextBuffer(Buffer & outputBuffer) throw (CException) {
74 
75  HRTFDatabase * hrtfDatabase = HRTFDatabase::Database();
76  unsigned numFrames = outputBuffer.mNumFrames;
77  unsigned tmpBufPtr, tmpHRTFPtr = 0;
78  unsigned hrtfLength = hrtfDatabase->hrtfLength();
79  unsigned hrtf2Use = hrtfLength / LEN_DOWNS;
80  unsigned winSize = hrtfDatabase->windowSize();
81 #ifdef CSL_DEBUG
82  logMsg("BinauralPanner::nextBuffer");
83 #endif
84  if (outputBuffer.mNumChannels != 2) {
85  logMsg(kLogError, "BinauralPanner needs a stereo output buffer");
86  return;
87  }
88  if (numFrames != mFramesPerBlock) {
90  "BinauralPanner::nextBuffer # frames %d wrong for HRTF size %d (use a block resizer).",
91  numFrames, mFramesPerBlock);
92  return;
93  }
94  outputBuffer.zeroBuffers(); // clear output
95 
96  for (unsigned i = 0; i < mSources.size(); i++) { // i loops through sources
97 
98  SpatialSource * source = (SpatialSource *) mSources[i]; // Get the sound source
99 
100  if (source->isActive()) { // if source is active
101  mInBuf.mNumFrames = winSize; // set in # frames
102  mInBuf.zeroBuffers(); // clear buffer
103  mInBuf.mNumFrames = numFrames; // set in # frames
104  bool samePos = ! source->positionChanged(); // check this before we grab the input
105 
106  source->nextBuffer(mInBuf); // get input from the source
107 
108 //////// // debugging: copy input to output
109 // memcpy(outputBuffer.buffer(0), mInBuf.buffer(0), numFrames * sizeof(sample));
110 // memcpy(outputBuffer.buffer(1), mInBuf.buffer(0), numFrames * sizeof(sample));
111 // return;
112 //////// // Get the cached data
113  BinauralSourceCache * tcache = (BinauralSourceCache *) mCache[i];
114  // set input complex fft pointer
115  mTmpBuf.setBuffer(0, (SampleBuffer) tcache->mInSpect[mBlockInd]);
116  mInBuf.mNumFrames = winSize; // dbl length
117 
118  mInFFT.nextBuffer(mInBuf, mTmpBuf); // Calculate input FFT into cache->mInSpect
119 
120  if (samePos) { // if the position of the source is the same.
121  if (mBlockInd > 0) // assign the same HRTF you had the previous round
122  tcache->mHRTF[mBlockInd] = tcache->mHRTF[mBlockInd - 1];
123  else
124  tcache->mHRTF[mBlockInd] = tcache->mHRTF[mNumBlocks - 1];
125 
126  } else { // if the position changed, then find the new HRTF
127  unsigned hrtfIndex = hrtfDatabase->hrtfAt(*source->position());
128  tcache->mHRTF[mBlockInd] = hrtfIndex;
129 // CPoint hrtfPosition = hrtfDatabase->hrtfAt(localHRTF)->mPosition;
130 // logMsg("\t\t\t\tHRTF: %5.1f @ %5.1f \t Source: %5.1f @ %5.1f",
131 // hrtfPosition.theta() * CSL_DEGS_PER_RAD, hrtfPosition.ele() * CSL_DEGS_PER_RAD,
132 // source->position()->theta() * CSL_DEGS_PER_RAD, source->position()->ele() * CSL_DEGS_PER_RAD);
133  }
134  // zero the complex spectrum buffers before summing loop
135  memset(mHOutL, 0, hrtf2Use * sizeof(SampleComplex));
136  memset(mHOutR, 0, hrtf2Use * sizeof(SampleComplex));
137 
138  // Do the convolution of HRTF with the input spectra
139  tmpBufPtr = mBlockInd; // set the block counter to the current block
140  do { // Go thru previous HRTFs doing complex multiply/accumulate
141  HRTF * tempHRTF = hrtfDatabase->hrtfAt(tcache->mHRTF[tmpBufPtr]);
142  for (unsigned j = 0; j < hrtf2Use; j++) { // loop over buffers of past FFT out
143  ComplexPtr in1j = tcache->mInSpect[tmpBufPtr][j]; // input spectrum
144  ComplexPtr inLj = tempHRTF->mHrtfL[tmpHRTFPtr][j]; // L/R HRTF
145  ComplexPtr inRj = tempHRTF->mHrtfR[tmpHRTFPtr][j];
146  ComplexPtr outLj = mHOutL[j]; // L/R summation buffers
147  ComplexPtr outRj = mHOutR[j];
148  cmac(in1j, inLj, outLj); // complex MAC macro
149  cmac(in1j, inRj, outRj);
150  // old-way: complex MAC loop with 2D arrays
151 // cmac(cache->mInSpect[tmpBufPtr][j], tempHRTF->mHrtfL[tmpHRTFPtr][j], mHOutL[j]);
152 // cmac(cache->mInSpect[tmpBufPtr][j], tempHRTF->mHrtfR[tmpHRTFPtr][j], mHOutR[j]);
153  }
154  if (++tmpBufPtr >= mNumBlocksToSum)
155  tmpBufPtr = 0;
156  if (++tmpHRTFPtr >= mNumBlocksToSum)
157  tmpHRTFPtr = 0;
158  } while (tmpBufPtr != mBlockInd); // end of loop through prior inputs and HRTF spectra
159 
160  // debugging: overwrite spectral sum with input
161 // memcpy(mHOutL, cache->mInSpect[mBlockInd], hrtfLength * sizeof(SampleComplex));
162 // memcpy(mHOutR, cache->mInSpect[mBlockInd], hrtfLength * sizeof(SampleComplex));
163  // Output IFFT stage
164  mTmpBuf.mNumFrames = winSize; // set up output buffer
165  mTmpBuf.setBuffer(0, (SampleBuffer) mHOutL); // IFFT input = summation buffer
166  mOutBuf.setBuffer(0, mIFFTOutL); // IFFT out = sample buffer
167 
168  mOutFFT.nextBuffer(mTmpBuf, mOutBuf); // do left IFFT
169 
170  mTmpBuf.setBuffer(0, (SampleBuffer) mHOutR);
171  mOutBuf.setBuffer(0, mIFFTOutR);
172 
173  mOutFFT.nextBuffer(mTmpBuf, mOutBuf); // do right IFFT
174 
175  SampleBuffer outL = outputBuffer.buffer(0); // Get pointers to output buffers (stereo)
176  SampleBuffer outR = outputBuffer.buffer(1);
177  SampleBuffer ifftL = mIFFTOutL; // IFFT real out data ptrs
178  SampleBuffer ifftR = mIFFTOutR;
179  SampleBuffer cacheL = tcache->mPrevOutL; // previous real output caches
180  SampleBuffer cacheR = tcache->mPrevOutR;
181 #ifdef USE_FFTW
182  sample scale = 1.0f / (float) numFrames; // window size scale
183 #else
184  sample scale = 1.0f ; // / sqrtf(numFrames);
185 // sample scale = 1.0f / sqrtf(numFrames);
186 #endif
187  for (unsigned j = 0; j < numFrames; j++) { // Overlap and add loop
188  *outL++ += ((*ifftL++ + *cacheL++) * scale); // sum/scale IFFT & cache
189  *outR++ += ((*ifftR++ + *cacheR++) * scale);
190  }
191  // copy top-half of output to cache
192  memcpy(tcache->mPrevOutL, mIFFTOutL + numFrames, numFrames * sizeof(sample));
193  memcpy(tcache->mPrevOutR, mIFFTOutR + numFrames, numFrames * sizeof(sample));
194  } // end if source active
195  } // end source loop
196 
197  if (mBlockInd == 0) // test/decrement panner's block counter
198  mBlockInd = mNumBlocksToSum; // for next call
199  mBlockInd--;
200 }
201 
202 
203 // BinauralSourceCache constructor
204 
206  mNumBlocks = parent->mNumBlocks;
207  unsigned framesPerBlock = parent->mFramesPerBlock;
208  unsigned hrtfLength = HRTFDatabase::Database()->hrtfLength() + 1;
209 // unsigned windowSize = HRTFDatabase::Database()->windowSize();
210 
211  // allocate buffers
212  SAFE_MALLOC(mPrevOutL, sample, framesPerBlock);
213  SAFE_MALLOC(mPrevOutR, sample, framesPerBlock);
214  SAFE_MALLOC(mHRTF, unsigned, mNumBlocks);
215 
216  // create/zero the complex arrays for past inp FFTs
218  for (unsigned i = 0; i < mNumBlocks; i++) {
219  SAFE_MALLOC(mInSpect[i], SampleComplex, hrtfLength);
220  }
221 }
222 
224  for (unsigned i = 0; i < mNumBlocks; i++) {
225  SAFE_FREE(mInSpect[i]);
226  }
230  SAFE_FREE(mHRTF);
231 }
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
SampleComplex * SampleComplexVector
complex vector
Definition: CSL_Types.h:202
SampleComplexVector * mHrtfR
2 arrays of arrays of mNumFFTBlocks * complex[513]: the HRTF data in blocks of complex # for stereo ...
Definition: BinauralDB.h:112
static HRTFDatabase * Database()
accessor for the singleton instance (lazy init)
Definition: BinauralDB.cpp:150
SampleComplexVector * mInSpect
complex spectra of mNumBlocks past inputs
Definition: Binaural.h:85
AdditiveInstrument.h – Sum-of-sines synthesis instrument class.
Definition: Accessor.h:17
unsigned numBlocks()
The length (in windows) of the impulse responses loaded.
Definition: BinauralDB.cpp:481
SampleBuffer mIFFTOutR
outputs of the IFFT
Definition: Binaural.h:70
forward declaration
Definition: Binaural.h:49
unsigned hrtfLength()
The length (in samples) of the Transfer Function buffer.
Definition: BinauralDB.cpp:469
BinauralSourceCache(BinauralPanner *parent)
Definition: Binaural.cpp:205
SampleBuffer mIFFTOutL
Definition: Binaural.h:70
BinauralPanner(unsigned blockSize=HRTF_BLOCK_SIZE)
Definition: Binaural.cpp:23
SampleBuffer mPrevOutL
Definition: Binaural.h:86
#define ComplexPtr
shorthand
Definition: CSL_Types.h:201
#define LEN_DOWNS
Definition: Binaural.h:37
void nextBuffer(Buffer &outputBuffer)
work-horse method reads & transforms input, sums previous data, and takes the IFFT for each of multip...
Definition: Binaural.cpp:73
unsigned mNumBlocks
size of caches (16)
Definition: Binaural.h:88
SampleBuffer mPrevOutR
previous sample window from the IFFT
Definition: Binaural.h:86
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
unsigned windowSize()
The size of the analysis window (in samples).
Definition: BinauralDB.cpp:463
#define cmac(in1, in2, out)
Definition: BinauralDB.h:89
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
#define SUM_DOWNS
Binaural.h – Specification of the HRTF-based binaural spatializer. This is the CSL 5 version that us...
Definition: Binaural.h:35
unsigned mNumBlocksToSum
blocks to include in sum per HRTF
Definition: Binaural.h:60
HRTFDatabase: has a vector of HRTFs and can access them by position – hrtfAt(CPoint) – or by index ...
Definition: BinauralDB.h:128
Temp Spatial Sound Source.
Definition: SpatialSource.h:29
float sample
(could be changed to int, or double)
Definition: CSL_Types.h:191
void * cache()
Returns an instance of its cache data per sound source.
Definition: Binaural.cpp:65
BufferContentType mType
Data type flag set the internal size variables (no buffer allocation takes place) ...
Definition: CSL_Core.h:124
HRTF: holds the data that corresponds to an HRTF for a single position. It has a list of complex buff...
Definition: BinauralDB.h:101
virtual bool isActive()
query whether I'm currently active (Envelopes can go inactive)
Definition: CSL_Core.h:273
#define SAFE_FREE(ptr)
Definition: CGestalt.h:137
BinauralSourceCache: used for caching previous state of spatial sources. This has the history of inpu...
Definition: Binaural.h:80
unsigned mNumBlocks
blocks per HRTF
Definition: Binaural.h:59
SampleComplexVector * mHrtfL
Definition: BinauralDB.h:112
virtual bool positionChanged()
Returns whether the sound source position changed since last block call.
Definition: SpatialSource.h:63
unsigned hrtfAt(CPoint srcPos)
answer the index of the HRTF nearest the given point
Definition: BinauralDB.cpp:434
unsigned mFramesPerBlock
frames per block
Definition: Binaural.h:61
void setCopyPolicy(BufferCopyPolicy ch)
get/set the receiver's buffer copy policy
Definition: CSL_Core.h:256
FFT complex spectral frames.
Definition: CSL_Core.h:73
#define FFT_DOWNS
Definition: Binaural.h:36
Buffer – the multi-channel sample buffer class (passed around between generators and IO guys)...
Definition: CSL_Core.h:106
ignore extra buffer channels
Definition: CSL_Core.h:213
void allocateBuffers()
fcn to malloc storage buffers
Definition: CSL_Core.cpp:122
SampleComplexVector mHOutR
buffers for the mixed HRTF & input data pre-IFFT
Definition: Binaural.h:69
unsigned * mHRTF
list of block IDs of the "split" hrtf
Definition: Binaural.h:87
void setNumChannels(unsigned ch)
get/set the receiver's number of outputs
Definition: CSL_Core.h:253
SampleComplexVector mHOutL
Definition: Binaural.h:69
sample SampleComplex[2]
array-of-2 complex # type (like FFTW)
Definition: CSL_Types.h:198
#define SAFE_MALLOC(ptr, type, len)
Useful Macros.
Definition: CGestalt.h:103
Base class of CSL exceptions (written upper-case). Has a string message.