- Timestamp:
- Apr 3, 2006 9:43:36 AM (19 years ago)
- Location:
- pjproject/trunk
- Files:
-
- 11 edited
Legend:
- Unmodified
- Added
- Removed
-
pjproject/trunk/pjmedia/build/pjmedia.dsp
r358 r371 66 66 # PROP Target_Dir "" 67 67 # ADD BASE CPP /nologo /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_MBCS" /D "_LIB" /YX /FD /GZ /c 68 # 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 /c68 # 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 69 69 # SUBTRACT CPP /YX 70 70 # ADD BASE RSC /l 0x409 /d "_DEBUG" … … 97 97 98 98 SOURCE=..\src\pjmedia\dsound.c 99 # PROP Exclude_From_Build 1100 99 # End Source File 101 100 # Begin Source File … … 134 133 135 134 SOURCE=..\src\pjmedia\pasound.c 135 136 !IF "$(CFG)" == "pjmedia - Win32 Release" 137 138 !ELSEIF "$(CFG)" == "pjmedia - Win32 Debug" 139 140 !ENDIF 141 136 142 # End Source File 137 143 # Begin Source File -
pjproject/trunk/pjmedia/include/pjmedia/config.h
r358 r371 22 22 #include <pj/config.h> 23 23 24 /* *25 * Unless specified otherwise, PortAudio is enabled by default.24 /* 25 * Types of sound stream backends. 26 26 */ 27 # ifndef PJMEDIA_HAS_PORTAUDIO_SOUND28 # define PJMEDIA_HAS_PORTAUDIO_SOUND 129 # endif27 #define PJMEDIA_SOUND_NULL_SOUND 0 28 #define PJMEDIA_SOUND_PORTAUDIO_SOUND 1 29 #define PJMEDIA_SOUND_WIN32_DIRECT_SOUND 2 30 30 31 31 32 32 /** 33 * Unless specified otherwise, Null sound is disabled. 34 * This option is mutually exclusive with PortAudio sound, or otherwise 35 * duplicate symbols error will occur. 33 * Unless specified otherwise, sound device uses PortAudio implementation 34 * by default. 36 35 */ 37 #ifndef PJMEDIA_ HAS_NULL_SOUND38 # define PJMEDIA_ HAS_NULL_SOUND 036 #ifndef PJMEDIA_SOUND_IMPLEMENTATION 37 # define PJMEDIA_SOUND_IMPLEMENTATION PJMEDIA_SOUND_PORTAUDIO_SOUND 39 38 #endif 40 39 -
pjproject/trunk/pjmedia/include/pjmedia/errno.h
r358 r371 20 20 #define __PJMEDIA_ERRNO_H__ 21 21 22 #include <pjmedia/types.h> 22 23 #include <pj/errno.h> 23 24 -
pjproject/trunk/pjmedia/src/pjmedia/dsound.c
r121 r371 17 17 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 18 18 */ 19 #include <pjmedia/sound.h> 20 #include <pjmedia/errno.h> 21 #include <pj/assert.h> 22 #include <pj/log.h> 23 #include <pj/os.h> 24 #include <pj/string.h> 25 26 #if PJMEDIA_SOUND_IMPLEMENTATION == PJMEDIA_SOUND_WIN32_DIRECT_SOUND 27 19 28 #ifdef _MSC_VER 20 //# pragma warning(disable: 4201) // non-standard extension: nameless struct/union 21 # pragma warning(push, 3) 29 # pragma warning(push, 3) 22 30 #endif 23 #include <pj/config.h> 24 #include <pj/os.h> 25 #include <pj/log.h> 26 31 32 #include <windows.h> 33 #include <mmsystem.h> 27 34 #include <dsound.h> 28 #include <stdio.h> 29 #include <assert.h> 30 #include <pjmedia/sound.h> 31 32 #define THIS_FILE "dsound.c" 33 34 /* 35 * Constants 36 */ 37 #define PACKET_BUFFER_COUNT 4 38 39 typedef struct PJ_Direct_Sound_Device PJ_Direct_Sound_Device; 40 41 42 /* 43 * DirectSound Factory Operations 44 */ 45 static pj_status_t dsound_init(void); 46 static const char *dsound_get_name(void); 47 static pj_status_t dsound_destroy(void); 48 static pj_status_t dsound_enum_devices(int *count, char *dev_names[]); 49 static pj_status_t dsound_create_dev(const char *dev_name, pj_snd_dev *dev); 50 static pj_status_t dsound_destroy_dev(pj_snd_dev *dev); 51 52 53 /* 54 * DirectSound Device Operations 55 */ 56 static pj_status_t dsound_dev_open( pj_snd_dev *dev, pj_snd_role_t role ); 57 static pj_status_t dsound_dev_close( pj_snd_dev *dev ); 58 static pj_status_t dsound_dev_play( pj_snd_dev *dev ); 59 static pj_status_t dsound_dev_record( pj_snd_dev *dev ); 60 61 /* 62 * Utils. 63 */ 64 static pj_status_t dsound_destroy_dsound_dev( PJ_Direct_Sound_Device *dsDev ); 65 66 67 static pj_snd_dev_factory dsound_factory = 68 { 69 &dsound_init, 70 &dsound_get_name, 71 &dsound_destroy, 72 &dsound_enum_devices, 73 &dsound_create_dev, 74 &dsound_destroy_dev 35 36 #ifdef _MSC_VER 37 # pragma warning(pop) 38 #endif 39 40 41 42 #define THIS_FILE "dsound.c" 43 #define BITS_PER_SAMPLE 16 44 #define BYTES_PER_SAMPLE (BITS_PER_SAMPLE/8) 45 46 #define MAX_PACKET_BUFFER_COUNT 32 47 #define DEFAULT_BUFFER_COUNT 5 48 49 50 51 /* Individual DirectSound capture/playback stream descriptor */ 52 struct dsound_stream 53 { 54 union 55 { 56 struct 57 { 58 LPDIRECTSOUND lpDs; 59 LPDIRECTSOUNDBUFFER lpDsBuffer; 60 } play; 61 62 struct 63 { 64 LPDIRECTSOUNDCAPTURE lpDs; 65 LPDIRECTSOUNDCAPTUREBUFFER lpDsBuffer; 66 } capture; 67 } ds; 68 69 HANDLE hEvent; 70 LPDIRECTSOUNDNOTIFY lpDsNotify; 71 DWORD dwBytePos; 72 DWORD dwDsBufferSize; 73 pj_timestamp timestamp; 75 74 }; 76 75 77 static struct pj_snd_dev_op dsound_dev_op = 78 { 79 &dsound_dev_open, 80 &dsound_dev_close, 81 &dsound_dev_play, 82 &dsound_dev_record 76 77 /* Sound stream. */ 78 struct pjmedia_snd_stream 79 { 80 pjmedia_dir dir; /**< Sound direction. */ 81 pj_pool_t *pool; /**< Memory pool. */ 82 83 pjmedia_snd_rec_cb rec_cb; /**< Capture callback. */ 84 pjmedia_snd_play_cb play_cb; /**< Playback callback. */ 85 void *user_data; /**< Application data. */ 86 87 struct dsound_stream play_strm; /**< Playback stream. */ 88 struct dsound_stream rec_strm; /**< Capture stream. */ 89 90 void *buffer; /**< Temp. frame buffer. */ 91 unsigned samples_per_frame; /**< Samples per frame. */ 92 93 pj_thread_t *thread; /**< Thread handle. */ 94 pj_bool_t thread_quit_flag; /**< Quit signal to thread */ 83 95 }; 84 96 85 #define DSOUND_TYPE_PLAYER 1 86 #define DSOUND_TYPE_RECORDER 2 87 88 typedef struct Direct_Sound_Descriptor 89 { 90 int type; 91 92 LPDIRECTSOUND lpDsPlay; 93 LPDIRECTSOUNDBUFFER lpDsPlayBuffer; 94 95 LPDIRECTSOUNDCAPTURE lpDsCapture; 96 LPDIRECTSOUNDCAPTUREBUFFER lpDsCaptureBuffer; 97 98 LPDIRECTSOUNDNOTIFY lpDsNotify; 99 HANDLE hEvent; 100 HANDLE hThread; 101 HANDLE hStartEvent; 102 DWORD dwThreadQuitFlag; 103 pj_thread_desc thread_desc; 104 pj_thread_t *thread; 105 } Direct_Sound_Descriptor; 106 107 struct PJ_Direct_Sound_Device 108 { 109 Direct_Sound_Descriptor playDesc; 110 Direct_Sound_Descriptor recDesc; 111 }; 112 113 struct Thread_Param 114 { 115 pj_snd_dev *dev; 116 Direct_Sound_Descriptor *desc; 117 }; 118 119 PJ_DEF(pj_snd_dev_factory*) pj_dsound_get_factory() 120 { 121 return &dsound_factory; 122 } 123 124 /* 125 * Init DirectSound. 126 */ 127 static pj_status_t dsound_init(void) 128 { 129 /* Nothing to do. */ 130 return 0; 131 } 132 133 /* 134 * Get the name of the factory. 135 */ 136 static const char *dsound_get_name(void) 137 { 138 return "DirectSound"; 139 } 140 141 /* 142 * Destroy DirectSound. 143 */ 144 static pj_status_t dsound_destroy(void) 145 { 146 /* TODO: clean up devices in case application haven't done it. */ 147 return 0; 148 } 149 150 /* 151 * Enum devices in the system. 152 */ 153 static pj_status_t dsound_enum_devices(int *count, char *dev_names[]) 154 { 155 dev_names[0] = "DirectSound Default Device"; 156 *count = 1; 157 return 0; 158 } 159 160 /* 161 * Create DirectSound device. 162 */ 163 static pj_status_t dsound_create_dev(const char *dev_name, pj_snd_dev *dev) 164 { 165 PJ_Direct_Sound_Device *dsDev; 166 167 /* TODO: create based on the name. */ 168 PJ_TODO(DSOUND_CREATE_DEVICE_BY_NAME); 169 170 /* Create DirectSound structure. */ 171 dsDev = malloc(sizeof(*dsDev)); 172 if (!dsDev) { 173 PJ_LOG(1,(THIS_FILE, "No memory to allocate device!")); 174 return -1; 175 } 176 memset(dsDev, 0, sizeof(*dsDev)); 177 178 /* Associate DirectSound device with device. */ 179 dev->device = dsDev; 180 dev->op = &dsound_dev_op; 181 182 return 0; 183 } 184 185 /* 186 * Destroy DirectSound device. 187 */ 188 static pj_status_t dsound_destroy_dev( pj_snd_dev *dev ) 189 { 190 if (dev->device) { 191 free(dev->device); 192 dev->device = NULL; 193 } 194 return 0; 195 } 196 197 static void dsound_release_descriptor(Direct_Sound_Descriptor *desc) 198 { 199 if (desc->lpDsNotify) 200 IDirectSoundNotify_Release( desc->lpDsNotify ); 201 202 if (desc->hEvent) 203 CloseHandle(desc->hEvent); 204 205 if (desc->lpDsPlayBuffer) 206 IDirectSoundBuffer_Release( desc->lpDsPlayBuffer ); 207 208 if (desc->lpDsPlay) 209 IDirectSound_Release( desc->lpDsPlay ); 210 211 if (desc->lpDsCaptureBuffer) 212 IDirectSoundCaptureBuffer_Release(desc->lpDsCaptureBuffer); 213 214 if (desc->lpDsCapture) 215 IDirectSoundCapture_Release(desc->lpDsCapture); 216 } 217 218 /* 219 * Destroy DirectSound resources. 220 */ 221 static pj_status_t dsound_destroy_dsound_dev( PJ_Direct_Sound_Device *dsDev ) 222 { 223 dsound_release_descriptor( &dsDev->playDesc ); 224 dsound_release_descriptor( &dsDev->recDesc ); 225 memset(dsDev, 0, sizeof(*dsDev)); 226 return 0; 227 } 228 229 static void init_waveformatex (PCMWAVEFORMAT *pcmwf, pj_snd_dev *dev) 230 { 231 memset(pcmwf, 0, sizeof(PCMWAVEFORMAT)); 97 98 static pj_pool_factory *pool_factory; 99 100 101 static void init_waveformatex (PCMWAVEFORMAT *pcmwf, 102 unsigned clock_rate, 103 unsigned channel_count) 104 { 105 pj_memset(pcmwf, 0, sizeof(PCMWAVEFORMAT)); 232 106 pcmwf->wf.wFormatTag = WAVE_FORMAT_PCM; 233 pcmwf->wf.nChannels = 1;234 pcmwf->wf.nSamplesPerSec = dev->param.samples_per_sec;235 pcmwf->wf.nBlockAlign = dev->param.bytes_per_frame;236 pcmwf->wf.nAvgBytesPerSec = 237 dev->param.samples_per_sec * dev->param.bytes_per_frame;238 pcmwf->wBitsPerSample = dev->param.bits_per_sample; 239 } 107 pcmwf->wf.nChannels = (pj_uint16_t)channel_count; 108 pcmwf->wf.nSamplesPerSec = clock_rate; 109 pcmwf->wf.nBlockAlign = (pj_uint16_t)(channel_count * BYTES_PER_SAMPLE); 110 pcmwf->wf.nAvgBytesPerSec = clock_rate * channel_count * BYTES_PER_SAMPLE; 111 pcmwf->wBitsPerSample = BITS_PER_SAMPLE; 112 } 113 240 114 241 115 /* 242 116 * Initialize DirectSound player device. 243 117 */ 244 static pj_status_t dsound_init_player (pj_snd_dev *dev) 118 static pj_status_t init_player_stream( struct dsound_stream *ds_strm, 119 unsigned clock_rate, 120 unsigned channel_count, 121 unsigned samples_per_frame, 122 unsigned buffer_count) 245 123 { 246 124 HRESULT hr; … … 248 126 PCMWAVEFORMAT pcmwf; 249 127 DSBUFFERDESC dsbdesc; 250 DSBPOSITIONNOTIFY dsPosNotify[PACKET_BUFFER_COUNT]; 128 DSBPOSITIONNOTIFY dsPosNotify[MAX_PACKET_BUFFER_COUNT]; 129 unsigned bytes_per_frame; 251 130 unsigned i; 252 PJ_Direct_Sound_Device *dsDev = dev->device; 253 254 /* 255 * Check parameters. 256 */ 257 if (dev->play_cb == NULL) { 258 assert(0); 259 return -1; 260 } 261 if (dev->device == NULL) { 262 assert(0); 263 return -1; 264 } 265 266 PJ_LOG(4,(THIS_FILE, "Creating DirectSound player device")); 131 132 133 PJ_ASSERT_RETURN(buffer_count <= MAX_PACKET_BUFFER_COUNT, PJ_EINVAL); 267 134 268 135 /* 269 136 * Create DirectSound device. 270 137 */ 271 hr = DirectSoundCreate(NULL, &ds Dev->playDesc.lpDsPlay, NULL);138 hr = DirectSoundCreate(NULL, &ds_strm->ds.play.lpDs, NULL); 272 139 if (FAILED(hr)) 273 goto on_error;140 return PJ_RETURN_OS_ERROR(hr); 274 141 275 142 hwnd = GetForegroundWindow(); … … 277 144 hwnd = GetDesktopWindow(); 278 145 } 279 hr = IDirectSound_SetCooperativeLevel( ds Dev->playDesc.lpDsPlay, hwnd,146 hr = IDirectSound_SetCooperativeLevel( ds_strm->ds.play.lpDs, hwnd, 280 147 DSSCL_PRIORITY); 281 148 if FAILED(hr) 282 goto on_error;149 return PJ_RETURN_OS_ERROR(hr); 283 150 284 151 /* 285 * Create DirectSound play buffer. 286 */ 287 // Set up wave format structure. 288 init_waveformatex (&pcmwf, dev); 289 290 // Set up DSBUFFERDESC structure. 291 memset(&dsbdesc, 0, sizeof(DSBUFFERDESC)); 152 * Set up wave format structure for initialize DirectSound play 153 * buffer. 154 */ 155 init_waveformatex(&pcmwf, clock_rate, channel_count); 156 bytes_per_frame = samples_per_frame * BYTES_PER_SAMPLE; 157 158 /* Set up DSBUFFERDESC structure. */ 159 pj_memset(&dsbdesc, 0, sizeof(DSBUFFERDESC)); 292 160 dsbdesc.dwSize = sizeof(DSBUFFERDESC); 293 dsbdesc.dwFlags = DSBCAPS_CTRLVOLUME | DSBCAPS_CTRLPOSITIONNOTIFY | 294 DSBCAPS_GETCURRENTPOSITION2; 295 296 dsbdesc.dwBufferBytes = 297 (PACKET_BUFFER_COUNT * dev->param.bytes_per_frame * 298 dev->param.frames_per_packet); 161 dsbdesc.dwFlags = DSBCAPS_CTRLVOLUME | DSBCAPS_CTRLPOSITIONNOTIFY; 162 /* DSBCAPS_GETCURRENTPOSITION2 */ 163 164 dsbdesc.dwBufferBytes = buffer_count * bytes_per_frame; 299 165 dsbdesc.lpwfxFormat = (LPWAVEFORMATEX)&pcmwf; 300 166 301 // Create buffer. 302 hr = IDirectSound_CreateSoundBuffer(dsDev->playDesc.lpDsPlay, &dsbdesc, 303 &dsDev->playDesc.lpDsPlayBuffer, NULL); 167 /* 168 * Create DirectSound playback buffer. 169 */ 170 hr = IDirectSound_CreateSoundBuffer(ds_strm->ds.play.lpDs, &dsbdesc, 171 &ds_strm->ds.play.lpDsBuffer, NULL); 304 172 if (FAILED(hr) ) 305 goto on_error;173 return PJ_RETURN_OS_ERROR(hr); 306 174 307 175 /* 308 176 * Create event for play notification. 309 177 */ 310 ds Dev->playDesc.hEvent = CreateEvent( NULL, FALSE, FALSE, NULL);311 if (ds Dev->playDesc.hEvent == NULL)312 goto on_error;178 ds_strm->hEvent = CreateEvent( NULL, FALSE, FALSE, NULL); 179 if (ds_strm->hEvent == NULL) 180 return pj_get_os_error(); 313 181 314 182 /* 315 183 * Setup notification for play. 316 184 */ 317 hr = IDirectSoundBuffer_QueryInterface( ds Dev->playDesc.lpDsPlayBuffer,185 hr = IDirectSoundBuffer_QueryInterface( ds_strm->ds.play.lpDsBuffer, 318 186 &IID_IDirectSoundNotify, 319 (LPVOID *)&ds Dev->playDesc.lpDsNotify);187 (LPVOID *)&ds_strm->lpDsNotify); 320 188 if (FAILED(hr)) 321 goto on_error; 322 323 324 for (i=0; i<PACKET_BUFFER_COUNT; ++i) { 325 dsPosNotify[i].dwOffset = i * dev->param.bytes_per_frame * 326 dev->param.frames_per_packet; 327 dsPosNotify[i].hEventNotify = dsDev->playDesc.hEvent; 328 } 329 330 hr = IDirectSoundNotify_SetNotificationPositions( dsDev->playDesc.lpDsNotify, 331 PACKET_BUFFER_COUNT, 189 return PJ_RETURN_OS_ERROR(hr); 190 191 192 for (i=0; i<buffer_count; ++i) { 193 dsPosNotify[i].dwOffset = i * bytes_per_frame; 194 dsPosNotify[i].hEventNotify = ds_strm->hEvent; 195 } 196 197 hr = IDirectSoundNotify_SetNotificationPositions( ds_strm->lpDsNotify, 198 buffer_count, 332 199 dsPosNotify); 333 200 if (FAILED(hr)) 334 goto on_error; 201 return PJ_RETURN_OS_ERROR(hr); 202 203 204 hr = IDirectSoundBuffer_SetCurrentPosition(ds_strm->ds.play.lpDsBuffer, 0); 205 if (FAILED(hr)) 206 return PJ_RETURN_OS_ERROR(hr); 207 208 209 ds_strm->dwBytePos = 0; 210 ds_strm->dwDsBufferSize = buffer_count * bytes_per_frame; 211 ds_strm->timestamp.u64 = 0; 212 335 213 336 214 /* Done setting up play device. */ 337 PJ_LOG(4,(THIS_FILE, "DirectSound player device created")); 338 339 return 0; 340 341 on_error: 342 PJ_LOG(2,(THIS_FILE, "Error creating player device, hresult=0x%x", hr)); 343 dsound_destroy_dsound_dev(dsDev); 344 return -1; 345 } 215 PJ_LOG(5,(THIS_FILE, " DirectSound player stream initialized")); 216 217 return PJ_SUCCESS; 218 } 219 346 220 347 221 /* 348 222 * Initialize DirectSound recorder device 349 223 */ 350 static pj_status_t dsound_init_recorder (pj_snd_dev *dev) 224 static pj_status_t init_capture_stream( struct dsound_stream *ds_strm, 225 unsigned clock_rate, 226 unsigned channel_count, 227 unsigned samples_per_frame, 228 unsigned buffer_count) 351 229 { 352 230 HRESULT hr; 353 231 PCMWAVEFORMAT pcmwf; 354 232 DSCBUFFERDESC dscbdesc; 355 DSBPOSITIONNOTIFY dsPosNotify[PACKET_BUFFER_COUNT]; 233 DSBPOSITIONNOTIFY dsPosNotify[MAX_PACKET_BUFFER_COUNT]; 234 unsigned bytes_per_frame; 356 235 unsigned i; 357 PJ_Direct_Sound_Device *dsDev = dev->device; 358 359 /* 360 * Check parameters. 361 */ 362 if (dev->rec_cb == NULL) { 363 assert(0); 364 return -1; 365 } 366 if (dev->device == NULL) { 367 assert(0); 368 return -1; 369 } 370 371 PJ_LOG(4,(THIS_FILE, "Creating DirectSound recorder device")); 236 237 238 PJ_ASSERT_RETURN(buffer_count <= MAX_PACKET_BUFFER_COUNT, PJ_EINVAL); 239 372 240 373 241 /* 374 242 * Creating recorder device. 375 243 */ 376 hr = DirectSoundCaptureCreate(NULL, &ds Dev->recDesc.lpDsCapture, NULL);244 hr = DirectSoundCaptureCreate(NULL, &ds_strm->ds.capture.lpDs, NULL); 377 245 if (FAILED(hr)) 378 goto on_error; 379 380 /* Init wave format */ 381 init_waveformatex (&pcmwf, dev); 246 return PJ_RETURN_OS_ERROR(hr); 247 248 249 /* Init wave format to initialize buffer */ 250 init_waveformatex( &pcmwf, clock_rate, channel_count); 251 bytes_per_frame = samples_per_frame * BYTES_PER_SAMPLE; 382 252 383 253 /* … … 385 255 * to play buffer creation earlier. 386 256 */ 387 memset(&dscbdesc, 0, sizeof(DSCBUFFERDESC));257 pj_memset(&dscbdesc, 0, sizeof(DSCBUFFERDESC)); 388 258 dscbdesc.dwSize = sizeof(DSCBUFFERDESC); 389 259 dscbdesc.dwFlags = DSCBCAPS_WAVEMAPPED ; 390 dscbdesc.dwBufferBytes = 391 (PACKET_BUFFER_COUNT * dev->param.bytes_per_frame * 392 dev->param.frames_per_packet); 260 dscbdesc.dwBufferBytes = buffer_count * bytes_per_frame; 393 261 dscbdesc.lpwfxFormat = (LPWAVEFORMATEX)&pcmwf; 394 262 395 hr = IDirectSoundCapture_CreateCaptureBuffer( ds Dev->recDesc.lpDsCapture,263 hr = IDirectSoundCapture_CreateCaptureBuffer( ds_strm->ds.capture.lpDs, 396 264 &dscbdesc, 397 &ds Dev->recDesc.lpDsCaptureBuffer,265 &ds_strm->ds.capture.lpDsBuffer, 398 266 NULL); 399 267 if (FAILED(hr)) 400 goto on_error;268 return PJ_RETURN_OS_ERROR(hr); 401 269 402 270 /* 403 271 * Create event for play notification. 404 272 */ 405 ds Dev->recDesc.hEvent = CreateEvent( NULL, FALSE, FALSE, NULL);406 if (ds Dev->recDesc.hEvent == NULL)407 goto on_error;273 ds_strm->hEvent = CreateEvent( NULL, FALSE, FALSE, NULL); 274 if (ds_strm->hEvent == NULL) 275 return pj_get_os_error(); 408 276 409 277 /* 410 278 * Setup notifications for recording. 411 279 */ 412 hr = IDirectSoundCaptureBuffer_QueryInterface( ds Dev->recDesc.lpDsCaptureBuffer,280 hr = IDirectSoundCaptureBuffer_QueryInterface( ds_strm->ds.capture.lpDsBuffer, 413 281 &IID_IDirectSoundNotify, 414 (LPVOID *)&ds Dev->recDesc.lpDsNotify);282 (LPVOID *)&ds_strm->lpDsNotify); 415 283 if (FAILED(hr)) 416 goto on_error; 417 418 419 for (i=0; i<PACKET_BUFFER_COUNT; ++i) { 420 dsPosNotify[i].dwOffset = i * dev->param.bytes_per_frame * 421 dev->param.frames_per_packet; 422 dsPosNotify[i].hEventNotify = dsDev->recDesc.hEvent; 423 } 424 425 hr = IDirectSoundNotify_SetNotificationPositions( dsDev->recDesc.lpDsNotify, 426 PACKET_BUFFER_COUNT, 284 return PJ_RETURN_OS_ERROR(hr); 285 286 287 for (i=0; i<buffer_count; ++i) { 288 dsPosNotify[i].dwOffset = i * bytes_per_frame; 289 dsPosNotify[i].hEventNotify = ds_strm->hEvent; 290 } 291 292 hr = IDirectSoundNotify_SetNotificationPositions( ds_strm->lpDsNotify, 293 buffer_count, 427 294 dsPosNotify); 428 295 if (FAILED(hr)) 429 goto on_error; 296 return PJ_RETURN_OS_ERROR(hr); 297 298 hr = IDirectSoundCaptureBuffer_GetCurrentPosition( ds_strm->ds.capture.lpDsBuffer, 299 NULL, &ds_strm->dwBytePos ); 300 if (FAILED(hr)) 301 return PJ_RETURN_OS_ERROR(hr); 302 303 ds_strm->timestamp.u64 = 0; 304 ds_strm->dwDsBufferSize = buffer_count * bytes_per_frame; 430 305 431 306 /* Done setting up recorder device. */ 432 PJ_LOG(4,(THIS_FILE, "DirectSound recorder device created")); 433 434 return 0; 435 436 on_error: 437 PJ_LOG(4,(THIS_FILE, "Error creating device, hresult=%d", hr)); 438 dsound_destroy_dsound_dev(dsDev); 439 return -1; 440 } 441 442 /* 443 * Initialize DirectSound device. 444 */ 445 static pj_status_t dsound_dev_open( pj_snd_dev *dev, pj_snd_role_t role ) 446 { 447 PJ_Direct_Sound_Device *dsDev = dev->device; 448 pj_status_t status; 449 450 dsDev->playDesc.type = DSOUND_TYPE_PLAYER; 451 dsDev->recDesc.type = DSOUND_TYPE_RECORDER; 452 453 if (role & PJ_SOUND_PLAYER) { 454 status = dsound_init_player (dev); 455 if (status != 0) 456 return status; 457 } 458 459 if (role & PJ_SOUND_RECORDER) { 460 status = dsound_init_recorder (dev); 461 if (status != 0) 462 return status; 463 } 464 465 return 0; 466 } 467 468 /* 469 * Close DirectSound device. 470 */ 471 static pj_status_t dsound_dev_close( pj_snd_dev *dev ) 472 { 473 PJ_LOG(4,(THIS_FILE, "Closing DirectSound device")); 474 475 if (dev->device) { 476 PJ_Direct_Sound_Device *dsDev = dev->device; 477 478 if (dsDev->playDesc.hThread) { 479 PJ_LOG(4,(THIS_FILE, "Stopping DirectSound player")); 480 dsDev->playDesc.dwThreadQuitFlag = 1; 481 SetEvent(dsDev->playDesc.hEvent); 482 if (WaitForSingleObject(dsDev->playDesc.hThread, 1000) != WAIT_OBJECT_0) { 483 PJ_LOG(4,(THIS_FILE, "Timed out waiting player thread to quit")); 484 TerminateThread(dsDev->playDesc.hThread, -1); 485 IDirectSoundBuffer_Stop( dsDev->playDesc.lpDsPlayBuffer ); 486 } 487 488 pj_thread_destroy (dsDev->playDesc.thread); 489 } 490 491 if (dsDev->recDesc.hThread) { 492 PJ_LOG(4,(THIS_FILE, "Stopping DirectSound recorder")); 493 dsDev->recDesc.dwThreadQuitFlag = 1; 494 SetEvent(dsDev->recDesc.hEvent); 495 if (WaitForSingleObject(dsDev->recDesc.hThread, 1000) != WAIT_OBJECT_0) { 496 PJ_LOG(4,(THIS_FILE, "Timed out waiting recorder thread to quit")); 497 TerminateThread(dsDev->recDesc.hThread, -1); 498 IDirectSoundCaptureBuffer_Stop( dsDev->recDesc.lpDsCaptureBuffer ); 499 } 500 501 pj_thread_destroy (dsDev->recDesc.thread); 502 } 503 504 dsound_destroy_dsound_dev( dev->device ); 505 dev->op = NULL; 506 } 507 508 PJ_LOG(4,(THIS_FILE, "DirectSound device closed")); 509 return 0; 510 } 307 PJ_LOG(5,(THIS_FILE, " DirectSound capture stream initialized")); 308 309 return PJ_SUCCESS; 310 } 311 312 511 313 512 314 static BOOL AppReadDataFromBuffer(LPDIRECTSOUNDCAPTUREBUFFER lpDsb, // The buffer. … … 584 386 * Player thread. 585 387 */ 586 static DWORD WINAPI dsound_dev_thread(void *arg) 587 { 588 struct Thread_Param *param = arg; 589 pj_snd_dev *dev = param->dev; 590 Direct_Sound_Descriptor *desc = param->desc; 591 unsigned bytes_per_pkt = dev->param.bytes_per_frame * 592 dev->param.frames_per_packet; 593 unsigned samples_per_pkt = dev->param.samples_per_frame * 594 dev->param.frames_per_packet; 595 void *buffer = NULL; 596 DWORD size; 388 static int dsound_dev_thread(void *arg) 389 { 390 pjmedia_snd_stream *strm = arg; 391 HANDLE events[2]; 392 unsigned eventCount; 597 393 pj_status_t status; 598 DWORD sample_pos; 599 DWORD byte_pos; 600 DWORD buffer_size = 601 (PACKET_BUFFER_COUNT * dev->param.bytes_per_frame * 602 dev->param.frames_per_packet); 603 HRESULT hr; 604 605 PJ_LOG(4,(THIS_FILE, "DirectSound thread starting")); 606 607 /* Allocate buffer for sound data */ 608 buffer = malloc(bytes_per_pkt); 609 if (!buffer) { 610 PJ_LOG(1,(THIS_FILE, "Unable to allocate packet buffer!")); 611 return (DWORD)-1; 612 } 613 614 desc->thread = pj_thread_register ("dsound", desc->thread_desc); 615 if (desc->thread == NULL) 616 return (DWORD)-1; 617 618 /* 619 * Start playing or recording! 620 */ 621 if (desc->type == DSOUND_TYPE_PLAYER) { 622 hr = IDirectSoundBuffer_SetCurrentPosition( desc->lpDsPlayBuffer, 0); 623 if (FAILED(hr)) { 624 PJ_LOG(1,(THIS_FILE, "DirectSound play: SetCurrentPosition() error %d", hr)); 625 goto on_error; 626 } 627 628 hr = IDirectSoundBuffer_Play(desc->lpDsPlayBuffer, 0, 0, DSBPLAY_LOOPING); 629 if (FAILED(hr)) { 630 PJ_LOG(1,(THIS_FILE, "DirectSound: Play() error %d", hr)); 631 goto on_error; 632 } 633 } else { 634 hr = IDirectSoundCaptureBuffer_Start(desc->lpDsCaptureBuffer, DSCBSTART_LOOPING ); 635 if (FAILED(hr)) { 636 PJ_LOG(1,(THIS_FILE, "DirectSound: Record() error %d", hr)); 637 goto on_error; 638 } 639 } 640 641 /* 642 * Reset initial positions. 643 */ 644 byte_pos = 0xFFFFFFFF; 645 sample_pos = 0; 646 647 /* 648 * Wait to get the first notification. 649 */ 650 if (WaitForSingleObject(desc->hEvent, 100) != WAIT_OBJECT_0) { 651 PJ_LOG(1,(THIS_FILE, "DirectSound: error getting notification")); 652 goto on_error; 653 } 654 655 656 /* Get initial byte position. */ 657 if (desc->type == DSOUND_TYPE_PLAYER) { 658 hr = IDirectSoundBuffer_GetCurrentPosition( desc->lpDsPlayBuffer, 659 NULL, &byte_pos ); 660 if (FAILED(hr)) { 661 PJ_LOG(1,(THIS_FILE, "DirectSound: unable to get " 662 "position, err %d", hr)); 663 goto on_error; 664 } 665 } else { 666 hr = IDirectSoundCaptureBuffer_GetCurrentPosition( desc->lpDsCaptureBuffer, 667 NULL, &byte_pos ); 668 if (FAILED(hr)) { 669 PJ_LOG(1,(THIS_FILE, "DirectSound: unable to get " 670 "position, err %d", hr)); 671 goto on_error; 672 } 673 } 674 675 /* Signal main thread that we're running. */ 676 assert( desc->hStartEvent ); 677 SetEvent( desc->hStartEvent ); 394 395 396 eventCount = 0; 397 if (strm->dir & PJMEDIA_DIR_PLAYBACK) 398 events[eventCount++] = strm->play_strm.hEvent; 399 if (strm->dir & PJMEDIA_DIR_CAPTURE) 400 events[eventCount++] = strm->rec_strm.hEvent; 401 402 403 /* Raise self priority */ 404 SetThreadPriority( GetCurrentThread(), THREAD_PRIORITY_HIGHEST); 678 405 679 406 /* … … 682 409 * application and write it to DirectSound buffer. 683 410 */ 684 do{411 while (!strm->thread_quit_flag) { 685 412 686 /* Call callback to get sound data */ 687 if (desc->type == DSOUND_TYPE_PLAYER) { 688 size = bytes_per_pkt; 689 status = (*dev->play_cb)(dev, sample_pos, buffer, &size); 413 DWORD rc; 414 pjmedia_dir signalled_dir; 415 struct dsound_stream *dsound_strm; 416 417 rc = WaitForMultipleObjects(eventCount, events, FALSE, 100); 418 if (rc < WAIT_OBJECT_0 || rc >= WAIT_OBJECT_0+eventCount) 419 continue; 420 421 422 if (rc == WAIT_OBJECT_0) { 423 if (events[0] == strm->play_strm.hEvent) 424 signalled_dir = PJMEDIA_DIR_PLAYBACK; 425 else 426 signalled_dir = PJMEDIA_DIR_CAPTURE; 427 } else { 428 if (events[1] == strm->play_strm.hEvent) 429 signalled_dir = PJMEDIA_DIR_PLAYBACK; 430 else 431 signalled_dir = PJMEDIA_DIR_CAPTURE; 432 } 433 434 435 if (signalled_dir == PJMEDIA_DIR_PLAYBACK) { 436 437 dsound_strm = &strm->play_strm; 438 439 /* Get frame from application. */ 440 status = (*strm->play_cb)(strm->user_data, 441 dsound_strm->timestamp.u32.lo, 442 strm->buffer, 443 strm->samples_per_frame * 444 BYTES_PER_SAMPLE); 445 if (status != PJ_SUCCESS) 446 break; 447 448 /* Write to DirectSound buffer. */ 449 AppWriteDataToBuffer( dsound_strm->ds.play.lpDsBuffer, 450 dsound_strm->dwBytePos, 451 (LPBYTE)strm->buffer, 452 strm->samples_per_frame * BYTES_PER_SAMPLE); 453 454 } else { 455 BOOL rc; 456 457 dsound_strm = &strm->rec_strm; 458 459 /* Capture from DirectSound buffer. */ 460 rc = AppReadDataFromBuffer(dsound_strm->ds.capture.lpDsBuffer, 461 dsound_strm->dwBytePos, 462 (LPBYTE)strm->buffer, 463 strm->samples_per_frame * 464 BYTES_PER_SAMPLE); 465 466 if (!rc) { 467 pj_memset(strm->buffer, 0, strm->samples_per_frame * 468 BYTES_PER_SAMPLE); 469 } 470 471 /* Call callback */ 472 status = (*strm->rec_cb)(strm->user_data, 473 dsound_strm->timestamp.u32.lo, 474 strm->buffer, 475 strm->samples_per_frame * 476 BYTES_PER_SAMPLE); 690 477 691 478 /* Quit thread on error. */ 692 if (status != 0)479 if (status != PJ_SUCCESS) 693 480 break; 694 481 695 /* Write zeroes when we've got nothing from application. */696 if (size == 0) {697 memset(buffer, 0, bytes_per_pkt);698 size = bytes_per_pkt;699 }700 701 /* Write to DirectSound buffer. */702 AppWriteDataToBuffer( desc->lpDsPlayBuffer, byte_pos,703 (LPBYTE)buffer, size);704 705 } else {706 /* Capture from DirectSound buffer. */707 size = bytes_per_pkt;708 if (AppReadDataFromBuffer( desc->lpDsCaptureBuffer, byte_pos,709 (LPBYTE)buffer, size)) {710 711 } else {712 memset(buffer, 0, size);713 }714 715 /* Call callback */716 status = (*dev->rec_cb)(dev, sample_pos, buffer, size);717 718 /* Quit thread on error. */719 if (status != 0)720 break;721 482 } 722 483 723 484 /* Increment position. */ 724 byte_pos += size; 725 if (byte_pos >= buffer_size) 726 byte_pos -= buffer_size; 727 sample_pos += samples_per_pkt; 728 729 while (WaitForSingleObject(desc->hEvent, 500) != WAIT_OBJECT_0 && 730 (!desc->dwThreadQuitFlag)) 731 { 732 Sleep(1); 485 dsound_strm->dwBytePos += strm->samples_per_frame * BYTES_PER_SAMPLE; 486 if (dsound_strm->dwBytePos >= dsound_strm->dwDsBufferSize) 487 dsound_strm->dwBytePos -= dsound_strm->dwDsBufferSize; 488 dsound_strm->timestamp.u64 += strm->samples_per_frame; 489 } 490 491 492 PJ_LOG(5,(THIS_FILE, "DirectSound: thread stopping..")); 493 494 return 0; 495 } 496 497 498 499 /* 500 * Init sound library. 501 */ 502 PJ_DEF(pj_status_t) pjmedia_snd_init(pj_pool_factory *factory) 503 { 504 pool_factory = factory; 505 return PJ_SUCCESS; 506 } 507 508 /* 509 * Deinitialize sound library. 510 */ 511 PJ_DEF(pj_status_t) pjmedia_snd_deinit(void) 512 { 513 return PJ_SUCCESS; 514 } 515 516 /* 517 * Get device count. 518 */ 519 PJ_DEF(int) pjmedia_snd_get_dev_count(void) 520 { 521 return 1; 522 } 523 524 /* 525 * Get device info. 526 */ 527 PJ_DEF(const pjmedia_snd_dev_info*) pjmedia_snd_get_dev_info(unsigned index) 528 { 529 static pjmedia_snd_dev_info info; 530 531 PJ_UNUSED_ARG(index); 532 533 pj_memset(&info, 0, sizeof(info)); 534 535 info.default_samples_per_sec = 44100; 536 info.input_count = 1; 537 info.output_count = 1; 538 pj_ansi_strcpy(info.name, "Default DirectSound Device"); 539 540 return &info; 541 } 542 543 544 /* 545 * Open stream. 546 */ 547 static pj_status_t open_stream( pjmedia_dir dir, 548 int rec_id, 549 int play_id, 550 unsigned clock_rate, 551 unsigned channel_count, 552 unsigned samples_per_frame, 553 unsigned bits_per_sample, 554 pjmedia_snd_rec_cb rec_cb, 555 pjmedia_snd_play_cb play_cb, 556 void *user_data, 557 pjmedia_snd_stream **p_snd_strm) 558 { 559 pj_pool_t *pool; 560 pjmedia_snd_stream *strm; 561 pj_status_t status; 562 563 564 /* Make sure sound subsystem has been initialized with 565 * pjmedia_snd_init() 566 */ 567 PJ_ASSERT_RETURN( pool_factory != NULL, PJ_EINVALIDOP ); 568 569 570 /* Can only support 16bits per sample */ 571 PJ_ASSERT_RETURN(bits_per_sample == BITS_PER_SAMPLE, PJ_EINVAL); 572 573 /* Don't support device at present */ 574 PJ_UNUSED_ARG(rec_id); 575 PJ_UNUSED_ARG(play_id); 576 577 578 /* Create and Initialize stream descriptor */ 579 pool = pj_pool_create(pool_factory, "dsound-dev", 1000, 1000, NULL); 580 PJ_ASSERT_RETURN(pool != NULL, PJ_ENOMEM); 581 582 strm = pj_pool_zalloc(pool, sizeof(pjmedia_snd_stream)); 583 strm->dir = dir; 584 strm->pool = pool; 585 strm->rec_cb = rec_cb; 586 strm->play_cb = play_cb; 587 strm->user_data = user_data; 588 589 strm->samples_per_frame = samples_per_frame; 590 strm->buffer = pj_pool_alloc(pool, samples_per_frame * BYTES_PER_SAMPLE); 591 if (!strm->buffer) { 592 pj_pool_release(pool); 593 return PJ_ENOMEM; 594 } 595 596 /* Create player stream */ 597 if (dir & PJMEDIA_DIR_PLAYBACK) { 598 status = init_player_stream( &strm->play_strm, clock_rate, 599 channel_count, samples_per_frame, 600 DEFAULT_BUFFER_COUNT ); 601 if (status != PJ_SUCCESS) { 602 pjmedia_snd_stream_close(strm); 603 return status; 733 604 } 734 } while (!desc->dwThreadQuitFlag); 735 736 737 PJ_LOG(4,(THIS_FILE, "DirectSound: stopping..")); 738 739 free(buffer); 740 if (desc->type == DSOUND_TYPE_PLAYER) { 741 IDirectSoundBuffer_Stop( desc->lpDsPlayBuffer ); 742 } else { 743 IDirectSoundCaptureBuffer_Stop( desc->lpDsCaptureBuffer ); 744 } 745 return 0; 746 747 on_error: 748 PJ_LOG(4,(THIS_FILE, "DirectSound play stopping")); 749 750 if (buffer) 751 free(buffer); 752 if (desc->type == DSOUND_TYPE_PLAYER) { 753 IDirectSoundBuffer_Stop( desc->lpDsPlayBuffer ); 754 } else { 755 IDirectSoundCaptureBuffer_Stop( desc->lpDsCaptureBuffer ); 756 } 757 desc->dwThreadQuitFlag = 1; 758 759 /* Signal main thread that we failed to initialize */ 760 assert( desc->hStartEvent ); 761 SetEvent( desc->hStartEvent ); 762 return -1; 763 } 764 765 766 /* 767 * Generic starter for play/record. 768 */ 769 static pj_status_t dsound_dev_play_record( pj_snd_dev *dev, 770 Direct_Sound_Descriptor *desc ) 771 { 772 DWORD threadId; 773 int op_type = desc->type; 774 const char *op_name = (op_type == DSOUND_TYPE_PLAYER) ? "play" : "record"; 775 struct Thread_Param param; 776 777 PJ_LOG(4,(THIS_FILE, "DirectSound %s()", op_name)); 778 779 /* 780 * Create event for the thread to signal us that it is starting or 781 * quitting during startup. 782 */ 783 desc->hStartEvent = CreateEvent(NULL, FALSE, FALSE, NULL); 784 if (desc->hStartEvent == NULL) { 785 PJ_LOG(1,(THIS_FILE, "DirectSound %s: unable to create event", op_name)); 786 return -1; 787 } 788 789 param.dev = dev; 790 param.desc = desc; 791 792 /* 793 * Create thread to handle feeding up data to player/recorder. 794 */ 795 desc->hThread = NULL; 796 desc->dwThreadQuitFlag = 0; 797 desc->hThread = CreateThread( NULL, 0, &dsound_dev_thread, ¶m, 798 CREATE_SUSPENDED, &threadId); 799 if (!desc->hThread) { 800 PJ_LOG(1,(THIS_FILE, "DirectSound %s(): unable to create thread", op_name)); 801 return -1; 802 } 803 804 SetThreadPriority( desc->hThread, THREAD_PRIORITY_HIGHEST); 805 806 /* 807 * Resume thread. 808 */ 809 if (ResumeThread(desc->hThread) == (DWORD)-1) { 810 PJ_LOG(1,(THIS_FILE, "DirectSound %s(): unable to resume thread", op_name)); 811 goto on_error; 812 } 813 814 /* 815 * Wait until we've got signal from the thread that it has successfully 816 * started, or when it is quitting. 817 */ 818 WaitForSingleObject( desc->hStartEvent, INFINITE); 819 820 /* We can destroy the event now. */ 821 CloseHandle( desc->hStartEvent ); 822 desc->hStartEvent = NULL; 823 824 /* Examine thread status. */ 825 if (desc->dwThreadQuitFlag != 0) { 826 /* Thread failed to initialize */ 827 WaitForSingleObject(desc->hThread, INFINITE); 828 CloseHandle(desc->hThread); 829 desc->hThread = NULL; 830 return -1; 831 } 832 833 return 0; 834 835 on_error: 836 TerminateThread(desc->hThread, -1); 837 CloseHandle(desc->hThread); 838 desc->hThread = NULL; 839 return -1; 840 } 841 842 /* 843 * Start playing. 844 */ 845 static pj_status_t dsound_dev_play( pj_snd_dev *dev ) 846 { 847 PJ_Direct_Sound_Device *dsDev = dev->device; 848 849 assert(dsDev); 850 if (!dsDev) { 851 assert(0); 852 return -1; 853 } 854 855 return dsound_dev_play_record( dev, &dsDev->playDesc ); 856 } 857 858 /* 859 * Start recording. 860 */ 861 static pj_status_t dsound_dev_record( pj_snd_dev *dev ) 862 { 863 PJ_Direct_Sound_Device *dsDev = dev->device; 864 865 assert(dsDev); 866 if (!dsDev) { 867 assert(0); 868 return -1; 869 } 870 871 return dsound_dev_play_record( dev, &dsDev->recDesc ); 872 } 873 874 #ifdef _MSC_VER 875 # pragma warning(pop) 876 # pragma warning(disable: 4514) // unreferenced inline function has been removed 877 #endif 605 } 606 607 /* Create capture stream */ 608 if (dir & PJMEDIA_DIR_CAPTURE) { 609 status = init_capture_stream( &strm->rec_strm, clock_rate, 610 channel_count, samples_per_frame, 611 DEFAULT_BUFFER_COUNT); 612 if (status != PJ_SUCCESS) { 613 pjmedia_snd_stream_close(strm); 614 return status; 615 } 616 } 617 618 619 /* Create and start the thread */ 620 status = pj_thread_create(pool, "dsound", &dsound_dev_thread, strm, 621 0, 0, &strm->thread); 622 if (status != PJ_SUCCESS) { 623 pjmedia_snd_stream_close(strm); 624 return status; 625 } 626 627 *p_snd_strm = strm; 628 629 return PJ_SUCCESS; 630 } 631 632 /* 633 * Open stream. 634 */ 635 PJ_DEF(pj_status_t) pjmedia_snd_open_rec( int index, 636 unsigned clock_rate, 637 unsigned channel_count, 638 unsigned samples_per_frame, 639 unsigned bits_per_sample, 640 pjmedia_snd_rec_cb rec_cb, 641 void *user_data, 642 pjmedia_snd_stream **p_snd_strm) 643 { 644 PJ_ASSERT_RETURN(rec_cb && p_snd_strm, PJ_EINVAL); 645 646 return open_stream( PJMEDIA_DIR_CAPTURE, index, -1, 647 clock_rate, channel_count, samples_per_frame, 648 bits_per_sample, rec_cb, NULL, user_data, 649 p_snd_strm); 650 } 651 652 PJ_DEF(pj_status_t) pjmedia_snd_open_player( int index, 653 unsigned clock_rate, 654 unsigned channel_count, 655 unsigned samples_per_frame, 656 unsigned bits_per_sample, 657 pjmedia_snd_play_cb play_cb, 658 void *user_data, 659 pjmedia_snd_stream **p_snd_strm) 660 { 661 PJ_ASSERT_RETURN(play_cb && p_snd_strm, PJ_EINVAL); 662 663 return open_stream( PJMEDIA_DIR_PLAYBACK, -1, index, 664 clock_rate, channel_count, samples_per_frame, 665 bits_per_sample, NULL, play_cb, user_data, 666 p_snd_strm); 667 } 668 669 /* 670 * Open both player and recorder. 671 */ 672 PJ_DEF(pj_status_t) pjmedia_snd_open( int rec_id, 673 int play_id, 674 unsigned clock_rate, 675 unsigned channel_count, 676 unsigned samples_per_frame, 677 unsigned bits_per_sample, 678 pjmedia_snd_rec_cb rec_cb, 679 pjmedia_snd_play_cb play_cb, 680 void *user_data, 681 pjmedia_snd_stream **p_snd_strm) 682 { 683 PJ_ASSERT_RETURN(rec_cb && play_cb && p_snd_strm, PJ_EINVAL); 684 685 return open_stream( PJMEDIA_DIR_CAPTURE_PLAYBACK, rec_id, play_id, 686 clock_rate, channel_count, samples_per_frame, 687 bits_per_sample, rec_cb, play_cb, user_data, 688 p_snd_strm ); 689 } 690 691 /* 692 * Start stream. 693 */ 694 PJ_DEF(pj_status_t) pjmedia_snd_stream_start(pjmedia_snd_stream *stream) 695 { 696 HRESULT hr; 697 698 PJ_UNUSED_ARG(stream); 699 700 if (stream->play_strm.ds.play.lpDsBuffer) { 701 hr = IDirectSoundBuffer_Play(stream->play_strm.ds.play.lpDsBuffer, 702 0, 0, DSBPLAY_LOOPING); 703 if (FAILED(hr)) 704 return PJ_RETURN_OS_ERROR(hr); 705 } 706 707 if (stream->rec_strm.ds.capture.lpDsBuffer) { 708 hr = IDirectSoundCaptureBuffer_Start(stream->rec_strm.ds.capture.lpDsBuffer, 709 DSCBSTART_LOOPING ); 710 if (FAILED(hr)) 711 return PJ_RETURN_OS_ERROR(hr); 712 } 713 714 return PJ_SUCCESS; 715 } 716 717 /* 718 * Stop stream. 719 */ 720 PJ_DEF(pj_status_t) pjmedia_snd_stream_stop(pjmedia_snd_stream *stream) 721 { 722 PJ_ASSERT_RETURN(stream != NULL, PJ_EINVAL); 723 724 if (stream->play_strm.ds.play.lpDsBuffer) { 725 PJ_LOG(5,(THIS_FILE, "Stopping DirectSound playback stream")); 726 IDirectSoundBuffer_Stop( stream->play_strm.ds.play.lpDsBuffer ); 727 } 728 729 if (stream->rec_strm.ds.capture.lpDsBuffer) { 730 PJ_LOG(5,(THIS_FILE, "Stopping DirectSound capture stream")); 731 IDirectSoundCaptureBuffer_Stop(stream->rec_strm.ds.capture.lpDsBuffer); 732 } 733 734 return PJ_SUCCESS; 735 } 736 737 738 /* 739 * Destroy stream. 740 */ 741 PJ_DEF(pj_status_t) pjmedia_snd_stream_close(pjmedia_snd_stream *stream) 742 { 743 PJ_ASSERT_RETURN(stream != NULL, PJ_EINVAL); 744 745 pjmedia_snd_stream_stop(stream); 746 747 if (stream->play_strm.lpDsNotify) { 748 IDirectSoundNotify_Release( stream->play_strm.lpDsNotify ); 749 stream->play_strm.lpDsNotify = NULL; 750 } 751 752 if (stream->play_strm.hEvent) { 753 CloseHandle(stream->play_strm.hEvent); 754 stream->play_strm.hEvent = NULL; 755 } 756 757 if (stream->play_strm.ds.play.lpDsBuffer) { 758 IDirectSoundBuffer_Release( stream->play_strm.ds.play.lpDsBuffer ); 759 stream->play_strm.ds.play.lpDsBuffer = NULL; 760 } 761 762 if (stream->play_strm.ds.play.lpDs) { 763 IDirectSound_Release( stream->play_strm.ds.play.lpDs ); 764 stream->play_strm.ds.play.lpDs = NULL; 765 } 766 767 if (stream->rec_strm.lpDsNotify) { 768 IDirectSoundNotify_Release( stream->rec_strm.lpDsNotify ); 769 stream->rec_strm.lpDsNotify = NULL; 770 } 771 772 if (stream->rec_strm.hEvent) { 773 CloseHandle(stream->rec_strm.hEvent); 774 stream->rec_strm.hEvent = NULL; 775 } 776 777 if (stream->rec_strm.ds.capture.lpDsBuffer) { 778 IDirectSoundCaptureBuffer_Release( stream->rec_strm.ds.capture.lpDsBuffer ); 779 stream->rec_strm.ds.capture.lpDsBuffer = NULL; 780 } 781 782 if (stream->rec_strm.ds.capture.lpDs) { 783 IDirectSoundCapture_Release( stream->rec_strm.ds.capture.lpDs ); 784 stream->rec_strm.ds.capture.lpDs = NULL; 785 } 786 787 if (stream->thread) { 788 stream->thread_quit_flag = 1; 789 pj_thread_join(stream->thread); 790 pj_thread_destroy(stream->thread); 791 stream->thread = NULL; 792 } 793 794 pj_pool_release(stream->pool); 795 796 return PJ_SUCCESS; 797 } 798 799 800 #endif /* PJMEDIA_SOUND_IMPLEMENTATION */ 801 -
pjproject/trunk/pjmedia/src/pjmedia/errno.c
r370 r371 148 148 149 149 /* See if the error comes from PortAudio. */ 150 #if defined(PJMEDIA_HAS_PORTAUDIO_SOUND) && PJMEDIA_HAS_PORTAUDIO_SOUND!=0150 #if PJMEDIA_SOUND_IMPLEMENTATION==PJMEDIA_SOUND_PORTAUDIO_SOUND 151 151 if (statcode >= PJMEDIA_ERRNO_FROM_PORTAUDIO(paNotInitialized) && 152 152 statcode < PJMEDIA_ERRNO_FROM_PORTAUDIO(paNotInitialized + 10000)) … … 164 164 165 165 } else 166 #endif /* PJMEDIA_ HAS_PORTAUDIO_SOUND*/166 #endif /* PJMEDIA_SOUND_IMPLEMENTATION */ 167 167 if (statcode >= PJMEDIA_ERRNO_START && 168 168 statcode < PJMEDIA_ERRNO_START + PJ_ERRNO_SPACE_SIZE) -
pjproject/trunk/pjmedia/src/pjmedia/nullsound.c
r352 r371 20 20 #include <pj/assert.h> 21 21 22 #if defined(PJMEDIA_HAS_NULL_SOUND) && PJMEDIA_HAS_NULL_SOUND!=022 #if PJMEDIA_SOUND_IMPLEMENTATION==PJMEDIA_SOUND_NULL_SOUND 23 23 24 24 static pjmedia_snd_dev_info null_info = … … 143 143 144 144 145 #endif /* PJMEDIA_ HAS_NULL_SOUND*/145 #endif /* PJMEDIA_SOUND_IMPLEMENTATION */ -
pjproject/trunk/pjmedia/src/pjmedia/pasound.c
r358 r371 24 24 #include <portaudio.h> 25 25 26 #if defined(PJMEDIA_HAS_PORTAUDIO_SOUND) && PJMEDIA_HAS_PORTAUDIO_SOUND!=026 #if PJMEDIA_SOUND_IMPLEMENTATION==PJMEDIA_SOUND_PORTAUDIO_SOUND 27 27 28 28 #define THIS_FILE "pasound.c" … … 281 281 stream->dir = PJMEDIA_DIR_CAPTURE; 282 282 stream->user_data = user_data; 283 stream->samples_per_sec = samples_per_frame;283 stream->samples_per_sec = clock_rate; 284 284 stream->bytes_per_sample = bits_per_sample / 8; 285 285 stream->channel_count = channel_count; … … 374 374 stream->dir = stream->dir = PJMEDIA_DIR_PLAYBACK; 375 375 stream->user_data = user_data; 376 stream->samples_per_sec = samples_per_frame;376 stream->samples_per_sec = clock_rate; 377 377 stream->bytes_per_sample = bits_per_sample / 8; 378 378 stream->channel_count = channel_count; … … 384 384 outputParam.hostApiSpecificStreamInfo = NULL; 385 385 outputParam.sampleFormat = sampleFormat; 386 outputParam.suggestedLatency = paDevInfo->defaultLowInputLatency;386 outputParam.suggestedLatency = 1.0 * samples_per_frame / clock_rate;; 387 387 388 388 paHostApiInfo = Pa_GetHostApiInfo(paDevInfo->hostApi); … … 399 399 } 400 400 401 PJ_LOG(5,(THIS_FILE, "%s opening device % s(%s) for playing, sample rate=%d"401 PJ_LOG(5,(THIS_FILE, "%s opening device %d: %s(%s) for playing, sample rate=%d" 402 402 ", ch=%d, " 403 403 "bits=%d, %d samples per frame", 404 404 (err==0 ? "Success" : "Error"), 405 paDevInfo->name, paHostApiInfo->name,405 index, paDevInfo->name, paHostApiInfo->name, 406 406 clock_rate, channel_count, 407 407 bits_per_sample, samples_per_frame)); … … 495 495 stream->dir = PJMEDIA_DIR_CAPTURE_PLAYBACK; 496 496 stream->user_data = user_data; 497 stream->samples_per_sec = samples_per_frame;497 stream->samples_per_sec = clock_rate; 498 498 stream->bytes_per_sample = bits_per_sample / 8; 499 499 stream->channel_count = channel_count; … … 621 621 622 622 623 #endif /* PJMEDIA_ HAS_PORTAUDIO_SOUND*/623 #endif /* PJMEDIA_SOUND_IMPLEMENTATION */ -
pjproject/trunk/pjmedia/src/pjmedia/portaudio/pa_win_hostapis.c
r65 r371 59 59 { 60 60 61 #ifndef PA_NO_DS 62 PaWinDs_Initialize, 63 #endif 64 61 65 #ifndef PA_NO_WMME 62 66 PaWinMme_Initialize, 63 #endif64 65 #ifndef PA_NO_DS66 PaWinDs_Initialize,67 67 #endif 68 68 -
pjproject/trunk/pjsip-apps/build/sample_debug.dsp
r361 r371 51 51 LINK32=link.exe 52 52 # 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 53 # 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 /ma chine:I386 /out:"../bin/samples/sampledebug_vc6.exe"53 # 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" 54 54 55 55 !ELSEIF "$(CFG)" == "sample_debug - Win32 Debug" -
pjproject/trunk/pjsip-apps/src/samples/debug.c
r361 r371 28 28 * #include "playfile.c" 29 29 */ 30 #include " resampleplay.c"30 #include "playfile.c" 31 31 -
pjproject/trunk/pjsip-apps/src/samples/sndinfo.c
r353 r371 76 76 static int play_counter; 77 77 static int rec_counter; 78 static int min_delay = 0xFFFF, max_delay; 79 static char play_delays[1000]; 78 80 79 81 static pj_status_t play_cb(void *user_data, pj_uint32_t timestamp, 80 82 void *output, unsigned size) 81 83 { 84 static pj_time_val last_cb; 85 82 86 ++play_counter; 87 88 if (last_cb.sec == 0 && last_cb.msec == 0) { 89 pj_gettimeofday(&last_cb); 90 } else { 91 pj_time_val now, saved; 92 int delay; 93 94 pj_gettimeofday(&now); 95 saved = now; 96 PJ_TIME_VAL_SUB(now, last_cb); 97 delay = PJ_TIME_VAL_MSEC(now); 98 99 if (delay < min_delay) 100 min_delay = delay; 101 if (delay > max_delay) 102 max_delay = delay; 103 104 last_cb = saved; 105 106 play_delays[play_counter-1] = delay; 107 } 108 83 109 return PJ_SUCCESS; 84 110 } … … 99 125 title, errmsg, status); 100 126 } 101 127 102 128 static int open_device(int dev_id, pjmedia_dir dir, 103 129 int clock_rate, int nchannel, int bits) … … 107 133 pjmedia_snd_stream *strm; 108 134 const char *dirtype; 135 unsigned i; 109 136 110 137 switch (dir) { … … 166 193 167 194 puts("Success."); 195 196 printf("Delay: "); 197 for (i=0; i<play_counter; ++i) 198 printf("%d ", play_delays[i]); 199 200 puts(""); 201 if (dir & PJMEDIA_DIR_PLAYBACK) { 202 printf("Callback interval: min interval=%d ms, max interval=%d ms\n", 203 min_delay, max_delay); 204 } 205 206 168 207 return 0; 169 208 }
Note: See TracChangeset
for help on using the changeset viewer.