aboutsummaryrefslogtreecommitdiff
path: root/fft/fft.c
diff options
context:
space:
mode:
Diffstat (limited to 'fft/fft.c')
-rw-r--r--fft/fft.c154
1 files changed, 154 insertions, 0 deletions
diff --git a/fft/fft.c b/fft/fft.c
new file mode 100644
index 0000000..69c0971
--- /dev/null
+++ b/fft/fft.c
@@ -0,0 +1,154 @@
+#include "fft.h"
+#include <math.h>
+#include <string.h>
+
+void fft_write(struct fmplayer_fft_data *data, const int16_t *buf, unsigned len) {
+ if (len > FFTLEN) {
+ unsigned discard = FFTLEN - len;
+ buf += discard*2;
+ len = FFTLEN;
+ }
+ unsigned towrite = FFTLEN - data->ind;
+ if (towrite > len) towrite = len;
+ for (unsigned i = 0; i < towrite; i++) {
+ data->buf[data->ind+i] = ((uint32_t)buf[2*i+0] + buf[2*i+1]) / 2;
+ }
+ data->ind = (data->ind + towrite) % FFTLEN;
+ buf += towrite*2;
+ len -= towrite;
+
+ for (unsigned i = 0; i < len; i++) {
+ data->buf[i] = ((uint32_t)buf[2*i+0] + buf[2*i+1]) / 2;
+ }
+ data->ind = (data->ind + len) % FFTLEN;
+}
+
+static const uint16_t fftfreqtab[FFTDISPLEN+1] = {
+ 0,
+ 18, 19, 21, 22, 23, 25, 26, 28, 29, 31,
+ 33, 35, 37, 39, 42, 44, 47, 50, 53, 56,
+ 59, 63, 66, 70, 75, 79, 84, 89, 94, 100,
+ 106, 112, 119, 126, 133, 141, 150, 158, 168, 178,
+ 189, 200, 212, 224, 238, 252, 267, 283, 300, 317,
+ 336, 356, 378, 400, 424, 449, 476, 504, 534, 566,
+ 600, 635, 673, 713, 756, 801, 848, 899, 952, 1009,
+};
+
+enum {
+ HFFTLENBIT = 12,
+ HFFTLEN = 1<<HFFTLENBIT,
+};
+
+static uint16_t window[FFTLEN];
+static float tritab[FFTLEN + FFTLEN/4];
+
+void fft_init_table(void) {
+ const double pi = acos(0.0) * 2.0;
+ double alpha = 0.54;
+ double beta = 1.0 - alpha;
+ for (unsigned i = 0; i < FFTLEN; i++) {
+ double v = alpha - beta * cos(2.0*pi*i/(FFTLEN-1));
+ window[i] = v * (1<<16);
+ }
+ for (unsigned i = 0; i < (FFTLEN + FFTLEN/4); i++) {
+ tritab[i] = sin(2.0*pi*i/FFTLEN);
+ }
+}
+
+static float coscalc(unsigned i) {
+ return tritab[(i & (FFTLEN-1)) + FFTLEN/4];
+}
+
+static float sincalc(unsigned i) {
+ return tritab[i & (FFTLEN-1)];
+}
+
+static float ar(unsigned i) {
+ return 0.5f*(1.0f-sincalc(i));
+}
+
+static float ai(unsigned i) {
+ return -0.5f*coscalc(i);
+}
+
+static float br(unsigned i) {
+ return 0.5f*(1.0f+sincalc(i));
+}
+
+static float bi(unsigned i) {
+ return 0.5f*coscalc(i);
+}
+
+static void fft_real(float *fftbuf) {
+ unsigned b = 0;
+ for (unsigned i = 0; i < HFFTLEN; i++) {
+ unsigned ii = 0;
+ for (unsigned bit = 0; bit < HFFTLENBIT; bit++) {
+ ii |= ((i >> bit) & 1u) << (HFFTLENBIT-bit-1);
+ }
+ fftbuf[(!b)*FFTLEN+i*2+0] = fftbuf[b*FFTLEN+ii*2+0];
+ fftbuf[(!b)*FFTLEN+i*2+1] = fftbuf[b*FFTLEN+ii*2+1];
+ }
+ b = !b;
+ for (unsigned bit = 0; bit < HFFTLENBIT; bit++) {
+ for (unsigned i = 0; i < HFFTLEN; i++) {
+ unsigned ei = i & ~(1u<<bit);
+ unsigned oi = i | (1u<<bit);
+ float are = fftbuf[b*FFTLEN+oi*2+0];
+ float aim = fftbuf[b*FFTLEN+oi*2+1];
+ float bre = coscalc(i<<(HFFTLENBIT-bit));
+ float bim = sincalc(i<<(HFFTLENBIT-bit));
+ float cre = are*bre - aim*bim;
+ float cim = are*bim + aim*bre;
+ fftbuf[(!b)*FFTLEN+i*2+0] = fftbuf[b*FFTLEN+ei*2+0] + cre;
+ fftbuf[(!b)*FFTLEN+i*2+1] = fftbuf[b*FFTLEN+ei*2+1] + cim;
+ }
+ b = !b;
+ }
+ for (unsigned i = 0; i < HFFTLEN; i++) {
+ float xr = fftbuf[b*FFTLEN+i*2+0];
+ float rxr = fftbuf[b*FFTLEN+0];
+ if (i) rxr = fftbuf[b*FFTLEN+(HFFTLEN-i)*2+0];
+ float xi = fftbuf[b*FFTLEN+i*2+1];
+ float rxi = fftbuf[b*FFTLEN+1];
+ if (i) rxi = fftbuf[b*FFTLEN+(HFFTLEN-i)*2+1];
+ fftbuf[(!b)*FFTLEN+i*2+0] = xr * ar(i) - xi * ai(i) + rxr * br(i) + rxi * bi(i);
+ fftbuf[(!b)*FFTLEN+i*2+1] = xi * ar(i) + xr * ai(i) + rxr * bi(i) - rxi * br(i);
+ }
+ if (!b) {
+ memcpy(fftbuf, fftbuf+FFTLEN, FFTLEN*sizeof(fftbuf[0]));
+ }
+}
+
+void fft_calc(struct fmplayer_fft_disp_data *ddata, struct fmplayer_fft_input_data *idata) {
+ for (int i = 0; i < FFTLEN; i++) {
+ int fi = (i + idata->fdata.ind) % FFTLEN;
+ idata->work[i] = (((int32_t)idata->fdata.buf[fi]) * window[i]) >> 16;
+ }
+ for (int i = 0; i < FFTLEN; i++) {
+ idata->fwork[i] = ((float)idata->work[i])/32768;
+ }
+ fft_real(idata->fwork);
+ for (int i = 1; i < FFTLEN/2; i++) {
+ float re = idata->fwork[i*2];
+ float im = idata->fwork[i*2+1];
+ idata->fwork[i] = sqrtf((re*re) + (im*im));
+ }
+ for (int i = 0; i < FFTLEN/2; i++) {
+ idata->fwork[i] = idata->fwork[i] / sqrtf(FFTLEN);
+ }
+ float dbuf[FFTDISPLEN];
+ for (int i = 0; i < FFTDISPLEN; i++) {
+ dbuf[i] = 0.0f;
+ for (int j = fftfreqtab[i]; j < fftfreqtab[i+1]; j++) {
+ dbuf[i] += idata->fwork[j];
+ }
+ dbuf[i] /= fftfreqtab[i+1] - fftfreqtab[i];
+ }
+ for (int i = 0; i < FFTDISPLEN; i++) {
+ float res = (dbuf[i] > (1.0f / 256)) ? (4.0f*log2f(dbuf[i]) + 32.0f) : 0.0f;
+ if (res > 31.0f) res = 31.0f;
+ if (res < 0.0f) res = 0.0f;
+ ddata->buf[i] = res;
+ }
+}