diff --git a/linphone/coreapi/linphonecore.c b/linphone/coreapi/linphonecore.c index 2f9803f25dc4f9b5e89cab05c04e10125644f52c..1f5b910159e19f5cfe0ee64b5d6af32114774b25 100644 --- a/linphone/coreapi/linphonecore.c +++ b/linphone/coreapi/linphonecore.c @@ -411,6 +411,8 @@ void sound_config_read(LinphoneCore *lc) linphone_core_enable_echo_limiter(lc, lp_config_get_int(lc->config,"sound","echolimiter",0)); + linphone_core_enable_agc(lc, + lp_config_get_int(lc->config,"sound","agc",0)); } void sip_config_read(LinphoneCore *lc) @@ -1435,6 +1437,7 @@ void linphone_core_init_media_streams(LinphoneCore *lc){ } } + audio_stream_enable_automatic_gain_control(lc->audiostream,linphone_core_agc_enabled(lc)); #ifdef VIDEO_ENABLED if (lc->video_conf.display || lc->video_conf.capture) lc->videostream=video_stream_new(linphone_core_get_video_port(lc),linphone_core_ipv6_enabled(lc)); @@ -1951,6 +1954,14 @@ bool_t linphone_core_echo_limiter_enabled(const LinphoneCore *lc){ return lc->sound_conf.ea; } +void linphone_core_enable_agc(LinphoneCore *lc, bool_t val){ + lc->sound_conf.agc=val; +} + +bool_t linphone_core_agc_enabled(const LinphoneCore *lc){ + return lc->sound_conf.agc; +} + void linphone_core_send_dtmf(LinphoneCore *lc,char dtmf) { diff --git a/linphone/coreapi/linphonecore.h b/linphone/coreapi/linphonecore.h index 8334d9bd81d8d55aa7a21a38fd2955f04ff18680..ab7b5641f98469f7b3a1c3277f53e7aeb63257c5 100644 --- a/linphone/coreapi/linphonecore.h +++ b/linphone/coreapi/linphonecore.h @@ -107,6 +107,7 @@ typedef struct sound_config char *remote_ring; bool_t ec; bool_t ea; + bool_t agc; } sound_config_t; typedef struct codecs_config @@ -713,6 +714,9 @@ bool_t linphone_core_echo_cancelation_enabled(LinphoneCore *lc); void linphone_core_enable_echo_limiter(LinphoneCore *lc, bool_t val); bool_t linphone_core_echo_limiter_enabled(const LinphoneCore *lc); +void linphone_core_enable_agc(LinphoneCore *lc, bool_t val); +bool_t linphone_core_agc_enabled(const LinphoneCore *lc); + void linphone_core_set_presence_info(LinphoneCore *lc,int minutes_away,const char *contact,LinphoneOnlineStatus os); LinphoneOnlineStatus linphone_core_get_presence_info(const LinphoneCore *lc); diff --git a/linphone/linphone.kdevelop b/linphone/linphone.kdevelop index 23f8b7837a9874ded986cb204f5b6163c5dd85cd..5068dc8f674aad49223f61bb552c090deaf52fc8 100644 --- a/linphone/linphone.kdevelop +++ b/linphone/linphone.kdevelop @@ -8,10 +8,24 @@ <primarylanguage>C</primarylanguage> <ignoreparts/> <projectname>linphone</projectname> + <projectdirectory>.</projectdirectory> + <absoluteprojectpath>false</absoluteprojectpath> + <description></description> + <defaultencoding></defaultencoding> </general> <kdevcustomproject> <run> <directoryradio>executable</directoryradio> + <mainprogram>/home/smorlat/sources/git/linphone/linphone</mainprogram> + <programargs></programargs> + <globaldebugarguments></globaldebugarguments> + <globalcwd>/home/smorlat/sources/git/linphone/linphone</globalcwd> + <useglobalprogram>false</useglobalprogram> + <terminal>false</terminal> + <autocompile>false</autocompile> + <autoinstall>false</autoinstall> + <autokdesu>false</autokdesu> + <envvars/> </run> <filetypes> <filetype>*.java</filetype> @@ -374,11 +388,51 @@ <path>win32acm/wineacm.h</path> <path>win32acm/wrapper.h</path> </blacklist> + <build> + <buildtool>make</buildtool> + <builddir></builddir> + </build> + <other> + <prio>0</prio> + <otherbin></otherbin> + <defaulttarget></defaulttarget> + <otheroptions></otheroptions> + <selectedenvironment>default</selectedenvironment> + <environments> + <default/> + </environments> + </other> + <make> + <abortonerror>true</abortonerror> + <numberofjobs>0</numberofjobs> + <prio>0</prio> + <dontact>false</dontact> + <makebin></makebin> + <defaulttarget></defaulttarget> + <makeoptions></makeoptions> + <selectedenvironment>default</selectedenvironment> + <environments> + <default/> + </environments> + </make> </kdevcustomproject> <kdevdebugger> <general> - <dbgshell/> + <dbgshell></dbgshell> + <gdbpath></gdbpath> + <configGdbScript></configGdbScript> + <runShellScript></runShellScript> + <runGdbScript></runGdbScript> + <breakonloadinglibs>true</breakonloadinglibs> + <separatetty>false</separatetty> + <floatingtoolbar>false</floatingtoolbar> + <raiseGDBOnStart>false</raiseGDBOnStart> </general> + <display> + <staticmembers>false</staticmembers> + <demanglenames>true</demanglenames> + <outputradix>10</outputradix> + </display> </kdevdebugger> <kdevdoctreeview> <ignoretocs> @@ -470,6 +524,19 @@ <alwaysIncludeNamespaces>false</alwaysIncludeNamespaces> <includePaths>.;</includePaths> </codecompletion> + <creategettersetter> + <prefixGet></prefixGet> + <prefixSet>set</prefixSet> + <prefixVariable>m_,_</prefixVariable> + <parameterName>theValue</parameterName> + <inlineGet>true</inlineGet> + <inlineSet>true</inlineSet> + </creategettersetter> + <splitheadersource> + <enabled>false</enabled> + <synchronize>true</synchronize> + <orientation>Vertical</orientation> + </splitheadersource> </kdevcppsupport> <kdevfileview> <groups> @@ -481,4 +548,10 @@ <hidenonprojectfiles>false</hidenonprojectfiles> </tree> </kdevfileview> + <cppsupportpart> + <filetemplates> + <interfacesuffix>.h</interfacesuffix> + <implementationsuffix>.cpp</implementationsuffix> + </filetemplates> + </cppsupportpart> </kdevelop> diff --git a/linphone/mediastreamer2/include/mediastreamer2/mediastream.h b/linphone/mediastreamer2/include/mediastreamer2/mediastream.h index e41f939da2822ad0d9c9adcd69da79b9860b768c..61d7dab974b46fd25b8393a6e95aad0ecb2dbd0e 100644 --- a/linphone/mediastreamer2/include/mediastreamer2/mediastream.h +++ b/linphone/mediastreamer2/include/mediastreamer2/mediastream.h @@ -53,6 +53,7 @@ struct _AudioStream EchoLimiterType el_type; /*use echo limiter: two MSVolume, measured input level controlling local output level*/ bool_t play_dtmfs; bool_t use_gc; + bool_t use_agc; }; #ifdef __cplusplus @@ -105,6 +106,9 @@ void audio_stream_enable_echo_limiter(AudioStream *stream, EchoLimiterType type) /*enable gain control, to be done before start() */ void audio_stream_enable_gain_control(AudioStream *stream, bool_t val); +/*enable automatic gain control, to be done before start() */ +void audio_stream_enable_automatic_gain_control(AudioStream *stream, bool_t val); + void audio_stream_set_mic_gain(AudioStream *stream, float gain); /* stop the audio streaming thread and free everything*/ diff --git a/linphone/mediastreamer2/include/mediastreamer2/msvolume.h b/linphone/mediastreamer2/include/mediastreamer2/msvolume.h index 1bb208dc7c5d790c894d30c1aa85e54107c03d2f..14574eecdd1785029d24403ab14e022d337d68d9 100644 --- a/linphone/mediastreamer2/include/mediastreamer2/msvolume.h +++ b/linphone/mediastreamer2/include/mediastreamer2/msvolume.h @@ -48,6 +48,8 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. #define MS_VOLUME_SET_EA_FORCE MS_FILTER_METHOD(MS_VOLUME_ID,7,float) +#define MS_VOLUME_ENABLE_AGC MS_FILTER_METHOD(MS_VOLUME_ID,8,int) + extern MSFilterDesc ms_volume_desc; #endif diff --git a/linphone/mediastreamer2/src/audiostream.c b/linphone/mediastreamer2/src/audiostream.c index bb0edda7cb5fa384577e672a611a19009fbe24ba..516d6498031b7a8b3a6b8ef115693d441a3a6273 100644 --- a/linphone/mediastreamer2/src/audiostream.c +++ b/linphone/mediastreamer2/src/audiostream.c @@ -257,6 +257,13 @@ int audio_stream_start_full(AudioStream *stream, RtpProfile *profile, const char } } + if (stream->use_agc){ + int tmp=1; + if (stream->volsend==NULL) + stream->volsend=ms_filter_new(MS_VOLUME_ID); + ms_filter_call_method(stream->volsend,MS_VOLUME_ENABLE_AGC,&tmp); + } + /* give the sound filters some properties */ ms_filter_call_method(stream->soundread,MS_FILTER_SET_SAMPLE_RATE,&pt->clock_rate); ms_filter_call_method(stream->soundwrite,MS_FILTER_SET_SAMPLE_RATE,&pt->clock_rate); @@ -382,6 +389,7 @@ AudioStream *audio_stream_new(int locport, bool_t ipv6){ stream->rtpsend=ms_filter_new(MS_RTP_SEND_ID); stream->play_dtmfs=TRUE; stream->use_gc=FALSE; + stream->use_agc=FALSE; return stream; } @@ -406,6 +414,10 @@ void audio_stream_enable_gain_control(AudioStream *stream, bool_t val){ stream->use_gc=val; } +void audio_stream_enable_automatic_gain_control(AudioStream *stream, bool_t val){ + stream->use_agc=val; +} + void audio_stream_set_mic_gain(AudioStream *stream, float gain){ if (stream->volsend){ ms_filter_call_method(stream->volsend,MS_VOLUME_SET_GAIN,&gain); diff --git a/linphone/mediastreamer2/src/msvolume.c b/linphone/mediastreamer2/src/msvolume.c index 3de030e5ee674822ece994e918078c446882062e..a816bc89cd2206b6b9d04a73824946f97c501493 100644 --- a/linphone/mediastreamer2/src/msvolume.c +++ b/linphone/mediastreamer2/src/msvolume.c @@ -17,9 +17,17 @@ along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ +#ifdef HAVE_CONFIG_H +#include "mediastreamer-config.h" +#endif + #include "mediastreamer2/msvolume.h" #include <math.h> +#ifdef HAVE_SPEEXDSP +#include <speex/speex_preprocess.h> +#endif + static const float max_e=32767*32767; static const float coef=0.1; static const float gain_k=0.02; @@ -36,7 +44,14 @@ typedef struct Volume{ float thres; float force; MSFilter *peer; +#ifdef HAVE_SPEEXDSP + SpeexPreprocessState *speex_pp; +#endif + int sample_rate; + int nsamples; + MSBufferizer *buffer; bool_t ea_active; + bool_t agc_enabled; }Volume; static void volume_init(MSFilter *f){ @@ -49,10 +64,19 @@ static void volume_init(MSFilter *f){ v->thres=noise_thres; v->force=en_weight; v->peer=NULL; + v->agc_enabled=FALSE; + v->buffer=ms_bufferizer_new(); + v->sample_rate=8000; + v->nsamples=80; +#ifdef HAVE_SPEEXDSP + v->speex_pp=NULL; +#endif f->data=v; } static void volume_uninit(MSFilter *f){ + Volume *v=(Volume*)f->data; + ms_bufferizer_destroy(v->buffer); ms_free(f->data); } @@ -63,12 +87,29 @@ static int volume_get(MSFilter *f, void *arg){ return 0; } +static int volume_set_sample_rate(MSFilter *f, void *arg){ + Volume *v=(Volume*)f->data; + v->sample_rate=*(int*)arg; + return 0; +} + static int volume_get_linear(MSFilter *f, void *arg){ float *farg=(float*)arg; Volume *v=(Volume*)f->data; *farg=(v->energy+1)/max_e; return 0; } +#ifdef HAVE_SPEEXDSP +static void volume_agc_process(Volume *v, mblk_t *om){ + speex_preprocess_run(v->speex_pp,(int16_t*)om->b_rptr); +} +#else + +static void volume_agc_process(Volume *v, mblk_t *om){ +} + +#endif + static inline float compute_gain(float static_gain, float energy, float weight){ float ret=static_gain*(1 - (energy*weight)); @@ -129,6 +170,12 @@ static int volume_set_peer(MSFilter *f, void *arg){ return 0; } +static int volume_set_agc(MSFilter *f, void *arg){ + Volume *v=(Volume*)f->data; + v->agc_enabled=*(int*)arg; + return 0; +} + static int volume_set_ea_threshold(MSFilter *f, void*arg){ Volume *v=(Volume*)f->data; float val=*(float*)arg; @@ -162,30 +209,88 @@ static inline int16_t saturate(float val){ return (val>32767) ? 32767 : ( (val<-32767) ? -32767 : val); } +static float update_energy(int16_t *signal, int numsamples, float last_energy_value){ + int i; + float en=last_energy_value; + for (i=0;i<numsamples;++i){ + float s=(float)signal[i]; + en=(s*s*coef) + (1.0-coef)*en; + } + return en; +} + +static void apply_gain(mblk_t *m, float gain){ + int16_t *sample; + for ( sample=(int16_t*)m->b_rptr; + sample<(int16_t*)m->b_wptr; + ++sample){ + float s=*sample; + *sample=saturate(s*gain); + } +} + +static void volume_preprocess(MSFilter *f){ + Volume *v=(Volume*)f->data; + /*process agc by chunks of 10 ms*/ + v->nsamples=(int)(0.01*(float)v->sample_rate); + if (v->agc_enabled){ + ms_message("AGC is enabled."); +#ifdef HAVE_SPEEXDSP + if (v->speex_pp==NULL){ + int tmp=1; + v->speex_pp=speex_preprocess_state_init(v->nsamples,v->sample_rate); + if (speex_preprocess_ctl(v->speex_pp,SPEEX_PREPROCESS_SET_AGC,&tmp)==-1){ + ms_warning("Speex AGC is not available."); + } + tmp=0; + speex_preprocess_ctl(v->speex_pp,SPEEX_PREPROCESS_SET_VAD,&tmp); + speex_preprocess_ctl(v->speex_pp,SPEEX_PREPROCESS_SET_DENOISE,&tmp); + speex_preprocess_ctl(v->speex_pp,SPEEX_PREPROCESS_SET_DEREVERB,&tmp); + } +#else + ms_error("No AGC possible, mediastreamer2 was compiled without libspeexdsp."); +#endif + } +} + + + static void volume_process(MSFilter *f){ mblk_t *m; - int16_t *sample; Volume *v=(Volume*)f->data; float en=v->energy; - while((m=ms_queue_get(f->inputs[0]))!=NULL){ - for ( sample=(int16_t*)m->b_rptr; - sample<(int16_t*)m->b_wptr; - ++sample){ - float s=*sample; - en=(s*s*coef) + (1.0-coef)*en; - } - if (v->peer){ - volume_echo_avoider_process(v); + + if (v->agc_enabled){ + mblk_t *om; + int nbytes=v->nsamples*2; + ms_bufferizer_put_from_queue(v->buffer,f->inputs[0]); + while(ms_bufferizer_get_avail(v->buffer)>=nbytes){ + om=allocb(nbytes,0); + ms_bufferizer_read(v->buffer,om->b_wptr,nbytes); + om->b_wptr+=nbytes; + en=update_energy((int16_t*)om->b_rptr,om->b_wptr-om->b_rptr,en); + volume_agc_process(v,om); + + if (v->peer){ + volume_echo_avoider_process(v); + } + if (v->gain!=1){ + apply_gain(om,v->gain); + } + ms_queue_put(f->outputs[0],om); } - if (v->gain!=1){ - for ( sample=(int16_t*)m->b_rptr; - sample<(int16_t*)m->b_wptr; - ++sample){ - float s=*sample; - *sample=saturate(s*v->gain); + }else{ + /*light processing: no agc. Work in place in the input buffer*/ + while((m=ms_queue_get(f->inputs[0]))!=NULL){ + en=update_energy((int16_t*)m->b_rptr,m->b_wptr-m->b_rptr,en); + if (v->peer){ + volume_echo_avoider_process(v); + } + if (v->gain!=1){ + apply_gain(m,v->gain); } + ms_queue_put(f->outputs[0],m); } - ms_queue_put(f->outputs[0],m); } v->energy=en; } @@ -199,19 +304,22 @@ static MSFilterMethod methods[]={ { MS_VOLUME_SET_EA_THRESHOLD , volume_set_ea_threshold }, { MS_VOLUME_SET_EA_SPEED , volume_set_ea_speed }, { MS_VOLUME_SET_EA_FORCE , volume_set_ea_force }, + { MS_FILTER_SET_SAMPLE_RATE, volume_set_sample_rate }, + { MS_VOLUME_ENABLE_AGC , volume_set_agc }, { 0 , NULL } }; #ifndef _MSC_VER MSFilterDesc ms_volume_desc={ .name="MSVolume", - .text=N_("A filter to make level measurements on 16 bits pcm audio stream"), + .text=N_("A filter that controls and measure sound volume"), .id=MS_VOLUME_ID, .category=MS_FILTER_OTHER, .ninputs=1, .noutputs=1, .init=volume_init, .uninit=volume_uninit, + .preprocess=volume_preprocess, .process=volume_process, .methods=methods }; @@ -219,7 +327,7 @@ MSFilterDesc ms_volume_desc={ MSFilterDesc ms_volume_desc={ MS_VOLUME_ID, "MSVolume", - N_("A filter to make level measurements on 16 bits pcm audio stream"), + N_("A filter that controls and measure sound volume"), MS_FILTER_OTHER, NULL, 1, diff --git a/linphone/mediastreamer2/tests/mediastream.c b/linphone/mediastreamer2/tests/mediastream.c index 4edd0e01a516d1a65b0b499153e3bc8c68a4b62c..065113fba52fdd4f9306a553754b73e39d8d62f9 100644 --- a/linphone/mediastreamer2/tests/mediastream.c +++ b/linphone/mediastreamer2/tests/mediastream.c @@ -122,8 +122,10 @@ const char *usage="mediastream --local <port> --remote <ip:port> --payload <payl "[ --jitter <miliseconds>]\n" "[ --width <pixels>]\n" "[ --height <pixels> ]\n" - "[ --bitrate <bits per seconds>]\n"; -static void run_media_streams(int localport, const char *remote_ip, int remoteport, int payload, const char *fmtp, int jitter, bool_t ec, int bitrate, MSVideoSize vs); + "[ --bitrate <bits per seconds>]\n" + "[ --ec (enable echo canceller)]\n" + "[ --agc (enable automatic gain control)]\n"; +static void run_media_streams(int localport, const char *remote_ip, int remoteport, int payload, const char *fmtp, int jitter, bool_t ec, int bitrate, MSVideoSize vs, bool_t agc); int main(int argc, char * argv[]) @@ -136,6 +138,7 @@ int main(int argc, char * argv[]) int bitrate=0; MSVideoSize vs; bool_t ec=FALSE; + bool_t agc=FALSE; /*create the rtp session */ ortp_init(); ortp_set_log_level_mask(ORTP_MESSAGE|ORTP_WARNING|ORTP_ERROR|ORTP_FATAL); @@ -188,14 +191,16 @@ int main(int argc, char * argv[]) vs.height=atoi(argv[i]); }else if (strcmp(argv[i],"--ec")==0){ ec=TRUE; + }else if (strcmp(argv[i],"--agc")==0){ + agc=TRUE; } } - run_media_streams(localport,ip,remoteport,payload,fmtp,jitter,ec,bitrate,vs); + run_media_streams(localport,ip,remoteport,payload,fmtp,jitter,ec,bitrate,vs, agc); return 0; } -void run_media_streams(int localport, const char *remote_ip, int remoteport, int payload, const char *fmtp, int jitter, bool_t ec, int bitrate, MSVideoSize vs) +void run_media_streams(int localport, const char *remote_ip, int remoteport, int payload, const char *fmtp, int jitter, bool_t ec, int bitrate, MSVideoSize vs, bool_t agc) { AudioStream *audio=NULL; #ifdef VIDEO_ENABLED @@ -218,7 +223,13 @@ void run_media_streams(int localport, const char *remote_ip, int remoteport, in if (pt->type!=PAYLOAD_VIDEO){ printf("Starting audio stream.\n"); - audio=audio_stream_start(profile,localport,remote_ip,remoteport,payload,jitter, ec); + MSSndCardManager *manager=ms_snd_card_manager_get(); + audio=audio_stream_new(localport,ms_is_ipv6(remote_ip)); + audio_stream_enable_automatic_gain_control(audio,agc); + audio_stream_start_now(audio,profile,remote_ip,remoteport,remoteport+1,payload,jitter, + ms_snd_card_manager_get_default_playback_card(manager), + ms_snd_card_manager_get_default_capture_card(manager), + ec); if (audio) session=audio->session; }else{ #ifdef VIDEO_ENABLED