- Timestamp:
- Feb 21, 2009 2:21:59 PM (15 years ago)
- File:
-
- 1 edited
Legend:
- Unmodified
- Added
- Removed
-
pjproject/branches/projects/aps-direct/pjmedia/src/pjmedia/wmme_sound.c
r2366 r2468 1 #include <pjmedia/sound.h>2 #include <pjmedia/errno.h>3 #include <pj/assert.h>4 #include <pj/log.h>5 #include <pj/os.h>6 #include <pj/string.h>7 8 #if PJMEDIA_SOUND_IMPLEMENTATION == PJMEDIA_SOUND_WIN32_MME_SOUND9 10 #ifdef _MSC_VER11 # pragma warning(push, 3)12 #endif13 14 #include <windows.h>15 #include <mmsystem.h>16 17 #ifdef _MSC_VER18 # pragma warning(pop)19 #endif20 21 #if defined(PJ_WIN32_WINCE) && PJ_WIN32_WINCE!=022 # pragma comment(lib, "Coredll.lib")23 #elif defined(_MSC_VER)24 # pragma comment(lib, "winmm.lib")25 #endif26 27 28 #define THIS_FILE "wmme_sound.c"29 #define BITS_PER_SAMPLE 1630 #define BYTES_PER_SAMPLE (BITS_PER_SAMPLE/8)31 32 #define MAX_PACKET_BUFFER_COUNT 3233 #define MAX_HARDWARE 1634 35 struct wmme_dev_info36 {37 pjmedia_snd_dev_info info;38 unsigned deviceId;39 };40 41 static unsigned dev_count;42 static struct wmme_dev_info dev_info[MAX_HARDWARE];43 static pj_bool_t snd_initialized = PJ_FALSE;44 45 /* Latency settings */46 static unsigned snd_input_latency = PJMEDIA_SND_DEFAULT_REC_LATENCY;47 static unsigned snd_output_latency = PJMEDIA_SND_DEFAULT_PLAY_LATENCY;48 49 50 /* Individual WMME capture/playback stream descriptor */51 struct wmme_stream52 {53 union54 {55 HWAVEIN In;56 HWAVEOUT Out;57 } hWave;58 59 WAVEHDR *WaveHdr;60 HANDLE hEvent;61 DWORD dwBufIdx;62 DWORD dwMaxBufIdx;63 pj_timestamp timestamp;64 };65 66 67 /* Sound stream. */68 struct pjmedia_snd_stream69 {70 pjmedia_dir dir; /**< Sound direction. */71 int play_id; /**< Playback dev id. */72 int rec_id; /**< Recording dev id. */73 pj_pool_t *pool; /**< Memory pool. */74 75 pjmedia_snd_rec_cb rec_cb; /**< Capture callback. */76 pjmedia_snd_play_cb play_cb; /**< Playback callback. */77 void *user_data; /**< Application data. */78 79 struct wmme_stream play_strm; /**< Playback stream. */80 struct wmme_stream rec_strm; /**< Capture stream. */81 82 void *buffer; /**< Temp. frame buffer. */83 unsigned clock_rate; /**< Clock rate. */84 unsigned samples_per_frame; /**< Samples per frame. */85 unsigned bits_per_sample; /**< Bits per sample. */86 unsigned channel_count; /**< Channel count. */87 88 pj_thread_t *thread; /**< Thread handle. */89 HANDLE thread_quit_event; /**< Quit signal to thread */90 };91 92 93 static pj_pool_factory *pool_factory;94 95 static void init_waveformatex (LPWAVEFORMATEX pcmwf,96 unsigned clock_rate,97 unsigned channel_count)98 {99 pj_bzero(pcmwf, sizeof(PCMWAVEFORMAT));100 pcmwf->wFormatTag = WAVE_FORMAT_PCM;101 pcmwf->nChannels = (pj_uint16_t)channel_count;102 pcmwf->nSamplesPerSec = clock_rate;103 pcmwf->nBlockAlign = (pj_uint16_t)(channel_count * BYTES_PER_SAMPLE);104 pcmwf->nAvgBytesPerSec = clock_rate * channel_count * BYTES_PER_SAMPLE;105 pcmwf->wBitsPerSample = BITS_PER_SAMPLE;106 }107 108 109 /*110 * Initialize WMME player device.111 */112 static pj_status_t init_player_stream( pj_pool_t *pool,113 struct wmme_stream *wmme_strm,114 int dev_id,115 unsigned clock_rate,116 unsigned channel_count,117 unsigned samples_per_frame,118 unsigned buffer_count)119 {120 MMRESULT mr;121 WAVEFORMATEX pcmwf;122 unsigned bytes_per_frame;123 unsigned i;124 125 PJ_ASSERT_RETURN(buffer_count <= MAX_PACKET_BUFFER_COUNT, PJ_EINVAL);126 127 /* Check device ID */128 if (dev_id == -1)129 dev_id = 0;130 131 PJ_ASSERT_RETURN(dev_id >= 0 && dev_id < (int)dev_count, PJ_EINVAL);132 133 /*134 * Create a wait event.135 */136 wmme_strm->hEvent = CreateEvent(NULL, FALSE, FALSE, NULL);137 if (NULL == wmme_strm->hEvent)138 return pj_get_os_error();139 140 /*141 * Set up wave format structure for opening the device.142 */143 init_waveformatex(&pcmwf, clock_rate, channel_count);144 bytes_per_frame = samples_per_frame * BYTES_PER_SAMPLE;145 146 /*147 * Open wave device.148 */149 mr = waveOutOpen(&wmme_strm->hWave.Out, dev_info[dev_id].deviceId, &pcmwf,150 (DWORD)wmme_strm->hEvent, 0, CALLBACK_EVENT);151 if (mr != MMSYSERR_NOERROR)152 /* TODO: This is for HRESULT/GetLastError() */153 PJ_RETURN_OS_ERROR(mr);154 155 /* Pause the wave out device */156 mr = waveOutPause(wmme_strm->hWave.Out);157 if (mr != MMSYSERR_NOERROR)158 /* TODO: This is for HRESULT/GetLastError() */159 PJ_RETURN_OS_ERROR(mr);160 161 /*162 * Create the buffers.163 */164 wmme_strm->WaveHdr = pj_pool_zalloc(pool, sizeof(WAVEHDR) * buffer_count);165 for (i = 0; i < buffer_count; ++i)166 {167 wmme_strm->WaveHdr[i].lpData = pj_pool_zalloc(pool, bytes_per_frame);168 wmme_strm->WaveHdr[i].dwBufferLength = bytes_per_frame;169 mr = waveOutPrepareHeader(wmme_strm->hWave.Out,170 &(wmme_strm->WaveHdr[i]),171 sizeof(WAVEHDR));172 if (mr != MMSYSERR_NOERROR)173 /* TODO: This is for HRESULT/GetLastError() */174 PJ_RETURN_OS_ERROR(mr);175 mr = waveOutWrite(wmme_strm->hWave.Out, &(wmme_strm->WaveHdr[i]),176 sizeof(WAVEHDR));177 if (mr != MMSYSERR_NOERROR)178 /* TODO: This is for HRESULT/GetLastError() */179 PJ_RETURN_OS_ERROR(mr);180 }181 182 wmme_strm->dwBufIdx = 0;183 wmme_strm->dwMaxBufIdx = buffer_count;184 wmme_strm->timestamp.u64 = 0;185 186 /* Done setting up play device. */187 PJ_LOG(5, (THIS_FILE,188 " WaveAPI Sound player \"%s\" initialized (clock_rate=%d, "189 "channel_count=%d, samples_per_frame=%d (%dms))",190 dev_info[dev_id].info.name,191 clock_rate, channel_count, samples_per_frame,192 samples_per_frame * 1000 / clock_rate));193 194 return PJ_SUCCESS;195 }196 197 198 /*199 * Initialize Windows Multimedia recorder device200 */201 static pj_status_t init_capture_stream( pj_pool_t *pool,202 struct wmme_stream *wmme_strm,203 int dev_id,204 unsigned clock_rate,205 unsigned channel_count,206 unsigned samples_per_frame,207 unsigned buffer_count)208 {209 MMRESULT mr;210 WAVEFORMATEX pcmwf;211 unsigned bytes_per_frame;212 unsigned i;213 214 PJ_ASSERT_RETURN(buffer_count <= MAX_PACKET_BUFFER_COUNT, PJ_EINVAL);215 216 /* Check device ID */217 if (dev_id == -1)218 dev_id = 0;219 220 PJ_ASSERT_RETURN(dev_id >= 0 && dev_id < (int)dev_count, PJ_EINVAL);221 222 /*223 * Create a wait event.224 */225 wmme_strm->hEvent = CreateEvent(NULL, FALSE, FALSE, NULL);226 if (NULL == wmme_strm->hEvent)227 return pj_get_os_error();228 229 /*230 * Set up wave format structure for opening the device.231 */232 init_waveformatex(&pcmwf, clock_rate, channel_count);233 bytes_per_frame = samples_per_frame * BYTES_PER_SAMPLE;234 235 /*236 * Open wave device.237 */238 mr = waveInOpen(&wmme_strm->hWave.In, dev_info[dev_id].deviceId, &pcmwf,239 (DWORD)wmme_strm->hEvent, 0, CALLBACK_EVENT);240 if (mr != MMSYSERR_NOERROR)241 /* TODO: This is for HRESULT/GetLastError() */242 PJ_RETURN_OS_ERROR(mr);243 244 /*245 * Create the buffers.246 */247 wmme_strm->WaveHdr = pj_pool_zalloc(pool, sizeof(WAVEHDR) * buffer_count);248 for (i = 0; i < buffer_count; ++i)249 {250 wmme_strm->WaveHdr[i].lpData = pj_pool_zalloc(pool, bytes_per_frame);251 wmme_strm->WaveHdr[i].dwBufferLength = bytes_per_frame;252 mr = waveInPrepareHeader(wmme_strm->hWave.In, &(wmme_strm->WaveHdr[i]),253 sizeof(WAVEHDR));254 if (mr != MMSYSERR_NOERROR)255 /* TODO: This is for HRESULT/GetLastError() */256 PJ_RETURN_OS_ERROR(mr);257 mr = waveInAddBuffer(wmme_strm->hWave.In, &(wmme_strm->WaveHdr[i]),258 sizeof(WAVEHDR));259 if (mr != MMSYSERR_NOERROR)260 /* TODO: This is for HRESULT/GetLastError() */261 PJ_RETURN_OS_ERROR(mr);262 }263 264 wmme_strm->dwBufIdx = 0;265 wmme_strm->dwMaxBufIdx = buffer_count;266 wmme_strm->timestamp.u64 = 0;267 268 /* Done setting up play device. */269 PJ_LOG(5,(THIS_FILE,270 " WaveAPI Sound recorder \"%s\" initialized (clock_rate=%d, "271 "channel_count=%d, samples_per_frame=%d (%dms))",272 dev_info[dev_id].info.name,273 clock_rate, channel_count, samples_per_frame,274 samples_per_frame * 1000 / clock_rate));275 276 return PJ_SUCCESS;277 }278 279 280 281 /*282 * WMME capture and playback thread.283 */284 static int PJ_THREAD_FUNC wmme_dev_thread(void *arg)285 {286 pjmedia_snd_stream *strm = arg;287 HANDLE events[3];288 unsigned eventCount;289 unsigned bytes_per_frame;290 pj_status_t status = PJ_SUCCESS;291 292 293 eventCount = 0;294 events[eventCount++] = strm->thread_quit_event;295 if (strm->dir & PJMEDIA_DIR_PLAYBACK)296 events[eventCount++] = strm->play_strm.hEvent;297 if (strm->dir & PJMEDIA_DIR_CAPTURE)298 events[eventCount++] = strm->rec_strm.hEvent;299 300 301 /* Raise self priority. We don't want the audio to be distorted by302 * system activity.303 */304 #if defined(PJ_WIN32_WINCE) && PJ_WIN32_WINCE != 0305 if (strm->dir & PJMEDIA_DIR_PLAYBACK)306 CeSetThreadPriority(GetCurrentThread(), 153);307 else308 CeSetThreadPriority(GetCurrentThread(), 247);309 #else310 SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_TIME_CRITICAL);311 #endif312 313 /* Calculate bytes per frame */314 bytes_per_frame = strm->samples_per_frame * BYTES_PER_SAMPLE;315 316 /*317 * Loop while not signalled to quit, wait for event objects to be318 * signalled by WMME capture and play buffer.319 */320 while (status == PJ_SUCCESS)321 {322 323 DWORD rc;324 pjmedia_dir signalled_dir;325 326 rc = WaitForMultipleObjects(eventCount, events, FALSE, INFINITE);327 if (rc < WAIT_OBJECT_0 || rc >= WAIT_OBJECT_0 + eventCount)328 continue;329 330 if (rc == WAIT_OBJECT_0)331 break;332 333 if (rc == (WAIT_OBJECT_0 + 1))334 {335 if (events[1] == strm->play_strm.hEvent)336 signalled_dir = PJMEDIA_DIR_PLAYBACK;337 else338 signalled_dir = PJMEDIA_DIR_CAPTURE;339 }340 else341 {342 if (events[2] == strm->play_strm.hEvent)343 signalled_dir = PJMEDIA_DIR_PLAYBACK;344 else345 signalled_dir = PJMEDIA_DIR_CAPTURE;346 }347 348 349 if (signalled_dir == PJMEDIA_DIR_PLAYBACK)350 {351 struct wmme_stream *wmme_strm = &strm->play_strm;352 MMRESULT mr = MMSYSERR_NOERROR;353 status = PJ_SUCCESS;354 355 /*356 * Windows Multimedia has requested us to feed some frames to357 * playback buffer.358 */359 360 while (wmme_strm->WaveHdr[wmme_strm->dwBufIdx].dwFlags & WHDR_DONE)361 {362 void* buffer = wmme_strm->WaveHdr[wmme_strm->dwBufIdx].lpData;363 364 PJ_LOG(5,(THIS_FILE, "Finished writing buffer %d",365 wmme_strm->dwBufIdx));366 367 /* Get frame from application. */368 status = (*strm->play_cb)(strm->user_data,369 wmme_strm->timestamp.u32.lo,370 buffer,371 bytes_per_frame);372 373 if (status != PJ_SUCCESS)374 break;375 376 /* Write to the device. */377 mr = waveOutWrite(wmme_strm->hWave.Out,378 &(wmme_strm->WaveHdr[wmme_strm->dwBufIdx]),379 sizeof(WAVEHDR));380 if (mr != MMSYSERR_NOERROR)381 {382 status = PJ_STATUS_FROM_OS(mr);383 break;384 }385 386 /* Increment position. */387 if (++wmme_strm->dwBufIdx >= wmme_strm->dwMaxBufIdx)388 wmme_strm->dwBufIdx = 0;389 wmme_strm->timestamp.u64 += strm->samples_per_frame /390 strm->channel_count;391 }392 }393 else394 {395 struct wmme_stream *wmme_strm = &strm->rec_strm;396 MMRESULT mr = MMSYSERR_NOERROR;397 status = PJ_SUCCESS;398 399 /*400 * Windows Multimedia has indicated that it has some frames ready401 * in the capture buffer. Get as much frames as possible to402 * prevent overflows.403 */404 #if 0405 {406 static DWORD tc = 0;407 DWORD now = GetTickCount();408 DWORD i = 0;409 DWORD bits = 0;410 411 if (tc == 0) tc = now;412 413 for (i = 0; i < wmme_strm->dwMaxBufIdx; ++i)414 {415 bits = bits << 4;416 bits |= wmme_strm->WaveHdr[i].dwFlags & WHDR_DONE;417 }418 PJ_LOG(5,(THIS_FILE, "Record Signal> Index: %d, Delta: %4.4d, "419 "Flags: %6.6x\n",420 wmme_strm->dwBufIdx,421 now - tc,422 bits));423 tc = now;424 }425 #endif426 427 while (wmme_strm->WaveHdr[wmme_strm->dwBufIdx].dwFlags & WHDR_DONE)428 {429 char* buffer = (char*)430 wmme_strm->WaveHdr[wmme_strm->dwBufIdx].lpData;431 unsigned cap_len =432 wmme_strm->WaveHdr[wmme_strm->dwBufIdx].dwBytesRecorded;433 434 /*435 PJ_LOG(5,(THIS_FILE, "Read %d bytes from buffer %d", cap_len,436 wmme_strm->dwBufIdx));437 */438 439 if (cap_len < bytes_per_frame)440 pj_bzero(buffer + cap_len, bytes_per_frame - cap_len);441 442 /* Copy the audio data out of the wave buffer. */443 pj_memcpy(strm->buffer, buffer, bytes_per_frame);444 445 /* Re-add the buffer to the device. */446 mr = waveInAddBuffer(wmme_strm->hWave.In,447 &(wmme_strm->WaveHdr[wmme_strm->dwBufIdx]),448 sizeof(WAVEHDR));449 if (mr != MMSYSERR_NOERROR) {450 status = PJ_STATUS_FROM_OS(mr);451 break;452 }453 454 /* Call callback */455 status = (*strm->rec_cb)(strm->user_data,456 wmme_strm->timestamp.u32.lo,457 strm->buffer,458 bytes_per_frame);459 460 if (status != PJ_SUCCESS)461 break;462 463 /* Increment position. */464 if (++wmme_strm->dwBufIdx >= wmme_strm->dwMaxBufIdx)465 wmme_strm->dwBufIdx = 0;466 wmme_strm->timestamp.u64 += strm->samples_per_frame /467 strm->channel_count;468 }469 }470 }471 472 PJ_LOG(5,(THIS_FILE, "WMME: thread stopping.."));473 return 0;474 }475 476 477 /*478 * Init sound library.479 */480 PJ_DEF(pj_status_t) pjmedia_snd_init(pj_pool_factory *factory)481 {482 unsigned c;483 int i;484 int inputDeviceCount, outputDeviceCount, maximumPossibleDeviceCount;485 486 if (snd_initialized)487 return PJ_SUCCESS;488 489 pj_bzero(&dev_info, sizeof(dev_info));490 491 dev_count = 0;492 pool_factory = factory;493 494 /* Enumerate sound playback devices */495 maximumPossibleDeviceCount = 0;496 497 inputDeviceCount = waveInGetNumDevs();498 if (inputDeviceCount > 0)499 /* assume there is a WAVE_MAPPER */500 maximumPossibleDeviceCount += inputDeviceCount + 1;501 502 outputDeviceCount = waveOutGetNumDevs();503 if (outputDeviceCount > 0)504 /* assume there is a WAVE_MAPPER */505 maximumPossibleDeviceCount += outputDeviceCount + 1;506 507 if (maximumPossibleDeviceCount >= MAX_HARDWARE)508 {509 pj_assert(!"Too many hardware found");510 PJ_LOG(3,(THIS_FILE, "Too many hardware found, "511 "some devices will not be listed"));512 }513 514 if (inputDeviceCount > 0)515 {516 /* -1 is the WAVE_MAPPER */517 for (i = -1; i < inputDeviceCount && dev_count < MAX_HARDWARE; ++i)518 {519 UINT uDeviceID = (UINT)((i==-1) ? WAVE_MAPPER : i);520 WAVEINCAPS wic;521 MMRESULT mr;522 523 pj_bzero(&wic, sizeof(WAVEINCAPS));524 525 mr = waveInGetDevCaps(uDeviceID, &wic, sizeof(WAVEINCAPS));526 527 if (mr == MMSYSERR_NOMEM)528 return PJ_ENOMEM;529 530 if (mr != MMSYSERR_NOERROR)531 continue;532 533 #ifdef UNICODE534 WideCharToMultiByte(CP_ACP, 0, wic.szPname, wcslen(wic.szPname),535 dev_info[dev_count].info.name, 64, NULL, NULL);536 #else537 strncpy(dev_info[dev_count].info.name, wic.szPname, MAXPNAMELEN);538 #endif539 if (uDeviceID == WAVE_MAPPER)540 strcat(dev_info[dev_count].info.name, " - Input");541 542 dev_info[dev_count].info.input_count = wic.wChannels;543 dev_info[dev_count].info.output_count = 0;544 dev_info[dev_count].info.default_samples_per_sec = 44100;545 dev_info[dev_count].deviceId = uDeviceID;546 547 /* Sometimes a device can return a rediculously large number of548 * channels. This happened with an SBLive card on a Windows ME box.549 * It also happens on Win XP!550 */551 if ((dev_info[dev_count].info.input_count < 1) ||552 (dev_info[dev_count].info.input_count > 256))553 dev_info[dev_count].info.input_count = 2;554 555 ++dev_count;556 }557 }558 559 if( outputDeviceCount > 0 )560 {561 /* -1 is the WAVE_MAPPER */562 for (i = -1; i < outputDeviceCount && dev_count < MAX_HARDWARE; ++i)563 {564 UINT uDeviceID = (UINT)((i==-1) ? WAVE_MAPPER : i);565 WAVEOUTCAPS woc;566 MMRESULT mr;567 568 pj_bzero(&woc, sizeof(WAVEOUTCAPS));569 570 mr = waveOutGetDevCaps(uDeviceID, &woc, sizeof(WAVEOUTCAPS));571 572 if (mr == MMSYSERR_NOMEM)573 return PJ_ENOMEM;574 575 if (mr != MMSYSERR_NOERROR)576 continue;577 578 #ifdef UNICODE579 WideCharToMultiByte(CP_ACP, 0, woc.szPname, wcslen(woc.szPname),580 dev_info[dev_count].info.name, 64, NULL, NULL);581 #else582 strncpy(dev_info[dev_count].info.name, woc.szPname, MAXPNAMELEN);583 #endif584 if (uDeviceID == WAVE_MAPPER)585 strcat(dev_info[dev_count].info.name, " - Output");586 587 dev_info[dev_count].info.output_count = woc.wChannels;588 dev_info[dev_count].info.input_count = 0;589 dev_info[dev_count].deviceId = uDeviceID;590 /* TODO: Perform a search! */591 dev_info[dev_count].info.default_samples_per_sec = 44100;592 593 /* Sometimes a device can return a rediculously large number of channels.594 * This happened with an SBLive card on a Windows ME box.595 * It also happens on Win XP!596 */597 if ((dev_info[dev_count].info.output_count < 1) ||598 (dev_info[dev_count].info.output_count > 256))599 dev_info[dev_count].info.output_count = 2;600 601 ++dev_count;602 }603 }604 605 PJ_LOG(4, (THIS_FILE, "WMME initialized, found %d devices:", dev_count));606 for (c = 0; c < dev_count; ++c)607 {608 PJ_LOG(4, (THIS_FILE, " dev_id %d: %s (in=%d, out=%d)",609 c,610 dev_info[c].info.name,611 dev_info[c].info.input_count,612 dev_info[c].info.output_count));613 }614 return PJ_SUCCESS;615 }616 617 /*618 * Deinitialize sound library.619 */620 PJ_DEF(pj_status_t) pjmedia_snd_deinit(void)621 {622 snd_initialized = PJ_FALSE;623 return PJ_SUCCESS;624 }625 626 /*627 * Get device count.628 */629 PJ_DEF(int) pjmedia_snd_get_dev_count(void)630 {631 return dev_count;632 }633 634 /*635 * Get device info.636 */637 PJ_DEF(const pjmedia_snd_dev_info*) pjmedia_snd_get_dev_info(unsigned index)638 {639 if (index == (unsigned)-1)640 index = 0;641 642 PJ_ASSERT_RETURN(index < dev_count, NULL);643 644 return &dev_info[index].info;645 }646 647 648 /*649 * Open stream.650 */651 static pj_status_t open_stream(pjmedia_dir dir,652 int rec_id,653 int play_id,654 unsigned clock_rate,655 unsigned channel_count,656 unsigned samples_per_frame,657 unsigned bits_per_sample,658 pjmedia_snd_rec_cb rec_cb,659 pjmedia_snd_play_cb play_cb,660 void *user_data,661 pjmedia_snd_stream **p_snd_strm)662 {663 pj_pool_t *pool;664 pjmedia_snd_stream *strm;665 pj_status_t status;666 667 668 /* Make sure sound subsystem has been initialized with669 * pjmedia_snd_init()670 */671 PJ_ASSERT_RETURN(pool_factory != NULL, PJ_EINVALIDOP);672 673 674 /* Can only support 16bits per sample */675 PJ_ASSERT_RETURN(bits_per_sample == BITS_PER_SAMPLE, PJ_EINVAL);676 677 /* Create and Initialize stream descriptor */678 pool = pj_pool_create(pool_factory, "wmme-dev", 1000, 1000, NULL);679 PJ_ASSERT_RETURN(pool != NULL, PJ_ENOMEM);680 681 strm = pj_pool_zalloc(pool, sizeof(pjmedia_snd_stream));682 strm->dir = dir;683 strm->play_id = play_id;684 strm->rec_id = rec_id;685 strm->pool = pool;686 strm->rec_cb = rec_cb;687 strm->play_cb = play_cb;688 strm->user_data = user_data;689 strm->clock_rate = clock_rate;690 strm->samples_per_frame = samples_per_frame;691 strm->bits_per_sample = bits_per_sample;692 strm->channel_count = channel_count;693 strm->buffer = pj_pool_alloc(pool, samples_per_frame * BYTES_PER_SAMPLE);694 if (!strm->buffer)695 {696 pj_pool_release(pool);697 return PJ_ENOMEM;698 }699 700 /* Create player stream */701 if (dir & PJMEDIA_DIR_PLAYBACK)702 {703 unsigned buf_count;704 705 buf_count = snd_output_latency * clock_rate * channel_count /706 samples_per_frame / 1000;707 708 status = init_player_stream(strm->pool,709 &strm->play_strm,710 play_id,711 clock_rate,712 channel_count,713 samples_per_frame,714 buf_count);715 716 if (status != PJ_SUCCESS)717 {718 pjmedia_snd_stream_close(strm);719 return status;720 }721 }722 723 /* Create capture stream */724 if (dir & PJMEDIA_DIR_CAPTURE)725 {726 unsigned buf_count;727 728 buf_count = snd_input_latency * clock_rate * channel_count /729 samples_per_frame / 1000;730 731 status = init_capture_stream(strm->pool,732 &strm->rec_strm,733 rec_id,734 clock_rate,735 channel_count,736 samples_per_frame,737 buf_count);738 739 if (status != PJ_SUCCESS)740 {741 pjmedia_snd_stream_close(strm);742 return status;743 }744 }745 746 /* Create the stop event */747 strm->thread_quit_event = CreateEvent(NULL, FALSE, FALSE, NULL);748 if (strm->thread_quit_event == NULL)749 return pj_get_os_error();750 751 /* Create and start the thread */752 status = pj_thread_create(pool, "wmme", &wmme_dev_thread, strm, 0, 0,753 &strm->thread);754 if (status != PJ_SUCCESS)755 {756 pjmedia_snd_stream_close(strm);757 return status;758 }759 760 *p_snd_strm = strm;761 762 return PJ_SUCCESS;763 }764 765 /*766 * Open stream.767 */768 PJ_DEF(pj_status_t) pjmedia_snd_open_rec(int index,769 unsigned clock_rate,770 unsigned channel_count,771 unsigned samples_per_frame,772 unsigned bits_per_sample,773 pjmedia_snd_rec_cb rec_cb,774 void *user_data,775 pjmedia_snd_stream **p_snd_strm)776 {777 PJ_ASSERT_RETURN(rec_cb && p_snd_strm, PJ_EINVAL);778 779 return open_stream( PJMEDIA_DIR_CAPTURE,780 index,781 -1,782 clock_rate,783 channel_count,784 samples_per_frame,785 bits_per_sample,786 rec_cb,787 NULL,788 user_data,789 p_snd_strm);790 }791 792 PJ_DEF(pj_status_t) pjmedia_snd_open_player(int index,793 unsigned clock_rate,794 unsigned channel_count,795 unsigned samples_per_frame,796 unsigned bits_per_sample,797 pjmedia_snd_play_cb play_cb,798 void *user_data,799 pjmedia_snd_stream **p_snd_strm)800 {801 PJ_ASSERT_RETURN(play_cb && p_snd_strm, PJ_EINVAL);802 803 return open_stream( PJMEDIA_DIR_PLAYBACK,804 -1,805 index,806 clock_rate,807 channel_count,808 samples_per_frame,809 bits_per_sample,810 NULL,811 play_cb,812 user_data,813 p_snd_strm);814 }815 816 /*817 * Open both player and recorder.818 */819 PJ_DEF(pj_status_t) pjmedia_snd_open(int rec_id,820 int play_id,821 unsigned clock_rate,822 unsigned channel_count,823 unsigned samples_per_frame,824 unsigned bits_per_sample,825 pjmedia_snd_rec_cb rec_cb,826 pjmedia_snd_play_cb play_cb,827 void *user_data,828 pjmedia_snd_stream **p_snd_strm)829 {830 PJ_ASSERT_RETURN(rec_cb && play_cb && p_snd_strm, PJ_EINVAL);831 832 return open_stream( PJMEDIA_DIR_CAPTURE_PLAYBACK,833 rec_id,834 play_id,835 clock_rate,836 channel_count,837 samples_per_frame,838 bits_per_sample,839 rec_cb,840 play_cb,841 user_data,842 p_snd_strm);843 }844 845 /*846 * Get stream info.847 */848 PJ_DEF(pj_status_t) pjmedia_snd_stream_get_info(pjmedia_snd_stream *strm,849 pjmedia_snd_stream_info *pi)850 {851 PJ_ASSERT_RETURN(strm && pi, PJ_EINVAL);852 853 pj_bzero(pi, sizeof(*pi));854 pi->dir = strm->dir;855 pi->play_id = strm->play_id;856 pi->rec_id = strm->rec_id;857 pi->clock_rate = strm->clock_rate;858 pi->channel_count = strm->channel_count;859 pi->samples_per_frame = strm->samples_per_frame;860 pi->bits_per_sample = strm->bits_per_sample;861 pi->rec_latency = snd_input_latency * strm->clock_rate *862 strm->channel_count / 1000;863 pi->play_latency = snd_output_latency * strm->clock_rate *864 strm->channel_count / 1000;865 866 return PJ_SUCCESS;867 }868 869 870 /*871 * Start stream.872 */873 PJ_DEF(pj_status_t) pjmedia_snd_stream_start(pjmedia_snd_stream *stream)874 {875 MMRESULT mr;876 877 PJ_UNUSED_ARG(stream);878 879 if (stream->play_strm.hWave.Out != NULL)880 {881 mr = waveOutRestart(stream->play_strm.hWave.Out);882 if (mr != MMSYSERR_NOERROR)883 /* TODO: This macro is supposed to be used for HRESULT, fix. */884 PJ_RETURN_OS_ERROR(mr);885 PJ_LOG(5,(THIS_FILE, "WMME playback stream started"));886 }887 888 if (stream->rec_strm.hWave.In != NULL)889 {890 mr = waveInStart(stream->rec_strm.hWave.In);891 if (mr != MMSYSERR_NOERROR)892 /* TODO: This macro is supposed to be used for HRESULT, fix. */893 PJ_RETURN_OS_ERROR(mr);894 PJ_LOG(5,(THIS_FILE, "WMME capture stream started"));895 }896 897 return PJ_SUCCESS;898 }899 900 /*901 * Stop stream.902 */903 PJ_DEF(pj_status_t) pjmedia_snd_stream_stop(pjmedia_snd_stream *stream)904 {905 MMRESULT mr;906 907 PJ_ASSERT_RETURN(stream != NULL, PJ_EINVAL);908 909 if (stream->play_strm.hWave.Out != NULL)910 {911 mr = waveOutPause(stream->play_strm.hWave.Out);912 if (mr != MMSYSERR_NOERROR)913 /* TODO: This macro is supposed to be used for HRESULT, fix. */914 PJ_RETURN_OS_ERROR(mr);915 PJ_LOG(5,(THIS_FILE, "Stopped WMME playback stream"));916 }917 918 if (stream->rec_strm.hWave.In != NULL)919 {920 mr = waveInStop(stream->rec_strm.hWave.In);921 if (mr != MMSYSERR_NOERROR)922 /* TODO: This macro is supposed to be used for HRESULT, fix. */923 PJ_RETURN_OS_ERROR(mr);924 PJ_LOG(5,(THIS_FILE, "Stopped WMME capture stream"));925 }926 927 return PJ_SUCCESS;928 }929 930 931 /*932 * Destroy stream.933 */934 PJ_DEF(pj_status_t) pjmedia_snd_stream_close(pjmedia_snd_stream *stream)935 {936 unsigned i;937 938 PJ_ASSERT_RETURN(stream != NULL, PJ_EINVAL);939 940 pjmedia_snd_stream_stop(stream);941 942 if (stream->thread)943 {944 SetEvent(stream->thread_quit_event);945 pj_thread_join(stream->thread);946 pj_thread_destroy(stream->thread);947 stream->thread = NULL;948 }949 950 /* Unprepare the headers and close the play device */951 if (stream->play_strm.hWave.Out)952 {953 waveOutReset(stream->play_strm.hWave.Out);954 for (i = 0; i < stream->play_strm.dwMaxBufIdx; ++i)955 waveOutUnprepareHeader(stream->play_strm.hWave.Out,956 &(stream->play_strm.WaveHdr[i]),957 sizeof(WAVEHDR));958 waveOutClose(stream->play_strm.hWave.Out);959 stream->play_strm.hWave.Out = NULL;960 }961 962 /* Close the play event */963 if (stream->play_strm.hEvent)964 {965 CloseHandle(stream->play_strm.hEvent);966 stream->play_strm.hEvent = NULL;967 }968 969 /* Unprepare the headers and close the record device */970 if (stream->rec_strm.hWave.In)971 {972 waveInReset(stream->rec_strm.hWave.In);973 for (i = 0; i < stream->play_strm.dwMaxBufIdx; ++i)974 waveInUnprepareHeader(stream->rec_strm.hWave.In,975 &(stream->rec_strm.WaveHdr[i]),976 sizeof(WAVEHDR));977 waveInClose(stream->rec_strm.hWave.In);978 stream->rec_strm.hWave.In = NULL;979 }980 981 /* Close the record event */982 if (stream->rec_strm.hEvent)983 {984 CloseHandle(stream->rec_strm.hEvent);985 stream->rec_strm.hEvent = NULL;986 }987 988 pj_pool_release(stream->pool);989 990 return PJ_SUCCESS;991 }992 993 /*994 * Set sound latency.995 */996 PJ_DEF(pj_status_t) pjmedia_snd_set_latency(unsigned input_latency,997 unsigned output_latency)998 {999 snd_input_latency = (input_latency == 0)?1000 PJMEDIA_SND_DEFAULT_REC_LATENCY : input_latency;1001 snd_output_latency = (output_latency == 0)?1002 PJMEDIA_SND_DEFAULT_PLAY_LATENCY : output_latency;1003 1004 return PJ_SUCCESS;1005 }1006 1007 #endif /* PJMEDIA_SOUND_IMPLEMENTATION */1008
Note: See TracChangeset
for help on using the changeset viewer.