📄 dsound.c
字号:
/* $Id: dsound.c 1041 2007-03-05 13:13:22Z bennylp $ */
/*
* Copyright (C) 2003-2007 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
#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 PJMEDIA_SOUND_BUFFER_COUNT
#define DEFAULT_BUFFER_COUNT PJMEDIA_SOUND_BUFFER_COUNT
#define MAX_HARDWARE 16
struct dsound_dev_info
{
pjmedia_snd_dev_info info;
LPGUID lpGuid;
};
static unsigned dev_count;
static struct dsound_dev_info dev_info[MAX_HARDWARE];
static int snd_init_count;
/* 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;
};
/* 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. */
pj_bool_t thread_quit_flag; /**< 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 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;
/* 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;
/* 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 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[2];
unsigned eventCount;
unsigned bytes_per_frame;
pj_status_t status;
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. 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 (!strm->thread_quit_flag) {
DWORD rc;
pjmedia_dir signalled_dir;
rc = WaitForMultipleObjects(eventCount, events, FALSE,
100);
if (rc < WAIT_OBJECT_0 || rc >= WAIT_OBJECT_0+eventCount)
continue;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -