diff --git a/linphone/coreapi/linphonecore.c b/linphone/coreapi/linphonecore.c
index 2f9803f25..1f5b91015 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 8334d9bd8..ab7b5641f 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 23f8b7837..5068dc8f6 100644
--- a/linphone/linphone.kdevelop
+++ b/linphone/linphone.kdevelop
@@ -8,10 +8,24 @@
C
linphone
+ .
+ false
+
+
executable
+ /home/smorlat/sources/git/linphone/linphone
+
+
+ /home/smorlat/sources/git/linphone/linphone
+ false
+ false
+ false
+ false
+ false
+
*.java
@@ -374,11 +388,51 @@
win32acm/wineacm.h
win32acm/wrapper.h
+
+ make
+
+
+
+ 0
+
+
+
+ default
+
+
+
+
+
+ true
+ 0
+ 0
+ false
+
+
+
+ default
+
+
+
+
-
+
+
+
+
+
+ true
+ false
+ false
+ false
+
+ false
+ true
+ 10
+
@@ -470,6 +524,19 @@
false
.;
+
+
+ set
+ m_,_
+ theValue
+ true
+ true
+
+
+ false
+ true
+ Vertical
+
@@ -481,4 +548,10 @@
false
+
+
+ .h
+ .cpp
+
+
diff --git a/linphone/mediastreamer2/include/mediastreamer2/mediastream.h b/linphone/mediastreamer2/include/mediastreamer2/mediastream.h
index e41f939da..61d7dab97 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 1bb208dc7..14574eecd 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 bb0edda7c..516d64980 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 3de030e5e..a816bc89c 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
+#ifdef HAVE_SPEEXDSP
+#include
+#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 void volume_process(MSFilter *f){
- mblk_t *m;
+static float update_energy(int16_t *signal, int numsamples, float last_energy_value){
+ int i;
+ float en=last_energy_value;
+ for (i=0;idata;
- 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->gain!=1){
- for ( sample=(int16_t*)m->b_rptr;
+ for ( sample=(int16_t*)m->b_rptr;
sample<(int16_t*)m->b_wptr;
++sample){
- float s=*sample;
- *sample=saturate(s*v->gain);
+ 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;
+ Volume *v=(Volume*)f->data;
+ float en=v->energy;
+
+ 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);
+ }
+ }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 4edd0e01a..065113fba 100644
--- a/linphone/mediastreamer2/tests/mediastream.c
+++ b/linphone/mediastreamer2/tests/mediastream.c
@@ -122,8 +122,10 @@ const char *usage="mediastream --local --remote --payload ]\n"
"[ --width ]\n"
"[ --height ]\n"
- "[ --bitrate ]\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 ]\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