2 Improved FFT and IFFT UGens for SuperCollider 3
3 Copyright (c) 2007-2008 Dan Stowell, incorporating code from
4 SuperCollider 3 Copyright (c) 2002 James McCartney.
7 This program is free software; you can redistribute it and/or modify
8 it under the terms of the GNU General Public License as published by
9 the Free Software Foundation; either version 2 of the License, or
10 (at your option) any later version.
12 This program is distributed in the hope that it will be useful,
13 but WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 GNU General Public License for more details.
17 You should have received a copy of the GNU General Public License
18 along with this program; if not, write to the Free Software
19 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
23 #include "FFT_UGens.h"
25 // We include vDSP even if not using for FFT, since we want to use some vectorised add/mul tricks
26 #if defined(__APPLE__) && !defined(SC_IPHONE)
27 #include "vecLib/vDSP.h"
30 struct FFTBase
: public Unit
35 int m_pos
, m_fullbufsize
, m_audiosize
; // "fullbufsize" includes any zero-padding, "audiosize" does not.
36 int m_log2n_full
, m_log2n_audio
;
42 int m_hopsize
, m_shuntsize
; // These add up to m_audiosize
48 struct FFT
: public FFTBase
53 struct IFFT
: public FFTBase
59 struct FFTTrigger
: public FFTBase
61 int m_numPeriods
, m_periodsRemain
, m_polar
;
64 //////////////////////////////////////////////////////////////////////////////////////////////////
68 void FFT_Ctor(FFT
* unit
);
69 void FFT_ClearUnitOutputs(FFT
*unit
, int wrongNumSamples
);
70 void FFT_next(FFT
*unit
, int inNumSamples
);
71 void FFT_Dtor(FFT
* unit
);
73 void IFFT_Ctor(IFFT
* unit
);
74 void IFFT_next(IFFT
*unit
, int inNumSamples
);
75 void IFFT_Dtor(IFFT
* unit
);
77 void FFTTrigger_Ctor(FFTTrigger
* unit
);
78 void FFTTrigger_next(FFTTrigger
* unit
, int inNumSamples
);
81 //////////////////////////////////////////////////////////////////////////////////////////////////
83 static int FFTBase_Ctor(FFTBase
*unit
, int frmsizinput
)
85 World
*world
= unit
->mWorld
;
87 uint32 bufnum
= (uint32
)ZIN0(0);
89 if (bufnum
>= world
->mNumSndBufs
) {
90 int localBufNum
= bufnum
- world
->mNumSndBufs
;
91 Graph
*parent
= unit
->mParent
;
92 if(localBufNum
<= parent
->localMaxBufNum
) {
93 buf
= parent
->mLocalSndBufs
+ localBufNum
;
95 if(unit
->mWorld
->mVerbosity
> -1){ Print("FFTBase_Ctor error: invalid buffer number: %i.\n", bufnum
); }
99 buf
= world
->mSndBufs
+ bufnum
;
103 if(unit
->mWorld
->mVerbosity
> -1){ Print("FFTBase_Ctor error: Buffer %i not initialised.\n", bufnum
); }
107 unit
->m_fftsndbuf
= buf
;
108 unit
->m_fftbufnum
= bufnum
;
109 unit
->m_fullbufsize
= buf
->samples
;
110 int framesize
= (int)ZIN0(frmsizinput
);
112 unit
->m_audiosize
= buf
->samples
;
114 unit
->m_audiosize
= sc_min(buf
->samples
, framesize
);
116 unit
->m_log2n_full
= LOG2CEIL(unit
->m_fullbufsize
);
117 unit
->m_log2n_audio
= LOG2CEIL(unit
->m_audiosize
);
120 // Although FFTW allows non-power-of-two buffers (vDSP doesn't), this would complicate the windowing, so we don't allow it.
121 if (!ISPOWEROFTWO(unit
->m_fullbufsize
)) {
122 Print("FFTBase_Ctor error: buffer size (%i) not a power of two.\n", unit
->m_fullbufsize
);
125 else if (!ISPOWEROFTWO(unit
->m_audiosize
)) {
126 Print("FFTBase_Ctor error: audio frame size (%i) not a power of two.\n", unit
->m_audiosize
);
129 else if (unit
->m_audiosize
< SC_FFT_MINSIZE
||
130 (((int)(unit
->m_audiosize
/ unit
->mWorld
->mFullRate
.mBufLength
))
131 * unit
->mWorld
->mFullRate
.mBufLength
!= unit
->m_audiosize
)) {
132 Print("FFTBase_Ctor error: audio frame size (%i) not a multiple of the block size (%i).\n", unit
->m_audiosize
, unit
->mWorld
->mFullRate
.mBufLength
);
143 //////////////////////////////////////////////////////////////////////////////////////////////////
145 void FFT_Ctor(FFT
*unit
)
147 unit
->m_wintype
= (int)ZIN0(3); // wintype may be used by the base ctor
148 if(!FFTBase_Ctor(unit
, 5)){
149 SETCALC(FFT_ClearUnitOutputs
);
150 // These zeroes are to prevent the dtor freeing things that don't exist:
155 int fullbufsize
= unit
->m_fullbufsize
* sizeof(float);
156 int audiosize
= unit
->m_audiosize
* sizeof(float);
158 int hopsize
= (int)(sc_max(sc_min(ZIN0(2), 1.f
), 0.f
) * unit
->m_audiosize
);
159 if (hopsize
< unit
->mWorld
->mFullRate
.mBufLength
) {
160 Print("FFT_Ctor: hopsize smaller than SC's block size (%i) - automatically corrected.\n", hopsize
, unit
->mWorld
->mFullRate
.mBufLength
);
161 hopsize
= unit
->mWorld
->mFullRate
.mBufLength
;
162 } else if (((int)(hopsize
/ unit
->mWorld
->mFullRate
.mBufLength
)) * unit
->mWorld
->mFullRate
.mBufLength
164 Print("FFT_Ctor: hopsize (%i) not an exact multiple of SC's block size (%i) - automatically corrected.\n", hopsize
, unit
->mWorld
->mFullRate
.mBufLength
);
165 hopsize
= ((int)(hopsize
/ unit
->mWorld
->mFullRate
.mBufLength
)) * unit
->mWorld
->mFullRate
.mBufLength
;
167 unit
->m_hopsize
= hopsize
;
168 unit
->m_shuntsize
= unit
->m_audiosize
- hopsize
;
170 unit
->m_inbuf
= (float*)RTAlloc(unit
->mWorld
, audiosize
);
172 SCWorld_Allocator
alloc(ft
, unit
->mWorld
);
173 unit
->m_scfft
= scfft_create(unit
->m_fullbufsize
, unit
->m_audiosize
, (SCFFT_WindowFunction
)unit
->m_wintype
, unit
->m_inbuf
,
174 unit
->m_fftsndbuf
->data
, kForward
, alloc
);
176 memset(unit
->m_inbuf
, 0, audiosize
);
178 //Print("FFT_Ctor: hopsize %i, shuntsize %i, bufsize %i, wintype %i, \n",
179 // unit->m_hopsize, unit->m_shuntsize, unit->m_bufsize, unit->m_wintype);
181 if (INRATE(1) == calc_FullRate
) {
182 unit
->m_numSamples
= unit
->mWorld
->mFullRate
.mBufLength
;
184 unit
->m_numSamples
= 1;
190 void FFT_Dtor(FFT
*unit
)
192 SCWorld_Allocator
alloc(ft
, unit
->mWorld
);
194 scfft_destroy(unit
->m_scfft
, alloc
);
197 RTFree(unit
->mWorld
, unit
->m_inbuf
);
200 // Ordinary ClearUnitOutputs outputs zero, potentially telling the IFFT (+ PV UGens) to act on buffer zero, so let's skip that:
201 void FFT_ClearUnitOutputs(FFT
*unit
, int wrongNumSamples
)
206 void FFT_next(FFT
*unit
, int wrongNumSamples
)
209 float *out
= unit
->m_inbuf
+ unit
->m_pos
+ unit
->m_shuntsize
;
211 // int numSamples = unit->mWorld->mFullRate.mBufLength;
212 int numSamples
= unit
->m_numSamples
;
215 memcpy(out
, in
, numSamples
* sizeof(float));
217 unit
->m_pos
+= numSamples
;
219 bool gate
= ZIN0(4) > 0.f
; // Buffer shunting continues, but no FFTing
221 if (unit
->m_pos
!= unit
->m_hopsize
|| !unit
->m_fftsndbuf
->data
|| unit
->m_fftsndbuf
->samples
!= unit
->m_fullbufsize
) {
222 if(unit
->m_pos
== unit
->m_hopsize
)
229 scfft_dofft(unit
->m_scfft
);
230 unit
->m_fftsndbuf
->coord
= coord_Complex
;
231 ZOUT0(0) = unit
->m_fftbufnum
;
235 // Shunt input buf down
236 memcpy(unit
->m_inbuf
, unit
->m_inbuf
+ unit
->m_hopsize
, unit
->m_shuntsize
* sizeof(float));
240 /////////////////////////////////////////////////////////////////////////////////////////////
242 void IFFT_Ctor(IFFT
* unit
){
243 unit
->m_wintype
= (int)ZIN0(1); // wintype may be used by the base ctor
244 if(!FFTBase_Ctor(unit
, 2)){
245 SETCALC(*ClearUnitOutputs
);
246 // These zeroes are to prevent the dtor freeing things that don't exist:
251 // This will hold the transformed and progressively overlap-added data ready for outputting.
252 unit
->m_olabuf
= (float*)RTAlloc(unit
->mWorld
, unit
->m_audiosize
* sizeof(float));
253 memset(unit
->m_olabuf
, 0, unit
->m_audiosize
* sizeof(float));
255 SCWorld_Allocator
alloc(ft
, unit
->mWorld
);
256 unit
->m_scfft
= scfft_create(unit
->m_fullbufsize
, unit
->m_audiosize
, (SCFFT_WindowFunction
)unit
->m_wintype
, unit
->m_fftsndbuf
->data
,
257 unit
->m_fftsndbuf
->data
, kBackward
, alloc
);
259 // "pos" will be reset to zero when each frame comes in. Until then, the following ensures silent output at first:
260 unit
->m_pos
= 0; //unit->m_audiosize;
262 if (unit
->mCalcRate
== calc_FullRate
) {
263 unit
->m_numSamples
= unit
->mWorld
->mFullRate
.mBufLength
;
265 unit
->m_numSamples
= 1;
271 void IFFT_Dtor(IFFT
* unit
)
274 RTFree(unit
->mWorld
, unit
->m_olabuf
);
276 SCWorld_Allocator
alloc(ft
, unit
->mWorld
);
278 scfft_destroy(unit
->m_scfft
, alloc
);
281 void IFFT_next(IFFT
*unit
, int wrongNumSamples
)
283 float *out
= OUT(0); // NB not ZOUT0
285 // Load state from struct into local scope
286 int pos
= unit
->m_pos
;
287 int fullbufsize
= unit
->m_fullbufsize
;
288 int audiosize
= unit
->m_audiosize
;
289 // int numSamples = unit->mWorld->mFullRate.mBufLength;
290 int numSamples
= unit
->m_numSamples
;
291 float *olabuf
= unit
->m_olabuf
;
292 float fbufnum
= ZIN0(0);
294 // Only run the IFFT if we're receiving a new block of input data - otherwise just output data already received
296 // Ensure it's in cartesian format, not polar
297 ToComplexApx(unit
->m_fftsndbuf
);
299 float* fftbuf
= unit
->m_fftsndbuf
->data
;
301 scfft_doifft(unit
->m_scfft
);
303 // Then shunt the "old" time-domain output down by one hop
305 int shuntsamps
= audiosize
- hopsamps
;
306 if(hopsamps
!= audiosize
) // There's only copying to be done if the position isn't all the way to the end of the buffer
307 memcpy(olabuf
, olabuf
+hopsamps
, shuntsamps
* sizeof(float));
309 // Then mix the "new" time-domain data in - adding at first, then just setting (copying) where the "old" is supposed to be zero.
310 #if defined(__APPLE__) && !defined(SC_IPHONE)
311 vDSP_vadd(olabuf
, 1, fftbuf
, 1, olabuf
, 1, shuntsamps
);
313 // NB we re-use the "pos" variable temporarily here for write rather than read
314 for(pos
= 0; pos
< shuntsamps
; ++pos
){
315 olabuf
[pos
] += fftbuf
[pos
];
318 memcpy(olabuf
+ shuntsamps
, fftbuf
+ shuntsamps
, (hopsamps
) * sizeof(float));
320 // Move the pointer back to zero, which is where playback will next begin
323 } // End of has-the-chain-fired
325 // Now we can output some stuff, as long as there is still data waiting to be output.
326 // If there is NOT data waiting to be output, we output zero. (Either irregular/negative-overlap
327 // FFT firing, or FFT has given up, or at very start of execution.)
329 ClearUnitOutputs(unit
, numSamples
);
331 memcpy(out
, olabuf
+ pos
, numSamples
* sizeof(float));
337 /////////////////////////////////////////////////////////////////////////////////////////////
339 void FFTTrigger_Ctor(FFTTrigger
*unit
)
341 World
*world
= unit
->mWorld
;
344 uint32 bufnum = (uint32)IN0(0);
345 Print("FFTTrigger_Ctor: bufnum is %i\n", bufnum);
346 if (bufnum >= world->mNumSndBufs) bufnum = 0;
347 SndBuf *buf = world->mSndBufs + bufnum;
351 uint32 bufnum
= (uint32
)IN0(0);
352 //Print("FFTTrigger_Ctor: bufnum is %i\n", bufnum);
354 if (bufnum
>= world
->mNumSndBufs
) {
355 int localBufNum
= bufnum
- world
->mNumSndBufs
;
356 Graph
*parent
= unit
->mParent
;
357 if(localBufNum
<= parent
->localMaxBufNum
) {
358 buf
= parent
->mLocalSndBufs
+ localBufNum
;
361 buf
= world
->mSndBufs
+ bufnum
;
364 buf
= world
->mSndBufs
+ bufnum
;
369 unit
->m_fftsndbuf
= buf
;
370 unit
->m_fftbufnum
= bufnum
;
371 unit
->m_fullbufsize
= buf
->samples
;
373 int numSamples
= unit
->mWorld
->mFullRate
.mBufLength
;
374 float dataHopSize
= IN0(1);
375 int initPolar
= unit
->m_polar
= (int)IN0(2);
376 unit
->m_numPeriods
= unit
->m_periodsRemain
= (int)(((float)unit
->m_fullbufsize
* dataHopSize
) / numSamples
) - 1;
378 buf
->coord
= (IN0(2) == 1.f
) ? coord_Polar
: coord_Complex
;
381 SETCALC(FFTTrigger_next
);
384 void FFTTrigger_next(FFTTrigger
*unit
, int inNumSamples
)
386 if (unit
->m_periodsRemain
> 0) {
388 unit
->m_periodsRemain
--;
390 ZOUT0(0) = unit
->m_fftbufnum
;
392 unit
->m_periodsRemain
= unit
->m_numPeriods
;
397 void initFFT(InterfaceTable
*inTable
)
402 DefineDtorUnit(IFFT
);
403 DefineSimpleUnit(FFTTrigger
);