Index: pjproject/trunk/pjmedia/src/pjmedia/echo_speex.c
===================================================================
--- pjproject/trunk/pjmedia/src/pjmedia/echo_speex.c	(revision 4981)
+++ pjproject/trunk/pjmedia/src/pjmedia/echo_speex.c	(revision 6129)
@@ -36,8 +36,10 @@
 {
     SpeexEchoState	 *state;
-    SpeexPreprocessState *preprocess;
+    SpeexDecorrState 	 *decorr;
+    SpeexPreprocessState **preprocess;
 
     unsigned		  samples_per_frame;
-    unsigned		  prefetch;
+    unsigned		  channel_count;
+    unsigned		  spf_per_channel;
     unsigned		  options;
     pj_int16_t		 *tmp_frame;
@@ -58,5 +60,5 @@
 {
     speex_ec *echo;
-    int sampling_rate;
+    int i, sampling_rate;
 
     *p_echo = NULL;
@@ -65,9 +67,11 @@
     PJ_ASSERT_RETURN(echo != NULL, PJ_ENOMEM);
 
+    echo->channel_count = channel_count;
     echo->samples_per_frame = samples_per_frame;
+    echo->spf_per_channel = samples_per_frame / channel_count;
     echo->options = options;
 
-#if 0
-    echo->state = speex_echo_state_init_mc(echo->samples_per_frame,
+#if 1
+    echo->state = speex_echo_state_init_mc(echo->spf_per_channel,
 					   clock_rate * tail_ms / 1000,
 					   channel_count, channel_count);
@@ -80,7 +84,11 @@
     					clock_rate * tail_ms / 1000);
 #endif
-    if (echo->state == NULL) {
+    if (echo->state == NULL)
 	return PJ_ENOMEM;
-    }
+
+    echo->decorr = speex_decorrelate_new(clock_rate, channel_count,
+    					 echo->spf_per_channel);
+    if (echo->decorr == NULL)
+	return PJ_ENOMEM;
 
     /* Set sampling rate */
@@ -89,46 +97,48 @@
 		   &sampling_rate);
 
-    echo->preprocess = speex_preprocess_state_init(echo->samples_per_frame,
-						   clock_rate);
-    if (echo->preprocess == NULL) {
-	speex_echo_state_destroy(echo->state);
+    /* We need to create one state per channel processed. */
+    echo->preprocess = PJ_POOL_ZALLOC_T(pool, SpeexPreprocessState *);
+    for (i = 0; i < channel_count; i++) {
+	spx_int32_t enabled;
+
+    	echo->preprocess[i] = speex_preprocess_state_init(
+    				  echo->spf_per_channel, clock_rate);
+    	if (echo->preprocess[i] == NULL) {
+    	    speex_aec_destroy(echo);
+	    return PJ_ENOMEM;
+	}
+
+    	/* Enable/disable AGC & denoise */
+	enabled = PJMEDIA_SPEEX_AEC_USE_AGC;
+	speex_preprocess_ctl(echo->preprocess[i], SPEEX_PREPROCESS_SET_AGC,
+			     &enabled);
+
+	enabled = PJMEDIA_SPEEX_AEC_USE_DENOISE;
+	speex_preprocess_ctl(echo->preprocess[i],
+			     SPEEX_PREPROCESS_SET_DENOISE, &enabled);
+
+    	/* Currently, VAD and dereverb are set at default setting. */
+    	/*
+    	enabled = 1;
+    	speex_preprocess_ctl(echo->preprocess[i], SPEEX_PREPROCESS_SET_VAD,
+			     &enabled);
+    	speex_preprocess_ctl(echo->preprocess[i],
+    			     SPEEX_PREPROCESS_SET_DEREVERB,
+			     &enabled);
+	*/
+
+    	/* Control echo cancellation in the preprocessor */
+   	speex_preprocess_ctl(echo->preprocess[i],
+   			     SPEEX_PREPROCESS_SET_ECHO_STATE, echo->state);
+    }
+
+    /* Create temporary frame for echo cancellation */
+    echo->tmp_frame = (pj_int16_t*) pj_pool_zalloc(pool, sizeof(pj_int16_t) *
+    						   channel_count *
+    						   samples_per_frame);
+    if (!echo->tmp_frame) {
+    	speex_aec_destroy(echo);
 	return PJ_ENOMEM;
     }
-
-    /* Disable all preprocessing, we only want echo cancellation */
-#if 0
-    disabled = 0;
-    enabled = 1;
-    speex_preprocess_ctl(echo->preprocess, SPEEX_PREPROCESS_SET_DENOISE, 
-			 &enabled);
-    speex_preprocess_ctl(echo->preprocess, SPEEX_PREPROCESS_SET_AGC, 
-			 &disabled);
-    speex_preprocess_ctl(echo->preprocess, SPEEX_PREPROCESS_SET_VAD, 
-			 &disabled);
-    speex_preprocess_ctl(echo->preprocess, SPEEX_PREPROCESS_SET_DEREVERB, 
-			 &enabled);
-#endif
-
-    /* Enable/disable AGC & denoise */
-    {
-	spx_int32_t enabled;
-
-	enabled = PJMEDIA_SPEEX_AEC_USE_AGC;
-	speex_preprocess_ctl(echo->preprocess, SPEEX_PREPROCESS_SET_AGC, 
-			     &enabled);
-
-	enabled = PJMEDIA_SPEEX_AEC_USE_DENOISE;
-	speex_preprocess_ctl(echo->preprocess, SPEEX_PREPROCESS_SET_DENOISE, 
-			     &enabled);
-    }
-
-    /* Control echo cancellation in the preprocessor */
-   speex_preprocess_ctl(echo->preprocess, SPEEX_PREPROCESS_SET_ECHO_STATE, 
-			echo->state);
-
-
-    /* Create temporary frame for echo cancellation */
-    echo->tmp_frame = (pj_int16_t*) pj_pool_zalloc(pool, 2*samples_per_frame);
-    PJ_ASSERT_RETURN(echo->tmp_frame != NULL, PJ_ENOMEM);
 
     /* Done */
@@ -145,4 +155,5 @@
 {
     speex_ec *echo = (speex_ec*) state;
+    unsigned i;
 
     PJ_ASSERT_RETURN(echo && echo->state, PJ_EINVAL);
@@ -153,6 +164,16 @@
     }
 
+    if (echo->decorr) {
+	speex_decorrelate_destroy(echo->decorr);
+	echo->decorr = NULL;
+    }
+
     if (echo->preprocess) {
-	speex_preprocess_state_destroy(echo->preprocess);
+        for (i = 0; i < echo->channel_count; i++) {
+	    if (echo->preprocess[i]) {
+	    	speex_preprocess_state_destroy(echo->preprocess[i]);
+	    	echo->preprocess[i] = NULL;
+	    }
+	}
 	echo->preprocess = NULL;
     }
@@ -182,4 +203,5 @@
 {
     speex_ec *echo = (speex_ec*) state;
+    unsigned i;
 
     /* Sanity checks */
@@ -193,6 +215,25 @@
 
 
-    /* Preprocess output */
-    speex_preprocess_run(echo->preprocess, (spx_int16_t*)echo->tmp_frame);
+    /* Preprocess output per channel */
+    for (i = 0; i < echo->channel_count; i++) {
+    	spx_int16_t *buf = (spx_int16_t*)echo->tmp_frame;
+    	unsigned j;
+
+        /* De-interleave each channel. */
+        if (echo->channel_count > 1) {
+    	    for (j = 0; j < echo->spf_per_channel; j++) {
+    	        rec_frm[j] = echo->tmp_frame[j * echo->channel_count + i];
+    	    }
+    	    buf = (spx_int16_t*)rec_frm;
+    	}
+
+	speex_preprocess_run(echo->preprocess[i], buf);
+
+        if (echo->channel_count > 1) {
+    	    for (j = 0; j < echo->spf_per_channel; j++) {
+    	        echo->tmp_frame[j * echo->channel_count + i] = rec_frm[j];
+    	    }
+    	}
+    }
 
     /* Copy temporary buffer back to original rec_frm */
@@ -213,4 +254,13 @@
     /* Sanity checks */
     PJ_ASSERT_RETURN(echo && play_frm, PJ_EINVAL);
+
+    /* Channel decorrelation algorithm is useful for multi-channel echo
+     * cancellation only .
+     */
+    if (echo->channel_count > 1) {
+    	pjmedia_copy_samples(echo->tmp_frame, play_frm, echo->samples_per_frame);
+    	speex_decorrelate(echo->decorr, (spx_int16_t*)echo->tmp_frame,
+    		      	  (spx_int16_t*)play_frm, 100);
+    }
 
     speex_echo_playback(echo->state, (spx_int16_t*)play_frm);
@@ -228,4 +278,5 @@
 {
     speex_ec *echo = (speex_ec*) state;
+    unsigned i;
 
     /* Sanity checks */
@@ -235,11 +286,31 @@
 
     /* Cancel echo */
-    pjmedia_copy_samples(echo->tmp_frame, rec_frm, echo->samples_per_frame);
     speex_echo_capture(echo->state,
-		       (spx_int16_t*)echo->tmp_frame,
-		       (spx_int16_t*)rec_frm);
-
-    /* Apply preprocessing */
-    speex_preprocess_run(echo->preprocess, (spx_int16_t*)rec_frm);
+		       (spx_int16_t*)rec_frm,
+		       (spx_int16_t*)echo->tmp_frame);
+
+    /* Apply preprocessing per channel. */
+    for (i = 0; i < echo->channel_count; i++) {
+    	spx_int16_t *buf = (spx_int16_t*)echo->tmp_frame;
+    	unsigned j;
+
+        /* De-interleave each channel. */
+        if (echo->channel_count > 1) {
+    	    for (j = 0; j < echo->spf_per_channel; j++) {
+    	        rec_frm[j] = echo->tmp_frame[j * echo->channel_count + i];
+    	    }
+    	    buf = (spx_int16_t*)rec_frm;
+    	}
+
+	speex_preprocess_run(echo->preprocess[i], buf);
+
+        if (echo->channel_count > 1) {
+    	    for (j = 0; j < echo->spf_per_channel; j++) {
+    	        echo->tmp_frame[j * echo->channel_count + i] = rec_frm[j];
+    	    }
+    	}
+    }
+
+    pjmedia_copy_samples(rec_frm, echo->tmp_frame, echo->samples_per_frame);
 
     return PJ_SUCCESS;
