blob: 4ae48353d3ee1194872a6ef2cbb68d46fe5e0968 [file] [log] [blame]
Alexandre Lision8af73cb2013-12-10 14:11:20 -05001/* $Id: wmme_dev.c 4537 2013-06-19 06:47:43Z riza $ */
Tristan Matthews0a329cc2013-07-17 13:20:14 -04002/*
3 * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com)
4 * Copyright (C) 2003-2008 Benny Prijono <benny@prijono.org>
5 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, write to the Free Software
18 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
19 */
20#include <pjmedia-audiodev/audiodev_imp.h>
21#include <pj/assert.h>
22#include <pj/log.h>
23#include <pj/os.h>
24#include <pj/string.h>
25#include <pj/unicode.h>
26
27#if PJMEDIA_AUDIO_DEV_HAS_WMME
28
29#ifdef _MSC_VER
30# pragma warning(push, 3)
31#endif
32
33#include <windows.h>
34#include <mmsystem.h>
35#include <mmreg.h>
36
37#ifdef _MSC_VER
38# pragma warning(pop)
39#endif
40
41#ifndef PJMEDIA_WMME_DEV_USE_MMDEVICE_API
42# define PJMEDIA_WMME_DEV_USE_MMDEVICE_API \
43 (defined(_WIN32_WINNT) && (_WIN32_WINNT>=0x0600))
44#endif
45
46#if PJMEDIA_WMME_DEV_USE_MMDEVICE_API != 0
47# define DRV_QUERYFUNCTIONINSTANCEID (DRV_RESERVED + 17)
48# define DRV_QUERYFUNCTIONINSTANCEIDSIZE (DRV_RESERVED + 18)
49#endif
50
51/* mingw lacks WAVE_FORMAT_ALAW/MULAW */
52#ifndef WAVE_FORMAT_ALAW
53# define WAVE_FORMAT_ALAW 0x0006
54#endif
55#ifndef WAVE_FORMAT_MULAW
56# define WAVE_FORMAT_MULAW 0x0007
57#endif
58
59#if defined(PJ_WIN32_WINCE) && PJ_WIN32_WINCE!=0
60# pragma comment(lib, "Coredll.lib")
61#elif defined(_MSC_VER)
62# pragma comment(lib, "winmm.lib")
63#endif
64
65
66#define THIS_FILE "wmme_dev.c"
67
68/* WMME device info */
69struct wmme_dev_info
70{
71 pjmedia_aud_dev_info info;
72 unsigned deviceId;
73 const wchar_t *endpointId;
74};
75
76/* WMME factory */
77struct wmme_factory
78{
79 pjmedia_aud_dev_factory base;
80 pj_pool_t *base_pool;
81 pj_pool_t *pool;
82 pj_pool_factory *pf;
83
84 unsigned dev_count;
85 struct wmme_dev_info *dev_info;
86};
87
88
89/* Individual WMME capture/playback stream descriptor */
90struct wmme_channel
91{
92 union
93 {
94 HWAVEIN In;
95 HWAVEOUT Out;
96 } hWave;
97
98 WAVEHDR *WaveHdr;
99 HANDLE hEvent;
100 DWORD dwBufIdx;
101 DWORD dwMaxBufIdx;
102 pj_timestamp timestamp;
103};
104
105
106/* Sound stream. */
107struct wmme_stream
108{
109 pjmedia_aud_stream base; /**< Base stream */
110 pjmedia_aud_param param; /**< Settings */
111 pj_pool_t *pool; /**< Memory pool. */
112
113 pjmedia_aud_rec_cb rec_cb; /**< Capture callback. */
114 pjmedia_aud_play_cb play_cb; /**< Playback callback. */
115 void *user_data; /**< Application data. */
116
117 struct wmme_channel play_strm; /**< Playback stream. */
118 struct wmme_channel rec_strm; /**< Capture stream. */
119
120 void *buffer; /**< Temp. frame buffer. */
121 pjmedia_format_id fmt_id; /**< Frame format */
122 pj_uint8_t silence_char; /**< Silence pattern */
123 unsigned bytes_per_frame; /**< Bytes per frame */
124
125 pjmedia_frame_ext *xfrm; /**< Extended frame buffer */
126 unsigned xfrm_size; /**< Total ext frm size */
127
128 pj_thread_t *thread; /**< Thread handle. */
129 HANDLE thread_quit_event; /**< Quit signal to thread */
130};
131
132
133/* Prototypes */
134static pj_status_t factory_init(pjmedia_aud_dev_factory *f);
135static pj_status_t factory_destroy(pjmedia_aud_dev_factory *f);
136static pj_status_t factory_refresh(pjmedia_aud_dev_factory *f);
137static unsigned factory_get_dev_count(pjmedia_aud_dev_factory *f);
138static pj_status_t factory_get_dev_info(pjmedia_aud_dev_factory *f,
139 unsigned index,
140 pjmedia_aud_dev_info *info);
141static pj_status_t factory_default_param(pjmedia_aud_dev_factory *f,
142 unsigned index,
143 pjmedia_aud_param *param);
144static pj_status_t factory_create_stream(pjmedia_aud_dev_factory *f,
145 const pjmedia_aud_param *param,
146 pjmedia_aud_rec_cb rec_cb,
147 pjmedia_aud_play_cb play_cb,
148 void *user_data,
149 pjmedia_aud_stream **p_aud_strm);
150
151static pj_status_t stream_get_param(pjmedia_aud_stream *strm,
152 pjmedia_aud_param *param);
153static pj_status_t stream_get_cap(pjmedia_aud_stream *strm,
154 pjmedia_aud_dev_cap cap,
155 void *value);
156static pj_status_t stream_set_cap(pjmedia_aud_stream *strm,
157 pjmedia_aud_dev_cap cap,
158 const void *value);
159static pj_status_t stream_start(pjmedia_aud_stream *strm);
160static pj_status_t stream_stop(pjmedia_aud_stream *strm);
161static pj_status_t stream_destroy(pjmedia_aud_stream *strm);
162
163
164/* Operations */
165static pjmedia_aud_dev_factory_op factory_op =
166{
167 &factory_init,
168 &factory_destroy,
169 &factory_get_dev_count,
170 &factory_get_dev_info,
171 &factory_default_param,
172 &factory_create_stream,
173 &factory_refresh
174};
175
176static pjmedia_aud_stream_op stream_op =
177{
178 &stream_get_param,
179 &stream_get_cap,
180 &stream_set_cap,
181 &stream_start,
182 &stream_stop,
183 &stream_destroy
184};
185
186
187/****************************************************************************
188 * Factory operations
189 */
190/*
191 * Init WMME audio driver.
192 */
193pjmedia_aud_dev_factory* pjmedia_wmme_factory(pj_pool_factory *pf)
194{
195 struct wmme_factory *f;
196 pj_pool_t *pool;
197
198 pool = pj_pool_create(pf, "WMME base", 1000, 1000, NULL);
199 f = PJ_POOL_ZALLOC_T(pool, struct wmme_factory);
200 f->pf = pf;
201 f->base_pool = pool;
202 f->base.op = &factory_op;
203
204 return &f->base;
205}
206
207/* Internal: Windows Vista and Windows 7 have their device
208 * names truncated when using the waveXXX api. The names
209 * should be acquired from the MMDevice APIs
210 */
211#if PJMEDIA_WMME_DEV_USE_MMDEVICE_API != 0
212
213#define COBJMACROS
214#include <mmdeviceapi.h>
215#define INITGUID
216#include <Guiddef.h>
217#include <FunctionDiscoveryKeys_devpkey.h>
218
219DEFINE_GUID(CLSID_MMDeviceEnumerator, 0xBCDE0395, 0xE52F, 0x467C,
220 0x8E, 0x3D, 0xC4, 0x57, 0x92, 0x91, 0x69, 0x2E);
221DEFINE_GUID(IID_IMMDeviceEnumerator, 0xA95664D2, 0x9614, 0x4F35,
222 0xA7, 0x46, 0xDE, 0x8D, 0xB6, 0x36, 0x17, 0xE6);
223
224static void get_dev_names(pjmedia_aud_dev_factory *f)
225{
226 struct wmme_factory *wf = (struct wmme_factory*)f;
227 HRESULT coinit = S_OK;
228 HRESULT hr = S_OK;
229 IMMDeviceEnumerator *pEnumerator = NULL;
230 IMMDeviceCollection *pDevices = NULL;
231 UINT cDevices = 0;
232 UINT nDevice = 0;
233
234 coinit = CoInitializeEx(NULL, COINIT_MULTITHREADED);
235 if (coinit == RPC_E_CHANGED_MODE)
236 coinit = CoInitializeEx(NULL, COINIT_APARTMENTTHREADED);
237 if (FAILED(coinit))
238 goto on_error;
239
240 hr = CoCreateInstance(&CLSID_MMDeviceEnumerator, NULL,
241 CLSCTX_INPROC_SERVER, &IID_IMMDeviceEnumerator,
242 (void**)&pEnumerator);
243 if (FAILED(hr))
244 goto on_error;
245 hr = IMMDeviceEnumerator_EnumAudioEndpoints(pEnumerator, eAll,
246 DEVICE_STATE_ACTIVE,
247 &pDevices);
248 if (FAILED(hr))
249 goto on_error;
250 hr = IMMDeviceCollection_GetCount(pDevices, &cDevices);
251 if (FAILED(hr))
252 goto on_error;
253
254 for (nDevice = 0; nDevice < cDevices; ++nDevice) {
255 IMMDevice *pDevice = NULL;
256 IPropertyStore *pProps = NULL;
257 LPWSTR pwszID = NULL;
258 PROPVARIANT varName;
259 unsigned i;
260
261 PropVariantInit(&varName);
262
263 hr = IMMDeviceCollection_Item(pDevices, nDevice, &pDevice);
264 if (FAILED(hr))
265 goto cleanup;
266 hr = IMMDevice_GetId(pDevice, &pwszID);
267 if (FAILED(hr))
268 goto cleanup;
269 hr = IMMDevice_OpenPropertyStore(pDevice, STGM_READ, &pProps);
270 if (FAILED(hr))
271 goto cleanup;
272 hr = IPropertyStore_GetValue(pProps, &PKEY_Device_FriendlyName,
273 &varName);
274 if (FAILED(hr))
275 goto cleanup;
276
277 for (i = 0; i < wf->dev_count; ++i) {
278 if (0 == wcscmp(wf->dev_info[i].endpointId, pwszID)) {
279 wcstombs(wf->dev_info[i].info.name, varName.pwszVal,
280 sizeof(wf->dev_info[i].info.name));
281 break;
282 }
283 }
284
285 PropVariantClear(&varName);
286
287 cleanup:
288 if (pProps)
289 IPropertyStore_Release(pProps);
290 if (pwszID)
291 CoTaskMemFree(pwszID);
292 if (pDevice)
293 hr = IMMDevice_Release(pDevice);
294 }
295
296on_error:
297 if (pDevices)
298 hr = IMMDeviceCollection_Release(pDevices);
299
300 if (pEnumerator)
301 hr = IMMDeviceEnumerator_Release(pEnumerator);
302
303 if (SUCCEEDED(coinit))
304 CoUninitialize();
305}
306
307#else
308
309static void get_dev_names(pjmedia_aud_dev_factory *f)
310{
311 PJ_UNUSED_ARG(f);
312}
313
314#endif
315
316/* Internal: build device info from WAVEINCAPS/WAVEOUTCAPS */
317static void build_dev_info(UINT deviceId, struct wmme_dev_info *wdi,
318 const WAVEINCAPS *wic, const WAVEOUTCAPS *woc)
319{
320#define WIC_WOC(wic,woc,field) (wic? wic->field : woc->field)
321
322 pj_bzero(wdi, sizeof(*wdi));
323 wdi->deviceId = deviceId;
324
325 /* Device Name */
326 if (deviceId==WAVE_MAPPER) {
327 strncpy(wdi->info.name, "Wave mapper", sizeof(wdi->info.name));
328 wdi->info.name[sizeof(wdi->info.name)-1] = '\0';
329 } else {
330 const pj_char_t *szPname = WIC_WOC(wic, woc, szPname);
331 PJ_DECL_ANSI_TEMP_BUF(wTmp, sizeof(wdi->info.name));
332
333 strncpy(wdi->info.name,
334 PJ_NATIVE_TO_STRING(szPname, wTmp, PJ_ARRAY_SIZE(wTmp)),
335 sizeof(wdi->info.name));
336 wdi->info.name[sizeof(wdi->info.name)-1] = '\0';
337 }
338
339 wdi->info.default_samples_per_sec = 16000;
340 strcpy(wdi->info.driver, "WMME");
341
342 if (wic) {
343 wdi->info.input_count = wic->wChannels;
344 wdi->info.caps |= PJMEDIA_AUD_DEV_CAP_INPUT_LATENCY;
345
346 /* Sometimes a device can return a rediculously large number of
347 * channels. This happened with an SBLive card on a Windows ME box.
348 * It also happens on Win XP!
349 */
350 if (wdi->info.input_count<1 || wdi->info.input_count>256) {
351 wdi->info.input_count = 2;
352 }
353 }
354
355 if (woc) {
356 wdi->info.output_count = woc->wChannels;
357 wdi->info.caps |= PJMEDIA_AUD_DEV_CAP_OUTPUT_LATENCY;
358
359 if (woc->dwSupport & WAVECAPS_VOLUME) {
360 wdi->info.caps |= PJMEDIA_AUD_DEV_CAP_OUTPUT_VOLUME_SETTING;
361 }
362
363 /* Sometimes a device can return a rediculously large number of
364 * channels. This happened with an SBLive card on a Windows ME box.
365 * It also happens on Win XP!
366 */
367 if (wdi->info.output_count<1 || wdi->info.output_count>256) {
368 wdi->info.output_count = 2;
369 }
370 }
371
372 /* Extended formats */
373 wdi->info.caps |= PJMEDIA_AUD_DEV_CAP_EXT_FORMAT;
374 wdi->info.ext_fmt_cnt = 2;
375 pjmedia_format_init_audio(&wdi->info.ext_fmt[0],
376 PJMEDIA_FORMAT_PCMU, 8000, 1, 8,
377 20000, 64000, 64000);
378 pjmedia_format_init_audio(&wdi->info.ext_fmt[0],
379 PJMEDIA_FORMAT_PCMA, 8000, 1, 8,
380 20000, 64000, 64000);
381}
382
383/* API: init factory */
384static pj_status_t factory_init(pjmedia_aud_dev_factory *f)
385{
386 pj_status_t ret = factory_refresh(f);
387 if (ret != PJ_SUCCESS)
388 return ret;
389
390 PJ_LOG(4, (THIS_FILE, "WMME initialized"));
391 return PJ_SUCCESS;
392}
393
394/* API: refresh the device list */
395static pj_status_t factory_refresh(pjmedia_aud_dev_factory *f)
396{
397 struct wmme_factory *wf = (struct wmme_factory*)f;
398 unsigned c;
399 int i;
400 int inputDeviceCount, outputDeviceCount, devCount=0;
401 pj_bool_t waveMapperAdded = PJ_FALSE;
402
403 if (wf->pool != NULL) {
404 pj_pool_release(wf->pool);
405 wf->pool = NULL;
406 }
407
408 /* Enumerate sound devices */
409 wf->dev_count = 0;
410 wf->pool = pj_pool_create(wf->pf, "WMME", 1000, 1000, NULL);
411
412 inputDeviceCount = waveInGetNumDevs();
413 devCount += inputDeviceCount;
414
415 outputDeviceCount = waveOutGetNumDevs();
416 devCount += outputDeviceCount;
417
418 if (devCount) {
419 /* Assume there is WAVE_MAPPER */
420 devCount += 2;
421 }
422
423 if (devCount==0) {
424 PJ_LOG(4,(THIS_FILE, "WMME found no sound devices"));
425 /* Enabling this will cause pjsua-lib initialization to fail when there
426 * is no sound device installed in the system, even when pjsua has been
427 * run with --null-audio. Moreover, it might be better to think that
428 * the WMME backend initialization is successfull, regardless there is
429 * no audio device installed, as later application can check it using
430 * get_dev_count().
431 return PJMEDIA_EAUD_NODEV;
432 */
433 return PJ_SUCCESS;
434 }
435
436 wf->dev_info = (struct wmme_dev_info*)
437 pj_pool_calloc(wf->pool, devCount,
438 sizeof(struct wmme_dev_info));
439
440 if (inputDeviceCount && outputDeviceCount) {
441 /* Attempt to add WAVE_MAPPER as input and output device */
442 WAVEINCAPS wic;
443 MMRESULT mr;
444
445 pj_bzero(&wic, sizeof(WAVEINCAPS));
446 mr = waveInGetDevCaps(WAVE_MAPPER, &wic, sizeof(WAVEINCAPS));
447
448 if (mr == MMSYSERR_NOERROR) {
449 WAVEOUTCAPS woc;
450
451 pj_bzero(&woc, sizeof(WAVEOUTCAPS));
452 mr = waveOutGetDevCaps(WAVE_MAPPER, &woc, sizeof(WAVEOUTCAPS));
453 if (mr == MMSYSERR_NOERROR) {
454 build_dev_info(WAVE_MAPPER, &wf->dev_info[wf->dev_count],
455 &wic, &woc);
456 wf->dev_info[wf->dev_count].endpointId = L"";
457 ++wf->dev_count;
458 waveMapperAdded = PJ_TRUE;
459 }
460 }
461
462 }
463
464 if (inputDeviceCount > 0) {
465 /* -1 is the WAVE_MAPPER */
466 for (i = (waveMapperAdded? 0 : -1); i < inputDeviceCount; ++i) {
467 UINT uDeviceID = (UINT)((i==-1) ? WAVE_MAPPER : i);
468 WAVEINCAPS wic;
469 MMRESULT mr;
470 DWORD cbEndpointId;
471
472 pj_bzero(&wic, sizeof(WAVEINCAPS));
473
474 mr = waveInGetDevCaps(uDeviceID, &wic, sizeof(WAVEINCAPS));
475
476 if (mr == MMSYSERR_NOMEM)
477 return PJ_ENOMEM;
478
479 if (mr != MMSYSERR_NOERROR)
480 continue;
481
482 build_dev_info(uDeviceID, &wf->dev_info[wf->dev_count],
483 &wic, NULL);
484
485#if PJMEDIA_WMME_DEV_USE_MMDEVICE_API != 0
486 /* Try to get the endpoint id of the audio device */
487 wf->dev_info[wf->dev_count].endpointId = L"";
488
489 mr = waveInMessage((HWAVEIN)IntToPtr(uDeviceID),
490 DRV_QUERYFUNCTIONINSTANCEIDSIZE,
491 (DWORD_PTR)&cbEndpointId, (DWORD_PTR)NULL);
492 if (mr == MMSYSERR_NOERROR) {
493 const wchar_t **epid = &wf->dev_info[wf->dev_count].endpointId;
494 *epid = (const wchar_t*) pj_pool_calloc(wf->pool,
495 cbEndpointId, 1);
496 mr = waveInMessage((HWAVEIN)IntToPtr(uDeviceID),
497 DRV_QUERYFUNCTIONINSTANCEID,
498 (DWORD_PTR)*epid,
499 cbEndpointId);
500 }
501#else
502 PJ_UNUSED_ARG(cbEndpointId);
503#endif
504
505 ++wf->dev_count;
506 }
507 }
508
509 if( outputDeviceCount > 0 )
510 {
511 /* -1 is the WAVE_MAPPER */
512 for (i = (waveMapperAdded? 0 : -1); i < outputDeviceCount; ++i) {
513 UINT uDeviceID = (UINT)((i==-1) ? WAVE_MAPPER : i);
514 WAVEOUTCAPS woc;
515 MMRESULT mr;
516 DWORD cbEndpointId;
517
518 pj_bzero(&woc, sizeof(WAVEOUTCAPS));
519
520 mr = waveOutGetDevCaps(uDeviceID, &woc, sizeof(WAVEOUTCAPS));
521
522 if (mr == MMSYSERR_NOMEM)
523 return PJ_ENOMEM;
524
525 if (mr != MMSYSERR_NOERROR)
526 continue;
527
528 build_dev_info(uDeviceID, &wf->dev_info[wf->dev_count],
529 NULL, &woc);
530
531#if PJMEDIA_WMME_DEV_USE_MMDEVICE_API != 0
532 /* Try to get the endpoint id of the audio device */
533 wf->dev_info[wf->dev_count].endpointId = L"";
534
535 mr = waveOutMessage((HWAVEOUT)IntToPtr(uDeviceID),
536 DRV_QUERYFUNCTIONINSTANCEIDSIZE,
537 (DWORD_PTR)&cbEndpointId, (DWORD_PTR)NULL);
538 if (mr == MMSYSERR_NOERROR) {
539 const wchar_t **epid = &wf->dev_info[wf->dev_count].endpointId;
540 *epid = (const wchar_t*)pj_pool_calloc(wf->pool,
541 cbEndpointId, 1);
542 mr = waveOutMessage((HWAVEOUT)IntToPtr(uDeviceID),
543 DRV_QUERYFUNCTIONINSTANCEID,
544 (DWORD_PTR)*epid, cbEndpointId);
545 }
546#else
547 PJ_UNUSED_ARG(cbEndpointId);
548#endif
549
550 ++wf->dev_count;
551 }
552 }
553
554 /* On Windows Vista and Windows 7 get the full device names */
555 get_dev_names(f);
556
557 PJ_LOG(4, (THIS_FILE, "WMME found %d devices:",
558 wf->dev_count));
559 for (c = 0; c < wf->dev_count; ++c) {
560 PJ_LOG(4, (THIS_FILE, " dev_id %d: %s (in=%d, out=%d)",
561 c,
562 wf->dev_info[c].info.name,
563 wf->dev_info[c].info.input_count,
564 wf->dev_info[c].info.output_count));
565 }
566
567 return PJ_SUCCESS;
568}
569
570/* API: destroy factory */
571static pj_status_t factory_destroy(pjmedia_aud_dev_factory *f)
572{
573 struct wmme_factory *wf = (struct wmme_factory*)f;
574 pj_pool_t *pool = wf->base_pool;
575
576 pj_pool_release(wf->pool);
577 wf->base_pool = NULL;
578 pj_pool_release(pool);
579
580 return PJ_SUCCESS;
581}
582
583/* API: get number of devices */
584static unsigned factory_get_dev_count(pjmedia_aud_dev_factory *f)
585{
586 struct wmme_factory *wf = (struct wmme_factory*)f;
587 return wf->dev_count;
588}
589
590/* API: get device info */
591static pj_status_t factory_get_dev_info(pjmedia_aud_dev_factory *f,
592 unsigned index,
593 pjmedia_aud_dev_info *info)
594{
595 struct wmme_factory *wf = (struct wmme_factory*)f;
596
597 PJ_ASSERT_RETURN(index < wf->dev_count, PJMEDIA_EAUD_INVDEV);
598
599 pj_memcpy(info, &wf->dev_info[index].info, sizeof(*info));
600
601 return PJ_SUCCESS;
602}
603
604/* API: create default device parameter */
605static pj_status_t factory_default_param(pjmedia_aud_dev_factory *f,
606 unsigned index,
607 pjmedia_aud_param *param)
608{
609 struct wmme_factory *wf = (struct wmme_factory*)f;
610 struct wmme_dev_info *di = &wf->dev_info[index];
611
612 PJ_ASSERT_RETURN(index < wf->dev_count, PJMEDIA_EAUD_INVDEV);
613
614 pj_bzero(param, sizeof(*param));
615 if (di->info.input_count && di->info.output_count) {
616 param->dir = PJMEDIA_DIR_CAPTURE_PLAYBACK;
617 param->rec_id = index;
618 param->play_id = index;
619 } else if (di->info.input_count) {
620 param->dir = PJMEDIA_DIR_CAPTURE;
621 param->rec_id = index;
622 param->play_id = PJMEDIA_AUD_INVALID_DEV;
623 } else if (di->info.output_count) {
624 param->dir = PJMEDIA_DIR_PLAYBACK;
625 param->play_id = index;
626 param->rec_id = PJMEDIA_AUD_INVALID_DEV;
627 } else {
628 return PJMEDIA_EAUD_INVDEV;
629 }
630
631 param->clock_rate = di->info.default_samples_per_sec;
632 param->channel_count = 1;
633 param->samples_per_frame = di->info.default_samples_per_sec * 20 / 1000;
634 param->bits_per_sample = 16;
635 param->flags = PJMEDIA_AUD_DEV_CAP_INPUT_LATENCY |
636 PJMEDIA_AUD_DEV_CAP_OUTPUT_LATENCY;
637 param->input_latency_ms = PJMEDIA_SND_DEFAULT_REC_LATENCY;
638 param->output_latency_ms = PJMEDIA_SND_DEFAULT_PLAY_LATENCY;
639
640 return PJ_SUCCESS;
641}
642
643/* Internal: init WAVEFORMATEX */
644static pj_status_t init_waveformatex(LPWAVEFORMATEX wfx,
645 const pjmedia_aud_param *prm)
646{
647
648 pj_bzero(wfx, sizeof(WAVEFORMATEX));
649 if (prm->ext_fmt.id == PJMEDIA_FORMAT_L16) {
650 enum { BYTES_PER_SAMPLE = 2 };
651 wfx->wFormatTag = WAVE_FORMAT_PCM;
652 wfx->nChannels = (pj_uint16_t)prm->channel_count;
653 wfx->nSamplesPerSec = prm->clock_rate;
654 wfx->nBlockAlign = (pj_uint16_t)(prm->channel_count *
655 BYTES_PER_SAMPLE);
656 wfx->nAvgBytesPerSec = prm->clock_rate * prm->channel_count *
657 BYTES_PER_SAMPLE;
658 wfx->wBitsPerSample = 16;
659
660 return PJ_SUCCESS;
661
662 } else if ((prm->flags & PJMEDIA_AUD_DEV_CAP_EXT_FORMAT) &&
663 (prm->ext_fmt.id == PJMEDIA_FORMAT_PCMA ||
664 prm->ext_fmt.id == PJMEDIA_FORMAT_PCMU))
665 {
666 unsigned ptime;
667
668 ptime = prm->samples_per_frame * 1000 /
669 (prm->clock_rate * prm->channel_count);
670 wfx->wFormatTag = (pj_uint16_t)
671 ((prm->ext_fmt.id==PJMEDIA_FORMAT_PCMA) ?
672 WAVE_FORMAT_ALAW : WAVE_FORMAT_MULAW);
673 wfx->nChannels = (pj_uint16_t)prm->channel_count;
674 wfx->nSamplesPerSec = prm->clock_rate;
675 wfx->nAvgBytesPerSec = prm->clock_rate * prm->channel_count;
676 wfx->nBlockAlign = (pj_uint16_t)(wfx->nAvgBytesPerSec * ptime /
677 1000);
678 wfx->wBitsPerSample = 8;
679 wfx->cbSize = 0;
680
681 return PJ_SUCCESS;
682
683 } else {
684
685 return PJMEDIA_EAUD_BADFORMAT;
686
687 }
688}
689
690/* Get format name */
691static const char *get_fmt_name(pj_uint32_t id)
692{
693 static char name[8];
694
695 if (id == PJMEDIA_FORMAT_L16)
696 return "PCM";
697 pj_memcpy(name, &id, 4);
698 name[4] = '\0';
699 return name;
700}
701
702/* Internal: create WMME player device. */
703static pj_status_t init_player_stream( struct wmme_factory *wf,
704 pj_pool_t *pool,
705 struct wmme_stream *parent,
706 struct wmme_channel *wmme_strm,
707 const pjmedia_aud_param *prm,
708 unsigned buffer_count)
709{
710 MMRESULT mr;
711 WAVEFORMATEX wfx;
712 unsigned i, ptime;
713 DWORD flag;
714 pj_status_t status;
715
716 PJ_ASSERT_RETURN(prm->play_id < (int)wf->dev_count, PJ_EINVAL);
717
718 /*
719 * Create a wait event.
720 */
721 wmme_strm->hEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
722 if (NULL == wmme_strm->hEvent)
723 return pj_get_os_error();
724
725 /*
726 * Set up wave format structure for opening the device.
727 */
728 status = init_waveformatex(&wfx, prm);
729 if (status != PJ_SUCCESS)
730 return status;
731
732 ptime = prm->samples_per_frame * 1000 /
733 (prm->clock_rate * prm->channel_count);
734 parent->bytes_per_frame = wfx.nAvgBytesPerSec * ptime / 1000;
735
736 flag = CALLBACK_EVENT;
737 if (prm->ext_fmt.id == PJMEDIA_FORMAT_L16)
738 flag |= WAVE_FORMAT_DIRECT;
739
740 /*
741 * Open wave device.
742 */
743 mr = waveOutOpen(&wmme_strm->hWave.Out,
744 wf->dev_info[prm->play_id].deviceId,
745 &wfx, (DWORD)(pj_ssize_t)wmme_strm->hEvent, 0, flag);
746 if (mr != MMSYSERR_NOERROR) {
747 return PJMEDIA_AUDIODEV_ERRNO_FROM_WMME_OUT(mr);
748 }
749
750 /* Pause the wave out device */
751 mr = waveOutPause(wmme_strm->hWave.Out);
752 if (mr != MMSYSERR_NOERROR) {
753 return PJMEDIA_AUDIODEV_ERRNO_FROM_WMME_OUT(mr);
754 }
755
756 /*
757 * Create the buffers.
758 */
759 wmme_strm->WaveHdr = (WAVEHDR*)
760 pj_pool_zalloc(pool, sizeof(WAVEHDR) * buffer_count);
761 for (i = 0; i < buffer_count; ++i) {
762 wmme_strm->WaveHdr[i].lpData = pj_pool_zalloc(pool,
763 parent->bytes_per_frame);
764 wmme_strm->WaveHdr[i].dwBufferLength = parent->bytes_per_frame;
765 mr = waveOutPrepareHeader(wmme_strm->hWave.Out,
766 &(wmme_strm->WaveHdr[i]),
767 sizeof(WAVEHDR));
768 if (mr != MMSYSERR_NOERROR) {
769 return PJMEDIA_AUDIODEV_ERRNO_FROM_WMME_OUT(mr);
770 }
771 mr = waveOutWrite(wmme_strm->hWave.Out, &(wmme_strm->WaveHdr[i]),
772 sizeof(WAVEHDR));
773 if (mr != MMSYSERR_NOERROR) {
774 return PJMEDIA_AUDIODEV_ERRNO_FROM_WMME_OUT(mr);
775 }
776 }
777
778 wmme_strm->dwBufIdx = 0;
779 wmme_strm->dwMaxBufIdx = buffer_count;
780 wmme_strm->timestamp.u64 = 0;
781
782 /* Done setting up play device. */
783 PJ_LOG(4, (THIS_FILE,
784 " WaveAPI Sound player \"%s\" initialized ("
785 "format=%s, clock_rate=%d, "
786 "channel_count=%d, samples_per_frame=%d (%dms))",
787 wf->dev_info[prm->play_id].info.name,
788 get_fmt_name(prm->ext_fmt.id),
789 prm->clock_rate, prm->channel_count, prm->samples_per_frame,
790 prm->samples_per_frame * 1000 / prm->clock_rate));
791
792 return PJ_SUCCESS;
793}
794
795
796/* Internal: create Windows Multimedia recorder device */
797static pj_status_t init_capture_stream( struct wmme_factory *wf,
798 pj_pool_t *pool,
799 struct wmme_stream *parent,
800 struct wmme_channel *wmme_strm,
801 const pjmedia_aud_param *prm,
802 unsigned buffer_count)
803{
804 MMRESULT mr;
805 WAVEFORMATEX wfx;
806 DWORD flag;
807 unsigned i, ptime;
808
809 PJ_ASSERT_RETURN(prm->rec_id < (int)wf->dev_count, PJ_EINVAL);
810
811 /*
812 * Create a wait event.
813 */
814 wmme_strm->hEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
815 if (NULL == wmme_strm->hEvent) {
816 return pj_get_os_error();
817 }
818
819 /*
820 * Set up wave format structure for opening the device.
821 */
822 init_waveformatex(&wfx, prm);
823 ptime = prm->samples_per_frame * 1000 /
824 (prm->clock_rate * prm->channel_count);
825 parent->bytes_per_frame = wfx.nAvgBytesPerSec * ptime / 1000;
826
827 flag = CALLBACK_EVENT;
828 if (prm->ext_fmt.id == PJMEDIA_FORMAT_L16)
829 flag |= WAVE_FORMAT_DIRECT;
830
831 /*
832 * Open wave device.
833 */
834 mr = waveInOpen(&wmme_strm->hWave.In,
835 wf->dev_info[prm->rec_id].deviceId,
836 &wfx, (DWORD)(pj_ssize_t)wmme_strm->hEvent, 0, flag);
837 if (mr != MMSYSERR_NOERROR) {
838 return PJMEDIA_AUDIODEV_ERRNO_FROM_WMME_IN(mr);
839 }
840
841 /*
842 * Create the buffers.
843 */
844 wmme_strm->WaveHdr = (WAVEHDR*)
845 pj_pool_zalloc(pool, sizeof(WAVEHDR) * buffer_count);
846 for (i = 0; i < buffer_count; ++i) {
847 wmme_strm->WaveHdr[i].lpData = pj_pool_zalloc(pool,
848 parent->bytes_per_frame);
849 wmme_strm->WaveHdr[i].dwBufferLength = parent->bytes_per_frame;
850 mr = waveInPrepareHeader(wmme_strm->hWave.In,
851 &(wmme_strm->WaveHdr[i]),
852 sizeof(WAVEHDR));
853 if (mr != MMSYSERR_NOERROR) {
854 return PJMEDIA_AUDIODEV_ERRNO_FROM_WMME_IN(mr);
855 }
856 mr = waveInAddBuffer(wmme_strm->hWave.In, &(wmme_strm->WaveHdr[i]),
857 sizeof(WAVEHDR));
858 if (mr != MMSYSERR_NOERROR) {
859 return PJMEDIA_AUDIODEV_ERRNO_FROM_WMME_IN(mr);
860 }
861 }
862
863 wmme_strm->dwBufIdx = 0;
864 wmme_strm->dwMaxBufIdx = buffer_count;
865 wmme_strm->timestamp.u64 = 0;
866
867 /* Done setting up play device. */
868 PJ_LOG(4,(THIS_FILE,
869 " WaveAPI Sound recorder \"%s\" initialized "
870 "(format=%s, clock_rate=%d, "
871 "channel_count=%d, samples_per_frame=%d (%dms))",
872 wf->dev_info[prm->rec_id].info.name,
873 get_fmt_name(prm->ext_fmt.id),
874 prm->clock_rate, prm->channel_count, prm->samples_per_frame,
875 prm->samples_per_frame * 1000 / prm->clock_rate));
876
877 return PJ_SUCCESS;
878}
879
880
881/* WMME capture and playback thread. */
882static int PJ_THREAD_FUNC wmme_dev_thread(void *arg)
883{
884 struct wmme_stream *strm = (struct wmme_stream*)arg;
885 HANDLE events[3];
886 unsigned eventCount;
887 pj_status_t status = PJ_SUCCESS;
888 static unsigned rec_cnt, play_cnt;
889 enum { MAX_BURST = 1000 };
890
891 rec_cnt = play_cnt = 0;
892
893 eventCount = 0;
894 events[eventCount++] = strm->thread_quit_event;
895 if (strm->param.dir & PJMEDIA_DIR_PLAYBACK)
896 events[eventCount++] = strm->play_strm.hEvent;
897 if (strm->param.dir & PJMEDIA_DIR_CAPTURE)
898 events[eventCount++] = strm->rec_strm.hEvent;
899
900
901 /* Raise self priority. We don't want the audio to be distorted by
902 * system activity.
903 */
904#if defined(PJ_WIN32_WINCE) && PJ_WIN32_WINCE != 0
905 if (strm->param.dir & PJMEDIA_DIR_PLAYBACK)
906 CeSetThreadPriority(GetCurrentThread(), 153);
907 else
908 CeSetThreadPriority(GetCurrentThread(), 247);
909#else
910 SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_TIME_CRITICAL);
911#endif
912
913 /*
914 * Loop while not signalled to quit, wait for event objects to be
915 * signalled by WMME capture and play buffer.
916 */
917 while (status == PJ_SUCCESS)
918 {
919
920 DWORD rc;
921 pjmedia_dir signalled_dir;
922
923 /* Swap hWaveIn and hWaveOut to get equal opportunity for both */
924 if (eventCount==3) {
925 HANDLE hTemp = events[2];
926 events[2] = events[1];
927 events[1] = hTemp;
928 }
929
930 rc = WaitForMultipleObjects(eventCount, events, FALSE, INFINITE);
931 if (rc < WAIT_OBJECT_0 || rc >= WAIT_OBJECT_0 + eventCount)
932 continue;
933
934 if (rc == WAIT_OBJECT_0)
935 break;
936
937 if (rc == (WAIT_OBJECT_0 + 1))
938 {
939 if (events[1] == strm->play_strm.hEvent)
940 signalled_dir = PJMEDIA_DIR_PLAYBACK;
941 else
942 signalled_dir = PJMEDIA_DIR_CAPTURE;
943 }
944 else
945 {
946 if (events[2] == strm->play_strm.hEvent)
947 signalled_dir = PJMEDIA_DIR_PLAYBACK;
948 else
949 signalled_dir = PJMEDIA_DIR_CAPTURE;
950 }
951
952
953 if (signalled_dir == PJMEDIA_DIR_PLAYBACK)
954 {
955 struct wmme_channel *wmme_strm = &strm->play_strm;
956 unsigned burst;
957
958 status = PJ_SUCCESS;
959
960 /*
961 * Windows Multimedia has requested us to feed some frames to
962 * playback buffer.
963 */
964
965 for (burst=0; burst<MAX_BURST &&
966 (wmme_strm->WaveHdr[wmme_strm->dwBufIdx].dwFlags & WHDR_DONE);
967 ++burst)
968 {
969 void *buffer = wmme_strm->WaveHdr[wmme_strm->dwBufIdx].lpData;
970 pjmedia_frame pcm_frame, *frame;
971 MMRESULT mr = MMSYSERR_NOERROR;
972
973 //PJ_LOG(5,(THIS_FILE, "Finished writing buffer %d",
974 // wmme_strm->dwBufIdx));
975
976 if (strm->fmt_id == PJMEDIA_FORMAT_L16) {
977 /* PCM mode */
978 frame = &pcm_frame;
979
980 frame->type = PJMEDIA_FRAME_TYPE_AUDIO;
981 frame->size = strm->bytes_per_frame;
982 frame->buf = buffer;
983 frame->timestamp.u64 = wmme_strm->timestamp.u64;
984 frame->bit_info = 0;
985 } else {
986 /* Codec mode */
987 frame = &strm->xfrm->base;
988
989 strm->xfrm->base.type = PJMEDIA_FRAME_TYPE_EXTENDED;
990 strm->xfrm->base.size = strm->bytes_per_frame;
991 strm->xfrm->base.buf = NULL;
992 strm->xfrm->base.timestamp.u64 = wmme_strm->timestamp.u64;
993 strm->xfrm->base.bit_info = 0;
994 }
995
996 /* Get frame from application. */
997 //PJ_LOG(5,(THIS_FILE, "xxx %u play_cb", play_cnt++));
998 status = (*strm->play_cb)(strm->user_data, frame);
999
1000 if (status != PJ_SUCCESS)
1001 break;
1002
1003 if (strm->fmt_id == PJMEDIA_FORMAT_L16) {
1004 /* PCM mode */
1005 if (frame->type == PJMEDIA_FRAME_TYPE_NONE) {
1006 pj_bzero(buffer, strm->bytes_per_frame);
1007 } else if (frame->type == PJMEDIA_FRAME_TYPE_EXTENDED) {
1008 pj_assert(!"Frame type not supported");
1009 } else if (frame->type == PJMEDIA_FRAME_TYPE_AUDIO) {
1010 /* Nothing to do */
1011 } else {
1012 pj_assert(!"Frame type not supported");
1013 }
1014 } else {
1015 /* Codec mode */
1016 if (frame->type == PJMEDIA_FRAME_TYPE_NONE) {
1017 pj_memset(buffer, strm->silence_char,
1018 strm->bytes_per_frame);
1019 } else if (frame->type == PJMEDIA_FRAME_TYPE_EXTENDED) {
1020 unsigned sz;
1021 sz = pjmedia_frame_ext_copy_payload(strm->xfrm,
1022 buffer,
1023 strm->bytes_per_frame);
1024 if (sz < strm->bytes_per_frame) {
1025 pj_memset((char*)buffer+sz,
1026 strm->silence_char,
1027 strm->bytes_per_frame - sz);
1028 }
1029 } else {
1030 pj_assert(!"Frame type not supported");
1031 }
1032 }
1033
1034 /* Write to the device. */
1035 mr = waveOutWrite(wmme_strm->hWave.Out,
1036 &(wmme_strm->WaveHdr[wmme_strm->dwBufIdx]),
1037 sizeof(WAVEHDR));
1038 if (mr != MMSYSERR_NOERROR) {
1039 status = PJMEDIA_AUDIODEV_ERRNO_FROM_WMME_OUT(mr);
1040 break;
1041 }
1042
1043 /* Increment position. */
1044 if (++wmme_strm->dwBufIdx >= wmme_strm->dwMaxBufIdx)
1045 wmme_strm->dwBufIdx = 0;
1046 wmme_strm->timestamp.u64 += strm->param.samples_per_frame /
1047 strm->param.channel_count;
1048 } /* for */
1049 }
1050 else
1051 {
1052 struct wmme_channel *wmme_strm = &strm->rec_strm;
1053 unsigned burst;
1054 MMRESULT mr = MMSYSERR_NOERROR;
1055 status = PJ_SUCCESS;
1056
1057 /*
1058 * Windows Multimedia has indicated that it has some frames ready
1059 * in the capture buffer. Get as much frames as possible to
1060 * prevent overflows.
1061 */
1062#if 0
1063 {
1064 static DWORD tc = 0;
1065 DWORD now = GetTickCount();
1066 DWORD i = 0;
1067 DWORD bits = 0;
1068
1069 if (tc == 0) tc = now;
1070
1071 for (i = 0; i < wmme_strm->dwMaxBufIdx; ++i)
1072 {
1073 bits = bits << 4;
1074 bits |= wmme_strm->WaveHdr[i].dwFlags & WHDR_DONE;
1075 }
1076 PJ_LOG(5,(THIS_FILE, "Record Signal> Index: %d, Delta: %4.4d, "
1077 "Flags: %6.6x\n",
1078 wmme_strm->dwBufIdx,
1079 now - tc,
1080 bits));
1081 tc = now;
1082 }
1083#endif
1084
1085 for (burst=0; burst<MAX_BURST &&
1086 (wmme_strm->WaveHdr[wmme_strm->dwBufIdx].dwFlags & WHDR_DONE);
1087 ++burst)
1088 {
1089 char* buffer = (char*)
1090 wmme_strm->WaveHdr[wmme_strm->dwBufIdx].lpData;
1091 unsigned cap_len =
1092 wmme_strm->WaveHdr[wmme_strm->dwBufIdx].dwBytesRecorded;
1093 pjmedia_frame pcm_frame, *frame;
1094
1095 /*
1096 PJ_LOG(5,(THIS_FILE, "Read %d bytes from buffer %d", cap_len,
1097 wmme_strm->dwBufIdx));
1098 */
1099
1100 if (strm->fmt_id == PJMEDIA_FORMAT_L16) {
1101 /* PCM mode */
1102 if (cap_len < strm->bytes_per_frame)
1103 pj_bzero(buffer + cap_len,
1104 strm->bytes_per_frame - cap_len);
1105
1106 /* Copy the audio data out of the wave buffer. */
1107 pj_memcpy(strm->buffer, buffer, strm->bytes_per_frame);
1108
1109 /* Prepare frame */
1110 frame = &pcm_frame;
1111 frame->type = PJMEDIA_FRAME_TYPE_AUDIO;
1112 frame->buf = strm->buffer;
1113 frame->size = strm->bytes_per_frame;
1114 frame->timestamp.u64 = wmme_strm->timestamp.u64;
1115 frame->bit_info = 0;
1116
1117 } else {
1118 /* Codec mode */
1119 frame = &strm->xfrm->base;
1120
1121 frame->type = PJMEDIA_FRAME_TYPE_EXTENDED;
1122 frame->buf = NULL;
1123 frame->size = strm->bytes_per_frame;
1124 frame->timestamp.u64 = wmme_strm->timestamp.u64;
1125 frame->bit_info = 0;
1126
1127 strm->xfrm->samples_cnt = 0;
1128 strm->xfrm->subframe_cnt = 0;
1129 pjmedia_frame_ext_append_subframe(
1130 strm->xfrm, buffer,
1131 strm->bytes_per_frame *8,
1132 strm->param.samples_per_frame
1133 );
1134 }
1135
1136 /* Re-add the buffer to the device. */
1137 mr = waveInAddBuffer(wmme_strm->hWave.In,
1138 &(wmme_strm->WaveHdr[wmme_strm->dwBufIdx]),
1139 sizeof(WAVEHDR));
1140 if (mr != MMSYSERR_NOERROR) {
1141 status = PJMEDIA_AUDIODEV_ERRNO_FROM_WMME_IN(mr);
1142 break;
1143 }
1144
1145
1146 /* Call callback */
1147 //PJ_LOG(5,(THIS_FILE, "xxx %u rec_cb", rec_cnt++));
1148 status = (*strm->rec_cb)(strm->user_data, frame);
1149 if (status != PJ_SUCCESS)
1150 break;
1151
1152 /* Increment position. */
1153 if (++wmme_strm->dwBufIdx >= wmme_strm->dwMaxBufIdx)
1154 wmme_strm->dwBufIdx = 0;
1155 wmme_strm->timestamp.u64 += strm->param.samples_per_frame /
1156 strm->param.channel_count;
1157 } /* for */
1158 }
1159 }
1160
1161 PJ_LOG(5,(THIS_FILE, "WMME: thread stopping.."));
1162 return 0;
1163}
1164
1165
1166/* API: create stream */
1167static pj_status_t factory_create_stream(pjmedia_aud_dev_factory *f,
1168 const pjmedia_aud_param *param,
1169 pjmedia_aud_rec_cb rec_cb,
1170 pjmedia_aud_play_cb play_cb,
1171 void *user_data,
1172 pjmedia_aud_stream **p_aud_strm)
1173{
1174 struct wmme_factory *wf = (struct wmme_factory*)f;
1175 pj_pool_t *pool;
1176 struct wmme_stream *strm;
1177 pj_uint8_t silence_char;
1178 pj_status_t status;
1179
1180 switch (param->ext_fmt.id) {
1181 case PJMEDIA_FORMAT_L16:
1182 silence_char = '\0';
1183 break;
1184 case PJMEDIA_FORMAT_ALAW:
1185 silence_char = (pj_uint8_t)'\xd5';
1186 break;
1187 case PJMEDIA_FORMAT_ULAW:
1188 silence_char = (pj_uint8_t)'\xff';
1189 break;
1190 default:
1191 return PJMEDIA_EAUD_BADFORMAT;
1192 }
1193
1194 /* Create and Initialize stream descriptor */
1195 pool = pj_pool_create(wf->pf, "wmme-dev", 1000, 1000, NULL);
1196 PJ_ASSERT_RETURN(pool != NULL, PJ_ENOMEM);
1197
1198 strm = PJ_POOL_ZALLOC_T(pool, struct wmme_stream);
1199 pj_memcpy(&strm->param, param, sizeof(*param));
1200 strm->pool = pool;
1201 strm->rec_cb = rec_cb;
1202 strm->play_cb = play_cb;
1203 strm->user_data = user_data;
1204 strm->fmt_id = param->ext_fmt.id;
1205 strm->silence_char = silence_char;
1206
1207 /* Create player stream */
1208 if (param->dir & PJMEDIA_DIR_PLAYBACK) {
1209 unsigned buf_count;
1210
1211 if ((param->flags & PJMEDIA_AUD_DEV_CAP_OUTPUT_LATENCY)==0) {
1212 strm->param.flags |= PJMEDIA_AUD_DEV_CAP_OUTPUT_LATENCY;
1213 strm->param.output_latency_ms = PJMEDIA_SND_DEFAULT_PLAY_LATENCY;
1214 }
1215
1216 buf_count = strm->param.output_latency_ms * param->clock_rate *
1217 param->channel_count / param->samples_per_frame / 1000;
1218
1219 status = init_player_stream(wf, strm->pool,
1220 strm,
1221 &strm->play_strm,
1222 param,
1223 buf_count);
1224
1225 if (status != PJ_SUCCESS) {
1226 stream_destroy(&strm->base);
1227 return status;
1228 }
1229 }
1230
1231 /* Create capture stream */
1232 if (param->dir & PJMEDIA_DIR_CAPTURE) {
1233 unsigned buf_count;
1234
1235 if ((param->flags & PJMEDIA_AUD_DEV_CAP_INPUT_LATENCY)==0) {
1236 strm->param.flags |= PJMEDIA_AUD_DEV_CAP_INPUT_LATENCY;
1237 strm->param.input_latency_ms = PJMEDIA_SND_DEFAULT_REC_LATENCY;
1238 }
1239
1240 buf_count = strm->param.input_latency_ms * param->clock_rate *
1241 param->channel_count / param->samples_per_frame / 1000;
1242
1243 status = init_capture_stream(wf, strm->pool,
1244 strm,
1245 &strm->rec_strm,
1246 param,
1247 buf_count);
1248
1249 if (status != PJ_SUCCESS) {
1250 stream_destroy(&strm->base);
1251 return status;
1252 }
1253 }
1254
1255 strm->buffer = pj_pool_alloc(pool, strm->bytes_per_frame);
1256 if (!strm->buffer) {
1257 pj_pool_release(pool);
1258 return PJ_ENOMEM;
1259 }
1260
1261 /* If format is extended, must create buffer for the extended frame. */
1262 if (strm->fmt_id != PJMEDIA_FORMAT_L16) {
1263 strm->xfrm_size = sizeof(pjmedia_frame_ext) +
1264 32 * sizeof(pjmedia_frame_ext_subframe) +
1265 strm->bytes_per_frame + 4;
1266 strm->xfrm = (pjmedia_frame_ext*)
1267 pj_pool_alloc(pool, strm->xfrm_size);
1268 }
1269
1270 /* Create the stop event */
1271 strm->thread_quit_event = CreateEvent(NULL, FALSE, FALSE, NULL);
1272 if (strm->thread_quit_event == NULL) {
1273 status = pj_get_os_error();
1274 stream_destroy(&strm->base);
1275 return status;
1276 }
1277
1278 /* Create and start the thread */
1279 status = pj_thread_create(pool, "wmme", &wmme_dev_thread, strm, 0, 0,
1280 &strm->thread);
1281 if (status != PJ_SUCCESS) {
1282 stream_destroy(&strm->base);
1283 return status;
1284 }
1285
1286 /* Apply the remaining settings */
1287 if (param->flags & PJMEDIA_AUD_DEV_CAP_OUTPUT_VOLUME_SETTING) {
1288 stream_set_cap(&strm->base, PJMEDIA_AUD_DEV_CAP_OUTPUT_VOLUME_SETTING,
1289 &param->output_vol);
1290 }
1291
1292
1293 /* Done */
1294 strm->base.op = &stream_op;
1295 *p_aud_strm = &strm->base;
1296
1297 return PJ_SUCCESS;
1298}
1299
1300/* API: Get stream info. */
1301static pj_status_t stream_get_param(pjmedia_aud_stream *s,
1302 pjmedia_aud_param *pi)
1303{
1304 struct wmme_stream *strm = (struct wmme_stream*)s;
1305
1306 PJ_ASSERT_RETURN(strm && pi, PJ_EINVAL);
1307
1308 pj_memcpy(pi, &strm->param, sizeof(*pi));
1309
1310 /* Update the volume setting */
1311 if (stream_get_cap(s, PJMEDIA_AUD_DEV_CAP_OUTPUT_VOLUME_SETTING,
1312 &pi->output_vol) == PJ_SUCCESS)
1313 {
1314 pi->flags |= PJMEDIA_AUD_DEV_CAP_OUTPUT_VOLUME_SETTING;
1315 }
1316
1317 return PJ_SUCCESS;
1318}
1319
1320/* API: get capability */
1321static pj_status_t stream_get_cap(pjmedia_aud_stream *s,
1322 pjmedia_aud_dev_cap cap,
1323 void *pval)
1324{
1325 struct wmme_stream *strm = (struct wmme_stream*)s;
1326
1327 PJ_ASSERT_RETURN(s && pval, PJ_EINVAL);
1328
1329 if (cap==PJMEDIA_AUD_DEV_CAP_INPUT_LATENCY &&
1330 (strm->param.dir & PJMEDIA_DIR_CAPTURE))
1331 {
1332 /* Recording latency */
1333 *(unsigned*)pval = strm->param.input_latency_ms;
1334 return PJ_SUCCESS;
1335 } else if (cap==PJMEDIA_AUD_DEV_CAP_OUTPUT_LATENCY &&
1336 (strm->param.dir & PJMEDIA_DIR_PLAYBACK))
1337 {
1338 /* Playback latency */
1339 *(unsigned*)pval = strm->param.output_latency_ms;
1340 return PJ_SUCCESS;
1341 } else if (cap==PJMEDIA_AUD_DEV_CAP_OUTPUT_VOLUME_SETTING &&
1342 strm->play_strm.hWave.Out)
1343 {
1344 /* Output volume setting */
1345 DWORD waveVol;
1346 MMRESULT mr;
1347
1348 mr = waveOutGetVolume(strm->play_strm.hWave.Out, &waveVol);
1349 if (mr != MMSYSERR_NOERROR) {
1350 return PJMEDIA_AUDIODEV_ERRNO_FROM_WMME_OUT(mr);
1351 }
1352
1353 waveVol &= 0xFFFF;
1354 *(unsigned*)pval = (waveVol * 100) / 0xFFFF;
1355 return PJ_SUCCESS;
1356 } else {
1357 return PJMEDIA_EAUD_INVCAP;
1358 }
1359}
1360
1361/* API: set capability */
1362static pj_status_t stream_set_cap(pjmedia_aud_stream *s,
1363 pjmedia_aud_dev_cap cap,
1364 const void *pval)
1365{
1366 struct wmme_stream *strm = (struct wmme_stream*)s;
1367
1368 PJ_ASSERT_RETURN(s && pval, PJ_EINVAL);
1369
1370 if (cap==PJMEDIA_AUD_DEV_CAP_OUTPUT_VOLUME_SETTING &&
1371 strm->play_strm.hWave.Out)
1372 {
1373 /* Output volume setting */
1374 unsigned vol = *(unsigned*)pval;
1375 DWORD waveVol;
1376 MMRESULT mr;
1377 pj_status_t status;
1378
1379 if (vol > 100)
1380 vol = 100;
1381
1382 waveVol = (vol * 0xFFFF) / 100;
1383 waveVol |= (waveVol << 16);
1384
1385 mr = waveOutSetVolume(strm->play_strm.hWave.Out, waveVol);
1386 status = (mr==MMSYSERR_NOERROR)? PJ_SUCCESS :
1387 PJMEDIA_AUDIODEV_ERRNO_FROM_WMME_OUT(mr);
1388 if (status == PJ_SUCCESS) {
1389 strm->param.output_vol = *(unsigned*)pval;
1390 }
1391 return status;
1392 }
1393
1394 return PJMEDIA_EAUD_INVCAP;
1395}
1396
1397/* API: Start stream. */
1398static pj_status_t stream_start(pjmedia_aud_stream *strm)
1399{
1400 struct wmme_stream *stream = (struct wmme_stream*)strm;
1401 MMRESULT mr;
1402
1403 if (stream->play_strm.hWave.Out != NULL)
1404 {
1405 mr = waveOutRestart(stream->play_strm.hWave.Out);
1406 if (mr != MMSYSERR_NOERROR) {
1407 return PJMEDIA_AUDIODEV_ERRNO_FROM_WMME_OUT(mr);
1408 }
1409 PJ_LOG(4,(THIS_FILE, "WMME playback stream started"));
1410 }
1411
1412 if (stream->rec_strm.hWave.In != NULL)
1413 {
1414 mr = waveInStart(stream->rec_strm.hWave.In);
1415 if (mr != MMSYSERR_NOERROR) {
1416 return PJMEDIA_AUDIODEV_ERRNO_FROM_WMME_IN(mr);
1417 }
1418 PJ_LOG(4,(THIS_FILE, "WMME capture stream started"));
1419 }
1420
1421 return PJ_SUCCESS;
1422}
1423
1424/* API: Stop stream. */
1425static pj_status_t stream_stop(pjmedia_aud_stream *strm)
1426{
1427 struct wmme_stream *stream = (struct wmme_stream*)strm;
1428 MMRESULT mr;
1429
1430 PJ_ASSERT_RETURN(stream != NULL, PJ_EINVAL);
1431
1432 if (stream->play_strm.hWave.Out != NULL)
1433 {
1434 mr = waveOutPause(stream->play_strm.hWave.Out);
1435 if (mr != MMSYSERR_NOERROR) {
1436 return PJMEDIA_AUDIODEV_ERRNO_FROM_WMME_OUT(mr);
1437 }
1438 PJ_LOG(4,(THIS_FILE, "Stopped WMME playback stream"));
1439 }
1440
1441 if (stream->rec_strm.hWave.In != NULL)
1442 {
1443 mr = waveInStop(stream->rec_strm.hWave.In);
1444 if (mr != MMSYSERR_NOERROR) {
1445 return PJMEDIA_AUDIODEV_ERRNO_FROM_WMME_IN(mr);
1446 }
1447 PJ_LOG(4,(THIS_FILE, "Stopped WMME capture stream"));
1448 }
1449
1450 return PJ_SUCCESS;
1451}
1452
1453
1454/* API: Destroy stream. */
1455static pj_status_t stream_destroy(pjmedia_aud_stream *strm)
1456{
1457 struct wmme_stream *stream = (struct wmme_stream*)strm;
1458 unsigned i;
1459
1460 PJ_ASSERT_RETURN(stream != NULL, PJ_EINVAL);
1461
1462 stream_stop(strm);
1463
1464 /* Stop the stream thread */
1465 if (stream->thread)
1466 {
1467 SetEvent(stream->thread_quit_event);
1468 pj_thread_join(stream->thread);
1469 pj_thread_destroy(stream->thread);
1470 stream->thread = NULL;
1471 }
1472
1473 /* Close the thread quit event */
1474 if (stream->thread_quit_event)
1475 {
1476 CloseHandle(stream->thread_quit_event);
1477 stream->thread_quit_event = NULL;
1478 }
1479
1480 /* Unprepare the headers and close the play device */
1481 if (stream->play_strm.hWave.Out)
1482 {
1483 waveOutReset(stream->play_strm.hWave.Out);
1484 for (i = 0; i < stream->play_strm.dwMaxBufIdx; ++i)
1485 waveOutUnprepareHeader(stream->play_strm.hWave.Out,
1486 &(stream->play_strm.WaveHdr[i]),
1487 sizeof(WAVEHDR));
1488 waveOutClose(stream->play_strm.hWave.Out);
1489 stream->play_strm.hWave.Out = NULL;
1490 }
1491
1492 /* Close the play event */
1493 if (stream->play_strm.hEvent)
1494 {
1495 CloseHandle(stream->play_strm.hEvent);
1496 stream->play_strm.hEvent = NULL;
1497 }
1498
1499 /* Unprepare the headers and close the record device */
1500 if (stream->rec_strm.hWave.In)
1501 {
1502 waveInReset(stream->rec_strm.hWave.In);
1503 for (i = 0; i < stream->play_strm.dwMaxBufIdx; ++i)
1504 waveInUnprepareHeader(stream->rec_strm.hWave.In,
1505 &(stream->rec_strm.WaveHdr[i]),
1506 sizeof(WAVEHDR));
1507 waveInClose(stream->rec_strm.hWave.In);
1508 stream->rec_strm.hWave.In = NULL;
1509 }
1510
1511 /* Close the record event */
1512 if (stream->rec_strm.hEvent)
1513 {
1514 CloseHandle(stream->rec_strm.hEvent);
1515 stream->rec_strm.hEvent = NULL;
1516 }
1517
1518 pj_pool_release(stream->pool);
1519
1520 return PJ_SUCCESS;
1521}
1522
1523#endif /* PJMEDIA_AUDIO_DEV_HAS_WMME */
1524