Index: /pjproject/trunk/pjmedia/build/pjmedia.dsp
===================================================================
--- /pjproject/trunk/pjmedia/build/pjmedia.dsp	(revision 202)
+++ /pjproject/trunk/pjmedia/build/pjmedia.dsp	(revision 203)
@@ -88,4 +88,8 @@
 # Begin Source File
 
+SOURCE=..\src\pjmedia\audio_conf.c
+# End Source File
+# Begin Source File
+
 SOURCE=..\src\pjmedia\codec.c
 # End Source File
@@ -156,4 +160,8 @@
 
 # PROP Default_Filter "h;hpp;hxx;hm;inl"
+# Begin Source File
+
+SOURCE=..\include\pjmedia\audio_conf.h
+# End Source File
 # Begin Source File
 
Index: /pjproject/trunk/pjmedia/include/pjmedia/audio_conf.h
===================================================================
--- /pjproject/trunk/pjmedia/include/pjmedia/audio_conf.h	(revision 203)
+++ /pjproject/trunk/pjmedia/include/pjmedia/audio_conf.h	(revision 203)
@@ -0,0 +1,78 @@
+/* $Id$ */
+/* 
+ * Copyright (C) 2003-2006 Benny Prijono <benny@prijono.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA 
+ */
+#ifndef __PJMEDIA_CONF_H__
+#define __PJMEDIA_CONF_H__
+
+
+/**
+ * @file conf.h
+ * @brief Conference bridge.
+ */
+#include <pjmedia/types.h>
+
+/**
+ * Opaque type for conference bridge.
+ */
+typedef struct pjmedia_conf pjmedia_conf;
+
+
+/**
+ * Create conference bridge.
+ */
+PJ_DECL(pj_status_t) pjmedia_conf_create( pj_pool_t *pool,
+					  unsigned max_ports,
+					  pjmedia_conf **p_conf );
+
+
+/**
+ * Add stream port to the conference bridge.
+ */
+PJ_DECL(pj_status_t) pjmedia_conf_add_port( pjmedia_conf *conf,
+					    pj_pool_t *pool,
+					    pjmedia_stream_port *strm_port,
+					    const pj_str_t *port_name,
+					    unsigned *p_port );
+
+
+/**
+ * Mute or unmute port.
+ */
+PJ_DECL(pj_status_t) pjmedia_conf_set_mute( pjmedia_conf *conf,
+					    unsigned port,
+					    pj_bool_t mute );
+
+
+/**
+ * Set the specified port to be member of conference bridge.
+ */
+PJ_DECL(pj_status_t) pjmedia_conf_set_membership( pjmedia_conf *conf,
+						  unsigned port,
+						  pj_bool_t enabled );
+
+
+/**
+ * Remove the specified port.
+ */
+PJ_DECL(pj_status_t) pjmedia_conf_remove_port( pjmedia_conf *conf,
+					       unsigned port );
+
+
+
+#endif	/* __PJMEDIA_CONF_H__ */
+
Index: /pjproject/trunk/pjmedia/include/pjmedia/stream.h
===================================================================
--- /pjproject/trunk/pjmedia/include/pjmedia/stream.h	(revision 202)
+++ /pjproject/trunk/pjmedia/include/pjmedia/stream.h	(revision 203)
@@ -99,4 +99,21 @@
 
 /**
+ * Stream ports.
+ */
+struct pjmedia_stream_port
+{
+    /**
+     * Sink port.
+     */
+    pj_status_t (*put_frame)(const pj_int16_t *frame, pj_size_t frame_cnt);
+
+    /**
+     * Source port.
+     */
+    pj_status_t (*get_frame)(pj_int16_t *frame, pj_size_t frame_cnt);
+};
+
+
+/**
  * Create a media stream based on the specified stream parameter.
  * All channels in the stream initially will be inactive.
Index: /pjproject/trunk/pjmedia/include/pjmedia/types.h
===================================================================
--- /pjproject/trunk/pjmedia/include/pjmedia/types.h	(revision 202)
+++ /pjproject/trunk/pjmedia/include/pjmedia/types.h	(revision 203)
@@ -152,4 +152,9 @@
 
 /**
+ * @see pjmedia_stream_port
+ */
+typedef struct pjmedia_stream_port pjmedia_stream_port;
+
+/**
  * Typedef for media stream.
  */
Index: /pjproject/trunk/pjmedia/include/pjmedia/vad.h
===================================================================
--- /pjproject/trunk/pjmedia/include/pjmedia/vad.h	(revision 202)
+++ /pjproject/trunk/pjmedia/include/pjmedia/vad.h	(revision 203)
@@ -27,7 +27,9 @@
 #include <pjmedia/types.h>
 
+PJ_BEGIN_DECL
+
 
 /**
- * Opaque data type for pjmedia vad.
+ * @see pjmedia_vad
  */
 typedef struct pjmedia_vad  pjmedia_vad;
@@ -49,4 +51,39 @@
 
 /**
+ * Set the vad to operate in adaptive mode.
+ *
+ * @param vad		    The vad
+ * @param frame_size Number of samplse per frame.
+ *
+ * @return		    PJ_SUCCESS on success.
+ */
+PJ_DECL(pj_status_t) pjmedia_vad_set_adaptive( pjmedia_vad *vad,
+					       unsigned frame_size);
+
+
+/**
+ * Set the vad to operate in fixed threshold mode.
+ *
+ * @param vad		    The vad
+ * @param frame_size Number of samplse per frame.
+ * @param threshold	    The silence threshold.
+ *
+ * @return		    PJ_SUCCESS on success.
+ */
+PJ_DECL(pj_status_t) pjmedia_vad_set_fixed( pjmedia_vad *vad,
+					    unsigned frame_size,
+					    unsigned threshold );
+
+/**
+ * Disable the vad.
+ *
+ * @param vad		The vad
+ *
+ * @return		PJ_SUCCESS on success.
+ */
+PJ_DECL(pj_status_t) pjmedia_vad_disable( pjmedia_vad *vad );
+
+
+/**
  * Calculate average signal level for the given samples.
  *
@@ -57,6 +94,6 @@
  *			divided by number of samples.
  */
-PJ_DECL(pj_uint32_t) pjmedia_vad_calc_avg_signal_level( pj_int16_t samples[],
-							pj_size_t count );
+PJ_DECL(pj_int32_t) pjmedia_vad_calc_avg_signal( const pj_int16_t samples[],
+						 pj_size_t count );
 
 
@@ -67,16 +104,16 @@
  * @param samples	Pointer to 16-bit PCM input samples.
  * @param count		Number of samples in the input.
- * @param p_silence	Pointer to receive the silence detection result.
- *			Non-zero value indicates that that input is considered
- *			as silence.
+ * @param p_level	Optional pointer to receive average signal level
+ *			of the input samples.
  *
  * @return		PJ_SUCCESS on success.
  */
-PJ_DECL(pj_status_t) pjmedia_vad_detect_silence( pjmedia_vad *vad,
-						 pj_int16_t samples[],
-						 pj_size_t count,
-						 pj_bool_t *p_silence);
+PJ_DECL(pj_bool_t) pjmedia_vad_detect_silence( pjmedia_vad *vad,
+					       const pj_int16_t samples[],
+					       pj_size_t count,
+					       pj_int32_t *p_level);
 
 
+PJ_END_DECL
 
 #endif	/* __PJMEDIA_VAD_H__ */
Index: /pjproject/trunk/pjmedia/src/pjmedia/audio_conf.c
===================================================================
--- /pjproject/trunk/pjmedia/src/pjmedia/audio_conf.c	(revision 203)
+++ /pjproject/trunk/pjmedia/src/pjmedia/audio_conf.c	(revision 203)
@@ -0,0 +1,405 @@
+/* $Id$ */
+/* 
+ * Copyright (C) 2003-2006 Benny Prijono <benny@prijono.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA 
+ */
+#include <pjmedia/audio_conf.h>
+#include <pjmedia/vad.h>
+#include <pjmedia/stream.h>
+#include <pjmedia/sound.h>
+#include <pjmedia/errno.h>
+#include <pj/assert.h>
+#include <pj/log.h>
+#include <pj/pool.h>
+#include <pj/string.h>
+
+
+
+#define THIS_FILE   "audio_conf.c"
+
+
+struct conf_port
+{
+    pj_str_t		 name;
+    pjmedia_stream_port	*port;
+    pj_bool_t		 online;
+    pj_bool_t		 is_member;
+    pjmedia_vad		*vad;
+    pj_int32_t		 level;
+};
+
+/*
+ * Conference bridge.
+ */
+struct pjmedia_conf
+{
+    unsigned		  max_ports;	/**< Maximum ports.		*/
+    unsigned		  port_cnt;	/**< Current number of ports.	*/
+    pj_snd_stream	 *snd_rec;	/**< Sound recorder stream.	*/
+    pj_snd_stream	 *snd_player;	/**< Sound player stream.	*/
+    struct conf_port	**port;		/**< Array of ports.		*/
+    pj_int16_t		 *rec_buf;	/**< Sample buffer for rec.	*/
+    pj_int16_t		 *play_buf;	/**< Sample buffer for player	*/
+    unsigned		  samples_cnt;	/**< Samples per frame.		*/
+    pj_size_t		  buf_size;	/**< Buffer size, in bytes.	*/
+};
+
+
+/* Prototypes */
+static pj_status_t play_cb( /* in */  void *user_data,
+			    /* in */  pj_uint32_t timestamp,
+			    /* out */ void *output,
+			    /* out */ unsigned size);
+static pj_status_t rec_cb(  /* in */  void *user_data,
+			    /* in */  pj_uint32_t timestamp,
+			    /* in */  const void *input,
+			    /* in*/   unsigned size);
+
+
+/*
+ * Create conference bridge.
+ */
+PJ_DEF(pj_status_t) pjmedia_conf_create( pj_pool_t *pool,
+					 unsigned max_ports,
+					 pjmedia_conf **p_conf )
+{
+    pjmedia_conf *conf;
+    pj_snd_stream_info snd_info;
+    pj_status_t status;
+
+    conf = pj_pool_zalloc(pool, sizeof(pjmedia_conf));
+    conf->max_ports = max_ports;
+    conf->port = pj_pool_zalloc(pool, max_ports*sizeof(void*));
+
+    /* Create default parameters. */
+    pj_memset(&snd_info, 0, sizeof(snd_info));
+    snd_info.samples_per_sec = 8000;
+    snd_info.bits_per_sample = 16;
+    snd_info.samples_per_frame = 160;
+    snd_info.bytes_per_frame = 16000;
+    snd_info.frames_per_packet = 1;
+
+    /* Create buffers. */
+    conf->samples_cnt = snd_info.samples_per_frame;
+    conf->buf_size = snd_info.samples_per_frame * snd_info.bits_per_sample / 8;
+    conf->rec_buf = pj_pool_alloc(pool,  conf->buf_size);
+    conf->play_buf = pj_pool_alloc(pool, conf->buf_size );
+
+
+    /* Open recorder. */
+    conf->snd_rec = pj_snd_open_recorder(-1 ,&snd_info, &rec_cb, conf);
+    if (conf->snd_rec == NULL) {
+	status = -1;
+	goto on_error;
+    }
+
+    /* Open player */
+    conf->snd_player = pj_snd_open_player(-1, &snd_info, &play_cb, conf);
+    if (conf->snd_player == NULL) {
+	status = -1;
+	goto on_error;
+    }
+
+    /* Done */
+
+    *p_conf = conf;
+
+    return PJ_SUCCESS;
+
+
+on_error:
+    if (conf->snd_rec) {
+	pj_snd_stream_stop(conf->snd_rec);
+	pj_snd_stream_close(conf->snd_rec);
+	conf->snd_rec = NULL;
+    }
+    if (conf->snd_player) {
+	pj_snd_stream_stop(conf->snd_player);
+	pj_snd_stream_close(conf->snd_player);
+	conf->snd_player = NULL;
+    }
+    return status;
+}
+
+/*
+ * Activate sound device.
+ */
+static pj_status_t activate_conf( pjmedia_conf *conf )
+{
+    char errmsg[PJ_ERR_MSG_SIZE];
+    pj_status_t status;
+
+    /* Start recorder. */
+    status = pj_snd_stream_start(conf->snd_rec);
+    if (status != PJ_SUCCESS)
+	goto on_error;
+
+    /* Start player. */
+    status = pj_snd_stream_start(conf->snd_rec);
+    if (status != PJ_SUCCESS)
+	goto on_error;
+
+    return PJ_SUCCESS;
+
+on_error:
+    pj_strerror(status, errmsg, sizeof(errmsg));
+    PJ_LOG(4,(THIS_FILE, "Error starting sound player/recorder: %s",
+	      errmsg));
+    return status;
+}
+
+
+/*
+ * Suspend sound device
+ */
+static void suspend_conf( pjmedia_conf *conf )
+{
+    pj_snd_stream_stop(conf->snd_rec);
+    pj_snd_stream_stop(conf->snd_player);
+}
+
+
+/*
+ * Add stream port to the conference bridge.
+ */
+PJ_DEF(pj_status_t) pjmedia_conf_add_port( pjmedia_conf *conf,
+					   pj_pool_t *pool,
+					   pjmedia_stream_port *strm_port,
+					   const pj_str_t *port_name,
+					   unsigned *p_port )
+{
+    struct conf_port *conf_port;
+    unsigned index;
+    pj_status_t status;
+
+    PJ_ASSERT_RETURN(conf && pool && strm_port && port_name && p_port, 
+		     PJ_EINVAL);
+
+    if (conf->port_cnt >= conf->max_ports) {
+	pj_assert(!"Too many ports");
+	return PJ_ETOOMANY;
+    }
+
+    /* Create port structure. */
+    conf_port = pj_pool_zalloc(pool, sizeof(struct conf_port));
+    pj_strdup_with_null(pool, &conf_port->name, port_name);
+    conf_port->port = strm_port;
+    conf_port->online = PJ_TRUE;
+    conf_port->level = 0;
+
+    /* Create VAD for this port. */
+    status = pjmedia_vad_create(pool, &conf_port->vad);
+    if (status != PJ_SUCCESS)
+	return status;
+
+    /* Set vad settings. */
+    pjmedia_vad_set_adaptive(conf_port->vad, conf->samples_cnt);
+
+    /* Find empty port in the conference bridge. */
+    for (index=0; index < conf->max_ports; ++index) {
+	if (conf->port[index] == NULL)
+	    break;
+    }
+
+    pj_assert(index != conf->max_ports);
+
+    /* Put the port. */
+    conf->port[index] = conf_port;
+    conf->port_cnt++;
+
+    /* If this is the first port, activate sound device. */
+    if (conf->port_cnt == 1) {
+	status = activate_conf(conf);;
+	if (status != PJ_SUCCESS) {
+	    conf->port[index] = NULL;
+	    --conf->port_cnt;
+	    return status;
+	}
+    }
+
+    /* Done. */
+    return PJ_SUCCESS;
+}
+
+
+/*
+ * Mute or unmute port.
+ */
+PJ_DEF(pj_status_t) pjmedia_conf_set_mute( pjmedia_conf *conf,
+					   unsigned port,
+					   pj_bool_t mute )
+{
+    /* Check arguments */
+    PJ_ASSERT_RETURN(conf && port < conf->max_ports, PJ_EINVAL);
+
+    /* Port must be valid. */
+    PJ_ASSERT_RETURN(conf->port[port] != NULL, PJ_EINVAL);
+
+    conf->port[port]->online = !mute;
+    
+    return PJ_SUCCESS;
+}
+
+
+/*
+ * Set the specified port to be member of conference bridge.
+ */
+PJ_DEF(pj_status_t) pjmedia_conf_set_membership( pjmedia_conf *conf,
+						 unsigned port,
+						 pj_bool_t enabled )
+{
+    /* Check arguments */
+    PJ_ASSERT_RETURN(conf && port < conf->max_ports, PJ_EINVAL);
+
+    /* Port must be valid. */
+    PJ_ASSERT_RETURN(conf->port[port] != NULL, PJ_EINVAL);
+
+    conf->port[port]->is_member = enabled;
+    
+    return PJ_SUCCESS;
+}
+
+
+/*
+ * Remove the specified port.
+ */
+PJ_DEF(pj_status_t) pjmedia_conf_remove_port( pjmedia_conf *conf,
+					      unsigned port )
+{
+    /* Check arguments */
+    PJ_ASSERT_RETURN(conf && port < conf->max_ports, PJ_EINVAL);
+
+    /* Port must be valid. */
+    PJ_ASSERT_RETURN(conf->port[port] != NULL, PJ_EINVAL);
+
+    /* Suspend the sound devices.
+     * Don't want to remove port while port is being accessed by sound
+     * device's threads.
+     */
+    suspend_conf(conf);
+
+    /* Remove the port. */
+    conf->port[port] = NULL;
+    --conf->port_cnt;
+
+    /* Reactivate sound device if ports are not zero */
+    if (conf->port_cnt != 0)
+	activate_conf(conf);
+
+    return PJ_SUCCESS;
+}
+
+
+/*
+ * Player callback.
+ */
+static pj_status_t play_cb( /* in */  void *user_data,
+			    /* in */  pj_uint32_t timestamp,
+			    /* out */ void *output,
+			    /* out */ unsigned size)
+{
+    pjmedia_conf *conf = user_data;
+    pj_int16_t *output_buf = output;
+    pj_int32_t highest_level = 0;
+    int highest_index = -1;
+    unsigned sources = 0;
+    unsigned i, j;
+    
+    PJ_UNUSED_ARG(timestamp);
+
+    /* Clear temporary buffer. */
+    pj_memset(output_buf, 0, size);
+
+    /* Get frames from ports. */
+    for (i=0; i<conf->max_ports; ++i) {
+	struct conf_port *conf_port = conf->port[i];
+	pj_int32_t level;
+	pj_bool_t silence;
+
+	if (!conf_port)
+	    continue;
+
+	conf_port->port->get_frame(conf->play_buf, conf->samples_cnt);
+	silence = pjmedia_vad_detect_silence(conf_port->vad,
+					     conf->play_buf, 
+					     conf->samples_cnt,
+					     &level);
+	if (!silence) {
+	    if (level > highest_level) {
+		highest_index = i;
+		highest_level = level;
+	    }
+
+	    ++sources;
+
+	    for (j=0; j<conf->samples_cnt; ++j) {
+		output_buf[j] = (pj_int16_t)(output_buf[j] + conf->play_buf[j]);
+	    }
+	}
+    }
+
+    /* Calculate average signal. */
+    if (sources) {
+	for (j=0; j<conf->samples_cnt; ++j) {
+	    output_buf[j] = (pj_int16_t)(output_buf[j] / sources);
+	}
+    }
+
+    /* Broadcast to conference member. */
+    for (i=0; i<conf->max_ports; ++i) {
+	struct conf_port *conf_port = conf->port[i];
+
+	if (!conf_port)
+	    continue;
+
+	if (!conf_port->is_member)
+	    continue;
+
+	conf_port->port->put_frame(output_buf, conf->samples_cnt);
+    }
+
+    return PJ_SUCCESS;
+}
+
+/*
+ * Recorder callback.
+ */
+static pj_status_t rec_cb(  /* in */  void *user_data,
+			    /* in */  pj_uint32_t timestamp,
+			    /* in */  const void *input,
+			    /* in*/   unsigned size)
+{
+    pjmedia_conf *conf = user_data;
+    unsigned i;
+
+    PJ_UNUSED_ARG(timestamp);
+    PJ_UNUSED_ARG(size);
+
+    for (i=0; i<conf->max_ports; ++i) {
+	struct conf_port *conf_port = conf->port[i];
+
+	if (!conf_port)
+	    continue;
+
+	if (!conf_port->online)
+	    continue;
+
+	conf_port->port->put_frame(input, conf->samples_cnt);
+    }
+    
+    return PJ_SUCCESS;
+}
+
Index: /pjproject/trunk/pjmedia/src/pjmedia/g711.c
===================================================================
--- /pjproject/trunk/pjmedia/src/pjmedia/g711.c	(revision 202)
+++ /pjproject/trunk/pjmedia/src/pjmedia/g711.c	(revision 203)
@@ -35,8 +35,8 @@
 
 /* Algorithm prototypes. */
-static unsigned char linear2alaw(int		pcm_val);   /* 2's complement (16-bit range) */
-static int	     alaw2linear(unsigned char	a_val);
-static unsigned char linear2ulaw(int		pcm_val);
-static int	     ulaw2linear(unsigned char	u_val);
+unsigned char linear2alaw(int		pcm_val);   /* 2's complement (16-bit range) */
+int	      alaw2linear(unsigned char	a_val);
+unsigned char linear2ulaw(int		pcm_val);
+int	      ulaw2linear(unsigned char	u_val);
 
 /* Prototypes for G711 factory */
@@ -594,5 +594,5 @@
  * John Wiley & Sons, pps 98-111 and 472-476.
  */
-static unsigned char
+unsigned char
 linear2ulaw(
 	int		pcm_val)	/* 2's complement (16-bit range) */
Index: /pjproject/trunk/pjmedia/src/pjmedia/vad.c
===================================================================
--- /pjproject/trunk/pjmedia/src/pjmedia/vad.c	(revision 202)
+++ /pjproject/trunk/pjmedia/src/pjmedia/vad.c	(revision 203)
@@ -19,24 +19,250 @@
 #include <pjmedia/vad.h>
 #include <pjmedia/errno.h>
-
+#include <pj/assert.h>
+#include <pj/log.h>
+#include <pj/pool.h>
+
+
+#define THIS_FILE   "vad.c"
+
+typedef enum pjmedia_vad_mode {
+    VAD_MODE_NONE,
+    VAD_MODE_FIXED,
+    VAD_MODE_ADAPTIVE
+} pjmedia_vad_mode;
+
+
+/**
+ * This structure holds the vad state.
+ */
+struct pjmedia_vad
+{
+    int	      mode;		/**< VAD mode.				    */
+    unsigned  frame_size;	/**< Samples per frame.			    */
+
+
+    unsigned  min_signal_cnt;	/**< # of signal frames.before talk burst   */
+    unsigned  min_silence_cnt;	/**< # of silence frames before silence.    */
+    unsigned  recalc_cnt;	/**< # of frames before adaptive recalc.    */
+
+    pj_bool_t in_talk;		/**< In talk burst?			    */
+    unsigned  cur_cnt;		/**< # of frames in current mode.	    */
+    unsigned  signal_cnt;	/**< # of signal frames received.	    */
+    unsigned  silence_cnt;	/**< # of silence frames received	    */
+    unsigned  cur_threshold;	/**< Current silence threshold.		    */
+    unsigned  weakest_signal;	/**< Weakest signal detected.		    */
+    unsigned  loudest_silence;	/**< Loudest silence detected.		    */
+};
+
+
+
+unsigned char linear2ulaw(int		pcm_val);
 
 PJ_DEF(pj_status_t) pjmedia_vad_create( pj_pool_t *pool,
 					pjmedia_vad **p_vad)
 {
-    return PJ_EINVALIDOP;
-}
-
-PJ_DEF(pj_uint32_t) pjmedia_vad_calc_avg_signal_level(pj_int16_t samples[],
-						      pj_size_t count)
-{
-    return PJ_EINVALIDOP;
-}
-
-PJ_DEF(pj_status_t) pjmedia_vad_detect_silence( pjmedia_vad *vad,
-						pj_int16_t samples[],
-						pj_size_t count,
-						pj_bool_t *p_silence)
-{
-    return PJ_EINVALIDOP;
-}
-
+    pjmedia_vad *vad;
+
+    PJ_ASSERT_RETURN(pool && p_vad, PJ_EINVAL);
+
+    vad = pj_pool_zalloc(pool, sizeof(struct pjmedia_vad));
+
+    vad->weakest_signal = 0xFFFFFFFFUL;
+    vad->loudest_silence = 0;
+    vad->signal_cnt = 0;
+    vad->silence_cnt = 0;
+    
+    /* Restart in adaptive, silent mode */
+    vad->in_talk = PJ_FALSE;
+    pjmedia_vad_set_adaptive( vad, 160 );
+
+    *p_vad = vad;
+    return PJ_SUCCESS;
+}
+
+PJ_DEF(pj_status_t) pjmedia_vad_set_adaptive( pjmedia_vad *vad,
+					      unsigned frame_size)
+{
+    PJ_ASSERT_RETURN(vad && frame_size, PJ_EINVAL);
+
+    vad->frame_size = frame_size;
+    vad->mode = VAD_MODE_ADAPTIVE;
+    vad->min_signal_cnt = 3;
+    vad->min_silence_cnt = 20;
+    vad->recalc_cnt = 30;
+    vad->cur_threshold = 20;
+
+    return PJ_SUCCESS;
+}
+
+PJ_DEF(pj_status_t) pjmedia_vad_set_fixed( pjmedia_vad *vad,
+					   unsigned frame_size,
+					   unsigned threshold )
+{
+    PJ_ASSERT_RETURN(vad && frame_size, PJ_EINVAL);
+
+    vad->mode = VAD_MODE_FIXED;
+    vad->frame_size = frame_size;
+    vad->cur_threshold = threshold;
+
+    return PJ_SUCCESS;
+}
+
+PJ_DEF(pj_status_t) pjmedia_vad_disable( pjmedia_vad *vad )
+{
+    PJ_ASSERT_RETURN(vad, PJ_EINVAL);
+
+    vad->mode = VAD_MODE_NONE;
+
+    return PJ_SUCCESS;
+}
+
+
+PJ_DEF(pj_int32_t) pjmedia_vad_calc_avg_signal(const pj_int16_t samples[],
+					       pj_size_t count)
+{
+    pj_uint32_t sum = 0;
+    
+    const pj_int16_t * pcm = samples;
+    const pj_int16_t * end = samples + count;
+
+    if (count==0)
+	return 0;
+
+    while (pcm != end) {
+	if (*pcm < 0)
+	    sum -= *pcm++;
+	else
+	    sum += *pcm++;
+    }
+    
+    return (pj_int32_t)(sum / count);
+}
+
+PJ_DEF(pj_bool_t) pjmedia_vad_detect_silence( pjmedia_vad *vad,
+					      const pj_int16_t samples[],
+					      pj_size_t count,
+					      pj_int32_t *p_level)
+{
+    pj_uint32_t level;
+    pj_bool_t have_signal;
+
+    /* Always return false if VAD is disabled */
+    if (vad->mode == VAD_MODE_NONE) {
+	if (p_level)
+	    *p_level = -1;
+	return PJ_FALSE;
+    }
+    
+    /* Calculate average signal level. */
+    level = pjmedia_vad_calc_avg_signal(samples, count);
+    
+    /* Report to caller, if required. */
+    if (p_level)
+	*p_level = level;
+
+    /* Convert PCM level to ulaw */
+    level = linear2ulaw(level) ^ 0xff;
+    
+    /* Do we have signal? */
+    have_signal = level > vad->cur_threshold;
+    
+    /* We we're in transition between silence and signel, increment the 
+     * current frame counter. We will only switch mode when we have enough
+     * frames.
+     */
+    if (vad->in_talk != have_signal) {
+	unsigned limit;
+
+	vad->cur_cnt++;
+
+	limit = (vad->in_talk ? vad->min_silence_cnt : 
+				vad->min_signal_cnt);
+
+	if (vad->cur_cnt > limit) {
+
+	    /* Swap mode */
+	    vad->in_talk = !vad->in_talk;
+	    
+	    /* Restart adaptive cur_threshold measurements */
+	    vad->weakest_signal = 0xFFFFFFFFUL;
+	    vad->loudest_silence = 0;
+	    vad->signal_cnt = 0;
+	    vad->silence_cnt = 0;
+	}
+
+    } else {
+	/* Reset frame count */
+	vad->cur_cnt = 0;
+    }
+    
+    /* For fixed threshold vad, everything is done. */
+    if (vad->mode == VAD_MODE_FIXED) {
+	return !vad->in_talk;
+    }
+    
+
+    /* Count the number of silent and signal frames and calculate min/max */
+    if (have_signal) {
+	if (level < vad->weakest_signal)
+	    vad->weakest_signal = level;
+	vad->signal_cnt++;
+    }
+    else {
+	if (level > vad->loudest_silence)
+	    vad->loudest_silence = level;
+	vad->silence_cnt++;
+    }
+
+    /* See if we have had enough frames to look at proportions of 
+     * silence/signal frames.
+     */
+    if ((vad->signal_cnt + vad->silence_cnt) > vad->recalc_cnt) {
+	
+	/* Adjust silence threshold by looking at the proportions of
+	 * signal and silence frames.
+	 */
+	if (vad->signal_cnt >= vad->recalc_cnt) {
+	    /* All frames where signal frames.
+	     * Increase silence threshold.
+	     */
+	    vad->cur_threshold += (vad->weakest_signal - vad->cur_threshold)/4;
+	    PJ_LOG(5,(THIS_FILE, "Vad cur_threshold increased to %d",
+		      vad->cur_threshold));
+	}
+	else if (vad->silence_cnt >= vad->recalc_cnt) {
+	    /* All frames where silence frames.
+	     * Decrease silence threshold.
+	     */
+	    vad->cur_threshold = (vad->cur_threshold+vad->loudest_silence)/2+1;
+	    PJ_LOG(5,(THIS_FILE, "Vad cur_threshold decreased to %d",
+		      vad->cur_threshold));
+	}
+	else { 
+	    pj_bool_t updated = PJ_TRUE;
+
+	    /* Adjust according to signal/silence proportions. */
+	    if (vad->signal_cnt > vad->silence_cnt * 2)
+		vad->cur_threshold++;
+	    else if (vad->silence_cnt >  vad->signal_cnt* 2)
+		vad->cur_threshold--;
+	    else
+		updated = PJ_FALSE;
+
+	    if (updated) {
+		PJ_LOG(5,(THIS_FILE,
+			  "Vad cur_threshold updated to %d",
+			  vad->cur_threshold));
+	    }
+	}
+
+	/* Reset. */
+	vad->weakest_signal = 0xFFFFFFFFUL;
+	vad->loudest_silence = 0;
+	vad->signal_cnt = 0;
+	vad->silence_cnt = 0;
+    }
+    
+    return !vad->in_talk;
+}
+
