-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathfrft.c
567 lines (474 loc) · 18.1 KB
/
frft.c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
#include "m_pd.h"
#include <complex.h>
#include <fftw3.h>
#include <math.h>
#include <string.h>
#define FFTW_COMPLEX(r,i) ((r) + I * (i))
#define UNUSED(s) ((void)s)
static t_class *frft_class;
typedef struct _frft {
t_object x_obj;
t_symbol *x_array_in_real; // Input array name (real)
t_symbol *x_array_in_imag; // Input array name (imaginary)
t_symbol *x_array_out_real; // Output array name (real)
t_symbol *x_array_out_imag; // Output array name (imaginary)
t_float x_alpha; // FRFT power
// FFTW resources
int last_size; // Last processed array size
int conv_size; // Size of main buffers
int saved_chirp_size; // Size of chirp buffer
fftw_complex *buf; // Main buffer
fftw_complex *work; // Work buffer
fftw_complex *saved_chirp;// Cached chirp
fftw_plan signal_fft; // FFT plans
fftw_plan kernel_fft;
fftw_plan ifft_conv;
fftw_plan ifft_shift;
fftw_plan fft_shift;
// For list processing
t_float *imag_buf; // Store imaginary values from right inlet
int imag_buf_size; // Size of stored imaginary values
t_inlet *in_imag; // Inlet for imaginary part
t_outlet *list_out_real; // Real part output
t_outlet *list_out_imag; // Imaginary part output
} t_frft;
// Function declarations
static void frft_free(t_frft *x);
static void frft_bang(t_frft *x);
static void frft_list(t_frft *x, t_symbol *s, int argc, t_atom *argv);
static void *frft_new(t_symbol *s, int argc, t_atom *argv);
static void frft_set(t_frft *x, t_symbol *s1, t_symbol *s2, t_symbol *s3, t_symbol *s4);
static void frft_alpha(t_frft *x, t_floatarg f);
static void frft_process(t_frft *x, int N);
static void frft_imag(t_frft *x, t_symbol *s, int argc, t_atom *argv);
// Helper functions (from original frft~)
static void flip_signal(fftw_complex *buf, int N) {
for(int i = 0; i < N/2; i++) {
fftw_complex temp = buf[i];
buf[i] = buf[N-1-i];
buf[N-1-i] = temp;
}
}
static void fft_with_shift(t_frft *x, int N) {
for(int i = 0; i < N; i++) {
int shifted_idx = (i + N/2) % N;
x->work[i] = x->buf[shifted_idx];
}
fftw_execute(x->fft_shift);
double scale = 1.0/sqrt(N);
for(int i = 0; i < N; i++) {
int shifted_idx = (i + N/2) % N;
x->buf[shifted_idx] = x->work[i] * scale;
}
}
static void ifft_with_shift(t_frft *x, int N) {
for(int i = 0; i < N; i++) {
x->work[i] = x->buf[(i + N/2) % N];
}
fftw_execute(x->ifft_shift);
double sN = sqrt(N);
for(int i = 0; i < N; i++) {
x->buf[(i + N/2) % N] = x->work[i] * sN / N;
}
}
static void sincinterp(t_frft *x, int N) {
int work_size = 2*N-1;
memset(x->work, 0, sizeof(fftw_complex) * work_size);
for(int i = 0; i < N; i++) {
x->work[2*i] = x->buf[i];
}
int kernel_size = 4*N-5;
for(int i = 0; i < kernel_size; i++) {
double t = (i - (2*N-3)) / 2.0;
x->buf[i] = (fabs(t) < 1e-10) ? 1.0 : sin(M_PI * t)/(M_PI * t);
}
int conv_size = 8*N-6;
memset(x->work + work_size, 0, sizeof(fftw_complex) * (conv_size - work_size));
memset(x->buf + kernel_size, 0, sizeof(fftw_complex) * (conv_size - kernel_size));
fftw_execute(x->signal_fft);
fftw_execute(x->kernel_fft);
for(int i = 0; i < conv_size; i++) {
x->work[i] *= x->buf[i];
}
fftw_execute(x->ifft_conv);
double scale = 1.0/conv_size;
for(int i = 0; i < work_size; i++) {
x->buf[i] = x->work[i + 2*N-3] * scale;
}
}
static void frft_process(t_frft *x, int N) {
double a = fmod(x->x_alpha, 4.0);
if(a < 0) a += 4.0;
// Handle special cases
if(fabs(a) < 1e-10) return; // a = 0: identity
if(fabs(a - 2.0) < 1e-10) { // a = 2: signal flip
flip_signal(x->buf, N);
return;
}
if(fabs(a - 1.0) < 1e-10) { // a = 1: regular FFT
fft_with_shift(x, N);
return;
}
if(fabs(a - 3.0) < 1e-10) { // a = 3: inverse FFT
ifft_with_shift(x, N);
return;
}
// Reduce to interval 0.5 < a < 1.5
if(a > 2.0) {
a = a - 2.0;
flip_signal(x->buf, N);
}
if(a > 1.5) {
a = a - 1.0;
fft_with_shift(x, N);
}
if(a < 0.5) {
a = a + 1.0;
ifft_with_shift(x, N);
}
// Core FRFT computation
double alpha = a * M_PI / 2;
double tana2 = tan(alpha / 2);
double sina = sin(alpha);
// 1. Sinc interpolation
sincinterp(x, N);
// 2. Simple zero padding
int sinc_size = 2*N-1;
memcpy(x->work, x->buf, sizeof(fftw_complex) * sinc_size);
int final_size = 4*N-3;
memset(x->buf, 0, sizeof(fftw_complex) * final_size);
int left_pad = N-1;
memcpy(x->buf + left_pad, x->work, sizeof(fftw_complex) * sinc_size);
// 3. First chirp multiplication
int pad_size = 4*N-3;
for(int i = 0; i < pad_size; i++) {
double t = -2*N + 2 + i;
x->work[i] = x->buf[i] * cexp(I * -M_PI/N * tana2/4 * t * t);
}
// 4. Second chirp (convolution)
int conv_size = 8*N-6;
double c = M_PI/N/sina/4;
memcpy(x->buf, x->work, sizeof(fftw_complex) * pad_size);
memset(x->work, 0, sizeof(fftw_complex) * conv_size);
memcpy(x->work, x->buf, sizeof(fftw_complex) * pad_size);
for(int i = 0; i < conv_size; i++) {
double t = -(4*N - 4) + i;
x->buf[i] = cexp(I * c * t * t);
}
fftw_execute(x->signal_fft);
fftw_execute(x->kernel_fft);
for(int i = 0; i < conv_size; i++) {
x->work[i] *= x->buf[i];
}
fftw_execute(x->ifft_conv);
double scale = sqrt(c/M_PI) / conv_size;
int slice_start = 4*N-4;
for(int i = 0; i < pad_size; i++) {
double t = -2*N + 2 + i;
x->buf[i] = x->work[i + slice_start] * scale *
cexp(I * -M_PI/N * tana2/4 * t * t);
}
// 5. Final phase factor and extract result
double final_phase = -M_PI/4 * (1-a);
fftw_complex final_scale = cexp(I * final_phase);
for(int i = 0; i < N; i++) {
x->work[i] = x->buf[N-1 + 2*i] * final_scale;
}
memcpy(x->buf, x->work, sizeof(fftw_complex) * N);
}
// Helper function for buffer management
static int prepare_buffers(t_frft *x, int size) {
if (size != x->last_size) {
// Clean up old resources
frft_free(x);
x->last_size = 0; // Reset since it might have been used in free
x->conv_size = 0;
x->saved_chirp_size = 0;
int conv_size = 8 * size - 6;
// Allocate new resources
x->conv_size = conv_size;
x->saved_chirp_size = 4 * size - 3;
x->buf = (fftw_complex *)getbytes(sizeof(fftw_complex) * conv_size);
x->work = (fftw_complex *)getbytes(sizeof(fftw_complex) * conv_size);
x->saved_chirp = (fftw_complex *)getbytes(sizeof(fftw_complex) * x->saved_chirp_size);
if (!x->buf || !x->work || !x->saved_chirp) {
pd_error(x, "out of memory");
frft_free(x); // Clean up on failure
x->last_size = 0;
x->conv_size = 0;
x->saved_chirp_size = 0;
return 0;
}
x->signal_fft = fftw_plan_dft_1d(conv_size, x->work, x->work,
FFTW_FORWARD, FFTW_PATIENT);
x->kernel_fft = fftw_plan_dft_1d(conv_size, x->buf, x->buf,
FFTW_FORWARD, FFTW_PATIENT);
x->ifft_conv = fftw_plan_dft_1d(conv_size, x->work, x->work,
FFTW_BACKWARD, FFTW_PATIENT);
x->ifft_shift = fftw_plan_dft_1d(size, x->work, x->work,
FFTW_BACKWARD, FFTW_PATIENT);
x->fft_shift = fftw_plan_dft_1d(size, x->work, x->work,
FFTW_FORWARD, FFTW_PATIENT);
if (!x->signal_fft || !x->kernel_fft || !x->ifft_conv ||
!x->ifft_shift || !x->fft_shift) {
pd_error(x, "FFT plan creation failed");
frft_free(x); // Clean up on failure
x->last_size = 0;
x->conv_size = 0;
x->saved_chirp_size = 0;
return 0;
}
x->last_size = size;
}
return 1;
}
static void frft_bang(t_frft *x)
{
t_garray *array_in_real, *array_in_imag = NULL;
t_garray *array_out_real, *array_out_imag = NULL;
t_word *vec_in_real, *vec_in_imag = NULL;
t_word *vec_out_real, *vec_out_imag = NULL;
int size_in_real, size_in_imag = 0;
int size_out_real, size_out_imag = 0;
// Get real input array
if (!(array_in_real = (t_garray *)pd_findbyclass(x->x_array_in_real, garray_class))) {
pd_error(x, "%s: no such array", x->x_array_in_real->s_name);
return;
}
if (!garray_getfloatwords(array_in_real, &size_in_real, &vec_in_real)) {
pd_error(x, "%s: bad template", x->x_array_in_real->s_name);
return;
}
// Get imaginary input array if specified
if (x->x_array_in_imag && x->x_array_in_imag != &s_) {
if (!(array_in_imag = (t_garray *)pd_findbyclass(x->x_array_in_imag, garray_class))) {
pd_error(x, "%s: no such array", x->x_array_in_imag->s_name);
return;
}
if (!garray_getfloatwords(array_in_imag, &size_in_imag, &vec_in_imag)) {
pd_error(x, "%s: bad template", x->x_array_in_imag->s_name);
return;
}
if (size_in_imag != size_in_real) {
pd_error(x, "imaginary input array size doesn't match real input array size");
return;
}
}
// Get real output array
if (!(array_out_real = (t_garray *)pd_findbyclass(x->x_array_out_real, garray_class))) {
pd_error(x, "%s: no such array", x->x_array_out_real->s_name);
return;
}
if (!garray_getfloatwords(array_out_real, &size_out_real, &vec_out_real)) {
pd_error(x, "%s: bad template", x->x_array_out_real->s_name);
return;
}
// Get imaginary output array if specified
if (x->x_array_out_imag && x->x_array_out_imag != &s_) {
if (!(array_out_imag = (t_garray *)pd_findbyclass(x->x_array_out_imag, garray_class))) {
pd_error(x, "%s: no such array", x->x_array_out_imag->s_name);
return;
}
if (!garray_getfloatwords(array_out_imag, &size_out_imag, &vec_out_imag)) {
pd_error(x, "%s: bad template", x->x_array_out_imag->s_name);
return;
}
}
// Check output sizes
if (size_out_real < size_in_real) {
pd_error(x, "warning: real output array %s is smaller than input array (%d < %d)",
x->x_array_out_real->s_name, size_out_real, size_in_real);
}
if (array_out_imag && size_out_imag < size_in_real) {
pd_error(x, "warning: imaginary output array %s is smaller than input array (%d < %d)",
x->x_array_out_imag->s_name, size_out_imag, size_in_real);
}
// Prepare buffers
if (!prepare_buffers(x, size_in_real)) return;
// Copy input array to buffer
for (int i = 0; i < size_in_real; i++) {
t_float imag = vec_in_imag ? vec_in_imag[i].w_float : 0.0f;
x->buf[i] = FFTW_COMPLEX(vec_in_real[i].w_float, imag);
}
// Process
frft_process(x, size_in_real);
// Copy to output arrays
int write_size_real = (size_out_real < size_in_real) ? size_out_real : size_in_real;
for (int i = 0; i < write_size_real; i++) {
vec_out_real[i].w_float = creal(x->buf[i]);
}
if (array_out_imag) {
int write_size_imag = (size_out_imag < size_in_real) ? size_out_imag : size_in_real;
for (int i = 0; i < write_size_imag; i++) {
vec_out_imag[i].w_float = cimag(x->buf[i]);
}
garray_redraw(array_out_imag);
}
garray_redraw(array_out_real);
}
void frft_imag(t_frft *x, t_symbol *s, int argc, t_atom *argv)
{
UNUSED(s);
// Free old buffer if it exists
if (x->imag_buf) {
freebytes(x->imag_buf, x->imag_buf_size * sizeof(t_float));
x->imag_buf = NULL;
x->imag_buf_size = 0; // Reset size immediately after freeing
}
if (argc == 0) return; // No new data to store
// Allocate and fill new buffer
x->imag_buf = (t_float *)getbytes(argc * sizeof(t_float));
if (!x->imag_buf) {
pd_error(x, "out of memory");
x->imag_buf_size = 0; // Make sure size is 0 if allocation failed
return;
}
// Set size only after successful allocation
x->imag_buf_size = argc;
// Copy values
for (int i = 0; i < argc; i++) {
x->imag_buf[i] = atom_getfloat(argv + i);
}
}
static void frft_list(t_frft *x, t_symbol *s, int argc, t_atom *argv)
{
UNUSED(s);
if (argc == 0) return;
// Check for size mismatch with stored imaginary values
if (x->imag_buf) { // Only check if we have imaginary values stored
if (x->imag_buf_size != argc) {
pd_error(x, "size mismatch between real (%d) and imaginary (%d) parts",
argc, x->imag_buf_size);
// Clear stored imaginary values since they can't be used
freebytes(x->imag_buf, x->imag_buf_size * sizeof(t_float));
x->imag_buf = NULL;
x->imag_buf_size = 0;
return;
}
// Additional safety check for non-null buffer with zero size
if (x->imag_buf_size == 0) {
// Cleanup inconsistent state
freebytes(x->imag_buf, sizeof(t_float)); // Free minimum size
x->imag_buf = NULL;
}
}
// Prepare buffers
if (!prepare_buffers(x, argc)) return;
// Copy input list to buffer
for (int i = 0; i < argc; i++) {
t_float imag = 0.0f; // Default to 0
// Use stored imaginary values if available
if (x->imag_buf) {
imag = x->imag_buf[i];
}
x->buf[i] = FFTW_COMPLEX(atom_getfloat(argv + i), imag);
}
// Clear imaginary buffer after use
if (x->imag_buf) {
freebytes(x->imag_buf, x->imag_buf_size * sizeof(t_float));
x->imag_buf = NULL;
x->imag_buf_size = 0;
}
// Process
frft_process(x, argc);
// Create output lists
t_atom *outv_real = (t_atom *)getbytes(sizeof(t_atom) * argc);
t_atom *outv_imag = (t_atom *)getbytes(sizeof(t_atom) * argc);
if (!outv_real || !outv_imag) {
pd_error(x, "out of memory");
if (outv_real) freebytes(outv_real, sizeof(t_atom) * argc);
if (outv_imag) freebytes(outv_imag, sizeof(t_atom) * argc);
return;
}
// Fill output lists
for (int i = 0; i < argc; i++) {
SETFLOAT(outv_real + i, creal(x->buf[i]));
SETFLOAT(outv_imag + i, cimag(x->buf[i]));
}
// Output results
outlet_list(x->list_out_imag, &s_list, argc, outv_imag);
outlet_list(x->list_out_real, &s_list, argc, outv_real);
// Clean up
freebytes(outv_real, sizeof(t_atom) * argc);
freebytes(outv_imag, sizeof(t_atom) * argc);
}
static void frft_set(t_frft *x, t_symbol *s1, t_symbol *s2, t_symbol *s3, t_symbol *s4)
{
x->x_array_in_real = s1;
x->x_array_out_real = s2;
x->x_array_in_imag = s3;
x->x_array_out_imag = s4;
}
static void frft_alpha(t_frft *x, t_floatarg f)
{
x->x_alpha = f;
}
static void *frft_new(t_symbol *s, int argc, t_atom *argv)
{
UNUSED(s);
t_frft *x = (t_frft *)pd_new(frft_class);
// Parse arguments: [array_in_real] [array_out_real] [array_in_imag] [array_out_imag] [alpha]
x->x_array_in_real = (argc > 0 && argv[0].a_type == A_SYMBOL) ?
atom_getsymbol(argv) : gensym("array1");
x->x_array_out_real = (argc > 1 && argv[1].a_type == A_SYMBOL) ?
atom_getsymbol(argv + 1) : gensym("array2");
x->x_array_in_imag = (argc > 2 && argv[2].a_type == A_SYMBOL) ?
atom_getsymbol(argv + 2) : &s_; // Use empty symbol for no array
x->x_array_out_imag = (argc > 3 && argv[3].a_type == A_SYMBOL) ?
atom_getsymbol(argv + 3) : &s_;
x->x_alpha = (argc > 4 && argv[4].a_type == A_FLOAT) ?
atom_getfloat(argv + 4) : 1.0;
// Initialize buffers
x->last_size = 0;
x->conv_size = 0;
x->saved_chirp_size = 0;
x->buf = NULL;
x->work = NULL;
x->saved_chirp = NULL;
x->signal_fft = NULL;
x->kernel_fft = NULL;
x->ifft_conv = NULL;
x->ifft_shift = NULL;
x->fft_shift = NULL;
// Initialize list input buffer
x->imag_buf = NULL;
x->imag_buf_size = 0;
// Create inlet for imaginary part lists
x->in_imag = inlet_new(&x->x_obj, &x->x_obj.ob_pd, &s_list, gensym("imag"));
// Create outlets for list processing
x->list_out_real = outlet_new(&x->x_obj, &s_list);
x->list_out_imag = outlet_new(&x->x_obj, &s_list);
return (void *)x;
}
static void frft_free(t_frft *x)
{
// Free FFTW resources
if (x->buf) freebytes(x->buf, sizeof(fftw_complex) * x->conv_size);
if (x->work) freebytes(x->work, sizeof(fftw_complex) * x->conv_size);
if (x->saved_chirp) freebytes(x->saved_chirp, sizeof(fftw_complex) * x->saved_chirp_size);
if (x->signal_fft) fftw_destroy_plan(x->signal_fft);
if (x->kernel_fft) fftw_destroy_plan(x->kernel_fft);
if (x->ifft_shift) fftw_destroy_plan(x->ifft_shift);
if (x->fft_shift) fftw_destroy_plan(x->fft_shift);
if (x->ifft_conv) fftw_destroy_plan(x->ifft_conv);
// Free list input buffer
if (x->imag_buf) freebytes(x->imag_buf, x->imag_buf_size * sizeof(t_float));
// Inlets/outlets are automatically freed by Pd
}
void frft_setup(void)
{
frft_class = class_new(gensym("frft"),
(t_newmethod)frft_new,
(t_method)frft_free,
sizeof(t_frft),
CLASS_DEFAULT,
A_GIMME, 0);
class_addbang(frft_class, frft_bang);
class_addmethod(frft_class, (t_method)frft_set,
gensym("set"), A_SYMBOL, A_SYMBOL, A_SYMBOL, A_SYMBOL, 0);
class_addmethod(frft_class, (t_method)frft_alpha,
gensym("alpha"), A_FLOAT, 0);
class_addmethod(frft_class, (t_method)frft_imag, // Add method for imaginary input
gensym("imag"), A_GIMME, 0);
class_addlist(frft_class, frft_list);
}