Index: pjproject/trunk/pjmedia/include/pjmedia/delaybuf.h
===================================================================
--- pjproject/trunk/pjmedia/include/pjmedia/delaybuf.h	(revision 1832)
+++ pjproject/trunk/pjmedia/include/pjmedia/delaybuf.h	(revision 1833)
@@ -67,13 +67,15 @@
  * @param name		    Optional name for the buffer for log 
  *			    identification.
+ * @param clock_rate	    Number of samples processed per second.
  * @param samples_per_frame Number of samples per frame.
- * @param max_cnt	    Maximum number of delay to be accommodated,
+ * @param max_frames	    Maximum number of delay to be accommodated,
  *			    in number of frames.
  * @param delay		    The delay to be applied, in number of frames.
- *			    If the value is -1, the delay buffer will
+ *			    If the value is -1 or 0, the delay buffer will
  *			    learn about the delay automatically. If
  *			    the value is greater than zero, then this
  *			    value will be used and no learning will be
  *			    performed.
+ * @param option	    Option flags, must be zero for now.
  * @param p_b		    Pointer to receive the delay buffer instance.
  *
@@ -84,7 +86,9 @@
 PJ_DECL(pj_status_t) pjmedia_delay_buf_create(pj_pool_t *pool,
 					      const char *name,
+					      unsigned clock_rate,
 					      unsigned samples_per_frame,
-					      unsigned max_cnt,
+					      unsigned max_frames,
 					      int delay,
+					      unsigned options,
 					      pjmedia_delay_buf **p_b);
 
@@ -132,4 +136,23 @@
 PJ_DECL(pj_status_t) pjmedia_delay_buf_learn(pjmedia_delay_buf *b);
 
+/**
+ * Reset delay buffer. This will clear the buffer's content. But keep
+ * the learning result.
+ *
+ * @param b		    The delay buffer.
+ *
+ * @return		    PJ_SUCCESS on success or the appropriate error.
+ */
+PJ_DECL(pj_status_t) pjmedia_delay_buf_reset(pjmedia_delay_buf *b);
+
+/**
+ * Destroy delay buffer.
+ *
+ * @param b	    Delay buffer session.
+ *
+ * @return	    PJ_SUCCESS normally.
+ */
+PJ_DECL(pj_status_t) pjmedia_delay_buf_destroy(pjmedia_delay_buf *b);
+
 
 PJ_END_DECL
Index: pjproject/trunk/pjmedia/src/pjmedia/conference.c
===================================================================
--- pjproject/trunk/pjmedia/src/pjmedia/conference.c	(revision 1832)
+++ pjproject/trunk/pjmedia/src/pjmedia/conference.c	(revision 1833)
@@ -19,4 +19,5 @@
 #include <pjmedia/conference.h>
 #include <pjmedia/alaw_ulaw.h>
+#include <pjmedia/delaybuf.h>
 #include <pjmedia/errno.h>
 #include <pjmedia/port.h>
@@ -57,10 +58,5 @@
 #define THIS_FILE	"conference.c"
 
-/* When delay buffer is used, we only need 1 frame buffering */
-#if defined(PJMEDIA_SOUND_USE_DELAYBUF) && PJMEDIA_SOUND_USE_DELAYBUF!=0
-#   define RX_BUF_COUNT	    1
-#else
-#   define RX_BUF_COUNT	    PJMEDIA_SOUND_BUFFER_COUNT
-#endif
+#define RX_BUF_COUNT	    PJMEDIA_SOUND_BUFFER_COUNT
 
 #define BYTES_PER_SAMPLE    2
@@ -184,15 +180,20 @@
     unsigned		 tx_buf_count;	/**< # of samples in the buffer.    */
 
-    /* Snd buffers is a special buffer for sound device port (port 0, master
-     * port). It's not used by other ports.
+    /* Delay buffer is a special buffer for sound device port (port 0, master
+     * port) and other passive ports (sound device port is also passive port).
      *
-     * There are multiple numbers of this buffer, because we can not expect
-     * the mic and speaker thread to run equally after one another. In most
-     * systems, each thread will run multiple times before the other thread
-     * gains execution time. For example, in my system, mic thread is called
-     * three times, then speaker thread is called three times, and so on.
-     */
-    int			 snd_write_pos, snd_read_pos;
-    pj_int16_t		*snd_buf[RX_BUF_COUNT];	/**< Buffer 		    */
+     * We need the delay buffer because we can not expect the mic and speaker 
+     * thread to run equally after one another. In most systems, each thread 
+     * will run multiple times before the other thread gains execution time. 
+     * For example, in my system, mic thread is called three times, then 
+     * speaker thread is called three times, and so on. This we call burst.
+     *
+     * There is also possibility of drift, unbalanced rate between put_frame
+     * and get_frame operation, in passive ports. If drift happens, snd_buf
+     * needs to be expanded or shrinked. 
+     *
+     * Burst and drift are handled by delay buffer.
+     */
+    pjmedia_delay_buf	*delay_buf;
 };
 
@@ -227,4 +228,5 @@
 				  pjmedia_frame *frame);
 static pj_status_t destroy_port(pjmedia_port *this_port);
+static pj_status_t destroy_port_pasv(pjmedia_port *this_port);
 
 
@@ -378,5 +380,4 @@
 {
     struct conf_port *conf_port;
-    unsigned i;
     pj_status_t status;
 
@@ -386,15 +387,14 @@
 	return status;
 
-    /* Passive port has rx buffers. */
-    for (i=0; i<RX_BUF_COUNT; ++i) {
-	conf_port->snd_buf[i] = (pj_int16_t*)
-				pj_pool_zalloc(pool, conf->samples_per_frame *
-					      sizeof(conf_port->snd_buf[0][0]));
-	if (conf_port->snd_buf[i] == NULL) {
-	    return PJ_ENOMEM;
-	}
-    }
-    conf_port->snd_write_pos = 0;
-    conf_port->snd_read_pos = 0;
+    /* Passive port has delay buf. */
+    status = pjmedia_delay_buf_create(pool, name->ptr, 
+				      conf->clock_rate,
+				      conf->samples_per_frame,
+				      RX_BUF_COUNT, /* max */
+				      -1, /* delay */
+				      0, /* options */
+				      &conf_port->delay_buf);
+    if (status != PJ_SUCCESS)
+	return status;
 
     *p_conf_port = conf_port;
@@ -445,4 +445,5 @@
 					      0,    /* Options */
 					      &conf->snd_dev_port);
+
 	}
 
@@ -607,4 +608,15 @@
 }
 
+static pj_status_t destroy_port_pasv(pjmedia_port *this_port) {
+    pjmedia_conf *conf = (pjmedia_conf*) this_port->port_data.pdata;
+    struct conf_port *port = conf->ports[this_port->port_data.ldata];
+    pj_status_t status;
+
+    status = pjmedia_delay_buf_destroy(port->delay_buf);
+    if (status == PJ_SUCCESS)
+	port->delay_buf = NULL;
+
+    return status;
+}
 
 /*
@@ -786,5 +798,5 @@
     port->get_frame = &get_frame_pasv;
     port->put_frame = &put_frame;
-    port->on_destroy = NULL;
+    port->on_destroy = &destroy_port_pasv;
 
     
@@ -955,5 +967,7 @@
 		  dst_port->name.ptr));
 
-	
+	/* if source port is passive port and has no listener, reset delaybuf */
+	if (src_port->delay_buf && src_port->listener_cnt == 0)
+	    pjmedia_delay_buf_reset(src_port->delay_buf);
     }
 
@@ -1617,27 +1631,15 @@
 	}
 
-	/* Get frame from this port. 
-	 * For port zero (sound port) and passive ports, get the frame  from 
-	 * the rx_buffer instead.
+	/* Get frame from this port.
+	 * For passive ports, get the frame from the delay_buf.
+	 * For other ports, get the frame from the port. 
 	 */
-	if (conf_port->port == NULL) {
-	    pj_int16_t *snd_buf;
-
-	    if (conf_port->snd_read_pos == conf_port->snd_write_pos) {
-		conf_port->snd_read_pos = 
-		    (conf_port->snd_write_pos+RX_BUF_COUNT-RX_BUF_COUNT/2) % 
-			RX_BUF_COUNT;
-	    }
-
-	    /* Skip if this port is muted/disabled. */
-	    if (conf_port->rx_setting != PJMEDIA_PORT_ENABLE) {
-		conf_port->rx_level = 0;
+	if (conf_port->delay_buf != NULL) {
+	    pj_status_t status;
+	
+	    status = pjmedia_delay_buf_get(conf_port->delay_buf,
+				  (pj_int16_t*)frame->buf);
+	    if (status != PJ_SUCCESS)
 		continue;
-	    }
-
-	    snd_buf = conf_port->snd_buf[conf_port->snd_read_pos];
-	    pjmedia_copy_samples((pj_int16_t*)frame->buf, snd_buf, 
-				 conf->samples_per_frame);
-	    conf_port->snd_read_pos = (conf_port->snd_read_pos+1) % RX_BUF_COUNT;
 
 	} else {
@@ -1831,5 +1833,5 @@
 
 /*
- * Recorder callback.
+ * Recorder (or passive port) callback.
  */
 static pj_status_t put_frame(pjmedia_port *this_port, 
@@ -1838,6 +1840,5 @@
     pjmedia_conf *conf = (pjmedia_conf*) this_port->port_data.pdata;
     struct conf_port *port = conf->ports[this_port->port_data.ldata];
-    const pj_int16_t *input = (const pj_int16_t*) frame->buf;
-    pj_int16_t *target_snd_buf;
+    pj_status_t status;
 
     /* Check for correct size. */
@@ -1846,4 +1847,7 @@
 		      PJMEDIA_ENCSAMPLESPFRAME);
 
+    /* Check existance of delay_buf instance */
+    PJ_ASSERT_RETURN( port->delay_buf, PJ_EBUG );
+
     /* Skip if this port is muted/disabled. */
     if (port->rx_setting != PJMEDIA_PORT_ENABLE) {
@@ -1856,16 +1860,7 @@
     }
 
-
-    /* Determine which rx_buffer to fill in */
-    target_snd_buf = port->snd_buf[port->snd_write_pos];
-    
-    /* Copy samples from audio device to target rx_buffer */
-    pjmedia_copy_samples(target_snd_buf, input, conf->samples_per_frame);
-
-    /* Switch buffer */
-    port->snd_write_pos = (port->snd_write_pos+1)%RX_BUF_COUNT;
-
-
-    return PJ_SUCCESS;
-}
-
+    status = pjmedia_delay_buf_put(port->delay_buf, (pj_int16_t*)frame->buf);
+
+    return status;
+}
+
Index: pjproject/trunk/pjmedia/src/pjmedia/delaybuf.c
===================================================================
--- pjproject/trunk/pjmedia/src/pjmedia/delaybuf.c	(revision 1832)
+++ pjproject/trunk/pjmedia/src/pjmedia/delaybuf.c	(revision 1833)
@@ -20,8 +20,19 @@
 #include <pjmedia/delaybuf.h>
 #include <pjmedia/errno.h>
+#include <pjmedia/wsola.h>
 #include <pj/assert.h>
+#include <pj/lock.h>
 #include <pj/log.h>
 #include <pj/pool.h>
 
+
+#if 0
+#   define TRACE__(x) PJ_LOG(3,x)
+#else
+#   define TRACE__(x)
+#endif
+
+
+/* Type of states of delay buffer */
 enum state
 {
@@ -31,4 +42,5 @@
 };
 
+/* Type of operation of delay buffer */
 enum OP
 {
@@ -38,11 +50,16 @@
 };
 
-/* The following macros represent cycles of test. */
-/* Since there are two operations performed (get & put), */
-/* these macros value must be minimum 2 and should be even number */
+/* The following macros represent cycles of test. Since there are two
+ * operations performed (get & put), these macros minimum value must be 2 
+ * and should be even number.
+ */
 #define WAITING_COUNT	4
 #define LEARN_COUNT	16
 
-/* Number of buffers to add to learnt level for additional stability */
+/* Number of buffers to add to learnt level for additional stability
+ * Please note that wsola_discard() needs minimum 3 frames, so max buffer 
+ * count should be minimally 3, setting SAFE_MARGIN to 2 will guarantees 
+ * this.
+ */
 #define SAFE_MARGIN	2
 
@@ -67,34 +84,46 @@
 struct pjmedia_delay_buf
 {
-    char	 obj_name[PJ_MAX_OBJ_NAME];
-
-    pj_int16_t	*frame_buf;
-    unsigned	 put_pos;
-    unsigned	 get_pos;
-    unsigned	 buf_cnt;
-
-    unsigned	 samples_per_frame;
-    unsigned	 max_cnt;
-    enum state	 state;
+    char	     obj_name[PJ_MAX_OBJ_NAME];
+
+    pj_lock_t	    *lock;		/**< Lock object.		     */
+
+    pj_int16_t	    *frame_buf;
+    enum state	     state;		/**< State of delay buffer	     */
+    unsigned	     samples_per_frame; /**< Number of samples in one frame  */
+    unsigned	     max_frames;	/**< Buffer allocated, in frames     */
+
+    unsigned	     put_pos;		/**< Position for put op, in samples */
+    unsigned	     get_pos;		/**< Position for get op, in samples */
+    unsigned	     buf_cnt;		/**< Number of buffered samples	     */
+    unsigned	     max_cnt;		/**< Max number of buffered samples  */
 
     struct {
-	unsigned level;
+	unsigned     level;		/**< Burst level storage on learning */
     } op[2];
-
-    enum OP	 last_op;
-    unsigned	 max_level;
-    unsigned	 state_count;
+    enum OP	     last_op;		/**< Last op (GET or PUT) of learning*/
+    unsigned	     state_count;	/**< Counter of op cycles of learning*/
+
+    unsigned	     max_level;		/**< Learning result: burst level    */
+
+    pjmedia_wsola   *wsola;		/**< Drift handler		     */
 };
 
 PJ_DEF(pj_status_t) pjmedia_delay_buf_create( pj_pool_t *pool,
 					      const char *name,
+					      unsigned clock_rate,
 					      unsigned samples_per_frame,
-					      unsigned max_cnt,
+					      unsigned max_frames,
 					      int delay,
+					      unsigned options,
 					      pjmedia_delay_buf **p_b)
 {
     pjmedia_delay_buf *b;
-
-    PJ_ASSERT_RETURN(pool && samples_per_frame && max_cnt && p_b, PJ_EINVAL);
+    pj_status_t status;
+
+    PJ_ASSERT_RETURN(pool && samples_per_frame && max_frames && p_b, PJ_EINVAL);
+    PJ_ASSERT_RETURN((int)max_frames >= delay, PJ_EINVAL);
+    PJ_ASSERT_RETURN(options==0, PJ_EINVAL);
+
+    PJ_UNUSED_ARG(options);
 
     if (!name) {
@@ -105,25 +134,180 @@
 
     pj_ansi_strncpy(b->obj_name, name, PJ_MAX_OBJ_NAME-1);
+    b->samples_per_frame = samples_per_frame;
+    b->max_frames = max_frames;
+
+    status = pj_lock_create_recursive_mutex(pool, b->obj_name, 
+					    &b->lock);
+    if (status != PJ_SUCCESS)
+	return status;
+
     b->frame_buf = (pj_int16_t*)
-		   pj_pool_zalloc(pool, samples_per_frame * max_cnt * 
-					 sizeof(pj_int16_t));
-    b->samples_per_frame = samples_per_frame;
-    b->max_cnt = max_cnt;
+		   pj_pool_zalloc(pool, samples_per_frame * max_frames * 
+				  sizeof(pj_int16_t));
 
     if (delay >= 0) {
-	PJ_ASSERT_RETURN(delay <= (int)max_cnt, PJ_EINVAL);
+	if (delay == 0)
+	    delay = 1;
 	b->max_level = delay;
+	b->max_cnt = delay * samples_per_frame;
 	b->state = STATE_RUNNING;
     } else {
+	b->max_cnt = max_frames * samples_per_frame;
 	b->last_op = OP_UNDEFINED;
 	b->state = STATE_WAITING;
-	b->buf_cnt = 0;
-    }
+    }
+
+    status = pjmedia_wsola_create(pool, clock_rate, samples_per_frame, 
+				  PJMEDIA_WSOLA_NO_PLC, &b->wsola);
+    if (status != PJ_SUCCESS)
+	return status;
 
     *p_b = b;
 
-    PJ_LOG(5,(b->obj_name,"Delay buffer created"));
+    TRACE__((b->obj_name,"Delay buffer created"));
 
     return PJ_SUCCESS;
+}
+
+PJ_DEF(pj_status_t) pjmedia_delay_buf_destroy(pjmedia_delay_buf *b)
+{
+    pj_status_t status;
+
+    PJ_ASSERT_RETURN(b, PJ_EINVAL);
+
+    pj_lock_acquire(b->lock);
+
+    status = pjmedia_wsola_destroy(b->wsola);
+    if (status == PJ_SUCCESS)
+	b->wsola = NULL;
+
+    pj_lock_release(b->lock);
+    pj_lock_destroy(b->lock);
+    b->lock = NULL;
+
+    return status;
+}
+
+/* This function will erase samples from delay buffer.
+ * The number of erased samples is guaranteed to be >= erase_cnt.
+ */
+static void shrink_buffer(pjmedia_delay_buf *b, unsigned erase_cnt)
+{
+    unsigned buf1len;
+    unsigned buf2len;
+    pj_status_t status;
+
+    pj_assert(b && erase_cnt);
+
+    if (b->get_pos < b->put_pos) {
+	/* sssss .. sssss
+	 *  ^          ^
+	 *  G          P
+	 */
+	buf1len = b->put_pos - b->get_pos;
+	buf2len = 0;
+    } else {
+	/* sssss .. sssss
+	 *  ^ ^
+	 *  P G
+	 */
+	buf1len = b->max_cnt - b->get_pos;
+	buf2len = b->put_pos;
+    }
+
+    /* Consistency checking */
+    pj_assert((buf1len + buf2len) == b->buf_cnt);
+
+    if (buf1len != 0)
+	status = pjmedia_wsola_discard(b->wsola, 
+				       &b->frame_buf[b->get_pos], buf1len,
+				       b->frame_buf, buf2len,
+				       &erase_cnt);
+    else
+	status = pjmedia_wsola_discard(b->wsola, 
+				       b->frame_buf, buf2len,
+				       NULL, 0,
+				       &erase_cnt);
+
+    if ((status == PJ_SUCCESS) && (erase_cnt > 0)) {
+	/* WSOLA discard will shrink only the second buffer, but it may
+	 * also shrink first buffer if second buffer is 'destroyed', so
+	 * it is safe to just set the new put_pos.
+	 */
+	if (b->put_pos >= erase_cnt)
+	    b->put_pos -= erase_cnt;
+	else
+	    b->put_pos = b->max_cnt - (erase_cnt - b->put_pos);
+
+	b->buf_cnt -= erase_cnt;
+
+	PJ_LOG(5,(b->obj_name,"Successfully shrinking %d samples, "
+		  "buf_cnt=%d", erase_cnt, b->buf_cnt));
+    }
+
+    /* Shrinking failed or erased count is less than requested,
+     * delaybuf needs to drop eldest samples, this is bad since the voice
+     * samples may not have smooth transition.
+     */
+    if (b->buf_cnt + b->samples_per_frame > b->max_cnt) {
+	erase_cnt = b->buf_cnt + b->samples_per_frame - b->max_cnt;
+
+	b->buf_cnt -= erase_cnt;
+
+	/* Shift get_pos forward */
+	b->get_pos = (b->get_pos + erase_cnt) % b->max_cnt;
+
+	PJ_LOG(4,(b->obj_name,"Shrinking failed or insufficient, dropping"
+		  " %d eldest samples, buf_cnt=%d", erase_cnt, b->buf_cnt));
+    }
+}
+
+static void set_max_cnt(pjmedia_delay_buf *b, unsigned new_max_cnt)
+{
+    unsigned old_max_cnt = b->max_cnt;
+
+    /* nothing to adjust */
+    if (old_max_cnt == new_max_cnt)
+	return;
+
+    /* For now, only support shrinking */
+    pj_assert(old_max_cnt > new_max_cnt);
+
+    shrink_buffer(b, old_max_cnt - new_max_cnt);
+
+    /* Adjust buffer to accomodate the new max_cnt so the samples is secured.
+     * This is done by make get_pos = 0 
+     */
+    if (b->get_pos <= b->put_pos) {
+	/* sssss .. sssss
+	 *   ^         ^
+	 *   G         P
+	 */
+	/* Consistency checking */
+	pj_assert((b->put_pos - b->get_pos) <= new_max_cnt);
+	pj_assert((b->put_pos - b->get_pos) == b->buf_cnt);
+
+	pjmedia_move_samples(b->frame_buf, &b->frame_buf[b->get_pos], 
+			     b->get_pos);
+	b->put_pos -= b->get_pos;
+	b->get_pos = 0;
+    } else {
+	/* sssss .. sssss
+	 *          ^  ^
+	 *          P  G
+	 */
+	unsigned d = old_max_cnt - b->get_pos;
+
+	/* Consistency checking */
+	pj_assert((b->get_pos - b->put_pos) >= (old_max_cnt - new_max_cnt));
+
+	/* Make get_pos = 0, shift right the leftmost block first */
+	pjmedia_move_samples(&b->frame_buf[d], b->frame_buf, d);
+	pjmedia_copy_samples(b->frame_buf, &b->frame_buf[b->get_pos], d);
+	b->put_pos += d;
+	b->get_pos = 0;
+    }
+
+    b->max_cnt = new_max_cnt;
 }
 
@@ -158,16 +342,21 @@
 		b->max_level += SAFE_MARGIN;
 		
-		PJ_LOG(5,(b->obj_name,"Delay buffer start running, level=%u",
-		    b->max_level));
-
 		/* buffer not enough! */
-		if (b->max_level > b->max_cnt) {
-		    b->max_level = b->max_cnt;
-		    PJ_LOG(2,(b->obj_name,"Delay buffer %s learning result " \
-			"exceeds the maximum delay allowed",
-			b->max_level));
+		if (b->max_level > b->max_frames) {
+		    PJ_LOG(4,(b->obj_name,"Delay buffer learning result (%d)"
+			      " exceeds the maximum delay allowed (%d)",
+			      b->max_level,
+			      b->max_frames));
+
+		    b->max_level = b->max_frames;
 		}
 
+		/* we need to set new max_cnt & adjust buffer */
+		set_max_cnt(b, b->max_level * b->samples_per_frame);
+
 		b->state = STATE_RUNNING;
+
+		PJ_LOG(4,(b->obj_name,"Delay buffer start running, level=%u",
+			  b->max_level));
 	    }
 	}
@@ -182,59 +371,129 @@
 					   pj_int16_t frame[])
 {
+    pj_status_t status;
+
+    PJ_ASSERT_RETURN(b && frame, PJ_EINVAL);
+
+    pj_lock_acquire(b->lock);
+
     update(b, OP_PUT);
     
-    if (b->state != STATE_RUNNING)
-	return PJ_EPENDING;
-
-    pj_memcpy(&b->frame_buf[b->put_pos * b->samples_per_frame], frame, 
-	b->samples_per_frame*sizeof(pj_int16_t));
-
-    /* overflow case */
-    if (b->put_pos == b->get_pos && b->buf_cnt) {
-	if (++b->get_pos == b->max_level)
-	    b->get_pos = 0;
-
-	b->put_pos = b->get_pos;
-
-	PJ_LOG(5,(b->obj_name,"Warning: buffer overflow"));
-
-	return PJ_ETOOMANY;
-    }
-
-    ++b->buf_cnt;
-
-    if (++b->put_pos == b->max_level)
-	b->put_pos = 0;
-
+    status = pjmedia_wsola_save(b->wsola, frame, PJ_FALSE);
+    if (status != PJ_SUCCESS) {
+	pj_lock_release(b->lock);
+	return status;
+    }
+
+    /* Overflow checking */
+    if (b->buf_cnt + b->samples_per_frame > b->max_cnt)
+    {
+	/* shrink one frame or just the diff? */
+	//unsigned erase_cnt = b->samples_per_frame;
+	unsigned erase_cnt = b->buf_cnt + b->samples_per_frame - b->max_cnt;
+
+	shrink_buffer(b, erase_cnt);
+    }
+
+    /* put the frame on put_pos */
+    if (b->put_pos + b->samples_per_frame <= b->max_cnt) {
+	pjmedia_copy_samples(&b->frame_buf[b->put_pos], frame, 
+			     b->samples_per_frame);
+    } else {
+	int remainder = b->put_pos + b->samples_per_frame - b->max_cnt;
+
+	pjmedia_copy_samples(&b->frame_buf[b->put_pos], frame, 
+			     b->samples_per_frame - remainder);
+	pjmedia_copy_samples(&b->frame_buf[0], 
+			     &frame[b->samples_per_frame - remainder], 
+			     remainder);
+    }
+
+    /* Update put_pos & buf_cnt */
+    b->put_pos = (b->put_pos + b->samples_per_frame) % b->max_cnt;
+    b->buf_cnt += b->samples_per_frame;
+
+    pj_lock_release(b->lock);
     return PJ_SUCCESS;
 }
 
-PJ_DEF(pj_status_t) pjmedia_delay_buf_get(pjmedia_delay_buf *b,
+PJ_DEF(pj_status_t) pjmedia_delay_buf_get( pjmedia_delay_buf *b,
 					   pj_int16_t frame[])
 {
+    pj_status_t status;
+
+    PJ_ASSERT_RETURN(b && frame, PJ_EINVAL);
+
+    pj_lock_acquire(b->lock);
+
     update(b, OP_GET);
 
-    if (b->state != STATE_RUNNING || !b->buf_cnt) {
-	if (b->state == STATE_RUNNING)
-	    PJ_LOG(5,(b->obj_name,"Warning: delay buffer empty"));
-
-	pj_bzero(frame, b->samples_per_frame*sizeof(pj_int16_t));
-	return PJ_EPENDING;
-    }
-
-    pj_memcpy(frame, &b->frame_buf[b->get_pos * b->samples_per_frame],
-	b->samples_per_frame*sizeof(pj_int16_t));
-
-    if (++b->get_pos == b->max_level)
-	b->get_pos = 0;
-
-    --b->buf_cnt;
+    /* Starvation checking */
+    if (b->buf_cnt < b->samples_per_frame) {
+
+	PJ_LOG(5,(b->obj_name,"Underflow, buf_cnt=%d, will generate 1 frame",
+		  b->buf_cnt));
+
+	status = pjmedia_wsola_generate(b->wsola, frame);
+
+	if (status == PJ_SUCCESS) {
+	    TRACE__((b->obj_name,"Successfully generate 1 frame"));
+	    if (b->buf_cnt == 0) {
+		pj_lock_release(b->lock);
+		return PJ_SUCCESS;
+	    }
+
+	    /* Put generated frame into buffer */
+	    if (b->put_pos + b->samples_per_frame <= b->max_cnt) {
+		pjmedia_copy_samples(&b->frame_buf[b->put_pos], frame, 
+				     b->samples_per_frame);
+	    } else {
+		int remainder = b->put_pos + b->samples_per_frame - b->max_cnt;
+
+		pjmedia_copy_samples(&b->frame_buf[b->put_pos], &frame[0], 
+				     b->samples_per_frame - remainder);
+		pjmedia_copy_samples(&b->frame_buf[0], 
+				     &frame[b->samples_per_frame - remainder], 
+				     remainder);
+	    }
+    	
+	    b->put_pos = (b->put_pos + b->samples_per_frame) % b->max_cnt;
+	    b->buf_cnt += b->samples_per_frame;
+
+	} else {
+	    /* Give all what delay buffer has, then pad zeroes */
+	    PJ_LOG(4,(b->obj_name,"Error generating frame, status=%d", 
+		      status));
+
+	    pjmedia_copy_samples(frame, &b->frame_buf[b->get_pos], b->buf_cnt);
+	    pjmedia_zero_samples(&frame[b->buf_cnt], 
+				 b->samples_per_frame - b->buf_cnt);
+	    b->get_pos += b->buf_cnt;
+	    b->buf_cnt = 0;
+
+	    /* Consistency checking */
+	    pj_assert(b->get_pos == b->put_pos);
+
+	    pj_lock_release(b->lock);
+
+	    return PJ_SUCCESS;
+	}
+    }
+
+    pjmedia_copy_samples(frame, &b->frame_buf[b->get_pos],
+			 b->samples_per_frame);
+
+    b->get_pos = (b->get_pos + b->samples_per_frame) % b->max_cnt;
+    b->buf_cnt -= b->samples_per_frame;
+
+    pj_lock_release(b->lock);
 
     return PJ_SUCCESS;
 }
 
-PJ_DECL(pj_status_t) pjmedia_delay_buf_learn(pjmedia_delay_buf *b)
+PJ_DEF(pj_status_t) pjmedia_delay_buf_learn(pjmedia_delay_buf *b)
 {
     PJ_ASSERT_RETURN(b, PJ_EINVAL);
+
+    pj_lock_acquire(b->lock);
 
     b->last_op = OP_UNDEFINED;
@@ -243,11 +502,31 @@
     b->state_count = 0;
     b->max_level = 0;
+    b->max_cnt = b->max_frames * b->samples_per_frame;
+
+    pjmedia_delay_buf_reset(b);
+
+    pj_lock_release(b->lock);
+
+    PJ_LOG(5,(b->obj_name,"Delay buffer start learning"));
+
+    return PJ_SUCCESS;
+}
+
+PJ_DEF(pj_status_t) pjmedia_delay_buf_reset(pjmedia_delay_buf *b)
+{
+    PJ_ASSERT_RETURN(b, PJ_EINVAL);
+
+    pj_lock_acquire(b->lock);
 
     /* clean up buffer */
     b->buf_cnt = 0;
     b->put_pos = b->get_pos = 0;
-
-    PJ_LOG(5,(b->obj_name,"Delay buffer start learning"));
+    pjmedia_wsola_reset(b->wsola, 0);
+
+    pj_lock_release(b->lock);
+
+    PJ_LOG(5,(b->obj_name,"Delay buffer resetted"));
 
     return PJ_SUCCESS;
 }
+
Index: pjproject/trunk/pjmedia/src/pjmedia/sound_port.c
===================================================================
--- pjproject/trunk/pjmedia/src/pjmedia/sound_port.c	(revision 1832)
+++ pjproject/trunk/pjmedia/src/pjmedia/sound_port.c	(revision 1833)
@@ -33,4 +33,6 @@
 #define THIS_FILE	    "sound_port.c"
 
+#define TEST_OVERFLOW_UNDERFLOW
+
 enum
 {
@@ -102,10 +104,25 @@
     if (snd_port->delay_buf) {
 	status = pjmedia_delay_buf_get(snd_port->delay_buf, (pj_int16_t*)output);
-	if (status != PJ_SUCCESS) {
+	if (status != PJ_SUCCESS)
 	    pj_bzero(output, size);
-	}
 
 	frame.type = PJMEDIA_FRAME_TYPE_AUDIO;
 	pjmedia_port_put_frame(port, &frame);
+
+#ifdef TEST_OVERFLOW_UNDERFLOW
+	{
+	    static int count = 1;
+	    if (++count % 10 == 0) {
+		status = pjmedia_delay_buf_get(snd_port->delay_buf, 
+					       (pj_int16_t*)output);
+		if (status != PJ_SUCCESS)
+		    pj_bzero(output, size);
+
+		frame.type = PJMEDIA_FRAME_TYPE_AUDIO;
+		pjmedia_port_put_frame(port, &frame);
+	    }
+	}
+#endif
+
     }
 #endif
@@ -372,5 +389,6 @@
     
 #if PJMEDIA_SOUND_USE_DELAYBUF
-    status = pjmedia_delay_buf_create(pool, "snd_buff", samples_per_frame, 
+    status = pjmedia_delay_buf_create(pool, "snd_buff", 
+				      clock_rate, samples_per_frame, 
 				      PJMEDIA_SOUND_BUFFER_COUNT, -1, 
 				      &snd_port->delay_buf);
Index: pjproject/trunk/pjmedia/src/test/wsola_test.c
===================================================================
--- pjproject/trunk/pjmedia/src/test/wsola_test.c	(revision 1832)
+++ pjproject/trunk/pjmedia/src/test/wsola_test.c	(revision 1833)
@@ -328,10 +328,7 @@
     pool = pj_pool_create(&cp.factory, "", 1000, 1000, NULL);
 
-    mem_test(pool);
-    return 0;
-
     srand(2);
 
-    rc = expand(pool, "beet44.pcm", "output.pcm", 1, 0, 0);
+    rc = expand(pool, "beet44.pcm", "output.pcm", 0, 0, 0);
     //rc = compress(pool, "beet44.pcm", "output.pcm", 2);
 
