Index: /pjproject/trunk/pjmedia/build/pjmedia.dsp
===================================================================
--- /pjproject/trunk/pjmedia/build/pjmedia.dsp	(revision 370)
+++ /pjproject/trunk/pjmedia/build/pjmedia.dsp	(revision 371)
@@ -66,5 +66,5 @@
 # PROP Target_Dir ""
 # ADD BASE CPP /nologo /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_MBCS" /D "_LIB" /YX /FD /GZ /c
-# ADD CPP /nologo /MTd /W4 /Gm /GX /ZI /Od /I "../include" /I "../../pjlib/include" /I "../../pjlib-util/include" /I "../src/pjmedia/portaudio" /D "_DEBUG" /D "PA_NO_ASIO" /D "PA_NO_DS" /D "WIN32" /D "_MBCS" /D "_LIB" /D PJ_WIN32=1 /D PJ_M_I386=1 /FR /FD /GZ /c
+# ADD CPP /nologo /MTd /W4 /Gm /GX /ZI /Od /I "../include" /I "../../pjlib/include" /I "../../pjlib-util/include" /I "../src/pjmedia/portaudio" /D "_DEBUG" /D "PA_NO_ASIO" /D "WIN32" /D "_MBCS" /D "_LIB" /D PJ_WIN32=1 /D PJ_M_I386=1 /FR /FD /GZ /c
 # SUBTRACT CPP /YX
 # ADD BASE RSC /l 0x409 /d "_DEBUG"
@@ -97,5 +97,4 @@
 
 SOURCE=..\src\pjmedia\dsound.c
-# PROP Exclude_From_Build 1
 # End Source File
 # Begin Source File
@@ -134,4 +133,11 @@
 
 SOURCE=..\src\pjmedia\pasound.c
+
+!IF  "$(CFG)" == "pjmedia - Win32 Release"
+
+!ELSEIF  "$(CFG)" == "pjmedia - Win32 Debug"
+
+!ENDIF 
+
 # End Source File
 # Begin Source File
Index: /pjproject/trunk/pjmedia/include/pjmedia/config.h
===================================================================
--- /pjproject/trunk/pjmedia/include/pjmedia/config.h	(revision 370)
+++ /pjproject/trunk/pjmedia/include/pjmedia/config.h	(revision 371)
@@ -22,19 +22,18 @@
 #include <pj/config.h>
 
-/**
- * Unless specified otherwise, PortAudio is enabled by default.
+/*
+ * Types of sound stream backends.
  */
-#ifndef PJMEDIA_HAS_PORTAUDIO_SOUND
-#   define PJMEDIA_HAS_PORTAUDIO_SOUND	    1
-#endif
+#define PJMEDIA_SOUND_NULL_SOUND	    0
+#define PJMEDIA_SOUND_PORTAUDIO_SOUND	    1
+#define PJMEDIA_SOUND_WIN32_DIRECT_SOUND    2
 
 
 /**
- * Unless specified otherwise, Null sound is disabled.
- * This option is mutually exclusive with PortAudio sound, or otherwise
- * duplicate symbols error will occur.
+ * Unless specified otherwise, sound device uses PortAudio implementation
+ * by default.
  */
-#ifndef PJMEDIA_HAS_NULL_SOUND
-#   define PJMEDIA_HAS_NULL_SOUND	    0
+#ifndef PJMEDIA_SOUND_IMPLEMENTATION
+#   define PJMEDIA_SOUND_IMPLEMENTATION	    PJMEDIA_SOUND_PORTAUDIO_SOUND
 #endif
 
Index: /pjproject/trunk/pjmedia/include/pjmedia/errno.h
===================================================================
--- /pjproject/trunk/pjmedia/include/pjmedia/errno.h	(revision 370)
+++ /pjproject/trunk/pjmedia/include/pjmedia/errno.h	(revision 371)
@@ -20,4 +20,5 @@
 #define __PJMEDIA_ERRNO_H__
 
+#include <pjmedia/types.h>
 #include <pj/errno.h>
 
Index: /pjproject/trunk/pjmedia/src/pjmedia/dsound.c
===================================================================
--- /pjproject/trunk/pjmedia/src/pjmedia/dsound.c	(revision 370)
+++ /pjproject/trunk/pjmedia/src/pjmedia/dsound.c	(revision 371)
@@ -17,230 +17,108 @@
  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA 
  */
+#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_DIRECT_SOUND
+
 #ifdef _MSC_VER
-//# pragma warning(disable: 4201)   // non-standard extension: nameless struct/union
-# pragma warning(push, 3)
+#   pragma warning(push, 3)
 #endif
-#include <pj/config.h>
-#include <pj/os.h>
-#include <pj/log.h>
-
+
+#include <windows.h>
+#include <mmsystem.h>
 #include <dsound.h>
-#include <stdio.h>
-#include <assert.h>
-#include <pjmedia/sound.h>
-
-#define THIS_FILE   "dsound.c"
-
-/*
- * Constants
- */
-#define PACKET_BUFFER_COUNT 4
-
-typedef struct PJ_Direct_Sound_Device PJ_Direct_Sound_Device;
-
-
-/*
- * DirectSound Factory Operations
- */
-static pj_status_t dsound_init(void);
-static const char *dsound_get_name(void);
-static pj_status_t dsound_destroy(void);
-static pj_status_t dsound_enum_devices(int *count, char *dev_names[]);
-static pj_status_t dsound_create_dev(const char *dev_name, pj_snd_dev *dev);
-static pj_status_t dsound_destroy_dev(pj_snd_dev *dev);
-
-
-/*
- * DirectSound Device Operations
- */
-static pj_status_t dsound_dev_open( pj_snd_dev *dev, pj_snd_role_t role );
-static pj_status_t dsound_dev_close( pj_snd_dev *dev );
-static pj_status_t dsound_dev_play( pj_snd_dev *dev );
-static pj_status_t dsound_dev_record( pj_snd_dev *dev );
-
-/*
- * Utils.
- */
-static pj_status_t dsound_destroy_dsound_dev( PJ_Direct_Sound_Device *dsDev );
-
-
-static pj_snd_dev_factory dsound_factory = 
-{
-    &dsound_init,
-    &dsound_get_name,
-    &dsound_destroy,
-    &dsound_enum_devices,
-    &dsound_create_dev,
-    &dsound_destroy_dev
+
+#ifdef _MSC_VER
+#   pragma warning(pop)
+#endif
+
+
+
+#define THIS_FILE	    "dsound.c"
+#define BITS_PER_SAMPLE	    16
+#define BYTES_PER_SAMPLE    (BITS_PER_SAMPLE/8)
+
+#define MAX_PACKET_BUFFER_COUNT	    32
+#define DEFAULT_BUFFER_COUNT	    5
+
+
+
+/* Individual DirectSound capture/playback stream descriptor */
+struct dsound_stream
+{
+    union 
+    {
+	struct
+	{
+	    LPDIRECTSOUND	    lpDs;
+	    LPDIRECTSOUNDBUFFER	    lpDsBuffer;
+	} play;
+
+	struct
+	{
+	    LPDIRECTSOUNDCAPTURE	lpDs;
+	    LPDIRECTSOUNDCAPTUREBUFFER	lpDsBuffer;
+	} capture;
+    } ds;
+
+    HANDLE		    hEvent;
+    LPDIRECTSOUNDNOTIFY	    lpDsNotify;
+    DWORD		    dwBytePos;
+    DWORD		    dwDsBufferSize;
+    pj_timestamp	    timestamp;
 };
 
-static struct pj_snd_dev_op dsound_dev_op = 
-{
-    &dsound_dev_open,
-    &dsound_dev_close,
-    &dsound_dev_play,
-    &dsound_dev_record
+
+/* Sound stream.    */
+struct pjmedia_snd_stream
+{
+    pjmedia_dir		    dir;		/**< Sound direction.	    */
+    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 dsound_stream    play_strm;		/**< Playback stream.	    */
+    struct dsound_stream    rec_strm;		/**< Capture stream.	    */
+
+    void		   *buffer;		/**< Temp. frame buffer.    */
+    unsigned		    samples_per_frame;	/**< Samples per frame.	    */
+
+    pj_thread_t		   *thread;		/**< Thread handle.	    */
+    pj_bool_t		    thread_quit_flag;	/**< Quit signal to thread  */
 };
 
-#define DSOUND_TYPE_PLAYER	1
-#define DSOUND_TYPE_RECORDER	2
-
-typedef struct Direct_Sound_Descriptor
-{
-    int			type;
-
-    LPDIRECTSOUND	lpDsPlay;
-    LPDIRECTSOUNDBUFFER	lpDsPlayBuffer;
-
-    LPDIRECTSOUNDCAPTURE	lpDsCapture;
-    LPDIRECTSOUNDCAPTUREBUFFER	lpDsCaptureBuffer;
-
-    LPDIRECTSOUNDNOTIFY	lpDsNotify;
-    HANDLE		hEvent;
-    HANDLE		hThread;
-    HANDLE		hStartEvent;
-    DWORD		dwThreadQuitFlag;
-    pj_thread_desc	thread_desc;
-    pj_thread_t	       *thread;
-} Direct_Sound_Descriptor;
-
-struct PJ_Direct_Sound_Device
-{
-    Direct_Sound_Descriptor playDesc;
-    Direct_Sound_Descriptor recDesc;
-};
-
-struct Thread_Param
-{
-    pj_snd_dev	    *dev;
-    Direct_Sound_Descriptor *desc;
-};
-
-PJ_DEF(pj_snd_dev_factory*) pj_dsound_get_factory()
-{
-    return &dsound_factory;
-}
-
-/*
- * Init DirectSound.
- */
-static pj_status_t dsound_init(void)
-{
-    /* Nothing to do. */
-    return 0;
-}
-
-/*
- * Get the name of the factory.
- */
-static const char *dsound_get_name(void)
-{
-    return "DirectSound";
-}
-
-/*
- * Destroy DirectSound.
- */
-static pj_status_t dsound_destroy(void)
-{
-    /* TODO: clean up devices in case application haven't done it. */
-    return 0;
-}
-
-/*
- * Enum devices in the system.
- */
-static pj_status_t dsound_enum_devices(int *count, char *dev_names[])
-{
-    dev_names[0] = "DirectSound Default Device";
-    *count = 1;
-    return 0;
-}
-
-/*
- * Create DirectSound device.
- */
-static pj_status_t dsound_create_dev(const char *dev_name, pj_snd_dev *dev)
-{
-    PJ_Direct_Sound_Device *dsDev;
-
-    /* TODO: create based on the name. */
-    PJ_TODO(DSOUND_CREATE_DEVICE_BY_NAME);
-    
-    /* Create DirectSound structure. */
-    dsDev = malloc(sizeof(*dsDev));
-    if (!dsDev) {
-	PJ_LOG(1,(THIS_FILE, "No memory to allocate device!"));
-	return -1;
-    }
-    memset(dsDev, 0, sizeof(*dsDev));
-
-    /* Associate DirectSound device with device. */
-    dev->device = dsDev;
-    dev->op = &dsound_dev_op;
-
-    return 0;
-}
-
-/*
- * Destroy DirectSound device.
- */
-static pj_status_t dsound_destroy_dev( pj_snd_dev *dev )
-{
-    if (dev->device) {
-	free(dev->device);
-	dev->device = NULL;
-    }
-    return 0;
-}
-
-static void dsound_release_descriptor(Direct_Sound_Descriptor *desc)
-{
-    if (desc->lpDsNotify)
-	IDirectSoundNotify_Release( desc->lpDsNotify );
-    
-    if (desc->hEvent)
-	CloseHandle(desc->hEvent);
-
-    if (desc->lpDsPlayBuffer)
-	IDirectSoundBuffer_Release( desc->lpDsPlayBuffer );
-
-    if (desc->lpDsPlay)
-	IDirectSound_Release( desc->lpDsPlay );
-
-    if (desc->lpDsCaptureBuffer)
-	IDirectSoundCaptureBuffer_Release(desc->lpDsCaptureBuffer);
-
-    if (desc->lpDsCapture)
-	IDirectSoundCapture_Release(desc->lpDsCapture);
-}
-
-/*
- * Destroy DirectSound resources.
- */
-static pj_status_t dsound_destroy_dsound_dev( PJ_Direct_Sound_Device *dsDev )
-{
-    dsound_release_descriptor( &dsDev->playDesc );
-    dsound_release_descriptor( &dsDev->recDesc );
-    memset(dsDev, 0, sizeof(*dsDev));
-    return 0;
-}
-
-static void init_waveformatex (PCMWAVEFORMAT *pcmwf, pj_snd_dev *dev)
-{
-    memset(pcmwf, 0, sizeof(PCMWAVEFORMAT)); 
+
+static pj_pool_factory *pool_factory;
+
+
+static void init_waveformatex (PCMWAVEFORMAT *pcmwf, 
+			       unsigned clock_rate,
+			       unsigned channel_count)
+{
+    pj_memset(pcmwf, 0, sizeof(PCMWAVEFORMAT)); 
     pcmwf->wf.wFormatTag = WAVE_FORMAT_PCM; 
-    pcmwf->wf.nChannels = 1;
-    pcmwf->wf.nSamplesPerSec = dev->param.samples_per_sec;
-    pcmwf->wf.nBlockAlign = dev->param.bytes_per_frame;
-    pcmwf->wf.nAvgBytesPerSec = 
-	dev->param.samples_per_sec * dev->param.bytes_per_frame;
-    pcmwf->wBitsPerSample = dev->param.bits_per_sample;
-}
+    pcmwf->wf.nChannels = (pj_uint16_t)channel_count;
+    pcmwf->wf.nSamplesPerSec = clock_rate;
+    pcmwf->wf.nBlockAlign = (pj_uint16_t)(channel_count * BYTES_PER_SAMPLE);
+    pcmwf->wf.nAvgBytesPerSec = clock_rate * channel_count * BYTES_PER_SAMPLE;
+    pcmwf->wBitsPerSample = BITS_PER_SAMPLE;
+}
+
 
 /*
  * Initialize DirectSound player device.
  */
-static pj_status_t dsound_init_player (pj_snd_dev *dev)
+static pj_status_t init_player_stream( struct dsound_stream *ds_strm,
+				       unsigned clock_rate,
+				       unsigned channel_count,
+				       unsigned samples_per_frame,
+				       unsigned buffer_count)
 {
     HRESULT hr;
@@ -248,28 +126,17 @@
     PCMWAVEFORMAT pcmwf; 
     DSBUFFERDESC dsbdesc;
-    DSBPOSITIONNOTIFY dsPosNotify[PACKET_BUFFER_COUNT];
+    DSBPOSITIONNOTIFY dsPosNotify[MAX_PACKET_BUFFER_COUNT];
+    unsigned bytes_per_frame;
     unsigned i;
-    PJ_Direct_Sound_Device *dsDev = dev->device;
-
-    /*
-     * Check parameters.
-     */
-    if (dev->play_cb == NULL) {
-	assert(0);
-	return -1;
-    }
-    if (dev->device == NULL) {
-	assert(0);
-	return -1;
-    }
-
-    PJ_LOG(4,(THIS_FILE, "Creating DirectSound player device"));
+
+
+    PJ_ASSERT_RETURN(buffer_count <= MAX_PACKET_BUFFER_COUNT, PJ_EINVAL);
 
     /*
      * Create DirectSound device.
      */
-    hr = DirectSoundCreate(NULL, &dsDev->playDesc.lpDsPlay, NULL);
+    hr = DirectSoundCreate(NULL, &ds_strm->ds.play.lpDs, NULL);
     if (FAILED(hr))
-	goto on_error;
+	return PJ_RETURN_OS_ERROR(hr);
 
     hwnd = GetForegroundWindow();
@@ -277,107 +144,110 @@
 	hwnd = GetDesktopWindow();
     }    
-    hr = IDirectSound_SetCooperativeLevel( dsDev->playDesc.lpDsPlay, hwnd, 
+    hr = IDirectSound_SetCooperativeLevel( ds_strm->ds.play.lpDs, hwnd, 
 					   DSSCL_PRIORITY);
     if FAILED(hr)
-	goto on_error;
+	return PJ_RETURN_OS_ERROR(hr);
     
     /*
-     * Create DirectSound play buffer.
-     */    
-    // Set up wave format structure. 
-    init_waveformatex (&pcmwf, dev);
-
-    // Set up DSBUFFERDESC structure. 
-    memset(&dsbdesc, 0, sizeof(DSBUFFERDESC)); 
+     * Set up wave format structure for initialize DirectSound play
+     * buffer. 
+     */
+    init_waveformatex(&pcmwf, clock_rate, channel_count);
+    bytes_per_frame = samples_per_frame * BYTES_PER_SAMPLE;
+
+    /* Set up DSBUFFERDESC structure. */
+    pj_memset(&dsbdesc, 0, sizeof(DSBUFFERDESC)); 
     dsbdesc.dwSize = sizeof(DSBUFFERDESC); 
-    dsbdesc.dwFlags = DSBCAPS_CTRLVOLUME | DSBCAPS_CTRLPOSITIONNOTIFY |
-		      DSBCAPS_GETCURRENTPOSITION2;
-
-    dsbdesc.dwBufferBytes = 
-	(PACKET_BUFFER_COUNT * dev->param.bytes_per_frame * 
-	 dev->param.frames_per_packet); 
+    dsbdesc.dwFlags = DSBCAPS_CTRLVOLUME | DSBCAPS_CTRLPOSITIONNOTIFY;
+    /* DSBCAPS_GETCURRENTPOSITION2 */
+
+    dsbdesc.dwBufferBytes = buffer_count * bytes_per_frame;
     dsbdesc.lpwfxFormat = (LPWAVEFORMATEX)&pcmwf; 
 
-    // Create buffer. 
-    hr = IDirectSound_CreateSoundBuffer(dsDev->playDesc.lpDsPlay, &dsbdesc, 
-					&dsDev->playDesc.lpDsPlayBuffer, NULL); 
+    /*
+     * Create DirectSound playback buffer. 
+     */
+    hr = IDirectSound_CreateSoundBuffer(ds_strm->ds.play.lpDs, &dsbdesc, 
+					&ds_strm->ds.play.lpDsBuffer, NULL); 
     if (FAILED(hr) )
-	goto on_error;
+	return PJ_RETURN_OS_ERROR(hr);
 
     /*
      * Create event for play notification.
      */
-    dsDev->playDesc.hEvent = CreateEvent( NULL, FALSE, FALSE, NULL);
-    if (dsDev->playDesc.hEvent == NULL)
-	goto on_error;
+    ds_strm->hEvent = CreateEvent( NULL, FALSE, FALSE, NULL);
+    if (ds_strm->hEvent == NULL)
+	return pj_get_os_error();
 
     /*
      * Setup notification for play.
      */
-    hr = IDirectSoundBuffer_QueryInterface( dsDev->playDesc.lpDsPlayBuffer, 
+    hr = IDirectSoundBuffer_QueryInterface( ds_strm->ds.play.lpDsBuffer, 
 					    &IID_IDirectSoundNotify, 
-					    (LPVOID *)&dsDev->playDesc.lpDsNotify); 
+					    (LPVOID *)&ds_strm->lpDsNotify); 
     if (FAILED(hr))
-	goto on_error;
-
-    
-    for (i=0; i<PACKET_BUFFER_COUNT; ++i) {
-	dsPosNotify[i].dwOffset = i * dev->param.bytes_per_frame * 
-				  dev->param.frames_per_packet;
-	dsPosNotify[i].hEventNotify = dsDev->playDesc.hEvent;
-    }
-    
-    hr = IDirectSoundNotify_SetNotificationPositions( dsDev->playDesc.lpDsNotify, 
-						      PACKET_BUFFER_COUNT, 
+	return PJ_RETURN_OS_ERROR(hr);
+
+    
+    for (i=0; i<buffer_count; ++i) {
+	dsPosNotify[i].dwOffset = i * bytes_per_frame;
+	dsPosNotify[i].hEventNotify = ds_strm->hEvent;
+    }
+    
+    hr = IDirectSoundNotify_SetNotificationPositions( ds_strm->lpDsNotify, 
+						      buffer_count, 
 						      dsPosNotify);
     if (FAILED(hr))
-	goto on_error;
+	return PJ_RETURN_OS_ERROR(hr);
+
+
+    hr = IDirectSoundBuffer_SetCurrentPosition(ds_strm->ds.play.lpDsBuffer, 0);
+    if (FAILED(hr))
+	return PJ_RETURN_OS_ERROR(hr);
+
+
+    ds_strm->dwBytePos = 0;
+    ds_strm->dwDsBufferSize = buffer_count * bytes_per_frame;
+    ds_strm->timestamp.u64 = 0;
+
 
     /* Done setting up play device. */
-    PJ_LOG(4,(THIS_FILE, "DirectSound player device created"));
-
-    return 0;
-
-on_error:
-    PJ_LOG(2,(THIS_FILE, "Error creating player device, hresult=0x%x", hr));
-    dsound_destroy_dsound_dev(dsDev);
-    return -1;
-}
+    PJ_LOG(5,(THIS_FILE, " DirectSound player stream initialized"));
+
+    return PJ_SUCCESS;
+}
+
 
 /*
  * Initialize DirectSound recorder device
  */
-static pj_status_t dsound_init_recorder (pj_snd_dev *dev)
+static pj_status_t init_capture_stream( struct dsound_stream *ds_strm,
+				        unsigned clock_rate,
+				        unsigned channel_count,
+				        unsigned samples_per_frame,
+				        unsigned buffer_count)
 {
     HRESULT hr;
     PCMWAVEFORMAT pcmwf; 
     DSCBUFFERDESC dscbdesc;
-    DSBPOSITIONNOTIFY dsPosNotify[PACKET_BUFFER_COUNT];
+    DSBPOSITIONNOTIFY dsPosNotify[MAX_PACKET_BUFFER_COUNT];
+    unsigned bytes_per_frame;
     unsigned i;
-    PJ_Direct_Sound_Device *dsDev = dev->device;
-
-    /*
-     * Check parameters.
-     */
-    if (dev->rec_cb == NULL) {
-	assert(0);
-	return -1;
-    }
-    if (dev->device == NULL) {
-	assert(0);
-	return -1;
-    }
-
-    PJ_LOG(4,(THIS_FILE, "Creating DirectSound recorder device"));
+
+
+    PJ_ASSERT_RETURN(buffer_count <= MAX_PACKET_BUFFER_COUNT, PJ_EINVAL);
+
 
     /*
      * Creating recorder device.
      */
-    hr = DirectSoundCaptureCreate(NULL, &dsDev->recDesc.lpDsCapture, NULL);
+    hr = DirectSoundCaptureCreate(NULL, &ds_strm->ds.capture.lpDs, NULL);
     if (FAILED(hr))
-	goto on_error;
-
-    /* Init wave format */
-    init_waveformatex (&pcmwf, dev);
+	return PJ_RETURN_OS_ERROR(hr);
+
+
+    /* Init wave format to initialize buffer */
+    init_waveformatex( &pcmwf, clock_rate, channel_count);
+    bytes_per_frame = samples_per_frame * BYTES_PER_SAMPLE;
 
     /* 
@@ -385,128 +255,60 @@
      * to play buffer creation earlier.
      */
-    memset(&dscbdesc, 0, sizeof(DSCBUFFERDESC));
+    pj_memset(&dscbdesc, 0, sizeof(DSCBUFFERDESC));
     dscbdesc.dwSize = sizeof(DSCBUFFERDESC); 
     dscbdesc.dwFlags = DSCBCAPS_WAVEMAPPED ;
-    dscbdesc.dwBufferBytes = 
-	(PACKET_BUFFER_COUNT * dev->param.bytes_per_frame * 
-	 dev->param.frames_per_packet); 
+    dscbdesc.dwBufferBytes = buffer_count * bytes_per_frame; 
     dscbdesc.lpwfxFormat = (LPWAVEFORMATEX)&pcmwf; 
 
-    hr = IDirectSoundCapture_CreateCaptureBuffer( dsDev->recDesc.lpDsCapture,
+    hr = IDirectSoundCapture_CreateCaptureBuffer( ds_strm->ds.capture.lpDs,
 						  &dscbdesc, 
-						  &dsDev->recDesc.lpDsCaptureBuffer, 
+						  &ds_strm->ds.capture.lpDsBuffer,
 						  NULL);
     if (FAILED(hr))
-	goto on_error;
+	return PJ_RETURN_OS_ERROR(hr);
 
     /*
      * Create event for play notification.
      */
-    dsDev->recDesc.hEvent = CreateEvent( NULL, FALSE, FALSE, NULL);
-    if (dsDev->recDesc.hEvent == NULL)
-	goto on_error;
+    ds_strm->hEvent = CreateEvent( NULL, FALSE, FALSE, NULL);
+    if (ds_strm->hEvent == NULL)
+	return pj_get_os_error();
 
     /*
      * Setup notifications for recording.
      */
-    hr = IDirectSoundCaptureBuffer_QueryInterface( dsDev->recDesc.lpDsCaptureBuffer, 
+    hr = IDirectSoundCaptureBuffer_QueryInterface( ds_strm->ds.capture.lpDsBuffer, 
 						   &IID_IDirectSoundNotify, 
-						   (LPVOID *)&dsDev->recDesc.lpDsNotify); 
+						   (LPVOID *)&ds_strm->lpDsNotify); 
     if (FAILED(hr))
-	goto on_error;
-
-    
-    for (i=0; i<PACKET_BUFFER_COUNT; ++i) {
-	dsPosNotify[i].dwOffset = i * dev->param.bytes_per_frame * 
-				  dev->param.frames_per_packet;
-	dsPosNotify[i].hEventNotify = dsDev->recDesc.hEvent;
-    }
-    
-    hr = IDirectSoundNotify_SetNotificationPositions( dsDev->recDesc.lpDsNotify, 
-						      PACKET_BUFFER_COUNT, 
+	return PJ_RETURN_OS_ERROR(hr);
+
+    
+    for (i=0; i<buffer_count; ++i) {
+	dsPosNotify[i].dwOffset = i * bytes_per_frame;
+	dsPosNotify[i].hEventNotify = ds_strm->hEvent;
+    }
+    
+    hr = IDirectSoundNotify_SetNotificationPositions( ds_strm->lpDsNotify, 
+						      buffer_count, 
 						      dsPosNotify);
     if (FAILED(hr))
-	goto on_error;
+	return PJ_RETURN_OS_ERROR(hr);
+
+    hr = IDirectSoundCaptureBuffer_GetCurrentPosition( ds_strm->ds.capture.lpDsBuffer, 
+						       NULL, &ds_strm->dwBytePos );
+    if (FAILED(hr))
+	return PJ_RETURN_OS_ERROR(hr);
+
+    ds_strm->timestamp.u64 = 0;
+    ds_strm->dwDsBufferSize = buffer_count * bytes_per_frame;
 
     /* Done setting up recorder device. */
-    PJ_LOG(4,(THIS_FILE, "DirectSound recorder device created"));
-
-    return 0;
-
-on_error:
-    PJ_LOG(4,(THIS_FILE, "Error creating device, hresult=%d", hr));
-    dsound_destroy_dsound_dev(dsDev);
-    return -1;
-}
-
-/*
- * Initialize DirectSound device.
- */
-static pj_status_t dsound_dev_open( pj_snd_dev *dev, pj_snd_role_t role )
-{
-    PJ_Direct_Sound_Device *dsDev = dev->device;
-    pj_status_t status;
-
-    dsDev->playDesc.type = DSOUND_TYPE_PLAYER;
-    dsDev->recDesc.type = DSOUND_TYPE_RECORDER;
-
-    if (role & PJ_SOUND_PLAYER) {
-	status = dsound_init_player (dev);
-	if (status != 0)
-	    return status;
-    }
-
-    if (role & PJ_SOUND_RECORDER) {
-	status = dsound_init_recorder (dev);
-	if (status != 0)
-	    return status;
-    }
-
-    return 0;
-}
-
-/*
- * Close DirectSound device.
- */
-static pj_status_t dsound_dev_close( pj_snd_dev *dev )
-{
-    PJ_LOG(4,(THIS_FILE, "Closing DirectSound device"));
-
-    if (dev->device) {
-	PJ_Direct_Sound_Device *dsDev = dev->device;
-
-	if (dsDev->playDesc.hThread) {
-	    PJ_LOG(4,(THIS_FILE, "Stopping DirectSound player"));
-	    dsDev->playDesc.dwThreadQuitFlag = 1;
-	    SetEvent(dsDev->playDesc.hEvent);
-	    if (WaitForSingleObject(dsDev->playDesc.hThread, 1000) != WAIT_OBJECT_0) {
-		PJ_LOG(4,(THIS_FILE, "Timed out waiting player thread to quit"));
-		TerminateThread(dsDev->playDesc.hThread, -1);
-		IDirectSoundBuffer_Stop( dsDev->playDesc.lpDsPlayBuffer );
-	    }
-	    
-	    pj_thread_destroy (dsDev->playDesc.thread);
-	}
-
-	if (dsDev->recDesc.hThread) {
-	    PJ_LOG(4,(THIS_FILE, "Stopping DirectSound recorder"));
-	    dsDev->recDesc.dwThreadQuitFlag = 1;
-	    SetEvent(dsDev->recDesc.hEvent);
-	    if (WaitForSingleObject(dsDev->recDesc.hThread, 1000) != WAIT_OBJECT_0) {
-		PJ_LOG(4,(THIS_FILE, "Timed out waiting recorder thread to quit"));
-		TerminateThread(dsDev->recDesc.hThread, -1);
-		IDirectSoundCaptureBuffer_Stop( dsDev->recDesc.lpDsCaptureBuffer );
-	    }
-	    
-	    pj_thread_destroy (dsDev->recDesc.thread);
-	}
-
-	dsound_destroy_dsound_dev( dev->device );
-	dev->op = NULL;
-    }
-
-    PJ_LOG(4,(THIS_FILE, "DirectSound device closed"));
-    return 0;
-}
+    PJ_LOG(5,(THIS_FILE, " DirectSound capture stream initialized"));
+
+    return PJ_SUCCESS;
+}
+
+
 
 static BOOL AppReadDataFromBuffer(LPDIRECTSOUNDCAPTUREBUFFER lpDsb, // The buffer.
@@ -584,96 +386,21 @@
  * Player thread.
  */
-static DWORD WINAPI dsound_dev_thread(void *arg)
-{
-    struct Thread_Param *param = arg;
-    pj_snd_dev *dev = param->dev;
-    Direct_Sound_Descriptor *desc = param->desc;
-    unsigned bytes_per_pkt = dev->param.bytes_per_frame *
-			     dev->param.frames_per_packet;
-    unsigned samples_per_pkt = dev->param.samples_per_frame *
-			       dev->param.frames_per_packet;
-    void *buffer = NULL;
-    DWORD size;
+static int dsound_dev_thread(void *arg)
+{
+    pjmedia_snd_stream *strm = arg;
+    HANDLE events[2];
+    unsigned eventCount;
     pj_status_t status;
-    DWORD sample_pos;
-    DWORD byte_pos;
-    DWORD buffer_size = 
-	(PACKET_BUFFER_COUNT * dev->param.bytes_per_frame * 
-	 dev->param.frames_per_packet);
-    HRESULT hr;
-
-    PJ_LOG(4,(THIS_FILE, "DirectSound thread starting"));
-
-    /* Allocate buffer for sound data */
-    buffer = malloc(bytes_per_pkt);
-    if (!buffer) {
-	PJ_LOG(1,(THIS_FILE, "Unable to allocate packet buffer!"));
-	return (DWORD)-1;
-    }
-
-    desc->thread = pj_thread_register ("dsound", desc->thread_desc);
-    if (desc->thread == NULL)
-	return (DWORD)-1;
-
-    /*
-     * Start playing or recording!
-     */
-    if (desc->type == DSOUND_TYPE_PLAYER) {
-	hr = IDirectSoundBuffer_SetCurrentPosition( desc->lpDsPlayBuffer, 0);
-	if (FAILED(hr)) {
-	    PJ_LOG(1,(THIS_FILE, "DirectSound play: SetCurrentPosition() error %d", hr));
-	    goto on_error;
-	}
-
-	hr = IDirectSoundBuffer_Play(desc->lpDsPlayBuffer, 0, 0, DSBPLAY_LOOPING);
-	if (FAILED(hr)) {
-	    PJ_LOG(1,(THIS_FILE, "DirectSound: Play() error %d", hr));
-	    goto on_error;
-	}
-    } else {
-	hr = IDirectSoundCaptureBuffer_Start(desc->lpDsCaptureBuffer, DSCBSTART_LOOPING );
-	if (FAILED(hr)) {
-	    PJ_LOG(1,(THIS_FILE, "DirectSound: Record() error %d", hr));
-	    goto on_error;
-	}
-    }
-
-    /*
-     * Reset initial positions.
-     */
-    byte_pos = 0xFFFFFFFF;
-    sample_pos = 0;
-
-    /*
-     * Wait to get the first notification.
-     */
-    if (WaitForSingleObject(desc->hEvent, 100) != WAIT_OBJECT_0) {
-	PJ_LOG(1,(THIS_FILE, "DirectSound: error getting notification"));
-	goto on_error;
-    }
-
-	
-    /* Get initial byte position. */
-    if (desc->type == DSOUND_TYPE_PLAYER) {
-	hr = IDirectSoundBuffer_GetCurrentPosition( desc->lpDsPlayBuffer, 
-						    NULL, &byte_pos );
-	if (FAILED(hr)) {
-	    PJ_LOG(1,(THIS_FILE, "DirectSound: unable to get "
-				 "position, err %d", hr));
-	    goto on_error;
-	}
-    } else {
-	hr = IDirectSoundCaptureBuffer_GetCurrentPosition( desc->lpDsCaptureBuffer, 
-							   NULL, &byte_pos );
-	if (FAILED(hr)) {
-	    PJ_LOG(1,(THIS_FILE, "DirectSound: unable to get "
-				 "position, err %d", hr));
-	    goto on_error;
-	}
-    }
-
-    /* Signal main thread that we're running. */
-    assert( desc->hStartEvent );
-    SetEvent( desc->hStartEvent );
+
+
+    eventCount = 0;
+    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 */
+    SetThreadPriority( GetCurrentThread(), THREAD_PRIORITY_HIGHEST);
 
     /*
@@ -682,196 +409,393 @@
      * application and write it to DirectSound buffer.
      */
-    do {
+    while (!strm->thread_quit_flag) {
 	
-	/* Call callback to get sound data */
-	if (desc->type == DSOUND_TYPE_PLAYER) {
-	    size = bytes_per_pkt;
-	    status = (*dev->play_cb)(dev, sample_pos, buffer, &size);
+	DWORD rc;
+	pjmedia_dir signalled_dir;
+	struct dsound_stream *dsound_strm;
+
+	rc = WaitForMultipleObjects(eventCount, events, FALSE, 100);
+	if (rc < WAIT_OBJECT_0 || rc >= WAIT_OBJECT_0+eventCount)
+	    continue;
+
+
+	if (rc == WAIT_OBJECT_0) {
+	    if (events[0] == strm->play_strm.hEvent)
+		signalled_dir = PJMEDIA_DIR_PLAYBACK;
+	    else
+		signalled_dir = PJMEDIA_DIR_CAPTURE;
+	} else {
+	    if (events[1] == strm->play_strm.hEvent)
+		signalled_dir = PJMEDIA_DIR_PLAYBACK;
+	    else
+		signalled_dir = PJMEDIA_DIR_CAPTURE;
+	}
+
+
+	if (signalled_dir == PJMEDIA_DIR_PLAYBACK) {
+	    
+	    dsound_strm = &strm->play_strm;
+
+	    /* Get frame from application. */
+	    status = (*strm->play_cb)(strm->user_data, 
+				      dsound_strm->timestamp.u32.lo,
+				      strm->buffer,
+				      strm->samples_per_frame * 
+					BYTES_PER_SAMPLE);
+	    if (status != PJ_SUCCESS)
+		break;
+
+	    /* Write to DirectSound buffer. */
+	    AppWriteDataToBuffer( dsound_strm->ds.play.lpDsBuffer, 
+				  dsound_strm->dwBytePos,
+				  (LPBYTE)strm->buffer, 
+				  strm->samples_per_frame * BYTES_PER_SAMPLE);
+
+	} else {
+	    BOOL rc;
+
+	    dsound_strm = &strm->rec_strm;
+
+	    /* Capture from DirectSound buffer. */
+	    rc = AppReadDataFromBuffer(dsound_strm->ds.capture.lpDsBuffer, 
+				       dsound_strm->dwBytePos,
+				       (LPBYTE)strm->buffer, 
+				       strm->samples_per_frame * 
+					BYTES_PER_SAMPLE);
+	    
+	    if (!rc) {
+		pj_memset(strm->buffer, 0, strm->samples_per_frame * 
+					    BYTES_PER_SAMPLE);
+	    }
+
+	    /* Call callback */
+	    status = (*strm->rec_cb)(strm->user_data, 
+				     dsound_strm->timestamp.u32.lo, 
+				     strm->buffer, 
+				     strm->samples_per_frame * 
+					BYTES_PER_SAMPLE);
 
 	    /* Quit thread on error. */
-	    if (status != 0)
+	    if (status != PJ_SUCCESS)
 		break;
 
-	    /* Write zeroes when we've got nothing from application. */
-	    if (size == 0) {
-		memset(buffer, 0, bytes_per_pkt);
-		size = bytes_per_pkt;
-	    }
-
-	    /* Write to DirectSound buffer. */
-	    AppWriteDataToBuffer( desc->lpDsPlayBuffer, byte_pos,
-				  (LPBYTE)buffer, size);
-
-	} else {
-	    /* Capture from DirectSound buffer. */
-	    size = bytes_per_pkt;
-	    if (AppReadDataFromBuffer( desc->lpDsCaptureBuffer, byte_pos,
-				       (LPBYTE)buffer, size)) {
-
-	    } else {
-		memset(buffer, 0, size);
-	    }
-
-	    /* Call callback */
-	    status = (*dev->rec_cb)(dev, sample_pos, buffer, size);
-
-	    /* Quit thread on error. */
-	    if (status != 0)
-		break;
 	}
 
 	/* Increment position. */
-	byte_pos += size;
-	if (byte_pos >= buffer_size)
-	    byte_pos -= buffer_size;
-	sample_pos += samples_per_pkt;
-
-	while (WaitForSingleObject(desc->hEvent, 500) != WAIT_OBJECT_0 &&
-	       (!desc->dwThreadQuitFlag)) 
-	{
-	    Sleep(1);
+	dsound_strm->dwBytePos += strm->samples_per_frame * BYTES_PER_SAMPLE;
+	if (dsound_strm->dwBytePos >= dsound_strm->dwDsBufferSize)
+	    dsound_strm->dwBytePos -= dsound_strm->dwDsBufferSize;
+	dsound_strm->timestamp.u64 += strm->samples_per_frame;
+    }
+
+
+    PJ_LOG(5,(THIS_FILE, "DirectSound: thread stopping.."));
+
+    return 0;
+}
+
+
+
+/*
+ * Init sound library.
+ */
+PJ_DEF(pj_status_t) pjmedia_snd_init(pj_pool_factory *factory)
+{
+    pool_factory = factory;
+    return PJ_SUCCESS;
+}
+
+/*
+ * Deinitialize sound library.
+ */
+PJ_DEF(pj_status_t) pjmedia_snd_deinit(void)
+{
+    return PJ_SUCCESS;
+}
+
+/*
+ * Get device count.
+ */
+PJ_DEF(int) pjmedia_snd_get_dev_count(void)
+{
+    return 1;
+}
+
+/*
+ * Get device info.
+ */
+PJ_DEF(const pjmedia_snd_dev_info*) pjmedia_snd_get_dev_info(unsigned index)
+{
+    static pjmedia_snd_dev_info info;
+
+    PJ_UNUSED_ARG(index);
+
+    pj_memset(&info, 0, sizeof(info));
+
+    info.default_samples_per_sec = 44100;
+    info.input_count = 1;
+    info.output_count = 1;
+    pj_ansi_strcpy(info.name, "Default DirectSound Device");
+
+    return &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);
+
+    /* Don't support device at present */
+    PJ_UNUSED_ARG(rec_id);
+    PJ_UNUSED_ARG(play_id);
+    
+
+    /* Create and Initialize stream descriptor */
+    pool = pj_pool_create(pool_factory, "dsound-dev", 1000, 1000, NULL);
+    PJ_ASSERT_RETURN(pool != NULL, PJ_ENOMEM);
+
+    strm = pj_pool_zalloc(pool, sizeof(pjmedia_snd_stream));
+    strm->dir = dir;
+    strm->pool = pool;
+    strm->rec_cb = rec_cb;
+    strm->play_cb = play_cb;
+    strm->user_data = user_data;
+
+    strm->samples_per_frame = samples_per_frame;
+    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) {
+	status = init_player_stream( &strm->play_strm, clock_rate,
+				     channel_count, samples_per_frame,
+				     DEFAULT_BUFFER_COUNT );
+	if (status != PJ_SUCCESS) {
+	    pjmedia_snd_stream_close(strm);
+	    return status;
 	}
-    } while (!desc->dwThreadQuitFlag);
-
-
-    PJ_LOG(4,(THIS_FILE, "DirectSound: stopping.."));
-
-    free(buffer);
-    if (desc->type == DSOUND_TYPE_PLAYER) {
-	IDirectSoundBuffer_Stop( desc->lpDsPlayBuffer );
-    } else {
-	IDirectSoundCaptureBuffer_Stop( desc->lpDsCaptureBuffer );
-    }
-    return 0;
-
-on_error:
-    PJ_LOG(4,(THIS_FILE, "DirectSound play stopping"));
-
-    if (buffer) 
-	free(buffer);
-    if (desc->type == DSOUND_TYPE_PLAYER) {
-	IDirectSoundBuffer_Stop( desc->lpDsPlayBuffer );
-    } else {
-	IDirectSoundCaptureBuffer_Stop( desc->lpDsCaptureBuffer );
-    }
-    desc->dwThreadQuitFlag = 1;
-
-    /* Signal main thread that we failed to initialize */
-    assert( desc->hStartEvent );
-    SetEvent( desc->hStartEvent );
-    return -1;
-}
-
-
-/*
- * Generic starter for play/record.
- */
-static pj_status_t dsound_dev_play_record( pj_snd_dev *dev,
-					   Direct_Sound_Descriptor *desc )
-{
-    DWORD threadId;
-    int op_type = desc->type;
-    const char *op_name = (op_type == DSOUND_TYPE_PLAYER) ? "play" : "record";
-    struct Thread_Param param;
-
-    PJ_LOG(4,(THIS_FILE, "DirectSound %s()", op_name));
-
-    /*
-     * Create event for the thread to signal us that it is starting or
-     * quitting during startup.
-     */
-    desc->hStartEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
-    if (desc->hStartEvent == NULL) {
-	PJ_LOG(1,(THIS_FILE, "DirectSound %s: unable to create event", op_name));
-	return -1;
-    }
-
-    param.dev = dev;
-    param.desc = desc;
-
-    /*
-     * Create thread to handle feeding up data to player/recorder.
-     */
-    desc->hThread = NULL;
-    desc->dwThreadQuitFlag = 0;
-    desc->hThread = CreateThread( NULL, 0, &dsound_dev_thread, &param, 
-					    CREATE_SUSPENDED, &threadId);
-    if (!desc->hThread) {
-	PJ_LOG(1,(THIS_FILE, "DirectSound %s(): unable to create thread", op_name));
-	return -1;
-    }
-
-    SetThreadPriority( desc->hThread, THREAD_PRIORITY_HIGHEST);
-
-    /*
-     * Resume thread.
-     */
-    if (ResumeThread(desc->hThread) == (DWORD)-1) {
-	PJ_LOG(1,(THIS_FILE, "DirectSound %s(): unable to resume thread", op_name));
-	goto on_error;
-    }
-
-    /*
-     * Wait until we've got signal from the thread that it has successfully
-     * started, or when it is quitting.
-     */
-    WaitForSingleObject( desc->hStartEvent, INFINITE);
-
-    /* We can destroy the event now. */
-    CloseHandle( desc->hStartEvent );
-    desc->hStartEvent = NULL;
-
-    /* Examine thread status. */
-    if (desc->dwThreadQuitFlag != 0) {
-	/* Thread failed to initialize */
-	WaitForSingleObject(desc->hThread, INFINITE);
-	CloseHandle(desc->hThread);
-	desc->hThread = NULL;
-	return -1;
-    }
-
-    return 0;
-
-on_error:
-    TerminateThread(desc->hThread, -1);
-    CloseHandle(desc->hThread);
-    desc->hThread = NULL;
-    return -1;
-}
-
-/*
- * Start playing.
- */
-static pj_status_t dsound_dev_play( pj_snd_dev *dev )
-{
-    PJ_Direct_Sound_Device *dsDev = dev->device;
-
-    assert(dsDev);
-    if (!dsDev) {
-	assert(0);
-	return -1;
-    }
-
-    return dsound_dev_play_record( dev, &dsDev->playDesc );
-}
-
-/*
- * Start recording.
- */
-static pj_status_t dsound_dev_record( pj_snd_dev *dev )
-{
-    PJ_Direct_Sound_Device *dsDev = dev->device;
-
-    assert(dsDev);
-    if (!dsDev) {
-	assert(0);
-	return -1;
-    }
-
-    return dsound_dev_play_record( dev, &dsDev->recDesc );
-}
-
-#ifdef _MSC_VER
-# pragma warning(pop)
-# pragma warning(disable: 4514)	// unreferenced inline function has been removed
-#endif
+    }
+
+    /* Create capture stream */
+    if (dir & PJMEDIA_DIR_CAPTURE) {
+	status = init_capture_stream( &strm->rec_strm, clock_rate,
+				      channel_count, samples_per_frame,
+				      DEFAULT_BUFFER_COUNT);
+	if (status != PJ_SUCCESS) {
+	    pjmedia_snd_stream_close(strm);
+	    return status;
+	}
+    }
+
+
+    /* Create and start the thread */
+    status = pj_thread_create(pool, "dsound", &dsound_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 );
+}
+
+/*
+ * Start stream.
+ */
+PJ_DEF(pj_status_t) pjmedia_snd_stream_start(pjmedia_snd_stream *stream)
+{
+    HRESULT hr;
+
+    PJ_UNUSED_ARG(stream);
+
+    if (stream->play_strm.ds.play.lpDsBuffer) {
+	hr = IDirectSoundBuffer_Play(stream->play_strm.ds.play.lpDsBuffer, 
+				     0, 0, DSBPLAY_LOOPING);
+	if (FAILED(hr))
+	    return PJ_RETURN_OS_ERROR(hr);
+    }
+    
+    if (stream->rec_strm.ds.capture.lpDsBuffer) {
+	hr = IDirectSoundCaptureBuffer_Start(stream->rec_strm.ds.capture.lpDsBuffer,
+					     DSCBSTART_LOOPING );
+	if (FAILED(hr))
+	    return PJ_RETURN_OS_ERROR(hr);
+    }
+
+    return PJ_SUCCESS;
+}
+
+/*
+ * Stop stream.
+ */
+PJ_DEF(pj_status_t) pjmedia_snd_stream_stop(pjmedia_snd_stream *stream)
+{
+    PJ_ASSERT_RETURN(stream != NULL, PJ_EINVAL);
+
+    if (stream->play_strm.ds.play.lpDsBuffer) {
+	PJ_LOG(5,(THIS_FILE, "Stopping DirectSound playback stream"));
+	IDirectSoundBuffer_Stop( stream->play_strm.ds.play.lpDsBuffer );
+    }
+
+    if (stream->rec_strm.ds.capture.lpDsBuffer) {
+	PJ_LOG(5,(THIS_FILE, "Stopping DirectSound capture stream"));
+	IDirectSoundCaptureBuffer_Stop(stream->rec_strm.ds.capture.lpDsBuffer);
+    }
+
+    return PJ_SUCCESS;
+}
+
+
+/*
+ * Destroy stream.
+ */
+PJ_DEF(pj_status_t) pjmedia_snd_stream_close(pjmedia_snd_stream *stream)
+{
+    PJ_ASSERT_RETURN(stream != NULL, PJ_EINVAL);
+
+    pjmedia_snd_stream_stop(stream);
+
+    if (stream->play_strm.lpDsNotify) {
+	IDirectSoundNotify_Release( stream->play_strm.lpDsNotify );
+	stream->play_strm.lpDsNotify = NULL;
+    }
+    
+    if (stream->play_strm.hEvent) {
+	CloseHandle(stream->play_strm.hEvent);
+	stream->play_strm.hEvent = NULL;
+    }
+
+    if (stream->play_strm.ds.play.lpDsBuffer) {
+	IDirectSoundBuffer_Release( stream->play_strm.ds.play.lpDsBuffer );
+	stream->play_strm.ds.play.lpDsBuffer = NULL;
+    }
+
+    if (stream->play_strm.ds.play.lpDs) {
+	IDirectSound_Release( stream->play_strm.ds.play.lpDs );
+	stream->play_strm.ds.play.lpDs = NULL;
+    }
+
+    if (stream->rec_strm.lpDsNotify) {
+	IDirectSoundNotify_Release( stream->rec_strm.lpDsNotify );
+	stream->rec_strm.lpDsNotify = NULL;
+    }
+    
+    if (stream->rec_strm.hEvent) {
+	CloseHandle(stream->rec_strm.hEvent);
+	stream->rec_strm.hEvent = NULL;
+    }
+
+    if (stream->rec_strm.ds.capture.lpDsBuffer) {
+	IDirectSoundCaptureBuffer_Release( stream->rec_strm.ds.capture.lpDsBuffer );
+	stream->rec_strm.ds.capture.lpDsBuffer = NULL;
+    }
+
+    if (stream->rec_strm.ds.capture.lpDs) {
+	IDirectSoundCapture_Release( stream->rec_strm.ds.capture.lpDs );
+	stream->rec_strm.ds.capture.lpDs = NULL;
+    }
+
+    if (stream->thread) {
+	stream->thread_quit_flag = 1;
+	pj_thread_join(stream->thread);
+	pj_thread_destroy(stream->thread);
+	stream->thread = NULL;
+    }
+
+    pj_pool_release(stream->pool);
+
+    return PJ_SUCCESS;
+}
+
+
+#endif	/* PJMEDIA_SOUND_IMPLEMENTATION */
+
Index: /pjproject/trunk/pjmedia/src/pjmedia/errno.c
===================================================================
--- /pjproject/trunk/pjmedia/src/pjmedia/errno.c	(revision 370)
+++ /pjproject/trunk/pjmedia/src/pjmedia/errno.c	(revision 371)
@@ -148,5 +148,5 @@
 
     /* See if the error comes from PortAudio. */
-#if defined(PJMEDIA_HAS_PORTAUDIO_SOUND) && PJMEDIA_HAS_PORTAUDIO_SOUND!=0
+#if PJMEDIA_SOUND_IMPLEMENTATION==PJMEDIA_SOUND_PORTAUDIO_SOUND
     if (statcode >= PJMEDIA_ERRNO_FROM_PORTAUDIO(paNotInitialized) &&
 	statcode <  PJMEDIA_ERRNO_FROM_PORTAUDIO(paNotInitialized + 10000))
@@ -164,5 +164,5 @@
 
     } else 
-#endif	/* PJMEDIA_HAS_PORTAUDIO_SOUND */
+#endif	/* PJMEDIA_SOUND_IMPLEMENTATION */
     if (statcode >= PJMEDIA_ERRNO_START && 
 	       statcode < PJMEDIA_ERRNO_START + PJ_ERRNO_SPACE_SIZE)
Index: /pjproject/trunk/pjmedia/src/pjmedia/nullsound.c
===================================================================
--- /pjproject/trunk/pjmedia/src/pjmedia/nullsound.c	(revision 370)
+++ /pjproject/trunk/pjmedia/src/pjmedia/nullsound.c	(revision 371)
@@ -20,5 +20,5 @@
 #include <pj/assert.h>
 
-#if defined(PJMEDIA_HAS_NULL_SOUND) && PJMEDIA_HAS_NULL_SOUND!=0
+#if PJMEDIA_SOUND_IMPLEMENTATION==PJMEDIA_SOUND_NULL_SOUND
 
 static pjmedia_snd_dev_info null_info = 
@@ -143,3 +143,3 @@
 
 
-#endif	/* PJMEDIA_HAS_NULL_SOUND */
+#endif	/* PJMEDIA_SOUND_IMPLEMENTATION */
Index: /pjproject/trunk/pjmedia/src/pjmedia/pasound.c
===================================================================
--- /pjproject/trunk/pjmedia/src/pjmedia/pasound.c	(revision 370)
+++ /pjproject/trunk/pjmedia/src/pjmedia/pasound.c	(revision 371)
@@ -24,5 +24,5 @@
 #include <portaudio.h>
 
-#if defined(PJMEDIA_HAS_PORTAUDIO_SOUND) && PJMEDIA_HAS_PORTAUDIO_SOUND!=0
+#if PJMEDIA_SOUND_IMPLEMENTATION==PJMEDIA_SOUND_PORTAUDIO_SOUND
 
 #define THIS_FILE	"pasound.c"
@@ -281,5 +281,5 @@
     stream->dir = PJMEDIA_DIR_CAPTURE;
     stream->user_data = user_data;
-    stream->samples_per_sec = samples_per_frame;
+    stream->samples_per_sec = clock_rate;
     stream->bytes_per_sample = bits_per_sample / 8;
     stream->channel_count = channel_count;
@@ -374,5 +374,5 @@
     stream->dir = stream->dir = PJMEDIA_DIR_PLAYBACK;
     stream->user_data = user_data;
-    stream->samples_per_sec = samples_per_frame;
+    stream->samples_per_sec = clock_rate;
     stream->bytes_per_sample = bits_per_sample / 8;
     stream->channel_count = channel_count;
@@ -384,5 +384,5 @@
     outputParam.hostApiSpecificStreamInfo = NULL;
     outputParam.sampleFormat = sampleFormat;
-    outputParam.suggestedLatency = paDevInfo->defaultLowInputLatency;
+    outputParam.suggestedLatency = 1.0 * samples_per_frame / clock_rate;;
 
     paHostApiInfo = Pa_GetHostApiInfo(paDevInfo->hostApi);
@@ -399,9 +399,9 @@
     }
 
-    PJ_LOG(5,(THIS_FILE, "%s opening device %s(%s) for playing, sample rate=%d"
+    PJ_LOG(5,(THIS_FILE, "%s opening device %d: %s(%s) for playing, sample rate=%d"
 			 ", ch=%d, "
 			 "bits=%d, %d samples per frame",
 			 (err==0 ? "Success" : "Error"),
-			 paDevInfo->name, paHostApiInfo->name, 
+			 index, paDevInfo->name, paHostApiInfo->name, 
 			 clock_rate, channel_count,
 		 	 bits_per_sample, samples_per_frame));
@@ -495,5 +495,5 @@
     stream->dir = PJMEDIA_DIR_CAPTURE_PLAYBACK;
     stream->user_data = user_data;
-    stream->samples_per_sec = samples_per_frame;
+    stream->samples_per_sec = clock_rate;
     stream->bytes_per_sample = bits_per_sample / 8;
     stream->channel_count = channel_count;
@@ -621,3 +621,3 @@
 
 
-#endif	/* PJMEDIA_HAS_PORTAUDIO_SOUND */
+#endif	/* PJMEDIA_SOUND_IMPLEMENTATION */
Index: /pjproject/trunk/pjmedia/src/pjmedia/portaudio/pa_win_hostapis.c
===================================================================
--- /pjproject/trunk/pjmedia/src/pjmedia/portaudio/pa_win_hostapis.c	(revision 370)
+++ /pjproject/trunk/pjmedia/src/pjmedia/portaudio/pa_win_hostapis.c	(revision 371)
@@ -59,10 +59,10 @@
     {
 
+#ifndef PA_NO_DS
+        PaWinDs_Initialize,
+#endif
+
 #ifndef PA_NO_WMME
         PaWinMme_Initialize,
-#endif
-
-#ifndef PA_NO_DS
-        PaWinDs_Initialize,
 #endif
 
Index: /pjproject/trunk/pjsip-apps/build/sample_debug.dsp
===================================================================
--- /pjproject/trunk/pjsip-apps/build/sample_debug.dsp	(revision 370)
+++ /pjproject/trunk/pjsip-apps/build/sample_debug.dsp	(revision 371)
@@ -51,5 +51,5 @@
 LINK32=link.exe
 # ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /machine:I386
-# ADD LINK32 ole32.lib user32.lib dsound.lib dxguid.lib netapi32.lib mswsock.lib ws2_32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /machine:I386 /out:"../bin/samples/sampledebug_vc6.exe"
+# ADD LINK32 ole32.lib user32.lib dsound.lib dxguid.lib netapi32.lib mswsock.lib ws2_32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /map /machine:I386 /out:"../bin/samples/sampledebug_vc6.exe"
 
 !ELSEIF  "$(CFG)" == "sample_debug - Win32 Debug"
Index: /pjproject/trunk/pjsip-apps/src/samples/debug.c
===================================================================
--- /pjproject/trunk/pjsip-apps/src/samples/debug.c	(revision 370)
+++ /pjproject/trunk/pjsip-apps/src/samples/debug.c	(revision 371)
@@ -28,4 +28,4 @@
  *  #include "playfile.c"
  */
-#include "resampleplay.c"
+#include "playfile.c"
 
Index: /pjproject/trunk/pjsip-apps/src/samples/sndinfo.c
===================================================================
--- /pjproject/trunk/pjsip-apps/src/samples/sndinfo.c	(revision 370)
+++ /pjproject/trunk/pjsip-apps/src/samples/sndinfo.c	(revision 371)
@@ -76,9 +76,35 @@
 static int play_counter;
 static int rec_counter;
+static int min_delay = 0xFFFF, max_delay;
+static char play_delays[1000];
 
 static pj_status_t play_cb(void *user_data, pj_uint32_t timestamp,
 			   void *output, unsigned size)
 {
+    static pj_time_val last_cb;
+
     ++play_counter;
+
+    if (last_cb.sec == 0 && last_cb.msec == 0) {
+	pj_gettimeofday(&last_cb);
+    } else {
+	pj_time_val now, saved;
+	int delay;
+
+	pj_gettimeofday(&now);
+	saved = now;
+	PJ_TIME_VAL_SUB(now, last_cb);
+	delay = PJ_TIME_VAL_MSEC(now);
+
+	if (delay < min_delay)
+	    min_delay = delay;
+	if (delay > max_delay)
+	    max_delay = delay;
+
+	last_cb = saved;
+
+	play_delays[play_counter-1] = delay;
+    }
+
     return PJ_SUCCESS;
 }
@@ -99,5 +125,5 @@
 	    title, errmsg, status);
 }
-		
+
 static int open_device(int dev_id, pjmedia_dir dir,
 		       int clock_rate, int nchannel, int bits)
@@ -107,4 +133,5 @@
     pjmedia_snd_stream *strm;
     const char *dirtype;
+    unsigned i;
 
     switch (dir) {
@@ -166,4 +193,16 @@
 
     puts("Success.");
+
+    printf("Delay: ");
+    for (i=0; i<play_counter; ++i)
+	printf("%d ", play_delays[i]);
+
+    puts("");
+    if (dir & PJMEDIA_DIR_PLAYBACK) {
+	printf("Callback interval: min interval=%d ms, max interval=%d ms\n",
+	       min_delay, max_delay);
+    }
+    
+
     return 0;
 }
