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 audiosize
= unit
->m_audiosize
* sizeof(float);
157 int hopsize
= (int)(sc_max(sc_min(ZIN0(2), 1.f
), 0.f
) * unit
->m_audiosize
);
158 if (hopsize
< unit
->mWorld
->mFullRate
.mBufLength
) {
159 Print("FFT_Ctor: hopsize smaller than SC's block size (%i) - automatically corrected.\n", hopsize
, unit
->mWorld
->mFullRate
.mBufLength
);
160 hopsize
= unit
->mWorld
->mFullRate
.mBufLength
;
161 } else if (((int)(hopsize
/ unit
->mWorld
->mFullRate
.mBufLength
)) * unit
->mWorld
->mFullRate
.mBufLength
163 Print("FFT_Ctor: hopsize (%i) not an exact multiple of SC's block size (%i) - automatically corrected.\n", hopsize
, unit
->mWorld
->mFullRate
.mBufLength
);
164 hopsize
= ((int)(hopsize
/ unit
->mWorld
->mFullRate
.mBufLength
)) * unit
->mWorld
->mFullRate
.mBufLength
;
166 unit
->m_hopsize
= hopsize
;
167 unit
->m_shuntsize
= unit
->m_audiosize
- hopsize
;
169 unit
->m_inbuf
= (float*)RTAlloc(unit
->mWorld
, audiosize
);
171 SCWorld_Allocator
alloc(ft
, unit
->mWorld
);
172 unit
->m_scfft
= scfft_create(unit
->m_fullbufsize
, unit
->m_audiosize
, (SCFFT_WindowFunction
)unit
->m_wintype
, unit
->m_inbuf
,
173 unit
->m_fftsndbuf
->data
, kForward
, alloc
);
175 memset(unit
->m_inbuf
, 0, audiosize
);
177 //Print("FFT_Ctor: hopsize %i, shuntsize %i, bufsize %i, wintype %i, \n",
178 // unit->m_hopsize, unit->m_shuntsize, unit->m_bufsize, unit->m_wintype);
180 if (INRATE(1) == calc_FullRate
) {
181 unit
->m_numSamples
= unit
->mWorld
->mFullRate
.mBufLength
;
183 unit
->m_numSamples
= 1;
189 void FFT_Dtor(FFT
*unit
)
191 SCWorld_Allocator
alloc(ft
, unit
->mWorld
);
193 scfft_destroy(unit
->m_scfft
, alloc
);
196 RTFree(unit
->mWorld
, unit
->m_inbuf
);
199 // Ordinary ClearUnitOutputs outputs zero, potentially telling the IFFT (+ PV UGens) to act on buffer zero, so let's skip that:
200 void FFT_ClearUnitOutputs(FFT
*unit
, int wrongNumSamples
)
205 void FFT_next(FFT
*unit
, int wrongNumSamples
)
208 float *out
= unit
->m_inbuf
+ unit
->m_pos
+ unit
->m_shuntsize
;
210 // int numSamples = unit->mWorld->mFullRate.mBufLength;
211 int numSamples
= unit
->m_numSamples
;
214 memcpy(out
, in
, numSamples
* sizeof(float));
216 unit
->m_pos
+= numSamples
;
218 bool gate
= ZIN0(4) > 0.f
; // Buffer shunting continues, but no FFTing
220 if (unit
->m_pos
!= unit
->m_hopsize
|| !unit
->m_fftsndbuf
->data
|| unit
->m_fftsndbuf
->samples
!= unit
->m_fullbufsize
) {
221 if(unit
->m_pos
== unit
->m_hopsize
)
228 scfft_dofft(unit
->m_scfft
);
229 unit
->m_fftsndbuf
->coord
= coord_Complex
;
230 ZOUT0(0) = unit
->m_fftbufnum
;
234 // Shunt input buf down
235 memcpy(unit
->m_inbuf
, unit
->m_inbuf
+ unit
->m_hopsize
, unit
->m_shuntsize
* sizeof(float));
239 /////////////////////////////////////////////////////////////////////////////////////////////
241 void IFFT_Ctor(IFFT
* unit
){
242 unit
->m_wintype
= (int)ZIN0(1); // wintype may be used by the base ctor
243 if(!FFTBase_Ctor(unit
, 2)){
244 SETCALC(*ClearUnitOutputs
);
245 // These zeroes are to prevent the dtor freeing things that don't exist:
250 // This will hold the transformed and progressively overlap-added data ready for outputting.
251 unit
->m_olabuf
= (float*)RTAlloc(unit
->mWorld
, unit
->m_audiosize
* sizeof(float));
252 memset(unit
->m_olabuf
, 0, unit
->m_audiosize
* sizeof(float));
254 SCWorld_Allocator
alloc(ft
, unit
->mWorld
);
255 unit
->m_scfft
= scfft_create(unit
->m_fullbufsize
, unit
->m_audiosize
, (SCFFT_WindowFunction
)unit
->m_wintype
, unit
->m_fftsndbuf
->data
,
256 unit
->m_fftsndbuf
->data
, kBackward
, alloc
);
258 // "pos" will be reset to zero when each frame comes in. Until then, the following ensures silent output at first:
259 unit
->m_pos
= 0; //unit->m_audiosize;
261 if (unit
->mCalcRate
== calc_FullRate
) {
262 unit
->m_numSamples
= unit
->mWorld
->mFullRate
.mBufLength
;
264 unit
->m_numSamples
= 1;
270 void IFFT_Dtor(IFFT
* unit
)
273 RTFree(unit
->mWorld
, unit
->m_olabuf
);
275 SCWorld_Allocator
alloc(ft
, unit
->mWorld
);
277 scfft_destroy(unit
->m_scfft
, alloc
);
280 void IFFT_next(IFFT
*unit
, int wrongNumSamples
)
282 float *out
= OUT(0); // NB not ZOUT0
284 // Load state from struct into local scope
285 int pos
= unit
->m_pos
;
286 int audiosize
= unit
->m_audiosize
;
287 // int numSamples = unit->mWorld->mFullRate.mBufLength;
288 int numSamples
= unit
->m_numSamples
;
289 float *olabuf
= unit
->m_olabuf
;
290 float fbufnum
= ZIN0(0);
292 // Only run the IFFT if we're receiving a new block of input data - otherwise just output data already received
294 // Ensure it's in cartesian format, not polar
295 ToComplexApx(unit
->m_fftsndbuf
);
297 float* fftbuf
= unit
->m_fftsndbuf
->data
;
299 scfft_doifft(unit
->m_scfft
);
301 // Then shunt the "old" time-domain output down by one hop
303 int shuntsamps
= audiosize
- hopsamps
;
304 if(hopsamps
!= audiosize
) // There's only copying to be done if the position isn't all the way to the end of the buffer
305 memcpy(olabuf
, olabuf
+hopsamps
, shuntsamps
* sizeof(float));
307 // Then mix the "new" time-domain data in - adding at first, then just setting (copying) where the "old" is supposed to be zero.
308 #if defined(__APPLE__) && !defined(SC_IPHONE)
309 vDSP_vadd(olabuf
, 1, fftbuf
, 1, olabuf
, 1, shuntsamps
);
311 // NB we re-use the "pos" variable temporarily here for write rather than read
312 for(pos
= 0; pos
< shuntsamps
; ++pos
){
313 olabuf
[pos
] += fftbuf
[pos
];
316 memcpy(olabuf
+ shuntsamps
, fftbuf
+ shuntsamps
, (hopsamps
) * sizeof(float));
318 // Move the pointer back to zero, which is where playback will next begin
321 } // End of has-the-chain-fired
323 // Now we can output some stuff, as long as there is still data waiting to be output.
324 // If there is NOT data waiting to be output, we output zero. (Either irregular/negative-overlap
325 // FFT firing, or FFT has given up, or at very start of execution.)
327 ClearUnitOutputs(unit
, numSamples
);
329 memcpy(out
, olabuf
+ pos
, numSamples
* sizeof(float));
335 /////////////////////////////////////////////////////////////////////////////////////////////
337 void FFTTrigger_Ctor(FFTTrigger
*unit
)
339 World
*world
= unit
->mWorld
;
342 uint32 bufnum = (uint32)IN0(0);
343 Print("FFTTrigger_Ctor: bufnum is %i\n", bufnum);
344 if (bufnum >= world->mNumSndBufs) bufnum = 0;
345 SndBuf *buf = world->mSndBufs + bufnum;
349 uint32 bufnum
= (uint32
)IN0(0);
350 //Print("FFTTrigger_Ctor: bufnum is %i\n", bufnum);
352 if (bufnum
>= world
->mNumSndBufs
) {
353 int localBufNum
= bufnum
- world
->mNumSndBufs
;
354 Graph
*parent
= unit
->mParent
;
355 if(localBufNum
<= parent
->localMaxBufNum
) {
356 buf
= parent
->mLocalSndBufs
+ localBufNum
;
359 buf
= world
->mSndBufs
+ bufnum
;
362 buf
= world
->mSndBufs
+ bufnum
;
367 unit
->m_fftsndbuf
= buf
;
368 unit
->m_fftbufnum
= bufnum
;
369 unit
->m_fullbufsize
= buf
->samples
;
371 int numSamples
= unit
->mWorld
->mFullRate
.mBufLength
;
372 float dataHopSize
= IN0(1);
373 unit
->m_numPeriods
= unit
->m_periodsRemain
= (int)(((float)unit
->m_fullbufsize
* dataHopSize
) / numSamples
) - 1;
375 buf
->coord
= (IN0(2) == 1.f
) ? coord_Polar
: coord_Complex
;
378 SETCALC(FFTTrigger_next
);
381 void FFTTrigger_next(FFTTrigger
*unit
, int inNumSamples
)
383 if (unit
->m_periodsRemain
> 0) {
385 unit
->m_periodsRemain
--;
387 ZOUT0(0) = unit
->m_fftbufnum
;
389 unit
->m_periodsRemain
= unit
->m_numPeriods
;
394 void initFFT(InterfaceTable
*inTable
)
399 DefineDtorUnit(IFFT
);
400 DefineSimpleUnit(FFTTrigger
);