From d2eec74a4d8c21aad495ba61539486b24d7ab8dc Mon Sep 17 00:00:00 2001 From: Guenter Geiger Date: Wed, 9 Oct 2002 10:19:04 +0000 Subject: moved from zexy/zexy to zexy svn path=/trunk/externals/zexy/; revision=169 --- src/z_limiter.c | 707 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 707 insertions(+) create mode 100644 src/z_limiter.c (limited to 'src/z_limiter.c') diff --git a/src/z_limiter.c b/src/z_limiter.c new file mode 100644 index 0000000..9f18efe --- /dev/null +++ b/src/z_limiter.c @@ -0,0 +1,707 @@ +/* + --------------------------------- limiter/compressor --------------------------------- + + for details on how it works watch out for "http://iem.kug.ac.at/~zmoelnig/pd" + ...and search for "limiter" + + mail2me4more!n4m8ion : zmoelnig@iem.kug.ac.at +*/ + +/* + this is a limiter/compressor-object + the limiter is based on Falkner's thesis + "Entwicklung eines digitalen Stereo-limiters mit Hilfe des Signalprozessors DSP56001" pp.14 + + 2108:forum::für::umläute:1999 all rights reserved and no warranties... + + see GNU-license for details (shipped with pd) +*/ + +#define LIMIT0 0 +#define LIMIT1 1 +#define COMPRESS 2 + +#include "zexy.h" +#include + +#ifdef NT +#define fabsf fabs +#pragma warning( disable : 4244 ) +#pragma warning( disable : 4305 ) +#endif + +#define LN2 .69314718056 +#define SINC1 .822462987 +#define SINC2 .404460777 +#define SINC3 -.188874003 +#define SINC4 -.143239449 +#define SINC5 .087796546 +#define SINC6 .06917082 +#define SINC7 -.041349667 +#define SINC8 -.030578954 +#define SINC9 .013226276 + +#define BUFSIZE 128 +#define XTRASAMPS 9 +#define TABLESIZE 512 /* compressor table */ + +/* ------------------------------------------------------------------------------------ */ +// first define the structs... + +static t_class *limiter_class; + +typedef struct _limctl +{ // variables changed by user + float limit, hold_samples, change_of_amplification; +} t_limctl; + +typedef struct _cmpctl +{ + float treshold, ratio; // uclimit is the very same is the limiter1-limit (decalculated relative to our treshold) + float uclimit, climit_inverse; // climit == compressed limit (uclimit == uncompressed limit) + + float limiter_limit; // start limiting (stop compressing); == tresh/limit; + + float treshdB, oneminusratio; +} t_cmpctl; + +typedef struct _inbuf +{ + float* ringbuf; + int buf_position; +} t_inbuf; + +typedef struct _limiter +{ + t_object x_obj; + + int number_of_inlets, s_n; + + // variables changed by process + + float amplification; + float samples_left, still_left; + + int mode; + + t_limctl *val1, *val2; + t_cmpctl *cmp; + + // note : limit is not the same for val1 & val2 : + // at val1 it is the limit of the INPUT_VALUE + // at val2 it is the limit for the AMPLIFICATION (in fact it is abs_limit1/abs_limit2) + + t_inbuf* in; + int buf_size; + +} t_limiter; + +/* ------------------------------------------------------------------------------------ */ +// then do the message - thing + +// do the user settings + +// calcs +static t_float calc_holdsamples(t_float htime, int buf) +{ // hold_time must be greater than buffer_time to make sure that any peak_sample is amplified with its own factor + float min_hold = buf / sys_getsr(); + return (0.001 * sys_getsr() * ((htime > min_hold)?htime:((min_hold > 50)?min_hold:50))); +} + +static t_float calc_coa(t_float hlife) +{ + return (exp(LN2 * 1000 / (((hlife > 0)?hlife:15) * sys_getsr()))); +} + +static void set_uclimit(t_limiter *x) +{ + t_cmpctl *c = x->cmp; + t_float limit = x->val1->limit, limitdB = rmstodb(limit), ratio = c->ratio, tresh = c->treshold, treshdB = rmstodb(tresh); + + c->climit_inverse = limit / tresh; + c->uclimit = tresh / dbtorms(treshdB+(limitdB - treshdB)/ratio); + + c->treshdB = treshdB; + c->oneminusratio = 1. - ratio; +} + +// settings + +static void set_treshold(t_limiter *x, float treshold) +{ + t_cmpctl *c = x->cmp; + float tresh = dbtorms (treshold); + if (tresh > x->val1->limit) tresh = x->val1->limit; + + c->treshold = tresh; + + set_uclimit(x); +} + +static void set_ratio(t_limiter *x, float ratio) +{ + if (ratio < 0) ratio = 1; + x->cmp->ratio = ratio; + + set_uclimit(x); +} + +static void set_mode(t_limiter *x, float mode) +{ + int modus = mode; + + switch (modus) { + case LIMIT0: + x->mode = LIMIT0; + break; + case LIMIT1: + x->mode = LIMIT1; + break; + case COMPRESS: + x->mode = COMPRESS; + break; + default: + x->mode = LIMIT0; + break; + } + post("mode set to %d", x->mode); +} + +static void set_LIMIT(t_limiter *x) +{ + set_mode(x, LIMIT0); +} + +static void set_CRACK(t_limiter *x) +{ + set_mode(x, LIMIT1); +} + +static void set_COMPRESS(t_limiter *x) +{ + set_mode(x, COMPRESS); +} + +static void set_bufsize(t_limiter *x, float size) +{ // this is really unneeded...and for historical reasons only + if (size < BUFSIZE) size = BUFSIZE; + x->buf_size = size + XTRASAMPS; +} + +static void set_limit(t_limiter *x, t_floatarg limit) +{ + if (limit < 0.00001) limit = 100; + x->val1->limit = dbtorms(limit); + + if (x->val1->limit < x->cmp->treshold) x->cmp->treshold = x->val1->limit; + set_uclimit(x); +} + +static void set_limits(t_limiter *x, t_floatarg limit1, t_floatarg limit2) +{ + t_float lim1, lim2; + + if (limit1 < 0.00001) limit1 = 100; + + lim1 = dbtorms(limit1); + lim2 = dbtorms(limit2); + + if (lim2 < lim1) + { + lim2 = 2*lim1; // this is to prevent lim2 (which should trigger the FAST regulation) + x->mode = 0; // to underrun the SLOW regulation; this would cause distortion + } + + x->val1->limit = lim1; + x->val2->limit = lim1/lim2; + + if (lim1 < x->cmp->treshold) x->cmp->treshold = lim1; + set_uclimit(x); +} + +static void set1(t_limiter *x, t_floatarg limit, t_floatarg hold, t_floatarg release) +{ + t_float lim = dbtorms(limit); + + x->val1->limit = (lim > 0)?lim:1; + x->val1->hold_samples = calc_holdsamples(hold, x->buf_size); + x->val1->change_of_amplification = calc_coa(release); + + if (lim < x->cmp->treshold) x->cmp->treshold = lim; + set_uclimit(x); +} + + +static void set2(t_limiter *x, t_floatarg limit, t_floatarg hold, t_floatarg release) +{ + t_float lim = dbtorms(limit); + x->val2->limit = (lim > x->val1->limit)?(x->val1->limit/lim):.5; + x->val2->hold_samples = calc_holdsamples(hold, x->buf_size); + x->val2->change_of_amplification = calc_coa(release); +} + + + +static void set_compressor(t_limiter *x, t_floatarg limit, t_floatarg treshold, t_floatarg ratio) +{ + t_cmpctl *c = x->cmp; + t_float lim = dbtorms(limit); + t_float tresh = dbtorms(treshold); + + if ((limit == 0) && (treshold = 0) && (ratio = 0)) {set_mode(x, COMPRESS); return;} + + if (tresh > lim) tresh = lim; + if (ratio < 0.) ratio = 1.; + + c->ratio = ratio; + x->val1->limit = lim; + c->treshold = tresh; + set_uclimit(x); + + set_mode(x, COMPRESS); +} + +static void reset(t_limiter *x) +{ + x->amplification = 1.; +} + +// verbose +static void status(t_limiter *x) +{ + t_limctl *v1 = x->val1; + t_limctl *v2 = x->val2; + t_cmpctl *c = x->cmp; + + t_float sr = sys_getsr() / 1000.; + + switch (x->mode) { + case LIMIT1: + post("%d-channel crack-limiter @ %fkHz\n" + "\noutput-limit\t= %fdB\nhold1\t\t= %fms\nrelease1\t= %fms\ncrack-limit\t= %fdB\nhold2\t\t= %fms\nrelease2\t= %fms\n" + "\namplify\t\t= %fdB\n", + x->number_of_inlets, sr, + rmstodb(v1->limit), (v1->hold_samples) / sr, LN2 / (log(v1->change_of_amplification) * sr), + rmstodb(v1->limit / v2->limit), (v2->hold_samples) / sr, LN2 / (log(v2->change_of_amplification) * sr), + x->amplification); + break; + case LIMIT0: + post("%d-channel limiter @ %fkHz\n" + "\noutput-limit\t= %fdB\nhold\t\t= %fms\nrelease\t\t= %fms\n" + "\namplify\t\t= %fdB\n", + x->number_of_inlets, sr, + rmstodb(v1->limit), (v1->hold_samples) / sr, LN2 / (log(v1->change_of_amplification) * sr), + rmstodb(x->amplification)); + break; + case COMPRESS: + post("%d-channel compressor @ %fkHz\n" + "\noutput-limit\t= %fdB\ntreshold\t= %fdB\ninput-limit\t= %f\nratio\t\t= 1:%f\n" + "\nhold\t\t= %fms\nrelease\t\t= %fms\n" + "\namplify\t\t= %fdB\n", + x->number_of_inlets, sr, + rmstodb(c->treshold * c->climit_inverse), rmstodb(c->treshold), rmstodb(c->treshold / c->uclimit), 1./c->ratio, + (v1->hold_samples) / sr, LN2 / (log(v1->change_of_amplification) * sr), + rmstodb(x->amplification)); + } +} + +static void helper(t_limiter *x) +{ + post("\n\n%c %d-channel limiter-object: mode %d", HEARTSYMBOL, x->number_of_inlets, x->mode); + poststring("\n'mode '\t\t\t: (0_limiter, 1_crack-limiter, 2_compressor)"); + poststring("\n'LIMIT'\t\t\t\t: set to LIMITer"); + poststring("\n'CRACK'\t\t\t\t: set to CRACK-limiter"); + poststring("\n'COMPRESS'\t\t\t\t: set to COMPRESSor"); + + switch (x->mode) { + case LIMIT0: + poststring("\n'limit '\t\t\t: set limit (in dB)" + "\n'set '\t: set limiter"); + break; + case LIMIT1: + poststring("\n'limits '\t: set limits (in dB)" + "\n'set '\t: set limiter 1" + "\n'set2 '\t: set crack-limiter"); + break; + case COMPRESS: + poststring("\n'ratio '\t\t: set compressratio (´0.5´ instead of ´1:2´)" + "\n'treshold '\t\t: set treshold of the compressor" + "\n'compress '\t: set compressor" + "\n..........note that is the same for COMPRESSOR and LIMITER.........."); + break; + default: + break; + } + poststring("\n'print'\t\t\t\t: view actual settings" + "\n'help'\t\t\t\t: view this\n"); + poststring("\ncreating arguments are :\n" + "\"limiter~ [ [ [ [...]]]]\": may be anything\n"); + endpost(); +} + + +/* ------------------------------------------------------------------------------------ */ +// now do the dsp - thing // +/* ------------------------------------------------------------------------------------ */ + +static t_int *oversampling_maxima(t_int *w) +{ + t_limiter *x = (t_limiter *)w[1]; + t_inbuf *buf = (t_inbuf *)w[2]; + t_float *in = (t_float *)w[3]; + t_float *out = (t_float *)w[4]; + + int n = x->s_n; + int bufsize = x->buf_size; + + int i = buf->buf_position; + + t_float *vp = buf->ringbuf, *ep = vp + bufsize, *bp = vp + XTRASAMPS + i; + + i += n; + + while (n--) + { + t_float os1, os2, max; + t_float last4, last3, last2, last1, sinccurrent, current, next1, next2, next3, next4; + + if (bp == ep) + { + vp[0] = bp[-9]; + vp[1] = bp[-8]; + vp[2] = bp[-7]; + vp[3] = bp[-6]; + vp[4] = bp[-5]; + vp[5] = bp[-4]; + vp[6] = bp[-3]; + vp[7] = bp[-2]; + vp[8] = bp[-1]; + + bp = vp + XTRASAMPS; + i -= bufsize - XTRASAMPS; + } + + os1= fabsf(SINC8 * (last4 = bp[-8]) + + SINC6 * (last3 = bp[-7]) + + SINC4 * (last2 = bp[-6]) + + SINC2 * (last1 = bp[-5]) + + (sinccurrent = SINC1 * (current = bp[-4])) + + SINC3 * (next1 = bp[-3]) + + SINC5 * (next2 = bp[-2]) + + SINC7 * (next3 = bp[-1]) + + SINC9 * (next4 = bp[0])); + + os2= fabsf(SINC8 * next4 + + SINC4 * next3 + + SINC6 * next2 + + SINC2 * next1 + + sinccurrent + + SINC3 * last1 + + SINC5 * last2 + + SINC7 * last3 + + SINC9 * last4); + + max = fabsf(current); + + if (max < os1) + { + max = os1; + } + if (max < os2) + { + max = os2; + } + + *bp++ = *in++; + if (*out++ < max) *(out-1) = max; + } + buf->buf_position = i; + + return (w+5); +} + + +static t_int *limiter_perform(t_int *w) +{ + t_limiter *x=(t_limiter *)w[1]; + int n = x->s_n; + + t_float *in = (t_float *)w[2]; + t_float *out= (t_float *)w[3]; + + t_limctl *v1 = (t_limctl *)(x->val1); + t_limctl *v2 = (t_limctl *)(x->val2); + t_cmpctl *c = (t_cmpctl *)(x->cmp); + + // now let's make things a little bit faster + + // these must not be changed by process + const t_float limit = v1->limit; + const t_float holdlong = v1->hold_samples; + const t_float coa_long = v1->change_of_amplification; + + const t_float alimit = v2->limit; + const t_float holdshort = v2->hold_samples; + const t_float coa_short = v2->change_of_amplification; + + t_float tresh = c->treshold; + t_float uclimit = c->uclimit; + t_float climit_inv = c->climit_inverse; + + t_float oneminusratio = c->oneminusratio; + + // these will be changed by process + t_float amp = x->amplification; + t_float samplesleft = x->samples_left; + t_float stillleft = x->still_left; + + // an intern variable... + t_float max_val; + + switch (x->mode) { + case LIMIT0: + while (n--) + { + max_val = *in; + + // the MAIN routine for the 1-treshold-limiter + + if ((max_val * amp) > limit) + { + amp = limit / max_val; + samplesleft = holdlong; + } else + { + if (samplesleft > 0) + { + samplesleft--; + } else + { + if ((amp *= coa_long) > 1) amp = 1; + } + } + + *out++ = amp; + *in++ = 0; + } + break; + case LIMIT1: + while (n--) + { + max_val = *in; + // the main routine 2 + + if ((max_val * amp) > limit) + { + samplesleft = ((amp = (limit / max_val)) < alimit)?holdshort:holdlong; + stillleft = holdlong; + } else + { + if (samplesleft > 0) + { + samplesleft--; + stillleft--; + } else + { + if (amp < alimit) + { + if ((amp *= coa_short) > 1) amp = 1; + } else + { + if (stillleft > 0) + { + samplesleft = stillleft; + } else + { + if ((amp *= coa_long) > 1) amp = 1; + } + } + } + } + *out++ = amp; + *in++ = 0; + } + x->still_left = stillleft; + break; + case COMPRESS: + while (n--) + { + max_val = *in; + + // the MAIN routine for the compressor (very similar to the 1-treshold-limiter) + + if (max_val * amp > tresh) { + amp = tresh / max_val; + samplesleft = holdlong; + } else + if (samplesleft > 0) samplesleft--; + else if ((amp *= coa_long) > 1) amp = 1; + + if (amp < 1.) + if (amp > uclimit) // amp is still UnCompressed uclimit==limitIN/tresh; + *out++ = pow(amp, oneminusratio); + else *out++ = amp * climit_inv; // amp must fit for limiting : amp(new) = limit/maxval; = amp(old)*limitOUT/tresh; + else *out++ = 1.; + + *in++ = 0.; + } + break; + default: + while (n--) *out++ = *in++ = 0.; + break; + } + + // now return the goodies + x->amplification = amp; + x->samples_left = samplesleft; + + return (w+4); +} + + +#if 0 +static t_int *route_through(t_int *w) +{ + t_float *in = (t_float *)w[1]; + t_float *out = (t_float *)w[2]; + int n = (int)w[3]; + + while(n--) + { + *out++ = *in; + *in++ = 0; + } + + return (w+4); +} +#endif + +void limiter_dsp(t_limiter *x, t_signal **sp) +{ + int i = 0; + t_float* sig_buf = (t_float *)getbytes(sizeof(t_float) * sp[0]->s_n); + + x->s_n = sp[0]->s_n; + + if (x->amplification == 0) x->amplification = 0.0000001; + + if (x->val2->limit >= 1) x->mode = 0; + + while (i < x->number_of_inlets) + { + dsp_add(oversampling_maxima, 4, x, &(x->in[i]), sp[i]->s_vec, sig_buf); + i++; + } + + dsp_add(limiter_perform, 3, x, sig_buf, sp[i]->s_vec); +} + + + +/* ------------------------------------------------------------------------------------ */ +// finally do the creation - things + +void *limiter_new(t_symbol *s, int argc, t_atom *argv) +{ + t_limiter *x = (t_limiter *)pd_new(limiter_class); + + int i = 0; + + if (argc) set_bufsize(x, atom_getfloat(argv)); + else + { + argc = 1; + set_bufsize(x, 0); + } + + if (argc > 64) argc=64; + if (argc == 0) argc=1; + + x->number_of_inlets = argc--; + + while (argc--) + { + inlet_new(&x->x_obj, &x->x_obj.ob_pd, &s_signal, &s_signal); + } + + outlet_new(&x->x_obj, &s_signal); + + x->in = (t_inbuf*)getbytes(sizeof(t_inbuf) * x->number_of_inlets); + while (i < x->number_of_inlets) + { + int n; + t_float* buf = (float *)getbytes(sizeof(float) * x->buf_size); + x->in[i].ringbuf = buf; + x->in[i].buf_position = 0; + for (n = 0; n < x->buf_size; n++) x->in[i].ringbuf[n] = 0.; + i++; + } + + x->val1 = (t_limctl *)getbytes(sizeof(t_limctl)); + x->val2 = (t_limctl *)getbytes(sizeof(t_limctl)); + x->cmp = (t_cmpctl *)getbytes(sizeof(t_cmpctl)); + + x->cmp->ratio = 1.; + x->cmp->treshold = 1; + + set1(x, 100, 30, 139); + set2(x, 110, 5, 14.2); + + x->amplification= 1; + x->samples_left = x->still_left = x->mode = 0; + + return (x); +} + +void limiter_free(t_limiter *x) +{ + int i=0; + + freebytes(x->val1, sizeof(t_limctl)); + freebytes(x->val2, sizeof(t_limctl)); + freebytes(x->cmp , sizeof(t_cmpctl)); + + while (i < x->number_of_inlets) freebytes(x->in[i++].ringbuf, x->buf_size * sizeof(t_float)); + + freebytes(x->in, x->number_of_inlets * sizeof(t_inbuf)); +} + + + +/* ------------------------------------------------------------------------------------ */ +/* ------------------------------------------------------------------------------------ */ + + + +void z_limiter_setup(void) +{ + limiter_class = class_new(gensym("limiter~"), (t_newmethod)limiter_new, (t_method)limiter_free, + sizeof(t_limiter), 0, A_GIMME, 0); + + class_addmethod(limiter_class, nullfn, gensym("signal"), 0); + class_addmethod(limiter_class, (t_method)limiter_dsp, gensym("dsp"), 0); + + class_addmethod(limiter_class, (t_method)helper, gensym("help"), 0); + class_addmethod(limiter_class, (t_method)status, gensym("print"), 0); + class_sethelpsymbol(limiter_class, gensym("zexy/limiter~")); + + class_addmethod(limiter_class, (t_method)set_mode, gensym("mode"), A_FLOAT, 0); + class_addmethod(limiter_class, (t_method)set_LIMIT, gensym("LIMIT"), 0); + class_addmethod(limiter_class, (t_method)set_CRACK, gensym("CRACK"), 0); + class_addmethod(limiter_class, (t_method)set_COMPRESS, gensym("COMPRESS"), 0); + + + class_addmethod(limiter_class, (t_method)set_treshold, gensym("tresh"), A_FLOAT, 0); + class_addmethod(limiter_class, (t_method)set_treshold, gensym("treshold"), A_FLOAT, 0); + class_addmethod(limiter_class, (t_method)set_ratio, gensym("ratio"), A_FLOAT, 0); + class_addmethod(limiter_class, (t_method)set1, gensym("set"), A_FLOAT, A_FLOAT, A_FLOAT, 0); + class_addmethod(limiter_class, (t_method)set2, gensym("set2"), A_FLOAT, A_FLOAT, A_FLOAT, 0); + class_addmethod(limiter_class, (t_method)set_compressor,gensym("compress"), A_FLOAT, A_FLOAT, A_FLOAT, 0); + + class_addmethod(limiter_class, (t_method)set_limits, gensym("limits"), A_FLOAT, A_FLOAT, 0); + class_addmethod(limiter_class, (t_method)set_limit, gensym("limit"), A_FLOAT, 0); + class_addfloat (limiter_class, set_limit); + + class_addmethod(limiter_class, (t_method)reset, gensym("reset"), 0); + +} -- cgit v1.2.1