summaryrefslogtreecommitdiff
path: root/pjmedia/src/pjmedia/dsound.c
diff options
context:
space:
mode:
Diffstat (limited to 'pjmedia/src/pjmedia/dsound.c')
-rw-r--r--pjmedia/src/pjmedia/dsound.c861
1 files changed, 861 insertions, 0 deletions
diff --git a/pjmedia/src/pjmedia/dsound.c b/pjmedia/src/pjmedia/dsound.c
new file mode 100644
index 00000000..04244717
--- /dev/null
+++ b/pjmedia/src/pjmedia/dsound.c
@@ -0,0 +1,861 @@
+/* $Header: /pjproject/pjmedia/src/pjmedia/dsound.c 6 6/14/05 12:54a Bennylp $ */
+
+#ifdef _MSC_VER
+//# pragma warning(disable: 4201) // non-standard extension: nameless struct/union
+# pragma warning(push, 3)
+#endif
+#include <pj/config.h>
+#include <pj/os.h>
+#include <pj/log.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
+};
+
+static struct pj_snd_dev_op dsound_dev_op =
+{
+ &dsound_dev_open,
+ &dsound_dev_close,
+ &dsound_dev_play,
+ &dsound_dev_record
+};
+
+#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));
+ 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;
+}
+
+/*
+ * Initialize DirectSound player device.
+ */
+static pj_status_t dsound_init_player (pj_snd_dev *dev)
+{
+ HRESULT hr;
+ HWND hwnd;
+ PCMWAVEFORMAT pcmwf;
+ DSBUFFERDESC dsbdesc;
+ DSBPOSITIONNOTIFY dsPosNotify[PACKET_BUFFER_COUNT];
+ 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"));
+
+ /*
+ * Create DirectSound device.
+ */
+ hr = DirectSoundCreate(NULL, &dsDev->playDesc.lpDsPlay, NULL);
+ if (FAILED(hr))
+ goto on_error;
+
+ hwnd = GetForegroundWindow();
+ if (hwnd == NULL) {
+ hwnd = GetDesktopWindow();
+ }
+ hr = IDirectSound_SetCooperativeLevel( dsDev->playDesc.lpDsPlay, hwnd,
+ DSSCL_PRIORITY);
+ if FAILED(hr)
+ goto on_error;
+
+ /*
+ * Create DirectSound play buffer.
+ */
+ // Set up wave format structure.
+ init_waveformatex (&pcmwf, dev);
+
+ // Set up DSBUFFERDESC structure.
+ 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.lpwfxFormat = (LPWAVEFORMATEX)&pcmwf;
+
+ // Create buffer.
+ hr = IDirectSound_CreateSoundBuffer(dsDev->playDesc.lpDsPlay, &dsbdesc,
+ &dsDev->playDesc.lpDsPlayBuffer, NULL);
+ if (FAILED(hr) )
+ goto on_error;
+
+ /*
+ * Create event for play notification.
+ */
+ dsDev->playDesc.hEvent = CreateEvent( NULL, FALSE, FALSE, NULL);
+ if (dsDev->playDesc.hEvent == NULL)
+ goto on_error;
+
+ /*
+ * Setup notification for play.
+ */
+ hr = IDirectSoundBuffer_QueryInterface( dsDev->playDesc.lpDsPlayBuffer,
+ &IID_IDirectSoundNotify,
+ (LPVOID *)&dsDev->playDesc.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,
+ dsPosNotify);
+ if (FAILED(hr))
+ goto on_error;
+
+ /* 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;
+}
+
+/*
+ * Initialize DirectSound recorder device
+ */
+static pj_status_t dsound_init_recorder (pj_snd_dev *dev)
+{
+ HRESULT hr;
+ PCMWAVEFORMAT pcmwf;
+ DSCBUFFERDESC dscbdesc;
+ DSBPOSITIONNOTIFY dsPosNotify[PACKET_BUFFER_COUNT];
+ 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"));
+
+ /*
+ * Creating recorder device.
+ */
+ hr = DirectSoundCaptureCreate(NULL, &dsDev->recDesc.lpDsCapture, NULL);
+ if (FAILED(hr))
+ goto on_error;
+
+ /* Init wave format */
+ init_waveformatex (&pcmwf, dev);
+
+ /*
+ * Setup capture buffer using sound buffer structure that was passed
+ * to play buffer creation earlier.
+ */
+ 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.lpwfxFormat = (LPWAVEFORMATEX)&pcmwf;
+
+ hr = IDirectSoundCapture_CreateCaptureBuffer( dsDev->recDesc.lpDsCapture,
+ &dscbdesc,
+ &dsDev->recDesc.lpDsCaptureBuffer,
+ NULL);
+ if (FAILED(hr))
+ goto on_error;
+
+ /*
+ * Create event for play notification.
+ */
+ dsDev->recDesc.hEvent = CreateEvent( NULL, FALSE, FALSE, NULL);
+ if (dsDev->recDesc.hEvent == NULL)
+ goto on_error;
+
+ /*
+ * Setup notifications for recording.
+ */
+ hr = IDirectSoundCaptureBuffer_QueryInterface( dsDev->recDesc.lpDsCaptureBuffer,
+ &IID_IDirectSoundNotify,
+ (LPVOID *)&dsDev->recDesc.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,
+ dsPosNotify);
+ if (FAILED(hr))
+ goto on_error;
+
+ /* 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;
+}
+
+static BOOL AppReadDataFromBuffer(LPDIRECTSOUNDCAPTUREBUFFER lpDsb, // The buffer.
+ DWORD dwOffset, // Our own write cursor.
+ LPBYTE lpbSoundData, // Start of our data.
+ DWORD dwSoundBytes) // Size of block to copy.
+{
+ LPVOID lpvPtr1;
+ DWORD dwBytes1;
+ LPVOID lpvPtr2;
+ DWORD dwBytes2;
+ HRESULT hr;
+
+ // Obtain memory address of write block. This will be in two parts
+ // if the block wraps around.
+
+ hr = IDirectSoundCaptureBuffer_Lock( lpDsb, dwOffset, dwSoundBytes, &lpvPtr1,
+ &dwBytes1, &lpvPtr2, &dwBytes2, 0);
+
+ if SUCCEEDED(hr) {
+ // Read from pointers.
+ CopyMemory(lpbSoundData, lpvPtr1, dwBytes1);
+ if (lpvPtr2 != NULL)
+ CopyMemory(lpbSoundData+dwBytes1, lpvPtr2, dwBytes2);
+
+ // Release the data back to DirectSound.
+ hr = IDirectSoundCaptureBuffer_Unlock(lpDsb, lpvPtr1, dwBytes1, lpvPtr2, dwBytes2);
+ if SUCCEEDED(hr)
+ return TRUE;
+ }
+
+ // Lock, Unlock, or Restore failed.
+ return FALSE;
+}
+
+
+static BOOL AppWriteDataToBuffer(LPDIRECTSOUNDBUFFER lpDsb, // The buffer.
+ DWORD dwOffset, // Our own write cursor.
+ LPBYTE lpbSoundData, // Start of our data.
+ DWORD dwSoundBytes) // Size of block to copy.
+{
+ LPVOID lpvPtr1;
+ DWORD dwBytes1;
+ LPVOID lpvPtr2;
+ DWORD dwBytes2;
+ HRESULT hr;
+
+ // Obtain memory address of write block. This will be in two parts
+ // if the block wraps around.
+
+ hr = IDirectSoundBuffer_Lock( lpDsb, dwOffset, dwSoundBytes, &lpvPtr1,
+ &dwBytes1, &lpvPtr2, &dwBytes2, 0);
+
+ // If the buffer was lost, restore and retry lock.
+ if (DSERR_BUFFERLOST == hr) {
+ IDirectSoundBuffer_Restore(lpDsb);
+ hr = IDirectSoundBuffer_Lock( lpDsb, dwOffset, dwSoundBytes,
+ &lpvPtr1, &dwBytes1, &lpvPtr2, &dwBytes2, 0);
+ }
+ if SUCCEEDED(hr) {
+ CopyMemory(lpvPtr1, lpbSoundData, dwBytes1);
+ if (NULL != lpvPtr2)
+ CopyMemory(lpvPtr2, lpbSoundData+dwBytes1, dwBytes2);
+
+ hr = IDirectSoundBuffer_Unlock(lpDsb, lpvPtr1, dwBytes1, lpvPtr2, dwBytes2);
+ if SUCCEEDED(hr)
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+
+/*
+ * 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;
+ 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 );
+
+ /*
+ * Loop while not signalled to quit, wait for event object to be signalled
+ * by DirectSound play buffer, then request for sound data from the
+ * application and write it to DirectSound buffer.
+ */
+ do {
+
+ /* 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);
+
+ /* Quit thread on error. */
+ if (status != 0)
+ 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);
+ }
+ } 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