6#include <clod/math/fft.h>
8#define PI 3.141592653589793238462643383279502884197169399375105820974944592307f
13#define INPUT_FORMAT(opt) (((opt) / CLOD_FFT_INPUT) & 0xFF)
14#define OUTPUT_FORMAT(opt) (((opt) / CLOD_FFT_OUTPUT) & 0xFF)
16static float fft_sin(
float n) {
17 while (n > PI) n -= 2.0f * PI;
18 while (n < -PI) n += 2.0f * PI;
22 for (
int i = 1; i <= 12; i += 2) {
23 res += s = -s * n * n / (float)((i + 1) * (i + 2));
27static float fft_cos(
float n) {
28 while (n > PI) n -= 2.0f * PI;
29 while (n < -PI) n += 2.0f * PI;
33 for (
int i = 0; i <= 12; i += 2) {
34 res += s = -s * n * n / (float)((i + 1) * (i + 2));
38static float fft_atan(
const float x) {
39 if (x > 1.0f)
return PI / 2.0f - fft_atan(1.0f / x);
40 if (x < -1.0f)
return -PI / 2.0f - fft_atan(1.0f / x);
44 for (
int i = 1; i <= 36; i++) {
45 res += s = -s * x * x * (float)(2 * i - 1) / (float)(2 * i + 1);
49static float fft_sqrt(
const float x) {
50 if (x <= 0.0f)
return 0.0f;
51 float guess = x / 2.0f;
52 for (
int i = 0; i < 8; i++) {
53 guess = (guess + x / guess) / 2.0f;
57static float fft_atan2(
const float y,
const float x) {
58 if (x > 0.0f)
return fft_atan(y / x);
60 if (y > 0.0f)
return PI / 2.0f;
61 if (y < 0.0f)
return -PI / 2.0f;
64 if (y >= 0.0f)
return fft_atan(y / x) + PI;
65 return fft_atan(y / x) - PI;
68static void swap(
float *restrict a,
float *restrict b) {
77static void mul(
float a[2],
const float b[2]) {
78 const float tmp = a[R] * b[R] - a[I] * b[I];
79 a[I] = a[R] * b[I] + a[I] * b[R];
82static void complex_to_mag(
float *restrict data,
const size_t len) {
83 for (
size_t i = 0; i < len; i++) {
84 const float real = data[i * 2 + R];
85 const float imag = data[i * 2 + I];
86 data[i * 2 + R] = fft_sqrt(real * real + imag * imag);
87 data[i * 2 + I] = fft_atan2(imag, real);
101static void mag_to_complex(
float *restrict data,
const size_t len) {
102 for (
size_t i = 0; i <= len / 2; i++) {
103 const float mag = data[i * 2 + R];
104 const float phase = data[i * 2 + I];
105 const float real = mag * fft_cos(phase);
106 const float imag = mag * fft_sin(phase);
107 data[i * 2 + R] = real;
108 data[i * 2 + I] = imag;
110 if (i > 0 && i < len / 2) {
111 const size_t conjugate = len - i;
112 data[conjugate * 2 + R] = real;
113 data[conjugate * 2 + I] = -imag;
127static void pack(
float *restrict data,
const size_t len,
const float padding) {
129 while (i < len / 2) {
130 data[i] = data[i * 2];
134 data[i] = data[i * 2];
135 data[i * 2] = padding;
136 data[i * 2 + 1] = padding;
140static void invert_imag(
float *restrict data,
const size_t len) {
141 for (
size_t i = 0; i < len; i++) {
142 data[i * 2 + 1] *= -1.0f;
145static void invert_scale(
float *restrict data,
const size_t len) {
146 for (
size_t i = 0; i < len; i++) {
147 mul(&data[i * 2], (
float[2]){ 1.0f / (float)len, 0.0f });
150static void invert_scale_pack(
float *restrict data,
const size_t len,
const float padding) {
152 while (i < len / 2) {
153 float n[2] = { data[i * 2 + R], data[i * 2 + I] };
155 mul(n, (
float[2]){ 1.0f / (float)len, 0.0f });
160 float n[2] = { data[i * 2 + R], data[i * 2 + I] };
162 mul(n, (
float[2]){ 1.0f / (float)len, 0.0f });
164 data[i * 2 + R] = padding;
165 data[i * 2 + I] = padding;
170static void fft(
float *data,
const size_t len) {
171 for (
size_t i = 1, rev = 0; i < len; i++) {
178 if (data[i * 2] != data[i * 2]) {
180 debug(CLOD_DEBUG,
"NaN detected in FFT input data.");
182 if (data[i * 2 + 1] != data[i * 2 + 1]) {
183 data[i * 2 + 1] = 0.0f;
184 debug(CLOD_DEBUG,
"NaN detected in FFT input data.");
187 swap(data + i * 2, data + rev * 2);
191 for (
size_t s = 2; s <= len; s <<= 1) {
192 const float wn[2] = {
193 fft_cos(2.0f * PI / (
float)s),
194 fft_sin(2.0f * PI / (
float)s)
197 for (
size_t i = 0; i < len; i += s) {
198 float w[2] = {1.0f, 0.0f};
199 for (
size_t j = 0; j < s / 2; j++) {
200 const size_t index1 = 2 * (i + j);
201 const size_t index2 = 2 * (i + j + s / 2);
203 float u[2] = {data[index1], data[index1 + 1]};
204 float v[2] = {data[index2], data[index2 + 1]};
207 data[index1] = u[R] + v[R];
208 data[index1 + 1] = u[I] + v[I];
210 data[index2] = u[R] - v[R];
211 data[index2 + 1] = u[I] - v[I];
218void clod_fft(
float *restrict data,
const size_t len,
const int opt) {
219 if (len == 0)
goto error;
221 switch (INPUT_FORMAT(opt)) {
223 case CLOD_FFT_TIME_MAG_PACKED:
224 switch (OUTPUT_FORMAT(opt)) {
226 case CLOD_FFT_TIME_COMPLEX:
227 for (
size_t i = len; i > 0; i--) {
228 data[(i - 1) * 2] = data[i - 1];
229 data[(i - 1) * 2 + 1] = 0.0f;
233 case CLOD_FFT_TIME_MAG_PACKED:
234 for (
size_t i = len; i < len * 2; i++)
238 case CLOD_FFT_FREQ_COMPLEX:
239 for (
size_t i = len; i > 0; i--) {
240 data[(i - 1) * 2] = data[i - 1];
241 data[(i - 1) * 2 + 1] = 0.0f;
246 case CLOD_FFT_FREQ_MAG:
247 for (
size_t i = len; i > 0; i--) {
248 data[(i - 1) * 2] = data[i - 1];
249 data[(i - 1) * 2 + 1] = 0.0f;
252 complex_to_mag(data, len);
258 case CLOD_FFT_TIME_COMPLEX:
259 switch (OUTPUT_FORMAT(opt)) {
261 case CLOD_FFT_TIME_COMPLEX:
264 case CLOD_FFT_TIME_MAG_PACKED:
265 pack(data, len, 0.0f);
268 case CLOD_FFT_FREQ_COMPLEX:
272 case CLOD_FFT_FREQ_MAG:
274 complex_to_mag(data, len);
280 case CLOD_FFT_FREQ_COMPLEX:
281 switch (OUTPUT_FORMAT(opt)) {
283 case CLOD_FFT_TIME_COMPLEX:
284 invert_imag(data, len);
286 invert_scale(data, len);
289 case CLOD_FFT_TIME_MAG_PACKED:
290 invert_imag(data, len);
292 invert_scale_pack(data, len, 0.0f);
295 case CLOD_FFT_FREQ_COMPLEX:
298 case CLOD_FFT_FREQ_MAG:
299 complex_to_mag(data, len);
305 case CLOD_FFT_FREQ_MAG:
306 switch (OUTPUT_FORMAT(opt)) {
308 case CLOD_FFT_TIME_COMPLEX:
309 mag_to_complex(data, len);
310 invert_imag(data, len);
312 invert_scale(data, len);
315 case CLOD_FFT_TIME_MAG_PACKED:
316 mag_to_complex(data, len);
317 invert_imag(data, len);
319 invert_scale_pack(data, len, 0.0f);
322 case CLOD_FFT_FREQ_COMPLEX:
323 mag_to_complex(data, len);
326 case CLOD_FFT_FREQ_MAG:
336 debug(CLOD_DEBUG,
"Ignoring invalid FFT arguments (%ptr, %size, %bi).", (
void*)data, len, opt);