2 mediastreamer2 library - modular sound and video processing and streaming
3 Copyright (C) 2006 Simon MORLAT (simon.morlat@linphone.org)
5 This program is free software; you can redistribute it and/or
6 modify it under the terms of the GNU General Public License
7 as published by the Free Software Foundation; either version 2
8 of the License, or (at your option) any later version.
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU General Public License for more details.
15 You should have received a copy of the GNU General Public License
16 along with this program; if not, write to the Free Software
17 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
21 #include "mediastreamer-config.h"
24 #include "mediastreamer2/msvolume.h"
25 #include "mediastreamer2/msticker.h"
29 #include <speex/speex_preprocess.h>
32 static const float max_e=32767*32767;
33 static const float coef=0.1;
34 static const float gain_k=0.02;
35 static const float en_weight=4.0;
36 static const float noise_thres=0.1;
39 typedef struct Volume{
42 float gain; /*the one really applied, smoothed by noise gate and echo limiter*/
43 float static_gain; /*the one fixed by the user*/
47 float target_gain; /*the target gain choosed by echo limiter and noise gate*/
49 int sustain_time; /* time in ms for which echo limiter remains active after resuming from speech to silence.*/
50 uint64_t sustain_start;
53 SpeexPreprocessState *speex_pp;
57 int ng_cut_time; /*noise gate cut time, after last speech detected*/
64 bool_t noise_gate_enabled;
67 static void volume_init(MSFilter *f){
68 Volume *v=(Volume*)ms_new(Volume,1);
71 v->static_gain=v->gain=1;
81 v->buffer=ms_bufferizer_new();
84 v->noise_gate_enabled=FALSE;
85 v->ng_cut_time=100;/*milliseconds*/
87 v->ng_threshold=noise_thres;
95 static void volume_uninit(MSFilter *f){
96 Volume *v=(Volume*)f->data;
99 speex_preprocess_state_destroy(v->speex_pp);
101 ms_bufferizer_destroy(v->buffer);
105 static int volume_get(MSFilter *f, void *arg){
106 float *farg=(float*)arg;
107 Volume *v=(Volume*)f->data;
108 *farg=10*ortp_log10f((v->energy+1)/max_e);
113 static int volume_set_sample_rate(MSFilter *f, void *arg){
114 Volume *v=(Volume*)f->data;
115 v->sample_rate=*(int*)arg;
119 static int volume_get_linear(MSFilter *f, void *arg){
120 float *farg=(float*)arg;
121 Volume *v=(Volume*)f->data;
122 *farg=(v->energy+1)/max_e;
126 static void volume_agc_process(Volume *v, mblk_t *om){
127 speex_preprocess_run(v->speex_pp,(int16_t*)om->b_rptr);
131 static void volume_agc_process(Volume *v, mblk_t *om){
137 static inline float compute_gain(float static_gain, float energy, float weight){
138 float ret=static_gain*(1 - (energy*weight));
144 The principle of this algorithm is that we apply a gain to the input signal which is opposite to the
145 energy measured by the peer MSVolume.
146 For example if some noise is played by the speaker, then the signal captured by the microphone will be lowered.
147 The gain changes smoothly when the peer energy is decreasing, but is immediately changed when the peer energy is
151 static void volume_echo_avoider_process(Volume *v, uint64_t curtime){
154 ms_filter_call_method(v->peer,MS_VOLUME_GET_LINEAR,&peer_e);
157 if (peer_e>v->thres){
159 gain=compute_gain(v->static_gain,peer_e,v->force);
160 if (peer_e>v->last_peer_en)
163 v->sustain_start=curtime;
168 int peer_active=FALSE;
169 ms_filter_call_method(v->peer,MS_VOLUME_GET_EA_STATE,&peer_active);
170 if (peer_e>v->thres && ! peer_active){
172 gain=compute_gain(v->static_gain,peer_e,v->force);
176 if (curtime!=0 && (curtime-v->sustain_start)<v->sustain_time){
178 }else{/*restore normal gain*/
184 v->last_peer_en=peer_e;
186 ms_message("ea_active=%i, peer_e=%f gain=%f gain_k=%f force=%f",v->ea_active,peer_e,v->gain, v->gain_k,v->force);
189 static void volume_noise_gate_process(Volume *v , float energy, mblk_t *om){
190 int nsamples=((om->b_wptr-om->b_rptr)/2);
191 if ((energy/max_e)<v->ng_threshold){
192 v->ng_noise_dur+=(nsamples*1000)/v->sample_rate;
193 if (v->ng_noise_dur>v->ng_cut_time){
194 v->target_gain=v->ng_floorgain;
198 /*let the target gain unchanged, ie let the echo-limiter choose the gain*/
202 static int volume_set_gain(MSFilter *f, void *arg){
203 float *farg=(float*)arg;
204 Volume *v=(Volume*)f->data;
205 v->gain=v->static_gain=v->target_gain=*farg;
210 static int volume_get_ea_state(MSFilter *f, void *arg){
212 Volume *v=(Volume*)f->data;
217 static int volume_set_peer(MSFilter *f, void *arg){
218 MSFilter *p=(MSFilter*)arg;
219 Volume *v=(Volume*)f->data;
224 static int volume_set_agc(MSFilter *f, void *arg){
225 Volume *v=(Volume*)f->data;
226 v->agc_enabled=*(int*)arg;
230 static int volume_set_ea_threshold(MSFilter *f, void*arg){
231 Volume *v=(Volume*)f->data;
232 float val=*(float*)arg;
233 if (val<0 || val>1) {
234 ms_error("Error: threshold must be in range [0..1]");
241 static int volume_set_ea_speed(MSFilter *f, void*arg){
242 Volume *v=(Volume*)f->data;
243 float val=*(float*)arg;
244 if (val<0 || val>1) {
245 ms_error("Error: speed must be in range [0..1]");
252 static int volume_set_ea_force(MSFilter *f, void*arg){
253 Volume *v=(Volume*)f->data;
254 float val=*(float*)arg;
259 static int volume_set_ea_sustain(MSFilter *f, void *arg){
260 Volume *v=(Volume*)f->data;
261 v->sustain_time=*(int*)arg;
265 static int volume_enable_noise_gate(MSFilter *f, void *arg){
266 Volume *v=(Volume*)f->data;
267 v->noise_gate_enabled=*(int*)arg;
271 static int volume_set_noise_gate_threshold(MSFilter *f, void *arg){
272 Volume *v=(Volume*)f->data;
273 v->ng_threshold=*(float*)arg;
277 static int volume_set_noise_gate_floorgain(MSFilter *f, void *arg){
278 Volume *v=(Volume*)f->data;
279 v->ng_floorgain=*(float*)arg;
283 static inline int16_t saturate(float val){
284 return (val>32767) ? 32767 : ( (val<-32767) ? -32767 : val);
287 static float update_energy(int16_t *signal, int numsamples, float last_energy_value){
289 float en=last_energy_value;
290 for (i=0;i<numsamples;++i){
291 float s=(float)signal[i];
292 en=(s*s*coef) + (1.0-coef)*en;
297 static void apply_gain(Volume *v, mblk_t *m){
299 float gain=v->target_gain;
301 if (gain==1 && v->gain==1) return;
302 v->gain=(v->gain*(1-v->gain_k)) + (v->gain_k*gain);
303 for ( sample=(int16_t*)m->b_rptr;
304 sample<(int16_t*)m->b_wptr;
307 *sample=saturate(s*v->gain);
311 static void volume_preprocess(MSFilter *f){
312 Volume *v=(Volume*)f->data;
313 /*process agc by chunks of 10 ms*/
314 v->nsamples=(int)(0.01*(float)v->sample_rate);
316 ms_message("AGC is enabled.");
318 if (v->speex_pp==NULL){
320 v->speex_pp=speex_preprocess_state_init(v->nsamples,v->sample_rate);
321 if (speex_preprocess_ctl(v->speex_pp,SPEEX_PREPROCESS_SET_AGC,&tmp)==-1){
322 ms_warning("Speex AGC is not available.");
325 speex_preprocess_ctl(v->speex_pp,SPEEX_PREPROCESS_SET_VAD,&tmp);
326 speex_preprocess_ctl(v->speex_pp,SPEEX_PREPROCESS_SET_DENOISE,&tmp);
327 speex_preprocess_ctl(v->speex_pp,SPEEX_PREPROCESS_SET_DEREVERB,&tmp);
330 ms_error("No AGC possible, mediastreamer2 was compiled without libspeexdsp.");
337 static void volume_process(MSFilter *f){
339 Volume *v=(Volume*)f->data;
344 int nbytes=v->nsamples*2;
345 ms_bufferizer_put_from_queue(v->buffer,f->inputs[0]);
346 while(ms_bufferizer_get_avail(v->buffer)>=nbytes){
348 ms_bufferizer_read(v->buffer,om->b_wptr,nbytes);
350 en=update_energy((int16_t*)om->b_rptr,v->nsamples,en);
351 volume_agc_process(v,om);
354 volume_echo_avoider_process(v,f->ticker->time);
355 }else v->target_gain=v->static_gain;
357 if (v->noise_gate_enabled)
358 volume_noise_gate_process(v,en,om);
360 ms_queue_put(f->outputs[0],om);
363 /*light processing: no agc. Work in place in the input buffer*/
364 while((m=ms_queue_get(f->inputs[0]))!=NULL){
365 en=update_energy((int16_t*)m->b_rptr,(m->b_wptr-m->b_rptr)/2,en);
367 volume_echo_avoider_process(v,f->ticker->time);
368 }else v->target_gain=v->static_gain;
370 if (v->noise_gate_enabled)
371 volume_noise_gate_process(v,en,m);
373 ms_queue_put(f->outputs[0],m);
379 static MSFilterMethod methods[]={
380 { MS_VOLUME_GET , volume_get },
381 { MS_VOLUME_GET_LINEAR , volume_get_linear },
382 { MS_VOLUME_SET_GAIN , volume_set_gain },
383 { MS_VOLUME_GET_EA_STATE , volume_get_ea_state },
384 { MS_VOLUME_SET_PEER , volume_set_peer },
385 { MS_VOLUME_SET_EA_THRESHOLD , volume_set_ea_threshold },
386 { MS_VOLUME_SET_EA_SPEED , volume_set_ea_speed },
387 { MS_VOLUME_SET_EA_FORCE , volume_set_ea_force },
388 { MS_VOLUME_SET_EA_SUSTAIN, volume_set_ea_sustain },
389 { MS_FILTER_SET_SAMPLE_RATE, volume_set_sample_rate },
390 { MS_VOLUME_ENABLE_AGC , volume_set_agc },
391 { MS_VOLUME_ENABLE_NOISE_GATE, volume_enable_noise_gate},
392 { MS_VOLUME_SET_NOISE_GATE_THRESHOLD, volume_set_noise_gate_threshold},
393 { MS_VOLUME_SET_NOISE_GATE_FLOORGAIN, volume_set_noise_gate_floorgain},
398 MSFilterDesc ms_volume_desc={
400 .text=N_("A filter that controls and measure sound volume"),
402 .category=MS_FILTER_OTHER,
406 .uninit=volume_uninit,
407 .preprocess=volume_preprocess,
408 .process=volume_process,
412 MSFilterDesc ms_volume_desc={
415 N_("A filter that controls and measure sound volume"),
429 MS_FILTER_DESC_EXPORT(ms_volume_desc)