#include "MSPd.h" #include "fftease.h" #if MSP void *codepend_class; #endif #if PD static t_class *codepend_class; #endif #define OBJECT_NAME "codepend~" /* Adding -32dB pad for invert option. Also added latency mechanism in switching from normal to "invert" to avoid glitches from extreme amplitude disparities. Made all inlets of type signal (with float options). Threshold input is now linear, not dB (with Max doing the conversion if desired). -EL 10/1/2005 */ typedef struct _codepend { #if MSP t_pxobject x_obj; #endif #if PD t_object x_obj; float x_f; #endif int R; int N; int N2; int Nw; int Nw2; int D; int i; int inCount; int invert; int *bitshuffle; float threshold; float exponent; float *Wanal; float *Wsyn; float *inputOne; float *inputTwo; float *Hwin; float *bufferOne; float *bufferTwo; float *channelOne; float *channelTwo; float *output; float mult; float *trigland; short connected[8]; short mute; int overlap;//overlap factor int winfac;//window factor int vs;//vector size int invert_countdown; // delay onset of invert effect to avoid loud glitches int invert_nextstate;// next state for invert float invert_pad; } t_codepend; /* msp function prototypes */ void *codepend_new(t_symbol *s, int argc, t_atom *argv); t_int *offset_perform(t_int *w); t_int *codepend_perform(t_int *w); void codepend_dsp(t_codepend *x, t_signal **sp, short *count); void codepend_assist(t_codepend *x, void *b, long m, long a, char *s); void codepend_dest(t_codepend *x, double f); void codepend_invert(t_codepend *x, t_floatarg toggle); void codepend_free(t_codepend *x); void codepend_mute(t_codepend *x, t_floatarg toggle); void codepend_fftinfo(t_codepend *x); void codepend_tilde_setup(void); void codepend_winfac(t_codepend *x, t_floatarg o); void codepend_overlap(t_codepend *x, t_floatarg o); void codepend_init(t_codepend *x, short initialized); void codepend_pad(t_codepend *x, t_floatarg pad); #if MSP void main(void) { setup( (struct messlist **) &codepend_class, (void *) codepend_new, (method)dsp_free, (short) sizeof(t_codepend), 0, A_GIMME, 0); addmess((method)codepend_dsp, "dsp", A_CANT, 0); addmess((method)codepend_assist,"assist",A_CANT,0); addmess((method)codepend_invert,"invert", A_FLOAT, 0); addmess((method)codepend_mute,"mute", A_FLOAT, 0); addmess((method)codepend_pad,"pad", A_FLOAT, 0); addmess((method)codepend_overlap,"overlap", A_FLOAT, 0); addmess((method)codepend_winfac,"winfac", A_FLOAT, 0); addmess((method)codepend_fftinfo,"fftinfo", 0); addfloat((method) codepend_dest); dsp_initclass(); post("%s %s",OBJECT_NAME,FFTEASE_ANNOUNCEMENT); } /* float input handling routine (MSP only)*/ void codepend_dest(t_codepend *x, double df) { float f = (float) df; int inlet = x->x_obj.z_in; if ( inlet == 2 ) { x->exponent = f; } if ( inlet == 3 ){ /* x->threshold = (float) (pow( 10., (f * .05))); */ x->threshold = f; } } #endif #if PD void codepend_tilde_setup(void) { codepend_class = class_new(gensym("codepend~"), (t_newmethod)codepend_new, (t_method)codepend_free ,sizeof(t_codepend), 0,A_GIMME,0); CLASS_MAINSIGNALIN(codepend_class, t_codepend, x_f); class_addmethod(codepend_class, (t_method)codepend_dsp, gensym("dsp"), 0); class_addmethod(codepend_class, (t_method)codepend_assist, gensym("assist"), 0); class_addmethod(codepend_class, (t_method)codepend_invert, gensym("invert"), A_FLOAT,0); class_addmethod(codepend_class, (t_method)codepend_mute, gensym("mute"), A_FLOAT,0); class_addmethod(codepend_class, (t_method)codepend_pad, gensym("pad"), A_FLOAT,0); class_addmethod(codepend_class, (t_method)codepend_overlap, gensym("overlap"), A_FLOAT,0); class_addmethod(codepend_class, (t_method)codepend_winfac, gensym("winfac"), A_FLOAT,0); class_addmethod(codepend_class, (t_method)codepend_fftinfo, gensym("fftinfo"), A_CANT,0); post("%s %s",OBJECT_NAME,FFTEASE_ANNOUNCEMENT); } #endif void codepend_mute(t_codepend *x, t_floatarg toggle) { x->mute = (short)toggle; // post("mute set to %f, %d",toggle,x->mute); } void codepend_overlap(t_codepend *x, t_floatarg o) { if(!fftease_power_of_two((int)o)){ error("%f is not a power of two",o); return; } x->overlap = (int)o; codepend_init(x,1); } void codepend_winfac(t_codepend *x, t_floatarg f) { if(!fftease_power_of_two((int)f)){ error("%f is not a power of two",f); return; } x->winfac = (int)f; codepend_init(x,1); } void codepend_fftinfo( t_codepend *x ) { if( ! x->overlap ){ post("zero overlap!"); return; } post("%s: FFT size %d, hopsize %d, windowsize %d", OBJECT_NAME, x->N, x->N/x->overlap, x->Nw); } void codepend_free(t_codepend *x) { #if MSP dsp_free((t_pxobject *) x); #endif free(x->trigland); free(x->bitshuffle); free(x->Wanal); free(x->Wsyn); free(x->Hwin); free(x->inputOne); free(x->inputTwo); free(x->bufferOne); free(x->bufferTwo); free(x->channelOne); free(x->channelTwo); free(x->output); } void codepend_pad(t_codepend *x, t_floatarg pad) { x->invert_pad = pad; codepend_invert(x,x->invert);//resubmit to invert } void codepend_invert(t_codepend *x, t_floatarg toggle) { x->invert_nextstate = (short)toggle; x->invert_countdown = x->overlap; // delay effect for "overlap" vectors if(x->invert_nextstate){ // lower gain immediately; delay going to invert x->mult = (1. / (float) x->N) * x->invert_pad; } else { x->invert = 0; //immediately turn off invert; delay raising gain } } void codepend_assist (t_codepend *x, void *b, long msg, long arg, char *dst) { if (msg == 1) { switch (arg) { case 0: sprintf(dst,"(signal) Input One");break; case 1: sprintf(dst,"(signal) Input Two"); break; case 2: sprintf(dst,"(signal/float) Scaling Exponent"); break; case 3: sprintf(dst,"(signal/float) Inverse Threshold"); break; } } else { if (msg == 2) sprintf(dst,"(signal) Output"); } } void *codepend_new(t_symbol *s, int argc, t_atom *argv) { #if MSP t_codepend *x = (t_codepend *) newobject(codepend_class); dsp_setup((t_pxobject *)x,4); outlet_new((t_pxobject *)x, "signal"); // x->x_obj.z_misc |= Z_NO_INPLACE; // probably not needed #endif #if PD t_codepend *x = (t_codepend *)pd_new(codepend_class); /* add three additional signal inlets */ inlet_new(&x->x_obj, &x->x_obj.ob_pd,gensym("signal"), gensym("signal")); inlet_new(&x->x_obj, &x->x_obj.ob_pd,gensym("signal"), gensym("signal")); inlet_new(&x->x_obj, &x->x_obj.ob_pd,gensym("signal"), gensym("signal")); outlet_new(&x->x_obj, gensym("signal")); #endif /* optional arguments: scaling exponent, threshold (now linear), overlap, winfac */ x->exponent = atom_getfloatarg(0,argc,argv); x->threshold = atom_getfloatarg(1,argc,argv); x->overlap = atom_getfloatarg(2,argc,argv); x->winfac = atom_getfloatarg(3,argc,argv); /* x->threshold = (float) pow(10.0,(x->threshold * .05)); */ /* sanity check */ if(x->exponent < 0.25) x->exponent = 0.25; if(!fftease_power_of_two(x->overlap)){ x->overlap = 4; } if(!fftease_power_of_two(x->winfac)){ x->winfac = 1; } x->vs = sys_getblksize(); x->R = sys_getsr(); codepend_init(x,0); return (x); } void codepend_init(t_codepend *x, short initialized) { int i; x->D = x->vs; x->N = x->D * x->overlap; x->Nw = x->N * x->winfac; limit_fftsize(&x->N,&x->Nw,OBJECT_NAME); x->N2 = (x->N)>>1; x->Nw2 = (x->Nw)>>1; x->inCount = -(x->Nw); x->mult = 1. / (float) x->N; if(!initialized){ x->invert_pad = 0.025; // -32 dB x->invert_countdown = 0; x->mute = 0; x->invert = 0; x->Wanal = (float *) calloc(MAX_Nw, sizeof(float)); x->Wsyn = (float *) calloc(MAX_Nw, sizeof(float)); x->Hwin = (float *) calloc(MAX_Nw, sizeof(float)); x->inputOne = (float *) calloc(MAX_Nw, sizeof(float)); x->inputTwo = (float *) calloc(MAX_Nw, sizeof(float)); x->bufferOne = (float *) calloc(MAX_N, sizeof(float)); x->bufferTwo = (float *) calloc(MAX_N, sizeof(float)); x->channelOne = (float *) calloc(MAX_N+2, sizeof(float)); x->channelTwo = (float *) calloc(MAX_N+2, sizeof(float)); x->output = (float *) calloc(MAX_Nw, sizeof(float)); x->bitshuffle = (int *) calloc(MAX_N * 2, sizeof(int)); x->trigland = (float *) calloc(MAX_N * 2, sizeof(float)); } memset((char *)x->inputOne,0,x->Nw); memset((char *)x->inputTwo,0,x->Nw); memset((char *)x->output,0,x->Nw); memset((char *)x->bufferOne,0,x->N); memset((char *)x->bufferTwo,0,x->N); memset((char *)x->channelOne,0,(x->N+2)); memset((char *)x->channelTwo,0,(x->N+2)); if(x->invert){ x->mult *= x->invert_pad; } init_rdft( x->N, x->bitshuffle, x->trigland); makehanning( x->Hwin, x->Wanal, x->Wsyn, x->Nw, x->N, x->D, 1); } t_int *codepend_perform(t_int *w) { int i,j, inCount, R, N, N2, D, Nw, invert = 0, even, odd, *bitshuffle; float maxamp, threshold = 1., mult, exponent, a1, b1, a2, b2, *inputOne, *inputTwo, *bufferOne, *bufferTwo, *output, *Wanal, *Wsyn, *channelOne, *channelTwo, *trigland; /* get our inlets and outlets */ t_codepend *x = (t_codepend *) (w[1]); t_float *inOne = (t_float *)(w[2]); t_float *inTwo = (t_float *)(w[3]); t_float *vec_exponent = (t_float *)(w[4]); t_float *vec_threshold = (t_float *)(w[5]); t_float *out = (t_float *)(w[6]); t_int n = w[7]; short *connected = x->connected; /* dereference structure */ if(connected[2]) x->exponent = *vec_exponent; if(connected[3]){ x->threshold = *vec_threshold; /* x->threshold = (float) (pow( 10., (x->threshold * .05))); */ } if(x->mute){ while(n--) *out++ = 0.0; return w+8; } // do countdown if(x->invert_countdown > 0){ if(x->invert) { // we } else { } --(x->invert_countdown); if(! x->invert_countdown){ // countdown just ended if(x->invert_nextstate){ // moving to invert (gain is already down) x->invert = x->invert_nextstate; } else { // invert is already off - now reset gain x->mult = 1. / (float) x->N; } } } inputOne = x->inputOne; inputTwo = x->inputTwo; bufferOne = x->bufferOne; bufferTwo = x->bufferTwo; inCount = x->inCount; R = x->R; N = x->N; N2 = x->N2; D = x->D; Nw = x->Nw; Wanal = x->Wanal; Wsyn = x->Wsyn; output = x->output; channelOne = x->channelOne; channelTwo = x->channelTwo; bitshuffle = x->bitshuffle; trigland = x->trigland; mult = x->mult; invert = x->invert; exponent = x->exponent; if ( x->threshold != 0. ) threshold = x->threshold; /* fill our retaining buffers */ inCount += D; for ( j = 0 ; j < Nw - D ; j++ ) { inputOne[j] = inputOne[j+D]; inputTwo[j] = inputTwo[j+D]; } for ( j = Nw - D; j < Nw; j++ ) { inputOne[j] = *inOne++; inputTwo[j] = *inTwo++; } /* apply hamming window and fold our window buffer into the fft buffer */ fold( inputOne, Wanal, Nw, bufferOne, N, inCount ); fold( inputTwo, Wanal, Nw, bufferTwo, N, inCount ); /* do an fft */ rdft( N, 1, bufferOne, bitshuffle, trigland ); rdft( N, 1, bufferTwo, bitshuffle, trigland ); /* convert to polar coordinates from complex values */ if (invert) { for ( i = 0; i <= N2; i++ ) { float mag_1, mag_2; odd = ( even = i<<1 ) + 1; a1 = ( i == N2 ? *(bufferOne+1) : *(bufferOne+even) ); b1 = ( i == 0 || i == N2 ? 0. : *(bufferOne+odd) ); a2 = ( i == N2 ? *(bufferTwo+1) : *(bufferTwo+even) ); b2 = ( i == 0 || i == N2 ? 0. : *(bufferTwo+odd) ); /* complex division */ mag_1 = hypot( a1, b1 ); mag_2 = hypot( a2, b2 ); if ( mag_2 > threshold ) *(channelOne+even) = mag_1 / mag_2; else *(channelOne+even) = mag_1 / threshold; if ( mag_1 != 0. && mag_2 != 0. ) *(channelOne+odd) = atan2( b2, a2 ) - atan2( b1, a1 ); else *(channelOne+odd) = 0.; /* raise resulting magnitude to a desired power */ *(channelOne+even) = pow( *(channelOne+even), exponent ); } } else { for ( i = 0; i <= N2; i++ ) { float f_real, f_imag; odd = ( even = i<<1 ) + 1; a1 = ( i == N2 ? *(bufferOne+1) : *(bufferOne+even) ); b1 = ( i == 0 || i == N2 ? 0. : *(bufferOne+odd) ); a2 = ( i == N2 ? *(bufferTwo+1) : *(bufferTwo+even) ); b2 = ( i == 0 || i == N2 ? 0. : *(bufferTwo+odd) ); /* complex multiply */ f_real = (a1 * a2) - (b1 * b2); f_imag = (a1 * b2) + (b1 * a2); *(channelOne+even) = hypot( f_real, f_imag ); *(channelOne+odd) = -atan2( f_imag, f_real ); /* raise resulting magnitude to a desired power */ *(channelOne+even) = pow( *(channelOne+even), exponent ); } } /* convert back to complex form, read for the inverse fft */ for ( i = 0; i <= N2; i++ ) { odd = ( even = i<<1 ) + 1; *(bufferOne+even) = *(channelOne+even) * cos( *(channelOne+odd) ); if ( i != N2 ) *(bufferOne+odd) = -(*(channelOne+even)) * sin( *(channelOne+odd) ); } /* do an inverse fft */ rdft( N, -1, bufferOne, bitshuffle, trigland ); /* dewindow our result */ overlapadd( bufferOne, N, Wsyn, output, Nw, inCount); /* set our output and adjust our retaining output buffer */ for ( j = 0; j < D; j++ ) *out++ = output[j] * mult; for ( j = 0; j < Nw - D; j++ ) output[j] = output[j+D]; for ( j = Nw - D; j < Nw; j++ ) output[j] = 0.; /* restore state variables */ x->inCount = inCount % Nw; return (w+8); } void codepend_dsp(t_codepend *x, t_signal **sp, short *count) { long i; #if MSP for( i = 0; i < 4; i++ ){ x->connected[i] = count[i]; } #endif /* signal is always connected in Pd */ #if PD for( i = 0; i < 4; i++ ){ x->connected[i] = 1; } #endif /* reinitialize if vector size or sampling rate has been changed */ if(x->vs != sp[0]->s_n || x->R != sp[0]->s_sr){ x->vs = sp[0]->s_n; x->R = sp[0]->s_sr; codepend_init(x,1); } dsp_add(codepend_perform, 7, x, sp[0]->s_vec, sp[1]->s_vec, sp[2]->s_vec, sp[3]->s_vec, sp[4]->s_vec, sp[0]->s_n); }