Index: /pjproject/trunk/pjmedia/include/pjmedia/types.h
===================================================================
--- /pjproject/trunk/pjmedia/include/pjmedia/types.h	(revision 1826)
+++ /pjproject/trunk/pjmedia/include/pjmedia/types.h	(revision 1827)
@@ -191,5 +191,5 @@
 {
 #if 1
-    pj_bzero(samples, count*sizeof(pj_int16_t));
+    pj_bzero(samples, (count<<1));
 #elif 0
     unsigned i;
@@ -213,5 +213,5 @@
 {
 #if 1
-    pj_memcpy(dst, src, count*sizeof(pj_int16_t));
+    pj_memcpy(dst, src, (count<<1));
 #elif 0
     unsigned i;
@@ -227,4 +227,26 @@
 
 /**
+ * This is a general purpose function to copy samples from/to buffers with
+ * equal size. Since this function is needed by many parts of the library, 
+ * by putting this functionality in one place, it enables some.
+ * clever people to optimize this function.
+ */
+PJ_INLINE(void) pjmedia_move_samples(pj_int16_t *dst, const pj_int16_t *src,
+				     unsigned count)
+{
+#if 1
+    pj_memmove(dst, src, (count<<1));
+#elif 0
+    unsigned i;
+    for (i=0; i<count; ++i) dst[i] = src[i];
+#else
+    unsigned i;
+    count >>= 1;
+    for (i=0; i<count; ++i) 
+	((pj_int32_t*)dst)[i] = ((pj_int32_t*)src)[i];
+#endif
+}
+
+/**
  * @}
  */
Index: /pjproject/trunk/pjmedia/include/pjmedia/wsola.h
===================================================================
--- /pjproject/trunk/pjmedia/include/pjmedia/wsola.h	(revision 1826)
+++ /pjproject/trunk/pjmedia/include/pjmedia/wsola.h	(revision 1827)
@@ -59,5 +59,11 @@
      * Specify that the WSOLA will not be used for PLC.
      */
-    PJMEDIA_WSOLA_NO_PLC = 2
+    PJMEDIA_WSOLA_NO_PLC = 2,
+
+    /**
+     * Specify that the WSOLA will not be used to discard frames in
+     * non-contiguous buffer.
+     */
+    PJMEDIA_WSOLA_NO_DISCARD = 4
 };
 
@@ -130,11 +136,15 @@
 /**
  * Compress or compact the specified buffer by removing some audio samples
- * from the buffer, without altering the pitch.
+ * from the buffer, without altering the pitch. For this function to work, 
+ * total length of the buffer must be more than twice \a erase_cnt.
  * 
  * @param wsola	    WSOLA session.
- * @param buf	    Pointer to buffer. For this function to work, the buffer
- *		    must contain more than twice \a erase_cnt number of 
- *		    samples.
- * @param buf_cnt   Number of samples in the buffer.
+ * @param buf1	    Pointer to buffer. 
+ * @param buf1_cnt  Number of samples in the buffer.
+ * @param buf2	    Pointer to second buffer, if the buffer is not
+ *		    contiguous. Otherwise this parameter must be NULL.
+ * @param buf2_cnt  Number of samples in the second buffer, if the buffer
+ *		    is not contiguous. Otherwise this parameter should be
+ *		    zero.
  * @param erase_cnt On input, specify the number of samples to be erased.
  *		    This function may erase more or less than the requested 
@@ -147,6 +157,8 @@
  */
 PJ_DECL(pj_status_t) pjmedia_wsola_discard(pjmedia_wsola *wsola, 
-					   short buf[],
-					   unsigned buf_cnt, 
+					   short buf1[],
+					   unsigned buf1_cnt, 
+					   short buf2[],
+					   unsigned buf2_cnt,
 					   unsigned *erase_cnt);
 
Index: /pjproject/trunk/pjmedia/src/pjmedia/wsola.c
===================================================================
--- /pjproject/trunk/pjmedia/src/pjmedia/wsola.c	(revision 1826)
+++ /pjproject/trunk/pjmedia/src/pjmedia/wsola.c	(revision 1827)
@@ -39,4 +39,7 @@
 #define GEN_EXTRA_PTIME	(0.0)
 
+/* Number of frames in erase buffer */
+#define ERASE_CNT	((unsigned)3)
+
 
 #ifndef M_PI
@@ -67,4 +70,5 @@
     short    *frm;	        /* Pointer to next frame to play in buf */
     short    *mergebuf;	        /* Temporary merge buffer.		*/
+    short    *erasebuf;		/* Temporary erase buffer.		*/
 #if defined(PJ_HAS_FLOATING_POINT) && PJ_HAS_FLOATING_POINT!=0
     float    *hanning;		/* Hanning window.			*/
@@ -330,4 +334,10 @@
     }
 
+    if ((options & PJMEDIA_WSOLA_NO_DISCARD) == 0) {
+	wsola->erasebuf = (short*)pj_pool_calloc(pool, samples_per_frame *
+						       ERASE_CNT,
+						 sizeof(short));
+    }
+
     *p_wsola = wsola;
     return PJ_SUCCESS;
@@ -366,8 +376,8 @@
 
 	dist = wsola->frm - start;
-	memmove(wsola->frm + frmsz, start + frmsz, 
-		(wsola->buf+wsola->cur_cnt - (start+frmsz)) << 1);
-
-	memcpy(wsola->frm, wsola->mergebuf, frmsz << 1);
+	pjmedia_move_samples(wsola->frm + frmsz, start + frmsz, 
+			     wsola->buf+wsola->cur_cnt - (start+frmsz));
+
+	pjmedia_copy_samples(wsola->frm, wsola->mergebuf, frmsz);
 
 	wsola->cur_cnt = (pj_uint16_t)(wsola->cur_cnt + dist);
@@ -385,5 +395,5 @@
 
 static unsigned compress(pjmedia_wsola *wsola, short *buf, unsigned count,
-			 unsigned erase_cnt)
+			 unsigned del_cnt)
 {
     unsigned samples_del = 0, rep;
@@ -394,5 +404,5 @@
 	unsigned dist;
 
-	if (count <= (erase_cnt << 1)) {
+	if (count <= (del_cnt << 1)) {
 	    TRACE_((THIS_FILE, "Not enough samples to compress!"));
 	    return samples_del;
@@ -415,14 +425,14 @@
 	}
 
-	memmove(buf + frmsz, buf + frmsz + dist,
-		(count - frmsz - dist) * 2);
+	pjmedia_move_samples(buf + frmsz, buf + frmsz + dist,
+			     count - frmsz - dist);
 
 	count -= dist;
 	samples_del += dist;
 
-	if (samples_del >= erase_cnt) {
+	if (samples_del >= del_cnt) {
 	    TRACE_((THIS_FILE, 
 		    "Erased %d of %d requested after %d iteration(s)",
-		    samples_del, erase_cnt, rep));
+		    samples_del, del_cnt, rep));
 	    break;
 	}
@@ -453,9 +463,9 @@
 
 	
-	memcpy(frm, wsola->frm, wsola->samples_per_frame << 1);
+	pjmedia_copy_samples(frm, wsola->frm, wsola->samples_per_frame);
 	wsola->cur_cnt = (pj_uint16_t)(wsola->hist_cnt + 
 				       wsola->samples_per_frame);
-	memmove(wsola->buf, wsola->buf+wsola->samples_per_frame, 
-		wsola->cur_cnt << 1);
+	pjmedia_move_samples(wsola->buf, wsola->buf+wsola->samples_per_frame,
+			      wsola->cur_cnt);
 
     } else {
@@ -466,10 +476,10 @@
 	}
 
-	memcpy(wsola->buf + wsola->cur_cnt, frm, 
-	       wsola->samples_per_frame << 1);
-	memcpy(frm, wsola->frm, 
-	       wsola->samples_per_frame << 1);
-	memmove(wsola->buf, wsola->buf+wsola->samples_per_frame, 
-		wsola->cur_cnt << 1);
+	pjmedia_copy_samples(wsola->buf + wsola->cur_cnt, frm, 
+			     wsola->samples_per_frame);
+	pjmedia_copy_samples(frm, wsola->frm, 
+			     wsola->samples_per_frame);
+	pjmedia_move_samples(wsola->buf, wsola->buf+wsola->samples_per_frame,
+			     wsola->cur_cnt);
     }
     
@@ -493,7 +503,7 @@
 	 * rather than generating a new one.
 	 */
-	memcpy(frm, wsola->frm, wsola->samples_per_frame << 1);
-	memmove(wsola->buf, wsola->buf+wsola->samples_per_frame, 
-	        (wsola->cur_cnt - wsola->samples_per_frame) << 1);
+	pjmedia_copy_samples(frm, wsola->frm, wsola->samples_per_frame);
+	pjmedia_move_samples(wsola->buf, wsola->buf+wsola->samples_per_frame,
+			     wsola->cur_cnt - wsola->samples_per_frame);
 
 	pj_assert(wsola->cur_cnt >= 
@@ -512,7 +522,7 @@
 	expand(wsola, new_samples);
 
-	memcpy(frm, wsola->frm, wsola->samples_per_frame << 1);
-	memmove(wsola->buf, wsola->buf+wsola->samples_per_frame, 
-		(wsola->cur_cnt - wsola->samples_per_frame) << 1);
+	pjmedia_copy_samples(frm, wsola->frm, wsola->samples_per_frame);
+	pjmedia_move_samples(wsola->buf, wsola->buf+wsola->samples_per_frame,
+			     wsola->cur_cnt - wsola->samples_per_frame);
 
 	pj_assert(wsola->cur_cnt >= 
@@ -531,14 +541,79 @@
 
 PJ_DEF(pj_status_t) pjmedia_wsola_discard( pjmedia_wsola *wsola, 
-					   short buf[],
-					   unsigned buf_cnt, 
-					   unsigned *erase_cnt)
-{
-    PJ_ASSERT_RETURN(wsola && buf && buf_cnt && erase_cnt, PJ_EINVAL);
-    PJ_ASSERT_RETURN(*erase_cnt, PJ_EINVAL);
-
-    *erase_cnt = compress(wsola, buf, buf_cnt, *erase_cnt);
-    return (*erase_cnt) > 0 ? PJ_SUCCESS : PJ_ETOOSMALL;
-}
-
-
+					   short buf1[],
+					   unsigned buf1_cnt, 
+					   short buf2[],
+					   unsigned buf2_cnt,
+					   unsigned *del_cnt)
+{
+    PJ_ASSERT_RETURN(wsola && buf1 && buf1_cnt && del_cnt, PJ_EINVAL);
+    PJ_ASSERT_RETURN(*del_cnt, PJ_EINVAL);
+
+    if (buf2_cnt == 0) {
+	/* Buffer is contiguous space, no need to use temporary
+	 * buffer.
+	 */
+	*del_cnt = compress(wsola, buf1, buf1_cnt, *del_cnt);
+
+    } else {
+	PJ_ASSERT_RETURN(buf2, PJ_EINVAL);
+
+	if (buf1_cnt < ERASE_CNT * wsola->samples_per_frame &&
+	    buf2_cnt < ERASE_CNT * wsola->samples_per_frame &&
+	    wsola->erasebuf == NULL)
+	{
+	    /* We need erasebuf but WSOLA was created with 
+	     * PJMEDIA_WSOLA_NO_DISCARD flag.
+	     */
+	    pj_assert(!"WSOLA need erase buffer!");
+	    return PJ_EINVALIDOP;
+	}
+
+	if (buf2_cnt >= ERASE_CNT * wsola->samples_per_frame) {
+	    *del_cnt = compress(wsola, buf2, buf2_cnt, *del_cnt);
+	} else if (buf1_cnt >= ERASE_CNT * wsola->samples_per_frame) {
+	    unsigned max;
+
+	    *del_cnt = compress(wsola, buf1, buf1_cnt, *del_cnt);
+
+	    max = *del_cnt;
+	    if (max > buf2_cnt)
+		max = buf2_cnt;
+
+	    pjmedia_move_samples(buf1+buf1_cnt-(*del_cnt), buf2, 
+				 max);
+	    if (max < buf2_cnt) {
+		pjmedia_move_samples(buf2, buf2+(*del_cnt), 
+				     buf2_cnt-max);
+	    }
+
+	} else {
+	    unsigned buf_cnt = buf1_cnt + buf2_cnt;
+	    unsigned max;
+
+	    if (buf_cnt > ERASE_CNT * wsola->samples_per_frame)
+		buf_cnt = ERASE_CNT * wsola->samples_per_frame;
+
+	    pjmedia_copy_samples(wsola->erasebuf, buf1, buf1_cnt);
+	    pjmedia_copy_samples(wsola->erasebuf+buf1_cnt, buf2, 
+				 buf_cnt-buf1_cnt);
+
+	    *del_cnt = compress(wsola, wsola->erasebuf, buf_cnt, *del_cnt);
+
+	    buf_cnt -= (*del_cnt);
+
+	    max = buf_cnt;
+	    if (max > buf1_cnt)
+		max = buf1_cnt;
+	    pjmedia_copy_samples(buf1, wsola->erasebuf, max);
+
+	    if (max < buf_cnt) {
+		pjmedia_copy_samples(buf2, wsola->erasebuf+max, buf_cnt-max);
+	    }
+	}
+    }
+
+    return (*del_cnt) > 0 ? PJ_SUCCESS : PJ_ETOOSMALL;
+}
+
+
