FFT: Prevent user from attempting hops smaller than SC's block size
[supercollider.git] / server / plugins / UnpackFFTUGens.cpp
blobf18bd56233528ec4c4a1289459ca013a68542581
1 /*
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 {
30 int bufsize;
31 int latestMomentProcessed; // To avoid processing a given FFT frame more than once
33 int binindex;
35 bool wantmag; // yes for mag, no for phase
36 float outval;
38 //int numOldSkipped; // for debug
41 struct PackFFT : Unit {
42 int bufsize, numinvals, frombin, tobin;
43 bool zeroothers;
46 //////////////////////////////////////////////////////////////////////////////////////////////////
48 extern "C"
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);
61 InterfaceTable *ft;
63 ////////////////////////////////////////////////////////////////////////////////////////////////////////
66 void Unpack1FFT_Ctor(Unpack1FFT* unit)
68 unit->bufsize = (int)ZIN0(1);
69 unit->latestMomentProcessed = -1;
70 //unit->numOldSkipped = 0;
72 unit->outval = 0.f;
74 unit->binindex = (int)ZIN0(2);
76 if(ZIN0(3) == 0.f){
77 // Mags
78 if(unit->binindex == 0){
79 SETCALC(Unpack1FFT_next_dc);
80 }else if(unit->binindex == unit->bufsize >> 1){
81 SETCALC(Unpack1FFT_next_nyq);
82 } else {
83 SETCALC(Unpack1FFT_next_mag);
85 }else{
86 // Phases
87 if(unit->binindex == 0){
88 SETCALC(*ClearUnitOutputs);
89 }else if(unit->binindex == unit->bufsize >> 1){
90 SETCALC(*ClearUnitOutputs);
91 } else {
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; \
104 return; \
106 uint32 ibufnum = (uint32)fbufnum; \
107 World *world = unit->mWorld; \
108 SndBuf *buf; \
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; \
114 } else { \
115 buf = world->mSndBufs; \
116 if(unit->mWorld->mVerbosity > -1){ \
117 Print("Unpack1FFT_next: warning, bufnum too large: i%\n", ibufnum); \
120 } else { \
121 buf = world->mSndBufs + ibufnum; \
123 int binindex = unit->binindex; \
124 LOCK_SNDBUF(buf); \
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;
138 //}else{
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;
155 //}else{
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;
172 //}else{
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;
189 //}else{
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);
220 if (fbufnum < 0.f) {
221 ZOUT0(0) = -1.f;
222 return;
224 uint32 ibufnum = (uint32)fbufnum;
225 World *world = unit->mWorld;
226 SndBuf *buf;
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;
232 } else {
233 buf = world->mSndBufs;
235 } else {
236 buf = world->mSndBufs + ibufnum;
238 LOCK_SNDBUF(buf);
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"
256 if(frombin==0){
257 p->dc = DEMANDINPUT(PACKFFT_INPUTSOFFSET);
258 }else if(zeroothers){
259 p->dc = 0.f;
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){
267 p->nyq = 0.f;
269 //Print("New nyq (input #%i) is %g\n", numinvals, p->nyq);
271 // real, imag = (mag * cos(phase), mag * sin(phase))
272 float mag, 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);
284 if(zeroothers){
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;
292 ZOUT0(0) = fbufnum;
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)
303 ft= inTable;
305 DefineSimpleUnit(Unpack1FFT);
306 DefineSimpleUnit(PackFFT);