?? dsound.c
字號(hào):
/* $Id: dsound.c 974 2007-02-19 01:13:53Z 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 16struct dsound_dev_info{ pjmedia_snd_dev_info info; LPGUID lpGuid;};static unsigned dev_count;static struct dsound_dev_info dev_info[MAX_HARDWARE];/* 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;
?? 快捷鍵說明
復(fù)制代碼
Ctrl + C
搜索代碼
Ctrl + F
全屏模式
F11
切換主題
Ctrl + Shift + D
顯示快捷鍵
?
增大字號(hào)
Ctrl + =
減小字號(hào)
Ctrl + -