CSL  6.0
Convolver.cpp
Go to the documentation of this file.
1 ///
2 /// Convolver.cpp -- The way-simplified convolution class.
3 // Copyright(C) 2019 Stephen T. Pope. All rights reserved.
4 // THIS IS UNPUBLISHED PROPRIETARY SOURCE CODE.
5 // The copyright notice above does not evidence any actual or intended
6 // publication of such source code.
7 ///
8 
9 #include "Convolver.h"
10 #include "SoundFileJ.h"
11 #include "fft_N.h"
12 
13 using namespace csl;
14 
15 // Simple constructor
16 
18  Effect(),
19  mFFTSize(len) {
20  mFlSize = mFFTSize * sizeof(sample);
21  mNumBufs = 0;
22 }
23 
24 Convolver::Convolver(char * inFName, unsigned len) : Convolver(len) {
25  setIRFile(inFName);
26 }
27 
28 Convolver::Convolver(UnitGenerator & in, char * inFName, unsigned chan, unsigned len) :
29  Effect(in),
30  mFFTSize(len) {
31  mFlSize = mFFTSize * sizeof(sample);
32  mNumBufs = 0;
33  setIRFile(inFName, chan);
34 }
35 
36 // Destructor frees everything
37 
39  free_buffers();
40 }
41 
42 // free_buffers actualy frees everything
43 
45  if (mNumBufs) {
46  for (unsigned i = 0; i < mNumBufs; i++) {
51  }
59  mNumBufs = 0;
60  }
61 }
62 // if we're changing size, check buffers and allocate all new storage
63 
64 void Convolver::checkBuffers(unsigned newNumBufs) {
65  if (newNumBufs == mNumBufs)
66  return;
67  unsigned fftlen = mFFTSize;
68  logMsg(kLogInfo, "Convolver allocating %u buffers of %d", newNumBufs, fftlen);
69  if (mNumBufs) // free if previously allocated
70  free_buffers();
71  SAFE_MALLOC(mSpectrumBufferRe, Sample, fftlen); // allocate temp buffers
73  SAFE_MALLOC(mLastOutput, Sample, fftlen / 2);
74 
75  SAFE_MALLOC(mFilterFFTRe, SampleBuffer, newNumBufs); // allocate for storage of IR segments and input
77  SAFE_MALLOC(mInputFFTRe, SampleBuffer, newNumBufs);
78  SAFE_MALLOC(mInputFFTIm, SampleBuffer, newNumBufs);
79 
80  for (unsigned i = 0; i < newNumBufs; i++) { // loop to allocate/store the FFTs for each segment of the IR
81  SAFE_MALLOC(mFilterFFTRe[i], Sample, fftlen);
82  SAFE_MALLOC(mFilterFFTIm[i], Sample, fftlen);
83  SAFE_MALLOC(mInputFFTRe[i], Sample, fftlen);
84  SAFE_MALLOC(mInputFFTIm[i], Sample, fftlen);
85 // logMsg("Bufs: %x @ %x - %x @ %x", mFilterFFTRe[i], mFilterFFTIm[i], mInputFFTRe[i], mInputFFTIm[i]);
86  }
87 // logMsg("SBufs: %x @ %x\n", mSpectrumBufferRe, mSpectrumBufferIm);
88  mNumBufs = newNumBufs;
89  Fft_setup(fftlen);
90 }
91 
92 // set the IR file name; runs the analysis ffts
93 
94 void Convolver::setIRFile(char * inFName, unsigned chan) {
95  JSoundFile * inFile = JSoundFile::openSndfile(string(inFName));
96  unsigned frames = inFile->duration(); // # of frames in IR
97  if ( ! frames) {
98  logMsg(kLogFatal, "Convolver can't find IR file %s", inFName);
99  }
100  SampleBuffer data = 0;
101  if (chan >= inFile->numChannels())
102  data = inFile->buffer(0); // input pointer to 0th channel
103  else
104  data = inFile->buffer(chan); // input pointer to Nth channel
105  unsigned fftlen = mFFTSize;
106  unsigned newNumBufs = (frames / fftlen) + 1; // # windows needed for IR
107  mWindowCount = 0; // incremental counter
108  float norm = 1.0f / (float) fftlen; // normalization factor
109  checkBuffers(newNumBufs);
110 
111  for (unsigned i = 0; i < mNumBufs; i++) { // now take fft of IR file
112  SampleBuffer sPtr = mFilterFFTRe[i];
113  unsigned offs = i * fftlen;
114  for (unsigned j = 0; j < fftlen; j++) { // fill bottom half of sample buffer from input
115  unsigned k = j + offs;
116  if (k >= frames) *sPtr++ = 0.0; // copy into sample buffer
117  else *sPtr++ = data[k] * norm;
118  }
119  memset(mFilterFFTIm[i], 0, mFlSize);
120  // in-place complex fft - overwrites the input vectors
121  bool succ = Fft_transform(mFilterFFTRe[i], mFilterFFTIm[i], mFFTSize);
122  if ( ! succ)
123  logMsg(kLogError, "Error doing fft of IR");
124  }
125 }
126 
127 // Convolver::nextBuffer() does the heavy lifting of the block-wise convolution
128 
129 void Convolver::nextBuffer(Buffer & outputBuffer) throw (CException) {
130  unsigned numFrames = outputBuffer.mNumFrames; // local copies of important items
131  unsigned numBufs = mNumBufs;
132  unsigned windowCount = mWindowCount;
133  unsigned fftlen = mFFTSize;
134  unsigned wInp = windowCount % numBufs; // pseudo ring buffer addressing
135  unsigned rbufsize = numFrames * sizeof(sample); // I/O buffer size
136  unsigned cbufsize = fftlen * sizeof(sample); // FFT buffer size
137  sample norm = 1.0f / (float) fftlen;
138 
139  if (numFrames != fftlen / 2) {
141  "Convolver::nextBuffer # frames %d wrong for FFT size %d (use a block resizer).",
142  numFrames, mFFTSize);
143  return;
144  }
145  Effect::pullInput(numFrames); // get some input
146  SampleBuffer inp = mInputPtr; // pullInput sets mInputPtr
147  bzero(mInputFFTRe[wInp], cbufsize); // zero out the working FFT buffer before loop
148  bzero(mInputFFTIm[wInp], cbufsize);
149  memcpy(mInputFFTRe[wInp], inp, rbufsize); // copy input into the mInputFFTRe[] buffer
150 
151 // memcpy(outputBuffer.buffer(0), inp, cbufsize); // testing, just copy i to o
152 // return;
153  // in-place complex fft - overwrites the input vectors
154  bool succ = Fft_transform(mInputFFTRe[wInp], mInputFFTIm[wInp], fftlen);
155  if ( ! succ)
156  logMsg(kLogError, "Error doing fft of input");
157 
158  bzero(mSpectrumBufferRe, cbufsize); // zero out the working FFT buffer before loop
159  bzero(mSpectrumBufferIm, cbufsize);
160  for (unsigned i = 0; i < numBufs; i++) { // loop over IR windows
161  wInp = (numBufs + windowCount - i) % numBufs; // which input FFT to use
162 // logMsg("CMAC: [%d : %d] %x @ %x * %x @ %x -> %x @ %x", i, wInp,
163 // mFilterFFTRe[i], mFilterFFTIm[i],
164 // mInputFFTRe[wInp], mInputFFTIm[wInp],
165 // mSpectrumBufferRe, mSpectrumBufferIm);
166  complex_multiply_accumulate( // sum complex vectors
167  mFilterFFTRe[i], mFilterFFTIm[i], // mult current IR window
168  mInputFFTRe[wInp], mInputFFTIm[wInp], // by past input in reverse order
169  mSpectrumBufferRe, mSpectrumBufferIm); // sum into mSpectrumBuffer
170  }
171 // bzero(mSpectrumBufferRe, cbufsize); // testing 1 - synth a 517 Hz (44100 / 512 * 6) sine
172 // bzero(mSpectrumBufferIm, cbufsize);
173 // mSpectrumBufferRe[6] = 600.0;
174 // wInp = windowCount % numBufs; // testing 2 - just resynth the input
175 // memcpy(mSpectrumBufferRe, mInputFFTRe[wInp], cbufsize);
176 // memcpy(mSpectrumBufferIm, mInputFFTIm[wInp], cbufsize);
177  // execute the IFFT for spectral domain convolution
178  succ = Fft_inverseTransform(mSpectrumBufferRe, mSpectrumBufferIm, fftlen);
179  if ( ! succ)
180  logMsg(kLogError, "Error doing ifft");
181 
182  SampleBuffer outP = outputBuffer.buffer(0); // copy real vector to output, cross-fading between windows
183  SampleBuffer re = mSpectrumBufferRe;
184  SampleBuffer im = mSpectrumBufferIm;
185  SampleBuffer las = mLastOutput;
186  float fadeI = 0.0f; // fade in/out scale factors
187  float fadeO = 1.0f;
188  float step = 1.0 / numFrames;
189 
190  for (unsigned i = 0; i < numFrames; i++) {
191  *outP++ = *re++ * norm; // no cross-fade
192 // float samp1 = *re++ * fadeI; // of cross-fade last window with this one
193 // float samp2 = *las++ * fadeO;
194 // *outP++ = (samp1 + samp2) * norm;
195 // fadeI += step;
196 // fadeO -= step;
197  }
198  memcpy(mLastOutput, mSpectrumBufferRe + numFrames, rbufsize); // copy the upper half of the result into the cache
199  mWindowCount++;
200 }
201 
202 // fast complex MAC using non-interleaved complex arrays
203 
205  SampleBuffer rightRe, SampleBuffer rightIm,
206  SampleBuffer outRe, SampleBuffer outIm) {
207  SampleBuffer loleftR = leftRe; // local copies of args
208  SampleBuffer loleftI = leftIm;
209  SampleBuffer lorightR = rightRe;
210  SampleBuffer lorightI = rightIm;
211  SampleBuffer looutR = outRe;
212  SampleBuffer looutI = outIm;
213 // logMsg("CMAC: %x @ %x * %x @ %x -> %x @ %x", leftRe, leftIm, rightRe, rightIm, outRe, outIm);
214  unsigned frame = mFFTSize;
215  while (frame--) { // loop over frames
216  sample lRe = *loleftR++; // cache vector values
217  sample lIm = *loleftI++;
218  sample rRe = *lorightR++;
219  sample rIm = *lorightI++;
220  sample re = (lRe * rRe) - (lIm * rIm); // complex multiply
221  sample im = (lRe * rIm) + (lIm * rRe);
222  *looutR++ += re; // sum into work fft
223  *looutI++ += im;
224  }
225 }
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
void pullInput(Buffer &outputBuffer)
Definition: CSL_Core.cpp:1122
Convolver(unsigned len=512)
Constructors.
Definition: Convolver.cpp:17
void checkBuffers(unsigned newNumBufs)
alloc buffers
Definition: Convolver.cpp:64
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
float Sample
the same, written upper-case
Definition: CSL_Types.h:192
bool Fft_inverseTransform(CFTTYPE real[], CFTTYPE imag[], size_t n)
Definition: fft_N.c:87
void Fft_setup(size_t n)
Definition: fft_N.c:42
unsigned duration() const
number of frames in the sound file
Definition: SoundFileJ.cpp:94
unsigned mWindowCount
Definition: Convolver.h:37
SampleBuffer mSpectrumBufferIm
Definition: Convolver.h:44
unsigned mFFTSize
Definition: Convolver.h:37
~Convolver()
set the IR file name; runs the analysis ffts
Definition: Convolver.cpp:38
JUCE sound file.
Definition: SoundFileJ.h:19
virtual unsigned numChannels()
Definition: CSL_Core.h:252
SampleBufferArray mFilterFFTIm
Definition: Convolver.h:40
float sample
(could be changed to int, or double)
Definition: CSL_Types.h:191
The Convolver is a CSL Effect.
Definition: Convolver.h:24
SampleBufferArray mInputFFTRe
A list of past input spectra.
Definition: Convolver.h:41
bool Fft_transform(CFTTYPE real[], CFTTYPE imag[], size_t n)
Definition: fft_N.c:72
#define SAFE_FREE(ptr)
Definition: CGestalt.h:137
unsigned mFlSize
Definition: Convolver.h:37
static unsigned len
Definition: fft_N.c:39
virtual SampleBuffer buffer(unsigned bufNum)
Definition: SoundFile.h:134
SampleBuffer mSpectrumBufferRe
current summation buffer
Definition: Convolver.h:43
void nextBuffer(Buffer &outputBuffer)
main nextBuffer call does the fft/ifft
Definition: Convolver.cpp:129
Buffer – the multi-channel sample buffer class (passed around between generators and IO guys)...
Definition: CSL_Core.h:106
SampleBufferArray mFilterFFTRe
A ring buffer of IR fft buffers.
Definition: Convolver.h:39
void free_buffers()
Definition: Convolver.cpp:44
SampleBuffer mLastOutput
most-recent output
Definition: Convolver.h:45
forward declaration
Definition: CSL_Core.h:241
Enumeration for log message severity level.
Definition: CGestalt.h:155
SampleBufferArray mInputFFTIm
Definition: Convolver.h:42
unsigned mNumBufs
Definition: Convolver.h:37
static JSoundFile * openSndfile(string path, int start=-1, int stop=-1, bool doRead=true)
Factory methods.
Definition: SoundFileJ.cpp:17
#define SAFE_MALLOC(ptr, type, len)
Useful Macros.
Definition: CGestalt.h:103
void complex_multiply_accumulate(SampleBuffer leftRe, SampleBuffer leftIm, SampleBuffer rightRe, SampleBuffer rightIm, SampleBuffer outRe, SampleBuffer outIm)
fast complex MAC using non-interleaved complex arrays
Definition: Convolver.cpp:204
Base class of CSL exceptions (written upper-case). Has a string message.
void setIRFile(char *inFName, unsigned chan=0)
Definition: Convolver.cpp:94