#include "MSPd.h" #include "fftease.h" // adapted for Pd #define CUSHION_FRAMES (128) // pad for grabbing #define MAX_CHANNELS (2) #define MAX_RMS_BUFFER (0.250) #define MIN_RMS_BUFFER (.001) #define MAX_EVENTS (1024) #define MAX_RMS_FRAMES (32768) #define OBJECT_NAME "buffet~" #if __MSP__ void *buffet_class; #endif #if __PD__ static t_class *buffet_class; #endif typedef struct { float *b_samples; long b_valid; long b_nchans; long b_frames; } t_guffer; // stuff we care about from garrays and buffers typedef struct _buffet { #if __MSP__ t_pxobject x_obj; #endif #if __PD__ t_object x_obj; float x_f; #endif t_symbol *wavename; // name of waveform buffer t_guffer *wavebuf; // holds waveform samples t_guffer *destbuf; // for copying to another buffer float sr; // sampling rate short hosed; // buffers are bad float minframes; // minimum replacement block in sample frames float maxframes; // maximum replacement block in sample frames long storage_maxframes; // maxframe limit that current memory can handle float *storage; //temporary memory to store replacement block (set to maxframes * channels) long storage_bytes; // amount of currently allocated memory float fade; // fadein/fadeout time in sample frames float sync; // input from groove sync signal long swapframes; // number of frames in swap block long r1startframe; //start frame for block 1 long r2startframe; // start frame for block 2 float dc_coef; // filter coefficient float dc_gain; // normalization factor short initialized; // first time or not float *rmsbuf;// for onset analysis float rmschunk; // store lowest rms value in buffer void *list; // for start/end list void *bang; // completion bang void *floater; // outputs noise floor t_atom *listdata;// to report est. start/stop times of events in buffer float *analbuf; // contain overall envelope float *onset; // contain attack times for percussive evaluations short autoredraw; // to kill redraw if it impacts performance } t_buffet; void buffet_setbuf(t_buffet *x, t_symbol *wavename); void *buffet_new(t_symbol *msg, short argc, t_atom *argv); t_int *buffet_perform(t_int *w); void buffet_dsp(t_buffet *x, t_signal **sp, short *count); void buffet_dblclick(t_buffet *x); float buffet_boundrand(float min, float max); void buffet_assist (t_buffet *x, void *b, long msg, long arg, char *dst); void buffet_dsp_free(t_buffet *x); void buffet_swap(t_buffet *x); void buffet_specswap(t_buffet *x, t_symbol *msg, short argc, t_atom *argv); void buffet_retroblock(t_buffet *x); void buffet_nakedswap(t_buffet *x); void buffet_overlap(t_buffet *x, t_floatarg f); void buffet_minswap(t_buffet *x, t_floatarg f); void buffet_maxswap(t_buffet *x, t_floatarg f); void buffet_nosync_setswap(t_buffet *x); void buffet_info(t_buffet *x); void buffet_killdc(t_buffet *x); void buffet_rmschunk(t_buffet *x, t_symbol *msg, short argc, t_atom *argv); void buffet_fadein(t_buffet *x, t_floatarg f); void buffet_fadeout(t_buffet *x, t_floatarg f); void buffet_internal_fadein(t_buffet *x, t_symbol *msg, short argc, t_atom *argv); void buffet_internal_fadeout(t_buffet *x, t_symbol *msg, short argc, t_atom *argv); void buffet_dc_gain(t_buffet *x, t_floatarg f); void buffet_dc_coef(t_buffet *x, t_floatarg f); void buffet_normalize(t_buffet *x, t_floatarg f); void buffet_rotatetozero(t_buffet *x, t_floatarg f); void buffet_autoredraw(t_buffet *x, t_floatarg f); void buffet_erase(t_buffet *x, t_symbol *msg, short argc, t_atom *argv); void buffet_events(t_buffet *x, t_symbol *msg, short argc, t_atom *argv); void buffet_pevents(t_buffet *x, t_symbol *msg, short argc, t_atom *argv); void buffet_detect_onsets(t_buffet *x, t_symbol *msg, short argc, t_atom *argv); void buffet_detect_subband_onsets(t_buffet *x, t_symbol *msg, short argc, t_atom *argv); void buffet_copy_to_buffer(t_buffet *x, t_symbol *msg, short argc, t_atom *argv); int buffet_setdestbuf(t_buffet *x, t_symbol *wavename); void buffet_init(t_buffet *x, short initialized); void buffet_reverse(t_buffet *x); void buffet_redraw(t_buffet *x); void buffet_redraw_named(t_buffet *x, t_symbol *arrayname); void buffet_update(t_buffet *x); #if __PD__ void atom_arg_getfloat(float *c, long idx, long ac, t_atom *av); void atom_arg_getsym(t_symbol **c, long idx, long ac, t_atom *av); #endif #if __MSP__ void main(void) { setup((t_messlist **)&buffet_class, (method)buffet_new, (method)buffet_dsp_free, (short)sizeof(t_buffet), 0, A_GIMME, 0); addmess((method)buffet_dsp,"dsp", A_CANT, 0); addmess((method)buffet_swap,"swap", 0); addmess((method)buffet_specswap,"specswap", A_GIMME, 0); addmess((method)buffet_events,"events", A_GIMME, 0); addmess((method)buffet_pevents,"pevents", A_GIMME, 0); addmess((method)buffet_detect_onsets,"detect_onsets", A_GIMME, 0); addmess((method)buffet_detect_subband_onsets,"detect_subband_onsets", A_GIMME, 0); addmess((method)buffet_retroblock,"retroblock", 0); addmess((method)buffet_minswap,"minswap", A_FLOAT, 0); addmess((method)buffet_maxswap,"maxswap", A_FLOAT, 0); addmess((method)buffet_fadein,"fadein", A_FLOAT, 0); addmess((method)buffet_fadeout,"fadeout", A_FLOAT, 0); addmess((method)buffet_overlap,"overlap", A_FLOAT, 0); addmess((method)buffet_dc_gain,"dc_gain", A_FLOAT, 0); addmess((method)buffet_dc_coef,"dc_coef", A_FLOAT, 0); addmess((method)buffet_normalize,"normalize", A_FLOAT, 0); addmess((method)buffet_rotatetozero,"rotatetozero", A_FLOAT, 0); addmess((method)buffet_autoredraw,"autoredraw", A_FLOAT, 0); addmess((method)buffet_killdc,"killdc", 0); addmess((method)buffet_nakedswap,"nakedswap", 0); addmess((method)buffet_rmschunk,"rmschunk", A_GIMME, 0); addmess((method)buffet_erase,"erase", A_GIMME, 0); addmess((method)buffet_copy_to_buffer,"copy_to_buffer", A_GIMME, 0); addmess((method)buffet_internal_fadein,"internal_fadein", A_GIMME, 0); addmess((method)buffet_internal_fadeout,"internal_fadeout", A_GIMME, 0); addmess((method)buffet_assist,"assist", A_CANT, 0); addmess((method)buffet_dblclick,"dblclick", A_CANT, 0); addmess((method)buffet_setbuf,"setbuf", A_SYM, 0); addmess((method)buffet_reverse,"reverse", 0); addmess((method)buffet_info,"info", 0); dsp_initclass(); post("%s %s",OBJECT_NAME, LYONPOTPOURRI_MSG); } #endif #if __PD__ void buffet_tilde_setup(void) { buffet_class = class_new(gensym("buffet~"),(t_newmethod)buffet_new,(t_method)buffet_dsp_free, sizeof(t_buffet), 0, A_GIMME,0); CLASS_MAINSIGNALIN(buffet_class,t_buffet, x_f ); class_addmethod(buffet_class,(t_method)buffet_dsp,gensym("dsp"),A_CANT,0); class_addmethod(buffet_class,(t_method)buffet_swap,gensym("swap"),0); class_addmethod(buffet_class,(t_method)buffet_specswap,gensym("specswap"),A_GIMME,0); class_addmethod(buffet_class,(t_method)buffet_events,gensym("events"),A_GIMME,0); class_addmethod(buffet_class,(t_method)buffet_pevents,gensym("pevents"),A_GIMME,0); class_addmethod(buffet_class,(t_method)buffet_detect_onsets,gensym("detect_onsets"),A_GIMME,0); class_addmethod(buffet_class,(t_method)buffet_detect_subband_onsets,gensym("detect_subband_onsets"),A_GIMME,0); class_addmethod(buffet_class,(t_method)buffet_retroblock,gensym("retroblock"),0); class_addmethod(buffet_class,(t_method)buffet_minswap,gensym("minswap"),A_FLOAT,0); class_addmethod(buffet_class,(t_method)buffet_maxswap,gensym("maxswap"),A_FLOAT,0); class_addmethod(buffet_class,(t_method)buffet_fadein,gensym("fadein"),A_FLOAT,0); class_addmethod(buffet_class,(t_method)buffet_fadeout,gensym("fadeout"),A_FLOAT,0); class_addmethod(buffet_class,(t_method)buffet_overlap,gensym("overlap"),A_FLOAT,0); class_addmethod(buffet_class,(t_method)buffet_dc_gain,gensym("dc_gain"),A_FLOAT,0); class_addmethod(buffet_class,(t_method)buffet_dc_coef,gensym("dc_coef"),A_FLOAT,0); class_addmethod(buffet_class,(t_method)buffet_normalize,gensym("normalize"),A_FLOAT,0); class_addmethod(buffet_class,(t_method)buffet_rotatetozero,gensym("rotatetozero"),A_FLOAT,0); class_addmethod(buffet_class,(t_method)buffet_autoredraw,gensym("autoredraw"),A_FLOAT,0); class_addmethod(buffet_class,(t_method)buffet_killdc,gensym("killdc"),0); class_addmethod(buffet_class,(t_method)buffet_nakedswap,gensym("nakedswap"),0); class_addmethod(buffet_class,(t_method)buffet_rmschunk,gensym("rmschunk"),A_GIMME,0); class_addmethod(buffet_class,(t_method)buffet_erase,gensym("erase"),A_GIMME,0); class_addmethod(buffet_class,(t_method)buffet_copy_to_buffer,gensym("copy_to_buffer"),A_GIMME,0); class_addmethod(buffet_class,(t_method)buffet_internal_fadein,gensym("internal_fadein"),A_GIMME,0); class_addmethod(buffet_class,(t_method)buffet_internal_fadeout,gensym("internal_fadeout"),A_GIMME,0); class_addmethod(buffet_class,(t_method)buffet_dblclick,gensym("dblclick"),A_CANT,0); class_addmethod(buffet_class,(t_method)buffet_setbuf,gensym("setbuf"),A_SYMBOL,0); class_addmethod(buffet_class,(t_method)buffet_reverse,gensym("reverse"),0); class_addmethod(buffet_class,(t_method)buffet_info,gensym("info"),0); post("%s %s",OBJECT_NAME, LYONPOTPOURRI_MSG); } #endif void buffet_info(t_buffet *x) { long totalframes; buffet_setbuf(x, x->wavename); if( x->hosed ){ error("buffet~ needs a valid buffer"); return; } if( ! x->sr){ error("zero sample rate!"); return; } totalframes = x->wavebuf->b_frames; post("minswap: %f, maxswap: %f", 1000. * x->minframes / x->sr, 1000. * x->maxframes / x->sr); post("buffer size: %f", 1000. * totalframes / x->sr); } void buffet_overlap(t_buffet *x, t_floatarg f) { if( f < 0.01 ){ error("minimum fade time is 0.01 milliseconds"); return; } x->fade = f * .001 * x->sr; } void buffet_events(t_buffet *x, t_symbol *msg, short argc, t_atom *argv) { float *b_samples; long b_nchans; long b_frames; t_atom *listdata = x->listdata; float bufsize; float onthresh; float offthresh; long bufsamps; long aframes; // frames to analyze float tadv; long i,j; float meansq; float rmsval; long bindex; float ipos; short activated = 0; float realtime = 0.0; int event_count = 0; float buffer_duration; buffet_setbuf(x, x->wavename); b_samples = x->wavebuf->b_samples; b_nchans = x->wavebuf->b_nchans; b_frames = x->wavebuf->b_frames; // duration in ms. buffer_duration = 1000.0 * (float)b_frames / x->sr; bufsize = .001 * atom_getfloatarg(0,argc,argv); if(bufsize > MAX_RMS_BUFFER){ bufsize = MAX_RMS_BUFFER; post("%s: setting analysis buffer to maximum: %f",OBJECT_NAME, MAX_RMS_BUFFER * 1000.0); } else if(bufsize < MIN_RMS_BUFFER){ bufsize = MIN_RMS_BUFFER; post("%s: setting analysis buffer to maximum: %f",OBJECT_NAME, MIN_RMS_BUFFER * 1000.0); } onthresh = atom_getfloatarg(1,argc,argv); offthresh = atom_getfloatarg(2,argc,argv); bufsamps = x->sr * bufsize; bufsamps = bufsize * x->sr; tadv = (float)bufsamps / x->sr; // post("actual window size: %f",tadv); aframes = (long) ( (float) b_frames / (float)bufsamps ) - 1; if(aframes < 2){ error("%s: this buffer is too short to analyze",OBJECT_NAME); return; } // post("analyzing %d frames",aframes); for(i = 0; i < aframes; i++){ meansq = 0.0; ipos = b_nchans * i * bufsamps; /* only analyze first channel */ for(j = 0; j < bufsamps; j+= b_nchans){ bindex = ipos + j; meansq += b_samples[bindex] * b_samples[bindex]; } meansq /= (float) bufsamps; rmsval = sqrt(meansq); realtime += tadv; if(rmsval > onthresh && ! activated) { activated = 1; // post("event %d starts at %f",event_count+1, realtime); if(event_count >= MAX_EVENTS){ error("%s: exceeded maximum of %d events",OBJECT_NAME, MAX_EVENTS); break; } SETFLOAT(listdata+(event_count*2), realtime * 1000.0); } else if( rmsval < offthresh && activated ){ activated = 0; // post("event %d ends at %f",event_count, realtime); SETFLOAT(listdata+((event_count*2) + 1), realtime * 1000.0); ++event_count; } } if(activated){ post("%s: missed the end of the last event; setting to end of buffer",OBJECT_NAME); SETFLOAT(listdata+((event_count*2) + 1), buffer_duration); ++event_count; } outlet_list(x->list, 0, event_count * 2, listdata); } void buffet_pevents(t_buffet *x, t_symbol *msg, short argc, t_atom *argv) { float *b_samples; long b_nchans; long b_frames; t_atom *listdata = x->listdata; float bufsize; //float onthresh; //float offthresh; float diffthresh; long bufsamps; long aframes; // frames to analyze float tadv; //long current_frame = 0; long i,j; float meansq; float rmsval; long bindex; float ipos; //short activated = 0; float realtime = 0.0; int event_count = 0; float buffer_duration; float mindiff, maxdiff, absdiff,rmsdiff; float *analbuf = x->analbuf; float *onset = x->onset; //float endtime; buffet_setbuf(x, x->wavename); b_samples = x->wavebuf->b_samples; b_nchans = x->wavebuf->b_nchans; b_frames = x->wavebuf->b_frames; // duration in ms. buffer_duration = 1000.0 * (float)b_frames / x->sr; bufsize = .001 * atom_getfloatarg(0,argc,argv); if(bufsize > MAX_RMS_BUFFER){ bufsize = MAX_RMS_BUFFER; post("%s: setting analysis buffer to maximum: %f",OBJECT_NAME, MAX_RMS_BUFFER * 1000.0); } else if(bufsize < MIN_RMS_BUFFER){ bufsize = MIN_RMS_BUFFER; post("%s: setting analysis buffer to maximum: %f",OBJECT_NAME, MIN_RMS_BUFFER * 1000.0); } diffthresh = atom_getfloatarg(1,argc,argv); bufsamps = x->sr * bufsize; bufsamps = bufsize * x->sr; tadv = (float)bufsamps / x->sr; aframes = (long) ( (float) b_frames / (float)bufsamps ); if(aframes < 2){ error("%s: this buffer is too short to analyze",OBJECT_NAME); return; } if(aframes > MAX_RMS_FRAMES){ post("too many frames: try a larger buffer size"); return; } analbuf[0] = 0; // for first comparison for(i = 1; i < aframes; i++){ meansq = 0.0; ipos = b_nchans * i * bufsamps; /* only analyze first channel */ for(j = 0; j < bufsamps; j+= b_nchans){ bindex = ipos + j; meansq += b_samples[bindex] * b_samples[bindex]; } meansq /= (float) bufsamps; analbuf[i] = rmsval = sqrt(meansq); realtime += tadv; } realtime = 0; mindiff = 9999.; maxdiff = 0.0; /* LPF - use elsewhere? for(i = 1; i < aframes; i++ ){ analbuf[i] = (analbuf[i] + analbuf[i-1]) * 0.5; } */ // look for big changes in direction for(i = 1; i < aframes; i++ ){ rmsdiff = analbuf[i] - analbuf[i-1]; absdiff = fabs(rmsdiff); if(absdiff > maxdiff) maxdiff = absdiff; if(absdiff < mindiff) mindiff = absdiff; if( rmsdiff > diffthresh ){ // new onset[event_count] = (realtime + bufsize) * 1000.0; if(onset[event_count] < 0) onset[event_count] = 0; ++event_count; // post("rt %f diff %f",realtime * 1000.0,rmsdiff); } realtime += tadv; } // post("mindiff %f maxdiff %f",mindiff,maxdiff); if(event_count == 0){ post("%s: no events found",OBJECT_NAME); } for(i = 0; i < event_count; i++){ SETFLOAT(listdata + i, onset[i]); } outlet_list(x->list, 0, event_count, listdata); } void buffet_internal_fadeout(t_buffet *x, t_symbol *msg, short argc, t_atom *argv) { long fadeframes; long totalframes; int i,j,k; float env; float *b_samples; long b_nchans; long startframe; long endframe; if( ! x->sr){ error("zero sample rate!"); return; } buffet_setbuf(x, x->wavename); if(x->hosed){ return; } b_samples = x->wavebuf->b_samples; b_nchans = x->wavebuf->b_nchans; totalframes = x->wavebuf->b_frames; if(argc < 2){ post("%s: internal_fadeout requires start and end times",OBJECT_NAME); return; } startframe = .001 * x->sr * atom_getfloatarg(0,argc,argv); endframe = .001 * x->sr * atom_getfloatarg(1,argc,argv); if(startframe < 0 || endframe > totalframes || endframe <= startframe){ error("%s: bad frame boundaries to internal_fadeout: %d and %d",OBJECT_NAME,startframe, endframe); return; } fadeframes = endframe - startframe; for(i = (endframe-1) * b_nchans , k = 0; k < fadeframes ; i -= b_nchans, k++ ){ env = (float) k / (float) fadeframes; for(j = 0; j < b_nchans; j++){ b_samples[i + j] *= env; } } buffet_update(x); } void buffet_internal_fadein(t_buffet *x, t_symbol *msg, short argc, t_atom *argv) { long fadeframes; long totalframes; int i,j,k; float env; float *b_samples; long b_nchans; long startframe; long endframe; if( ! x->sr){ error("zero sample rate!"); return; } buffet_setbuf(x, x->wavename); if(x->hosed){ return; } b_samples = x->wavebuf->b_samples; b_nchans = x->wavebuf->b_nchans; totalframes = x->wavebuf->b_frames; if(argc < 2){ post("%s: internal_fadeout requires start and end times",OBJECT_NAME); return; } startframe = .001 * x->sr * atom_getfloatarg(0,argc,argv); endframe = .001 * x->sr * atom_getfloatarg(1,argc,argv); if(startframe < 0 || endframe > totalframes || endframe <= startframe){ error("%s: bad frame boundaries to internal_fadein: %d and %d",OBJECT_NAME,startframe, endframe); return; } fadeframes = endframe - startframe; for(i = startframe * b_nchans , k = 0; k < fadeframes ; i += b_nchans, k++ ){ env = (float) k / (float) fadeframes; for(j = 0; j < b_nchans; j++){ b_samples[i + j] *= env; } } buffet_update(x); } void buffet_reverse(t_buffet *x) { int i,j; // float env; float *b_samples; long b_nchans; long b_frames; float tmpsamp; long lenm1; buffet_setbuf(x, x->wavename); if(!x->wavebuf->b_valid){ return; } b_samples = x->wavebuf->b_samples; b_nchans = x->wavebuf->b_nchans; b_frames = x->wavebuf->b_frames; lenm1 = (b_frames - 1) * b_nchans; for( i = 0; i < (b_frames * b_nchans) / 2; i += b_nchans){ for(j = 0; j < b_nchans; j++){ tmpsamp = b_samples[(lenm1 - i) + j]; b_samples[(lenm1 - i) + j] = b_samples[i + j]; b_samples[i + j] = tmpsamp; } } buffet_update(x); } void buffet_update(t_buffet *x) { outlet_bang(x->bang); if(x->autoredraw){ buffet_redraw(x); } } void buffet_redraw_named(t_buffet *x, t_symbol *arrayname) { #if __PD__ t_garray *a; if (!(a = (t_garray *)pd_findbyclass(arrayname, garray_class))) { if (*arrayname->s_name) pd_error(x, "%s: %s: no such array",OBJECT_NAME, arrayname->s_name); } else { garray_redraw(a); } #endif } void buffet_redraw(t_buffet *x) { #if __PD__ t_garray *a; if (!(a = (t_garray *)pd_findbyclass(x->wavename, garray_class))) { if (*x->wavename->s_name) pd_error(x, "%s: %s: no such array",OBJECT_NAME, x->wavename->s_name); x->wavebuf->b_valid = 0; } else { garray_redraw(a); } #endif } void buffet_copy_to_buffer(t_buffet *x, t_symbol *msg, short argc, t_atom *argv) { t_symbol *destname; float *b_samples; long b_nchans; long b_frames; float *b_dest_samples; long b_dest_nchans; long b_dest_frames; long startframe; long endframe; long chunkframes; float fadein,fadeout; int fadeframes; // totalframes; float env; int i,j,k; buffet_setbuf(x, x->wavename); b_samples = x->wavebuf->b_samples; b_nchans = x->wavebuf->b_nchans; b_frames = x->wavebuf->b_frames; #if __MSP__ destname = atom_getsymarg(0,argc,argv); #endif #if __PD__ destname = atom_getsymbolarg(0,argc,argv); #endif if(! buffet_setdestbuf(x, destname)){ post("%s: could not find buffer %s",OBJECT_NAME,destname->s_name); return; } b_dest_samples = x->destbuf->b_samples; b_dest_nchans = x->destbuf->b_nchans; b_dest_frames = x->destbuf->b_frames; startframe = atom_getfloatarg(1,argc,argv) * .001 * x->sr; endframe = atom_getfloatarg(2,argc,argv) * .001 * x->sr; chunkframes = endframe - startframe; if(chunkframes <= 0){ return; } if(b_nchans != b_dest_nchans){ error("%s: channel mismatch with buffer %s",OBJECT_NAME, destname->s_name); return; } if(b_dest_frames < chunkframes ){ // post("%s: %s is too small, truncating copy region",OBJECT_NAME, destname->s_name); chunkframes = b_dest_frames; } if(startframe < 0 || endframe >= b_frames){ error("%s: bad frame range for source buffer: %d %d",OBJECT_NAME, startframe,endframe); return; } /* first clean out destination */ memset((char *)b_dest_samples, 0, b_dest_frames * b_dest_nchans * sizeof(float)); /* now copy segment */ memcpy(b_dest_samples, b_samples + (startframe * b_nchans), chunkframes * b_nchans * sizeof(float) ); if(argc == 5){ // post("enveloping"); fadein = atom_getfloatarg(3,argc,argv); fadeout = atom_getfloatarg(4,argc,argv); if(fadein > 0){ // post("fading in"); fadeframes = fadein * .001 * x->sr; if( fadeframes > b_dest_frames){ error("%s: fadein is too long",OBJECT_NAME); return; } for(i = 0 , k = 0; k < fadeframes ; i += b_dest_nchans, k++ ){ env = (float) k / (float) fadeframes; for(j = 0; j < b_dest_nchans; j++){ b_dest_samples[i + j] *= env; } } } if(fadeout > 0){ // post("fading out"); // totalframes = chunkframes; fadeframes = fadeout * .001 * x->sr; startframe = chunkframes - fadeframes; endframe = chunkframes; // endframe = .001 * x->sr * atom_getfloatarg(1,argc,argv); // stays the same if(startframe < 0){ error("%s: bad frame boundaries to internal_fadeout: %d and %d", OBJECT_NAME,startframe, endframe); return; } // fadeframes = endframe - startframe; // we already know this for(i = (chunkframes-1) * b_dest_nchans , k = 0; k < fadeframes ; i -= b_dest_nchans, k++ ){ env = (float) k / (float) fadeframes; for(j = 0; j < b_dest_nchans; j++){ b_dest_samples[i + j] *= env; } } } } buffet_redraw_named(x, destname); outlet_bang(x->bang); } int buffet_setdestbuf(t_buffet *x, t_symbol *wavename) { #if __MSP__ t_buffer *b; x->hosed = 0; if ((b = (t_buffer *)(wavename->s_thing)) && ob_sym(b) == gensym("buffer~")) { if( b->b_nchans > 2 ){ error("%s: wavetable must be a mono or stereo buffer", OBJECT_NAME); x->hosed = 1; x->destbuf->b_valid = 0; } else { x->destbuf->b_samples = b->b_samples; x->destbuf->b_valid = b->b_valid; x->destbuf->b_nchans = b->b_nchans; x->destbuf->b_frames = b->b_frames; } } else { error("%s: no such buffer~ %s", OBJECT_NAME, wavename->s_name); x->hosed = 1; x->destbuf->b_valid = 0; } #endif #if __PD__ t_garray *a; int b_frames; float *b_samples; if (!(a = (t_garray *)pd_findbyclass(wavename, garray_class))) { if (*wavename->s_name) pd_error(x, "%s: %s: no such array",OBJECT_NAME,wavename->s_name); x->destbuf->b_valid = 0; } else if (!garray_getfloatarray(a, &b_frames, &b_samples)) { pd_error(x, "%s: bad array for %s", wavename->s_name,OBJECT_NAME); x->destbuf->b_valid = 0; } else { x->destbuf->b_valid = 1; x->destbuf->b_frames = (long)b_frames; x->destbuf->b_nchans = 1; x->destbuf->b_samples = b_samples; garray_usedindsp(a); } #endif /*We added this in linux - better check that it works */ return x->destbuf->b_valid; } void buffet_rmschunk(t_buffet *x, t_symbol *msg, short argc, t_atom *argv) { float *b_samples; long b_nchans; long b_frames; long bufsamps; long i; float meansq; float rmsval; long bindex; float buffer_duration; long startframe; long endframe; buffet_setbuf(x, x->wavename); b_samples = x->wavebuf->b_samples; b_nchans = x->wavebuf->b_nchans; b_frames = x->wavebuf->b_frames; // duration in ms. buffer_duration = 1000.0 * (float)b_frames / x->sr; startframe = .001 * x->sr * atom_getfloatarg(0,argc,argv); endframe = .001 * x->sr * atom_getfloatarg(1,argc,argv); if(startframe < 0 || startframe >= b_frames - 1){ error("%s: naughty start frame: %d",OBJECT_NAME,startframe); return; } if(endframe < 2 || endframe >= b_frames){ error("%s: naughty start frame: %d",OBJECT_NAME,startframe); return; } bufsamps = (endframe - startframe); // post("analyzing %d samples",bufsamps); if(!bufsamps){ // post("%s: start and end are identical!",OBJECT_NAME); // post("instantaneous sample value [ch 1 only]: %f", b_samples[startframe*b_nchans]); return; } meansq = 0.0; for(i = startframe; i < endframe; i++){ bindex = b_nchans * i; /* only analyze first channel */ meansq += b_samples[bindex] * b_samples[bindex]; } meansq /= (float) bufsamps; rmsval = sqrt(meansq); x->rmschunk = rmsval; outlet_float(x->floater, (double)x->rmschunk); } void buffet_dc_gain(t_buffet *x, t_floatarg f) { x->dc_gain = f; } void buffet_dc_coef(t_buffet *x, t_floatarg f) { x->dc_coef = f; } void buffet_autoredraw(t_buffet *x, t_floatarg f) { x->autoredraw = (short)f; } void buffet_minswap(t_buffet *x, t_floatarg f) { if(f < 2.0 * 1000.0 * x->fade / x->sr){ error("minimum must be at least twice fade time which is %f", 1000. * x->fade / x->sr); return; } x->minframes = f * .001 * x->sr; // post("min set to %f samples",x->minframes); } void buffet_maxswap(t_buffet *x, t_floatarg f) { long oldmem; // long framelimit; long newframes; newframes = f * .001 * x->sr; if(newframes <= x->minframes){ error("max blocksize must exceed minimum blocksize, which is %f", 1000. * x->minframes/ x->sr); } if(newframes > x->storage_maxframes ){ // post("extending memory"); oldmem = x->storage_bytes; // post("old memory %d", oldmem); x->storage_maxframes = newframes; x->storage_bytes = (x->storage_maxframes + 1) * 2 * sizeof(float); // post("new memory %d", x->storage_bytes); x->storage = (float *) resizebytes((char *)x->storage, oldmem, x->storage_bytes); } x->maxframes = newframes; } void buffet_erase(t_buffet *x, t_symbol *msg, short argc, t_atom *argv) { float *b_samples; long b_nchans; long b_frames; long startframe, endframe; //int slab; //int offset; int i; if(argc < 2){ post("%s: erase requires start and end times",OBJECT_NAME); return; } buffet_setbuf(x, x->wavename); if(x->hosed){ return; } b_samples = x->wavebuf->b_samples; b_nchans = x->wavebuf->b_nchans; b_frames = x->wavebuf->b_frames; startframe = .001 * x->sr * atom_getfloatarg(0,argc,argv); endframe = .001 * x->sr * atom_getfloatarg(1,argc,argv); if(startframe < 0){ startframe = 0; } if(endframe > b_frames - 1){ endframe = b_frames - 1; } if(startframe >= b_frames - 1){ error("%s: naughty start frame: %d",OBJECT_NAME,startframe); return; } if(endframe < 2 || endframe <= startframe){ error("%s: naughty end frame: %d",OBJECT_NAME,endframe); return; } for(i = startframe * b_nchans; i < endframe * b_nchans; i++){ b_samples[i] = 0.0; } buffet_update(x); } void buffet_rotatetozero(t_buffet *x, t_floatarg f) { float target = (float) f; long shiftframes = (long) (target * 0.001 * x->sr); float *b_samples; long b_nchans; long b_frames; //long i,j,k; //long offsetindex; //long offsetframe; //float tmpsamp; float *tmpmem; buffet_setbuf(x, x->wavename); if(x->hosed){ return; } b_samples = x->wavebuf->b_samples; b_nchans = x->wavebuf->b_nchans; b_frames = x->wavebuf->b_frames; if(shiftframes <= 0 || shiftframes >= b_frames){ error("%s: shift target %f is out of range",OBJECT_NAME,target); return; } tmpmem = (float *) malloc(shiftframes * b_nchans * sizeof(float)); /* copy shift block to tmp */ memcpy(tmpmem, b_samples, shiftframes * b_nchans * sizeof(float)); /* now shift the rest to the top */ memmove(b_samples, b_samples + (shiftframes * b_nchans), (b_frames - shiftframes) * b_nchans * sizeof(float)); /* finally copy tmp to the tail */ memcpy(b_samples + (b_frames - shiftframes) * b_nchans,tmpmem, shiftframes * b_nchans * sizeof(float )); free(tmpmem); buffet_update(x); } void buffet_normalize(t_buffet *x, t_floatarg f) { float target = (float) f; float *b_samples; long b_nchans; long b_frames; long i; float maxamp = 0.0; float amptest; float rescale; if(target <= 0.0){ error("%s: normalize target %f is too low",OBJECT_NAME,target); return; } buffet_setbuf(x, x->wavename); if(x->hosed){ return; } b_samples = x->wavebuf->b_samples; b_nchans = x->wavebuf->b_nchans; b_frames = x->wavebuf->b_frames; for(i = 0; i < b_frames * b_nchans; i++){ amptest = fabs(b_samples[i]); if(maxamp < amptest) maxamp = amptest; } if(maxamp < .000000001){ post("%s: amplitude zero or too low to normalize in \"%s\"",OBJECT_NAME,x->wavename->s_name); return; } rescale = target / maxamp; if(rescale > .99 && rescale < 1.01){ post("%s: \"%s\" already normalized to %f",OBJECT_NAME,x->wavename->s_name,target); } else { for(i = 0; i < b_frames * b_nchans; i++){ b_samples[i] *= rescale; } } buffet_update(x); } void buffet_fadein(t_buffet *x, t_floatarg f) { long fadeframes; long totalframes; int i,j,k; float env; float *b_samples; long b_nchans; if( ! x->sr){ error("zero sample rate!"); return; } fadeframes = f * .001 * x->sr; //post("fading in %d frames",fadeframes); buffet_setbuf(x, x->wavename); if(x->hosed){ return; } b_samples = x->wavebuf->b_samples; b_nchans = x->wavebuf->b_nchans; totalframes = x->wavebuf->b_frames; if( fadeframes > totalframes){ error("fadein is too long"); return; } for(i = 0, k = 0;i < fadeframes * b_nchans; i += b_nchans, k++ ){ env = (float) k / (float) fadeframes; for(j = 0; j < b_nchans; j++){ b_samples[i + j] *= env; } } buffet_update(x); } void buffet_fadeout(t_buffet *x, t_floatarg f) { long fadeframes; long totalframes; int i,j,k; float env; float *b_samples; long b_nchans; if( ! x->sr){ error("zero sample rate!"); return; } fadeframes = f * .001 * x->sr; buffet_setbuf(x, x->wavename); if(x->hosed){ return; } b_samples = x->wavebuf->b_samples; b_nchans = x->wavebuf->b_nchans; totalframes = x->wavebuf->b_frames; if( fadeframes > totalframes){ error("%s: fadein is too long",OBJECT_NAME); return; } for(i = (totalframes-1) * b_nchans , k = 0; k < fadeframes ; i -= b_nchans, k++ ){ env = (float) k / (float) fadeframes; for(j = 0; j < b_nchans; j++){ b_samples[i + j] *= env; } } buffet_update(x); } void buffet_killdc(t_buffet *x) { // long fadeframes; long totalframes; int i,j; float *b_samples; long b_nchans; float dc_coef = x->dc_coef; float a0[MAX_CHANNELS]; float a1[MAX_CHANNELS]; float b0[MAX_CHANNELS]; float b1[MAX_CHANNELS]; buffet_setbuf(x, x->wavename); if(x->hosed){ return; } b_samples = x->wavebuf->b_samples; b_nchans = x->wavebuf->b_nchans; totalframes = x->wavebuf->b_frames; if(b_nchans > MAX_CHANNELS) { error("buffer has too many channels"); return; } for(j = 0; j < b_nchans; j++){ a0[j] = a1[j] = b0[j] = b1[j] = 0.0; } for(i = 0; i < totalframes * b_nchans; i += b_nchans){ for(j = 0; j < b_nchans; j++){ a0[j] = b_samples[i + j]; b0[j] = a0[j] - a1[j] + dc_coef * b1[j]; b_samples[i + j] = b0[j]; b1[j] = b0[j]; a1[j] = a0[j]; } } buffet_update(x); } void *buffet_new(t_symbol *msg, short argc, t_atom *argv) { // int i; srand(clock()); #if __MSP__ t_buffet *x = (t_buffet *)newobject(buffet_class); x->floater = floatout((t_pxobject *)x);// right outlet x->list = listout((t_pxobject *)x); // middle outlet x->bang = bangout((t_pxobject *)x); // left outlet dsp_setup((t_pxobject *)x,1); #endif #if __PD__ t_buffet *x = (t_buffet *)pd_new(buffet_class); x->bang = outlet_new(&x->x_obj, gensym("bang")); x->list = outlet_new(&x->x_obj, gensym("list")); x->floater = outlet_new(&x->x_obj, gensym("float")); #endif x->sr = sys_getsr(); if(! x->sr ) x->sr = 44100; if(argc < 1){ error("%s: you must provide a valid buffer name",OBJECT_NAME); return 0; } atom_arg_getsym(&x->wavename,0,argc,argv); atom_arg_getfloat(&x->minframes,1,argc,argv); atom_arg_getfloat(&x->maxframes,2,argc,argv); if(!x->minframes) x->minframes = 100; if(!x->maxframes) x->maxframes = x->minframes + 10; buffet_init(x,0); return (x); } void buffet_init(t_buffet *x, short initialized) { if(x->minframes <= 0) x->minframes = 250; if(x->maxframes <= 0) x->maxframes = 1000; if(!x->initialized){ x->minframes *= .001 * x->sr; x->storage_maxframes = x->maxframes *= .001 * x->sr; x->fade = .001 * 20 * x->sr; // 20 ms fadetime to start x->storage_bytes = (x->maxframes + 1) * 2 * sizeof(float); // stereo storage frames x->storage = (float *) getbytes(x->storage_bytes); x->dc_coef = .995; // for dc blocker x->dc_gain = 4.0; x->autoredraw = 1; x->rmsbuf = (float *) getbytes(MAX_RMS_BUFFER * x->sr * sizeof(float)); memset((char *)x->rmsbuf, 0, MAX_RMS_BUFFER * x->sr * sizeof(float)); x->listdata = (t_atom *)getbytes(MAX_EVENTS * sizeof(t_atom));// lots of events x->analbuf = (float *) getbytes(MAX_RMS_FRAMES * sizeof(float)); memset((char *)x->analbuf, 0, MAX_RMS_FRAMES * sizeof(float)); x->onset = (float *) getbytes(MAX_EVENTS * sizeof(float)); x->wavebuf = (t_guffer *) getbytes(1 * sizeof(t_guffer)); x->destbuf = (t_guffer *) getbytes(1 * sizeof(t_guffer)); } else { x->minframes *= .001 * x->sr; x->storage_maxframes = x->maxframes *= .001 * x->sr; x->fade = .001 * 20 * x->sr; // 20 ms fadetime to start x->storage_bytes = (x->maxframes + 1) * 2 * sizeof(float); // stereo storage frames x->storage = (float *) resizebytes((char *)x->storage_bytes,0,x->storage_bytes); x->rmsbuf = (float *)resizebytes((char *)x->rmsbuf,0,MAX_RMS_BUFFER * x->sr * sizeof(float)); memset((char *)x->rmsbuf, 0, MAX_RMS_BUFFER * x->sr * sizeof(float)); } } void buffet_nosync_setswap(t_buffet *x) { long totalframes = x->wavebuf->b_frames; float minframes = x->minframes; float maxframes = x->maxframes; long swapframes = x->swapframes; long r1startframe = x->r1startframe; long r2startframe = x->r2startframe; long r1endframe; long r2endframe; long region1; long region2; swapframes = buffet_boundrand(minframes, maxframes); r1startframe = buffet_boundrand(0.0, (float)(totalframes-swapframes)); r1endframe = r1startframe + swapframes; region1 = r1startframe; region2 = totalframes - r1endframe; if(swapframes > region1){ r2startframe = buffet_boundrand((float)r1endframe,(float)(totalframes-swapframes)); } else if(swapframes > region2) { r2startframe = buffet_boundrand(0.0,(float)(r1startframe-swapframes)); } else { // either region ok if(buffet_boundrand(0.0,1.0) > 0.5){ r2startframe = buffet_boundrand(0.0,(float)(r1startframe-swapframes)); } else { r2startframe = buffet_boundrand((float)r1endframe,(float)(totalframes-swapframes)); } } r2endframe = r2startframe + swapframes; if(r2startframe < 0 || r1startframe < 0){ error("start frame less than zero!"); return; } if(r2endframe >= totalframes || r1endframe >= totalframes){ error("end frame reads beyond buffer!"); return; } x->swapframes = swapframes; x->r1startframe = r1startframe; x->r2startframe = r2startframe; outlet_bang(x->bang); } void buffet_swap(t_buffet *x) { float maxframes = x->maxframes; float fade = x->fade; long totalframes; long swapframes; long start_sample1; long start_sample2; float *storage = x->storage; float *b_samples; float mix_sample; float fadein_gain; float fadeout_gain; float fadephase; long b_nchans; int i,j,k; buffet_setbuf(x, x->wavename); if(x->hosed){ return; } b_samples = x->wavebuf->b_samples; b_nchans = x->wavebuf->b_nchans; totalframes = x->wavebuf->b_frames; if(totalframes < maxframes * 2 + 1){ error("buffer must contain at least twice as many samples as the maximum swap size"); return; } if(b_nchans > 2){ error("buffet~ only accepts mono or stereo buffers"); return; } buffet_nosync_setswap(x); start_sample1 = x->r1startframe * b_nchans; start_sample2 = x->r2startframe * b_nchans; swapframes = x->swapframes; // store block1 samples for(i = 0; i < swapframes * b_nchans; i += b_nchans){ for(j = 0; j < b_nchans; j++){ storage[i+j] = b_samples[start_sample1 + i + j]; } } // swap block2 into block1 location, fadein for(i = 0, k = 0; i < fade * b_nchans; i += b_nchans, k++){ fadephase = ((float)k / (float) fade) * PIOVERTWO; fadein_gain = sin(fadephase); fadeout_gain = cos(fadephase); for(j = 0; j < b_nchans; j++){ mix_sample = fadein_gain * b_samples[start_sample2 + i + j] + fadeout_gain * b_samples[start_sample1 + i + j]; b_samples[start_sample1 + i + j] = mix_sample; } } // middle part, pure swap for(i = fade * b_nchans; i < (swapframes-fade) * b_nchans; i += b_nchans){ for(j = 0; j < b_nchans; j++){ b_samples[start_sample1 + i + j] = b_samples[start_sample2 + i + j]; } } // fade out for(i = (swapframes-fade) * b_nchans, k = 0; i < swapframes * b_nchans; i += b_nchans, k++){ fadephase = ((float)k / (float) fade) * PIOVERTWO; fadein_gain = sin(fadephase); fadeout_gain = cos(fadephase); for(j = 0; j < b_nchans; j++){ mix_sample = fadeout_gain * b_samples[start_sample2 + i + j] + fadein_gain * b_samples[start_sample1 + i + j]; b_samples[start_sample1 + i + j] = mix_sample; } } // now mix stored block1 into block2 location // swap block2 into block1 location, fadein for(i = 0, k = 0; i < fade * b_nchans; i += b_nchans, k++){ fadein_gain = (float)k / (float) fade; fadeout_gain = 1.0 - fadein_gain; for(j = 0; j < b_nchans; j++){ mix_sample = fadein_gain * storage[i + j] + fadeout_gain * b_samples[start_sample2 + i + j]; b_samples[start_sample2 + i + j] = mix_sample; } } // middle part, pure swap for(i = fade * b_nchans; i < (swapframes-fade) * b_nchans; i += b_nchans){ for(j = 0; j < b_nchans; j++){ b_samples[start_sample2 + i + j] = storage[i + j]; } } // fade out for(i = (swapframes-fade) * b_nchans, k = 0; i < swapframes * b_nchans; i += b_nchans, k++){ fadein_gain = (float)k / (float) fade; fadeout_gain = 1.0 - fadein_gain; for(j = 0; j < b_nchans; j++){ mix_sample = fadeout_gain * storage[i + j] + fadein_gain * b_samples[start_sample2 + i + j]; b_samples[start_sample2 + i + j] = mix_sample; } } buffet_update(x); } void buffet_specswap(t_buffet *x, t_symbol *msg, short argc, t_atom *argv) { // float minframes = x->minframes; float maxframes = x->maxframes; float fade = x->fade; long totalframes; long swapframes; long r1startframe; long r1endframe; long r2startframe; long r2endframe; long region1; long region2; long start_sample1; long start_sample2; float *storage = x->storage; float *b_samples; float mix_sample; float fadein_gain; float fadeout_gain; float fadephase; long b_nchans; int i,j,k; buffet_setbuf(x, x->wavename); if(x->hosed){ return; } b_samples = x->wavebuf->b_samples; b_nchans = x->wavebuf->b_nchans; totalframes = x->wavebuf->b_frames; if(totalframes < maxframes * 2 + 1){ error("buffer must contain at least twice as many samples as the maximum swap size"); return; } if(b_nchans > 2){ error("buffet~ only accepts mono or stereo buffers"); return; } r1startframe = x->sr * .001 * atom_getfloatarg(0,argc,argv); r2startframe = x->sr * .001 * atom_getfloatarg(1,argc,argv); swapframes = x->sr * .001 * atom_getfloatarg(2,argc,argv); if( r1startframe < 0 || r1startframe >= totalframes){ error("bad first skip time"); return; } if( r2startframe < 0 || r2startframe >= totalframes){ error("bad second skip time"); return; } // post("start1 %d start2 %d swaps %d",r1startframe,r2startframe,swapframes ); r1endframe = r1startframe + swapframes; r2endframe = r2startframe + swapframes; region1 = r1startframe; region2 = totalframes - r1endframe; /* post("min: %.0f, max: %.0f, total: %d, swap: %d",minframes, maxframes,totalframes, swapframes); post("r1st %d, r1end %d, region1 %d, region2 %d",r1startframe, r1endframe, region1, region2); */ if(swapframes > x->storage_maxframes) { error("swapsize %d is larger than %d; reset maximum swap.", swapframes,x->storage_maxframes ); return; } if(r1endframe >= totalframes){ error("block 1 reads beyond buffer!"); return; } if(r2endframe >= totalframes){ error("block 2 reads beyond buffer!"); return; } // store block1 samples start_sample1 = r1startframe * b_nchans; start_sample2 = r2startframe * b_nchans; for(i = 0; i < swapframes * b_nchans; i += b_nchans){ for(j = 0; j < b_nchans; j++){ storage[i+j] = b_samples[start_sample1 + i + j]; } } // swap block2 into block1 location, fadein for(i = 0, k = 0; i < fade * b_nchans; i += b_nchans, k++){ fadephase = ((float)k / (float) fade) * PIOVERTWO; fadein_gain = sin(fadephase); fadeout_gain = cos(fadephase); for(j = 0; j < b_nchans; j++){ mix_sample = fadein_gain * b_samples[start_sample2 + i + j] + fadeout_gain * b_samples[start_sample1 + i + j]; b_samples[start_sample1 + i + j] = mix_sample; } } // middle part, pure swap for(i = fade * b_nchans; i < (swapframes-fade) * b_nchans; i += b_nchans){ for(j = 0; j < b_nchans; j++){ b_samples[start_sample1 + i + j] = b_samples[start_sample2 + i + j]; } } // fade out for(i = (swapframes-fade) * b_nchans, k = 0; i < swapframes * b_nchans; i += b_nchans, k++){ fadephase = ((float)k / (float) fade) * PIOVERTWO; fadein_gain = sin(fadephase); fadeout_gain = cos(fadephase); for(j = 0; j < b_nchans; j++){ mix_sample = fadeout_gain * b_samples[start_sample2 + i + j] + fadein_gain * b_samples[start_sample1 + i + j]; b_samples[start_sample1 + i + j] = mix_sample; } } // now mix stored block1 into block2 location // swap block2 into block1 location, fadein for(i = 0, k = 0; i < fade * b_nchans; i += b_nchans, k++){ fadein_gain = (float)k / (float) fade; fadeout_gain = 1.0 - fadein_gain; for(j = 0; j < b_nchans; j++){ mix_sample = fadein_gain * storage[i + j] + fadeout_gain * b_samples[start_sample2 + i + j]; b_samples[start_sample2 + i + j] = mix_sample; } } // middle part, pure swap for(i = fade * b_nchans; i < (swapframes-fade) * b_nchans; i += b_nchans){ for(j = 0; j < b_nchans; j++){ b_samples[start_sample2 + i + j] = storage[i + j]; } } // fade out for(i = (swapframes-fade) * b_nchans, k = 0; i < swapframes * b_nchans; i += b_nchans, k++){ fadein_gain = (float)k / (float) fade; fadeout_gain = 1.0 - fadein_gain; for(j = 0; j < b_nchans; j++){ mix_sample = fadeout_gain * storage[i + j] + fadein_gain * b_samples[start_sample2 + i + j]; b_samples[start_sample2 + i + j] = mix_sample; } } buffet_update(x); } void buffet_retroblock(t_buffet *x) { float minframes = x->minframes; float maxframes = x->maxframes; float fade = x->fade; long totalframes; long swapframes; long r1startframe; long r1endframe; long start_sample1; float *storage = x->storage; float *b_samples; float mix_sample; float fadein_gain; float fadeout_gain; // float fadephase; long ub1, lb1; long ub2, lb2; long block1size,block2size; long b_nchans; long syncframe; int i,j,k; buffet_setbuf(x, x->wavename); if(x->hosed){ return; } b_samples = x->wavebuf->b_samples; b_nchans = x->wavebuf->b_nchans; totalframes = x->wavebuf->b_frames; if(totalframes < maxframes * b_nchans + 1){ error("buffer must contain at least twice as many samples as the maximum swap size"); return; } if(b_nchans > 2){ error("buffet~ only accepts mono or stereo buffers"); return; } syncframe = x->sync * totalframes; swapframes = buffet_boundrand(minframes, maxframes); if(x->sync <= 0.0 ){ // either no sync signal or it is zero r1startframe = buffet_boundrand(0.0, (float)(totalframes-swapframes)); r1endframe = r1startframe + swapframes; } else { // avoid swapping where we are playing from buffer lb1 = 0; ub1 = syncframe - CUSHION_FRAMES; // could be reading buffer backwards lb2 = ub1 + CUSHION_FRAMES; ub2 = totalframes - 1; block1size = ub1 - lb1; block2size = ub2 - lb2; if(block1size < maxframes && block2size < maxframes ) { error("could not reverse block"); return; } if(block1size >= maxframes && block2size >= maxframes){ if(buffet_boundrand(0.0,1.0) > 0.5){ r1startframe = buffet_boundrand(0.0, (float)(ub1-swapframes)); } else { r1startframe = buffet_boundrand((float)lb2, (float)(ub2-swapframes)); } } else if(block1size < maxframes) { r1startframe = buffet_boundrand((float)lb2, (float)(ub2-swapframes)); } else { r1startframe = buffet_boundrand(0.0, (float)(ub1-swapframes)); } r1endframe = r1startframe + swapframes; } if(r1endframe >= totalframes){ error("%s: retro beyond bounds",OBJECT_NAME); return; } // store block1 samples start_sample1 = r1startframe * b_nchans; // start_sample2 = r2startframe * b_nchans; // store block in reversed order for(k = 0, i = (swapframes-1) * b_nchans; i > 0; i -= b_nchans, k += b_nchans){ for(j = 0; j < b_nchans; j++){ storage[i+j] = b_samples[start_sample1 + k + j]; } } // swap block2 into block1 location, fadein for(i = 0, k = 0; i < fade * b_nchans; i += b_nchans, k++){ fadein_gain = (float)k / (float) fade; fadeout_gain = 1.0 - fadein_gain; for(j = 0; j < b_nchans; j++){ mix_sample = fadein_gain * storage[i + j] + fadeout_gain * b_samples[start_sample1 + i + j]; b_samples[start_sample1 + i + j] = mix_sample; } } // middle part, pure swap for(i = fade * b_nchans; i < (swapframes-fade) * b_nchans; i += b_nchans){ for(j = 0; j < b_nchans; j++){ b_samples[start_sample1 + i + j] = storage[i + j]; } } // fade out for(i = (swapframes-fade) * b_nchans, k = 0; i < swapframes * b_nchans; i += b_nchans, k++){ fadein_gain = (float)k / (float) fade; fadeout_gain = 1.0 - fadein_gain; for(j = 0; j < b_nchans; j++){ mix_sample = fadeout_gain * storage[i + j] + fadein_gain * b_samples[start_sample1 + i + j]; b_samples[start_sample1 + i + j] = mix_sample; } } buffet_update(x); } // clicky version void buffet_nakedswap(t_buffet *x) { //long long minframes = x->minframes; long maxframes = x->maxframes; long totalframes; long swapframes; long r1startframe; long r1endframe; long r2startframe; long r2endframe; long region1; long region2; long start_sample1; long start_sample2; float *storage = x->storage; float *b_samples; long b_nchans; int i,j; buffet_setbuf(x, x->wavename); b_samples = x->wavebuf->b_samples; b_nchans = x->wavebuf->b_nchans; totalframes = x->wavebuf->b_frames; if(totalframes < maxframes * 2 + 1){ error("buffer must contain at least twice as many samples as the maximum swap size"); return; } if(b_nchans != 2){ error("buffet~ only accepts stereo buffers"); return; } swapframes = buffet_boundrand((float)minframes, (float)maxframes); r1startframe = buffet_boundrand(0.0, (float)(totalframes-swapframes)); r1endframe = r1startframe + swapframes; region1 = r1startframe; region2 = totalframes - r1endframe; if(swapframes > region1){ r2startframe = buffet_boundrand((float)r1endframe,(float)(totalframes-swapframes)); } else if(swapframes > region2) { r2startframe = buffet_boundrand(0.0,(float)(r1startframe-swapframes)); } else { if(buffet_boundrand(0.0,1.0) > 0.5){ r2startframe = buffet_boundrand(0.0,(float)(r1startframe-swapframes)); } else { r2startframe = buffet_boundrand((float)r1endframe,(float)(totalframes-swapframes)); } } r2endframe = r2startframe + swapframes; if(r2startframe < 0 || r1startframe < 0){ error("start frame less than zero!"); return; } if(r2endframe >= totalframes || r1endframe >= totalframes){ error("end frame reads beyond buffer!"); return; } // Now swap the samples. For now no cross fade start_sample1 = r1startframe * b_nchans; start_sample2 = r2startframe * b_nchans; for(i = 0; i < swapframes * b_nchans; i += b_nchans){ for(j = 0; j < b_nchans; j++){ storage[i+j] = b_samples[start_sample1 + i + j]; } } // swap block1 into block2 for(i = 0; i < swapframes * b_nchans; i += b_nchans){ for(j = 0; j < b_nchans; j++){ b_samples[start_sample1 + i + j] = b_samples[start_sample2 + i + j]; } } // swap stored block into block1 for(i = 0; i < swapframes * b_nchans; i += b_nchans){ for(j = 0; j < b_nchans; j++){ b_samples[start_sample2 + i + j] = storage[i + j]; } } buffet_update(x); } void buffet_dblclick(t_buffet *x) { #if __MSP__ t_buffer *b; t_symbol *wavename = x->wavename; if ((b = (t_buffer *)(wavename->s_thing)) && ob_sym(b) == gensym("buffer~")) mess0((t_object *)b,gensym("dblclick")); #endif } void buffet_setbuf(t_buffet *x, t_symbol *wavename) { #if __MSP__ t_buffer *b; x->hosed = 0; if ((b = (t_buffer *)(wavename->s_thing)) && ob_sym(b) == gensym("buffer~")) { if( b->b_nchans > 2 ){ error("%s: wavetable must be a mono or stereo buffer", OBJECT_NAME); x->hosed = 1; } else { x->wavebuf->b_samples = b->b_samples; x->wavebuf->b_valid = b->b_valid; x->wavebuf->b_nchans = b->b_nchans; x->wavebuf->b_frames = b->b_frames; } } else { error("%s: no such buffer~ %s", OBJECT_NAME, wavename->s_name); x->hosed = 1; } x->wavename = wavename; #endif #if __PD__ t_garray *a; int b_frames; float *b_samples; if (!(a = (t_garray *)pd_findbyclass(wavename, garray_class))) { if (*wavename->s_name) pd_error(x, "%s: %s: no such array",OBJECT_NAME,wavename->s_name); x->wavebuf->b_samples = 0; x->wavebuf->b_valid = 0; } else if (!garray_getfloatarray(a, &b_frames, &b_samples)) { pd_error(x, "%s: bad array for %s", wavename->s_name,OBJECT_NAME); x->wavebuf->b_valid = 0; } else { x->wavebuf->b_valid = 1; x->wavebuf->b_frames = (long)b_frames; x->wavebuf->b_nchans = 1; x->wavebuf->b_samples = b_samples; garray_usedindsp(a); } #endif } t_int *buffet_perform(t_int *w) { t_buffet *x = (t_buffet *) (w[1]); float *sync = (t_float *)(w[2]); int n = w[3]; while(n--){ x->sync = *sync++; } return (w+4); } float buffet_boundrand(float min, float max) { return min + (max-min) * ((float) (rand() % RAND_MAX)/ (float) RAND_MAX); } void buffet_detect_onsets(t_buffet *x, t_symbol *msg, short argc, t_atom *argv) { float *b_samples; long b_nchans; long b_frames; t_atom *listdata = x->listdata; long startframe; long currentframe; long endframe; long sample_frames; long fft_frames; int R = x->sr; int N = 1024; int N2 = N / 2; int Nw = N; int Nw2 = Nw / 2; int D = N / 2; int i,j,k,m; // float sb1,sb2,sb3,sb4,energy=0,lastenergy; float freqcenter=0; float tmp; float fave; // freq average short first = 1; float dsum; short suppress = 0; float suppressrt = 0; float rt, rtadv; int inCount; float *Wanal; float *Wsyn; float *input; float *Hwin; float *buffer; float *channel; float *output; float *input_vec; float *specdiff; float *freqdiff; float *hfc; float *freqs, *amps; float lastfreqs[5]; float fdiffs[5]; float *onsets; float **loveboat; float *c_lastphase_in; float *c_lastphase_out; float c_fundamental; float c_factor_in; float *trigland; int *bitshuffle; int topanalbin; float threshold; int bytesize; int MAX_ONSETS = 2048; int onset_count = 0; buffet_setbuf(x, x->wavename); if( x->hosed ){ error("buffet~ needs a valid buffer"); return; } if(argc < 4){ post("%s: detect_onsets requires start and end times,threshold and FFTsize",OBJECT_NAME); return; } b_samples = x->wavebuf->b_samples; b_nchans = x->wavebuf->b_nchans; b_frames = x->wavebuf->b_frames; startframe = .001 * x->sr * atom_getfloatarg(0,argc,argv); endframe = .001 * x->sr * atom_getfloatarg(1,argc,argv); threshold = atom_getfloatarg(2,argc,argv); N = atom_getfloatarg(3,argc,argv); N2 = N / 2; Nw = N; Nw2 = Nw / 2; D = N / 2; sample_frames = endframe - startframe; fft_frames = sample_frames / D; c_fundamental = (float)R / (float)N; inCount = -Nw; c_factor_in = (float)R/((float)D * TWOPI); rt = startframe * 1000.0 / x->sr; rtadv = 1000. * (float)D/(float)R; post("we will analyze %d FFT frames",fft_frames); Wanal = (float *) getbytes(Nw * sizeof(float)); Wsyn = (float *) getbytes(Nw * sizeof(float)); Hwin = (float *) getbytes(Nw * sizeof(float)); input = (float *) getbytes(Nw * sizeof(float) ); output = (float *) getbytes(Nw * sizeof(float) ); buffer = (float *) getbytes(N * sizeof(float)); channel = (float *) getbytes((N+2) * sizeof(float) ); bitshuffle = (int *) getbytes(N * 2 * sizeof(int)); bytesize = N * 2 * sizeof(float); // post("allocing %d bytes to trigland",N * 2 * sizeof(float)); trigland = (float *) getbytes(N * 2 * sizeof(float)); // post("cleaning %d bytes in trigland",N * 2 * sizeof(float)); c_lastphase_in = (float *) getbytes((N2+1) * sizeof(float)); c_lastphase_out = (float *) getbytes((N2+1) * sizeof(float)); input_vec = (float *) getbytes(D * sizeof(float)); onsets = (float *) getbytes(MAX_ONSETS * sizeof(float)); specdiff = (float *) getbytes(fft_frames * sizeof(float)); freqdiff = (float *) getbytes(fft_frames * sizeof(float)); hfc = (float *) getbytes(fft_frames * sizeof(float)); loveboat = (float **) getbytes(fft_frames * sizeof(float *)); freqs = (float *) getbytes(N * sizeof(float)); amps = (float *) getbytes(N * sizeof(float)); for(i = 0; i < fft_frames; i++){ loveboat[i] = (float *) getbytes((N+2) * sizeof(float)); if(loveboat[i] == NULL){ error("memory error"); return; } // memset((char *)loveboat[i],0,(N+2)*sizeof(float)); } memset((char *)trigland,0, N * 2 * sizeof(float)); memset((char *)input,0,Nw * sizeof(float)); memset((char *)output,0,Nw * sizeof(float)); memset((char *)c_lastphase_in,0,(N2+1) * sizeof(float)); memset((char *)c_lastphase_out,0,(N2+1) * sizeof(float)); memset((char *)bitshuffle,0, 2 * N * sizeof(int)); makewindows(Hwin, Wanal, Wsyn, Nw, N, D); init_rdft(N, bitshuffle, trigland); currentframe = startframe; for(k = 0; k < fft_frames; k++){ for(j = 0; j < D; j++){ input_vec[j] = b_samples[(currentframe + j) * b_nchans];// only read first channel } currentframe += D; for ( j = 0 ; j < Nw - D ; j++ ){ input[j] = input[j+D]; } for (i = 0, j = Nw - D; j < Nw; j++, i++) { input[j] = input_vec[i]; } fold(input, Wanal, Nw, buffer, N, inCount); rdft(N, 1, buffer, bitshuffle, trigland); convert(buffer, loveboat[k], N2, c_lastphase_in, c_fundamental, c_factor_in); } topanalbin = 11000.0 / c_fundamental; // post("top bin %d",topanalbin); for(j = 0; j < 5; j++) lastfreqs[j] = 0; for(i = 0, k = 1; i < fft_frames - 1; i++, k++){ /* specdiff[i] = 0.0; freqdiff[i] = 0.0; lastenergy = energy; energy = 0; for(j = 2, m = 0; j < topanalbin * 2; j += 2, m++){ // skip lowest bin specdiff[i] += fabs(loveboat[k][j] - loveboat[i][j]); freqdiff[i] += fabs(loveboat[k][j+1] - loveboat[i][j+1])/loveboat[i][j+1]; hfc[i] += ((float)m/(float)topanalbin) * loveboat[i][j] * loveboat[i][j]; energy += fabs(loveboat[i][j]); } if(lastenergy > 0){ ediff = (energy - lastenergy) / lastenergy; } else { ediff = 0; } if(energy > 0){ rescale = 1. / energy; } freqcenter = 0; */ for(j = 0; j < 5; j++) lastfreqs[j] = freqs[j]; for(j = 2, m = 0; j < topanalbin * 2; j += 2, m++){ // skip lowest bin freqcenter += fabs(loveboat[i][j]) * fabs(loveboat[i][j+1]); amps[m] = fabs(loveboat[i][j]); freqs[m] = loveboat[i][j+1]; } /* swap top freqs to the top - but note there is a problem if some lower partials vacillate over which is the weakest since it will swap places and generate spurious change of frequency info. */ for(j = 0; j < topanalbin-1; j++){ for(m = j + 1 ; m < topanalbin; m++){ if(amps[m]> amps[j]){ tmp = amps[j]; amps[j] = amps[m]; amps[m] = tmp; tmp = freqs[j]; freqs[j] = freqs[m]; freqs[m] = tmp; } } } /* freqcenter *= rescale; sb1 = sb2 = sb3 = sb4 = 0; for(j = 0; j < 32; j += 2){ // skip lowest bin sb1 += fabs(loveboat[k][j] - loveboat[i][j]); } for(j = 32; j < 64; j += 2){ // skip lowest bin sb2 += fabs(loveboat[k][j] - loveboat[i][j]); } for(j = 64; j < 96; j += 2){ // skip lowest bin sb3 += fabs(loveboat[k][j] - loveboat[i][j]); } for(j = 96; j < 128; j += 2, m++){ // skip lowest bin sb4 += fabs(loveboat[k][j] - loveboat[i][j]); } */ /* post("%.2f: %.4f %.4f %.4f b1 %.4f b2 %.4f b3 %.4f b4 %.4f e %.4f edif %.4f", rt,specdiff[i],freqdiff[i],hfc[i],sb1,sb2,sb3,sb4,energy,ediff); post("%.2f: sd %.4f fd %.4f hfc %.4f e %.4f edif %.4f fc %.4f", rt,specdiff[i],freqdiff[i],hfc[i],energy,ediff,freqcenter); post("%.2f: f1 %.2f a1 %.4f f2 %.2f a2 %.4f f3 %.2f a3 %.4f f4 %.2f a4 %.4f f5 %.2f a5 %.4f", rt, freqs[0],amps[0],freqs[1],amps[1],freqs[2],amps[2],freqs[3],amps[3],freqs[4],amps[4]); */ if(first){ first = 0; for(j = 0; j < 5; j++){ lastfreqs[j] = freqs[j]; } } for(j = 0; j < 5; j++){ fave = (lastfreqs[j] + freqs[j]) * 0.5; if(fave > 0) fdiffs[j] = fabs(lastfreqs[j] - freqs[j])/fave; else fdiffs[j] = 0; } dsum = fdiffs[0] + .5 * fdiffs[1] + .25 * fdiffs[2] + .125 * fdiffs[3] + .0625 * fdiffs[4]; if(dsum > threshold && ! suppress){ suppress = 1; suppressrt = 0; onsets[onset_count++] = rt; /* post("%.2f: f1 %.2f d1 %.2f f2 %.2f d2 %.2f f3 %.2f d3 %.2f f4 %.2f d4 %.2f f5 %.2f d5 %.2f, dsum: %f", rt, freqs[0],fdiffs[0], freqs[1],fdiffs[1], freqs[2],fdiffs[2], freqs[3],fdiffs[3], freqs[4],fdiffs[4], dsum); */ } else { suppressrt += rtadv; if(suppressrt > 100){ // 100 ms latency suppress = 0; } } rt += rtadv; } post("%s: %d onsets detected",OBJECT_NAME,onset_count); for(i = 0; i < onset_count; i++){ SETFLOAT(listdata + i, onsets[i]); } outlet_list(x->list, 0, onset_count, listdata); outlet_bang(x->bang); // free memory at end of routine } void buffet_detect_subband_onsets(t_buffet *x, t_symbol *msg, short argc, t_atom *argv) { float *b_samples; long b_nchans; long b_frames; // t_atom *listdata = x->listdata; long startframe; long currentframe; long endframe; long sample_frames; long fft_frames; int R = x->sr; int N = 1024; int N2 = N / 2; int Nw = N; int Nw2 = Nw / 2; int D = N / 2; int i,j,k,l; int minbin,maxbin; // float sb1,sb2,sb3,sb4,energy=0,lastenergy,ediff; // float rescale,freqcenter=0; // float tmp; // float fave; // freq average // short first = 1; // float dsum; // short suppress = 0; // float suppressrt; float rt, rtadv; int inCount; float *Wanal; float *Wsyn; float *input; float *Hwin; float *buffer; float *channel; float *output; float *input_vec; float *specdiff; float *freqdiff; float *hfc; float *freqs, *amps; // float lastfreqs[5]; // float fdiffs[5]; float subband_energy[5]; float subband_freqdiff[5]; int subband_bincuts[6]; float *onsets; float **loveboat; // float current_frame; // int framecount; // // float frame_increment ; // float fpos; // float last_fpos; // float tadv; // for convert float *c_lastphase_in; float *c_lastphase_out; float c_fundamental; float c_factor_in; // float c_factor_out; // faster fft // float mult; float *trigland; int *bitshuffle; // int read_me; // int frames_read; // int topanalbin; float threshold; // int bytesize; int MAX_ONSETS = 2048; // int onset_count = 0; buffet_setbuf(x, x->wavename); if( x->hosed ){ error("buffet~ needs a valid buffer"); return; } if(argc < 4){ post("%s: detect_onsets requires start and end times,threshold and FFTsize",OBJECT_NAME); return; } b_samples = x->wavebuf->b_samples; b_nchans = x->wavebuf->b_nchans; b_frames = x->wavebuf->b_frames; startframe = .001 * x->sr * atom_getfloatarg(0,argc,argv); endframe = .001 * x->sr * atom_getfloatarg(1,argc,argv); threshold = atom_getfloatarg(2,argc,argv); N = atom_getfloatarg(3,argc,argv); N2 = N / 2; Nw = N; Nw2 = Nw / 2; D = N / 2; if(!endframe) endframe = b_frames; sample_frames = endframe - startframe; fft_frames = sample_frames / D; c_fundamental = (float)R / (float)N; inCount = -Nw; c_factor_in = (float)R/((float)D * TWOPI); rt = startframe * 1000.0 / x->sr; rtadv = 1000. * (float)D/(float)R; post("we will analyze %d FFT frames",fft_frames); subband_bincuts[0] = 1; subband_bincuts[1] = (int) (500.0 / c_fundamental); subband_bincuts[2] = (int) (1000.0 / c_fundamental); subband_bincuts[3] = (int) (2000.0 / c_fundamental); subband_bincuts[4] = (int) (4000.0 / c_fundamental); subband_bincuts[5] = (int) (8000.0 / c_fundamental); Wanal = (float *) getbytes(Nw * sizeof(float)); Wsyn = (float *) getbytes(Nw * sizeof(float)); Hwin = (float *) getbytes(Nw * sizeof(float)); input = (float *) getbytes(Nw * sizeof(float) ); output = (float *) getbytes(Nw * sizeof(float) ); buffer = (float *) getbytes(N * sizeof(float)); channel = (float *) getbytes((N+2) * sizeof(float) ); bitshuffle = (int *) getbytes(N * 2 * sizeof(int)); trigland = (float *) getbytes(N * 2 * sizeof(float)); c_lastphase_in = (float *) getbytes((N2+1) * sizeof(float)); c_lastphase_out = (float *) getbytes((N2+1) * sizeof(float)); input_vec = (float *) getbytes(D * sizeof(float)); onsets = (float *) getbytes(MAX_ONSETS * sizeof(float)); specdiff = (float *) getbytes(fft_frames * sizeof(float)); freqdiff = (float *) getbytes(fft_frames * sizeof(float)); hfc = (float *) getbytes(fft_frames * sizeof(float)); loveboat = (float **) getbytes(fft_frames * sizeof(float *)); freqs = (float *) getbytes(N * sizeof(float)); amps = (float *) getbytes(N * sizeof(float)); for(i = 0; i < fft_frames; i++){ loveboat[i] = (float *) getbytes((N+2) * sizeof(float)); if(loveboat[i] == NULL){ error("memory error"); return; } // memset((char *)loveboat[i],0,(N+2)*sizeof(float)); } memset((char *)trigland,0, N * 2 * sizeof(float)); memset((char *)input,0,Nw * sizeof(float)); memset((char *)output,0,Nw * sizeof(float)); memset((char *)c_lastphase_in,0,(N2+1) * sizeof(float)); memset((char *)c_lastphase_out,0,(N2+1) * sizeof(float)); memset((char *)bitshuffle,0, 2 * N * sizeof(int)); makewindows(Hwin, Wanal, Wsyn, Nw, N, D); init_rdft(N, bitshuffle, trigland); currentframe = startframe; for(k= 0; k < 5; k++){ minbin = subband_bincuts[k]; maxbin = subband_bincuts[k+1]; post("%d: minbin %d maxbin %d",k, minbin,maxbin); } for(k = 0; k < fft_frames; k++){ for(j = 0; j < D; j++){ input_vec[j] = b_samples[(currentframe + j) * b_nchans];// only read first channel } currentframe += D; for ( j = 0 ; j < Nw - D ; j++ ){ input[j] = input[j+D]; } for (i = 0, j = Nw - D; j < Nw; j++, i++) { input[j] = input_vec[i]; } fold(input, Wanal, Nw, buffer, N, inCount); rdft(N, 1, buffer, bitshuffle, trigland); convert(buffer, loveboat[k], N2, c_lastphase_in, c_fundamental, c_factor_in); } rt = rtadv; for(i = 0, j = 1; i < fft_frames - 1; i++, j++){ for(k= 0; k < 5; k++){ minbin = subband_bincuts[k]; maxbin = subband_bincuts[k+1]; subband_energy[k] = 0; subband_freqdiff[k] = 0; /* maxamp = 0; for(l = minbin * 2; l < maxbin * 2; l += 2){ } */ for(l = minbin * 2; l < maxbin * 2; l += 2){ // subband_energy[k] += (loveboat[j][l] * loveboat[j][l]) - (loveboat[i][l] * loveboat[i][l]); subband_freqdiff[k] += (fabs(loveboat[j][l+1] - loveboat[i][l+1])/loveboat[i][l+1]) * loveboat[j][l] * loveboat[j][l] ;// scale by energy } } /* post("rt: %.2f sb1 %f sb2 %f sb3 %f sb4 %f sb5 %f", rt, subband_energy[0],subband_energy[1],subband_energy[2],subband_energy[3],subband_energy[4] ); */ post("rt:%f fb1 %f fb2 %f fb3 %f fb4 %f fb5 %f", rt, subband_freqdiff[0],subband_freqdiff[1],subband_freqdiff[2],subband_freqdiff[3],subband_freqdiff[4] ); /* post("%.2f: f1 %.2f d1 %.2f f2 %.2f d2 %.2f f3 %.2f d3 %.2f f4 %.2f d4 %.2f f5 %.2f d5 %.2f, dsum: %f", rt, freqs[0],fdiffs[0], freqs[1],fdiffs[1], freqs[2],fdiffs[2], freqs[3],fdiffs[3], freqs[4],fdiffs[4], dsum); */ rt += rtadv; } /* post("%s: %d onsets detected",OBJECT_NAME,onset_count); for(i = 0; i < onset_count; i++){ SETFLOAT(listdata + i, onsets[i]); } outlet_list(x->list, 0, onset_count, listdata); outlet_bang(x->bang); */ // free memory at end of routine } /* should really be using malloc/free instead of annoying MSPd routines */ void buffet_dsp_free(t_buffet *x) { #if __MSP__ dsp_free((t_pxobject *)x); #endif freebytes(x->storage,0); freebytes(x->listdata,0); freebytes(x->rmsbuf,0); freebytes(x->analbuf,0); freebytes(x->onset,0); freebytes(x->wavebuf,0); freebytes(x->destbuf,0); } void buffet_dsp(t_buffet *x, t_signal **sp, short *count) { buffet_setbuf(x, x->wavename); if( x->hosed ){ error("buffet~ needs a valid buffer"); return; } if( x->sr != sp[0]->s_sr){ x->sr = sp[0]->s_sr; if(!x->sr){ post("%s: warning: zero sampling rate!",OBJECT_NAME); x->sr = 44100; } buffet_init(x,1); } dsp_add(buffet_perform, 3, x, sp[0]->s_vec, sp[0]->s_n); } #if __MSP__ void buffet_assist (t_buffet *x, void *b, long msg, long arg, char *dst) { if (msg==1) { switch (arg) { case 0: sprintf(dst,"(mesages) Groove Sync Signal Input"); break; } } else if (msg==2) { switch (arg){ case 0: sprintf(dst,"(bang) Operation Completed"); break; case 1: sprintf(dst,"(list) Buffer Event Times"); break; case 2: sprintf(dst,"(float) Buffer Segment RMS Value"); break; } } } #endif /*** MSP helper functions **/ #if __PD__ void atom_arg_getfloat(float *c, long idx, long ac, t_atom *av) { if (c&&ac&&av&&(idx