/* staticresynth~.c - resynthes a buffer~ with a single FFT -> randomised phase for each bin -> IFFT process Peter Worth 2008 ARGS - buffer name (must be single channel) MESSAGES - "set buffername" changes buffer, or reloads the same buffer to update */ #include #include #include "rngs.h" // good random number generation #include "ext.h" #include "ext_common.h" // contains CLIP macro #include "z_dsp.h" #include "buffer.h" // this defines our buffer's data structure etc #include "fftw3.h" // fftw library // size of the fft to do - this is basically the maximum amount of audio to take from the buffer (number of samples) // if the buffer is smaller than this then it zero pads the rest // actually, because it is a real to complex transform, the actual number of bins in the fft result is (NUMBINS/2) + 1 #define NUMBINS 524288 // roughly 12 seconds, is also a power of 2 for efficiency (fft rather than plain dft) void *staticresynth_class; typedef struct _staticresynth { t_pxobject l_obj; t_symbol *l_sym; t_buffer *l_buf; int used; int arraysize; int arraypos; int resynthchoice; float *resynthesised; } t_staticresynth; t_int *staticresynth_perform(t_int *w); void staticresynth_dsp(t_staticresynth *x, t_signal **sp); void staticresynth_set(t_staticresynth *x, t_symbol *s); void *staticresynth_new(t_symbol *s, long chan); void staticresynth_assist(t_staticresynth *x, void *b, long m, long a, char *s); void staticresynth_dblclick(t_staticresynth *x); t_symbol *ps_buffer; void staticresynth_free(t_staticresynth *x) { int i; fftwf_free(x->resynthesised); } void main(void) { setup((t_messlist **)&staticresynth_class, (method)staticresynth_new, (method)dsp_free, (short)sizeof(t_staticresynth), 0L, A_SYM, 0); //args for the object: buffer~ name addmess((method)staticresynth_dsp, "dsp", A_CANT, 0); addmess((method)staticresynth_set, "set", A_SYM, 0); addmess((method)staticresynth_assist, "assist", A_CANT, 0); addmess((method)staticresynth_dblclick, "dblclick", A_CANT, 0); dsp_initclass(); ps_buffer = gensym("buffer~"); } t_int *staticresynth_perform(t_int *w) { t_staticresynth *x = (t_staticresynth *)(w[1]); t_float *in = (t_float *)(w[2]); t_float *out = (t_float *)(w[3]); int n = (int)(w[4]); if (x->used == 0)//if other thread(?) is busy doing fft, do nothing { while (n--) { *out++ = 0.0f; } return (w+5); } while (n--) { if (x->arraypos >= x->arraysize) x->arraypos = 0; *out++ = x->resynthesised[x->arraypos]; x->arraypos++; } return (w+5); } // here's where we do stuff with the buffer~ void staticresynth_set(t_staticresynth *x, t_symbol *s) { t_buffer *b; t_float *frombuffer; t_float *paddedarray; int bufsize, fftsize; fftwf_plan p, ip; fftwf_complex *fftresult; float mag, phase; int i; // if the fft has already been used, free everything first if (x->used == 1) { x->used = 0; staticresynth_free(x); } x->arraysize = 0; x->arraypos = 0; // get the buffer~ x->l_sym = s; if ((b = (t_buffer *)(s->s_thing)) && ob_sym(b) == ps_buffer) { if (b->b_nchans != 1) { error("staticresynth~: buffer~ %s must have only 1 channel", s->s_name); x->l_buf = 0; } else { x->l_buf = b; } } else { error("staticresynth~: no buffer~ %s", s->s_name); x->l_buf = 0; } frombuffer = b->b_samples; bufsize = b->b_frames; x->arraysize = NUMBINS; x->arraypos = 0; fftsize = (NUMBINS/2)+1; //zero pad the stuff from the buffer into a new array paddedarray = (float*) fftwf_malloc(sizeof(float) * x->arraysize); memset(paddedarray, 0, x->arraysize * sizeof(float)); memcpy(paddedarray, frombuffer, (min(bufsize,x->arraysize)) * sizeof(float)); //do the fft fftresult = (fftwf_complex*) fftwf_malloc(sizeof(fftwf_complex) * fftsize); p = fftwf_plan_dft_r2c_1d(x->arraysize, paddedarray, fftresult, FFTW_ESTIMATE); fftwf_execute(p); //processing of fftresult to randomise phase //magnitude = sqrt(fftresult[0]^2 + fftresult[1]^2) //phase = atan(fftresult[i][1] / fftresult[i][0]) PlantSeeds(-1); SelectStream(0); for (i = 0; i < fftsize; i++) { mag = sqrt((fftresult[i][0] * fftresult[i][0]) + (fftresult[i][1] * fftresult[i][1])); phase = (2.0f*PI*Random()) - (PI); fftresult[i][0] = mag * (cos(phase)); fftresult[i][1] = mag * (sin(phase)); } //do the ifft x->resynthesised = (float*) fftwf_malloc(sizeof(float) * x->arraysize); ip = fftwf_plan_dft_c2r_1d(x->arraysize, fftresult, x->resynthesised, FFTW_ESTIMATE); fftwf_execute(ip); //clean up fftw stuff fftwf_destroy_plan(p); fftwf_destroy_plan(ip); fftwf_free(fftresult); fftwf_free(paddedarray); //normalise the resynthed array for (i = 0; i < x->arraysize; i++) { x->resynthesised[i] = x->resynthesised[i] / x->arraysize; //could we divide by bufsize instead to avoid altering magnitudes based on how much zero padding there is? } // post("staticresynth~: using %s", s->s_name); x->used = 1; } void staticresynth_dsp(t_staticresynth *x, t_signal **sp) { staticresynth_set(x,x->l_sym); dsp_add(staticresynth_perform, 4, x, sp[0]->s_vec, sp[1]->s_vec, sp[0]->s_n); } // this lets us double-click on staticresynth~ to open up the buffer~ it references void staticresynth_dblclick(t_staticresynth *x) { t_buffer *b; if ((b = (t_buffer *)(x->l_sym->s_thing)) && ob_sym(b) == ps_buffer) mess0((t_object *)b,gensym("dblclick")); } void staticresynth_assist(t_staticresynth *x, void *b, long m, long a, char *s) { if (m == ASSIST_OUTLET) sprintf(s,"(signal) Static resynthysis of a buffer~"); else { switch (a) { case 0: sprintf(s,"(signal) Input: does nothing"); break; } } } void *staticresynth_new(t_symbol *s, long partsizearg) { t_staticresynth *x = (t_staticresynth *)newobject(staticresynth_class); dsp_setup((t_pxobject *)x, 1); outlet_new((t_object *)x, "signal"); x->l_sym = s; x->used = 0; return (x); }