Updated libraries and applications to use the new Audio Device API

git-svn-id: https://svn.pjsip.org/repos/pjproject/branches/projects/aps-direct@2468 74dad513-b988-da41-8d7b-12977e46ad98
diff --git a/pjmedia/build/pjmedia.dsp b/pjmedia/build/pjmedia.dsp
index 5a935be..db23940 100644
--- a/pjmedia/build/pjmedia.dsp
+++ b/pjmedia/build/pjmedia.dsp
@@ -121,10 +121,6 @@
 # End Source File

 # Begin Source File

 

-SOURCE=..\src\pjmedia\dsound.c

-# End Source File

-# Begin Source File

-

 SOURCE=..\src\pjmedia\echo_common.c

 # End Source File

 # Begin Source File

@@ -177,14 +173,6 @@
 # End Source File

 # Begin Source File

 

-SOURCE=..\src\pjmedia\nullsound.c

-# End Source File

-# Begin Source File

-

-SOURCE=..\src\pjmedia\pasound.c

-# End Source File

-# Begin Source File

-

 SOURCE=..\src\pjmedia\plc_common.c

 # End Source File

 # Begin Source File

@@ -297,10 +285,6 @@
 # End Source File

 # Begin Source File

 

-SOURCE=..\src\pjmedia\wmme_sound.c

-# End Source File

-# Begin Source File

-

 SOURCE=..\src\pjmedia\wsola.c

 # End Source File

 # End Group

diff --git a/pjmedia/build/pjmedia_audiodev.dsp b/pjmedia/build/pjmedia_audiodev.dsp
index 305a838..44e632c 100644
--- a/pjmedia/build/pjmedia_audiodev.dsp
+++ b/pjmedia/build/pjmedia_audiodev.dsp
@@ -101,6 +101,16 @@
 # End Source File

 # Begin Source File

 

+SOURCE="..\src\pjmedia-audiodev\symb_aps_dev.cpp"

+# PROP Exclude_From_Build 1

+# End Source File

+# Begin Source File

+

+SOURCE="..\src\pjmedia-audiodev\symb_mda_dev.cpp"

+# PROP Exclude_From_Build 1

+# End Source File

+# Begin Source File

+

 SOURCE="..\src\pjmedia-audiodev\wmme_dev.c"

 # End Source File

 # End Group

diff --git a/pjmedia/include/pjmedia-audiodev/audiodev.h b/pjmedia/include/pjmedia-audiodev/audiodev.h
index 2d72e09..e051f2f 100644
--- a/pjmedia/include/pjmedia-audiodev/audiodev.h
+++ b/pjmedia/include/pjmedia-audiodev/audiodev.h
@@ -17,8 +17,8 @@
  * along with this program; if not, write to the Free Software
  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA 
  */
-#ifndef __AUDIODEV_H__
-#define __AUDIODEV_H__
+#ifndef __PJMEDIA_AUDIODEV_AUDIODEV_H__
+#define __PJMEDIA_AUDIODEV_AUDIODEV_H__
 
 /**
  * @file audiodev.h
@@ -51,11 +51,21 @@
  * @{
  */
 
-/** Device identifier */
-typedef pj_int32_t pjmedia_aud_dev_id;
+/** 
+ * Type for device index.
+ */
+typedef pj_int32_t pjmedia_aud_dev_index;
 
-/** Constant to denote default ID */
-#define PJMEDIA_AUD_DEV_DEFAULT_ID  (-1)
+/** 
+ * Constant to denote default device 
+ */
+#define PJMEDIA_AUD_DEV_DEFAULT  (-1)
+
+/** 
+ * Type for device unique identifier. The unique device ID can be used to save
+ * a reference to a particular device across software reboots.
+ */
+typedef pj_uint32_t pjmedia_aud_dev_uid;
 
 
 /**
@@ -66,7 +76,7 @@
  * Applications get these capabilities in the #pjmedia_aud_dev_info structure.
  *
  * Application can also set the specific features/capabilities when opening
- * the audio stream by setting the \a flags member of #pjmedia_aud_dev_param
+ * the audio stream by setting the \a flags member of #pjmedia_aud_param
  * structure.
  *
  * Once audio stream is running, application can also retrieve or set some
@@ -117,14 +127,14 @@
      * The value of this capability is an unsigned integer representing 
      * the audio volume in percent.
      */
-    PJMEDIA_AUD_DEV_CAP_INPUT_SIGNAL_VOLUME = 32,
+    PJMEDIA_AUD_DEV_CAP_INPUT_SIGNAL_METER = 32,
 
     /** 
      * Support for monitoring the current audio output signal volume. 
      * The value of this capability is an unsigned integer representing 
      * the audio volume in percent.
      */
-    PJMEDIA_AUD_DEV_CAP_OUTPUT_SIGNAL_VOLUME = 64,
+    PJMEDIA_AUD_DEV_CAP_OUTPUT_SIGNAL_METER = 64,
 
     /** 
      * Support for audio input routing. The value of this capability is an 
@@ -141,7 +151,7 @@
 
     /** 
      * The audio device has echo cancellation feature. The value of this
-     * capability is an integer containing boolean PJ_TRUE or PJ_FALSE.
+     * capability is a pj_bool_t containing boolean PJ_TRUE or PJ_FALSE.
      */
     PJMEDIA_AUD_DEV_CAP_EC = 512,
 
@@ -154,21 +164,21 @@
 
     /** 
      * The audio device has voice activity detection feature. The value
-     * of this capability is an integer containing boolean PJ_TRUE or 
+     * of this capability is a pj_bool_t containing boolean PJ_TRUE or 
      * PJ_FALSE.
      */
     PJMEDIA_AUD_DEV_CAP_VAD = 2048,
 
     /** 
      * The audio device has comfort noise generation feature. The value
-     * of this capability is an integer containing boolean PJ_TRUE or 
+     * of this capability is a pj_bool_t containing boolean PJ_TRUE or 
      * PJ_FALSE.
      */
     PJMEDIA_AUD_DEV_CAP_CNG = 4096,
 
     /** 
      * The audio device has packet loss concealment feature. The value
-     * of this capability is an integer containing boolean PJ_TRUE or 
+     * of this capability is a pj_bool_t containing boolean PJ_TRUE or 
      * PJ_FALSE.
      */
     PJMEDIA_AUD_DEV_CAP_PLC = 8192
@@ -225,7 +235,7 @@
     /** 
      * The underlying driver name 
      */
-    char driver[128];
+    char driver[32];
 
     /** 
      * Device capabilities, as bitmask combination of #pjmedia_aud_dev_cap.
@@ -289,9 +299,9 @@
 					  pjmedia_frame *frame);
 
 /**
- * This structure specifies the parameters to open the audio device stream.
+ * This structure specifies the parameters to open the audio stream.
  */
-typedef struct pjmedia_aud_dev_param
+typedef struct pjmedia_aud_param
 {
     /**
      * The audio direction. This setting is mandatory.
@@ -302,13 +312,13 @@
      * The audio recorder device ID. This setting is mandatory if the audio
      * direction includes input/capture direction.
      */
-    pjmedia_aud_dev_id rec_id;
+    pjmedia_aud_dev_index rec_id;
 
     /**
      * The audio playback device ID. This setting is mandatory if the audio
      * direction includes output/playback direction.
      */
-    pjmedia_aud_dev_id play_id;
+    pjmedia_aud_dev_index play_id;
 
     /** 
      * Clock rate/sampling rate. This setting is mandatory. 
@@ -386,7 +396,7 @@
      */
     pj_bool_t cng_enabled;
 
-} pjmedia_aud_dev_param;
+} pjmedia_aud_param;
 
 
 /** Forward declaration for pjmedia_aud_stream */
@@ -431,6 +441,19 @@
 
 
 /**
+ * Get string info for the specified capability.
+ *
+ * @param cap		The capability ID.
+ * @param p_desc	Optional pointer which will be filled with longer 
+ *			description about the capability.
+ *
+ * @return		Capability name.
+ */
+PJ_DECL(const char*) pjmedia_aud_dev_cap_name(pjmedia_aud_dev_cap cap,
+					      const char **p_desc);
+
+
+/**
  * Get the number of sound devices installed in the system.
  *
  * @return		The number of sound devices installed in the system.
@@ -439,17 +462,6 @@
 
 
 /**
- * Enumerate device ID's.
- *
- * @param max_count	Maximum number of device id's to retrieve.
- * @param ids		Array to receive the device id's.
- *
- * @return		The actual number of device id's filled in.
- */
-PJ_DECL(unsigned) pjmedia_aud_dev_enum(unsigned max_count,
-				       pjmedia_aud_dev_id ids[]);
-
-/**
  * Get device information.
  *
  * @param id		The audio device ID.
@@ -459,11 +471,24 @@
  * @return		PJ_SUCCESS on successful operation or the appropriate
  *			error code.
  */
-PJ_DECL(pj_status_t) pjmedia_aud_dev_get_info(pjmedia_aud_dev_id id,
+PJ_DECL(pj_status_t) pjmedia_aud_dev_get_info(pjmedia_aud_dev_index id,
 					      pjmedia_aud_dev_info *info);
 
 
 /**
+ * Lookup device index based on the driver and device name.
+ *
+ * @param drv_name	The driver name.
+ * @param dev_name	The device name.
+ *
+ * @return		PJ_SUCCESS if the device can be found.
+ */
+PJ_DECL(pj_status_t) pjmedia_aud_dev_lookup(const char *drv_name,
+					    const char *dev_name,
+					    pjmedia_aud_dev_index *id);
+
+
+/**
  * Initialize the audio device parameters with default values for the
  * specified device.
  *
@@ -474,8 +499,8 @@
  * @return		PJ_SUCCESS on successful operation or the appropriate
  *			error code.
  */
-PJ_DECL(pj_status_t) pjmedia_aud_dev_default_param(pjmedia_aud_dev_id id,
-						   pjmedia_aud_dev_param *param);
+PJ_DECL(pj_status_t) pjmedia_aud_dev_default_param(pjmedia_aud_dev_index id,
+						   pjmedia_aud_param *param);
 
 
 /**
@@ -492,11 +517,11 @@
  * @return		PJ_SUCCESS on successful operation or the appropriate
  *			error code.
  */
-PJ_DECL(pj_status_t) pjmedia_aud_stream_create(const pjmedia_aud_dev_param *param,
+PJ_DECL(pj_status_t) pjmedia_aud_stream_create(const pjmedia_aud_param *param,
 					       pjmedia_aud_rec_cb rec_cb,
 					       pjmedia_aud_play_cb play_cb,
 					       void *user_data,
-					       pjmedia_aud_stream **p_aud_strm);
+					       pjmedia_aud_stream **p_strm);
 
 /**
  * Get the running parameters for the specified audio stream.
@@ -509,7 +534,7 @@
  *			error code.
  */
 PJ_DECL(pj_status_t) pjmedia_aud_stream_get_param(pjmedia_aud_stream *strm,
-						  pjmedia_aud_dev_param *param);
+						  pjmedia_aud_param *param);
 
 /**
  * Get the value of a specific capability of the audio stream.
@@ -580,6 +605,9 @@
 /* Device not ready */
 #define PJMEDIA_EAUD_NOTREADY	-1
 
+/* Invalid audio capability or audio capability not supported */
+#define PJMEDIA_EAUD_INVCAP	-1
+
 /* Unknown system error */
 #define PJMEDIA_EAUD_SYSERR	-1
 
@@ -591,5 +619,5 @@
 PJ_END_DECL
 
 
-#endif	/* __AUDIODEV_H__ */
+#endif	/* __PJMEDIA_AUDIODEV_AUDIODEV_H__ */
 
diff --git a/pjmedia/include/pjmedia-audiodev/audiodev_imp.h b/pjmedia/include/pjmedia-audiodev/audiodev_imp.h
index 7251e90..a05b34c 100644
--- a/pjmedia/include/pjmedia-audiodev/audiodev_imp.h
+++ b/pjmedia/include/pjmedia-audiodev/audiodev_imp.h
@@ -79,14 +79,14 @@
      */
     pj_status_t (*default_param)(pjmedia_aud_dev_factory *f,
 				 unsigned index,
-				 pjmedia_aud_dev_param *param);
+				 pjmedia_aud_param *param);
 
     /**
      * Open the audio device and create audio stream. See
      * #pjmedia_aud_stream_create()
      */
     pj_status_t (*create_stream)(pjmedia_aud_dev_factory *f,
-				 const pjmedia_aud_dev_param *param,
+				 const pjmedia_aud_param *param,
 				 pjmedia_aud_rec_cb rec_cb,
 				 pjmedia_aud_play_cb play_cb,
 				 void *user_data,
@@ -119,7 +119,7 @@
      * See #pjmedia_aud_stream_get_param()
      */
     pj_status_t (*get_param)(pjmedia_aud_stream *strm,
-			     pjmedia_aud_dev_param *param);
+			     pjmedia_aud_param *param);
 
     /**
      * See #pjmedia_aud_stream_get_cap()
@@ -158,8 +158,8 @@
  */
 struct pjmedia_aud_stream
 {
-    /** Factory */
-    pjmedia_aud_dev_factory *factory;
+    /** Factory id (internal) */
+    unsigned factory_id;
 
     /** Operations */
     pjmedia_aud_stream_op *op;
diff --git a/pjmedia/include/pjmedia-audiodev/audiotest.h b/pjmedia/include/pjmedia-audiodev/audiotest.h
index e247078..16deec7 100644
--- a/pjmedia/include/pjmedia-audiodev/audiotest.h
+++ b/pjmedia/include/pjmedia-audiodev/audiotest.h
@@ -17,8 +17,8 @@
  * along with this program; if not, write to the Free Software
  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA 
  */
-#ifndef __AUDIOTEST_H__
-#define __AUDIOTEST_H__
+#ifndef __PJMEDIA_AUDIODEV_AUDIOTEST_H__
+#define __PJMEDIA_AUDIODEV_AUDIOTEST_H__
 
 /**
  * @file audiotest.h
@@ -91,7 +91,7 @@
 /**
  * Perform audio device testing.
  */
-PJ_DECL(pj_status_t) pjmedia_aud_test(const pjmedia_aud_dev_param *param,
+PJ_DECL(pj_status_t) pjmedia_aud_test(const pjmedia_aud_param *param,
 				      pjmedia_aud_test_results *result);
 
 /**
@@ -101,6 +101,6 @@
 PJ_END_DECL
 
 
-#endif	/* __AUDIOTEST_H__ */
+#endif	/* __PJMEDIA_AUDIODEV_AUDIOTEST_H__ */
 
 
diff --git a/pjmedia/include/pjmedia.h b/pjmedia/include/pjmedia.h
index 55e8977..836a399 100644
--- a/pjmedia/include/pjmedia.h
+++ b/pjmedia/include/pjmedia.h
@@ -52,7 +52,10 @@
 #include <pjmedia/sdp_neg.h>
 #include <pjmedia/session.h>
 #include <pjmedia/silencedet.h>
+/* This sound API is deprecated. Please see:
+http://trac.pjsip.org/repos/wiki/Audio_Dev_API
 #include <pjmedia/sound.h>
+*/
 #include <pjmedia/sound_port.h>
 #include <pjmedia/splitcomb.h>
 #include <pjmedia/stereo.h>
diff --git a/pjmedia/include/pjmedia/config.h b/pjmedia/include/pjmedia/config.h
index 870b228..915b732 100644
--- a/pjmedia/include/pjmedia/config.h
+++ b/pjmedia/include/pjmedia/config.h
@@ -64,99 +64,68 @@
  * Types of sound stream backends.
  */
 
-/** Constant for NULL sound backend. */
-#define PJMEDIA_SOUND_NULL_SOUND	    0
-
-/** Constant for PortAudio sound backend. */
-#define PJMEDIA_SOUND_PORTAUDIO_SOUND	    1
-
-/** Constant for Win32 DirectSound sound backend. */
-#define PJMEDIA_SOUND_WIN32_DIRECT_SOUND    2
-
-/** Constant for Win32 MME sound backend. */
-#define PJMEDIA_SOUND_WIN32_MME_SOUND	    3
-
-/** Constant for Symbian Multimedia Audio Stream backend. */
-#define PJMEDIA_SOUND_SYMB_MDA_SOUND	    4
-
-/** Constant for Symbian APS backend. */
-#define PJMEDIA_SOUND_SYMB_APS_SOUND	    5
-
-/** Constant for Symbian VAS backend. */
-#define PJMEDIA_SOUND_SYMB_VAS_SOUND	    6
-
-
-/** When this is set, pjmedia will not provide any sound device backend. 
- *  Application will have to provide its own sound device backend
- *  and link the application with it.
- */
-#define PJMEDIA_SOUND_EXTERNAL		    255
-
-
 /**
- * Unless specified otherwise, sound device uses PortAudio implementation
- * by default.
+ * This macro has been deprecated in releasee 1.1. Please see
+ * http://trac.pjsip.org/repos/wiki/Audio_Dev_API for more information.
  */
-#ifndef PJMEDIA_SOUND_IMPLEMENTATION
-#  if defined(PJ_WIN32) && PJ_WIN32!=0
-/*#   define PJMEDIA_SOUND_IMPLEMENTATION   PJMEDIA_SOUND_WIN32_DIRECT_SOUND*/
-/*#   define PJMEDIA_SOUND_IMPLEMENTATION   PJMEDIA_SOUND_WIN32_MME_SOUND*/
-#   define PJMEDIA_SOUND_IMPLEMENTATION	    PJMEDIA_SOUND_PORTAUDIO_SOUND
-#  else
-#   define PJMEDIA_SOUND_IMPLEMENTATION	    PJMEDIA_SOUND_PORTAUDIO_SOUND
-#  endif
+#if defined(PJMEDIA_SOUND_IMPLEMENTATION)
+#   error PJMEDIA_SOUND_IMPLEMENTATION has been deprecated
 #endif
 
 /**
- * Specify if the sound device implementation supports handling encoded
- * frames. Setting this to zero will activate some emulation in the
- * sound port.
+ * This macro has been deprecated in releasee 1.1. Please see
+ * http://trac.pjsip.org/repos/wiki/Audio_Dev_API for more information.
  */
-#if PJMEDIA_SOUND_IMPLEMENTATION==PJMEDIA_SOUND_SYMB_APS_SOUND || \
-    PJMEDIA_SOUND_IMPLEMENTATION==PJMEDIA_SOUND_SYMB_VAS_SOUND
-#   define PJMEDIA_SND_SUPPORT_OPEN2	1
-#else
-#   define PJMEDIA_SND_SUPPORT_OPEN2	0
+#if defined(PJMEDIA_PREFER_DIRECT_SOUND)
+#   error PJMEDIA_PREFER_DIRECT_SOUND has been deprecated
 #endif
 
 /**
- * Specify whether we prefer to use DirectSound on Windows.
- *
- * Default: 0
+ * Setting PJMEDIA_AUDIO_API to this value will completely deprecate the use
+ * of old API, and inclusion of <pjmedia/sound.h> in the code will raise 
+ * compilation error.
  */
-#ifndef PJMEDIA_PREFER_DIRECT_SOUND
-#   define PJMEDIA_PREFER_DIRECT_SOUND	    0
+#define PJMEDIA_AUDIO_API_NEW_ONLY	    1
+
+/**
+ * Setting PJMEDIA_AUDIO_API to this value enables application to use the old
+ * sound device API to access audio devices provided by the new audio device 
+ * API. 
+ */
+#define PJMEDIA_AUDIO_API_HAS_OLD_API	    2
+
+/**
+ * Setting PJMEDIA_AUDIO_API to this value enables old sound device 
+ * implementation to be accessed via both old and new API's. 
+ */
+#define PJMEDIA_AUDIO_API_HAS_OLD_DEVICE    3
+
+/**
+ * Specify how the audio API should handle compatibility with old sound API.
+ * Valid values are: PJMEDIA_AUDIO_API_HAS_OLD_API (default, 
+ * PJMEDIA_AUDIO_API_NEW_ONLY, or PJMEDIA_AUDIO_API_HAS_OLD_DEVICE. Please
+ * see http://trac.pjsip.org/repos/wiki/Audio_Dev_API for more info.
+ */
+#ifndef PJMEDIA_AUDIO_API
+#   define PJMEDIA_AUDIO_API		    PJMEDIA_AUDIO_API_NEW_ONLY
 #endif
 
 
 /**
- * Specify sound device latency default, in milisecond.
+ * Specify default sound device latency, in milisecond.
  */
 #ifndef PJMEDIA_SND_DEFAULT_REC_LATENCY
 #   define PJMEDIA_SND_DEFAULT_REC_LATENCY  100
 #endif
 
+/**
+ * Specify default sound device latency, in milisecond.
+ */
 #ifndef PJMEDIA_SND_DEFAULT_PLAY_LATENCY
 #   define PJMEDIA_SND_DEFAULT_PLAY_LATENCY 100
 #endif
 
 
-/**
- * Specify whether delay buffer is used for sound device.
- * When delay buffer is enabled, the sound device callback 
- * will be called one after another evenly.
- * The delay buffer also performs the best delay calculation
- * for the sound device, and will try to limit the delay caused
- * by uneven callback calls to this delay.
- *
- * When this setting is enabled, the PJMEDIA_SOUND_BUFFER_COUNT
- * macro will specify the maximum size of the delay buffer.
- */
-#ifndef PJMEDIA_SOUND_USE_DELAYBUF
-#   define PJMEDIA_SOUND_USE_DELAYBUF	    0
-#endif
-
-
 /*
  * Types of WSOLA backend algorithm.
  */
diff --git a/pjmedia/include/pjmedia/endpoint.h b/pjmedia/include/pjmedia/endpoint.h
index 0d8dd39..2cc386f 100644
--- a/pjmedia/include/pjmedia/endpoint.h
+++ b/pjmedia/include/pjmedia/endpoint.h
@@ -37,7 +37,6 @@
  * to create a media session (#pjmedia_session_create()).
  */
 
-#include <pjmedia/sound.h>
 #include <pjmedia/codec.h>
 #include <pjmedia/sdp.h>
 
diff --git a/pjmedia/include/pjmedia/sound.h b/pjmedia/include/pjmedia/sound.h
index 8fe3aac..27ac0dd 100644
--- a/pjmedia/include/pjmedia/sound.h
+++ b/pjmedia/include/pjmedia/sound.h
@@ -21,6 +21,15 @@
 #define __PJMEDIA_SOUND_H__
 
 
+/* This is legacy sound device code, which has been superseded by the
+ * new pjmedia-audiodev framework. Please see the documentation on how
+ * to use this legacy API.
+ */
+#if PJMEDIA_AUDIO_API==PJMEDIA_AUDIO_API_NEW_ONLY
+#   error "The sound device API is deprecated. Please see doc for details."
+#endif
+
+
 /**
  * @file sound.h
  * @brief Sound player and recorder device framework.
diff --git a/pjmedia/include/pjmedia/sound_port.h b/pjmedia/include/pjmedia/sound_port.h
index 4c943a2..7293a4d 100644
--- a/pjmedia/include/pjmedia/sound_port.h
+++ b/pjmedia/include/pjmedia/sound_port.h
@@ -24,7 +24,7 @@
  * @file sound_port.h
  * @brief Media port connection abstraction to sound device.
  */
-#include <pjmedia/sound.h>
+#include <pjmedia-audiodev/audiodev.h>
 #include <pjmedia/port.h>
 
 PJ_BEGIN_DECL
@@ -162,39 +162,17 @@
 
 
 /**
- * Create unidirectional or bidirectional sound port. This also allows
- * creating sound port with extended settings, e.g: audio format, see 
- * #pjmedia_snd_setting.
+ * Create sound device port according to the specified parameters.
  *
  * @param pool		    Pool to allocate sound port structure.
- * @param dir		    Sound device direction.
- * @param rec_id	    Device index for recorder/capture stream, or
- *			    -1 to use the first capable device.
- * @param play_id	    Device index for playback stream, or -1 to use 
- *			    the first capable device.
- * @param clock_rate	    Sound device's clock rate to set.
- * @param channel_count	    Set number of channels, 1 for mono, or 2 for
- *			    stereo. The channel count determines the format
- *			    of the frame.
- * @param samples_per_frame Number of samples per frame.
- * @param bits_per_sample   Set the number of bits per sample. The normal 
- *			    value for this parameter is 16 bits per sample.
- * @param setting	    Sound device extended settings, see 
- *			    #pjmedia_snd_setting.
+ * @param prm		    Sound device settings.
  * @param p_port	    Pointer to receive the sound device port instance.
  *
  * @return		    PJ_SUCCESS on success, or the appropriate error
  *			    code.
  */
 PJ_DECL(pj_status_t) pjmedia_snd_port_create2(pj_pool_t *pool,
-					      pjmedia_dir dir,
-					      int rec_id,
-					      int play_id,
-					      unsigned clock_rate,
-					      unsigned channel_count,
-					      unsigned samples_per_frame,
-					      unsigned bits_per_sample,
-					      const pjmedia_snd_setting *setting,
+					      const pjmedia_aud_param *prm,
 					      pjmedia_snd_port **p_port);
 
 
@@ -216,19 +194,23 @@
  *
  * @return		    The sound stream instance.
  */
-PJ_DECL(pjmedia_snd_stream*) pjmedia_snd_port_get_snd_stream(
+PJ_DECL(pjmedia_aud_stream*) pjmedia_snd_port_get_snd_stream(
 						pjmedia_snd_port *snd_port);
 
 
 /**
- * Configure the echo cancellation tail length. By default, echo canceller
- * is enabled in the sound device with the default tail length. After the
- * sound port is created, application can query the current echo canceller
- * tail length by calling #pjmedia_snd_port_get_ec_tail.
+ * Change the echo cancellation settings. The echo cancellation settings 
+ * should have been specified when this sound port was created, by setting
+ * the appropriate fields in the pjmedia_aud_param, because not all sound
+ * device implementation supports changing the EC setting once the device
+ * has been opened.
  *
- * Note that you should only change the EC settings when the sound port
- * is not connected to any downstream ports, otherwise race condition may
- * occur.
+ * The behavior of this function depends on whether device or software AEC
+ * is being used. If the device supports AEC, this function will forward
+ * the change request to the device and it will be up to the device whether
+ * to support the request. If software AEC is being used (the software EC
+ * will be used if the device does not support AEC), this function will
+ * change the software EC settings.
  *
  * @param snd_port	    The sound device port.
  * @param pool		    Pool to re-create the echo canceller if necessary.
@@ -236,6 +218,7 @@
  *			    miliseconds. If zero is specified, the EC would
  *			    be disabled.
  * @param options	    The options to be passed to #pjmedia_echo_create().
+ *			    This is only used if software EC is being used.
  *
  * @return		    PJ_SUCCESS on success.
  */
diff --git a/pjmedia/include/pjmedia/stream.h b/pjmedia/include/pjmedia/stream.h
index cf5ef47..0227f64 100644
--- a/pjmedia/include/pjmedia/stream.h
+++ b/pjmedia/include/pjmedia/stream.h
@@ -26,7 +26,6 @@
  * @brief Media Stream.
  */
 
-#include <pjmedia/sound.h>
 #include <pjmedia/codec.h>
 #include <pjmedia/endpoint.h>
 #include <pjmedia/port.h>
diff --git a/pjmedia/src/pjmedia-audiodev/audiodev.c b/pjmedia/src/pjmedia-audiodev/audiodev.c
index e2f209d..33e333c 100644
--- a/pjmedia/src/pjmedia-audiodev/audiodev.c
+++ b/pjmedia/src/pjmedia-audiodev/audiodev.c
@@ -19,15 +19,41 @@
  */
 #include <pjmedia-audiodev/audiodev_imp.h>
 #include <pj/errno.h>
+#include <pj/log.h>
+#include <pj/string.h>
+
+#define THIS_FILE   "audiodev.c"
+
+/* Capability names */
+static struct cap_info
+{
+    const char *name;
+    const char *info;
+} cap_infos[] = 
+{
+    {"ext-fmt",	    "Extended/non-PCM format"},
+    {"latency-in",  "Input latency/buffer size setting"},
+    {"latency-out", "Output latency/buffer size setting"},
+    {"vol-in",	    "Input volume setting"},
+    {"vol-out",	    "Output volume setting"},
+    {"meter-in",    "Input meter"},
+    {"meter-out",   "Output meter"},
+    {"route-in",    "Input routing"},
+    {"route-out",   "Output routing"},
+    {"aec",	    "Accoustic echo cancellation"},
+    {"aec-tail",    "Tail length setting for AEC"},
+    {"vad",	    "Voice activity detection"},
+    {"cng",	    "Comfort noise generation"},
+    {"plg",	    "Packet loss concealment"}
+};
+
 
 /*
- * The Device ID seen by application and driver is different. 
+ * The device index seen by application and driver is different. 
  *
- * At application level, device ID is a 32bit value. The high 16bit contains
- * the factory ID, and the low 16bit contains the device index in the 
- * specified factory. The device ID may also be -1 to denote default device.
- *
- * At driver level, device ID is a 16bit unsigned integer index.
+ * At application level, device index is index to global list of device.
+ * At driver level, device index is index to device list on that particular
+ * factory only.
  */
 #define MAKE_DEV_ID(f_id, index)   (((f_id & 0xFFFF) << 16) | (index & 0xFFFF))
 #define GET_INDEX(dev_id)	   ((dev_id) & 0xFFFF)
@@ -40,20 +66,32 @@
 pjmedia_aud_dev_factory* pjmedia_wmme_factory(pj_pool_factory *pf);
 
 #define MAX_DRIVERS	16
+#define MAX_DEVS	64
+
+/* typedef for factory creation function */
+typedef pjmedia_aud_dev_factory*  (*create_func_ptr)(pj_pool_factory*);
+
+/* driver structure */
+struct driver
+{
+    create_func_ptr	     create;	/* Creation function.		    */
+    pjmedia_aud_dev_factory *f;		/* Factory instance.		    */
+    char		     name[32];	/* Driver name			    */
+    unsigned		     dev_cnt;	/* Number of devices		    */
+    unsigned		     start_idx;	/* Start index in global list	    */
+};
 
 /* The audio subsystem */
 static struct aud_subsys
 {
-    unsigned	     init_count;
-    pj_pool_factory *pf;
-    unsigned	     factory_cnt;
+    unsigned	     init_count;	/* How many times init() is called  */
+    pj_pool_factory *pf;		/* The pool factory.		    */
 
-    struct factory
-    {
-	pjmedia_aud_dev_factory*   (*create)(pj_pool_factory*);
-	pjmedia_aud_dev_factory	    *f;
+    unsigned	     drv_cnt;		/* Number of drivers.		    */
+    struct driver    drv[MAX_DRIVERS];	/* Array of drivers.		    */
 
-    } factories[MAX_DRIVERS];
+    unsigned	     dev_cnt;		/* Total number of devices.	    */
+    pj_uint32_t	     dev_list[MAX_DEVS];/* Array of device IDs.		    */
 
 } aud_subsys;
 
@@ -73,29 +111,67 @@
     }
 
     aud_subsys.pf = pf;
-    aud_subsys.factory_cnt = 0;
+    aud_subsys.drv_cnt = 0;
+    aud_subsys.dev_cnt = 0;
 
-    aud_subsys.factories[aud_subsys.factory_cnt++].create = &pjmedia_pa_factory;
-    aud_subsys.factories[aud_subsys.factory_cnt++].create = &pjmedia_wmme_factory;
+    /* Register creation functions */
+    aud_subsys.drv[aud_subsys.drv_cnt++].create = &pjmedia_pa_factory;
+    aud_subsys.drv[aud_subsys.drv_cnt++].create = &pjmedia_wmme_factory;
 
-    for (i=0; i<aud_subsys.factory_cnt; ++i) {
+    /* Initialize each factory and build the device ID list */
+    for (i=0; i<aud_subsys.drv_cnt; ++i) {
 	pjmedia_aud_dev_factory *f;
+	pjmedia_aud_dev_info info;
+	unsigned j, dev_cnt;
 
-	f = (*aud_subsys.factories[i].create)(pf);
+	/* Create the factory */
+	f = (*aud_subsys.drv[i].create)(pf);
 	if (!f)
 	    continue;
 
+	/* Call factory->init() */
 	status = f->op->init(f);
 	if (status != PJ_SUCCESS) {
 	    f->op->destroy(f);
 	    continue;
 	}
 
-	aud_subsys.factories[i].f = f;
-	aud_subsys.factories[i].f->internal.id = i;
+	/* Build device list */
+	dev_cnt = f->op->get_dev_count(f);
+	if (dev_cnt == 0) {
+	    f->op->destroy(f);
+	    continue;
+	}
+
+	/* Get one device info */
+	status = f->op->get_dev_info(f, 0, &info);
+	if (status != PJ_SUCCESS) {
+	    f->op->destroy(f);
+	    continue;
+	}
+
+	/* Register the factory */
+	aud_subsys.drv[i].f = f;
+	aud_subsys.drv[i].f->internal.id = i;
+	aud_subsys.drv[i].start_idx = aud_subsys.dev_cnt;
+	pj_ansi_strncpy(aud_subsys.drv[i].name, info.driver,
+			sizeof(aud_subsys.drv[i].name));
+	aud_subsys.drv[i].name[sizeof(aud_subsys.drv[i].name)-1] = '\0';
+
+	/* Register devices */
+	if (aud_subsys.dev_cnt + dev_cnt > MAX_DEVS) {
+	    PJ_LOG(4,(THIS_FILE, "%d device(s) cannot be registered because"
+				  " there are too many sound devices",
+				  aud_subsys.dev_cnt + dev_cnt - MAX_DEVS));
+	    dev_cnt = MAX_DEVS - aud_subsys.dev_cnt;
+	}
+	for (j=0; j<dev_cnt; ++j) {
+	    aud_subsys.dev_list[aud_subsys.dev_cnt++] = MAKE_DEV_ID(i, j);
+	}
+
     }
 
-    return aud_subsys.factory_cnt ? PJ_SUCCESS : status;
+    return aud_subsys.drv_cnt ? PJ_SUCCESS : status;
 }
 
 /* API: get the pool factory registered to the audio subsystem. */
@@ -117,167 +193,239 @@
     }
     --aud_subsys.init_count;
 
-    for (i=0; i<aud_subsys.factory_cnt; ++i) {
-	pjmedia_aud_dev_factory *f = aud_subsys.factories[i].f;
+    for (i=0; i<aud_subsys.drv_cnt; ++i) {
+	pjmedia_aud_dev_factory *f = aud_subsys.drv[i].f;
 
 	if (!f)
 	    continue;
 
 	f->op->destroy(f);
-	aud_subsys.factories[i].f = NULL;
+	aud_subsys.drv[i].f = NULL;
     }
 
     return PJ_SUCCESS;
 }
 
+/* API: get capability name/info */
+PJ_DEF(const char*) pjmedia_aud_dev_cap_name(pjmedia_aud_dev_cap cap,
+					     const char **p_desc)
+{
+    char *desc;
+    unsigned i;
+
+    if (p_desc==NULL) p_desc = &desc;
+
+    for (i=0; i<PJ_ARRAY_SIZE(cap_infos); ++i) {
+	if ((1 << i)==cap)
+	    break;
+    }
+
+    if (i==32) {
+	*p_desc = "??";
+	return "??";
+    }
+
+    *p_desc = cap_infos[i].info;
+    return cap_infos[i].name;
+}
+
 /* API: Get the number of sound devices installed in the system. */
 PJ_DEF(unsigned) pjmedia_aud_dev_count(void)
 {
-    unsigned i, count = 0;
-
-    for (i=0; i<aud_subsys.factory_cnt; ++i) {
-	pjmedia_aud_dev_factory *f = aud_subsys.factories[i].f;
-
-	if (!f)
-	    continue;
-
-	count += f->op->get_dev_count(f);
-    }
-
-    return count;
+    return aud_subsys.dev_cnt;
 }
 
-/* API: Enumerate device ID's. */
-PJ_DEF(unsigned) pjmedia_aud_dev_enum(unsigned max_count,
-				      pjmedia_aud_dev_id ids[])
+/* Internal: lookup device id */
+static pj_status_t lookup_dev(pjmedia_aud_dev_index id,
+			      pjmedia_aud_dev_factory **p_f,
+			      unsigned *p_local_index)
 {
-    unsigned i, count = 0;
+    int f_id, index;
 
-    for (i=0; i<aud_subsys.factory_cnt && count < max_count; ++i) {
-	pjmedia_aud_dev_factory *f = aud_subsys.factories[i].f;
-	unsigned j, fcount;
+    if (id == PJMEDIA_AUD_DEV_DEFAULT)
+	id = DEFAULT_DEV_ID;
 
-	if (!f)
-	    continue;
+    PJ_ASSERT_RETURN(id>=0 && id<(int)aud_subsys.dev_cnt, 
+		     PJMEDIA_EAUD_INVDEV);
 
-	fcount = f->op->get_dev_count(f);
-	for (j=0; j<fcount && count<max_count; ++j) {
-	    ids[count++] = MAKE_DEV_ID(i, j);
-	}
-    }
+    f_id = GET_FID(aud_subsys.dev_list[id]);
+    index = GET_INDEX(aud_subsys.dev_list[id]);
 
-    return count;
+    if (f_id < 0 || f_id >= (int)aud_subsys.drv_cnt)
+	return PJMEDIA_EAUD_INVDEV;
+
+    if (index < 0 || index >= (int)aud_subsys.drv[f_id].dev_cnt)
+	return PJMEDIA_EAUD_INVDEV;
+
+    *p_f = aud_subsys.drv[f_id].f;
+    *p_local_index = (unsigned)index;
+
+    return PJ_SUCCESS;
+
 }
 
+/* Internal: convert local index to global device index */
+static pj_status_t make_global_index(pjmedia_aud_dev_factory *f,
+				     pjmedia_aud_dev_index *id)
+{
+    unsigned f_id = f->internal.id;
+
+    if (*id == PJMEDIA_AUD_DEV_DEFAULT)
+	return PJ_SUCCESS;
+
+    /* Check that factory still exists */
+    PJ_ASSERT_RETURN(f, PJ_EBUG);
+
+    /* Check that device index is valid */
+    PJ_ASSERT_RETURN(*id>=0 && *id<(int)aud_subsys.drv[f_id].dev_cnt, PJ_EBUG);
+
+    *id += aud_subsys.drv[f_id].start_idx;
+    return PJ_SUCCESS;
+}
 
 /* API: Get device information. */
-PJ_DEF(pj_status_t) pjmedia_aud_dev_get_info(pjmedia_aud_dev_id id,
+PJ_DEF(pj_status_t) pjmedia_aud_dev_get_info(pjmedia_aud_dev_index id,
 					     pjmedia_aud_dev_info *info)
 {
     pjmedia_aud_dev_factory *f;
-    int f_id, index;
+    unsigned index;
+    pj_status_t status;
 
-    if (id == PJMEDIA_AUD_DEV_DEFAULT_ID)
-	id = DEFAULT_DEV_ID;
+    PJ_ASSERT_RETURN(info, PJ_EINVAL);
 
-    f_id = GET_FID(id);
-    index = GET_INDEX(id);
-
-    if (f_id < 0 || f_id >= (int)aud_subsys.factory_cnt)
-	return PJMEDIA_EAUD_INVDEV;
-
-    f = aud_subsys.factories[f_id].f;
-    if (f == NULL)
-	return PJMEDIA_EAUD_INVDEV;
+    status = lookup_dev(id, &f, &index);
+    if (status != PJ_SUCCESS)
+	return status;
 
     return f->op->get_dev_info(f, index, info);
 }
 
+/* API: find device */
+PJ_DEF(pj_status_t) pjmedia_aud_dev_lookup( const char *drv_name,
+					    const char *dev_name,
+					    pjmedia_aud_dev_index *id)
+{
+    pjmedia_aud_dev_factory *f = NULL;
+    unsigned i, j;
+
+    PJ_ASSERT_RETURN(drv_name && dev_name && id, PJ_EINVAL);
+
+    for (i=0; i<aud_subsys.drv_cnt; ++i) {
+	if (!pj_ansi_stricmp(drv_name, aud_subsys.drv[i].name)) {
+	    f = aud_subsys.drv[i].f;
+	    break;
+	}
+    }
+
+    if (!f)
+	return PJ_ENOTFOUND;
+
+    for (j=0; j<aud_subsys.drv[i].dev_cnt; ++j) {
+	pjmedia_aud_dev_info info;
+	pj_status_t status;
+
+	status = f->op->get_dev_info(f, j, &info);
+	if (status != PJ_SUCCESS)
+	    return status;
+
+	if (!pj_ansi_stricmp(dev_name, info.name))
+	    break;
+    }
+
+    if (j==aud_subsys.drv[i].dev_cnt)
+	return PJ_ENOTFOUND;
+
+    *id = j;
+    make_global_index(f, id);
+
+    return PJ_SUCCESS;
+}
+
 /* API: Initialize the audio device parameters with default values for the
  * specified device.
  */
-PJ_DEF(pj_status_t) pjmedia_aud_dev_default_param(pjmedia_aud_dev_id id,
-						  pjmedia_aud_dev_param *param)
+PJ_DEF(pj_status_t) pjmedia_aud_dev_default_param(pjmedia_aud_dev_index id,
+						  pjmedia_aud_param *param)
 {
     pjmedia_aud_dev_factory *f;
-    int f_id, index;
+    unsigned index;
     pj_status_t status;
 
-    if (id == PJMEDIA_AUD_DEV_DEFAULT_ID)
-	id = DEFAULT_DEV_ID;
+    PJ_ASSERT_RETURN(param, PJ_EINVAL);
 
-    f_id = GET_FID(id);
-    index = GET_INDEX(id);
-
-    if (f_id < 0 || f_id >= (int)aud_subsys.factory_cnt)
-	return PJMEDIA_EAUD_INVDEV;
-
-    f = aud_subsys.factories[f_id].f;
-    if (f == NULL)
-	return PJMEDIA_EAUD_INVDEV;
+    status = lookup_dev(id, &f, &index);
+    if (status != PJ_SUCCESS)
+	return status;
 
     status = f->op->default_param(f, index, param);
     if (status != PJ_SUCCESS)
 	return status;
 
     /* Normalize device IDs */
-    if (param->rec_id != PJMEDIA_AUD_DEV_DEFAULT_ID)
-	param->rec_id = MAKE_DEV_ID(f_id, param->rec_id);
-    if (param->play_id != PJMEDIA_AUD_DEV_DEFAULT_ID)
-	param->play_id = MAKE_DEV_ID(f_id, param->play_id);
+    make_global_index(f, &param->rec_id);
+    make_global_index(f, &param->play_id);
 
     return PJ_SUCCESS;
 }
 
 /* API: Open audio stream object using the specified parameters. */
-PJ_DEF(pj_status_t) pjmedia_aud_stream_create(const pjmedia_aud_dev_param *p,
+PJ_DEF(pj_status_t) pjmedia_aud_stream_create(const pjmedia_aud_param *prm,
 					      pjmedia_aud_rec_cb rec_cb,
 					      pjmedia_aud_play_cb play_cb,
 					      void *user_data,
 					      pjmedia_aud_stream **p_aud_strm)
 {
-    pjmedia_aud_dev_factory *f;
-    pjmedia_aud_dev_param param;
-    int f_id;
+    pjmedia_aud_dev_factory *rec_f=NULL, *play_f=NULL, *f=NULL;
+    pjmedia_aud_param param;
     pj_status_t status;
 
+    PJ_ASSERT_RETURN(prm && prm->dir && p_aud_strm, PJ_EINVAL);
+
     /* Must make copy of param because we're changing device ID */
-    pj_memcpy(&param, p, sizeof(param));
+    pj_memcpy(&param, prm, sizeof(param));
 
-    /* Set default device */
-    if (param.rec_id == PJMEDIA_AUD_DEV_DEFAULT_ID) 
-	param.rec_id = DEFAULT_DEV_ID;
-    if (param.play_id == PJMEDIA_AUD_DEV_DEFAULT_ID) 
-	param.play_id = DEFAULT_DEV_ID;
+    /* Normalize rec_id */
+    if (param.dir & PJMEDIA_DIR_CAPTURE) {
+	unsigned index;
+
+	status = lookup_dev(param.rec_id, &rec_f, &index);
+	if (status != PJ_SUCCESS)
+	    return status;
+
+	param.rec_id = index;
+	f = rec_f;
+    }
+
+    /* Normalize play_id */
+    if (param.dir & PJMEDIA_DIR_PLAYBACK) {
+	unsigned index;
+
+	status = lookup_dev(param.play_id, &play_f, &index);
+	if (status != PJ_SUCCESS)
+	    return status;
+
+	param.play_id = index;
+	f = play_f;
+
+	/* For now, rec_id and play_id must belong to the same factory */
+	PJ_ASSERT_RETURN(rec_f == play_f, PJ_EINVAL);
+    }
+
     
-    if (param.dir & PJMEDIA_DIR_CAPTURE)
-	f_id = GET_FID(param.rec_id);
-    else
-	f_id = GET_FID(param.play_id);
-
-    if (f_id < 0 || f_id >= (int)aud_subsys.factory_cnt)
-	return PJMEDIA_EAUD_INVDEV;
-    
-    /* Normalize device id's */
-    param.rec_id = GET_INDEX(param.rec_id);
-    param.play_id = GET_INDEX(param.play_id);
-
-    f = aud_subsys.factories[f_id].f;
-    if (f == NULL)
-	return PJMEDIA_EAUD_INVDEV;
-
+    /* Create the stream */
     status = f->op->create_stream(f, &param, rec_cb, play_cb,
 				  user_data, p_aud_strm);
     if (status != PJ_SUCCESS)
 	return status;
 
-    (*p_aud_strm)->factory = f;
+    /* Assign factory id to the stream */
+    (*p_aud_strm)->factory_id = f->internal.id;
     return PJ_SUCCESS;
 }
 
 /* API: Get the running parameters for the specified audio stream. */
 PJ_DEF(pj_status_t) pjmedia_aud_stream_get_param(pjmedia_aud_stream *strm,
-						 pjmedia_aud_dev_param *param)
+						 pjmedia_aud_param *param)
 {
     pj_status_t status;
 
@@ -286,10 +434,8 @@
 	return status;
 
     /* Normalize device id's */
-    if (param->rec_id != PJMEDIA_AUD_DEV_DEFAULT_ID)
-	param->rec_id = MAKE_DEV_ID(strm->factory->internal.id, param->rec_id);
-    if (param->play_id != PJMEDIA_AUD_DEV_DEFAULT_ID)
-	param->play_id = MAKE_DEV_ID(strm->factory->internal.id, param->play_id);
+    make_global_index(aud_subsys.drv[strm->factory_id].f, &param->rec_id);
+    make_global_index(aud_subsys.drv[strm->factory_id].f, &param->play_id);
 
     return PJ_SUCCESS;
 }
diff --git a/pjmedia/src/pjmedia-audiodev/audiotest.c b/pjmedia/src/pjmedia-audiodev/audiotest.c
index 563e1e8..120f710 100644
--- a/pjmedia/src/pjmedia-audiodev/audiotest.c
+++ b/pjmedia/src/pjmedia-audiodev/audiotest.c
@@ -51,7 +51,7 @@
 struct test_data 
 {
     pj_pool_t			   *pool;
-    const pjmedia_aud_dev_param    *param;
+    const pjmedia_aud_param    *param;
     pjmedia_aud_test_results	   *result;
     pj_bool_t			    running;
     pj_bool_t			    has_error;
@@ -181,7 +181,7 @@
 }
 
 
-PJ_DEF(pj_status_t) pjmedia_aud_test( const pjmedia_aud_dev_param *param,
+PJ_DEF(pj_status_t) pjmedia_aud_test( const pjmedia_aud_param *param,
 				      pjmedia_aud_test_results *result)
 {
     pj_status_t status = PJ_SUCCESS;
diff --git a/pjmedia/src/pjmedia-audiodev/pa_dev.c b/pjmedia/src/pjmedia-audiodev/pa_dev.c
index 0a0e7a2..4750877 100644
--- a/pjmedia/src/pjmedia-audiodev/pa_dev.c
+++ b/pjmedia/src/pjmedia-audiodev/pa_dev.c
@@ -105,9 +105,9 @@
 				    pjmedia_aud_dev_info *info);
 static pj_status_t  pa_default_param(pjmedia_aud_dev_factory *f,
 				     unsigned index,
-				     pjmedia_aud_dev_param *param);
+				     pjmedia_aud_param *param);
 static pj_status_t  pa_create_stream(pjmedia_aud_dev_factory *f,
-				     const pjmedia_aud_dev_param *param,
+				     const pjmedia_aud_param *param,
 				     pjmedia_aud_rec_cb rec_cb,
 				     pjmedia_aud_play_cb play_cb,
 				     void *user_data,
@@ -115,7 +115,7 @@
 
 /* Stream prototypes */
 static pj_status_t strm_get_param(pjmedia_aud_stream *strm,
-				  pjmedia_aud_dev_param *param);
+				  pjmedia_aud_param *param);
 static pj_status_t strm_get_cap(pjmedia_aud_stream *strm,
 	 		        pjmedia_aud_dev_cap cap,
 			        void *value);
@@ -514,7 +514,7 @@
 /* API: fill in with default parameter. */
 static pj_status_t  pa_default_param(pjmedia_aud_dev_factory *f,
 				     unsigned index,
-				     pjmedia_aud_dev_param *param)
+				     pjmedia_aud_param *param)
 {
     pjmedia_aud_dev_info adi;
     pj_status_t status;
@@ -533,11 +533,11 @@
     } else if (adi.input_count) {
 	param->dir = PJMEDIA_DIR_CAPTURE;
 	param->rec_id = index;
-	param->play_id = PJMEDIA_AUD_DEV_DEFAULT_ID;
+	param->play_id = PJMEDIA_AUD_DEV_DEFAULT;
     } else if (adi.output_count) {
 	param->dir = PJMEDIA_DIR_PLAYBACK;
 	param->play_id = index;
-	param->rec_id = PJMEDIA_AUD_DEV_DEFAULT_ID;
+	param->rec_id = PJMEDIA_AUD_DEV_DEFAULT;
     } else {
 	return PJMEDIA_EAUD_INVDEV;
     }
@@ -669,13 +669,13 @@
 
 /* Internal: create capture/recorder stream */
 static pj_status_t create_rec_stream( struct pa_aud_factory *pa,
-				      const pjmedia_aud_dev_param *param,
+				      const pjmedia_aud_param *param,
 				      pjmedia_aud_rec_cb rec_cb,
 				      void *user_data,
 				      pjmedia_aud_stream **p_snd_strm)
 {
     pj_pool_t *pool;
-    pjmedia_aud_dev_id rec_id;
+    pjmedia_aud_dev_index rec_id;
     struct pa_aud_stream *stream;
     PaStreamParameters inputParam;
     int sampleFormat;
@@ -774,13 +774,13 @@
 
 /* Internal: create playback stream */
 static pj_status_t create_play_stream(struct pa_aud_factory *pa,
-				      const pjmedia_aud_dev_param *param,
+				      const pjmedia_aud_param *param,
 				      pjmedia_aud_play_cb play_cb,
 				      void *user_data,
 				      pjmedia_aud_stream **p_snd_strm)
 {
     pj_pool_t *pool;
-    pjmedia_aud_dev_id play_id;
+    pjmedia_aud_dev_index play_id;
     struct pa_aud_stream *stream;
     PaStreamParameters outputParam;
     int sampleFormat;
@@ -881,14 +881,14 @@
 
 /* Internal: Create both player and recorder stream */
 static pj_status_t create_bidir_stream(struct pa_aud_factory *pa,
-				       const pjmedia_aud_dev_param *param,
+				       const pjmedia_aud_param *param,
 				       pjmedia_aud_rec_cb rec_cb,
 				       pjmedia_aud_play_cb play_cb,
 				       void *user_data,
 				       pjmedia_aud_stream **p_snd_strm)
 {
     pj_pool_t *pool;
-    pjmedia_aud_dev_id rec_id, play_id;
+    pjmedia_aud_dev_index rec_id, play_id;
     struct pa_aud_stream *stream;
     PaStream *paStream = NULL;
     PaStreamParameters inputParam;
@@ -1060,7 +1060,7 @@
 
 /* API: create stream */
 static pj_status_t  pa_create_stream(pjmedia_aud_dev_factory *f,
-				     const pjmedia_aud_dev_param *param,
+				     const pjmedia_aud_param *param,
 				     pjmedia_aud_rec_cb rec_cb,
 				     pjmedia_aud_play_cb play_cb,
 				     void *user_data,
@@ -1091,7 +1091,7 @@
 
 /* API: Get stream parameters */
 static pj_status_t strm_get_param(pjmedia_aud_stream *s,
-				  pjmedia_aud_dev_param *pi)
+				  pjmedia_aud_param *pi)
 {
     struct pa_aud_stream *strm = (struct pa_aud_stream*)s;
     const PaStreamInfo *paPlaySI = NULL, *paRecSI = NULL;
diff --git a/pjmedia/src/pjmedia-audiodev/symb_aps_dev.cpp b/pjmedia/src/pjmedia-audiodev/symb_aps_dev.cpp
index 4c38cc4..d931275 100644
--- a/pjmedia/src/pjmedia-audiodev/symb_aps_dev.cpp
+++ b/pjmedia/src/pjmedia-audiodev/symb_aps_dev.cpp
@@ -71,7 +71,7 @@
     pj_pool_t		*pool;			/**< Memory pool.       */
 
     // Common settings.
-    pjmedia_aud_dev_param param;		/**< Stream param.	*/
+    pjmedia_aud_param param;		/**< Stream param.	*/
     pjmedia_aud_rec_cb   rec_cb;		/**< Record callback.  	*/
     pjmedia_aud_play_cb	 play_cb;		/**< Playback callback. */
     void                *user_data;		/**< Application data.  */
@@ -100,16 +100,16 @@
 					pjmedia_aud_dev_info *info);
 static pj_status_t factory_default_param(pjmedia_aud_dev_factory *f,
 					 unsigned index,
-					 pjmedia_aud_dev_param *param);
+					 pjmedia_aud_param *param);
 static pj_status_t factory_create_stream(pjmedia_aud_dev_factory *f,
-					 const pjmedia_aud_dev_param *param,
+					 const pjmedia_aud_param *param,
 					 pjmedia_aud_rec_cb rec_cb,
 					 pjmedia_aud_play_cb play_cb,
 					 void *user_data,
 					 pjmedia_aud_stream **p_aud_strm);
 
 static pj_status_t stream_get_param(pjmedia_aud_stream *strm,
-				    pjmedia_aud_dev_param *param);
+				    pjmedia_aud_param *param);
 static pj_status_t stream_get_cap(pjmedia_aud_stream *strm,
 				  pjmedia_aud_dev_cap cap,
 				  void *value);
@@ -1175,7 +1175,7 @@
 /* API: create default device parameter */
 static pj_status_t factory_default_param(pjmedia_aud_dev_factory *f,
 					 unsigned index,
-					 pjmedia_aud_dev_param *param)
+					 pjmedia_aud_param *param)
 {
     struct aps_factory *af = (struct aps_factory*)f;
 
@@ -1199,7 +1199,7 @@
 
 /* API: create stream */
 static pj_status_t factory_create_stream(pjmedia_aud_dev_factory *f,
-					 const pjmedia_aud_dev_param *param,
+					 const pjmedia_aud_param *param,
 					 pjmedia_aud_rec_cb rec_cb,
 					 pjmedia_aud_play_cb play_cb,
 					 void *user_data,
@@ -1332,7 +1332,7 @@
 
 /* API: Get stream info. */
 static pj_status_t stream_get_param(pjmedia_aud_stream *s,
-				    pjmedia_aud_dev_param *pi)
+				    pjmedia_aud_param *pi)
 {
     struct aps_stream *strm = (struct aps_stream*)s;
 
diff --git a/pjmedia/src/pjmedia-audiodev/symb_mda_dev.cpp b/pjmedia/src/pjmedia-audiodev/symb_mda_dev.cpp
index e97807d..02fba93 100644
--- a/pjmedia/src/pjmedia-audiodev/symb_mda_dev.cpp
+++ b/pjmedia/src/pjmedia-audiodev/symb_mda_dev.cpp
@@ -72,7 +72,7 @@
     pj_pool_t		*pool;			/**< Memory pool.       */
 
     // Common settings.
-    pjmedia_aud_dev_param param;		/**< Stream param.	*/
+    pjmedia_aud_param param;		/**< Stream param.	*/
 
     // Audio engine
     CPjAudioInputEngine	*in_engine;		/**< Record engine.	*/
@@ -89,16 +89,16 @@
 					pjmedia_aud_dev_info *info);
 static pj_status_t factory_default_param(pjmedia_aud_dev_factory *f,
 					 unsigned index,
-					 pjmedia_aud_dev_param *param);
+					 pjmedia_aud_param *param);
 static pj_status_t factory_create_stream(pjmedia_aud_dev_factory *f,
-					 const pjmedia_aud_dev_param *param,
+					 const pjmedia_aud_param *param,
 					 pjmedia_aud_rec_cb rec_cb,
 					 pjmedia_aud_play_cb play_cb,
 					 void *user_data,
 					 pjmedia_aud_stream **p_aud_strm);
 
 static pj_status_t stream_get_param(pjmedia_aud_stream *strm,
-				    pjmedia_aud_dev_param *param);
+				    pjmedia_aud_param *param);
 static pj_status_t stream_get_cap(pjmedia_aud_stream *strm,
 				  pjmedia_aud_dev_cap cap,
 				  void *value);
@@ -844,7 +844,7 @@
 /* API: create default device parameter */
 static pj_status_t factory_default_param(pjmedia_aud_dev_factory *f,
 					 unsigned index,
-					 pjmedia_aud_dev_param *param)
+					 pjmedia_aud_param *param)
 {
     struct mda_factory *af = (struct mda_factory*)f;
 
@@ -866,7 +866,7 @@
 
 /* API: create stream */
 static pj_status_t factory_create_stream(pjmedia_aud_dev_factory *f,
-					 const pjmedia_aud_dev_param *param,
+					 const pjmedia_aud_param *param,
 					 pjmedia_aud_rec_cb rec_cb,
 					 pjmedia_aud_play_cb play_cb,
 					 void *user_data,
@@ -921,7 +921,7 @@
 
 /* API: Get stream info. */
 static pj_status_t stream_get_param(pjmedia_aud_stream *s,
-				    pjmedia_aud_dev_param *pi)
+				    pjmedia_aud_param *pi)
 {
     struct mda_stream *strm = (struct mda_stream*)s;
 
diff --git a/pjmedia/src/pjmedia-audiodev/wmme_dev.c b/pjmedia/src/pjmedia-audiodev/wmme_dev.c
index 5a80101..1458d30 100644
--- a/pjmedia/src/pjmedia-audiodev/wmme_dev.c
+++ b/pjmedia/src/pjmedia-audiodev/wmme_dev.c
@@ -120,16 +120,16 @@
 					pjmedia_aud_dev_info *info);
 static pj_status_t factory_default_param(pjmedia_aud_dev_factory *f,
 					 unsigned index,
-					 pjmedia_aud_dev_param *param);
+					 pjmedia_aud_param *param);
 static pj_status_t factory_create_stream(pjmedia_aud_dev_factory *f,
-					 const pjmedia_aud_dev_param *param,
+					 const pjmedia_aud_param *param,
 					 pjmedia_aud_rec_cb rec_cb,
 					 pjmedia_aud_play_cb play_cb,
 					 void *user_data,
 					 pjmedia_aud_stream **p_aud_strm);
 
 static pj_status_t stream_get_param(pjmedia_aud_stream *strm,
-				    pjmedia_aud_dev_param *param);
+				    pjmedia_aud_param *param);
 static pj_status_t stream_get_cap(pjmedia_aud_stream *strm,
 				  pjmedia_aud_dev_cap cap,
 				  void *value);
@@ -398,7 +398,7 @@
 /* API: create default device parameter */
 static pj_status_t factory_default_param(pjmedia_aud_dev_factory *f,
 					 unsigned index,
-					 pjmedia_aud_dev_param *param)
+					 pjmedia_aud_param *param)
 {
     struct wmme_factory *wf = (struct wmme_factory*)f;
     struct wmme_dev_info *di = &wf->dev_info[index];
@@ -413,11 +413,11 @@
     } else if (di->info.input_count) {
 	param->dir = PJMEDIA_DIR_CAPTURE;
 	param->rec_id = index;
-	param->play_id = PJMEDIA_AUD_DEV_DEFAULT_ID;
+	param->play_id = PJMEDIA_AUD_DEV_DEFAULT;
     } else if (di->info.output_count) {
 	param->dir = PJMEDIA_DIR_PLAYBACK;
 	param->play_id = index;
-	param->rec_id = PJMEDIA_AUD_DEV_DEFAULT_ID;
+	param->rec_id = PJMEDIA_AUD_DEV_DEFAULT;
     } else {
 	return PJMEDIA_EAUD_INVDEV;
     }
@@ -815,7 +815,7 @@
 
 /* API: create stream */
 static pj_status_t factory_create_stream(pjmedia_aud_dev_factory *f,
-					 const pjmedia_aud_dev_param *param,
+					 const pjmedia_aud_param *param,
 					 pjmedia_aud_rec_cb rec_cb,
 					 pjmedia_aud_play_cb play_cb,
 					 void *user_data,
@@ -929,7 +929,7 @@
 
 /* API: Get stream info. */
 static pj_status_t stream_get_param(pjmedia_aud_stream *s,
-				    pjmedia_aud_dev_param *pi)
+				    pjmedia_aud_param *pi)
 {
     struct wmme_stream *strm = (struct wmme_stream*)s;
 
diff --git a/pjmedia/src/pjmedia/conf_switch.c b/pjmedia/src/pjmedia/conf_switch.c
index 97cebbe..ca8f3cd 100644
--- a/pjmedia/src/pjmedia/conf_switch.c
+++ b/pjmedia/src/pjmedia/conf_switch.c
@@ -113,7 +113,6 @@
     unsigned		  max_ports;	/**< Maximum ports.		    */
     unsigned		  port_cnt;	/**< Current number of ports.	    */
     unsigned		  connect_cnt;	/**< Total number of connections    */
-    pjmedia_snd_port	 *snd_dev_port;	/**< Sound device port.		    */
     pjmedia_port	 *master_port;	/**< Port zero's port.		    */
     char		  master_name_buf[80]; /**< Port0 name buffer.	    */
     pj_mutex_t		 *mutex;	/**< Conference mutex.		    */
@@ -187,67 +186,15 @@
     pj_str_t name = { "Master/sound", 12 };
     pj_status_t status;
 
-
     status = create_conf_port(pool, conf, conf->master_port, &name, &conf_port);
     if (status != PJ_SUCCESS)
 	return status;
 
-
-    /* Create sound device port: */
-
-    if ((conf->options & PJMEDIA_CONF_NO_DEVICE) == 0) {
-	pjmedia_snd_stream *strm;
-	pjmedia_snd_stream_info si;
-	pjmedia_port_info *master_port_info = (pjmedia_port_info*)
-					      &conf->master_port->info;
-
-	/*
-	 * If capture is disabled then create player only port.
-	 * Otherwise create bidirectional sound device port.
-	 */
-	if (conf->options & PJMEDIA_CONF_NO_MIC)  {
-	    status = pjmedia_snd_port_create_player(
-					pool, -1, 
-					master_port_info->clock_rate,
-					master_port_info->channel_count,
-					master_port_info->samples_per_frame,
-					master_port_info->bits_per_sample, 
-					0,	/* options */
-					&conf->snd_dev_port);
-
-	} else {
-	    status = pjmedia_snd_port_create( 
-					pool, -1, -1, 
-					master_port_info->clock_rate, 
-					master_port_info->channel_count, 
-					master_port_info->samples_per_frame,
-					master_port_info->bits_per_sample,
-					0,    /* Options */
-					&conf->snd_dev_port);
-	}
-
-	if (status != PJ_SUCCESS)
-	    return status;
-
-	strm = pjmedia_snd_port_get_snd_stream(conf->snd_dev_port);
-	status = pjmedia_snd_stream_get_info(strm, &si);
-	if (status == PJ_SUCCESS) {
-	    const pjmedia_snd_dev_info *snd_dev_info;
-	    if (conf->options & PJMEDIA_CONF_NO_MIC)
-		snd_dev_info = pjmedia_snd_get_dev_info(si.play_id);
-	    else
-		snd_dev_info = pjmedia_snd_get_dev_info(si.rec_id);
-	    pj_strdup2_with_null(pool, &conf_port->name, snd_dev_info->name);
-	}
-    }
-
-
      /* Add the port to the bridge */
     conf_port->slot = 0;
     conf->ports[0] = conf_port;
     conf->port_cnt++;
 
-
     PJ_LOG(5,(THIS_FILE, "Sound device successfully created for port 0"));
     return PJ_SUCCESS;
 }
@@ -311,18 +258,6 @@
     if (status != PJ_SUCCESS)
 	return status;
 
-    /* If sound device was created, connect sound device to the
-     * master port.
-     */
-    if (conf->snd_dev_port) {
-	status = pjmedia_snd_port_connect( conf->snd_dev_port, 
-					   conf->master_port );
-	if (status != PJ_SUCCESS) {
-	    pjmedia_conf_destroy(conf);
-	    return status;
-	}
-    }
-
     /* Done */
 
     *p_conf = conf;
@@ -359,12 +294,6 @@
 {
     PJ_ASSERT_RETURN(conf != NULL, PJ_EINVAL);
 
-    /* Destroy sound device port. */
-    if (conf->snd_dev_port) {
-	pjmedia_snd_port_destroy(conf->snd_dev_port);
-	conf->snd_dev_port = NULL;
-    }
-
     /* Destroy mutex */
     pj_mutex_destroy(conf->mutex);
 
diff --git a/pjmedia/src/pjmedia/conference.c b/pjmedia/src/pjmedia/conference.c
index 54c4975..a89aff3 100644
--- a/pjmedia/src/pjmedia/conference.c
+++ b/pjmedia/src/pjmedia/conference.c
@@ -465,8 +465,8 @@
     /* Create sound device port: */
 
     if ((conf->options & PJMEDIA_CONF_NO_DEVICE) == 0) {
-	pjmedia_snd_stream *strm;
-	pjmedia_snd_stream_info si;
+	pjmedia_aud_stream *strm;
+	pjmedia_aud_param param;
 
 	/*
 	 * If capture is disabled then create player only port.
@@ -494,14 +494,14 @@
 	    return status;
 
 	strm = pjmedia_snd_port_get_snd_stream(conf->snd_dev_port);
-	status = pjmedia_snd_stream_get_info(strm, &si);
+	status = pjmedia_aud_stream_get_param(strm, &param);
 	if (status == PJ_SUCCESS) {
-	    const pjmedia_snd_dev_info *snd_dev_info;
+	    pjmedia_aud_dev_info snd_dev_info;
 	    if (conf->options & PJMEDIA_CONF_NO_MIC)
-		snd_dev_info = pjmedia_snd_get_dev_info(si.play_id);
+		pjmedia_aud_dev_get_info(param.play_id, &snd_dev_info);
 	    else
-		snd_dev_info = pjmedia_snd_get_dev_info(si.rec_id);
-	    pj_strdup2_with_null(pool, &conf_port->name, snd_dev_info->name);
+		pjmedia_aud_dev_get_info(param.rec_id, &snd_dev_info);
+	    pj_strdup2_with_null(pool, &conf_port->name, snd_dev_info.name);
 	}
     }
 
diff --git a/pjmedia/src/pjmedia/endpoint.c b/pjmedia/src/pjmedia/endpoint.c
index fb66a81..a3e988d 100644
--- a/pjmedia/src/pjmedia/endpoint.c
+++ b/pjmedia/src/pjmedia/endpoint.c
@@ -20,6 +20,7 @@
 #include <pjmedia/endpoint.h>
 #include <pjmedia/errno.h>
 #include <pjmedia/sdp.h>
+#include <pjmedia-audiodev/audiodev.h>
 #include <pj/assert.h>
 #include <pj/ioqueue.h>
 #include <pj/log.h>
@@ -121,7 +122,7 @@
     endpt->thread_cnt = worker_cnt;
 
     /* Sound */
-    status = pjmedia_snd_init(pf);
+    status = pjmedia_aud_subsys_init(pf);
     if (status != PJ_SUCCESS)
 	goto on_error;
 
@@ -171,7 +172,7 @@
     if (endpt->ioqueue && endpt->own_ioqueue)
 	pj_ioqueue_destroy(endpt->ioqueue);
 
-    pjmedia_snd_deinit();
+    pjmedia_aud_subsys_shutdown();
     pj_pool_release(pool);
     return status;
 }
@@ -212,7 +213,7 @@
 
     endpt->pf = NULL;
 
-    pjmedia_snd_deinit();
+    pjmedia_aud_subsys_shutdown();
     pj_pool_release (endpt->pool);
 
     return PJ_SUCCESS;
diff --git a/pjmedia/src/pjmedia/sound_port.c b/pjmedia/src/pjmedia/sound_port.c
index 5c89699..3a09e9f 100644
--- a/pjmedia/src/pjmedia/sound_port.c
+++ b/pjmedia/src/pjmedia/sound_port.c
@@ -22,13 +22,11 @@
 #include <pjmedia/delaybuf.h>
 #include <pjmedia/echo.h>
 #include <pjmedia/errno.h>
-#include <pjmedia/plc.h>
 #include <pj/assert.h>
 #include <pj/log.h>
 #include <pj/rand.h>
 #include <pj/string.h>	    /* pj_memset() */
 
-//#define SIMULATE_LOST_PCT   20
 #define AEC_TAIL	    128	    /* default AEC length in ms */
 #define AEC_SUSPEND_LIMIT   5	    /* seconds of no activity	*/
 
@@ -40,107 +38,51 @@
 {
     int			 rec_id;
     int			 play_id;
-    pjmedia_snd_stream	*snd_stream;
+    pj_uint32_t		 aud_caps;
+    pjmedia_aud_param	 aud_param;
+    pjmedia_aud_stream	*aud_stream;
     pjmedia_dir		 dir;
     pjmedia_port	*port;
 
-    pjmedia_echo_state	*ec_state;
-    unsigned		 aec_tail_len;
-
-    pj_bool_t		 ec_suspended;
-    unsigned		 ec_suspend_count;
-    unsigned		 ec_suspend_limit;
-
-    pjmedia_plc		*plc;
-
     unsigned		 clock_rate;
     unsigned		 channel_count;
     unsigned		 samples_per_frame;
     unsigned		 bits_per_sample;
-    pjmedia_snd_setting  setting;
 
-#if PJMEDIA_SOUND_USE_DELAYBUF
-    pjmedia_delay_buf	*delay_buf;
-#endif
-
-    /* Encoded sound emulation */
-#if !defined(PJMEDIA_SND_SUPPORT_OPEN2) || !PJMEDIA_SND_SUPPORT_OPEN2
-    unsigned		 frm_buf_size;
-    pj_uint8_t		*put_frm_buf;
-    pj_uint8_t		*get_frm_buf;
-#endif
+    /* software ec */
+    pjmedia_echo_state	*ec_state;
+    unsigned		 ec_options;
+    unsigned		 ec_tail_len;
+    pj_bool_t		 ec_suspended;
+    unsigned		 ec_suspend_count;
+    unsigned		 ec_suspend_limit;
 };
 
 /*
  * The callback called by sound player when it needs more samples to be
  * played.
  */
-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 play_cb(void *user_data, pjmedia_frame *frame)
 {
     pjmedia_snd_port *snd_port = (pjmedia_snd_port*) user_data;
     pjmedia_port *port;
-    pjmedia_frame frame;
+    unsigned required_size = frame->size;
     pj_status_t status;
 
     port = snd_port->port;
     if (port == NULL)
 	goto no_frame;
 
-    frame.buf = output;
-    frame.size = size;
-    frame.timestamp.u32.hi = 0;
-    frame.timestamp.u32.lo = timestamp;
-
-#if PJMEDIA_SOUND_USE_DELAYBUF
-    if (snd_port->delay_buf) {
-	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);
-
-#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
-
-    status = pjmedia_port_get_frame(port, &frame);
+    status = pjmedia_port_get_frame(port, frame);
     if (status != PJ_SUCCESS)
 	goto no_frame;
 
-    if (frame.type != PJMEDIA_FRAME_TYPE_AUDIO)
+    if (frame->type != PJMEDIA_FRAME_TYPE_AUDIO)
 	goto no_frame;
 
     /* Must supply the required samples */
-    pj_assert(frame.size == size);
-
-#ifdef SIMULATE_LOST_PCT
-    /* Simulate packet lost */
-    if (pj_rand() % 100 < SIMULATE_LOST_PCT) {
-	PJ_LOG(4,(THIS_FILE, "Frame dropped"));
-	goto no_frame;
-    }
-#endif
-
-    if (snd_port->plc)
-	pjmedia_plc_save(snd_port->plc, (pj_int16_t*) output);
+    PJ_UNUSED_ARG(required_size);
+    pj_assert(frame->size == required_size);
 
     if (snd_port->ec_state) {
 	if (snd_port->ec_suspended) {
@@ -149,7 +91,7 @@
 	    PJ_LOG(4,(THIS_FILE, "EC activated"));
 	}
 	snd_port->ec_suspend_count = 0;
-	pjmedia_echo_playback(snd_port->ec_state, (pj_int16_t*)output);
+	pjmedia_echo_playback(snd_port->ec_state, (pj_int16_t*)frame->buf);
     }
 
 
@@ -165,22 +107,10 @@
 	}
 	if (snd_port->ec_state) {
 	    /* To maintain correct delay in EC */
-	    pjmedia_echo_playback(snd_port->ec_state, (pj_int16_t*)output);
+	    pjmedia_echo_playback(snd_port->ec_state, (pj_int16_t*)frame->buf);
 	}
     }
 
-    /* Apply PLC */
-    if (snd_port->plc) {
-
-	pjmedia_plc_generate(snd_port->plc, (pj_int16_t*) output);
-#ifdef SIMULATE_LOST_PCT
-	PJ_LOG(4,(THIS_FILE, "Lost frame generated"));
-#endif
-    } else {
-	pj_bzero(output, size);
-    }
-
-
     return PJ_SUCCESS;
 }
 
@@ -189,14 +119,10 @@
  * The callback called by sound recorder when it has finished capturing a
  * frame.
  */
-static pj_status_t rec_cb(/* in */   void *user_data,
-			  /* in */   pj_uint32_t timestamp,
-			  /* in */   void *input,
-			  /* in*/    unsigned size)
+static pj_status_t rec_cb(void *user_data, pjmedia_frame *frame)
 {
     pjmedia_snd_port *snd_port = (pjmedia_snd_port*) user_data;
     pjmedia_port *port;
-    pjmedia_frame frame;
 
     port = snd_port->port;
     if (port == NULL)
@@ -204,28 +130,10 @@
 
     /* Cancel echo */
     if (snd_port->ec_state && !snd_port->ec_suspended) {
-	pjmedia_echo_capture(snd_port->ec_state, (pj_int16_t*) input, 0);
+	pjmedia_echo_capture(snd_port->ec_state, (pj_int16_t*) frame->buf, 0);
     }
 
-#if PJMEDIA_SOUND_USE_DELAYBUF
-    if (snd_port->delay_buf) {
-	pjmedia_delay_buf_put(snd_port->delay_buf, (pj_int16_t*)input);
-    } else {
-	frame.buf = (void*)input;
-	frame.size = size;
-	frame.type = PJMEDIA_FRAME_TYPE_AUDIO;
-	frame.timestamp.u32.lo = timestamp;
-
-	pjmedia_port_put_frame(port, &frame);
-    }
-#else
-    frame.buf = (void*)input;
-    frame.size = size;
-    frame.type = PJMEDIA_FRAME_TYPE_AUDIO;
-    frame.timestamp.u32.lo = timestamp;
-
-    pjmedia_port_put_frame(port, &frame);
-#endif
+    pjmedia_port_put_frame(port, frame);
 
     return PJ_SUCCESS;
 }
@@ -234,118 +142,19 @@
  * The callback called by sound player when it needs more samples to be
  * played. This version is for non-PCM data.
  */
-static pj_status_t play_cb_ext(/* in */   void *user_data,
-			       /* in */   pj_uint32_t timestamp,
-			       /* out */  void *output,
-			       /* out */  unsigned size)
+static pj_status_t play_cb_ext(void *user_data, pjmedia_frame *frame)
 {
-#if defined(PJMEDIA_SND_SUPPORT_OPEN2) && PJMEDIA_SND_SUPPORT_OPEN2!=0
-    /* This is the version to use when the sound device supports
-     * open2().
-     */
     pjmedia_snd_port *snd_port = (pjmedia_snd_port*) user_data;
-    pjmedia_port *port;
-    pjmedia_frame *frame = (pjmedia_frame*) output;
-    pj_status_t status;
+    pjmedia_port *port = snd_port->port;
 
-    PJ_UNUSED_ARG(size);
-    PJ_UNUSED_ARG(timestamp);
-
-    port = snd_port->port;
     if (port == NULL) {
 	frame->type = PJMEDIA_FRAME_TYPE_NONE;
 	return PJ_SUCCESS;
     }
 
-    status = pjmedia_port_get_frame(port, frame);
-
-    return status;
-#else
-    /* This is the emulation version */
-    pjmedia_snd_port *snd_port = (pjmedia_snd_port*) user_data;
-    pjmedia_port *port = snd_port->port;
-    pjmedia_frame_ext *fx = (pjmedia_frame_ext*) snd_port->get_frm_buf;
-    pj_status_t status;
-
-    if (port==NULL) {
-	goto no_frame;
-    }
-
-    pj_bzero(fx, sizeof(*fx));
-    fx->base.type = PJMEDIA_FRAME_TYPE_NONE;
-    fx->base.buf = ((pj_uint8_t*)fx) + sizeof(*fx);
-    fx->base.size = snd_port->frm_buf_size - sizeof(*fx);
-    fx->base.timestamp.u32.hi = 0;
-    fx->base.timestamp.u32.lo = timestamp;
-
-    status = pjmedia_port_get_frame(port, &fx->base);
-    if (status != PJ_SUCCESS)
-	goto no_frame;
-
-    if (fx->base.type == PJMEDIA_FRAME_TYPE_AUDIO) {
-	pj_assert(fx->base.size == size);
-	pj_memcpy(output, fx->base.buf, size);
-    } else if (fx->base.type == PJMEDIA_FRAME_TYPE_EXTENDED) {
-	void (*decoder)(pj_int16_t*, const pj_uint8_t*, pj_size_t) = NULL;
-	unsigned i, size_decoded;
-
-	switch (snd_port->setting.format.id) {
-	case PJMEDIA_FORMAT_PCMA:
-	    decoder = &pjmedia_alaw_decode;
-	    break;
-	case PJMEDIA_FORMAT_PCMU:
-	    decoder = &pjmedia_ulaw_decode;
-	    break;
-	default:
-	    PJ_LOG(1,(THIS_FILE, "Unsupported format %d", 
-		      snd_port->setting.format.id));
-	    goto no_frame;
-	}
-
-	if (fx->samples_cnt > size>>1) {
-	    PJ_LOG(4,(THIS_FILE, "Frame too large by %d samples", 
-		      fx->samples_cnt - (size>>1)));
-	} else if (fx->samples_cnt < size>>1) {
-	    PJ_LOG(4,(THIS_FILE, "Not enough frame by %d samples", 
-		      (size>>1) - fx->samples_cnt));
-	}
-
-	for (i=0, size_decoded=0; 
-	     i<fx->subframe_cnt && size_decoded<size; 
-	     ++i) 
-	{
-	    pjmedia_frame_ext_subframe *subfrm;
-
-	    subfrm = pjmedia_frame_ext_get_subframe(fx, i);	    
-
-	    if (!subfrm || subfrm->bitlen==0)
-		continue;
-
-	    if ((subfrm->bitlen>>3) > (int)(size-size_decoded)) {
-		subfrm->bitlen = (pj_uint16_t)((size-size_decoded) << 3);
-	    }
-
-	    (*decoder)((short*)((pj_uint8_t*)output + size_decoded),
-		       subfrm->data, subfrm->bitlen>>3);
-
-	    size_decoded += (subfrm->bitlen>>3) << 1;
-	}
-
-	if (size_decoded < size) {
-	    pj_bzero((pj_uint8_t*)output + size_decoded, size-size_decoded);
-	}
-
-    } else {
-	goto no_frame;
-    }
+    pjmedia_port_get_frame(port, frame);
 
     return PJ_SUCCESS;
-
-no_frame:
-    pj_bzero(output, size);
-    return PJ_SUCCESS;
-
-#endif	/* PJMEDIA_SND_SUPPORT_OPEN2 */
 }
 
 
@@ -353,21 +162,10 @@
  * The callback called by sound recorder when it has finished capturing a
  * frame. This version is for non-PCM data.
  */
-static pj_status_t rec_cb_ext(/* in */   void *user_data,
-			      /* in */   pj_uint32_t timestamp,
-			      /* in */   void *input,
-			      /* in*/    unsigned size)
+static pj_status_t rec_cb_ext(void *user_data, pjmedia_frame *frame)
 {
-#if defined(PJMEDIA_SND_SUPPORT_OPEN2) && PJMEDIA_SND_SUPPORT_OPEN2!=0
-    /* This is the version to use when the sound device supports
-     * open2().
-     */
     pjmedia_snd_port *snd_port = (pjmedia_snd_port*) user_data;
     pjmedia_port *port;
-    pjmedia_frame *frame = (pjmedia_frame*)input;
-
-    PJ_UNUSED_ARG(size);
-    PJ_UNUSED_ARG(timestamp);
 
     port = snd_port->port;
     if (port == NULL)
@@ -376,42 +174,6 @@
     pjmedia_port_put_frame(port, frame);
 
     return PJ_SUCCESS;
-#else
-    pjmedia_snd_port *snd_port = (pjmedia_snd_port*) user_data;
-    pjmedia_port *port = snd_port->port;
-    pjmedia_frame_ext *fx = (pjmedia_frame_ext*) snd_port->put_frm_buf;
-    void (*encoder)(pj_uint8_t*, const pj_int16_t*, pj_size_t) = NULL;
-
-    if (port==NULL)
-	return PJ_SUCCESS;
-
-    pj_bzero(fx, sizeof(*fx));
-    fx->base.buf = NULL;
-    fx->base.size = snd_port->frm_buf_size - sizeof(*fx);
-    fx->base.type = PJMEDIA_FRAME_TYPE_EXTENDED;
-    fx->base.timestamp.u32.lo = timestamp;
-
-    switch (snd_port->setting.format.id) {
-    case PJMEDIA_FORMAT_PCMA:
-	encoder = &pjmedia_alaw_encode;
-	break;
-    case PJMEDIA_FORMAT_PCMU:
-	encoder = &pjmedia_ulaw_encode;
-	break;
-    default:
-	PJ_LOG(1,(THIS_FILE, "Unsupported format %d", 
-		  snd_port->setting.format.id));
-	return PJ_SUCCESS;
-    }
-
-    (*encoder)((pj_uint8_t*)input, (pj_int16_t*)input, size >> 1);
-
-    pjmedia_frame_ext_append_subframe(fx, input, (size >> 1) << 3, 
-				      size >> 1);
-    pjmedia_port_put_frame(port, &fx->base);
-
-    return PJ_SUCCESS;
-#endif
 }
 
 /*
@@ -421,12 +183,13 @@
 static pj_status_t start_sound_device( pj_pool_t *pool,
 				       pjmedia_snd_port *snd_port )
 {
-    pjmedia_snd_rec_cb snd_rec_cb;
-    pjmedia_snd_play_cb snd_play_cb;
+    pjmedia_aud_rec_cb snd_rec_cb;
+    pjmedia_aud_play_cb snd_play_cb;
+    pjmedia_aud_param param_copy;
     pj_status_t status;
 
     /* Check if sound has been started. */
-    if (snd_port->snd_stream != NULL)
+    if (snd_port->aud_stream != NULL)
 	return PJ_SUCCESS;
 
     PJ_ASSERT_RETURN(snd_port->dir == PJMEDIA_DIR_CAPTURE ||
@@ -434,7 +197,38 @@
 		     snd_port->dir == PJMEDIA_DIR_CAPTURE_PLAYBACK,
 		     PJ_EBUG);
 
-    if (snd_port->setting.format.id == PJMEDIA_FORMAT_L16) {
+    /* Get device caps */
+    if (snd_port->aud_param.dir & PJMEDIA_DIR_CAPTURE) {
+	pjmedia_aud_dev_info dev_info;
+
+	status = pjmedia_aud_dev_get_info(snd_port->aud_param.rec_id, 
+					  &dev_info);
+	if (status != PJ_SUCCESS)
+	    return status;
+
+	snd_port->aud_caps = dev_info.caps;
+    } else {
+	snd_port->aud_caps = 0;
+    }
+
+    /* Process EC settings */
+    pj_memcpy(&param_copy, &snd_port->aud_param, sizeof(param_copy));
+    if (param_copy.flags & PJMEDIA_AUD_DEV_CAP_EC) {
+	/* EC is wanted */
+	if (snd_port->aud_caps & PJMEDIA_AUD_DEV_CAP_EC) {
+	    /* Device supports EC */
+	    /* Nothing to do */
+	} else {
+	    /* Device doesn't support EC, remove EC settings from
+	     * device parameters
+	     */
+	    param_copy.flags &= ~(PJMEDIA_AUD_DEV_CAP_EC |
+				  PJMEDIA_AUD_DEV_CAP_EC_TAIL);
+	}
+    }
+
+    /* Use different callback if format is not PCM */
+    if (snd_port->aud_param.ext_fmt.id == PJMEDIA_FORMAT_L16) {
 	snd_rec_cb = &rec_cb;
 	snd_play_cb = &play_cb;
     } else {
@@ -442,67 +236,48 @@
 	snd_play_cb = &play_cb_ext;
     }
 
-#if defined(PJMEDIA_SND_SUPPORT_OPEN2) && PJMEDIA_SND_SUPPORT_OPEN2!=0
-    status = pjmedia_snd_open2( snd_port->dir,
-				snd_port->rec_id, 
-				snd_port->play_id,
-				snd_port->clock_rate,
-				snd_port->channel_count,
-				snd_port->samples_per_frame,
-				snd_port->bits_per_sample,
-				snd_rec_cb,
-				snd_play_cb,
-				snd_port,
-				&snd_port->setting,
-				&snd_port->snd_stream);
-#else
-    status = pjmedia_snd_open(  snd_port->rec_id, 
-				snd_port->play_id,
-				snd_port->clock_rate,
-				snd_port->channel_count,
-				snd_port->samples_per_frame,
-				snd_port->bits_per_sample,
-				snd_rec_cb,
-				snd_play_cb,
-				snd_port,
-				&snd_port->snd_stream);
-#endif
+    /* Open the device */
+    status = pjmedia_aud_stream_create(&param_copy,
+				       snd_rec_cb,
+				       snd_play_cb,
+				       snd_port,
+				       &snd_port->aud_stream);
 
     if (status != PJ_SUCCESS)
 	return status;
 
-
-#ifdef SIMULATE_LOST_PCT
-    snd_port->setting.plc = PJ_TRUE;
-#endif
-
-    /* If we have player components, allocate buffer to save the last
-     * frame played to the speaker. The last frame is used for packet
-     * lost concealment (PLC) algorithm.
-     */
-    if ((snd_port->dir & PJMEDIA_DIR_PLAYBACK) &&
-	(snd_port->setting.plc)) 
-    {
-	status = pjmedia_plc_create(pool, snd_port->clock_rate, 
-				    snd_port->samples_per_frame * 
-					snd_port->channel_count,
-				    0, &snd_port->plc);
-	if (status != PJ_SUCCESS) {
-	    PJ_LOG(4,(THIS_FILE, "Unable to create PLC"));
-	    snd_port->plc = NULL;
-	}
-    }
-
     /* Inactivity limit before EC is suspended. */
     snd_port->ec_suspend_limit = AEC_SUSPEND_LIMIT *
 				 (snd_port->clock_rate / 
 				  snd_port->samples_per_frame);
 
+    /* Create software EC if parameter specifies EC but device 
+     * doesn't support EC
+     */
+    if ((snd_port->aud_param.flags & PJMEDIA_AUD_DEV_CAP_EC) &&
+	(snd_port->aud_caps & PJMEDIA_AUD_DEV_CAP_EC)==0)
+    {
+	if ((snd_port->aud_param.flags & PJMEDIA_AUD_DEV_CAP_EC_TAIL)==0) {
+	    snd_port->aud_param.flags |= PJMEDIA_AUD_DEV_CAP_EC_TAIL;
+	    snd_port->aud_param.ec_tail_ms = AEC_TAIL;
+	    PJ_LOG(4,(THIS_FILE, "AEC tail is set to default %u ms",
+				 snd_port->aud_param.ec_tail_ms));
+	}
+	    
+	status = pjmedia_snd_port_set_ec(snd_port, pool, 
+					 snd_port->aud_param.ec_tail_ms, 0);
+	if (status != PJ_SUCCESS) {
+	    pjmedia_aud_stream_destroy(snd_port->aud_stream);
+	    snd_port->aud_stream = NULL;
+	    return status;
+	}
+    }
+
     /* Start sound stream. */
-    status = pjmedia_snd_stream_start(snd_port->snd_stream);
+    status = pjmedia_aud_stream_start(snd_port->aud_stream);
     if (status != PJ_SUCCESS) {
-	pjmedia_snd_stream_close(snd_port->snd_stream);
-	snd_port->snd_stream = NULL;
+	pjmedia_aud_stream_destroy(snd_port->aud_stream);
+	snd_port->aud_stream = NULL;
 	return status;
     }
 
@@ -517,10 +292,10 @@
 static pj_status_t stop_sound_device( pjmedia_snd_port *snd_port )
 {
     /* Check if we have sound stream device. */
-    if (snd_port->snd_stream) {
-	pjmedia_snd_stream_stop(snd_port->snd_stream);
-	pjmedia_snd_stream_close(snd_port->snd_stream);
-	snd_port->snd_stream = NULL;
+    if (snd_port->aud_stream) {
+	pjmedia_aud_stream_stop(snd_port->aud_stream);
+	pjmedia_aud_stream_destroy(snd_port->aud_stream);
+	snd_port->aud_stream = NULL;
     }
 
     /* Destroy AEC */
@@ -546,48 +321,24 @@
 					     unsigned options,
 					     pjmedia_snd_port **p_port)
 {
-    pjmedia_snd_port *snd_port;
+    pjmedia_aud_param param;
+    pj_status_t status;
 
     PJ_UNUSED_ARG(options);
 
-    PJ_ASSERT_RETURN(pool && p_port, PJ_EINVAL);
+    status = pjmedia_aud_dev_default_param(rec_id, &param);
+    if (status != PJ_SUCCESS)
+	return status;
 
-    snd_port = PJ_POOL_ZALLOC_T(pool, pjmedia_snd_port);
-    PJ_ASSERT_RETURN(snd_port, PJ_ENOMEM);
+    param.dir = PJMEDIA_DIR_CAPTURE_PLAYBACK;
+    param.rec_id = rec_id;
+    param.play_id = play_id;
+    param.clock_rate = clock_rate;
+    param.channel_count = channel_count;
+    param.samples_per_frame = samples_per_frame;
+    param.bits_per_sample = bits_per_sample;
 
-    snd_port->rec_id = rec_id;
-    snd_port->play_id = play_id;
-    snd_port->dir = PJMEDIA_DIR_CAPTURE_PLAYBACK;
-    snd_port->clock_rate = clock_rate;
-    snd_port->channel_count = channel_count;
-    snd_port->samples_per_frame = samples_per_frame;
-    snd_port->bits_per_sample = bits_per_sample;
-    
-#if PJMEDIA_SOUND_USE_DELAYBUF
-    do {
-	pj_status_t status;
-	unsigned ptime;
-    
-	ptime = samples_per_frame * 1000 / (clock_rate * channel_count);
-    
-	status = pjmedia_delay_buf_create(pool, "snd_buff", 
-					  clock_rate, samples_per_frame,
-					  channel_count,
-					  PJMEDIA_SOUND_BUFFER_COUNT * ptime,
-					  0, &snd_port->delay_buf);
-	PJ_ASSERT_RETURN(status == PJ_SUCCESS, status);
-    } while (0);
-#endif
-
-    *p_port = snd_port;
-
-
-    /* Start sound device immediately.
-     * If there's no port connected, the sound callback will return
-     * empty signal.
-     */
-    return start_sound_device( pool, snd_port );
-
+    return pjmedia_snd_port_create2(pool, &param, p_port);
 }
 
 /*
@@ -602,29 +353,23 @@
 						 unsigned options,
 						 pjmedia_snd_port **p_port)
 {
-    pjmedia_snd_port *snd_port;
+    pjmedia_aud_param param;
+    pj_status_t status;
 
     PJ_UNUSED_ARG(options);
 
-    PJ_ASSERT_RETURN(pool && p_port, PJ_EINVAL);
+    status = pjmedia_aud_dev_default_param(dev_id, &param);
+    if (status != PJ_SUCCESS)
+	return status;
 
-    snd_port = PJ_POOL_ZALLOC_T(pool, pjmedia_snd_port);
-    PJ_ASSERT_RETURN(snd_port, PJ_ENOMEM);
+    param.dir = PJMEDIA_DIR_CAPTURE;
+    param.rec_id = dev_id;
+    param.clock_rate = clock_rate;
+    param.channel_count = channel_count;
+    param.samples_per_frame = samples_per_frame;
+    param.bits_per_sample = bits_per_sample;
 
-    snd_port->rec_id = dev_id;
-    snd_port->dir = PJMEDIA_DIR_CAPTURE;
-    snd_port->clock_rate = clock_rate;
-    snd_port->channel_count = channel_count;
-    snd_port->samples_per_frame = samples_per_frame;
-    snd_port->bits_per_sample = bits_per_sample;
-
-    *p_port = snd_port;
-
-    /* Start sound device immediately.
-     * If there's no port connected, the sound callback will return
-     * empty signal.
-     */
-    return start_sound_device( pool, snd_port );
+    return pjmedia_snd_port_create2(pool, &param, p_port);
 }
 
 
@@ -640,92 +385,50 @@
 						    unsigned options,
 						    pjmedia_snd_port **p_port)
 {
-    pjmedia_snd_port *snd_port;
+    pjmedia_aud_param param;
+    pj_status_t status;
 
     PJ_UNUSED_ARG(options);
 
-    PJ_ASSERT_RETURN(pool && p_port, PJ_EINVAL);
+    status = pjmedia_aud_dev_default_param(dev_id, &param);
+    if (status != PJ_SUCCESS)
+	return status;
 
-    snd_port = PJ_POOL_ZALLOC_T(pool, pjmedia_snd_port);
-    PJ_ASSERT_RETURN(snd_port, PJ_ENOMEM);
+    param.dir = PJMEDIA_DIR_PLAYBACK;
+    param.play_id = dev_id;
+    param.clock_rate = clock_rate;
+    param.channel_count = channel_count;
+    param.samples_per_frame = samples_per_frame;
+    param.bits_per_sample = bits_per_sample;
 
-    snd_port->play_id = dev_id;
-    snd_port->dir = PJMEDIA_DIR_PLAYBACK;
-    snd_port->clock_rate = clock_rate;
-    snd_port->channel_count = channel_count;
-    snd_port->samples_per_frame = samples_per_frame;
-    snd_port->bits_per_sample = bits_per_sample;
-
-    *p_port = snd_port;
-
-    /* Start sound device immediately.
-     * If there's no port connected, the sound callback will return
-     * empty signal.
-     */
-    return start_sound_device( pool, snd_port );
+    return pjmedia_snd_port_create2(pool, &param, p_port);
 }
 
 
 /*
- * Create bidirectional port.
+ * Create sound port.
  */
 PJ_DEF(pj_status_t) pjmedia_snd_port_create2(pj_pool_t *pool,
-					     pjmedia_dir dir,
-					     int rec_id,
-					     int play_id,
-					     unsigned clock_rate,
-					     unsigned channel_count,
-					     unsigned samples_per_frame,
-					     unsigned bits_per_sample,
-					     const pjmedia_snd_setting *setting,
+					     const pjmedia_aud_param *prm,
 					     pjmedia_snd_port **p_port)
 {
     pjmedia_snd_port *snd_port;
 
-    PJ_ASSERT_RETURN(pool && p_port, PJ_EINVAL);
+    PJ_ASSERT_RETURN(pool && prm && p_port, PJ_EINVAL);
 
     snd_port = PJ_POOL_ZALLOC_T(pool, pjmedia_snd_port);
     PJ_ASSERT_RETURN(snd_port, PJ_ENOMEM);
 
-    snd_port->dir = dir;
-    snd_port->rec_id = rec_id;
-    snd_port->play_id = play_id;
+    snd_port->dir = prm->dir;
+    snd_port->rec_id = prm->rec_id;
+    snd_port->play_id = prm->play_id;
     snd_port->dir = PJMEDIA_DIR_CAPTURE_PLAYBACK;
-    snd_port->clock_rate = clock_rate;
-    snd_port->channel_count = channel_count;
-    snd_port->samples_per_frame = samples_per_frame;
-    snd_port->bits_per_sample = bits_per_sample;
-    pj_memcpy(&snd_port->setting, setting, sizeof(*setting));
+    snd_port->clock_rate = prm->clock_rate;
+    snd_port->channel_count = prm->channel_count;
+    snd_port->samples_per_frame = prm->samples_per_frame;
+    snd_port->bits_per_sample = prm->bits_per_sample;
+    pj_memcpy(&snd_port->aud_param, prm, sizeof(*prm));
     
-#if PJMEDIA_SOUND_USE_DELAYBUF
-    if (snd_port->setting.format.u32 == PJMEDIA_FORMAT_L16) {
-	pj_status_t status;
-	unsigned ptime;
-    
-	ptime = samples_per_frame * 1000 / (clock_rate * channel_count);
-    
-	status = pjmedia_delay_buf_create(pool, "snd_buff", 
-					  clock_rate, samples_per_frame,
-					  channel_count,
-					  PJMEDIA_SOUND_BUFFER_COUNT * ptime,
-					  0, &snd_port->delay_buf);
-	PJ_ASSERT_RETURN(status == PJ_SUCCESS, status);
-    }
-#endif
-
-#if !defined(PJMEDIA_SND_SUPPORT_OPEN2) || PJMEDIA_SND_SUPPORT_OPEN2==0
-    /* For devices that doesn't support open2(), enable simulation */
-    if (snd_port->setting.format.id != PJMEDIA_FORMAT_L16) {
-	snd_port->frm_buf_size = sizeof(pjmedia_frame_ext) + 
-				 (samples_per_frame << 1) +
-				 16 * sizeof(pjmedia_frame_ext_subframe);
-	snd_port->put_frm_buf = (pj_uint8_t*)
-				pj_pool_alloc(pool, snd_port->frm_buf_size);
-	snd_port->get_frm_buf = (pj_uint8_t*)
-				pj_pool_alloc(pool, snd_port->frm_buf_size);
-    }
-#endif
-
     *p_port = snd_port;
 
 
@@ -752,23 +455,23 @@
 /*
  * Retrieve the sound stream associated by this sound device port.
  */
-PJ_DEF(pjmedia_snd_stream*) pjmedia_snd_port_get_snd_stream(
+PJ_DEF(pjmedia_aud_stream*) pjmedia_snd_port_get_snd_stream(
 						pjmedia_snd_port *snd_port)
 {
     PJ_ASSERT_RETURN(snd_port, NULL);
-    return snd_port->snd_stream;
+    return snd_port->aud_stream;
 }
 
 
 /*
- * Enable AEC
+ * Change EC settings.
  */
 PJ_DEF(pj_status_t) pjmedia_snd_port_set_ec( pjmedia_snd_port *snd_port,
 					     pj_pool_t *pool,
 					     unsigned tail_ms,
 					     unsigned options)
 {
-    pjmedia_snd_stream_info si;
+    pjmedia_aud_param prm;
     pj_status_t status;
 
     /* Sound must be opened in full-duplex mode */
@@ -776,43 +479,100 @@
 		     snd_port->dir == PJMEDIA_DIR_CAPTURE_PLAYBACK,
 		     PJ_EINVALIDOP);
 
-    /* Sound port must have 16bits per sample */
-    PJ_ASSERT_RETURN(snd_port->bits_per_sample == 16,
-		     PJ_EINVALIDOP);
+    /* Determine whether we use device or software EC */
+    if (snd_port->aud_caps & PJMEDIA_AUD_DEV_CAP_EC) {
+	/* We use device EC */
+	pj_bool_t ec_enabled;
 
-    /* Destroy AEC */
-    if (snd_port->ec_state) {
-	pjmedia_echo_destroy(snd_port->ec_state);
-	snd_port->ec_state = NULL;
-    }
-
-    snd_port->aec_tail_len = tail_ms;
-
-    if (tail_ms != 0) {
-	unsigned delay_ms;
-
-	status = pjmedia_snd_stream_get_info(snd_port->snd_stream, &si);
+	/* Query EC status */
+	status = pjmedia_aud_stream_get_cap(snd_port->aud_stream,
+					    PJMEDIA_AUD_DEV_CAP_EC,
+					    &ec_enabled);
 	if (status != PJ_SUCCESS)
-	    si.rec_latency = si.play_latency = 0;
+	    return status;
 
-	//No need to add input latency in the latency calculation,
-	//since actual input latency should be zero.
-	//delay_ms = (si.rec_latency + si.play_latency) * 1000 /
-	//	   snd_port->clock_rate;
-	delay_ms = si.play_latency * 1000 / snd_port->clock_rate;
-	status = pjmedia_echo_create2(pool, snd_port->clock_rate, 
-				      snd_port->channel_count,
-				      snd_port->samples_per_frame, 
-				      tail_ms, delay_ms,
-				      options, &snd_port->ec_state);
-	if (status != PJ_SUCCESS)
-	    snd_port->ec_state = NULL;
-	else
-	    snd_port->ec_suspended = PJ_FALSE;
+	if (tail_ms != 0) {
+	    /* Change EC setting */
+
+	    if (!ec_enabled) {
+		/* Enable EC first */
+		pj_bool_t value = PJ_TRUE;
+		status = pjmedia_aud_stream_set_cap(snd_port->aud_stream, 
+						    PJMEDIA_AUD_DEV_CAP_EC,
+						    &value);
+		if (status != PJ_SUCCESS)
+		    return status;
+	    }
+
+	    if ((snd_port->aud_caps & PJMEDIA_AUD_DEV_CAP_EC_TAIL)==0) {
+		/* Device does not support setting EC tail */
+		return PJMEDIA_EAUD_INVCAP;
+	    }
+
+	    return pjmedia_aud_stream_set_cap(snd_port->aud_stream,
+					      PJMEDIA_AUD_DEV_CAP_EC_TAIL,
+					      &tail_ms);
+
+	} else if (ec_enabled) {
+	    /* Disable EC */
+	    pj_bool_t value = PJ_FALSE;
+	    return pjmedia_aud_stream_set_cap(snd_port->aud_stream, 
+					      PJMEDIA_AUD_DEV_CAP_EC,
+					      &value);
+	} else {
+	    /* Request to disable EC but EC has been disabled */
+	    /* Do nothing */
+	    return PJ_SUCCESS;
+	}
+
     } else {
-	PJ_LOG(4,(THIS_FILE, "Echo canceller is now disabled in the "
-			     "sound port"));
-	status = PJ_SUCCESS;
+	/* We use software EC */
+	/* Sound port must have 16bits per sample */
+	PJ_ASSERT_RETURN(snd_port->bits_per_sample == 16,
+			 PJ_EINVALIDOP);
+
+	/* Check if there is change in parameters */
+	if (tail_ms==snd_port->ec_tail_len && options==snd_port->ec_options) {
+	    PJ_LOG(5,(THIS_FILE, "pjmedia_snd_port_set_ec() ignored, no "
+				 "change in settings"));
+	    return PJ_SUCCESS;
+	}
+
+	/* Destroy AEC */
+	if (snd_port->ec_state) {
+	    pjmedia_echo_destroy(snd_port->ec_state);
+	    snd_port->ec_state = NULL;
+	}
+
+	snd_port->ec_options = options;
+	snd_port->ec_tail_len = tail_ms;
+
+	if (tail_ms != 0) {
+	    unsigned delay_ms;
+
+	    status = pjmedia_aud_stream_get_param(snd_port->aud_stream, &prm);
+	    if (status != PJ_SUCCESS)
+		prm.input_latency_ms = prm.output_latency_ms = 0;
+
+	    //No need to add input latency in the latency calculation,
+	    //since actual input latency should be zero.
+	    //delay_ms = (si.rec_latency + si.play_latency) * 1000 /
+	    //	   snd_port->clock_rate;
+	    delay_ms = prm.output_latency_ms;
+	    status = pjmedia_echo_create2(pool, snd_port->clock_rate, 
+					  snd_port->channel_count,
+					  snd_port->samples_per_frame, 
+					  tail_ms, delay_ms,
+					  options, &snd_port->ec_state);
+	    if (status != PJ_SUCCESS)
+		snd_port->ec_state = NULL;
+	    else
+		snd_port->ec_suspended = PJ_FALSE;
+	} else {
+	    PJ_LOG(4,(THIS_FILE, "Echo canceller is now disabled in the "
+				 "sound port"));
+	    status = PJ_SUCCESS;
+	}
     }
 
     return status;
@@ -824,12 +584,42 @@
 						  unsigned *p_length)
 {
     PJ_ASSERT_RETURN(snd_port && p_length, PJ_EINVAL);
-    *p_length =  snd_port->ec_state ? snd_port->aec_tail_len : 0;
+
+    /* Determine whether we use device or software EC */
+    if (snd_port->aud_caps & PJMEDIA_AUD_DEV_CAP_EC) {
+	/* We use device EC */
+	pj_bool_t ec_enabled;
+	pj_status_t status;
+
+	/* Query EC status */
+	status = pjmedia_aud_stream_get_cap(snd_port->aud_stream,
+					    PJMEDIA_AUD_DEV_CAP_EC,
+					    &ec_enabled);
+	if (status != PJ_SUCCESS)
+	    return status;
+
+	if (!ec_enabled) {
+	    *p_length = 0;
+	} else if (snd_port->aud_caps & PJMEDIA_AUD_DEV_CAP_EC_TAIL) {
+	    /* Get device EC tail */
+	    status = pjmedia_aud_stream_get_cap(snd_port->aud_stream,
+						PJMEDIA_AUD_DEV_CAP_EC_TAIL,
+						p_length);
+	    if (status != PJ_SUCCESS)
+		return status;
+	} else {
+	    /* Just use default */
+	    *p_length = AEC_TAIL;
+	}
+
+    } else {
+	/* We use software EC */
+	*p_length =  snd_port->ec_state ? snd_port->ec_tail_len : 0;
+    }
     return PJ_SUCCESS;
 }
 
 
-
 /*
  * Connect a port.
  */
diff --git a/pjmedia/src/pjmedia/transport_ice.c b/pjmedia/src/pjmedia/transport_ice.c
index 7e0b6b7..77a2e34 100644
--- a/pjmedia/src/pjmedia/transport_ice.c
+++ b/pjmedia/src/pjmedia/transport_ice.c
@@ -21,6 +21,7 @@
 #include <pjnath/errno.h>
 #include <pj/assert.h>
 #include <pj/log.h>
+#include <pj/pool.h>
 #include <pj/rand.h>
 
 #define THIS_FILE   "transport_ice.c"
diff --git a/pjmedia/src/pjmedia/transport_loop.c b/pjmedia/src/pjmedia/transport_loop.c
index d9742d2..418e04a 100644
--- a/pjmedia/src/pjmedia/transport_loop.c
+++ b/pjmedia/src/pjmedia/transport_loop.c
@@ -22,6 +22,7 @@
 #include <pj/errno.h>
 #include <pj/ioqueue.h>
 #include <pj/log.h>
+#include <pj/pool.h>
 #include <pj/rand.h>
 #include <pj/string.h>
 
diff --git a/pjmedia/src/pjmedia/transport_udp.c b/pjmedia/src/pjmedia/transport_udp.c
index 1170271..2236392 100644
--- a/pjmedia/src/pjmedia/transport_udp.c
+++ b/pjmedia/src/pjmedia/transport_udp.c
@@ -23,6 +23,7 @@
 #include <pj/errno.h>
 #include <pj/ioqueue.h>
 #include <pj/log.h>
+#include <pj/pool.h>
 #include <pj/rand.h>
 #include <pj/string.h>
 
diff --git a/pjmedia/src/pjmedia/wmme_sound.c b/pjmedia/src/pjmedia/wmme_sound.c
index 8f94660..e69de29 100644
--- a/pjmedia/src/pjmedia/wmme_sound.c
+++ b/pjmedia/src/pjmedia/wmme_sound.c
@@ -1,1008 +0,0 @@
-#include <pjmedia/sound.h>
-#include <pjmedia/errno.h>
-#include <pj/assert.h>
-#include <pj/log.h>
-#include <pj/os.h>
-#include <pj/string.h>
-
-#if PJMEDIA_SOUND_IMPLEMENTATION == PJMEDIA_SOUND_WIN32_MME_SOUND
-
-#ifdef _MSC_VER
-#   pragma warning(push, 3)
-#endif
-
-#include <windows.h>
-#include <mmsystem.h>
-
-#ifdef _MSC_VER
-#   pragma warning(pop)
-#endif
-
-#if defined(PJ_WIN32_WINCE) && PJ_WIN32_WINCE!=0
-#   pragma comment(lib, "Coredll.lib")
-#elif defined(_MSC_VER)
-#   pragma comment(lib, "winmm.lib")
-#endif
-
-
-#define THIS_FILE			"wmme_sound.c"
-#define BITS_PER_SAMPLE			16
-#define BYTES_PER_SAMPLE		(BITS_PER_SAMPLE/8)
-
-#define MAX_PACKET_BUFFER_COUNT		32
-#define MAX_HARDWARE			16
-
-struct wmme_dev_info
-{
-    pjmedia_snd_dev_info info;
-    unsigned             deviceId;
-};
-
-static unsigned dev_count;
-static struct wmme_dev_info dev_info[MAX_HARDWARE];
-static pj_bool_t snd_initialized = PJ_FALSE;
-
-/* Latency settings */
-static unsigned snd_input_latency  = PJMEDIA_SND_DEFAULT_REC_LATENCY;
-static unsigned snd_output_latency = PJMEDIA_SND_DEFAULT_PLAY_LATENCY;
-
-
-/* Individual WMME capture/playback stream descriptor */
-struct wmme_stream
-{
-    union
-    {
-	HWAVEIN   In;
-	HWAVEOUT  Out;
-    } hWave;
-
-    WAVEHDR      *WaveHdr;
-    HANDLE        hEvent;
-    DWORD         dwBufIdx;
-    DWORD         dwMaxBufIdx;
-    pj_timestamp  timestamp;
-};
-
-
-/* Sound stream. */
-struct pjmedia_snd_stream
-{
-    pjmedia_dir          dir;               /**< Sound direction.      */
-    int                  play_id;           /**< Playback dev id.      */
-    int                  rec_id;            /**< Recording dev id.     */
-    pj_pool_t           *pool;              /**< Memory pool.          */
-
-    pjmedia_snd_rec_cb   rec_cb;            /**< Capture callback.     */
-    pjmedia_snd_play_cb  play_cb;           /**< Playback callback.    */
-    void                *user_data;         /**< Application data.     */
-
-    struct wmme_stream   play_strm;         /**< Playback stream.      */
-    struct wmme_stream   rec_strm;          /**< Capture stream.       */
-
-    void    		*buffer;	    /**< Temp. frame buffer.   */
-    unsigned             clock_rate;        /**< Clock rate.           */
-    unsigned             samples_per_frame; /**< Samples per frame.    */
-    unsigned             bits_per_sample;   /**< Bits per sample.      */
-    unsigned             channel_count;     /**< Channel count.        */
-
-    pj_thread_t         *thread;            /**< Thread handle.        */
-    HANDLE               thread_quit_event; /**< Quit signal to thread */
-};
-
-
-static pj_pool_factory *pool_factory;
-
-static void init_waveformatex (LPWAVEFORMATEX pcmwf, 
-			       unsigned clock_rate,
-			       unsigned channel_count)
-{
-    pj_bzero(pcmwf, sizeof(PCMWAVEFORMAT)); 
-    pcmwf->wFormatTag = WAVE_FORMAT_PCM; 
-    pcmwf->nChannels = (pj_uint16_t)channel_count;
-    pcmwf->nSamplesPerSec = clock_rate;
-    pcmwf->nBlockAlign = (pj_uint16_t)(channel_count * BYTES_PER_SAMPLE);
-    pcmwf->nAvgBytesPerSec = clock_rate * channel_count * BYTES_PER_SAMPLE;
-    pcmwf->wBitsPerSample = BITS_PER_SAMPLE;
-}
-
-
-/*
- * Initialize WMME player device.
- */
-static pj_status_t init_player_stream(  pj_pool_t *pool,
-				        struct wmme_stream *wmme_strm,
-					int dev_id,
-					unsigned clock_rate,
-					unsigned channel_count,
-					unsigned samples_per_frame,
-					unsigned buffer_count)
-{
-    MMRESULT mr;
-    WAVEFORMATEX pcmwf; 
-    unsigned bytes_per_frame;
-    unsigned i;
-
-    PJ_ASSERT_RETURN(buffer_count <= MAX_PACKET_BUFFER_COUNT, PJ_EINVAL);
-
-    /* Check device ID */
-    if (dev_id == -1)
-	dev_id = 0;
-
-    PJ_ASSERT_RETURN(dev_id >= 0 && dev_id < (int)dev_count, PJ_EINVAL);
-
-    /*
-     * Create a wait event.
-     */
-    wmme_strm->hEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
-    if (NULL == wmme_strm->hEvent)
-	return pj_get_os_error();
-
-    /*
-     * Set up wave format structure for opening the device.
-     */
-    init_waveformatex(&pcmwf, clock_rate, channel_count);
-    bytes_per_frame = samples_per_frame * BYTES_PER_SAMPLE;
-
-    /*
-     * Open wave device.
-     */
-    mr = waveOutOpen(&wmme_strm->hWave.Out, dev_info[dev_id].deviceId, &pcmwf, 
-		     (DWORD)wmme_strm->hEvent, 0, CALLBACK_EVENT);
-    if (mr != MMSYSERR_NOERROR)
-	/* TODO: This is for HRESULT/GetLastError() */
-	PJ_RETURN_OS_ERROR(mr);
-
-    /* Pause the wave out device */
-    mr = waveOutPause(wmme_strm->hWave.Out);
-    if (mr != MMSYSERR_NOERROR)
-	/* TODO: This is for HRESULT/GetLastError() */
-	PJ_RETURN_OS_ERROR(mr);
-
-    /*
-     * Create the buffers. 
-     */
-    wmme_strm->WaveHdr = pj_pool_zalloc(pool, sizeof(WAVEHDR) * buffer_count);
-    for (i = 0; i < buffer_count; ++i)
-    {
-	wmme_strm->WaveHdr[i].lpData = pj_pool_zalloc(pool, bytes_per_frame);
-	wmme_strm->WaveHdr[i].dwBufferLength = bytes_per_frame;
-	mr = waveOutPrepareHeader(wmme_strm->hWave.Out, 
-				  &(wmme_strm->WaveHdr[i]),
-				  sizeof(WAVEHDR));
-	if (mr != MMSYSERR_NOERROR)
-	    /* TODO: This is for HRESULT/GetLastError() */
-	    PJ_RETURN_OS_ERROR(mr); 
-	mr = waveOutWrite(wmme_strm->hWave.Out, &(wmme_strm->WaveHdr[i]), 
-			  sizeof(WAVEHDR));
-	if (mr != MMSYSERR_NOERROR)
-	    /* TODO: This is for HRESULT/GetLastError() */
-	    PJ_RETURN_OS_ERROR(mr);
-    }
-
-    wmme_strm->dwBufIdx = 0;
-    wmme_strm->dwMaxBufIdx = buffer_count;
-    wmme_strm->timestamp.u64 = 0;
-
-    /* Done setting up play device. */
-    PJ_LOG(5, (THIS_FILE, 
-	       " WaveAPI Sound player \"%s\" initialized (clock_rate=%d, "
-	       "channel_count=%d, samples_per_frame=%d (%dms))",
-	       dev_info[dev_id].info.name,
-	       clock_rate, channel_count, samples_per_frame,
-	       samples_per_frame * 1000 / clock_rate));
-
-    return PJ_SUCCESS;
-}
-
-
-/*
- * Initialize Windows Multimedia recorder device
- */
-static pj_status_t init_capture_stream( pj_pool_t *pool,
-					struct wmme_stream *wmme_strm,
-					int dev_id,
-					unsigned clock_rate,
-					unsigned channel_count,
-					unsigned samples_per_frame,
-					unsigned buffer_count)
-{
-    MMRESULT mr;
-    WAVEFORMATEX pcmwf; 
-    unsigned bytes_per_frame;
-    unsigned i;
-
-    PJ_ASSERT_RETURN(buffer_count <= MAX_PACKET_BUFFER_COUNT, PJ_EINVAL);
-
-    /* Check device ID */
-    if (dev_id == -1)
-	dev_id = 0;
-
-    PJ_ASSERT_RETURN(dev_id >= 0 && dev_id < (int)dev_count, PJ_EINVAL);
-
-    /*
-    * Create a wait event.
-    */
-    wmme_strm->hEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
-    if (NULL == wmme_strm->hEvent)
-	return pj_get_os_error();
-
-    /*
-     * Set up wave format structure for opening the device.
-     */
-    init_waveformatex(&pcmwf, clock_rate, channel_count);
-    bytes_per_frame = samples_per_frame * BYTES_PER_SAMPLE;
-
-    /*
-     * Open wave device.
-     */
-    mr = waveInOpen(&wmme_strm->hWave.In, dev_info[dev_id].deviceId, &pcmwf, 
-		    (DWORD)wmme_strm->hEvent, 0, CALLBACK_EVENT);
-    if (mr != MMSYSERR_NOERROR)
-	/* TODO: This is for HRESULT/GetLastError() */
-	PJ_RETURN_OS_ERROR(mr);
-
-    /*
-     * Create the buffers. 
-     */
-    wmme_strm->WaveHdr = pj_pool_zalloc(pool, sizeof(WAVEHDR) * buffer_count);
-    for (i = 0; i < buffer_count; ++i)
-    {
-	wmme_strm->WaveHdr[i].lpData = pj_pool_zalloc(pool, bytes_per_frame);
-	wmme_strm->WaveHdr[i].dwBufferLength = bytes_per_frame;
-	mr = waveInPrepareHeader(wmme_strm->hWave.In, &(wmme_strm->WaveHdr[i]),
-							sizeof(WAVEHDR));
-	if (mr != MMSYSERR_NOERROR)
-	    /* TODO: This is for HRESULT/GetLastError() */
-	    PJ_RETURN_OS_ERROR(mr);
-	mr = waveInAddBuffer(wmme_strm->hWave.In, &(wmme_strm->WaveHdr[i]), 
-			     sizeof(WAVEHDR));
-	if (mr != MMSYSERR_NOERROR)
-	    /* TODO: This is for HRESULT/GetLastError() */
-	    PJ_RETURN_OS_ERROR(mr);
-    }
-
-    wmme_strm->dwBufIdx = 0;
-    wmme_strm->dwMaxBufIdx = buffer_count;
-    wmme_strm->timestamp.u64 = 0;
-
-    /* Done setting up play device. */
-    PJ_LOG(5,(THIS_FILE, 
-	" WaveAPI Sound recorder \"%s\" initialized (clock_rate=%d, "
-	"channel_count=%d, samples_per_frame=%d (%dms))",
-	dev_info[dev_id].info.name,
-	clock_rate, channel_count, samples_per_frame,
-	samples_per_frame * 1000 / clock_rate));
-
-    return PJ_SUCCESS;
-}
-
-
-
-/*
-* WMME capture and playback thread.
-*/
-static int PJ_THREAD_FUNC wmme_dev_thread(void *arg)
-{
-    pjmedia_snd_stream *strm = arg;
-    HANDLE events[3];
-    unsigned eventCount;
-    unsigned bytes_per_frame;
-    pj_status_t status = PJ_SUCCESS;
-
-
-    eventCount = 0;
-    events[eventCount++] = strm->thread_quit_event;
-    if (strm->dir & PJMEDIA_DIR_PLAYBACK)
-	events[eventCount++] = strm->play_strm.hEvent;
-    if (strm->dir & PJMEDIA_DIR_CAPTURE)
-	events[eventCount++] = strm->rec_strm.hEvent;
-
-
-    /* Raise self priority. We don't want the audio to be distorted by
-     * system activity.
-     */
-#if defined(PJ_WIN32_WINCE) && PJ_WIN32_WINCE != 0
-    if (strm->dir & PJMEDIA_DIR_PLAYBACK)
-	CeSetThreadPriority(GetCurrentThread(), 153);
-    else
-	CeSetThreadPriority(GetCurrentThread(), 247);
-#else
-    SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_TIME_CRITICAL);
-#endif
-
-    /* Calculate bytes per frame */
-    bytes_per_frame = strm->samples_per_frame * BYTES_PER_SAMPLE;
-
-    /*
-     * Loop while not signalled to quit, wait for event objects to be 
-     * signalled by WMME capture and play buffer.
-     */
-    while (status == PJ_SUCCESS)
-    {
-
-	DWORD rc;
-	pjmedia_dir signalled_dir;
-
-	rc = WaitForMultipleObjects(eventCount, events, FALSE, INFINITE);
-	if (rc < WAIT_OBJECT_0 || rc >= WAIT_OBJECT_0 + eventCount)
-	    continue;
-
-	if (rc == WAIT_OBJECT_0)
-	    break;
-
-	if (rc == (WAIT_OBJECT_0 + 1))
-	{
-	    if (events[1] == strm->play_strm.hEvent)
-		signalled_dir = PJMEDIA_DIR_PLAYBACK;
-	    else
-		signalled_dir = PJMEDIA_DIR_CAPTURE;
-	}
-	else
-	{
-	    if (events[2] == strm->play_strm.hEvent)
-		signalled_dir = PJMEDIA_DIR_PLAYBACK;
-	    else
-		signalled_dir = PJMEDIA_DIR_CAPTURE;
-	}
-
-
-	if (signalled_dir == PJMEDIA_DIR_PLAYBACK)
-	{
-	    struct wmme_stream *wmme_strm = &strm->play_strm;
-	    MMRESULT mr = MMSYSERR_NOERROR;
-	    status = PJ_SUCCESS;
-
-	    /*
-	    * Windows Multimedia has requested us to feed some frames to
-	    * playback buffer.
-	    */
-
-	    while (wmme_strm->WaveHdr[wmme_strm->dwBufIdx].dwFlags & WHDR_DONE)
-	    {
-		void* buffer = wmme_strm->WaveHdr[wmme_strm->dwBufIdx].lpData;
-
-		PJ_LOG(5,(THIS_FILE, "Finished writing buffer %d", 
-			  wmme_strm->dwBufIdx));
-
-		/* Get frame from application. */
-		status = (*strm->play_cb)(strm->user_data, 
-					  wmme_strm->timestamp.u32.lo,
-					  buffer,
-					  bytes_per_frame);
-
-		if (status != PJ_SUCCESS)
-		    break;
-
-		/* Write to the device. */
-		mr = waveOutWrite(wmme_strm->hWave.Out, 
-				  &(wmme_strm->WaveHdr[wmme_strm->dwBufIdx]), 
-				  sizeof(WAVEHDR));
-		if (mr != MMSYSERR_NOERROR)
-		{
-		    status = PJ_STATUS_FROM_OS(mr);
-		    break;
-		}
-
-		/* Increment position. */
-		if (++wmme_strm->dwBufIdx >= wmme_strm->dwMaxBufIdx)
-		    wmme_strm->dwBufIdx = 0;
-		wmme_strm->timestamp.u64 += strm->samples_per_frame / 
-					    strm->channel_count;
-	    }
-	}
-	else
-	{
-	    struct wmme_stream *wmme_strm = &strm->rec_strm;
-	    MMRESULT mr = MMSYSERR_NOERROR;
-	    status = PJ_SUCCESS;
-
-	    /*
-	    * Windows Multimedia has indicated that it has some frames ready
-	    * in the capture buffer. Get as much frames as possible to
-	    * prevent overflows.
-	    */
-#if 0
-	    {
-		static DWORD tc = 0;
-		DWORD now = GetTickCount();
-		DWORD i = 0;
-		DWORD bits = 0;
-
-		if (tc == 0) tc = now;
-
-		for (i = 0; i < wmme_strm->dwMaxBufIdx; ++i)
-		{
-		    bits = bits << 4;
-		    bits |= wmme_strm->WaveHdr[i].dwFlags & WHDR_DONE;
-		}
-		PJ_LOG(5,(THIS_FILE, "Record Signal> Index: %d, Delta: %4.4d, "
-			  "Flags: %6.6x\n",
-			  wmme_strm->dwBufIdx,
-			  now - tc,
-			  bits));
-		tc = now;
-	    }
-#endif
-
-	    while (wmme_strm->WaveHdr[wmme_strm->dwBufIdx].dwFlags & WHDR_DONE)
-	    {
-		char* buffer = (char*)
-			       wmme_strm->WaveHdr[wmme_strm->dwBufIdx].lpData;
-		unsigned cap_len = 
-			wmme_strm->WaveHdr[wmme_strm->dwBufIdx].dwBytesRecorded;
-
-		/*
-		PJ_LOG(5,(THIS_FILE, "Read %d bytes from buffer %d", cap_len, 
-			  wmme_strm->dwBufIdx));
-		*/
-
-		if (cap_len < bytes_per_frame)
-		    pj_bzero(buffer + cap_len, bytes_per_frame - cap_len);
-
-		/* Copy the audio data out of the wave buffer. */
-		pj_memcpy(strm->buffer, buffer, bytes_per_frame);
-
-		/* Re-add the buffer to the device. */
-		mr = waveInAddBuffer(wmme_strm->hWave.In, 
-				     &(wmme_strm->WaveHdr[wmme_strm->dwBufIdx]), 
-				     sizeof(WAVEHDR));
-		if (mr != MMSYSERR_NOERROR) {
-		    status = PJ_STATUS_FROM_OS(mr);
-		    break;
-		}
-
-		/* Call callback */
-		status = (*strm->rec_cb)(strm->user_data, 
-					 wmme_strm->timestamp.u32.lo, 
-					 strm->buffer, 
-					 bytes_per_frame);
-
-		if (status != PJ_SUCCESS)
-		    break;
-
-		/* Increment position. */
-		if (++wmme_strm->dwBufIdx >= wmme_strm->dwMaxBufIdx)
-		    wmme_strm->dwBufIdx = 0;
-		wmme_strm->timestamp.u64 += strm->samples_per_frame / 
-					    strm->channel_count;
-	    }
-	}
-    }
-
-    PJ_LOG(5,(THIS_FILE, "WMME: thread stopping.."));
-    return 0;
-}
-
-
-/*
-* Init sound library.
-*/
-PJ_DEF(pj_status_t) pjmedia_snd_init(pj_pool_factory *factory)
-{
-    unsigned c;
-    int i;
-    int inputDeviceCount, outputDeviceCount, maximumPossibleDeviceCount;
-
-    if (snd_initialized)
-	return PJ_SUCCESS;
-
-    pj_bzero(&dev_info, sizeof(dev_info));
-
-    dev_count = 0;
-    pool_factory = factory;
-
-    /* Enumerate sound playback devices */
-    maximumPossibleDeviceCount = 0;
-
-    inputDeviceCount = waveInGetNumDevs();
-    if (inputDeviceCount > 0)
-	/* assume there is a WAVE_MAPPER */
-	maximumPossibleDeviceCount += inputDeviceCount + 1;
-
-    outputDeviceCount = waveOutGetNumDevs();
-    if (outputDeviceCount > 0)
-	/* assume there is a WAVE_MAPPER */
-	maximumPossibleDeviceCount += outputDeviceCount + 1;
-
-    if (maximumPossibleDeviceCount >= MAX_HARDWARE)
-    {
-	pj_assert(!"Too many hardware found");
-	PJ_LOG(3,(THIS_FILE, "Too many hardware found, "
-		  "some devices will not be listed"));
-    }
-
-    if (inputDeviceCount > 0)
-    {
-	/* -1 is the WAVE_MAPPER */
-	for (i = -1; i < inputDeviceCount && dev_count < MAX_HARDWARE; ++i)
-	{
-	    UINT uDeviceID = (UINT)((i==-1) ? WAVE_MAPPER : i);
-	    WAVEINCAPS wic;
-	    MMRESULT mr;
-
-	    pj_bzero(&wic, sizeof(WAVEINCAPS));
-
-	    mr = waveInGetDevCaps(uDeviceID, &wic, sizeof(WAVEINCAPS));
-
-	    if (mr == MMSYSERR_NOMEM)
-		return PJ_ENOMEM;
-
-	    if (mr != MMSYSERR_NOERROR)
-		continue;
-
-#ifdef UNICODE
-	    WideCharToMultiByte(CP_ACP, 0, wic.szPname, wcslen(wic.szPname), 
-				dev_info[dev_count].info.name, 64, NULL, NULL);
-#else
-	    strncpy(dev_info[dev_count].info.name, wic.szPname, MAXPNAMELEN);
-#endif
-	    if (uDeviceID == WAVE_MAPPER)
-		strcat(dev_info[dev_count].info.name, " - Input");
-
-	    dev_info[dev_count].info.input_count = wic.wChannels;
-	    dev_info[dev_count].info.output_count = 0;
-	    dev_info[dev_count].info.default_samples_per_sec = 44100;
-	    dev_info[dev_count].deviceId = uDeviceID;
-
-	    /* Sometimes a device can return a rediculously large number of 
-	     * channels. This happened with an SBLive card on a Windows ME box.
-	     * It also happens on Win XP!
-	     */
-	    if ((dev_info[dev_count].info.input_count < 1) || 
-		(dev_info[dev_count].info.input_count > 256))
-		dev_info[dev_count].info.input_count = 2;
-
-	    ++dev_count;
-	}
-    }
-
-    if( outputDeviceCount > 0 )
-    {
-	/* -1 is the WAVE_MAPPER */
-	for (i = -1; i < outputDeviceCount && dev_count < MAX_HARDWARE; ++i)
-	{
-	    UINT uDeviceID = (UINT)((i==-1) ? WAVE_MAPPER : i);
-	    WAVEOUTCAPS woc;
-	    MMRESULT mr;
-
-	    pj_bzero(&woc, sizeof(WAVEOUTCAPS));
-
-	    mr = waveOutGetDevCaps(uDeviceID, &woc, sizeof(WAVEOUTCAPS));
-
-	    if (mr == MMSYSERR_NOMEM)
-		return PJ_ENOMEM;
-
-	    if (mr != MMSYSERR_NOERROR)
-		continue;
-
-#ifdef UNICODE
-	    WideCharToMultiByte(CP_ACP, 0, woc.szPname, wcslen(woc.szPname),
-				dev_info[dev_count].info.name, 64, NULL, NULL);
-#else
-	    strncpy(dev_info[dev_count].info.name, woc.szPname, MAXPNAMELEN);
-#endif
-	    if (uDeviceID == WAVE_MAPPER)
-		strcat(dev_info[dev_count].info.name, " - Output");
-
-	    dev_info[dev_count].info.output_count = woc.wChannels;
-	    dev_info[dev_count].info.input_count = 0;
-	    dev_info[dev_count].deviceId = uDeviceID;
-	    /* TODO: Perform a search! */
-	    dev_info[dev_count].info.default_samples_per_sec = 44100;
-
-	    /* Sometimes a device can return a rediculously large number of channels.
-	     * This happened with an SBLive card on a Windows ME box.
-	     * It also happens on Win XP!
-	     */
-	    if ((dev_info[dev_count].info.output_count < 1) || 
-		(dev_info[dev_count].info.output_count > 256))
-		dev_info[dev_count].info.output_count = 2;
-
-	    ++dev_count;
-	}
-    }
-
-    PJ_LOG(4, (THIS_FILE, "WMME initialized, found %d devices:", dev_count));
-    for (c = 0; c < dev_count; ++c)
-    {
-	PJ_LOG(4, (THIS_FILE, " dev_id %d: %s  (in=%d, out=%d)", 
-	    c,
-	    dev_info[c].info.name,
-	    dev_info[c].info.input_count,
-	    dev_info[c].info.output_count));
-    }
-    return PJ_SUCCESS;
-}
-
-/*
- * Deinitialize sound library.
- */
-PJ_DEF(pj_status_t) pjmedia_snd_deinit(void)
-{
-    snd_initialized = PJ_FALSE;
-    return PJ_SUCCESS;
-}
-
-/*
- * Get device count.
- */
-PJ_DEF(int) pjmedia_snd_get_dev_count(void)
-{
-    return dev_count;
-}
-
-/*
- * Get device info.
- */
-PJ_DEF(const pjmedia_snd_dev_info*) pjmedia_snd_get_dev_info(unsigned index)
-{
-    if (index == (unsigned)-1) 
-	index = 0;
-
-    PJ_ASSERT_RETURN(index < dev_count, NULL);
-
-    return &dev_info[index].info;
-}
-
-
-/*
- * Open stream.
- */
-static pj_status_t open_stream(pjmedia_dir dir,
-			       int rec_id,
-			       int play_id,
-			       unsigned clock_rate,
-			       unsigned channel_count,
-			       unsigned samples_per_frame,
-			       unsigned bits_per_sample,
-			       pjmedia_snd_rec_cb rec_cb,
-			       pjmedia_snd_play_cb play_cb,
-			       void *user_data,
-			       pjmedia_snd_stream **p_snd_strm)
-{
-    pj_pool_t *pool;
-    pjmedia_snd_stream *strm;
-    pj_status_t status;
-
-
-    /* Make sure sound subsystem has been initialized with
-     * pjmedia_snd_init()
-     */
-    PJ_ASSERT_RETURN(pool_factory != NULL, PJ_EINVALIDOP);
-
-
-    /* Can only support 16bits per sample */
-    PJ_ASSERT_RETURN(bits_per_sample == BITS_PER_SAMPLE, PJ_EINVAL);
-
-    /* Create and Initialize stream descriptor */
-    pool = pj_pool_create(pool_factory, "wmme-dev", 1000, 1000, NULL);
-    PJ_ASSERT_RETURN(pool != NULL, PJ_ENOMEM);
-
-    strm = pj_pool_zalloc(pool, sizeof(pjmedia_snd_stream));
-    strm->dir = dir;
-    strm->play_id = play_id;
-    strm->rec_id = rec_id;
-    strm->pool = pool;
-    strm->rec_cb = rec_cb;
-    strm->play_cb = play_cb;
-    strm->user_data = user_data;
-    strm->clock_rate = clock_rate;
-    strm->samples_per_frame = samples_per_frame;
-    strm->bits_per_sample = bits_per_sample;
-    strm->channel_count = channel_count;
-    strm->buffer = pj_pool_alloc(pool, samples_per_frame * BYTES_PER_SAMPLE);
-    if (!strm->buffer)
-    {
-	pj_pool_release(pool);
-	return PJ_ENOMEM;
-    }
-
-    /* Create player stream */
-    if (dir & PJMEDIA_DIR_PLAYBACK)
-    {
-	unsigned buf_count;
-
-	buf_count = snd_output_latency * clock_rate * channel_count / 
-		    samples_per_frame / 1000;
-
-	status = init_player_stream(strm->pool,
-				    &strm->play_strm,
-				    play_id,
-				    clock_rate,
-				    channel_count,
-				    samples_per_frame,
-				    buf_count);
-
-	if (status != PJ_SUCCESS)
-	{
-	    pjmedia_snd_stream_close(strm);
-	    return status;
-	}
-    }
-
-    /* Create capture stream */
-    if (dir & PJMEDIA_DIR_CAPTURE)
-    {
-	unsigned buf_count;
-
-	buf_count = snd_input_latency * clock_rate * channel_count / 
-		    samples_per_frame / 1000;
-
-	status = init_capture_stream(strm->pool,
-				     &strm->rec_strm,
-				     rec_id,
-				     clock_rate,
-				     channel_count,
-				     samples_per_frame,
-				     buf_count);
-
-	if (status != PJ_SUCCESS)
-	{
-	    pjmedia_snd_stream_close(strm);
-	    return status;
-	}
-    }
-
-    /* Create the stop event */
-    strm->thread_quit_event = CreateEvent(NULL, FALSE, FALSE, NULL);
-    if (strm->thread_quit_event == NULL)
-	return pj_get_os_error();
-
-    /* Create and start the thread */
-    status = pj_thread_create(pool, "wmme", &wmme_dev_thread, strm, 0, 0, 
-			      &strm->thread);
-    if (status != PJ_SUCCESS)
-    {
-	pjmedia_snd_stream_close(strm);
-	return status;
-    }
-
-    *p_snd_strm = strm;
-
-    return PJ_SUCCESS;
-}
-
-/*
- * Open stream.
- */
-PJ_DEF(pj_status_t) pjmedia_snd_open_rec(int index,
-					 unsigned clock_rate,
-					 unsigned channel_count,
-					 unsigned samples_per_frame,
-					 unsigned bits_per_sample,
-					 pjmedia_snd_rec_cb rec_cb,
-					 void *user_data,
-					 pjmedia_snd_stream **p_snd_strm)
-{
-    PJ_ASSERT_RETURN(rec_cb && p_snd_strm, PJ_EINVAL);
-
-    return open_stream( PJMEDIA_DIR_CAPTURE,
-			index,
-			-1,
-			clock_rate,
-			channel_count,
-			samples_per_frame,
-			bits_per_sample,
-			rec_cb,
-			NULL,
-			user_data,
-			p_snd_strm);
-}
-
-PJ_DEF(pj_status_t) pjmedia_snd_open_player(int index,
-					    unsigned clock_rate,
-					    unsigned channel_count,
-					    unsigned samples_per_frame,
-					    unsigned bits_per_sample,
-					    pjmedia_snd_play_cb play_cb,
-					    void *user_data,
-					    pjmedia_snd_stream **p_snd_strm)
-{
-    PJ_ASSERT_RETURN(play_cb && p_snd_strm, PJ_EINVAL);
-
-    return open_stream( PJMEDIA_DIR_PLAYBACK,
-			-1,
-			index,
-			clock_rate,
-			channel_count,
-			samples_per_frame,
-			bits_per_sample,
-			NULL,
-			play_cb,
-			user_data,
-			p_snd_strm);
-}
-
-/*
- * Open both player and recorder.
- */
-PJ_DEF(pj_status_t) pjmedia_snd_open(int rec_id,
-				     int play_id,
-				     unsigned clock_rate,
-				     unsigned channel_count,
-				     unsigned samples_per_frame,
-				     unsigned bits_per_sample,
-				     pjmedia_snd_rec_cb rec_cb,
-				     pjmedia_snd_play_cb play_cb,
-				     void *user_data,
-				     pjmedia_snd_stream **p_snd_strm)
-{
-    PJ_ASSERT_RETURN(rec_cb && play_cb && p_snd_strm, PJ_EINVAL);
-
-    return open_stream( PJMEDIA_DIR_CAPTURE_PLAYBACK,
-			rec_id,
-			play_id,
-			clock_rate,
-			channel_count,
-			samples_per_frame,
-			bits_per_sample,
-			rec_cb,
-			play_cb,
-			user_data,
-			p_snd_strm);
-}
-
-/*
- * Get stream info.
- */
-PJ_DEF(pj_status_t) pjmedia_snd_stream_get_info(pjmedia_snd_stream *strm, 
-						pjmedia_snd_stream_info *pi)
-{
-    PJ_ASSERT_RETURN(strm && pi, PJ_EINVAL);
-
-    pj_bzero(pi, sizeof(*pi));
-    pi->dir = strm->dir;
-    pi->play_id = strm->play_id;
-    pi->rec_id = strm->rec_id;
-    pi->clock_rate = strm->clock_rate;
-    pi->channel_count = strm->channel_count;
-    pi->samples_per_frame = strm->samples_per_frame;
-    pi->bits_per_sample = strm->bits_per_sample;
-    pi->rec_latency = snd_input_latency * strm->clock_rate * 
-		      strm->channel_count / 1000;
-    pi->play_latency = snd_output_latency * strm->clock_rate * 
-		       strm->channel_count / 1000;
-
-    return PJ_SUCCESS;
-}
-
-
-/*
-* Start stream.
-*/
-PJ_DEF(pj_status_t) pjmedia_snd_stream_start(pjmedia_snd_stream *stream)
-{
-    MMRESULT mr;
-
-    PJ_UNUSED_ARG(stream);
-
-    if (stream->play_strm.hWave.Out != NULL)
-    {
-	mr = waveOutRestart(stream->play_strm.hWave.Out);
-	if (mr != MMSYSERR_NOERROR)
-	    /* TODO: This macro is supposed to be used for HRESULT, fix. */
-	    PJ_RETURN_OS_ERROR(mr);
-	PJ_LOG(5,(THIS_FILE, "WMME playback stream started"));
-    }
-
-    if (stream->rec_strm.hWave.In != NULL)
-    {
-	mr = waveInStart(stream->rec_strm.hWave.In);
-	if (mr != MMSYSERR_NOERROR)
-	    /* TODO: This macro is supposed to be used for HRESULT, fix. */
-	    PJ_RETURN_OS_ERROR(mr);
-	PJ_LOG(5,(THIS_FILE, "WMME capture stream started"));
-    }
-
-    return PJ_SUCCESS;
-}
-
-/*
- * Stop stream.
- */
-PJ_DEF(pj_status_t) pjmedia_snd_stream_stop(pjmedia_snd_stream *stream)
-{
-    MMRESULT mr;
-
-    PJ_ASSERT_RETURN(stream != NULL, PJ_EINVAL);
-
-    if (stream->play_strm.hWave.Out != NULL)
-    {
-	mr = waveOutPause(stream->play_strm.hWave.Out);
-	if (mr != MMSYSERR_NOERROR)
-	    /* TODO: This macro is supposed to be used for HRESULT, fix. */
-	    PJ_RETURN_OS_ERROR(mr);
-	PJ_LOG(5,(THIS_FILE, "Stopped WMME playback stream"));
-    }
-
-    if (stream->rec_strm.hWave.In != NULL)
-    {
-	mr = waveInStop(stream->rec_strm.hWave.In);
-	if (mr != MMSYSERR_NOERROR)
-	    /* TODO: This macro is supposed to be used for HRESULT, fix. */
-	    PJ_RETURN_OS_ERROR(mr);
-	PJ_LOG(5,(THIS_FILE, "Stopped WMME capture stream"));
-    }
-
-    return PJ_SUCCESS;
-}
-
-
-/*
- * Destroy stream.
- */
-PJ_DEF(pj_status_t) pjmedia_snd_stream_close(pjmedia_snd_stream *stream)
-{
-    unsigned i;
-
-    PJ_ASSERT_RETURN(stream != NULL, PJ_EINVAL);
-
-    pjmedia_snd_stream_stop(stream);
-
-    if (stream->thread)
-    {
-	SetEvent(stream->thread_quit_event);
-	pj_thread_join(stream->thread);
-	pj_thread_destroy(stream->thread);
-	stream->thread = NULL;
-    }
-
-    /* Unprepare the headers and close the play device */
-    if (stream->play_strm.hWave.Out)
-    {
-	waveOutReset(stream->play_strm.hWave.Out);
-	for (i = 0; i < stream->play_strm.dwMaxBufIdx; ++i)
-	    waveOutUnprepareHeader(stream->play_strm.hWave.Out, 
-				   &(stream->play_strm.WaveHdr[i]),
-				   sizeof(WAVEHDR));
-	waveOutClose(stream->play_strm.hWave.Out);
-	stream->play_strm.hWave.Out = NULL;
-    }
-
-    /* Close the play event */
-    if (stream->play_strm.hEvent)
-    {
-	CloseHandle(stream->play_strm.hEvent);
-	stream->play_strm.hEvent = NULL;
-    }
-
-    /* Unprepare the headers and close the record device */
-    if (stream->rec_strm.hWave.In)
-    {
-	waveInReset(stream->rec_strm.hWave.In);
-	for (i = 0; i < stream->play_strm.dwMaxBufIdx; ++i)
-	    waveInUnprepareHeader(stream->rec_strm.hWave.In, 
-				  &(stream->rec_strm.WaveHdr[i]),
-				  sizeof(WAVEHDR));
-	waveInClose(stream->rec_strm.hWave.In);
-	stream->rec_strm.hWave.In = NULL;
-    }
-
-    /* Close the record event */
-    if (stream->rec_strm.hEvent)
-    {
-	CloseHandle(stream->rec_strm.hEvent);
-	stream->rec_strm.hEvent = NULL;
-    }
-
-    pj_pool_release(stream->pool);
-
-    return PJ_SUCCESS;
-}
-
-/*
- * Set sound latency.
- */
-PJ_DEF(pj_status_t) pjmedia_snd_set_latency(unsigned input_latency, 
-					    unsigned output_latency)
-{
-    snd_input_latency  = (input_latency == 0)? 
-			  PJMEDIA_SND_DEFAULT_REC_LATENCY : input_latency;
-    snd_output_latency = (output_latency == 0)? 
-			 PJMEDIA_SND_DEFAULT_PLAY_LATENCY : output_latency;
-
-    return PJ_SUCCESS;
-}
-
-#endif	/* PJMEDIA_SOUND_IMPLEMENTATION */
-