Index: /pjproject/trunk/pjmedia/include/pjmedia/wsola.h
===================================================================
--- /pjproject/trunk/pjmedia/include/pjmedia/wsola.h	(revision 1843)
+++ /pjproject/trunk/pjmedia/include/pjmedia/wsola.h	(revision 1844)
@@ -76,4 +76,5 @@
  * @param clock_rate	    Sampling rate of audio playback.
  * @param samples_per_frame Number of samples per frame.
+ * @param channel_count	    Number of channels.
  * @param options	    Option flags, bitmask combination of
  *			    #pjmedia_wsola_option.
@@ -85,4 +86,5 @@
 					  unsigned clock_rate,
 					  unsigned samples_per_frame,
+					  unsigned channel_count,
 					  unsigned options,
 					  pjmedia_wsola **p_wsola);
Index: /pjproject/trunk/pjmedia/src/pjmedia/delaybuf.c
===================================================================
--- /pjproject/trunk/pjmedia/src/pjmedia/delaybuf.c	(revision 1843)
+++ /pjproject/trunk/pjmedia/src/pjmedia/delaybuf.c	(revision 1844)
@@ -158,5 +158,5 @@
     }
 
-    status = pjmedia_wsola_create(pool, clock_rate, samples_per_frame, 
+    status = pjmedia_wsola_create(pool, clock_rate, samples_per_frame, 1,
 				  PJMEDIA_WSOLA_NO_PLC, &b->wsola);
     if (status != PJ_SUCCESS)
Index: /pjproject/trunk/pjmedia/src/pjmedia/splitcomb.c
===================================================================
--- /pjproject/trunk/pjmedia/src/pjmedia/splitcomb.c	(revision 1843)
+++ /pjproject/trunk/pjmedia/src/pjmedia/splitcomb.c	(revision 1844)
@@ -49,27 +49,31 @@
 #define OP_GET		    (-1)
 
-/* Media flow directions */
+
+/* 
+ * Media flow directions:
+ *
+ *             put_frame() +-----+
+ *  UPSTREAM  ------------>|split|<--> DOWNSTREAM
+ *            <------------|comb |
+ *             get_frame() +-----+
+ *
+ */
 enum sc_dir
 {
-    /* This is the direction from the splitcomb to the downstream
-     * port(s), or when put_frame() is called to the splitcomb.
+    /* This is the media direction from the splitcomb to the 
+     * downstream port(s), which happens when:
+     *  - put_frame() is called to the splitcomb
+     *  - get_frame() is called to the reverse channel port.
      */
     DIR_DOWNSTREAM,
 
-    /* This is the direction from the downstream port to the splitcomb,
-     * or when get_frame() is called to the splitcomb.
+    /* This is the media direction from the downstream port to 
+     * the splitcomb, which happens when:
+     *  - get_frame() is called to the splitcomb
+     *  - put_frame() is called to the reverse channel port.
      */
     DIR_UPSTREAM
 };
 
-
-
-#if 0
-#   define TRACE_UP_(x)	PJ_LOG(5,x)
-#   define TRACE_DN_(x)	PJ_LOG(5,x)
-#else
-#   define TRACE_UP_(x)
-#   define TRACE_DN_(x)
-#endif
 
 
@@ -107,11 +111,35 @@
     unsigned	     ch_num;
 
-    /* Maximum burst before media flow is suspended */
+    /* Maximum burst before media flow is suspended.
+     * With reverse port, it's possible that either end of the 
+     * port doesn't actually process the media flow (meaning, it
+     * stops calling get_frame()/put_frame()). When this happens,
+     * the other end will encounter excessive underflow or overflow,
+     * depending on which direction is not actively processed by
+     * the stopping end.
+     *
+     * To avoid excessive underflow/overflow, the media flow will
+     * be suspended once underflow/overflow goes over this max_burst
+     * limit.
+     */
     int		     max_burst;
 
-    /* Maximum NULL frames received before media flow is suspended. */
+    /* When the media interface port of the splitcomb or the reverse
+     * channel port is registered to conference bridge, the bridge
+     * will transmit NULL frames to the media port when the media
+     * port is not receiving any audio from other slots (for example,
+     * when no other slots are connected to the media port).
+     *
+     * When this happens, we will generate zero frame to our buffer,
+     * to avoid underflow/overflow. But after too many NULL frames
+     * are received, we will pause the media flow instead, to save
+     * some processing.
+     *
+     * This value controls how many NULL frames can be received
+     * before we suspend media flow for a particular direction.
+     */
     unsigned	     max_null_frames;
 
-    /* A reverse port need a temporary buffer to store frame
+    /* A reverse port need a temporary buffer to store frames
      * (because of the different phase, see splitcomb.h for details). 
      * Since we can not expect get_frame() and put_frame() to be
@@ -314,5 +342,5 @@
     port->on_destroy = &rport_on_destroy;
 
-
+    /* Buffer settings */
     buf_cnt = options & 0xFF;
     if (buf_cnt == 0)
@@ -323,5 +351,5 @@
 
     /* Create downstream/put buffers */
-    status = pjmedia_delay_buf_create(pool, "scomb-down",
+    status = pjmedia_delay_buf_create(pool, "scombdb-dn",
 				      port->info.clock_rate,
 				      port->info.samples_per_frame,
@@ -333,5 +361,5 @@
 
     /* Create upstream/get buffers */
-    status = pjmedia_delay_buf_create(pool, "scomb-up",
+    status = pjmedia_delay_buf_create(pool, "scombdb-up",
 				      port->info.clock_rate,
 				      port->info.samples_per_frame,
@@ -446,6 +474,7 @@
 
 /*
- * "Write" a multichannel frame. This would split the multichannel frame
- * into individual mono channel, and write it to the appropriate port.
+ * "Write" a multichannel frame downstream. This would split 
+ * the multichannel frame into individual mono channel, and write 
+ * it to the appropriate port.
  */
 static pj_status_t put_frame(pjmedia_port *this_port, 
@@ -466,11 +495,4 @@
 	    } else {
 		struct reverse_port *rport = (struct reverse_port*)port;
-
-		/* Write zero port to delaybuf so that it doesn't underflow. 
-		 * If we don't do this, get_frame() on this direction will
-		 * cause delaybuf to generate missing frame and the last
-		 * frame transmitted to delaybuf will be replayed multiple
-		 * times, which doesn't sound good.
-		 */
 
 		/* Update the number of NULL frames received. Once we have too
@@ -490,4 +512,11 @@
 		}
 
+		/* Write zero port to delaybuf so that it doesn't underflow. 
+		 * If we don't do this, get_frame() on this direction will
+		 * cause delaybuf to generate missing frame and the last
+		 * frame transmitted to delaybuf will be replayed multiple
+		 * times, which doesn't sound good.
+		 */
+
 		/* Update rport state. */
 		op_update(rport, DIR_DOWNSTREAM, OP_PUT);
@@ -498,10 +527,10 @@
 
 		/* Generate zero frame. */
-		pjmedia_zero_samples(rport->tmp_up_buf, 
+		pjmedia_zero_samples(sc->put_buf, 
 				     this_port->info.samples_per_frame);
 
 		/* Put frame to delay buffer */
 		pjmedia_delay_buf_put(rport->buf[DIR_DOWNSTREAM].dbuf,
-				      rport->tmp_up_buf);
+				      sc->put_buf);
 
 	    }
@@ -566,5 +595,5 @@
 
 /*
- * Get a multichannel frame.
+ * Get a multichannel frame upstream.
  * This will get mono channel frame from each port and put the
  * mono frame into the multichannel frame.
@@ -617,5 +646,5 @@
 	    } else {
 		pjmedia_zero_samples(sc->get_buf, 
-				      rport->base.info.samples_per_frame);
+				     port->info.samples_per_frame);
 	    }
 
@@ -628,6 +657,4 @@
 			 this_port->info.channel_count,
 			 this_port->info.samples_per_frame);
-
-
 
 	has_frame = PJ_TRUE;
@@ -647,5 +674,7 @@
 static pj_status_t on_destroy(pjmedia_port *this_port)
 {
-    /* Nothing to do */
+    /* Nothing to do for the splitcomb
+     * Reverse ports must be destroyed separately.
+     */
     PJ_UNUSED_ARG(this_port);
 
@@ -655,5 +684,6 @@
 
 /*
- * Get a mono frame from a reversed phase channel.
+ * Put a frame in the reverse port (upstream direction). This frame
+ * will be picked up by get_frame() above.
  */
 static pj_status_t rport_put_frame(pjmedia_port *this_port, 
@@ -666,14 +696,4 @@
     /* Handle NULL frame */
     if (frame->type != PJMEDIA_FRAME_TYPE_AUDIO) {
-	TRACE_UP_((THIS_FILE, "Upstream write %d null samples at buf pos %d",
-		   this_port->info.samples_per_frame, rport->up_write_pos));
-
-	/* Write zero port to delaybuf so that it doesn't underflow. 
-	 * If we don't do this, get_frame() on this direction will
-	 * cause delaybuf to generate missing frame and the last
-	 * frame transmitted to delaybuf will be replayed multiple
-	 * times, which doesn't sound good.
-	 */
-
 	/* Update the number of NULL frames received. Once we have too
 	 * many of this, we'll stop calling op_update() to let the
@@ -688,4 +708,11 @@
 	}
 
+	/* Write zero port to delaybuf so that it doesn't underflow. 
+	 * If we don't do this, get_frame() on this direction will
+	 * cause delaybuf to generate missing frame and the last
+	 * frame transmitted to delaybuf will be replayed multiple
+	 * times, which doesn't sound good.
+	 */
+
 	/* Update rport state. */
 	op_update(rport, DIR_UPSTREAM, OP_PUT);
@@ -730,6 +757,6 @@
 
 
-/*
- * Get a mono frame from a reversed phase channel.
+/* Get a mono frame from a reversed phase channel (downstream direction).
+ * The frame is put by put_frame() call to the splitcomb.
  */
 static pj_status_t rport_get_frame(pjmedia_port *this_port, 
Index: /pjproject/trunk/pjmedia/src/pjmedia/wsola.c
===================================================================
--- /pjproject/trunk/pjmedia/src/pjmedia/wsola.c	(revision 1843)
+++ /pjproject/trunk/pjmedia/src/pjmedia/wsola.c	(revision 1844)
@@ -37,5 +37,5 @@
 
 /* Generate extra samples, in msec */
-#define GEN_EXTRA_PTIME	(0.0)
+#define GEN_EXTRA_PTIME	(5)
 
 /* Number of frames in erase buffer */
@@ -54,15 +54,41 @@
 
 
+/* Buffer content:
+ *
+ *   t0   time    tn
+ *   ---------------->
+ *
+ * +---------+-------+-------+---          --+
+ * | history | frame | extra | ...empty...   |
+ * +---------+-------+-------+----        ---+
+ * ^         ^               ^               ^
+ * buf    hist_cnt        cur_cnt          buf_cnt
+ *           or
+ *       frm pointer
+ *
+ * History count (hist_cnt) is a constant value, initialized upon 
+ * creation.
+ *
+ * At any particular time, the buffer will contain at least 
+ * (hist_cnt+samples_per_frame) samples.
+ *
+ * A "save" operation will append the frame to the end of the 
+ * buffer, return the samples right after history (the frm pointer),
+ * and shift the buffer by one frame.
+ */
+
+/* WSOLA structure */
 struct pjmedia_wsola
 {
     unsigned    clock_rate;	/* Sampling rate.			*/
-    pj_uint16_t samples_per_frame;/* Samples per frame.			*/
+    pj_uint16_t samples_per_frame;/* Samples per frame (const)		*/
+    pj_uint16_t channel_count;	/* Samples per frame (const)		*/
     pj_uint16_t options;	/* Options.				*/
-    pj_uint16_t hist_cnt;       /* # of history samples.		*/
-    pj_uint16_t buf_cnt;        /* Total buffer capacity		*/
+    pj_uint16_t hist_cnt;       /* # of history samples (const)		*/
+    pj_uint16_t buf_cnt;        /* Total buffer capacity (const)	*/
     pj_uint16_t cur_cnt;        /* Cur # of samples, inc. history	*/
-    pj_uint16_t template_size;	/* Template size.			*/
-    pj_uint16_t min_extra;	/* Min extra samples for merging.	*/
-    pj_uint16_t gen_extra;	/* Generate extra samples.		*/
+    pj_uint16_t template_size;	/* Template size (const)		*/
+    pj_uint16_t min_extra;	/* Min extra samples for merging (const)*/
+    pj_uint16_t gen_extra;	/* Generate extra samples (const)	*/
     pj_uint16_t expand_cnt;     /* Number of expansion currently done   */
 
@@ -124,5 +150,5 @@
     }
 
-    TRACE_((THIS_FILE, "found pitch at %u", best-beg));
+    /*TRACE_((THIS_FILE, "found pitch at %u", best-beg));*/
     return best;
 }
@@ -208,5 +234,5 @@
     }
 
-    TRACE_((THIS_FILE, "found pitch at %u", best-beg));
+    /*TRACE_((THIS_FILE, "found pitch at %u", best-beg));*/
     return best;
 }
@@ -293,4 +319,5 @@
 					  unsigned clock_rate,
 					  unsigned samples_per_frame,
+					  unsigned channel_count,
 					  unsigned options,
 					  pjmedia_wsola **p_wsola)
@@ -302,4 +329,5 @@
     PJ_ASSERT_RETURN(clock_rate <= 65535, PJ_EINVAL);
     PJ_ASSERT_RETURN(samples_per_frame < clock_rate, PJ_EINVAL);
+    PJ_ASSERT_RETURN(channel_count > 0, PJ_EINVAL);
 
     wsola = PJ_POOL_ZALLOC_T(pool, pjmedia_wsola);
@@ -307,4 +335,5 @@
     wsola->clock_rate= (pj_uint16_t) clock_rate;
     wsola->samples_per_frame = (pj_uint16_t) samples_per_frame;
+    wsola->channel_count = (pj_uint16_t) channel_count;
     wsola->options   = (pj_uint16_t) options;
     wsola->hist_cnt  = (pj_uint16_t)(samples_per_frame * HISTSZ);
@@ -360,7 +389,7 @@
     PJ_UNUSED_ARG(options);
 
-    pjmedia_zero_samples(wsola->buf, wsola->cur_cnt);
     wsola->cur_cnt = (pj_uint16_t)(wsola->hist_cnt + 
 				   wsola->samples_per_frame);
+    pjmedia_zero_samples(wsola->buf, wsola->cur_cnt);
     return PJ_SUCCESS;
 }
@@ -374,30 +403,45 @@
 
     for (rep=1;; ++rep) {
-	short *start;
-	unsigned dist;
-
-	start = find_pitch(wsola->frm, wsola->buf, 
-			   wsola->frm - (wsola->samples_per_frame >> 1),
+	short *start, *frm;
+	unsigned min_dist, max_dist, dist;
+
+	frm = wsola->buf + wsola->cur_cnt - frmsz;
+	pj_assert(frm - wsola->buf >= wsola->hist_cnt);
+
+	max_dist = wsola->hist_cnt;
+	min_dist = frmsz >> 1;
+
+	start = find_pitch(frm, frm - max_dist, frm - min_dist,
 			   wsola->template_size, 1);
 
+	/* Should we make sure that "start" is really aligned to
+	 * channel #0, in case of stereo? Probably not necessary, as
+	 * find_pitch() should have found the best match anyway.
+	 */
+
 	if (wsola->options & PJMEDIA_WSOLA_NO_HANNING) {
-	    overlapp_add_simple(wsola->mergebuf, wsola->samples_per_frame,
-				wsola->frm, start);
+	    overlapp_add_simple(wsola->mergebuf, frmsz,frm, start);
 	} else {
-	    overlapp_add(wsola->mergebuf, wsola->samples_per_frame,
-			 wsola->frm, start, wsola->hanning);
-	}
-
-	dist = wsola->frm - start;
-	pjmedia_move_samples(wsola->frm + frmsz, start + frmsz, 
+	    overlapp_add(wsola->mergebuf, frmsz, frm, start, wsola->hanning);
+	}
+
+	/* How many new samples do we have */
+	dist = frm - start;
+
+	/* Copy the "tail" (excess frame) to the end */
+	pjmedia_move_samples(frm + frmsz, start + frmsz, 
 			     wsola->buf+wsola->cur_cnt - (start+frmsz));
 
-	pjmedia_copy_samples(wsola->frm, wsola->mergebuf, frmsz);
-
+	/* Copy the merged frame */
+	pjmedia_copy_samples(frm, wsola->mergebuf, frmsz);
+
+	/* We have new samples */
 	wsola->cur_cnt = (pj_uint16_t)(wsola->cur_cnt + dist);
+
+	pj_assert(wsola->cur_cnt <= wsola->buf_cnt);
+
 	generated += dist;
 
 	if (generated >= needed) {
-	    assert(wsola->cur_cnt <= wsola->buf_cnt);
 	    TRACE_((THIS_FILE, "WSOLA frame expanded after %d iterations", 
 		    rep));
@@ -469,15 +513,22 @@
     if (prev_lost && extra >= wsola->min_extra) {
 	short *dst = wsola->buf + wsola->hist_cnt + wsola->samples_per_frame;
-	unsigned i;
-
+
+	/* Smoothen the transition. This will also erase the excess
+	 * samples
+	 */
 	overlapp_add_simple(dst, extra, dst, frm);
 
-	for (i=extra; i<wsola->samples_per_frame; ++i)
-	    dst[i] = frm[i];
-
-	
+	/* Copy remaining samples from the frame */
+	pjmedia_copy_samples(dst+extra, frm+extra, 
+			     wsola->samples_per_frame-extra);
+
+	/* Retrieve frame */
 	pjmedia_copy_samples(frm, wsola->frm, wsola->samples_per_frame);
+
+	/* Remove excess samples */
 	wsola->cur_cnt = (pj_uint16_t)(wsola->hist_cnt + 
 				       wsola->samples_per_frame);
+
+	/* Shift buffer */
 	pjmedia_move_samples(wsola->buf, wsola->buf+wsola->samples_per_frame,
 			      wsola->cur_cnt);
@@ -490,8 +541,13 @@
 	}
 
+	/* Append frame */
 	pjmedia_copy_samples(wsola->buf + wsola->cur_cnt, frm, 
 			     wsola->samples_per_frame);
+
+	/* Retrieve frame */
 	pjmedia_copy_samples(frm, wsola->frm, 
 			     wsola->samples_per_frame);
+
+	/* Shift buffer */
 	pjmedia_move_samples(wsola->buf, wsola->buf+wsola->samples_per_frame,
 			     wsola->cur_cnt);
Index: /pjproject/trunk/pjmedia/src/test/wsola_test.c
===================================================================
--- /pjproject/trunk/pjmedia/src/test/wsola_test.c	(revision 1843)
+++ /pjproject/trunk/pjmedia/src/test/wsola_test.c	(revision 1844)
@@ -6,5 +6,5 @@
 #include <assert.h>
 
-#define CLOCK_RATE	    44100
+#define CLOCK_RATE	    16000
 #define SAMPLES_PER_FRAME   (10 * CLOCK_RATE / 1000)
 
@@ -86,5 +86,5 @@
     if (!out) return 1;
 
-    pjmedia_wsola_create(pool, CLOCK_RATE, SAMPLES_PER_FRAME, 0, &wsola);
+    pjmedia_wsola_create(pool, CLOCK_RATE, SAMPLES_PER_FRAME, 1, 0, &wsola);
 
     samples = 0;
@@ -201,5 +201,5 @@
     if (!out) return 1;
 
-    pjmedia_wsola_create(pool, CLOCK_RATE, SAMPLES_PER_FRAME, 0, &wsola);
+    pjmedia_wsola_create(pool, CLOCK_RATE, SAMPLES_PER_FRAME, 1, 0, &wsola);
 
     elapsed.u64 = 0;
@@ -320,5 +320,5 @@
     pj_caching_pool cp;
     pj_pool_t *pool;
-    int rc;
+    int i, rc;
 
     //test_find_pitch();
@@ -330,6 +330,11 @@
     srand(2);
 
-    rc = expand(pool, "beet44.pcm", "output.pcm", 0, 0, 0);
-    //rc = compress(pool, "beet44.pcm", "output.pcm", 2);
+    rc = expand(pool, "galileo16.pcm", "temp1.pcm", 20, 0, 0);
+    rc = compress(pool, "temp1.pcm", "output.pcm", 1);
+
+    for (i=0; i<2; ++i) {
+	rc = expand(pool, "output.pcm", "temp1.pcm", 20, 0, 0);
+	rc = compress(pool, "temp1.pcm", "output.pcm", 1);
+    }
 
     if (rc != 0) {
