/* filter: apply IIR/FIR filters using Direct Form II copyright 2019 by the mpg123 project licensed under the terms of the LGPL 2.1 see COPYING and AUTHORS files in distribution or http://mpg123.org initially written by Thomas Orgis Since I spent a lot of time tuning filters for my resampler, I figured I should add generic digital filters to the syn123 API. There are multiple independent implementations of filters inside the resampler, using hardcoded parameters. They should be functionally equivalent to this generic code. Let it serve as a reference. The code should be straightforward to read, but is messed up a bit by the task to be runtime-tunable for single or double precision. Also, the coefficients are stored multiple times in a shifted manner to avoid shifting the filter history around and keeping efficient coefficient access. I guess this is a pretty standard optimization which worked well for the resampler. I use macros. Don't be scared. */ #define NO_GROW_BUF #define NO_SMAX #define NO_SMIN #include "syn123_int.h" // 0 ... ORDER // b0 stored // a0 == 1 // ringbuffer-optimized storage of the others // b[order][order] // a[order][order] // historic values, ringbuffer // w[channels][order] // So, one piece of storage of size order*(2*order+channels). // Theoretical limit on maximum order: data block must fit into size_t. // Checking overflow of order*order*2 + order*channels, in that order. #define ORDER_TOO_BIG(order, channels) \ ((order) == 0 || (channels) == 0) \ ? 0 \ : ( (order) > SIZE_MAX/2/(order) \ ? 1 \ : ( (order) > (SIZE_MAX-(order)*(order)*2)/(channels) \ ? 1 \ : 0 \ )\ ) // size of filter data block #define F_DATABLOCK(f, channels) ((f).order*(2*(f).order+channels)) // base addresses of b and a coefficient sets, ringbuffered #define Fb(f) ((f).data) #define Fa(f) ((f).data+(f).order*(f).order) // address of currently active coefficient set // the base is either f.b or f.a (which may be NULL) #define Fc(f, base) ((base) ? ((base)+(f).order*(f).n1) : NULL) // base address of current channel's history values #define Fw(f, c) ((f).data+F_DATABLOCK((f), (c))) #define FILTER_STRUCT(name, type) \ struct name \ { \ int flow; /* if the filter has had first data and is in flow now */ \ unsigned int order; /* filter order */ \ unsigned int n1; /* current position of most recent past */ \ type init_scale; /* scale for first sample as endless history */ \ type b0; /* coefficient b_0 */ \ type *data; /* dynamic coefficient and history data */ \ type *b; /* b coefficients b_1 ... b_N, pointer into the above */ \ type *a; /* same for a, or NULL if non-recursive */ \ }; FILTER_STRUCT(f_filter, float) FILTER_STRUCT(d_filter, double) // Initialization of Direct Form II history. // Return a constant value for w[n] that represents an endless history of given insample. // Given the recursive definition: w[n] = x[n] - sum_i(a[i] * w[n-i]) // Given my ansatz of endless stream of x[n-i] = x[n] and hence w[n-i] = w[n], it follows // that // w[n] = x[n] - sum_i(a[i] * w[n]) = x[n] - sum_i(a[i]) * w[n] // <=> (1 + sum_i(a[i])) w[n] = x[n] // <=> w[n] = 1 / (1 + sum_i(a[i])) * x[n] // Looks simple. Just a scale value. #define INIT_SCALE(name, type) \ static type name(unsigned int order, type *filter_a) \ { \ type asum = 1.; \ if(filter_a) \ for(unsigned int i=0; i 1e-12 || asum < -1e-12) ? (type)1./asum : 0; \ } INIT_SCALE(d_init_scale, double) INIT_SCALE(f_init_scale, float) // Static ring buffer index used for filter history and coefficients. // i: offset // n1: current position of the first entry // s: size of the ring buffer #define RING_INDEX(i, n1, s) ( ((n1)+(i)) % s ) // In the context of a filter: stored offset n1, configured order. #define F_RING_INDEX(f, i) RING_INDEX(i, (f).n1, (f).order) int attribute_align_arg syn123_setup_filter( syn123_handle *sh , int append, unsigned int order, double *b, double *a , int mixenc, int channels, int init_firstval ) { if(!sh) return SYN123_BAD_HANDLE; if(!append) { syn123_drop_filter(sh, sh->fc.count); if(sh->fc.count) return SYN123_WEIRD; } if(sh->fc.count) { if(!mixenc) mixenc = sh->fc.mixenc; if(!channels) channels = sh->fc.channels; } if(channels < 1) return SYN123_BAD_FMT; if(mixenc != MPG123_ENC_FLOAT_32 && mixenc != MPG123_ENC_FLOAT_64) return SYN123_BAD_ENC; if(!b) return SYN123_NO_DATA; if(a && a[0] != 1.) return SYN123_BAD_DATA; if(ORDER_TOO_BIG(order, channels) || sh->fc.count == SIZE_MAX) return SYN123_OVERFLOW; if(sh->fc.maxcount == sh->fc.count) { #define EXPAND_FILTERS(list, stype) \ { \ struct stype *tmp = realloc( list \ , sizeof(struct stype)*(sh->fc.maxcount+1) ); \ if(!tmp) \ return SYN123_DOOM; \ list = tmp; \ } if(mixenc == MPG123_ENC_FLOAT_32) EXPAND_FILTERS(sh->fc.ff, f_filter) else EXPAND_FILTERS(sh->fc.df, d_filter) // Storage for filter structs increased, nobody can take that away now. #undef EXPAND_FILTERS ++sh->fc.maxcount; } // Got enough struct storage, try to fill the filter. #define MAKE_FILTER(list, stype, scaler, type) \ { \ struct stype *tmp = list+sh->fc.count; \ tmp->flow = 0; \ tmp->order = order; \ tmp->b0 = b[0]; \ tmp->data = malloc(sizeof(type)*F_DATABLOCK(*tmp, channels)); \ if(!tmp->data) \ return SYN123_DOOM; \ tmp->b = Fb(*tmp); \ tmp->a = a ? Fa(*tmp) : NULL; \ for(tmp->n1=0; tmp->n1n1) \ { \ type *f_b = Fc(*tmp, tmp->b); \ type *f_a = Fc(*tmp, tmp->a); \ for(unsigned int o=0; on1 = 0; \ tmp->init_scale = init_firstval ? scaler(order, tmp->a) : 0.; \ if(tmp->init_scale == 0.) \ { \ for(int c=0; cflow = 1; \ } \ } if(mixenc == MPG123_ENC_FLOAT_32) MAKE_FILTER(sh->fc.ff, f_filter, f_init_scale, float) else MAKE_FILTER(sh->fc.df, d_filter, d_init_scale, double) #undef MAKE_FILTER // Got fresh filter with memory. Count it. ++sh->fc.count; // Finish off things that cannot error out. sh->fc.mixenc = mixenc; sh->fc.channels = channels; return SYN123_OK; } int attribute_align_arg syn123_query_filter( syn123_handle *sh, size_t position , size_t *count, unsigned int *order, double *b, double *a , int *mixenc, int *channels, int *init_firstval ) { if(!sh) return SYN123_BAD_HANDLE; if( (order || b || a || mixenc || channels || init_firstval) && position >= sh->fc.count ) return SYN123_NO_DATA; if(count) *count = sh->fc.count; if(!sh->fc.count) return SYN123_OK; if(channels) *channels = sh->fc.channels; if(mixenc) *mixenc = sh->fc.mixenc; #define FILTER_INFO(filter, type) \ { \ if(order) \ *order = filter.order; \ if(init_firstval) \ *init_firstval = filter.init_scale != 0. ? 1 : 0; \ if(b || a) \ { \ if(b) \ b[0] = filter.b0; \ if(a) \ a[0] = 1.; \ for(unsigned int o=0; ofc.mixenc == MPG123_ENC_FLOAT_32) FILTER_INFO(sh->fc.ff[position], float) else FILTER_INFO(sh->fc.df[position], double) #undef FILTER_INFO return SYN123_OK; } // This just forgets the filter's own storage, not the storage for // the filter structs. That would need realloc(), which might fail. void attribute_align_arg syn123_drop_filter(syn123_handle *sh, size_t count) { if(!sh) return; if(count > sh->fc.count) count = sh->fc.count; // Free data of indicated filters. for(size_t i=0; ifc.mixenc == MPG123_ENC_FLOAT_32 ? (void*)(sh->fc.ff[--sh->fc.count].data) : (void*)(sh->fc.df[--sh->fc.count].data) ); // count decreased now, maxcount still there } #define APPLY_ONE_FILTER(name, stype, type) \ static void name( struct stype *f \ , int channels, type *audio, size_t samples ) \ { \ if(!samples) \ return; \ if(!f->flow) \ { \ for(int c=0; cinit_scale*audio[c]; \ type *w = Fw(*f, c); \ for(unsigned int o=0; oorder; ++o) \ w[o] = iv; \ } \ f->n1 = 0; \ f->flow = 1; \ } \ /* Leave loop ordering and mono/stereo optimization to the future.*/ \ /* This is what is most logical for interleaved audio and the n1 logic */ \ if(f->a) /* IIR, possibly */ \ { \ for(size_t s=0; sb); \ type *a = Fc(*f, f->a); \ unsigned int next_n1 = F_RING_INDEX(*f, f->order-1); \ for(int c=0; corder; ++i) \ { \ ny += w[i]*b[i]; \ nw -= w[i]*a[i]; \ } \ nw += audio[c]; \ ny += f->b0 * nw; \ w[next_n1] = nw; \ audio[c] = ny; \ } \ f->n1 = next_n1; \ audio += channels; \ } \ } else /* FIR */ \ { \ for(size_t s=0; sb); \ unsigned int next_n1 = F_RING_INDEX(*f, f->order-1); \ for(int c=0; corder; ++i) \ ny += w[i]*b[i]; \ ny += f->b0 * nw; \ w[next_n1] = nw; \ audio[c] = ny; \ } \ f->n1 = next_n1; \ audio += channels; \ } \ } \ } APPLY_ONE_FILTER(apply_filter_float, f_filter, float) APPLY_ONE_FILTER(apply_filter_double, d_filter, double) // Finally, the filter application. int attribute_align_arg syn123_filter(syn123_handle *sh, void* buf, int encoding, size_t samples) { if(!sh) return SYN123_BAD_HANDLE; if(!sh->fc.count) // No filter means no change, all good. return SYN123_OK; #define APPLY_FILTERS(list, func) \ for(unsigned int i=0; ifc.count; ++i) \ func(list+i, sh->fc.channels, buf, samples) if(encoding == sh->fc.mixenc) { if(sh->fc.mixenc == MPG123_ENC_FLOAT_32) APPLY_FILTERS(sh->fc.ff, apply_filter_float); else APPLY_FILTERS(sh->fc.df, apply_filter_double); } else { return SYN123_BAD_ENC; } #undef APPLY_FILTERS return SYN123_OK; }