3 "Unpack FFT" UGens for SuperCollider 3.
4 Copyright (c) 2007 Dan Stowell. All rights reserved.
6 (Written during the SC Symposium 2007! Thanks to all whose conversation fed into this.)
9 This program is free software; you can redistribute it and/or modify
10 it under the terms of the GNU General Public License as published by
11 the Free Software Foundation; either version 2 of the License, or
12 (at your option) any later version.
14 This program is distributed in the hope that it will be useful,
15 but WITHOUT ANY WARRANTY; without even the implied warranty of
16 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 GNU General Public License for more details.
19 You should have received a copy of the GNU General Public License
20 along with this program; if not, write to the Free Software
21 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
25 #include "SC_PlugIn.h"
26 #include "SCComplex.h"
27 #include "FFT_UGens.h"
29 struct Unpack1FFT
: Unit
{
31 int latestMomentProcessed
; // To avoid processing a given FFT frame more than once
35 bool wantmag
; // yes for mag, no for phase
38 //int numOldSkipped; // for debug
41 struct PackFFT
: Unit
{
42 int bufsize
, numinvals
, frombin
, tobin
;
46 //////////////////////////////////////////////////////////////////////////////////////////////////
50 void Unpack1FFT_Ctor(Unpack1FFT
*unit
);
51 void Unpack1FFT_next_dc(Unpack1FFT
*unit
, int inNumSamples
);
52 void Unpack1FFT_next_nyq(Unpack1FFT
*unit
, int inNumSamples
);
53 void Unpack1FFT_next_mag(Unpack1FFT
*unit
, int inNumSamples
);
54 void Unpack1FFT_next_phase(Unpack1FFT
*unit
, int inNumSamples
);
56 void PackFFT_Ctor(PackFFT
*unit
);
57 void PackFFT_Dtor(PackFFT
*unit
);
58 void PackFFT_next(PackFFT
*unit
, int inNumSamples
);
63 ////////////////////////////////////////////////////////////////////////////////////////////////////////
66 void Unpack1FFT_Ctor(Unpack1FFT
* unit
)
68 unit
->bufsize
= (int)ZIN0(1);
69 unit
->latestMomentProcessed
= -1;
70 //unit->numOldSkipped = 0;
74 unit
->binindex
= (int)ZIN0(2);
78 if(unit
->binindex
== 0){
79 SETCALC(Unpack1FFT_next_dc
);
80 }else if(unit
->binindex
== unit
->bufsize
>> 1){
81 SETCALC(Unpack1FFT_next_nyq
);
83 SETCALC(Unpack1FFT_next_mag
);
87 if(unit
->binindex
== 0){
88 SETCALC(*ClearUnitOutputs
);
89 }else if(unit
->binindex
== unit
->bufsize
>> 1){
90 SETCALC(*ClearUnitOutputs
);
92 SETCALC(Unpack1FFT_next_phase
);
98 #define UNPACK1FFT_NEXT_COMMON float fbufnum = ZIN0(0); \
99 if (fbufnum < 0.f) { \
100 if(unit->mWorld->mVerbosity > -1){ \
101 Print("Unpack1FFT_next: warning, fbufnum < 0\n"); \
103 ZOUT0(0) = unit->outval; \
106 uint32 ibufnum = (uint32)fbufnum; \
107 World *world = unit->mWorld; \
109 if (ibufnum >= world->mNumSndBufs) { \
110 int localBufNum = ibufnum - world->mNumSndBufs; \
111 Graph *parent = unit->mParent; \
112 if(localBufNum <= parent->localBufNum) { \
113 buf = parent->mLocalSndBufs + localBufNum; \
115 buf = world->mSndBufs; \
116 if(unit->mWorld->mVerbosity > -1){ \
117 Print("Unpack1FFT_next: warning, bufnum too large: i%\n", ibufnum); \
121 buf = world->mSndBufs + ibufnum; \
123 int binindex __attribute__((__unused__)) = unit->binindex; \
125 SCComplexBuf *p = ToComplexApx(buf); \
129 void Unpack1FFT_next_mag(Unpack1FFT *unit, int inNumSamples)
131 if(unit
->latestMomentProcessed
!= unit
->mWorld
->mBufCounter
){
132 UNPACK1FFT_NEXT_COMMON
134 unit
->outval
= hypotf(p
->bin
[binindex
-1].real
, p
->bin
[binindex
-1].imag
);
136 unit
->latestMomentProcessed
= unit
->mWorld
->mBufCounter
; // So we won't copy it again, not this frame anyway
137 //unit->numOldSkipped = 0;
139 //Print("skipold{%i,%i}", unit->mWorld->mBufCounter, ++unit->numOldSkipped);
140 //Print("Calculation previously done - skipping. unit->mWorld->mBufCounter = %i\n", unit->mWorld->mBufCounter);
143 ZOUT0(0) = unit
->outval
;
146 void Unpack1FFT_next_phase(Unpack1FFT
*unit
, int inNumSamples
)
148 if(unit
->latestMomentProcessed
!= unit
->mWorld
->mBufCounter
){
149 UNPACK1FFT_NEXT_COMMON
151 unit
->outval
= atan2(p
->bin
[binindex
-1].imag
, p
->bin
[binindex
-1].real
);
153 unit
->latestMomentProcessed
= unit
->mWorld
->mBufCounter
; // So we won't copy it again, not this frame anyway
154 //unit->numOldSkipped = 0;
156 //Print("skipold{%i,%i}", unit->mWorld->mBufCounter, ++unit->numOldSkipped);
157 //Print("Calculation previously done - skipping. unit->mWorld->mBufCounter = %i\n", unit->mWorld->mBufCounter);
160 ZOUT0(0) = unit
->outval
;
163 void Unpack1FFT_next_dc(Unpack1FFT
*unit
, int inNumSamples
)
165 if(unit
->latestMomentProcessed
!= unit
->mWorld
->mBufCounter
){
166 UNPACK1FFT_NEXT_COMMON
168 unit
->outval
= p
->dc
;
170 unit
->latestMomentProcessed
= unit
->mWorld
->mBufCounter
; // So we won't copy it again, not this frame anyway
171 //unit->numOldSkipped = 0;
173 //Print("skipold{%i,%i}", unit->mWorld->mBufCounter, ++unit->numOldSkipped);
174 //Print("Calculation previously done - skipping. unit->mWorld->mBufCounter = %i\n", unit->mWorld->mBufCounter);
177 ZOUT0(0) = unit
->outval
;
180 void Unpack1FFT_next_nyq(Unpack1FFT
*unit
, int inNumSamples
)
182 if(unit
->latestMomentProcessed
!= unit
->mWorld
->mBufCounter
){
183 UNPACK1FFT_NEXT_COMMON
185 unit
->outval
= p
->nyq
;
187 unit
->latestMomentProcessed
= unit
->mWorld
->mBufCounter
; // So we won't copy it again, not this frame anyway
188 //unit->numOldSkipped = 0;
190 //Print("skipold{%i,%i}", unit->mWorld->mBufCounter, ++unit->numOldSkipped);
191 //Print("Calculation previously done - skipping. unit->mWorld->mBufCounter = %i\n", unit->mWorld->mBufCounter);
194 ZOUT0(0) = unit
->outval
;
197 ////////////////////////////////////////////////////////////////////////////////////////////////////////
200 void PackFFT_Ctor(PackFFT
* unit
)
202 SETCALC(PackFFT_next
);
204 unit
->bufsize
= (int)ZIN0(1);
205 unit
->frombin
= (int)ZIN0(2);
206 unit
->tobin
= (int)ZIN0(3);
207 unit
->zeroothers
= ZIN0(4) > 0;
208 unit
->numinvals
= (int)ZIN0(5);
210 // Print("PackFFT_Ctor: Passing chain through, val %g\n", ZIN0(0));
211 ZOUT0(0) = ZIN0(0); // Required: allows the buffer index to fall through nicely to the IFFT
214 #define PACKFFT_INPUTSOFFSET 6
216 void PackFFT_next(PackFFT
*unit
, int inNumSamples
)
218 /////////////////// cf PV_GET_BUF
219 float fbufnum
= ZIN0(0);
224 uint32 ibufnum
= (uint32
)fbufnum
;
225 World
*world
= unit
->mWorld
;
227 if (ibufnum
>= world
->mNumSndBufs
) {
228 int localBufNum
= ibufnum
- world
->mNumSndBufs
;
229 Graph
*parent
= unit
->mParent
;
230 if(localBufNum
<= parent
->localBufNum
) {
231 buf
= parent
->mLocalSndBufs
+ localBufNum
;
233 buf
= world
->mSndBufs
;
236 buf
= world
->mSndBufs
+ ibufnum
;
240 int numbins
= buf
->samples
- 2 >> 1;
241 /////////////////// cf PV_GET_BUF
243 //RM Print("PackFFT_next: fbufnum = %g\n", fbufnum);
245 int numinvals
= unit
->numinvals
;
247 SCComplexBuf
*p
= ToComplexApx(buf
);
249 int frombin
= unit
->frombin
;
250 int tobin
= unit
->tobin
;
251 int zeroothers
= unit
->zeroothers
;
254 // Load data from inputs into "p"
257 p
->dc
= DEMANDINPUT(PACKFFT_INPUTSOFFSET
);
258 }else if(zeroothers
){
261 //Print("New DC is %g\n", p->dc);
263 if(tobin
== numbins
+ 1){
264 //Print("PackFFT: Fetching nyquist from input #%i\n", (PACKFFT_INPUTSOFFSET + numinvals - 2 - frombin - frombin));
265 p
->nyq
= DEMANDINPUT(PACKFFT_INPUTSOFFSET
+ numinvals
- 2 - frombin
- frombin
);
266 }else if(zeroothers
){
269 //Print("New nyq (input #%i) is %g\n", numinvals, p->nyq);
271 // real, imag = (mag * cos(phase), mag * sin(phase))
273 int startat
= frombin
==0 ? 0 : frombin
-1;
274 int endbefore
= sc_min(numbins
, tobin
);
275 for(int i
= startat
; i
< endbefore
; i
++){
276 //Print("PackFFT: Fetching mag from input #%i\n", (i + i + PACKFFT_INPUTSOFFSET + 2 - frombin - frombin));
277 mag
= DEMANDINPUT(i
+ i
+ PACKFFT_INPUTSOFFSET
+ 2 - frombin
- frombin
);
278 phase
= DEMANDINPUT(i
+ i
+ PACKFFT_INPUTSOFFSET
+ 3 - frombin
- frombin
);
279 p
->bin
[i
].real
= mag
* cos(phase
);
280 p
->bin
[i
].imag
= mag
* sin(phase
);
282 //Print("New bin 7 is %g,%g\n", p->bin[7].real, p->bin[7].imag);
285 // Iterate through the ones we didn't fill in, wiping the magnitude
286 for(int i
=0; i
<startat
; i
++)
287 p
->bin
[i
].real
= p
->bin
[i
].imag
= 0.f
;
288 for(int i
=endbefore
; i
<numbins
; i
++)
289 p
->bin
[i
].real
= p
->bin
[i
].imag
= 0.f
;
293 //Print("PackFFT: fbufnum=%g, ibufnum=%i, numinvals=%i, frombin=%i, tobin=%i, zeroothers=%i\n",
294 // fbufnum, ibufnum, numinvals, frombin, tobin, zeroothers);
295 //Print("PackFFT: p->bin[4].real=%g, p->bin[4].imag=%g, p->bin[5].real=%g, p->bin[5].imag=%g\n",
296 // p->bin[4].real, p->bin[4].imag, p->bin[5].real, p->bin[5].imag);
298 ////////////////////////////////////////////////////////////////////////////////////////////////////////
301 PluginLoad(UnpackFFTUGens
)
305 DefineSimpleUnit(Unpack1FFT
);
306 DefineSimpleUnit(PackFFT
);