CSL  6.0
Convolver2.cpp
Go to the documentation of this file.
1 ///
2 /// Convolver2.cpp -- The simplified convolution class.
3 /// This is an FFT-based convolver that uses the CSL FFT_Wrapper.
4 /// stephen@heaveneverywhere.com - 1905
5 //
6 // Copyright(C) 2019 Stephen T. Pope. All rights reserved.
7 // THIS IS UNPUBLISHED PROPRIETARY SOURCE CODE.
8 // The copyright notice above does not evidence any actual or intended
9 // publication of such source code.
10 ///
11 
12 #include "Convolver2.h"
13 #include "SoundFileJ.h"
14 #ifdef ARM
15 #include "arm_cmplx_mult_cmplx_f32.c" // ARM optimized math code
16 #endif
17 
18 using namespace csl;
19 
20 // General constructor
21 
22 Convolver2::Convolver2(UnitGenerator & in, char * inFName, unsigned chan, unsigned len, bool norm) :
23  Effect(in),
24  mFFTSize(len),
25  mNormOut(norm),
26  mFFTAnalyzer(len, CSL_FFT_COMPLEX, CSL_FFT_FORWARD),
27  mFFTSynthesizer(len, CSL_FFT_COMPLEX, CSL_FFT_INVERSE),
28  mFFTBuffer(1, len) {
29  mFlSize = mFFTSize * sizeof(sample);
30  mNumBufs = 0;
31  setIRFile(inFName, chan);
32 }
33 
34 Convolver2::Convolver2(UnitGenerator & in, float * irData, unsigned nTaps, unsigned len, bool norm) :
35  Effect(in),
36  mFFTSize(len),
37  mNormOut(norm),
38  mFFTAnalyzer(len, CSL_FFT_COMPLEX, CSL_FFT_FORWARD),
39  mFFTSynthesizer(len, CSL_FFT_COMPLEX, CSL_FFT_INVERSE),
40  mFFTBuffer(1, len) {
41  mFlSize = mFFTSize * sizeof(sample);
42  mNumBufs = 0;
43  setIRData(irData, nTaps);
44 }
45 
46 // Destructor frees everything
47 
49  free_buffers();
50 }
51 
52 // free_buffers actualy frees everything
53 
55  if (mNumBufs) {
56  for (unsigned i = 0; i < mNumBufs; i++) {
58  SAFE_FREE(mInputFFT[i]);
59  }
64  mNumBufs = 0;
65  }
66 }
67 
68 // if we're changing size, check buffers and allocate all new storage
69 
70 void Convolver2::checkBuffers(unsigned newNumBufs) {
71  if (newNumBufs == mNumBufs)
72  return;
73  unsigned fftlen = mFFTSize + 1;
74 // logMsg(kLogInfo, "Convolver allocating %u buffers of %d for IR len %d -- ", newNumBufs, fftlen, mNumTaps);
75  if (mNumBufs) // free if previously allocated
76  free_buffers();
77  SAFE_MALLOC(mFilterFFT, SampleComplexVector, newNumBufs); // allocate for storage of IR segments and input
79  SAFE_MALLOC(mSpectrumBuffer, SampleComplex, fftlen); // allocate temp buffers
80  SAFE_MALLOC(mLastOutput, sample, fftlen / 2);
81 
82  for (unsigned i = 0; i < newNumBufs; i++) { // loop to allocate/store the FFTs for each segment of the IR
83  SAFE_MALLOC(mInputFFT[i], SampleComplex, fftlen);
85 // logMsg("Bufs: %x - %x", mInputFFT[i], mFilterFFT[i]);
86  }
87 // logMsg("SBuf: %x\n", mSpectrumBuffer);
88  mNumBufs = newNumBufs;
89 }
90 
91 // set the IR file name; run the analysis ffts
92 
93 void Convolver2::setIRFile(char * inFName, unsigned chan) {
94  JSoundFile * inFile = JSoundFile::openSndfile(string(inFName));
95  unsigned frames = inFile->duration(); // # of frames in IR
96  if ( ! frames) {
97  logMsg(kLogFatal, "Convolver2 can't find IR file %s", inFName);
98  }
99  SampleBuffer data = 0;
100  if (chan >= inFile->numChannels())
101  data = inFile->buffer(0); // input pointer to 0th channel
102  else
103  data = inFile->buffer(chan); // input pointer to Nth channel
104  setIRData(data, frames);
105  delete inFile;
106 }
107 
108 // set the IR data array; run the analysis ffts
109 
110 void Convolver2::setIRData(float * data, unsigned nTaps) {
111  mNumTaps = nTaps;
112  unsigned fftlen = mFFTSize;
113  unsigned fftl2 = mFFTSize / 2;
114  unsigned newNumBufs = (nTaps / fftl2) + 1; // # windows needed for IR
115  mWindowCount = 0; // incremental counter
116 // float norm = 1.0f / (float) fftlen; // normalization factor
117  checkBuffers(newNumBufs);
118 
119  for (unsigned i = 0; i < mNumBufs; i++) { // now take fft of IR file
121  bzero(sPtr, fftlen * sizeof(SampleComplex)); // zero out the working FFT buffer before loop
122  unsigned offs = i * fftl2;
123  for (unsigned j = 0; j < fftl2; j++) { // fill bottom half of sample buffer from input
124  unsigned k = j + offs;
125  if (k >= nTaps) *sPtr++ = 0.0; // copy into sample buffer
126  else *sPtr++ = data[k]; // * norm;
127  }
129  // in-place complex fft - overwrites the input vectors
130  mFFTAnalyzer.nextBuffer(mFFTBuffer, mFFTBuffer);
131  }
132 }
133 
134 // Convolver2::nextBuffer() does the heavy lifting of the block-wise convolution
135 
136 void Convolver2::nextBuffer(Buffer & outputBuffer) throw (CException) {
137  unsigned numFrames = outputBuffer.mNumFrames; // local copies of important items
138  unsigned numBufs = mNumBufs;
139  unsigned windowCount = mWindowCount;
140  unsigned fftlen = mFFTSize;
141  unsigned wInp = windowCount % numBufs; // pseudo ring buffer addressing
142  unsigned rbufsize = numFrames * sizeof(sample); // I/O buffer size
143  unsigned cbufsize = fftlen * sizeof(sample); // FFT buffer size
144  unsigned cxbufsize = fftlen * sizeof(SampleComplex);// complex buffer size
145  float norm = 1.0f / (float) fftlen;
146 
147  if (numFrames != fftlen / 2) {
149  "Convolver2::nextBuffer # frames %d wrong for FFT size %d (use a block resizer).",
150  numFrames, mFFTSize);
151  return;
152  }
153  Effect::pullInput(numFrames); // get some input
154  SampleBuffer inp = mInputPtr; // pullInput sets mInputPtr
155  bzero(mInputFFT[wInp], cxbufsize); // zero out the working FFT buffer before loop
156  memcpy(mInputFFT[wInp], inp, rbufsize); // copy input into the mInputFFT[] buffer
157 
158 // memcpy(outputBuffer.buffer(0), inp, cbufsize); // testing 0 - just copy in to out
159 // return;
160  // plug the input into the FFT buffer
161  mFFTBuffer.setBuffer(0, (SampleBuffer) mInputFFT[wInp]);
162  // in-place complex fft - overwrites the input vectors
163  mFFTAnalyzer.nextBuffer(mFFTBuffer, mFFTBuffer);
164 
165  bzero(mSpectrumBuffer, cxbufsize); // zero out the summation FFT buffer before loop
166  for (unsigned i = 0; i < numBufs; i++) { // loop over IR windows
167  wInp = (numBufs + windowCount - i) % numBufs; // which input FFT to use
168  complex_multiply_accumulate( // sum complex vectors
169  mFilterFFT[i], // mult current IR window
170  mInputFFT[wInp], // by past input in reverse order
171  mSpectrumBuffer); // sum into mSpectrumBuffer
172  }
173 // bzero(mSpectrumBuffer, cxbufsize); // testing 1 - synth a (44100 / 512 * X) Hz sine
174 // mSpectrumBuffer[6][0] = 0.25f;
175 // wInp = windowCount % numBufs; // testing 2 - just resynth the input
176 // memcpy(mSpectrumBuffer, mInputFFT[wInp], cxbufsize);
177 // mNormOut = true;
178  // plug in spectral sum buffer for IFFT
179  mFFTBuffer.setBuffer(0, (SampleBuffer) mSpectrumBuffer);
180  // execute the IFFT for spectral domain convolution
181  mFFTSynthesizer.nextBuffer(mFFTBuffer, mFFTBuffer);
182 
183  SampleBuffer outP = outputBuffer.buffer(0); // copy real vector to output
184  SampleBuffer res = (SampleBuffer) mSpectrumBuffer;
185  SampleBuffer prev = mLastOutput;
186 
187  if (mNormOut) {
188  for (unsigned i = 0; i < numFrames; i++) {
189  *outP++ = (*res++ + *prev++) * norm; // sum and scale
190  }
191  } else
192  for (unsigned i = 0; i < numFrames; i++)
193  *outP++ = (*res++ + *prev++); // no normalization
194  // remember 2nd half of output for later
195  memcpy(mLastOutput, ((SampleBuffer) mSpectrumBuffer) + numFrames, rbufsize);
196  mWindowCount++;
197 }
198 
199 // ARM FFT:
200 // void arm_cfft_f32 ( const arm_cfft_instance_f32 * S,
201 // float32_t * p1,
202 // uint8_t ifftFlag,
203 // uint8_t bitReverseFlag
204 // )
205 //
206 //
207 
208 // complex_multiply_accumulate - fast complex MAC using interleaved complex arrays
209 // It's an ASM library call on the BBGW
210 
212  SampleComplexVector output) {
213 #ifdef ARM // use NEON code for this
214  arm_cmplx_mult_cmplx_f32((const float32_t *) left, (const float32_t *) right,
215  (float32_t *) output, (uint32_t) mFFTSize);
216 #else
217  SampleComplexVector loleft = left; // local copies of args
218  SampleComplexVector loright = right;
219  SampleComplexVector looutput = output;
220  float re, im; // temps
221  float leftRe, rightRe, leftIm, rightIm;
222  unsigned frame = mFFTSize;
223 // printf("MAC: %x - %x - %x (%u)\n", left, right, output, frame);
224  while (frame--) { // loop over frames
225  leftRe = (*loleft)[0]; // cache vector values
226  rightRe = (*loright)[0];
227  leftIm = (*loleft)[1];
228  rightIm = (*loright)[1];
229  re = (leftRe * rightRe) - (leftIm * rightIm); // complex multiply
230  im = (leftRe * rightIm) + (leftIm * rightRe);
231  (*looutput)[0] += re; // sum into work fft
232  (*looutput)[1] += im;
233  loleft++; // increment register pointers
234  loright++;
235  looutput++;
236  }
237 #endif
238 }
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
void pullInput(Buffer &outputBuffer)
Definition: CSL_Core.cpp:1122
virtual void setBuffer(unsigned bufNum, SampleBuffer sPtr)
Definition: CSL_Core.h:158
AdditiveInstrument.h – Sum-of-sines synthesis instrument class.
Definition: Accessor.h:17
Effect – mix-in for classes that have unit generators as inputs (like filters).
Definition: CSL_Core.h:466
unsigned mFlSize
Definition: Convolver2.h:35
Convolver2(UnitGenerator &in, char *inFName, unsigned chan=0, unsigned len=512, bool norm=false)
Constructors.
Definition: Convolver2.cpp:22
unsigned mNumTaps
Definition: Convolver2.h:35
void free_buffers()
Definition: Convolver2.cpp:54
unsigned duration() const
number of frames in the sound file
Definition: SoundFileJ.cpp:94
void nextBuffer(Buffer &outputBuffer)
main nextBuffer call does the fft/ifft
Definition: Convolver2.cpp:136
JUCE sound file.
Definition: SoundFileJ.h:19
SampleComplexMatrix mInputFFT
A list of past input spectra.
Definition: Convolver2.h:38
virtual unsigned numChannels()
Definition: CSL_Core.h:252
float sample
(could be changed to int, or double)
Definition: CSL_Types.h:191
SampleComplexVector mSpectrumBuffer
temp summing complex vector
Definition: Convolver2.h:40
FFT_Wrapper mFFTAnalyzer
FFT analysis/synthesis wrappers.
Definition: Convolver2.h:43
#define SAFE_FREE(ptr)
Definition: CGestalt.h:137
static unsigned len
Definition: fft_N.c:39
virtual SampleBuffer buffer(unsigned bufNum)
Definition: SoundFile.h:134
SampleComplexMatrix mFilterFFT
list of IR ffts
Definition: Convolver2.h:39
void setIRFile(char *inFName, unsigned chan=0)
Definition: Convolver2.cpp:93
SampleBuffer mLastOutput
most-recent output (1/2 window)
Definition: Convolver2.h:41
void checkBuffers(unsigned newNumBufs)
alloc buffers
Definition: Convolver2.cpp:70
void setIRData(float *irData, unsigned nTaps)
Definition: Convolver2.cpp:110
Buffer – the multi-channel sample buffer class (passed around between generators and IO guys)...
Definition: CSL_Core.h:106
unsigned mWindowCount
Definition: Convolver2.h:35
forward declaration
Definition: CSL_Core.h:241
~Convolver2()
set the IR file name; runs the analysis ffts
Definition: Convolver2.cpp:48
void complex_multiply_accumulate(SampleComplexVector left, SampleComplexVector right, SampleComplexVector output)
fast complex MAC using non-interleaved complex arrays
Definition: Convolver2.cpp:211
unsigned mFFTSize
Definition: Convolver2.h:35
Buffer mFFTBuffer
buffer used for FFTs, no storage
Definition: Convolver2.h:45
static JSoundFile * openSndfile(string path, int start=-1, int stop=-1, bool doRead=true)
Factory methods.
Definition: SoundFileJ.cpp:17
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
unsigned mNumBufs
Definition: Convolver2.h:35
Base class of CSL exceptions (written upper-case). Has a string message.