Index: pjproject/trunk/pjmedia/include/pjmedia/delaybuf.h
===================================================================
--- pjproject/trunk/pjmedia/include/pjmedia/delaybuf.h	(revision 1971)
+++ pjproject/trunk/pjmedia/include/pjmedia/delaybuf.h	(revision 1972)
@@ -69,12 +69,9 @@
  * @param clock_rate	    Number of samples processed per second.
  * @param samples_per_frame Number of samples per frame.
- * @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 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 channel_count	    Number of channel per frame.
+ * @param max_delay	    Maximum number of delay to be accommodated,
+ *			    in ms, if this value is negative or less than 
+ *			    one frame time, default maximum delay used is
+ *			    400 ms.
  * @param option	    Option flags, must be zero for now.
  * @param p_b		    Pointer to receive the delay buffer instance.
@@ -88,6 +85,6 @@
 					      unsigned clock_rate,
 					      unsigned samples_per_frame,
-					      unsigned max_frames,
-					      int delay,
+					      unsigned channel_count,
+					      unsigned max_delay,
 					      unsigned options,
 					      pjmedia_delay_buf **p_b);
@@ -128,13 +125,4 @@
 
 /**
- * Reinitiate learning state. This will clear the buffer's content.
- *
- * @param b		    The delay buffer.
- *
- * @return		    PJ_SUCCESS on success or the appropriate error.
- */
-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.
Index: pjproject/trunk/pjmedia/src/pjmedia/conference.c
===================================================================
--- pjproject/trunk/pjmedia/src/pjmedia/conference.c	(revision 1971)
+++ pjproject/trunk/pjmedia/src/pjmedia/conference.c	(revision 1972)
@@ -419,4 +419,5 @@
     struct conf_port *conf_port;
     pj_status_t status;
+    unsigned ptime;
 
     /* Create port */
@@ -426,9 +427,11 @@
 
     /* Passive port has delay buf. */
+    ptime = conf->samples_per_frame * 1000 / conf->clock_rate / 
+	    conf->channel_count;
     status = pjmedia_delay_buf_create(pool, name->ptr, 
 				      conf->clock_rate,
 				      conf->samples_per_frame,
-				      RX_BUF_COUNT, /* max */
-				      -1, /* delay */
+				      conf->channel_count,
+				      RX_BUF_COUNT * ptime, /* max delay */
 				      0, /* options */
 				      &conf_port->delay_buf);
Index: pjproject/trunk/pjmedia/src/pjmedia/delaybuf.c
===================================================================
--- pjproject/trunk/pjmedia/src/pjmedia/delaybuf.c	(revision 1971)
+++ pjproject/trunk/pjmedia/src/pjmedia/delaybuf.c	(revision 1972)
@@ -24,4 +24,5 @@
 #include <pj/lock.h>
 #include <pj/log.h>
+#include <pj/math.h>
 #include <pj/pool.h>
 
@@ -33,79 +34,57 @@
 #endif
 
-
-/* Type of states of delay buffer */
-enum state
-{
-    STATE_WAITING,
-    STATE_LEARNING,
-    STATE_RUNNING
+/* Operation types of delay buffer */
+enum OP
+{
+    OP_PUT,
+    OP_GET
 };
 
-/* Type of operation of delay buffer */
-enum OP
-{
-    OP_PUT,
-    OP_GET,
-    OP_UNDEFINED
-};
-
-/* 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
- * 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
-
-/*
- * Some experimental data (with SAFE_MARGIN=1):
- *
- * System 1:
- *  - XP, WMME, 10ms ptime, 
- *  - Sennheiser Headset+USB sound card
- *  - Stable delaybuf level: 6, on WAITING_COUNT=4 and LEARNING_COUNT=48
- *
- * System 2:
- *  - XP, WMME, 10ms ptime
- *  - Onboard SoundMAX Digital Audio
- *  - Stable delaybuf level: 6, on WAITING_COUNT=4 and LEARNING_COUNT=48
- *
- * System 3:
- *  - MacBook Core 2 Duo, OSX 10.5, 10ms ptime
- *  - Stable delaybuf level: 2, on WAITING_COUNT=4 and LEARNING_COUNT=8
- */
-
+/* Specify time for delaybuf to recalculate effective delay, in ms.
+ */
+#define RECALC_TIME	    2000
+
+/* Default value of maximum delay, in ms, this value is used when 
+ * maximum delay requested is less than ptime (one frame length).
+ */
+#define DEFAULT_MAX_DELAY   400
+
+/* Number of frames to add to learnt level for additional stability.
+ */
+#define SAFE_MARGIN	    0
+
+/* This structure describes internal delaybuf settings and states.
+ */
 struct pjmedia_delay_buf
 {
+    /* Properties and configuration */
     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	     ptime;		/**< Frame time, in ms		     */
+    unsigned	     channel_count;	/**< Channel count, in ms	     */
+    unsigned	     max_cnt;		/**< Max buffered samples, in samples*/
+
+    /* Buffer pointer and counter */
     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;		/**< Burst level storage on learning */
-    } op[2];
+    unsigned	     eff_cnt;		/**< Effective count of buffered 
+					     samples to keep the optimum
+					     balance between delay and 
+					     stability. This is calculated 
+					     based on burst level.	     */
+
+    /* Learning vars */
+    unsigned	     level;		/**< Burst level counter	     */
     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    */
-
+    int		     recalc_timer;	/**< Timer for recalculating max_level*/
+    unsigned	     max_level;		/**< Current max burst level	     */
+
+    /* Drift handler */
     pjmedia_wsola   *wsola;		/**< Drift handler		     */
 };
+
 
 PJ_DEF(pj_status_t) pjmedia_delay_buf_create( pj_pool_t *pool,
@@ -113,6 +92,6 @@
 					      unsigned clock_rate,
 					      unsigned samples_per_frame,
-					      unsigned max_frames,
-					      int delay,
+					      unsigned channel_count,
+					      unsigned max_delay,
 					      unsigned options,
 					      pjmedia_delay_buf **p_b)
@@ -121,6 +100,6 @@
     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(pool && samples_per_frame && clock_rate && channel_count &&
+		     p_b, PJ_EINVAL);
     PJ_ASSERT_RETURN(options==0, PJ_EINVAL);
 
@@ -134,6 +113,14 @@
 
     pj_ansi_strncpy(b->obj_name, name, PJ_MAX_OBJ_NAME-1);
+
     b->samples_per_frame = samples_per_frame;
-    b->max_frames = max_frames;
+    b->channel_count = channel_count;
+    b->ptime = samples_per_frame * 1000 / clock_rate / channel_count;
+    if (max_delay < b->ptime)
+	max_delay = PJ_MAX(DEFAULT_MAX_DELAY, b->ptime);
+
+    b->max_cnt = samples_per_frame * max_delay / b->ptime;
+    b->eff_cnt = b->max_cnt >> 1;
+    b->recalc_timer = RECALC_TIME;
 
     status = pj_lock_create_recursive_mutex(pool, b->obj_name, 
@@ -143,18 +130,5 @@
 
     b->frame_buf = (pj_int16_t*)
-		   pj_pool_zalloc(pool, samples_per_frame * max_frames * 
-				  sizeof(pj_int16_t));
-
-    if (delay >= 0) {
-	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;
-    }
+		    pj_pool_zalloc(pool, b->max_cnt * sizeof(pj_int16_t));
 
     status = pjmedia_wsola_create(pool, clock_rate, samples_per_frame, 1,
@@ -183,4 +157,5 @@
 
     pj_lock_release(b->lock);
+
     pj_lock_destroy(b->lock);
     b->lock = NULL;
@@ -263,116 +238,56 @@
 }
 
-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)
+/* Fast increase, slow decrease */
+#define AGC_UP(cur, target) cur = (cur + target*3) >> 2
+#define AGC_DOWN(cur, target) cur = (cur*3 + target) >> 2
+#define AGC(cur, target) \
+    if (cur < target) AGC_UP(cur, target); \
+    else AGC_DOWN(cur, target)
+
+static void update(pjmedia_delay_buf *b, enum OP op)
+{
+    /* Sequential operation */
+    if (op == b->last_op) {
+	++b->level;
 	return;
-
-    /* For now, only support shrinking */
-    pj_assert(old_max_cnt > new_max_cnt);
-
-    /* Buffer empty, only need to reset pointers then set new max directly */
-    if (b->buf_cnt == 0) {
-	b->put_pos = b->get_pos = 0;
-	b->max_cnt = new_max_cnt;
-	return;
-    }
-
-    /* If samples number in the buffer > new_max_cnt, reduce samples first */
-    if (b->buf_cnt > new_max_cnt)
-	shrink_buffer(b, b->buf_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
+    } 
+
+    /* Switching operation */
+    if (b->level > b->max_level)
+	b->max_level = b->level;
+
+    b->recalc_timer -= (b->level * b->ptime) >> 1;
+
+    b->last_op = op;
+    b->level = 1;
+
+    /* Recalculate effective count based on max_level */
+    if (b->recalc_timer <= 0) {
+	unsigned new_eff_cnt = (b->max_level+SAFE_MARGIN)*b->samples_per_frame;
+
+	/* Smoothening effective count transition */
+	AGC(b->eff_cnt, new_eff_cnt);
+	
+	/* Make sure the new effective count is multiplication of 
+	 * channel_count, so let's round it up.
 	 */
-	/* 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;
-}
-
-static void update(pjmedia_delay_buf *b, enum OP op)
-{
-    enum OP other = (enum OP) !op;
-
-    switch (b->state) {
-    case STATE_RUNNING:
-	break;
-    case STATE_WAITING:
-	++b->op[op].level;
-	if (b->op[other].level != 0) {
-	    ++b->state_count;
-	    if (b->state_count == WAITING_COUNT) {
-		/* Start learning */
-		pjmedia_delay_buf_learn(b);
-	    }
-	}
-	b->last_op = op;
-	break;
-    case STATE_LEARNING:
-	++b->op[op].level;
-	if (b->last_op == other) {
-	    unsigned last_level = b->op[other].level;
-	    if (last_level > b->max_level)
-		b->max_level = last_level;
-	    b->op[other].level = 0;
-	    b->state_count++;
-	    if (b->state_count == LEARN_COUNT) {
-		/* give SAFE_MARGIN compensation for added stability */
-		b->max_level += SAFE_MARGIN;
-		
-		/* buffer not enough! */
-		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));
-	    }
-	}
-	b->last_op = op;
-	break;
-    }
-
-    
+	if (b->eff_cnt % b->channel_count)
+	    b->eff_cnt += b->channel_count - (b->eff_cnt % b->channel_count);
+
+	TRACE__((b->obj_name,"Cur eff_cnt=%d", b->eff_cnt));
+	
+	b->max_level = 0;
+	b->recalc_timer = RECALC_TIME;
+    }
+
+    /* See if we need to shrink the buffer to reduce delay */
+    if (b->buf_cnt > b->samples_per_frame + b->eff_cnt) {
+	unsigned erase_cnt = b->samples_per_frame >> 1;
+	unsigned old_buf_cnt = b->buf_cnt;
+
+	shrink_buffer(b, erase_cnt);
+	PJ_LOG(5,(b->obj_name,"Buffer size adjusted from %d to %d",
+		  old_buf_cnt, b->buf_cnt));
+    }
 }
 
@@ -500,5 +415,6 @@
 }
 
-PJ_DEF(pj_status_t) pjmedia_delay_buf_learn(pjmedia_delay_buf *b)
+
+PJ_DEF(pj_status_t) pjmedia_delay_buf_reset(pjmedia_delay_buf *b)
 {
     PJ_ASSERT_RETURN(b, PJ_EINVAL);
@@ -506,25 +422,5 @@
     pj_lock_acquire(b->lock);
 
-    b->last_op = OP_UNDEFINED;
-    b->op[OP_GET].level = b->op[OP_PUT].level = 0;
-    b->state = STATE_LEARNING;
-    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);
+    b->recalc_timer = RECALC_TIME;
 
     /* clean up buffer */
Index: pjproject/trunk/pjmedia/src/pjmedia/sound_port.c
===================================================================
--- pjproject/trunk/pjmedia/src/pjmedia/sound_port.c	(revision 1971)
+++ pjproject/trunk/pjmedia/src/pjmedia/sound_port.c	(revision 1972)
@@ -373,4 +373,5 @@
     pjmedia_snd_port *snd_port;
     pj_status_t status;
+    unsigned ptime;
 
     PJ_ASSERT_RETURN(pool && p_port, PJ_EINVAL);
@@ -389,11 +390,15 @@
     
 #if PJMEDIA_SOUND_USE_DELAYBUF
+    ptime = samples_per_frame * 1000 / (clock_rate * channel_count);
+
     status = pjmedia_delay_buf_create(pool, "snd_buff", 
-				      clock_rate, samples_per_frame, 
-				      PJMEDIA_SOUND_BUFFER_COUNT, -1, 
+				      clock_rate, samples_per_frame,
+				      channel_count,
+				      PJMEDIA_SOUND_BUFFER_COUNT * ptime,
 				      0, &snd_port->delay_buf);
     PJ_ASSERT_RETURN(status == PJ_SUCCESS, status);
 #else
     PJ_UNUSED_ARG(status);
+    PJ_UNUSED_ARG(ptime);
 #endif
 
Index: pjproject/trunk/pjmedia/src/pjmedia/splitcomb.c
===================================================================
--- pjproject/trunk/pjmedia/src/pjmedia/splitcomb.c	(revision 1971)
+++ pjproject/trunk/pjmedia/src/pjmedia/splitcomb.c	(revision 1972)
@@ -308,5 +308,5 @@
     struct splitcomb *sc = (struct splitcomb*) splitcomb;
     struct reverse_port *rport;
-    unsigned buf_cnt;
+    unsigned buf_cnt, ptime;
     pjmedia_port *port;
     pj_status_t status;
@@ -347,4 +347,7 @@
 	buf_cnt = MAX_BUF_CNT;
 
+    ptime = port->info.samples_per_frame * 1000 / port->info.clock_rate /
+	    port->info.channel_count;
+
     rport->max_burst = MAX_BURST;
     rport->max_null_frames = MAX_NULL_FRAMES;
@@ -354,5 +357,6 @@
 				      port->info.clock_rate,
 				      port->info.samples_per_frame,
-				      buf_cnt, -1, 0,
+				      port->info.channel_count,
+				      buf_cnt * ptime, 0,
 				      &rport->buf[DIR_DOWNSTREAM].dbuf);
     if (status != PJ_SUCCESS) {
@@ -364,5 +368,6 @@
 				      port->info.clock_rate,
 				      port->info.samples_per_frame,
-				      buf_cnt, -1, 0,
+				      port->info.channel_count,
+				      buf_cnt * ptime, 0,
 				      &rport->buf[DIR_UPSTREAM].dbuf);
     if (status != PJ_SUCCESS) {
