/* mediastreamer2 library - modular sound and video processing and streaming Copyright (C) 2009 Simon MORLAT (simon.morlat@linphone.org) This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #include #include #include #ifdef _MSC_VER #include #define alloca _alloca #endif #ifndef M_PI #define M_PI 3.14159265358979323846 #endif #ifdef MS_FIXED_POINT #define GAIN_ZERODB 20000 #else #define GAIN_ZERODB 1.0 #endif #define TAPS 128 typedef struct _EqualizerState{ int rate; int nfft; //number of fft points in time ms_word16_t *fft_cpx; int fir_len; ms_word16_t *fir; ms_mem_t *mem; /*memories for filtering computations*/ bool_t needs_update; bool_t active; } EqualizerState; static void equalizer_state_flatten(EqualizerState *s){ int i; ms_word16_t val=GAIN_ZERODB/s->nfft; s->fft_cpx[0]=val; for(i=1;infft;i+=2) s->fft_cpx[i]=val; } static EqualizerState * equalizer_state_new(int nfft){ EqualizerState *s=(EqualizerState *)ms_new0(EqualizerState,1); s->rate=8000; s->nfft=nfft; s->fft_cpx=(ms_word16_t*)ms_new0(ms_word16_t,s->nfft); equalizer_state_flatten(s); s->fir_len=s->nfft; s->fir=(ms_word16_t*)ms_new(ms_word16_t,s->fir_len); s->mem=(ms_mem_t*)ms_new0(ms_mem_t,s->fir_len); s->needs_update=TRUE; s->active=TRUE; return s; } static void equalizer_state_destroy(EqualizerState *s){ ms_free(s->fft_cpx); ms_free(s->fir); ms_free(s->mem); ms_free(s); } static int equalizer_state_hz_to_index(EqualizerState *s, int hz){ int ret; if (hz<0){ ms_error("Bad frequency value %i",hz); return -1; } if (hz>(s->rate/2)){ hz=(s->rate/2); } /*round to nearest integer*/ ret=((hz*s->nfft)+(s->rate/2))/s->rate; if (ret==s->nfft/2) ret=(s->nfft/2)-1; return ret; } static int equalizer_state_index2hz(EqualizerState *s, int index){ return (index * s->rate + s->nfft/2) / s->nfft; } static float gain_float(ms_word16_t val){ return (float)val/GAIN_ZERODB; } static float equalizer_state_get(EqualizerState *s, int freqhz){ int idx=equalizer_state_hz_to_index(s,freqhz); if (idx>=0) return gain_float(s->fft_cpx[idx*2])*s->nfft; return 0; } /* The natural peaking equalizer amplitude transfer function is multiplied to the discrete f-points. * Note that for PEQ no sqrt is needed for the overall calculation, applying it to gain yields the * same response. */ static float equalizer_compute_gainpoint(int f, int freq_0, float sqrt_gain, int freq_bw) { float k1, k2; k1 = ((float)(f*f)-(float)(freq_0*freq_0)); k1*= k1; k2 = (float)(f*freq_bw); k2*= k2; return (k1+k2*sqrt_gain)/(k1+k2/sqrt_gain); } static void equalizer_point_set(EqualizerState *s, int i, int f, float gain){ ms_message("Setting gain %f for freq_index %i (%i Hz)\n",gain,i,f); s->fft_cpx[1+((i-1)*2)] = (s->fft_cpx[1+((i-1)*2)]*(int)(gain*32768))/32768; } static void equalizer_state_set(EqualizerState *s, int freq_0, float gain, int freq_bw){ //int low,high; int i, f; int delta_f = equalizer_state_index2hz(s, 1); float sqrt_gain = sqrt(gain); int mid = equalizer_state_hz_to_index(s, freq_0); freq_bw-= delta_f/2; /* subtract a constant - compensates for limited fft steepness at low f */ if (freq_bw < delta_f/2) freq_bw = delta_f/2; i = mid; f = equalizer_state_index2hz(s, i); equalizer_point_set(s, i, f, gain); /* gain according to argument */ do { /* note: to better accomodate limited fft steepness, -delta is applied in f-calc ... */ i++; f = equalizer_state_index2hz(s, i); gain = equalizer_compute_gainpoint(f-delta_f, freq_0, sqrt_gain, freq_bw); equalizer_point_set(s, i, f, gain); } while (i1.1 || gain<0.9)); i = mid; do { /* ... and here +delta, as to */ i--; f = equalizer_state_index2hz(s, i); gain = equalizer_compute_gainpoint(f+delta_f, freq_0, sqrt_gain, freq_bw); equalizer_point_set(s, i, f, gain); } while (i>=0 && (gain>1.1 || gain<0.9)); s->needs_update=TRUE; } static void dump_table(ms_word16_t *t, int len){ int i; for(i=0;infft); ms_message("Spectral domain:"); dump_table(s->fft_cpx,s->nfft); ms_ifft(fft_handle,s->fft_cpx,s->fir); ms_fft_destroy(fft_handle); /* ms_message("Inverse fft result:"); dump_table(s->fir,s->fir_len); */ time_shift(s->fir,s->fir_len); /* ms_message("Time shifted:"); dump_table(s->fir,s->fir_len); */ norm_and_apodize(s->fir,s->fir_len); ms_message("Apodized impulse response:"); dump_table(s->fir,s->fir_len); s->needs_update=FALSE; } #ifdef MS_FIXED_POINT #define INT16_TO_WORD16(i,w,l) w=(i) #define WORD16_TO_INT16(i,w,l) i=(w) #else static void int16_to_word16(const int16_t *is, ms_word16_t *w, int l){ int i; for(i=0;ineeds_update) equalizer_state_compute_impulse_response(s); ms_word16_t *w; INT16_TO_WORD16(samples,w,nsamples); ms_fir_mem16(w,s->fir,w,nsamples,s->fir_len,s->mem); WORD16_TO_INT16(w,samples,nsamples); } static void equalizer_init(MSFilter *f){ f->data=equalizer_state_new(TAPS); } static void equalizer_uninit(MSFilter *f){ equalizer_state_destroy((EqualizerState*)f->data); } static void equalizer_process(MSFilter *f){ mblk_t *m; EqualizerState *s=(EqualizerState*)f->data; while((m=ms_queue_get(f->inputs[0]))!=NULL){ if (s->active){ equalizer_state_run(s,(int16_t*)m->b_rptr,(m->b_wptr-m->b_rptr)/2); } ms_queue_put(f->outputs[0],m); } } static int equalizer_set_gain(MSFilter *f, void *data){ EqualizerState *s=(EqualizerState*)f->data; MSEqualizerGain *d=(MSEqualizerGain*)data; equalizer_state_set(s,d->frequency,d->gain,d->width); return 0; } static int equalizer_get_gain(MSFilter *f, void *data){ EqualizerState *s=(EqualizerState*)f->data; MSEqualizerGain *d=(MSEqualizerGain*)data; d->gain=equalizer_state_get(s,d->frequency); d->width=0; return 0; } static int equalizer_set_rate(MSFilter *f, void *data){ EqualizerState *s=(EqualizerState*)f->data; s->rate=*(int*)data; s->needs_update=TRUE; return 0; } static int equalizer_set_active(MSFilter *f, void *data){ EqualizerState *s=(EqualizerState*)f->data; s->active=*(int*)data; return 0; } static int equalizer_dump(MSFilter *f, void *data){ EqualizerState *s=(EqualizerState*)f->data; float *t=(float*)data; int i; *t=s->fft_cpx[0]; t++; for (i=1;infft;i+=2){ *t=((float)s->fft_cpx[i]*(float)s->nfft)/(float)GAIN_ZERODB; t++; } return 0; } static int equalizer_get_nfreqs(MSFilter *f, void *data){ EqualizerState *s=(EqualizerState*)f->data; *(int*)data=s->nfft/2; return 0; } static MSFilterMethod equalizer_methods[]={ { MS_EQUALIZER_SET_GAIN , equalizer_set_gain }, { MS_EQUALIZER_GET_GAIN , equalizer_get_gain }, { MS_EQUALIZER_SET_ACTIVE , equalizer_set_active }, { MS_FILTER_SET_SAMPLE_RATE , equalizer_set_rate }, { MS_EQUALIZER_DUMP_STATE , equalizer_dump }, { MS_EQUALIZER_GET_NUM_FREQUENCIES, equalizer_get_nfreqs }, { 0 , NULL } }; #ifdef _MSC_VER MSFilterDesc ms_equalizer_desc={ MS_EQUALIZER_ID, "MSEqualizer", N_("Parametric sound equalizer."), MS_FILTER_OTHER, NULL, 1, 1, equalizer_init, NULL, equalizer_process, NULL, equalizer_uninit, equalizer_methods }; #else MSFilterDesc ms_equalizer_desc={ .id= MS_EQUALIZER_ID, .name="MSEqualizer", .text=N_("Parametric sound equalizer."), .category=MS_FILTER_OTHER, .ninputs=1, .noutputs=1, .init=equalizer_init, .process=equalizer_process, .uninit=equalizer_uninit, .methods=equalizer_methods }; #endif MS_FILTER_DESC_EXPORT(ms_equalizer_desc)