This is ug.fltdelay.c in view mode; [Download] [Up]
/* Interpolating Delay with Filter in Feedback Loop Unit Generator */ /* This unit generator implements the Karplus-Strong plucked-string algorithm as improved by David Jaffe and Julius Smith (see CMJ Vol. 7 No. 2, pp. 56-69). */ #include "mm.head.h" #include "ug.head.h" #include <stdio.h> #define IN 16 /* (bvpn) optional input block (for use as resonator)*/ #define PITCH 17 /* (bvpn) desired fundamental freq (use Hz postop !!)*/ #define DECAY 18 /* (bvpn) duration factor (0 to 1: 1 = full duration)*/ #define TABLE 19 /* (fvpn) function to initialize table (eg. from gen6)*/ #define LEVEL 20 /* (vpn) amplitude of pluck (0 to 1: 1 = loudest)*/ #define FINAL 21 /* (vpn) number of db down at p4 (0 to 90: 40 = norm)*/ #define ONSET 22 /* (vpn) attack time for pluck(0 to .1 sec: 0 = fast)*/ #define PLACE 23 /* (vpn) pluck point on string (0 to .5: 0 = normal)*/ #define FILTR 24 /* (vpn) lowpass prefilter cutoff freq(0 to .5 Srate)*/ #define NOISE 25 /* (vpn) time of initial noise burst (-1 to +0.1 sec)*/ #define STIFF 26 /* (vpn) stiffness (0 to 10: 10 = stiffest/sharpest)*/ #define DBUF 1 /* (vpnd) pointer to buffer */ #define DLEN 2 /* (vpnd) length of buffer (= funclength) */ #define NOW 3 /* (vpnd) offset of current sample in buffer */ #define DEL 4 /* (vpnd) integral sample delay */ #define COEF 5 /* (vpnd) coeficient for allpass filter */ #define DURCNT 6 /* (vpnd) counts down (p4 * Srate) samples */ #define NOICNT 7 /* (vpnd) counts down (NOISE * Srate) samples */ #define BEGCNT 8 /* (vpnd) counts down (ONSET * Srate) samples */ #define SHRINK 9 /* (vpnd) adjusted duration factor */ #define STRTCH 10 /* (vpnd) coeficient in averaging filter */ #define SDELAY 11 /* (vpnd) fractional delay of averaging filter */ #define OLDPIT 12 /* (vpnd) old pitch (used to detect change) */ #define OLDVAL 13 /* (vpnd) most recent output */ #define OLDOUT 14 /* (vpnd) delayed output from allpass filter */ #define OLDIN 15 /* (vpnd) delayed input to allpass filter */ fltdelay UGHEAD{ UGINIT; float *dbuf; float shrink, strtch, coef, Sdelay, Cdelay, rawdelay, avg, peak; float cycles, pitch, Pi2, stiff, temp, out, in, oldin, oldout, decay; float attenuate, frac, hzfac; double p4, alpha, beta, factor, cfactor, numer, denom; long dlen, now, durcnt, noicnt, begcnt, shutoff, offset, shift, delay; if(STARTNOTE){ if ( TYPE(TABLE) != 'f' ) { FORLIST(fp, next_fp, fplist) if(fp->f_index == (int) VAL(TABLE)) break; if(!fp){ Error++; PROUT("FLTDELAY: Function %.0f undefined\n",VAL(TABLE)); SEMIC; return; } LOC(TABLE) = fp->f_vals; LEN(TABLE) = fp->f_len; TYPE(TABLE) = 'f'; } p4 = *(note->plist+4); durcnt = LVAL(DURCNT) = p4 * Srate; noicnt = LVAL(NOICNT) = VAL(NOISE) * Srate; begcnt = LVAL(BEGCNT) = VAL(ONSET) * Srate; dlen = LVAL(DLEN) = LEN(TABLE) ; hzfac = (float) Srate / dlen; now = LVAL(NOW) = dlen - 1; FPTR(DBUF) = (float *) calloc(dlen, sizeof(float)); dbuf = FPTR(DBUF); /* load dbuf with values from function table */ for(i=0; i<dlen; i++) *(dbuf + i) = *( LOC(TABLE) + i ) ; /* calculate strtch to get specified decay in p4 sec for pitch (shrink = 1) */ shrink = 1.; pitch = VAL(OLDPIT) = hzfac * VAL(PITCH) ; cycles = p4 * pitch; temp = -.05 * VAL(FINAL) ; alpha = pow( 10., (temp / cycles) ); beta = 2. * alpha * alpha; Pi2 = 8. * atan(1.); factor = Pi2 * pitch / Srate; cfactor = cos( (double) factor); numer = beta - 1. - cfactor; /* if (numer < 0.) then alpha is too small to achieve by strtch alone, */ /* and shrink must be used as well */ if (numer < 0.) { shrink = sqrt(beta / (1. + cfactor)); numer = 0.; } denom = 4. - 4. * cfactor; strtch = VAL(STRTCH) = .5 - sqrt( numer / denom ); VAL(SHRINK) = shrink; /* calculate fractional sample delay due to averaging filter */ Sdelay = strtch * sin( (double) factor); Sdelay /= (factor * ( (1. - strtch) + strtch * cfactor ) ); VAL(SDELAY) = Sdelay; /* calculate integral delay */ rawdelay = Srate / pitch; delay = (float) rawdelay - Sdelay; /* calculate fractional delay required in allpass filter; this value */ /* is between 0 and 1 for normal stiffness */ Cdelay = rawdelay - Sdelay - delay; stiff = shift = VAL(STIFF) ; if (stiff > 0.) { Cdelay += stiff; delay -= stiff; } if(delay > dlen ){ fprintf(stderr,"\nCMUSIC: fltdelay, delay = %d > %d\n",delay,dlen); exit(-1) ; } if(delay <= 0){ fprintf(stderr,"\nCMUSIC: fltdelay, delay = %d < 1\n",delay); exit(-1); } LVAL(DEL) = delay; /* calculate coef for allpass filter; use approximation if possible */ if (rawdelay > 8.) coef = VAL(COEF) = (1. - Cdelay) / (1. + Cdelay); else{ numer = sin(.5 * factor * (1. - Cdelay)); denom = sin(.5 * factor * (1. + Cdelay)); coef = VAL(COEF) = numer / denom; } /* prefilter with one-pole lowpass filter with cutoff freq VAL(FILTR) */ alpha = .5; factor = Pi2 * VAL(FILTR); if ( VAL(FILTR) < .25 ){ beta = alpha * alpha; numer = 1. - beta * cos(factor); cfactor = cos(.5 * factor); temp = sqrt(1. - beta * cfactor * cfactor); temp *= 2. * alpha * sin(.5 * factor); coef = numer - temp; if (coef <= 0.) coef = numer + temp; coef /= (1. - beta); if (coef > 1.) coef = 1.; oldout = 0.; for (i=0; i<delay; i++){ *(dbuf+i) = (1. - coef) * *(dbuf+i) - coef * oldout; oldout = *(dbuf+i); } } /* comb filter 0<i<delay samples of dbuf to simulate plucking at VAL(PLACE); */ /* e.g., plucking at .5 eliminates all even harmonics */ shift = ((float) VAL(PLACE) * delay); if (shift > 0) { offset = delay - 1 - shift; for (i=0; i<delay; i++){ if (++offset >= delay) offset = 0; *(dbuf + i) -= *(dbuf + offset); } } /* run filter for -VAL(NOICNT) samples before starting */ VAL(OLDVAL) = *dbuf; VAL(OLDIN) = 0.; VAL(OLDOUT) = 0.; while (noicnt < 0){ /* averaging filter */ offset = now + delay; if (offset >= dlen) offset -= dlen; in = *(dbuf + offset); oldin = VAL(OLDVAL) ; out = (1. - strtch) * in + strtch * oldin; out *= shrink; VAL(OLDVAL) = in; /* allpass filter */ in = out; oldout = VAL(OLDOUT) ; oldin = VAL(OLDIN) ; if (coef == 1.) out = in; else out = coef * (in - oldout) + oldin; VAL(OLDOUT) = out; VAL(OLDIN) = in; /* input to delay line */ *(dbuf + now) = out; if (--now < 0) now = dlen - 1; if (++noicnt >= 0) now = dlen - 1; } /* preprocess 0<i<delay samples of dbuf to eliminate DC and normalize */ avg = peak = 0.; for(i=0; i<delay; i++) avg += *(dbuf + i); avg /= delay; for(i=0; i<delay; i++){ temp = *(dbuf + i) -= avg; if (temp < 0.) temp = - temp; if (temp > peak) peak = temp; } if (peak != 0.) peak = .49 * VAL(LEVEL) / peak; for(i=0; i<delay; i++) *(dbuf + i) *= peak; /* preprocess delay<i<dlen remaining samples to attain same peak; */ /* this only matters if VAL(NOISE) is positive */ peak = 0.; for(i=delay; i<dlen; i++){ temp = *(dbuf + i); if (temp < 0.) temp = - temp; if (temp > peak) peak = temp; } if (peak != 0.) peak = .49 * VAL(LEVEL) / peak; for(i=delay; i<dlen; i++) *(dbuf + i) *= peak; VAL(OLDVAL) = *dbuf; VAL(OLDIN) = 0.; VAL(OLDOUT) = 0.; } dbuf = FPTR(DBUF) ; durcnt = LVAL(DURCNT) ; noicnt = LVAL(NOICNT) ; begcnt = LVAL(BEGCNT) ; dlen = LVAL(DLEN) ; now = LVAL(NOW) ; strtch = VAL(STRTCH) ; shrink = VAL(SHRINK) ; stiff = VAL(STIFF) ; Sdelay = VAL(SDELAY) ; coef = VAL(COEF) ; delay = LVAL(DEL) ; hzfac = (float) Srate / dlen; shutoff = .02 * Srate; attenuate = .98; if (Srate >= 32768) attenuate = .99; if (begcnt > 0){ temp = VAL(ONSET) * Srate; frac = 1. / temp; } UGLOOP{ /* shutoff is exponential decay in final 20 msec */ if (durcnt-- < shutoff) shrink *= attenuate; decay = VAL(DECAY) ; if (decay == 1.) decay = shrink; else if (shrink < 1.) decay *= shrink; /* don't recalculate delay unless pitch has changed */ pitch = hzfac * VAL(PITCH) ; if ( pitch != VAL(OLDPIT) ){ /* calculate integral delay */ if(pitch <= 0.){ fprintf(stderr,"\nCMUSIC: fltdelay, pitch = %f <= 0.\n",pitch); exit(-1) ; } rawdelay = Srate / pitch; delay = (float) rawdelay - Sdelay; /* calculate fractional delay required in allpass filter; this */ /* value is between 0 and 1 for normal stiffness */ Cdelay = rawdelay - Sdelay - delay; if (stiff > 0.) { Cdelay += stiff; delay -= stiff; } /* if delay has decreased, reset filters */ if (delay == LVAL(DEL) - 1){ /* averaging filter */ offset = now + delay + 1; if (offset >= dlen) offset -= dlen; in = *(dbuf + offset); oldin = VAL(OLDVAL) ; out = (1. - strtch) * in + strtch * oldin; out *= decay; VAL(OLDVAL) = in; /* allpass filter */ in = out; VAL(OLDIN) = in; VAL(OLDOUT) = in; } /* if delay has increased, reset filters */ if (delay == LVAL(DEL) + 1){ offset = now + delay + 1; if (offset >= dlen) offset -= dlen; VAL(OLDVAL) = *(dbuf + offset); VAL(OLDOUT) = VAL(OLDIN) ; } /* calculate coef for allpass filter; use approximation if possible */ if (rawdelay > 8.) coef = VAL(COEF) = (1. - Cdelay) / (1. + Cdelay); else{ factor = 8. * atan(1.) * pitch / Srate; numer = sin(.5 * factor * (1. - Cdelay)); denom = sin(.5 * factor * (1. + Cdelay)); coef = VAL(COEF) = numer / denom; } if(delay > dlen ){ fprintf(stderr,"\nCMUSIC: fltdelay, delay = %d > %d\n",delay,dlen); exit(-1) ; } if(delay <= 0){ fprintf(stderr,"\nCMUSIC: fltdelay, delay = %d < 1\n",delay); exit(-1); } LVAL(DEL) = delay; VAL(OLDPIT) = pitch; } /* while noicnt is positive, output noise directly with no filtering */ if (noicnt > 0){ VAL(OUT) = *(dbuf + now); if (--noicnt <= 0) now = dlen; } /* otherwise, implement recirculating delay line with averaging and allpass */ /* filters in loop; dbuf holds (dlen / delay) most recent cycles*/ else { /* averaging filter */ offset = now + delay; if (offset >= dlen) offset -= dlen; in = *(dbuf + offset); oldin = VAL(OLDVAL) ; out = (1. - strtch) * in + strtch * oldin; out *= decay; VAL(OLDVAL) = in; /* allpass filter */ in = out; oldout = VAL(OLDOUT) ; oldin = VAL(OLDIN) ; if (coef == 1.) out = in; else out = coef * (in - oldout) + oldin; VAL(OLDOUT) = out; VAL(OLDIN) = in; out += VAL(IN) ; /* input to delay line */ *(dbuf + now) = out; /* attack amplitude envelope */ if (begcnt > 0) out *= (1. - frac * (begcnt--)); VAL(OUT) = out; } if (--now < 0) now = dlen - 1; LVAL(NOW) = now; LVAL(DURCNT) = durcnt; LVAL(NOICNT) = noicnt; LVAL(BEGCNT) = begcnt; VAL(SHRINK) = shrink; UGEND(0); } if(ENDNOTE){ free(dbuf); } }
These are the contents of the former NiCE NeXT User Group NeXTSTEP/OpenStep software archive, currently hosted by Netfuture.ch.