diff options
Diffstat (limited to 'pjmedia/src/pjmedia/dsound.c')
-rw-r--r-- | pjmedia/src/pjmedia/dsound.c | 1113 |
1 files changed, 0 insertions, 1113 deletions
diff --git a/pjmedia/src/pjmedia/dsound.c b/pjmedia/src/pjmedia/dsound.c deleted file mode 100644 index 367a6974..00000000 --- a/pjmedia/src/pjmedia/dsound.c +++ /dev/null @@ -1,1113 +0,0 @@ -/* $Id$ */ -/* - * Copyright (C) 2008-2009 Teluu Inc. (http://www.teluu.com) - * Copyright (C) 2003-2008 Benny Prijono <benny@prijono.org> - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - */ -#include <pjmedia/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 - -#define PJ_MIN(x, y) ((x < y) ? (x) : (y)) - -#ifdef _MSC_VER -# pragma warning(push, 3) -#endif - -#include <windows.h> -#include <mmsystem.h> -#include <dsound.h> - -#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 50 -#define MIN_PACKET_BUFFER_COUNT 2 - -#define MAX_HARDWARE 16 - -struct dsound_dev_info -{ - pjmedia_snd_dev_info info; - LPGUID lpGuid; - GUID guid; -}; - -static unsigned dev_count; -static struct dsound_dev_info dev_info[MAX_HARDWARE]; -static int snd_init_count; - -/* Latency settings */ -static unsigned snd_input_latency = PJMEDIA_SND_DEFAULT_REC_LATENCY; -static unsigned snd_output_latency = PJMEDIA_SND_DEFAULT_PLAY_LATENCY; - - -/* 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; - unsigned latency; -}; - - -/* 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 dsound_stream play_strm; /**< Playback stream. */ - struct dsound_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 (PCMWAVEFORMAT *pcmwf, - unsigned clock_rate, - unsigned channel_count) -{ - pj_bzero(pcmwf, sizeof(PCMWAVEFORMAT)); - pcmwf->wf.wFormatTag = WAVE_FORMAT_PCM; - 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 init_player_stream( struct dsound_stream *ds_strm, - int dev_id, - unsigned clock_rate, - unsigned channel_count, - unsigned samples_per_frame, - unsigned buffer_count) -{ - HRESULT hr; - HWND hwnd; - PCMWAVEFORMAT pcmwf; - DSBUFFERDESC dsbdesc; - DSBPOSITIONNOTIFY dsPosNotify[MAX_PACKET_BUFFER_COUNT]; - unsigned bytes_per_frame; - unsigned max_latency; - 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 DirectSound device. - */ - hr = DirectSoundCreate(dev_info[dev_id].lpGuid, &ds_strm->ds.play.lpDs, - NULL); - if (FAILED(hr)) - return PJ_RETURN_OS_ERROR(hr); - - hwnd = GetForegroundWindow(); - if (hwnd == NULL) { - hwnd = GetDesktopWindow(); - } - hr = IDirectSound_SetCooperativeLevel( ds_strm->ds.play.lpDs, hwnd, - DSSCL_PRIORITY); - if FAILED(hr) - return PJ_RETURN_OS_ERROR(hr); - - /* - * 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_bzero(&dsbdesc, sizeof(DSBUFFERDESC)); - dsbdesc.dwSize = sizeof(DSBUFFERDESC); - dsbdesc.dwFlags = DSBCAPS_CTRLVOLUME | DSBCAPS_CTRLPOSITIONNOTIFY | - DSBCAPS_GETCURRENTPOSITION2 | DSBCAPS_GLOBALFOCUS; - - dsbdesc.dwBufferBytes = buffer_count * bytes_per_frame; - dsbdesc.lpwfxFormat = (LPWAVEFORMATEX)&pcmwf; - - /* - * Create DirectSound playback buffer. - */ - hr = IDirectSound_CreateSoundBuffer(ds_strm->ds.play.lpDs, &dsbdesc, - &ds_strm->ds.play.lpDsBuffer, NULL); - if (FAILED(hr) ) - return PJ_RETURN_OS_ERROR(hr); - - /* - * Create event for play notification. - */ - 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( ds_strm->ds.play.lpDsBuffer, - &IID_IDirectSoundNotify, - (LPVOID *)&ds_strm->lpDsNotify); - if (FAILED(hr)) - 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)) - 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; - /* - * Play latency does not need to be on a frame boundry, it is just how far - * ahead of the read pointer we set the write pointer. So we should just - * use the user configured latency. However, if the latency measured in - * bytes causes more buffers than we are allowed, we must cap the latency - * at the time contained in 1-buffer_count. - */ - max_latency = (1 - buffer_count) * samples_per_frame * 1000 / clock_rate / - channel_count; - ds_strm->latency = PJ_MIN(max_latency, snd_output_latency); - - /* Done setting up play device. */ - PJ_LOG(5,(THIS_FILE, - " DirectSound 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 DirectSound recorder device - */ -static pj_status_t init_capture_stream( struct dsound_stream *ds_strm, - int dev_id, - unsigned clock_rate, - unsigned channel_count, - unsigned samples_per_frame, - unsigned buffer_count) -{ - HRESULT hr; - PCMWAVEFORMAT pcmwf; - DSCBUFFERDESC dscbdesc; - DSBPOSITIONNOTIFY dsPosNotify[MAX_PACKET_BUFFER_COUNT]; - 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); - - /* - * Creating recorder device. - */ - hr = DirectSoundCaptureCreate(dev_info[dev_id].lpGuid, - &ds_strm->ds.capture.lpDs, NULL); - if (FAILED(hr)) - 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; - - /* - * Setup capture buffer using sound buffer structure that was passed - * to play buffer creation earlier. - */ - pj_bzero(&dscbdesc, sizeof(DSCBUFFERDESC)); - dscbdesc.dwSize = sizeof(DSCBUFFERDESC); - dscbdesc.dwFlags = DSCBCAPS_WAVEMAPPED ; - dscbdesc.dwBufferBytes = buffer_count * bytes_per_frame; - dscbdesc.lpwfxFormat = (LPWAVEFORMATEX)&pcmwf; - - hr = IDirectSoundCapture_CreateCaptureBuffer( ds_strm->ds.capture.lpDs, - &dscbdesc, - &ds_strm->ds.capture.lpDsBuffer, - NULL); - if (FAILED(hr)) - return PJ_RETURN_OS_ERROR(hr); - - /* - * Create event for play notification. - */ - 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( ds_strm->ds.capture.lpDsBuffer, - &IID_IDirectSoundNotify, - (LPVOID *)&ds_strm->lpDsNotify); - if (FAILED(hr)) - 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)) - 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; - /* - * Capture latency must always be on a frame boundry, - * so compute it based off the calculated buffer_count. - */ - ds_strm->latency = buffer_count * samples_per_frame * 1000 / clock_rate / - channel_count; - - /* Done setting up recorder device. */ - PJ_LOG(5,(THIS_FILE, - " DirectSound capture \"%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; -} - - - -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. - pj_memcpy(lpbSoundData, lpvPtr1, dwBytes1); - if (lpvPtr2 != NULL) - pj_memcpy(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) { - pj_memcpy(lpvPtr1, lpbSoundData, dwBytes1); - if (NULL != lpvPtr2) - pj_memcpy(lpvPtr2, lpbSoundData+dwBytes1, dwBytes2); - - hr = IDirectSoundBuffer_Unlock(lpDsb, lpvPtr1, dwBytes1, lpvPtr2, dwBytes2); - if SUCCEEDED(hr) - return TRUE; - } - - return FALSE; -} - -/* - * Check if there is space in playing buffer. - */ -static unsigned dsound_play_empty_size(struct dsound_stream *dsound_strm) -{ - HRESULT hr; - long size_available; - DWORD writePos, readPos; - - hr = IDirectSoundBuffer_GetCurrentPosition(dsound_strm->ds.play.lpDsBuffer, - &readPos, &writePos); - if FAILED(hr) - return PJ_FALSE; - - if (readPos < dsound_strm->dwBytePos) - size_available = readPos + dsound_strm->dwDsBufferSize - - dsound_strm->dwBytePos; - else - size_available = readPos - dsound_strm->dwBytePos; - - return size_available; -} - - -/* - * Check if there are captured frames in DirectSound capture buffer. - */ -static unsigned dsound_captured_size(struct dsound_stream *dsound_strm) -{ - HRESULT hr; - long size_available; - DWORD writePos, readPos; - - hr = IDirectSoundCaptureBuffer_GetCurrentPosition( - dsound_strm->ds.capture.lpDsBuffer, - &writePos, &readPos); - if FAILED(hr) - return PJ_FALSE; - - if (readPos < dsound_strm->dwBytePos) - size_available = readPos + - (dsound_strm->dwDsBufferSize) - dsound_strm->dwBytePos; - else - size_available = readPos - dsound_strm->dwBytePos; - - return size_available; -} - -/* - * DirectSound capture and playback thread. - */ -static int dsound_dev_thread(void *arg) -{ - pjmedia_snd_stream *strm = arg; - HANDLE events[3]; - unsigned eventCount; - unsigned bytes_per_frame; - pj_status_t status; - - - 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. - */ - SetThreadPriority( GetCurrentThread(), THREAD_PRIORITY_HIGHEST); - - /* 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 DirectSound capture and play buffer. - */ - while (PJ_TRUE) { - - 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 dsound_stream *dsound_strm; - - /* - * DirectSound has requested us to feed some frames to - * playback buffer. - */ - - dsound_strm = &strm->play_strm; - status = PJ_SUCCESS; - - while (dsound_play_empty_size(dsound_strm) > bytes_per_frame) { - /* Get frame from application. */ - status = (*strm->play_cb)(strm->user_data, - dsound_strm->timestamp.u32.lo, - strm->buffer, - bytes_per_frame); - if (status != PJ_SUCCESS) - break; - - /* Write to DirectSound buffer. */ - AppWriteDataToBuffer( dsound_strm->ds.play.lpDsBuffer, - dsound_strm->dwBytePos, - (LPBYTE)strm->buffer, - bytes_per_frame); - - /* Increment position. */ - dsound_strm->dwBytePos += bytes_per_frame; - if (dsound_strm->dwBytePos >= dsound_strm->dwDsBufferSize) - dsound_strm->dwBytePos -= dsound_strm->dwDsBufferSize; - dsound_strm->timestamp.u64 += strm->samples_per_frame; - } - - } else { - /* - * DirectSound has indicated that it has some frames ready - * in the capture buffer. Get as much frames as possible to - * prevent overflows. - */ - struct dsound_stream *dsound_strm; - BOOL rc; - - dsound_strm = &strm->rec_strm; - - /* Fetch while we have more than 1 frame */ - while (dsound_captured_size(dsound_strm) > bytes_per_frame) { - - /* Capture from DirectSound buffer. */ - rc = AppReadDataFromBuffer(dsound_strm->ds.capture.lpDsBuffer, - dsound_strm->dwBytePos, - (LPBYTE)strm->buffer, - bytes_per_frame); - - if (!rc) { - pj_bzero(strm->buffer, bytes_per_frame); - } - - /* Call callback */ - status = (*strm->rec_cb)(strm->user_data, - dsound_strm->timestamp.u32.lo, - strm->buffer, - bytes_per_frame); - - /* Quit thread on error. */ - if (status != PJ_SUCCESS) - goto on_error; - - - /* Increment position. */ - dsound_strm->dwBytePos += bytes_per_frame; - if (dsound_strm->dwBytePos >= dsound_strm->dwDsBufferSize) - dsound_strm->dwBytePos -= dsound_strm->dwDsBufferSize; - dsound_strm->timestamp.u64 += strm->samples_per_frame; - } - } - } - - -on_error: - PJ_LOG(5,(THIS_FILE, "DirectSound: thread stopping..")); - return 0; -} - - -/* DirectSound enum device callback */ -static BOOL CALLBACK DSEnumCallback( LPGUID lpGuid, LPCTSTR lpcstrDescription, - LPCTSTR lpcstrModule, LPVOID lpContext) -{ - unsigned index, max = sizeof(dev_info[index].info.name); - pj_bool_t is_capture_device = (lpContext != NULL); - - - PJ_UNUSED_ARG(lpcstrModule); - - - /* Put the capture and playback of the same devices to the same - * dev_info item, by looking at the GUID. - */ - for (index=0; index<dev_count; ++index) { - if ((dev_info[index].lpGuid==NULL && lpGuid==NULL) || - pj_memcmp(&dev_info[index].guid, lpGuid, sizeof(GUID))==0) - { - break; - } - } - - if (index == dev_count) - ++dev_count; - else if (dev_count >= MAX_HARDWARE) { - pj_assert(!"Too many DirectSound hardware found"); - PJ_LOG(4,(THIS_FILE, "Too many hardware found, some devices will " - "not be listed")); - return FALSE; - } - -#ifdef UNICODE - WideCharToMultiByte(CP_ACP, 0, lpcstrDescription, wcslen(lpcstrDescription), - dev_info[index].info.name, max, NULL, NULL); -#else - strncpy(dev_info[index].info.name, lpcstrDescription, max); -#endif - - dev_info[index].info.name[max-1] = '\0'; - if (lpGuid == NULL) { - dev_info[index].lpGuid = NULL; - } else { - pj_memcpy(&dev_info[index].guid, lpGuid, sizeof(GUID)); - dev_info[index].lpGuid = &dev_info[index].guid; - } - dev_info[index].info.default_samples_per_sec = 44100; - - /* Just assumed that device supports stereo capture/playback */ - if (is_capture_device) - dev_info[index].info.input_count+=2; - else - dev_info[index].info.output_count+=2; - - return TRUE; -} - - -/* - * Init sound library. - */ -PJ_DEF(pj_status_t) pjmedia_snd_init(pj_pool_factory *factory) -{ - HRESULT hr; - unsigned i; - - if (++snd_init_count != 1) - return PJ_SUCCESS; - - pool_factory = factory; - - /* Enumerate sound playback devices */ - hr = DirectSoundEnumerate(&DSEnumCallback, NULL); - if (FAILED(hr)) - return PJ_RETURN_OS_ERROR(hr); - - /* Enumerate sound capture devices */ - hr = DirectSoundCaptureEnumerate(&DSEnumCallback, (void*)1); - if (FAILED(hr)) - return PJ_RETURN_OS_ERROR(hr); - - PJ_LOG(4,(THIS_FILE, "DirectSound initialized, found %d devices:", - dev_count)); - for (i=0; i<dev_count; ++i) { - PJ_LOG(4,(THIS_FILE, " dev_id %d: %s (in=%d, out=%d)", - i, dev_info[i].info.name, - dev_info[i].info.input_count, - dev_info[i].info.output_count)); - } - - return PJ_SUCCESS; -} - -/* - * Deinitialize sound library. - */ -PJ_DEF(pj_status_t) pjmedia_snd_deinit(void) -{ - --snd_init_count; - 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, "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->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 event for stopping the worker thread. - */ - strm->thread_quit_event = CreateEvent(NULL, FALSE, FALSE, NULL); - if (strm->thread_quit_event == NULL) { - status = pj_get_os_error(); - pj_pool_release(pool); - return status; - } - - /* Create player stream */ - if (dir & PJMEDIA_DIR_PLAYBACK) { - unsigned buffer_count; - - /* Calculate buffer count, in frame unit */ - buffer_count = clock_rate * snd_output_latency / samples_per_frame / - 1000; - /* There must always be one more buffer than required for the latency */ - buffer_count += 1; - if (buffer_count < MIN_PACKET_BUFFER_COUNT) - buffer_count = MIN_PACKET_BUFFER_COUNT; - if (buffer_count > MAX_PACKET_BUFFER_COUNT) - buffer_count = MAX_PACKET_BUFFER_COUNT; - - status = init_player_stream( &strm->play_strm, play_id, clock_rate, - channel_count, samples_per_frame, - buffer_count ); - if (status != PJ_SUCCESS) { - pjmedia_snd_stream_close(strm); - return status; - } - } - - /* Create capture stream */ - if (dir & PJMEDIA_DIR_CAPTURE) { - unsigned buffer_count; - - /* Calculate buffer count, in frame unit */ - buffer_count = clock_rate * snd_input_latency / samples_per_frame / - 1000; - if (buffer_count < MIN_PACKET_BUFFER_COUNT) - buffer_count = MIN_PACKET_BUFFER_COUNT; - if (buffer_count > MAX_PACKET_BUFFER_COUNT) - buffer_count = MAX_PACKET_BUFFER_COUNT; - - status = init_capture_stream( &strm->rec_strm, rec_id, clock_rate, - channel_count, samples_per_frame, - 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 ); -} - -/* - * 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 = strm->rec_strm.latency * strm->clock_rate * - strm->channel_count/ 1000; - pi->play_latency = strm->play_strm.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) -{ - HRESULT hr; - - if (stream->play_strm.ds.play.lpDsBuffer) { - hr = IDirectSoundBuffer_SetCurrentPosition( - stream->play_strm.ds.play.lpDsBuffer, 0); - if (FAILED(hr)) - return PJ_RETURN_OS_ERROR(hr); - - /* Set the write pointer ahead of the read pointer by latency bytes */ - stream->play_strm.dwBytePos = BYTES_PER_SAMPLE * stream->clock_rate * - stream->channel_count * stream->play_strm.latency / 1000; - - hr = IDirectSoundBuffer_Play(stream->play_strm.ds.play.lpDsBuffer, - 0, 0, DSBPLAY_LOOPING); - if (FAILED(hr)) - return PJ_RETURN_OS_ERROR(hr); - PJ_LOG(5,(THIS_FILE, "DirectSound playback stream started")); - } - - if (stream->rec_strm.ds.capture.lpDsBuffer) { - hr = IDirectSoundCaptureBuffer_GetCurrentPosition( - stream->rec_strm.ds.capture.lpDsBuffer, - NULL, &stream->rec_strm.dwBytePos ); - if (FAILED(hr)) - return PJ_RETURN_OS_ERROR(hr); - - hr = IDirectSoundCaptureBuffer_Start( - stream->rec_strm.ds.capture.lpDsBuffer, - DSCBSTART_LOOPING ); - if (FAILED(hr)) - return PJ_RETURN_OS_ERROR(hr); - PJ_LOG(5,(THIS_FILE, "DirectSound capture stream started")); - } - - 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->thread) { - pj_assert(stream->thread_quit_event); - SetEvent(stream->thread_quit_event); - pj_thread_join(stream->thread); - pj_thread_destroy(stream->thread); - stream->thread = NULL; - } - - if (stream->thread_quit_event) { - CloseHandle(stream->thread_quit_event); - stream->thread_quit_event = NULL; - } - - 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; - } - - - 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 */ - |