#include "m_pd.h" #include "ext13.h" #include #include #include #ifndef _WIN32 #include #include #include #include #else #include #endif /* ------------------------ scramble_tilde~ ----------------------------- */ static t_class *scramble_tilde_class; typedef struct _scramble_grain { t_float* L; t_float* R; int size; t_float maximum; struct _scramble_grain *next; }t_scramble_grain; typedef struct _scramble_tilde { t_object x_obj; int x_n; t_int x_channels; t_float play, analize; t_float dir, current_dir, pitch, actual_pitch, grain_r, autopitch; t_float valsum, valavg; int valsumcount, valsummax; int autofollow, playmode, semitones; t_scramble_grain *firstgrain; t_scramble_grain *workgrain; t_scramble_grain *ring; int grains, newgrains, w_grain, r_grain, n_grain, gotagrain, flush; int r_d, w_d, dist, mindist, lowptr, hiptr, lastlowptr; t_float lowval, hival, prevval, lowborder, normalize; int nsamples; t_outlet *trigger1; t_outlet *trigger2; } t_scramble_tilde; /* grain functions*/ static t_scramble_grain* scramble_tilde_newgrain(){ t_scramble_grain* thegrain; thegrain = getbytes( sizeof(t_scramble_grain)); thegrain->L = NULL; thegrain->R = NULL; thegrain->size = 0; thegrain->next = NULL; return (thegrain); } static t_scramble_grain* scramble_tilde_getgrain(t_scramble_grain* firstgrain, int n){ t_scramble_grain* thegrain = firstgrain; while (n--){ if (thegrain->next){ thegrain = thegrain->next; }else return (NULL); } return (thegrain); } static int scramble_tilde_getsomegrain(t_scramble_grain* firstgrain,int g){ t_scramble_grain* thegrain ; int r; do{ r = rand() % g; thegrain = scramble_tilde_getgrain(firstgrain, r); }while (thegrain->size == 0); return (r); } static void scramble_tilde_grainbuf(t_scramble_grain* grain, int c, int n){ if (!grain->size) grain->L = getbytes(n * sizeof(t_float)); else grain->L = resizebytes(grain->L, grain->size * sizeof(t_float), n * sizeof(t_float)); if (c == 2){ if (!grain->size) grain->R = getbytes(n * sizeof(t_float)); else grain->R = resizebytes(grain->R, grain->size * sizeof(t_float), n * sizeof(t_float)); } grain->size = n; } static void scramble_tilde_freegrain(t_scramble_grain* grain, int c){ if (grain->size){ freebytes(grain->L, grain->size * sizeof(t_float)); if (c == 2) freebytes(grain->R, grain->size * sizeof(t_float)); grain->size = 0; grain->next = NULL; } } t_int *scramble_tilde_perform(t_int *w) { t_scramble_tilde* x = (t_scramble_tilde*)(w[1]); int i; int erg=0; int n; t_float val, valL, valR, killval; #ifndef _WIN32 t_float* out[x->x_channels]; t_float* in[x->x_channels]; #else t_float** out = (t_float**)malloc(x->x_channels*sizeof(t_float*)); t_float** in = (t_float**)malloc(x->x_channels*sizeof(t_float*)); #endif float n_factor, frac, a, b, c, d, cminusb; int index; float *fp; t_atom at[2]; /* anything changed?*/ if (x->flush){ int i = x->grains; x->flush = 0; x->gotagrain = 0; while (i--) scramble_tilde_grainbuf(scramble_tilde_getgrain(x->firstgrain,i),x->x_channels,0); } if (x->newgrains){ int tmp = x->grains; if (x->newgrains > x->grains){ x->workgrain = scramble_tilde_getgrain(x->firstgrain,x->grains - 1); /*-1 ???*/ tmp = x->newgrains; x->newgrains -= x->grains; x->grains = tmp; while (x->newgrains--){ x->workgrain->next = scramble_tilde_newgrain(); x->workgrain = x->workgrain->next; } // post ("now %d grains",x->grains); }else{ if (x->newgrains < x->grains){ t_scramble_grain* tmpgrain; x->grains = x->newgrains; x->workgrain = scramble_tilde_getgrain(x->firstgrain,x->grains - 1); /* delete old grains */ while (x->workgrain->next){ tmpgrain = x->workgrain->next; scramble_tilde_freegrain(x->workgrain,x->x_channels); x->workgrain = tmpgrain; } /* reset readpointer if needed*/ if (x->r_grain >= x->grains){ x->r_grain = 0; x->grain_r = -1; } // post ("now %d grains",x->grains); } } x->newgrains=0; } if ((x->ring->size > x->x_n) || (x->ring->size < x->x_n) ){ // post ("scramble~: new size for ringbuffer:%d samples, %d channels, oldsize:%d",x->x_n,x->x_channels,x->ring->size); scramble_tilde_grainbuf(x->ring, x->x_channels ,x->x_n); x->x_n = x->ring->size; x->dist = 0; x->lowptr = x->r_d; x->lastlowptr = -1; x->lowval = x->lowborder; } for (i = 0; i < x->x_channels ;i++) in[i] = (t_float *)(w[2 + i]); for (i = 0;i < x->x_channels ;i++) out[i] = (t_float *)(w[2 + x->x_channels + i]); n = (int)(w[2 + x->x_channels * 2]);/*number of samples*/ // post ("n:%d",n); while (n--){ /*read from input*/ if (++x->r_d > x->x_n){ x->r_d = 0; } valL = *(t_float*)(x->ring->L + x->r_d) = *in[0]++; if (valL < 0) valL *= -1; if (x->x_channels == 2){ valR = *(t_float*)(x->ring->R + x->r_d) = *in[1]++; if (valR < 0) valR *= -1; val = valL + valR / 2.0; if (valL > x->hival){ x->hiptr = x->r_d; x->hival = valL; } if (valR > x->hival){ x->hiptr = x->r_d; x->hival = valR; } }else { val = valL; if (valL > x->hival){ x->hiptr = x->r_d; x->hival = valL; } } // if (val < 0) val *= -1; x->valsum += val; // if (x->valsumcount++ > x->mindist * 10){ if (x->valsumcount++ && (x->r_d == 0)){ x->valavg = x->valsum / x->valsumcount; x->valsumcount = 0; x->valsum = 0; if (x->autofollow && ( x->valavg > 0.003)) { x->lowborder = x->valavg; // post ("lowborder = %f",x->lowborder); } } if ((val < x->lowborder) && (x->prevval > x->lowborder)){ /* a new low-period */ x->dist = -1; x->lowptr = x->r_d; x->lowval = val; // post ("low"); } if ((x->r_d + 1) == x->lastlowptr){ /* one round without a point to cut */ x->lastlowptr = -1; x->lowval = x->lowborder; x->hival = 0; // post ("lastlowptr: reset"); } if (val < x->lowborder){x->dist++;} if (val <= x->lowval) { x->lowptr = x->r_d; x->lowval = val; /*found a point to cut*/ } if ((val > x->lowborder) && (x->prevval < x->lowborder) && ( x->dist < x->mindist)){ /*too short low-period*/ x->dist = 0; x->lowptr = x->r_d; x->lowval = x->lowborder; // post ("low too short"); } if ((val > x->lowborder) && (x->prevval < x->lowborder) && ( x->dist > x->mindist)){ /*found a low-period to cut */ if ((x->lastlowptr != -1) ){ int grainlen = 0; int i = 0; int wp = 1; /*first and last sample of grain should be 0.0*/ x->gotagrain = 1; /* how long is the new grain */ if (x->lastlowptr > x->lowptr){ grainlen = x->x_n - x->lastlowptr + x->lowptr; }else{ grainlen = x->lowptr - x->lastlowptr; } if (x->analize){ /*find and prepare the grain*/ if (++x->w_grain >= x->grains ) x->w_grain = 0; x->workgrain = scramble_tilde_getgrain (x->firstgrain, x->w_grain); scramble_tilde_grainbuf(x->workgrain, x->x_channels, grainlen + 2); *(t_float*)(x->workgrain->L) = 0.0; *(t_float*)(x->workgrain->L + x->workgrain->size -1) = 0.0; if (x->x_channels == 2){ *(t_float*)(x->workgrain->R) = 0.0; *(t_float*)(x->workgrain->R + x->workgrain->size -1) = 0.0; } x->workgrain->maximum = x->hival; /*notify the world*/ SETFLOAT(at, grainlen); SETFLOAT(at+1, x->w_grain + 1); outlet_list(x->trigger1, 0, 2, at); /*copy to the grain*/ i = x->lastlowptr; while (grainlen--){ if (++i >= x->x_n) i = 0; *(t_float*)(x->workgrain->L + wp ) = *(t_float*)(x->ring->L + i); if (x->x_channels == 2) *(t_float*)(x->workgrain->R + wp ) = *(t_float*)(x->ring->R + i); wp++; } }/*end if analize*/ // post ("copied: w_grain: %d",x->w_grain); }/* end lastlowptr != -1*/ x->dist = 0; x->hival = 0; x->lastlowptr = x->lowptr; }/*end found a low-period to cut */ x->prevval = val; }/*end while n-- (read from input)*/ /*--------------------playback--------------*/ n = (int)(w[2 + x->x_channels * 2]);/*number of samples*/ x->workgrain = scramble_tilde_getgrain (x->firstgrain, x->r_grain); if (x->normalize && x->workgrain) n_factor = x->normalize / x->workgrain->maximum; else n_factor = 1; while (n--){ int wgs; if (x->workgrain) wgs = x->workgrain->size - 2; else wgs = 0; if (( (x->grain_r >= wgs) || (x->grain_r < 1) || (x->workgrain == NULL) ) && x->play && x->gotagrain){ if (x->playmode < 2){ x->r_grain = scramble_tilde_getsomegrain(x->firstgrain, x->grains); x->workgrain = scramble_tilde_getgrain (x->firstgrain, x->r_grain); }else{ if (x->n_grain == -1){ x->play = 0; x->r_grain = 0; x->workgrain = NULL; }else{ x->r_grain = x->n_grain; x->workgrain = scramble_tilde_getgrain (x->firstgrain, x->r_grain); x->n_grain = -1; if ((x->r_grain == x->w_grain) || (x->workgrain == NULL)){ x->play = 0; x->r_grain = 0; x->workgrain = NULL; } else if (!x->workgrain->size){ x->play = 0; x->r_grain = 0; x->workgrain = NULL; } } }/*end if playmode < 2*/ if (x->workgrain){ if (((rand() % 200) / 100.0 - 1.0 ) < x->dir){ x->current_dir = 1; x->grain_r = 1; } else{ x->current_dir = -1; x->grain_r = x->workgrain->size -3; } if ( ( (x->autopitch >= 1.) && (x->semitones) ) || ( (x->autopitch) && (! x->semitones) ) ){ if (x->semitones){ int ap = (int)x->autopitch; int rauf = 0; int count ; if (rand() % 2 == 1){ rauf = 1;} /* post ("rauf:%d",rauf); */ x->actual_pitch = x->pitch; for (count = (rand() % ap); count >= 0; count--){ /*1.05946 = 12te wurzel aus 2 */ if (rauf){ x->actual_pitch = x->actual_pitch * 1.05946; }else{ x->actual_pitch = x->actual_pitch / 1.05946; } } }else{ if (((rand() % 200) / 100.0 - 1.0 ) > 0){ x->actual_pitch = x->pitch + x->pitch * ((rand() % 100 ) / 100.0 * x->autopitch); }else{ x->actual_pitch = x->pitch - x->pitch / ((rand() % 100 ) / 100.0 * x->autopitch); } }/*end if semitones*/ } else { x->actual_pitch = x->pitch; }/* end if autopitch*/ /* post ("x->actual_pitch:%f, x->autopitch:%f",x->actual_pitch,x->autopitch); */ if (x->normalize) n_factor = x->normalize / x->workgrain->maximum; else n_factor = 1; SETFLOAT(at, (x->workgrain->size - 2) / x->actual_pitch); SETFLOAT(at+1, x->r_grain + 1); outlet_list(x->trigger2, 0, 2, at); }/*end if workgrain !=NULL */ }/* end finding a new grain*/ if (x->play && x->gotagrain){ /*write graincontent to output*/ /* 4 point interpolation taken from ../src/d_array.c tabread4~ */ index = x->grain_r; if (index < 1) index = 1, frac = 0; else if (index > x->workgrain->size - 3) index = x->workgrain->size - 3, frac = 1; else frac = x->grain_r - index; fp = (t_float*)(x->workgrain->L + index); a = fp[-1]; b = fp[0]; c = fp[1]; d = fp[2]; cminusb = c-b; *out[0]++ = (b + frac * ( cminusb - 0.5f * (frac-1.) * ( (a - d + 3.0f * cminusb) * frac + (b - a - cminusb) ) )) * n_factor; if (x->x_channels == 2){ fp = (t_float*)(x->workgrain->R + index); a = fp[-1]; b = fp[0]; c = fp[1]; d = fp[2]; cminusb = c-b; *out[1]++ = (b + frac * ( cminusb - 0.5f * (frac-1.) * ( (a - d + 3.0f * cminusb) * frac + (b - a - cminusb) ) )) * n_factor; } x->grain_r += x->current_dir * x->actual_pitch; }else/* if play*/{ *out[0]++ = 0; if (x->x_channels == 2) *out[1]++ = 0; }/*end if play */ }/*end while n-- */ #ifdef _WIN32 free(in); free(out); #endif return (w + x->x_channels * 2 + 3); } static void scramble_tilde_dsp(t_scramble_tilde *x, t_signal **sp) { switch (x->x_channels) { case 1: dsp_add(scramble_tilde_perform, 4, x, sp[0]->s_vec, sp[1]->s_vec, sp[0]->s_n); // post ("1 channel"); break; case 2: dsp_add(scramble_tilde_perform, 6, x, sp[0]->s_vec, sp[1]->s_vec,sp[2]->s_vec, sp[3]->s_vec, sp[0]->s_n); // post ("2 channels"); break; } } static void scramble_tilde_free(t_scramble_tilde *x){ int n = x->grains - 1; while (n--){ scramble_tilde_freegrain (scramble_tilde_getgrain(x->firstgrain,n),x->x_channels); scramble_tilde_freegrain (x->ring,x->x_channels); } } static void *scramble_tilde_new(t_floatarg c,t_floatarg b) { t_scramble_tilde *x = (t_scramble_tilde *)pd_new(scramble_tilde_class); int i; // x->bufL = NULL; // x->bufR = NULL; x->x_channels = (t_int)c; if (x->x_channels > 2) { x->x_channels = 2; post ("maximum: 2 channels"); } if (x->x_channels < 1) x->x_channels = 1; outlet_new(&x->x_obj, gensym("signal")); if (x->x_channels == 2){ inlet_new(&x->x_obj, &x->x_obj.ob_pd, &s_signal, &s_signal); outlet_new(&x->x_obj, gensym("signal")); } x->trigger1 = outlet_new(&x->x_obj, &s_float); x->trigger2 = outlet_new(&x->x_obj, &s_float); x->dir = 1; x->pitch = 1; x->actual_pitch = 1; x->autopitch = 0; x->semitones = 1; x->autofollow = 1; x->playmode = 1; x->normalize = 0; x->analize = 1; x->flush = 0; x->x_n = (int)b; if (x->x_n >882000 ){x->x_n = 882000;} if (x->x_n < 88200 ){x->x_n = 88200;} /* x->rR = x->rL = x->wR = x->wL = NULL;*/ x->lowptr = 0; x->lastlowptr = x->r_d = x->grain_r = -1; x->mindist = 1024; x->lowborder = 0.35; // scramble_tilde_tempbuf(x,x->x_n); x->ring = scramble_tilde_newgrain(); scramble_tilde_grainbuf(x->ring,x->x_channels,x->x_n); x->valsum = x->valavg = x->valsumcount = 0; x->valsummax = 1024; /* the grains:*/ x->grains = 50; x->r_grain = 0; x->w_grain = x->n_grain = -1; x->firstgrain = x->workgrain = scramble_tilde_newgrain(); for (i = 1;i < x->grains;i++){ x->workgrain->next = scramble_tilde_newgrain(); x->workgrain = x->workgrain->next; } return (x); } void scramble_tilde_float(t_scramble_tilde* x, t_float n){ x->play = n; if (x->playmode == 2) { x->n_grain = (int)n - 1; x->grain_r = -1; } } void scramble_tilde_buffer(t_scramble_tilde* x, t_float n){ if (n > 64) x->x_n = (int)n; // post ("buffersize now:%d",x->x_n); } void scramble_tilde_threshold(t_scramble_tilde* x, t_float t){ if (t >0) { x->lowborder = t; x->autofollow = 0; }else{ post ("threshold must be a positive value (0.1 - 0.8 makes sense)"); } } void scramble_tilde_grains(t_scramble_tilde* x, t_float g){ if ((g > 1) && (g < 2048) ) x->newgrains = (int)g; else post ("scramble~: minimum # of grains must be 2 an maximum # is 2048"); } void scramble_tilde_mindist(t_scramble_tilde* x, t_float t){ if ((t > 0) && (t < x->x_n)) x->mindist = (int)t; else post ("scramble~: minimum distance must be positive value lower than buffersize"); } void scramble_tilde_direction(t_scramble_tilde* x, t_float d){ if (d > 1) d = 1; if (d < -1) d = -1; x->dir = d; } void scramble_tilde_autofollow(t_scramble_tilde* x){ x->autofollow = 1; } void scramble_tilde_pitch(t_scramble_tilde* x, t_float p){ if (p > 0) x->pitch = p; else post ("scramble~: pitch must be > 0"); } void scramble_tilde_autopitch(t_scramble_tilde* x, t_float p){ x->autopitch = p; } void scramble_tilde_semitones(t_scramble_tilde* x, t_float p){ x->semitones = (int)p; } void scramble_tilde_normalize(t_scramble_tilde* x, t_float n){ x->normalize = n; } void scramble_tilde_analize(t_scramble_tilde* x, t_float f){ x->analize = f; } void scramble_tilde_flush(t_scramble_tilde* x){ x->flush = 1; } void scramble_tilde_playmode(t_scramble_tilde* x, t_float p){ x->playmode = (int)p; if (x->playmode < 0) x->playmode = 0; if (x->playmode < 1) x->playmode = 2; switch (x->playmode){ case 0: post ("scramble~: playmode off"); break; case 1: post ("scramble~: active playmode"); break; case 2: post ("scramble~: passive playmode"); break; default: post ("scramble~: invalid playmode"); } } void scramble_tilde_setup(void) { scramble_tilde_class = class_new(gensym("scramble~"), (t_newmethod) scramble_tilde_new, 0, sizeof(t_scramble_tilde), 0, A_DEFFLOAT,A_DEFFLOAT, 0); class_addfloat(scramble_tilde_class,scramble_tilde_float); class_addmethod(scramble_tilde_class, nullfn, gensym("signal"), 0); class_addmethod(scramble_tilde_class, (t_method) scramble_tilde_dsp, gensym("dsp"), 0); class_addmethod(scramble_tilde_class, (t_method) scramble_tilde_buffer, gensym("buffer"), A_DEFFLOAT,0); class_addmethod(scramble_tilde_class, (t_method) scramble_tilde_threshold, gensym("threshold"), A_DEFFLOAT,0); class_addmethod(scramble_tilde_class, (t_method) scramble_tilde_grains, gensym("grains"), A_DEFFLOAT,0); class_addmethod(scramble_tilde_class, (t_method) scramble_tilde_mindist, gensym("min_length"), A_DEFFLOAT,0); class_addmethod(scramble_tilde_class, (t_method) scramble_tilde_direction, gensym("direction"), A_DEFFLOAT,0); class_addmethod(scramble_tilde_class, (t_method) scramble_tilde_autofollow, gensym("autofollow"),0); class_addmethod(scramble_tilde_class, (t_method) scramble_tilde_pitch, gensym("pitch"), A_DEFFLOAT,0); class_addmethod(scramble_tilde_class, (t_method) scramble_tilde_autopitch, gensym("autopitch"), A_DEFFLOAT,0); class_addmethod(scramble_tilde_class, (t_method) scramble_tilde_semitones, gensym("semitones"), A_DEFFLOAT,0); class_addmethod(scramble_tilde_class, (t_method) scramble_tilde_flush, gensym("flush"), 0); class_addmethod(scramble_tilde_class, (t_method) scramble_tilde_normalize, gensym("normalize"), A_DEFFLOAT,0); class_addmethod(scramble_tilde_class, (t_method) scramble_tilde_analize, gensym("analize"), A_DEFFLOAT,0); class_addmethod(scramble_tilde_class, (t_method) scramble_tilde_playmode, gensym("playmode"), A_DEFFLOAT,0); }