blob: c69c5c82ec8483bd5c8f9208fb7832ebf37d1451 [file] [log] [blame]
Benny Prijono598b01d2009-02-18 13:55:03 +00001/* $Id$ */
2/*
3 * Copyright (C) 2008-2009 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>
Benny Prijono598b01d2009-02-18 13:55:03 +000021#include <pj/assert.h>
22#include <pj/log.h>
23#include <pj/os.h>
24#include <pj/string.h>
25#include <pj/unicode.h>
Benny Prijono8eeab0b2009-03-04 19:00:28 +000026
27#if PJMEDIA_AUDIO_DEV_HAS_WMME
28
Benny Prijono598b01d2009-02-18 13:55:03 +000029#ifdef _MSC_VER
30# pragma warning(push, 3)
31#endif
32
33#include <windows.h>
34#include <mmsystem.h>
Benny Prijono2058f472009-02-22 17:15:34 +000035#include <mmreg.h>
Benny Prijono598b01d2009-02-18 13:55:03 +000036
37#ifdef _MSC_VER
38# pragma warning(pop)
39#endif
40
Benny Prijono9920dc32009-03-12 18:11:37 +000041/* mingw lacks WAVE_FORMAT_ALAW/MULAW */
42#ifndef WAVE_FORMAT_ALAW
43# define WAVE_FORMAT_ALAW 0x0006
44#endif
45#ifndef WAVE_FORMAT_MULAW
46# define WAVE_FORMAT_MULAW 0x0007
47#endif
48
Benny Prijono598b01d2009-02-18 13:55:03 +000049#if defined(PJ_WIN32_WINCE) && PJ_WIN32_WINCE!=0
50# pragma comment(lib, "Coredll.lib")
51#elif defined(_MSC_VER)
52# pragma comment(lib, "winmm.lib")
53#endif
54
55
56#define THIS_FILE "wmme_dev.c"
Benny Prijono598b01d2009-02-18 13:55:03 +000057
Benny Prijono598b01d2009-02-18 13:55:03 +000058/* WMME device info */
59struct wmme_dev_info
60{
61 pjmedia_aud_dev_info info;
62 unsigned deviceId;
63};
64
65/* WMME factory */
66struct wmme_factory
67{
68 pjmedia_aud_dev_factory base;
69 pj_pool_t *pool;
70 pj_pool_factory *pf;
71
72 unsigned dev_count;
73 struct wmme_dev_info *dev_info;
74};
75
76
77/* Individual WMME capture/playback stream descriptor */
78struct wmme_channel
79{
80 union
81 {
82 HWAVEIN In;
83 HWAVEOUT Out;
84 } hWave;
85
86 WAVEHDR *WaveHdr;
87 HANDLE hEvent;
88 DWORD dwBufIdx;
89 DWORD dwMaxBufIdx;
Benny Prijono598b01d2009-02-18 13:55:03 +000090 pj_timestamp timestamp;
91};
92
93
94/* Sound stream. */
95struct wmme_stream
96{
Benny Prijono7a380002009-03-09 12:55:29 +000097 pjmedia_aud_stream base; /**< Base stream */
98 pjmedia_aud_param param; /**< Settings */
Benny Prijono598b01d2009-02-18 13:55:03 +000099 pj_pool_t *pool; /**< Memory pool. */
100
101 pjmedia_aud_rec_cb rec_cb; /**< Capture callback. */
102 pjmedia_aud_play_cb play_cb; /**< Playback callback. */
103 void *user_data; /**< Application data. */
104
105 struct wmme_channel play_strm; /**< Playback stream. */
106 struct wmme_channel rec_strm; /**< Capture stream. */
107
108 void *buffer; /**< Temp. frame buffer. */
Benny Prijono5fad7be2009-02-22 21:33:20 +0000109 pjmedia_format_id fmt_id; /**< Frame format */
110 pj_uint8_t silence_char; /**< Silence pattern */
Benny Prijono2058f472009-02-22 17:15:34 +0000111 unsigned bytes_per_frame; /**< Bytes per frame */
Benny Prijono598b01d2009-02-18 13:55:03 +0000112
Benny Prijono2058f472009-02-22 17:15:34 +0000113 pjmedia_frame_ext *xfrm; /**< Extended frame buffer */
114 unsigned xfrm_size; /**< Total ext frm size */
115
Benny Prijono598b01d2009-02-18 13:55:03 +0000116 pj_thread_t *thread; /**< Thread handle. */
117 HANDLE thread_quit_event; /**< Quit signal to thread */
118};
119
120
121/* Prototypes */
122static pj_status_t factory_init(pjmedia_aud_dev_factory *f);
123static pj_status_t factory_destroy(pjmedia_aud_dev_factory *f);
124static unsigned factory_get_dev_count(pjmedia_aud_dev_factory *f);
125static pj_status_t factory_get_dev_info(pjmedia_aud_dev_factory *f,
126 unsigned index,
127 pjmedia_aud_dev_info *info);
128static pj_status_t factory_default_param(pjmedia_aud_dev_factory *f,
129 unsigned index,
Benny Prijono10454dc2009-02-21 14:21:59 +0000130 pjmedia_aud_param *param);
Benny Prijono598b01d2009-02-18 13:55:03 +0000131static pj_status_t factory_create_stream(pjmedia_aud_dev_factory *f,
Benny Prijono10454dc2009-02-21 14:21:59 +0000132 const pjmedia_aud_param *param,
Benny Prijono598b01d2009-02-18 13:55:03 +0000133 pjmedia_aud_rec_cb rec_cb,
134 pjmedia_aud_play_cb play_cb,
135 void *user_data,
136 pjmedia_aud_stream **p_aud_strm);
137
138static pj_status_t stream_get_param(pjmedia_aud_stream *strm,
Benny Prijono10454dc2009-02-21 14:21:59 +0000139 pjmedia_aud_param *param);
Benny Prijono598b01d2009-02-18 13:55:03 +0000140static pj_status_t stream_get_cap(pjmedia_aud_stream *strm,
141 pjmedia_aud_dev_cap cap,
142 void *value);
143static pj_status_t stream_set_cap(pjmedia_aud_stream *strm,
144 pjmedia_aud_dev_cap cap,
145 const void *value);
146static pj_status_t stream_start(pjmedia_aud_stream *strm);
147static pj_status_t stream_stop(pjmedia_aud_stream *strm);
148static pj_status_t stream_destroy(pjmedia_aud_stream *strm);
149
150
151/* Operations */
152static pjmedia_aud_dev_factory_op factory_op =
153{
154 &factory_init,
155 &factory_destroy,
156 &factory_get_dev_count,
157 &factory_get_dev_info,
158 &factory_default_param,
159 &factory_create_stream
160};
161
162static pjmedia_aud_stream_op stream_op =
163{
164 &stream_get_param,
165 &stream_get_cap,
166 &stream_set_cap,
167 &stream_start,
168 &stream_stop,
169 &stream_destroy
170};
171
Benny Prijono598b01d2009-02-18 13:55:03 +0000172
173/****************************************************************************
174 * Factory operations
175 */
176/*
177 * Init WMME audio driver.
178 */
179pjmedia_aud_dev_factory* pjmedia_wmme_factory(pj_pool_factory *pf)
180{
181 struct wmme_factory *f;
182 pj_pool_t *pool;
183
184 pool = pj_pool_create(pf, "WMME", 1000, 1000, NULL);
185 f = PJ_POOL_ZALLOC_T(pool, struct wmme_factory);
186 f->pf = pf;
187 f->pool = pool;
188 f->base.op = &factory_op;
189
190 return &f->base;
191}
192
193
194/* Internal: build device info from WAVEINCAPS/WAVEOUTCAPS */
195static void build_dev_info(UINT deviceId, struct wmme_dev_info *wdi,
Benny Prijono2058f472009-02-22 17:15:34 +0000196 const WAVEINCAPS *wic, const WAVEOUTCAPS *woc)
Benny Prijono598b01d2009-02-18 13:55:03 +0000197{
198#define WIC_WOC(wic,woc,field) (wic? wic->field : woc->field)
199
200 pj_bzero(wdi, sizeof(*wdi));
201 wdi->deviceId = deviceId;
202
203 /* Device Name */
204 if (deviceId==WAVE_MAPPER) {
205 strncpy(wdi->info.name, "Wave mapper", sizeof(wdi->info.name));
206 wdi->info.name[sizeof(wdi->info.name)-1] = '\0';
207 } else {
Benny Prijono2058f472009-02-22 17:15:34 +0000208 const pj_char_t *szPname = WIC_WOC(wic, woc, szPname);
Benny Prijono598b01d2009-02-18 13:55:03 +0000209 PJ_DECL_ANSI_TEMP_BUF(wTmp, sizeof(wdi->info.name));
210
211 strncpy(wdi->info.name,
212 PJ_NATIVE_TO_STRING(szPname, wTmp, PJ_ARRAY_SIZE(wTmp)),
213 sizeof(wdi->info.name));
214 wdi->info.name[sizeof(wdi->info.name)-1] = '\0';
215 }
216
217 wdi->info.default_samples_per_sec = 16000;
218 strcpy(wdi->info.driver, "WMME");
219
220 if (wic) {
221 wdi->info.input_count = wic->wChannels;
222 wdi->info.caps |= PJMEDIA_AUD_DEV_CAP_INPUT_LATENCY;
223
224 /* Sometimes a device can return a rediculously large number of
225 * channels. This happened with an SBLive card on a Windows ME box.
226 * It also happens on Win XP!
227 */
228 if (wdi->info.input_count<1 || wdi->info.input_count>256) {
229 wdi->info.input_count = 2;
230 }
231 }
232
233 if (woc) {
234 wdi->info.output_count = woc->wChannels;
235 wdi->info.caps |= PJMEDIA_AUD_DEV_CAP_OUTPUT_LATENCY;
236
237 if (woc->dwSupport & WAVECAPS_VOLUME) {
238 wdi->info.caps |= PJMEDIA_AUD_DEV_CAP_OUTPUT_VOLUME_SETTING;
239 }
240
241 /* Sometimes a device can return a rediculously large number of
242 * channels. This happened with an SBLive card on a Windows ME box.
243 * It also happens on Win XP!
244 */
245 if (wdi->info.output_count<1 || wdi->info.output_count>256) {
246 wdi->info.output_count = 2;
247 }
248 }
Benny Prijono2058f472009-02-22 17:15:34 +0000249
250 /* Extended formats */
251 wdi->info.caps |= PJMEDIA_AUD_DEV_CAP_EXT_FORMAT;
252 wdi->info.ext_fmt_cnt = 2;
Benny Prijonoc45d9512010-12-10 11:04:30 +0000253 pjmedia_format_init_audio(&wdi->info.ext_fmt[0],
254 PJMEDIA_FORMAT_PCMU, 8000, 1, 8,
255 20000, 64000, 64000);
256 pjmedia_format_init_audio(&wdi->info.ext_fmt[0],
257 PJMEDIA_FORMAT_PCMA, 8000, 1, 8,
258 20000, 64000, 64000);
Benny Prijono598b01d2009-02-18 13:55:03 +0000259}
260
261/* API: init factory */
262static pj_status_t factory_init(pjmedia_aud_dev_factory *f)
263{
264 struct wmme_factory *wf = (struct wmme_factory*)f;
265 unsigned c;
266 int i;
267 int inputDeviceCount, outputDeviceCount, devCount=0;
268 pj_bool_t waveMapperAdded = PJ_FALSE;
269
270 /* Enumerate sound devices */
271 wf->dev_count = 0;
272
273 inputDeviceCount = waveInGetNumDevs();
274 devCount += inputDeviceCount;
275
276 outputDeviceCount = waveOutGetNumDevs();
277 devCount += outputDeviceCount;
278
279 if (devCount) {
280 /* Assume there is WAVE_MAPPER */
281 devCount += 2;
282 }
283
284 if (devCount==0) {
285 PJ_LOG(4,(THIS_FILE, "WMME found no sound devices"));
Nanang Izzuddin0e7071c2009-10-13 11:19:57 +0000286 /* Enabling this will cause pjsua-lib initialization to fail when there
287 * is no sound device installed in the system, even when pjsua has been
288 * run with --null-audio. Moreover, it might be better to think that
289 * the WMME backend initialization is successfull, regardless there is
290 * no audio device installed, as later application can check it using
291 * get_dev_count().
Benny Prijono598b01d2009-02-18 13:55:03 +0000292 return PJMEDIA_EAUD_NODEV;
Nanang Izzuddin0e7071c2009-10-13 11:19:57 +0000293 */
294 return PJ_SUCCESS;
Benny Prijono598b01d2009-02-18 13:55:03 +0000295 }
296
297 wf->dev_info = (struct wmme_dev_info*)
298 pj_pool_calloc(wf->pool, devCount,
299 sizeof(struct wmme_dev_info));
300
Benny Prijono2058f472009-02-22 17:15:34 +0000301 if (inputDeviceCount && outputDeviceCount) {
Benny Prijono598b01d2009-02-18 13:55:03 +0000302 /* Attempt to add WAVE_MAPPER as input and output device */
303 WAVEINCAPS wic;
304 MMRESULT mr;
305
306 pj_bzero(&wic, sizeof(WAVEINCAPS));
307 mr = waveInGetDevCaps(WAVE_MAPPER, &wic, sizeof(WAVEINCAPS));
308
309 if (mr == MMSYSERR_NOERROR) {
310 WAVEOUTCAPS woc;
311
312 pj_bzero(&woc, sizeof(WAVEOUTCAPS));
313 mr = waveOutGetDevCaps(WAVE_MAPPER, &woc, sizeof(WAVEOUTCAPS));
314 if (mr == MMSYSERR_NOERROR) {
315 build_dev_info(WAVE_MAPPER, &wf->dev_info[wf->dev_count],
316 &wic, &woc);
317 ++wf->dev_count;
318 waveMapperAdded = PJ_TRUE;
319 }
320 }
321
322 }
323
324 if (inputDeviceCount > 0) {
325 /* -1 is the WAVE_MAPPER */
326 for (i = (waveMapperAdded? 0 : -1); i < inputDeviceCount; ++i) {
327 UINT uDeviceID = (UINT)((i==-1) ? WAVE_MAPPER : i);
328 WAVEINCAPS wic;
329 MMRESULT mr;
330
331 pj_bzero(&wic, sizeof(WAVEINCAPS));
332
333 mr = waveInGetDevCaps(uDeviceID, &wic, sizeof(WAVEINCAPS));
334
335 if (mr == MMSYSERR_NOMEM)
336 return PJ_ENOMEM;
337
338 if (mr != MMSYSERR_NOERROR)
339 continue;
340
341 build_dev_info(uDeviceID, &wf->dev_info[wf->dev_count],
342 &wic, NULL);
343 ++wf->dev_count;
344 }
345 }
346
347 if( outputDeviceCount > 0 )
348 {
349 /* -1 is the WAVE_MAPPER */
350 for (i = (waveMapperAdded? 0 : -1); i < outputDeviceCount; ++i) {
351 UINT uDeviceID = (UINT)((i==-1) ? WAVE_MAPPER : i);
352 WAVEOUTCAPS woc;
353 MMRESULT mr;
354
355 pj_bzero(&woc, sizeof(WAVEOUTCAPS));
356
357 mr = waveOutGetDevCaps(uDeviceID, &woc, sizeof(WAVEOUTCAPS));
358
359 if (mr == MMSYSERR_NOMEM)
360 return PJ_ENOMEM;
361
362 if (mr != MMSYSERR_NOERROR)
363 continue;
364
365 build_dev_info(uDeviceID, &wf->dev_info[wf->dev_count],
366 NULL, &woc);
367 ++wf->dev_count;
368 }
369 }
370
371 PJ_LOG(4, (THIS_FILE, "WMME initialized, found %d devices:",
372 wf->dev_count));
373 for (c = 0; c < wf->dev_count; ++c) {
374 PJ_LOG(4, (THIS_FILE, " dev_id %d: %s (in=%d, out=%d)",
375 c,
376 wf->dev_info[c].info.name,
377 wf->dev_info[c].info.input_count,
378 wf->dev_info[c].info.output_count));
379 }
380
381 return PJ_SUCCESS;
382}
383
384/* API: destroy factory */
385static pj_status_t factory_destroy(pjmedia_aud_dev_factory *f)
386{
387 struct wmme_factory *wf = (struct wmme_factory*)f;
388 pj_pool_t *pool = wf->pool;
389
390 wf->pool = NULL;
391 pj_pool_release(pool);
392
393 return PJ_SUCCESS;
394}
395
396/* API: get number of devices */
397static unsigned factory_get_dev_count(pjmedia_aud_dev_factory *f)
398{
399 struct wmme_factory *wf = (struct wmme_factory*)f;
400 return wf->dev_count;
401}
402
403/* API: get device info */
404static pj_status_t factory_get_dev_info(pjmedia_aud_dev_factory *f,
405 unsigned index,
406 pjmedia_aud_dev_info *info)
407{
408 struct wmme_factory *wf = (struct wmme_factory*)f;
409
410 PJ_ASSERT_RETURN(index < wf->dev_count, PJMEDIA_EAUD_INVDEV);
411
412 pj_memcpy(info, &wf->dev_info[index].info, sizeof(*info));
413
414 return PJ_SUCCESS;
415}
416
417/* API: create default device parameter */
418static pj_status_t factory_default_param(pjmedia_aud_dev_factory *f,
419 unsigned index,
Benny Prijono10454dc2009-02-21 14:21:59 +0000420 pjmedia_aud_param *param)
Benny Prijono598b01d2009-02-18 13:55:03 +0000421{
422 struct wmme_factory *wf = (struct wmme_factory*)f;
423 struct wmme_dev_info *di = &wf->dev_info[index];
424
425 PJ_ASSERT_RETURN(index < wf->dev_count, PJMEDIA_EAUD_INVDEV);
426
427 pj_bzero(param, sizeof(*param));
428 if (di->info.input_count && di->info.output_count) {
429 param->dir = PJMEDIA_DIR_CAPTURE_PLAYBACK;
430 param->rec_id = index;
431 param->play_id = index;
432 } else if (di->info.input_count) {
433 param->dir = PJMEDIA_DIR_CAPTURE;
434 param->rec_id = index;
Benny Prijono96e74f32009-02-22 12:00:12 +0000435 param->play_id = PJMEDIA_AUD_INVALID_DEV;
Benny Prijono598b01d2009-02-18 13:55:03 +0000436 } else if (di->info.output_count) {
437 param->dir = PJMEDIA_DIR_PLAYBACK;
438 param->play_id = index;
Benny Prijono96e74f32009-02-22 12:00:12 +0000439 param->rec_id = PJMEDIA_AUD_INVALID_DEV;
Benny Prijono598b01d2009-02-18 13:55:03 +0000440 } else {
441 return PJMEDIA_EAUD_INVDEV;
442 }
443
444 param->clock_rate = di->info.default_samples_per_sec;
445 param->channel_count = 1;
446 param->samples_per_frame = di->info.default_samples_per_sec * 20 / 1000;
447 param->bits_per_sample = 16;
Benny Prijono7a380002009-03-09 12:55:29 +0000448 param->flags = PJMEDIA_AUD_DEV_CAP_INPUT_LATENCY |
449 PJMEDIA_AUD_DEV_CAP_OUTPUT_LATENCY;
Benny Prijono598b01d2009-02-18 13:55:03 +0000450 param->input_latency_ms = PJMEDIA_SND_DEFAULT_REC_LATENCY;
451 param->output_latency_ms = PJMEDIA_SND_DEFAULT_PLAY_LATENCY;
452
453 return PJ_SUCCESS;
454}
455
456/* Internal: init WAVEFORMATEX */
Benny Prijono2058f472009-02-22 17:15:34 +0000457static pj_status_t init_waveformatex(LPWAVEFORMATEX wfx,
458 const pjmedia_aud_param *prm)
Benny Prijono598b01d2009-02-18 13:55:03 +0000459{
Benny Prijono2058f472009-02-22 17:15:34 +0000460
Nanang Izzuddinfa17c852009-10-05 18:13:08 +0000461 pj_bzero(wfx, sizeof(WAVEFORMATEX));
Benny Prijono2058f472009-02-22 17:15:34 +0000462 if (prm->ext_fmt.id == PJMEDIA_FORMAT_L16) {
Benny Prijono5fad7be2009-02-22 21:33:20 +0000463 enum { BYTES_PER_SAMPLE = 2 };
Benny Prijono2058f472009-02-22 17:15:34 +0000464 wfx->wFormatTag = WAVE_FORMAT_PCM;
465 wfx->nChannels = (pj_uint16_t)prm->channel_count;
466 wfx->nSamplesPerSec = prm->clock_rate;
467 wfx->nBlockAlign = (pj_uint16_t)(prm->channel_count *
468 BYTES_PER_SAMPLE);
469 wfx->nAvgBytesPerSec = prm->clock_rate * prm->channel_count *
470 BYTES_PER_SAMPLE;
Benny Prijono5fad7be2009-02-22 21:33:20 +0000471 wfx->wBitsPerSample = 16;
Benny Prijono2058f472009-02-22 17:15:34 +0000472
473 return PJ_SUCCESS;
474
475 } else if ((prm->flags & PJMEDIA_AUD_DEV_CAP_EXT_FORMAT) &&
476 (prm->ext_fmt.id == PJMEDIA_FORMAT_PCMA ||
477 prm->ext_fmt.id == PJMEDIA_FORMAT_PCMU))
478 {
479 unsigned ptime;
480
481 ptime = prm->samples_per_frame * 1000 /
482 (prm->clock_rate * prm->channel_count);
483 wfx->wFormatTag = (pj_uint16_t)
484 ((prm->ext_fmt.id==PJMEDIA_FORMAT_PCMA) ?
485 WAVE_FORMAT_ALAW : WAVE_FORMAT_MULAW);
486 wfx->nChannels = (pj_uint16_t)prm->channel_count;
487 wfx->nSamplesPerSec = prm->clock_rate;
488 wfx->nAvgBytesPerSec = prm->clock_rate * prm->channel_count;
489 wfx->nBlockAlign = (pj_uint16_t)(wfx->nAvgBytesPerSec * ptime /
490 1000);
491 wfx->wBitsPerSample = 8;
492 wfx->cbSize = 0;
493
494 return PJ_SUCCESS;
495
496 } else {
497
498 return PJMEDIA_EAUD_BADFORMAT;
499
500 }
Benny Prijono598b01d2009-02-18 13:55:03 +0000501}
502
Benny Prijono5fad7be2009-02-22 21:33:20 +0000503/* Get format name */
504static const char *get_fmt_name(pj_uint32_t id)
505{
506 static char name[8];
507
508 if (id == PJMEDIA_FORMAT_L16)
Benny Prijono7a380002009-03-09 12:55:29 +0000509 return "PCM";
Benny Prijono5fad7be2009-02-22 21:33:20 +0000510 pj_memcpy(name, &id, 4);
511 name[4] = '\0';
512 return name;
513}
Benny Prijono598b01d2009-02-18 13:55:03 +0000514
515/* Internal: create WMME player device. */
516static pj_status_t init_player_stream( struct wmme_factory *wf,
517 pj_pool_t *pool,
Benny Prijono2058f472009-02-22 17:15:34 +0000518 struct wmme_stream *parent,
Benny Prijono598b01d2009-02-18 13:55:03 +0000519 struct wmme_channel *wmme_strm,
Benny Prijono2058f472009-02-22 17:15:34 +0000520 const pjmedia_aud_param *prm,
Benny Prijono598b01d2009-02-18 13:55:03 +0000521 unsigned buffer_count)
522{
523 MMRESULT mr;
Benny Prijono2058f472009-02-22 17:15:34 +0000524 WAVEFORMATEX wfx;
525 unsigned i, ptime;
Benny Prijonocdf2c9c2009-07-18 09:21:09 +0000526 DWORD flag;
Benny Prijono2058f472009-02-22 17:15:34 +0000527 pj_status_t status;
Benny Prijono598b01d2009-02-18 13:55:03 +0000528
Benny Prijono2058f472009-02-22 17:15:34 +0000529 PJ_ASSERT_RETURN(prm->play_id < (int)wf->dev_count, PJ_EINVAL);
Benny Prijono598b01d2009-02-18 13:55:03 +0000530
531 /*
532 * Create a wait event.
533 */
534 wmme_strm->hEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
535 if (NULL == wmme_strm->hEvent)
536 return pj_get_os_error();
537
538 /*
539 * Set up wave format structure for opening the device.
540 */
Benny Prijono2058f472009-02-22 17:15:34 +0000541 status = init_waveformatex(&wfx, prm);
542 if (status != PJ_SUCCESS)
543 return status;
544
545 ptime = prm->samples_per_frame * 1000 /
546 (prm->clock_rate * prm->channel_count);
Benny Prijono5fad7be2009-02-22 21:33:20 +0000547 parent->bytes_per_frame = wfx.nAvgBytesPerSec * ptime / 1000;
Benny Prijono598b01d2009-02-18 13:55:03 +0000548
Benny Prijonocdf2c9c2009-07-18 09:21:09 +0000549 flag = CALLBACK_EVENT;
550 if (prm->ext_fmt.id == PJMEDIA_FORMAT_L16)
551 flag |= WAVE_FORMAT_DIRECT;
552
Benny Prijono598b01d2009-02-18 13:55:03 +0000553 /*
554 * Open wave device.
555 */
Benny Prijono2058f472009-02-22 17:15:34 +0000556 mr = waveOutOpen(&wmme_strm->hWave.Out,
557 wf->dev_info[prm->play_id].deviceId,
Benny Prijonocdf2c9c2009-07-18 09:21:09 +0000558 &wfx, (DWORD)wmme_strm->hEvent, 0, flag);
Benny Prijono598b01d2009-02-18 13:55:03 +0000559 if (mr != MMSYSERR_NOERROR) {
Benny Prijono8eeab0b2009-03-04 19:00:28 +0000560 return PJMEDIA_AUDIODEV_ERRNO_FROM_WMME_OUT(mr);
Benny Prijono598b01d2009-02-18 13:55:03 +0000561 }
562
563 /* Pause the wave out device */
564 mr = waveOutPause(wmme_strm->hWave.Out);
565 if (mr != MMSYSERR_NOERROR) {
Benny Prijono8eeab0b2009-03-04 19:00:28 +0000566 return PJMEDIA_AUDIODEV_ERRNO_FROM_WMME_OUT(mr);
Benny Prijono598b01d2009-02-18 13:55:03 +0000567 }
568
569 /*
570 * Create the buffers.
571 */
572 wmme_strm->WaveHdr = (WAVEHDR*)
573 pj_pool_zalloc(pool, sizeof(WAVEHDR) * buffer_count);
574 for (i = 0; i < buffer_count; ++i) {
Benny Prijono2058f472009-02-22 17:15:34 +0000575 wmme_strm->WaveHdr[i].lpData = pj_pool_zalloc(pool,
576 parent->bytes_per_frame);
577 wmme_strm->WaveHdr[i].dwBufferLength = parent->bytes_per_frame;
Benny Prijono598b01d2009-02-18 13:55:03 +0000578 mr = waveOutPrepareHeader(wmme_strm->hWave.Out,
579 &(wmme_strm->WaveHdr[i]),
580 sizeof(WAVEHDR));
581 if (mr != MMSYSERR_NOERROR) {
Benny Prijono8eeab0b2009-03-04 19:00:28 +0000582 return PJMEDIA_AUDIODEV_ERRNO_FROM_WMME_OUT(mr);
Benny Prijono598b01d2009-02-18 13:55:03 +0000583 }
584 mr = waveOutWrite(wmme_strm->hWave.Out, &(wmme_strm->WaveHdr[i]),
585 sizeof(WAVEHDR));
586 if (mr != MMSYSERR_NOERROR) {
Benny Prijono8eeab0b2009-03-04 19:00:28 +0000587 return PJMEDIA_AUDIODEV_ERRNO_FROM_WMME_OUT(mr);
Benny Prijono598b01d2009-02-18 13:55:03 +0000588 }
589 }
590
591 wmme_strm->dwBufIdx = 0;
592 wmme_strm->dwMaxBufIdx = buffer_count;
593 wmme_strm->timestamp.u64 = 0;
594
595 /* Done setting up play device. */
Benny Prijono2e988792009-02-23 10:21:33 +0000596 PJ_LOG(4, (THIS_FILE,
Benny Prijono5fad7be2009-02-22 21:33:20 +0000597 " WaveAPI Sound player \"%s\" initialized ("
598 "format=%s, clock_rate=%d, "
Benny Prijono598b01d2009-02-18 13:55:03 +0000599 "channel_count=%d, samples_per_frame=%d (%dms))",
Benny Prijono2058f472009-02-22 17:15:34 +0000600 wf->dev_info[prm->play_id].info.name,
Benny Prijono5fad7be2009-02-22 21:33:20 +0000601 get_fmt_name(prm->ext_fmt.id),
Benny Prijono2058f472009-02-22 17:15:34 +0000602 prm->clock_rate, prm->channel_count, prm->samples_per_frame,
603 prm->samples_per_frame * 1000 / prm->clock_rate));
Benny Prijono598b01d2009-02-18 13:55:03 +0000604
605 return PJ_SUCCESS;
606}
607
608
609/* Internal: create Windows Multimedia recorder device */
610static pj_status_t init_capture_stream( struct wmme_factory *wf,
611 pj_pool_t *pool,
Benny Prijono2058f472009-02-22 17:15:34 +0000612 struct wmme_stream *parent,
Benny Prijono598b01d2009-02-18 13:55:03 +0000613 struct wmme_channel *wmme_strm,
Benny Prijono2058f472009-02-22 17:15:34 +0000614 const pjmedia_aud_param *prm,
Benny Prijono598b01d2009-02-18 13:55:03 +0000615 unsigned buffer_count)
616{
617 MMRESULT mr;
Benny Prijono2058f472009-02-22 17:15:34 +0000618 WAVEFORMATEX wfx;
Benny Prijonocdf2c9c2009-07-18 09:21:09 +0000619 DWORD flag;
Benny Prijono2058f472009-02-22 17:15:34 +0000620 unsigned i, ptime;
Benny Prijono598b01d2009-02-18 13:55:03 +0000621
Benny Prijono2058f472009-02-22 17:15:34 +0000622 PJ_ASSERT_RETURN(prm->rec_id < (int)wf->dev_count, PJ_EINVAL);
Benny Prijono598b01d2009-02-18 13:55:03 +0000623
624 /*
625 * Create a wait event.
626 */
627 wmme_strm->hEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
628 if (NULL == wmme_strm->hEvent) {
629 return pj_get_os_error();
630 }
631
632 /*
633 * Set up wave format structure for opening the device.
634 */
Benny Prijono2058f472009-02-22 17:15:34 +0000635 init_waveformatex(&wfx, prm);
636 ptime = prm->samples_per_frame * 1000 /
637 (prm->clock_rate * prm->channel_count);
Benny Prijono5fad7be2009-02-22 21:33:20 +0000638 parent->bytes_per_frame = wfx.nAvgBytesPerSec * ptime / 1000;
Benny Prijono598b01d2009-02-18 13:55:03 +0000639
Benny Prijonocdf2c9c2009-07-18 09:21:09 +0000640 flag = CALLBACK_EVENT;
641 if (prm->ext_fmt.id == PJMEDIA_FORMAT_L16)
642 flag |= WAVE_FORMAT_DIRECT;
643
Benny Prijono598b01d2009-02-18 13:55:03 +0000644 /*
645 * Open wave device.
646 */
Benny Prijono2058f472009-02-22 17:15:34 +0000647 mr = waveInOpen(&wmme_strm->hWave.In,
648 wf->dev_info[prm->rec_id].deviceId,
Benny Prijonocdf2c9c2009-07-18 09:21:09 +0000649 &wfx, (DWORD)wmme_strm->hEvent, 0, flag);
Benny Prijono598b01d2009-02-18 13:55:03 +0000650 if (mr != MMSYSERR_NOERROR) {
Benny Prijono8eeab0b2009-03-04 19:00:28 +0000651 return PJMEDIA_AUDIODEV_ERRNO_FROM_WMME_IN(mr);
Benny Prijono598b01d2009-02-18 13:55:03 +0000652 }
653
654 /*
655 * Create the buffers.
656 */
657 wmme_strm->WaveHdr = (WAVEHDR*)
658 pj_pool_zalloc(pool, sizeof(WAVEHDR) * buffer_count);
659 for (i = 0; i < buffer_count; ++i) {
Benny Prijono2058f472009-02-22 17:15:34 +0000660 wmme_strm->WaveHdr[i].lpData = pj_pool_zalloc(pool,
661 parent->bytes_per_frame);
662 wmme_strm->WaveHdr[i].dwBufferLength = parent->bytes_per_frame;
Benny Prijono598b01d2009-02-18 13:55:03 +0000663 mr = waveInPrepareHeader(wmme_strm->hWave.In,
664 &(wmme_strm->WaveHdr[i]),
665 sizeof(WAVEHDR));
666 if (mr != MMSYSERR_NOERROR) {
Benny Prijono8eeab0b2009-03-04 19:00:28 +0000667 return PJMEDIA_AUDIODEV_ERRNO_FROM_WMME_IN(mr);
Benny Prijono598b01d2009-02-18 13:55:03 +0000668 }
669 mr = waveInAddBuffer(wmme_strm->hWave.In, &(wmme_strm->WaveHdr[i]),
670 sizeof(WAVEHDR));
671 if (mr != MMSYSERR_NOERROR) {
Benny Prijono8eeab0b2009-03-04 19:00:28 +0000672 return PJMEDIA_AUDIODEV_ERRNO_FROM_WMME_IN(mr);
Benny Prijono598b01d2009-02-18 13:55:03 +0000673 }
674 }
675
676 wmme_strm->dwBufIdx = 0;
677 wmme_strm->dwMaxBufIdx = buffer_count;
678 wmme_strm->timestamp.u64 = 0;
679
680 /* Done setting up play device. */
Benny Prijono2e988792009-02-23 10:21:33 +0000681 PJ_LOG(4,(THIS_FILE,
Benny Prijono5fad7be2009-02-22 21:33:20 +0000682 " WaveAPI Sound recorder \"%s\" initialized "
683 "(format=%s, clock_rate=%d, "
Benny Prijono598b01d2009-02-18 13:55:03 +0000684 "channel_count=%d, samples_per_frame=%d (%dms))",
Benny Prijono2058f472009-02-22 17:15:34 +0000685 wf->dev_info[prm->rec_id].info.name,
Benny Prijono5fad7be2009-02-22 21:33:20 +0000686 get_fmt_name(prm->ext_fmt.id),
Benny Prijono2058f472009-02-22 17:15:34 +0000687 prm->clock_rate, prm->channel_count, prm->samples_per_frame,
688 prm->samples_per_frame * 1000 / prm->clock_rate));
Benny Prijono598b01d2009-02-18 13:55:03 +0000689
690 return PJ_SUCCESS;
691}
692
693
694/* WMME capture and playback thread. */
695static int PJ_THREAD_FUNC wmme_dev_thread(void *arg)
696{
697 struct wmme_stream *strm = (struct wmme_stream*)arg;
698 HANDLE events[3];
699 unsigned eventCount;
Benny Prijono598b01d2009-02-18 13:55:03 +0000700 pj_status_t status = PJ_SUCCESS;
Benny Prijono5fad7be2009-02-22 21:33:20 +0000701 static unsigned rec_cnt, play_cnt;
Nanang Izzuddin719f7392009-03-27 15:22:35 +0000702 enum { MAX_BURST = 1000 };
Benny Prijono5fad7be2009-02-22 21:33:20 +0000703
704 rec_cnt = play_cnt = 0;
Benny Prijono598b01d2009-02-18 13:55:03 +0000705
Benny Prijono598b01d2009-02-18 13:55:03 +0000706 eventCount = 0;
707 events[eventCount++] = strm->thread_quit_event;
Benny Prijono7a380002009-03-09 12:55:29 +0000708 if (strm->param.dir & PJMEDIA_DIR_PLAYBACK)
Benny Prijono598b01d2009-02-18 13:55:03 +0000709 events[eventCount++] = strm->play_strm.hEvent;
Benny Prijono7a380002009-03-09 12:55:29 +0000710 if (strm->param.dir & PJMEDIA_DIR_CAPTURE)
Benny Prijono598b01d2009-02-18 13:55:03 +0000711 events[eventCount++] = strm->rec_strm.hEvent;
712
713
714 /* Raise self priority. We don't want the audio to be distorted by
715 * system activity.
716 */
717#if defined(PJ_WIN32_WINCE) && PJ_WIN32_WINCE != 0
Benny Prijono7a380002009-03-09 12:55:29 +0000718 if (strm->param.dir & PJMEDIA_DIR_PLAYBACK)
Benny Prijono598b01d2009-02-18 13:55:03 +0000719 CeSetThreadPriority(GetCurrentThread(), 153);
720 else
721 CeSetThreadPriority(GetCurrentThread(), 247);
722#else
723 SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_TIME_CRITICAL);
724#endif
725
Benny Prijono598b01d2009-02-18 13:55:03 +0000726 /*
727 * Loop while not signalled to quit, wait for event objects to be
728 * signalled by WMME capture and play buffer.
729 */
730 while (status == PJ_SUCCESS)
731 {
732
733 DWORD rc;
734 pjmedia_dir signalled_dir;
735
Benny Prijonocf6b5902009-03-10 12:05:23 +0000736 /* Swap hWaveIn and hWaveOut to get equal opportunity for both */
Benny Prijono5fad7be2009-02-22 21:33:20 +0000737 if (eventCount==3) {
738 HANDLE hTemp = events[2];
739 events[2] = events[1];
740 events[1] = hTemp;
741 }
742
Benny Prijono598b01d2009-02-18 13:55:03 +0000743 rc = WaitForMultipleObjects(eventCount, events, FALSE, INFINITE);
744 if (rc < WAIT_OBJECT_0 || rc >= WAIT_OBJECT_0 + eventCount)
745 continue;
746
747 if (rc == WAIT_OBJECT_0)
748 break;
749
750 if (rc == (WAIT_OBJECT_0 + 1))
751 {
752 if (events[1] == strm->play_strm.hEvent)
753 signalled_dir = PJMEDIA_DIR_PLAYBACK;
754 else
755 signalled_dir = PJMEDIA_DIR_CAPTURE;
756 }
757 else
758 {
759 if (events[2] == strm->play_strm.hEvent)
760 signalled_dir = PJMEDIA_DIR_PLAYBACK;
761 else
762 signalled_dir = PJMEDIA_DIR_CAPTURE;
763 }
764
765
766 if (signalled_dir == PJMEDIA_DIR_PLAYBACK)
767 {
768 struct wmme_channel *wmme_strm = &strm->play_strm;
Benny Prijonocf6b5902009-03-10 12:05:23 +0000769 unsigned burst;
Benny Prijono2058f472009-02-22 17:15:34 +0000770
Benny Prijono598b01d2009-02-18 13:55:03 +0000771 status = PJ_SUCCESS;
772
773 /*
774 * Windows Multimedia has requested us to feed some frames to
775 * playback buffer.
776 */
777
Benny Prijonocf6b5902009-03-10 12:05:23 +0000778 for (burst=0; burst<MAX_BURST &&
779 (wmme_strm->WaveHdr[wmme_strm->dwBufIdx].dwFlags & WHDR_DONE);
780 ++burst)
Benny Prijono598b01d2009-02-18 13:55:03 +0000781 {
782 void *buffer = wmme_strm->WaveHdr[wmme_strm->dwBufIdx].lpData;
Benny Prijono2058f472009-02-22 17:15:34 +0000783 pjmedia_frame pcm_frame, *frame;
Benny Prijono5fad7be2009-02-22 21:33:20 +0000784 MMRESULT mr = MMSYSERR_NOERROR;
Benny Prijono598b01d2009-02-18 13:55:03 +0000785
786 //PJ_LOG(5,(THIS_FILE, "Finished writing buffer %d",
787 // wmme_strm->dwBufIdx));
788
Benny Prijono5fad7be2009-02-22 21:33:20 +0000789 if (strm->fmt_id == PJMEDIA_FORMAT_L16) {
Benny Prijono2058f472009-02-22 17:15:34 +0000790 /* PCM mode */
791 frame = &pcm_frame;
792
793 frame->type = PJMEDIA_FRAME_TYPE_AUDIO;
794 frame->size = strm->bytes_per_frame;
795 frame->buf = buffer;
796 frame->timestamp.u64 = wmme_strm->timestamp.u64;
797 frame->bit_info = 0;
798 } else {
799 /* Codec mode */
800 frame = &strm->xfrm->base;
801
802 strm->xfrm->base.type = PJMEDIA_FRAME_TYPE_EXTENDED;
803 strm->xfrm->base.size = strm->bytes_per_frame;
804 strm->xfrm->base.buf = NULL;
805 strm->xfrm->base.timestamp.u64 = wmme_strm->timestamp.u64;
806 strm->xfrm->base.bit_info = 0;
807 }
Benny Prijono598b01d2009-02-18 13:55:03 +0000808
809 /* Get frame from application. */
Benny Prijono2e988792009-02-23 10:21:33 +0000810 //PJ_LOG(5,(THIS_FILE, "xxx %u play_cb", play_cnt++));
Benny Prijono2058f472009-02-22 17:15:34 +0000811 status = (*strm->play_cb)(strm->user_data, frame);
Benny Prijono598b01d2009-02-18 13:55:03 +0000812
813 if (status != PJ_SUCCESS)
814 break;
815
Benny Prijono5fad7be2009-02-22 21:33:20 +0000816 if (strm->fmt_id == PJMEDIA_FORMAT_L16) {
Benny Prijono2058f472009-02-22 17:15:34 +0000817 /* PCM mode */
818 if (frame->type == PJMEDIA_FRAME_TYPE_NONE) {
819 pj_bzero(buffer, strm->bytes_per_frame);
Benny Prijono2058f472009-02-22 17:15:34 +0000820 } else if (frame->type == PJMEDIA_FRAME_TYPE_EXTENDED) {
821 pj_assert(!"Frame type not supported");
822 } else if (frame->type == PJMEDIA_FRAME_TYPE_AUDIO) {
Benny Prijono5fad7be2009-02-22 21:33:20 +0000823 /* Nothing to do */
Benny Prijono2058f472009-02-22 17:15:34 +0000824 } else {
825 pj_assert(!"Frame type not supported");
826 }
827 } else {
828 /* Codec mode */
829 if (frame->type == PJMEDIA_FRAME_TYPE_NONE) {
Benny Prijono5fad7be2009-02-22 21:33:20 +0000830 pj_memset(buffer, strm->silence_char,
831 strm->bytes_per_frame);
Benny Prijono2058f472009-02-22 17:15:34 +0000832 } else if (frame->type == PJMEDIA_FRAME_TYPE_EXTENDED) {
833 unsigned sz;
834 sz = pjmedia_frame_ext_copy_payload(strm->xfrm,
835 buffer,
836 strm->bytes_per_frame);
Benny Prijono5fad7be2009-02-22 21:33:20 +0000837 if (sz < strm->bytes_per_frame) {
838 pj_memset((char*)buffer+sz,
839 strm->silence_char,
840 strm->bytes_per_frame - sz);
841 }
Benny Prijono2058f472009-02-22 17:15:34 +0000842 } else {
843 pj_assert(!"Frame type not supported");
844 }
Benny Prijono598b01d2009-02-18 13:55:03 +0000845 }
846
847 /* Write to the device. */
Benny Prijono5fad7be2009-02-22 21:33:20 +0000848 mr = waveOutWrite(wmme_strm->hWave.Out,
849 &(wmme_strm->WaveHdr[wmme_strm->dwBufIdx]),
850 sizeof(WAVEHDR));
851 if (mr != MMSYSERR_NOERROR) {
Benny Prijono8eeab0b2009-03-04 19:00:28 +0000852 status = PJMEDIA_AUDIODEV_ERRNO_FROM_WMME_OUT(mr);
Benny Prijono5fad7be2009-02-22 21:33:20 +0000853 break;
Benny Prijono598b01d2009-02-18 13:55:03 +0000854 }
855
856 /* Increment position. */
857 if (++wmme_strm->dwBufIdx >= wmme_strm->dwMaxBufIdx)
858 wmme_strm->dwBufIdx = 0;
Benny Prijono7a380002009-03-09 12:55:29 +0000859 wmme_strm->timestamp.u64 += strm->param.samples_per_frame /
860 strm->param.channel_count;
Benny Prijonocf6b5902009-03-10 12:05:23 +0000861 } /* for */
Benny Prijono598b01d2009-02-18 13:55:03 +0000862 }
863 else
864 {
865 struct wmme_channel *wmme_strm = &strm->rec_strm;
Benny Prijonocf6b5902009-03-10 12:05:23 +0000866 unsigned burst;
Benny Prijono598b01d2009-02-18 13:55:03 +0000867 MMRESULT mr = MMSYSERR_NOERROR;
868 status = PJ_SUCCESS;
869
870 /*
871 * Windows Multimedia has indicated that it has some frames ready
872 * in the capture buffer. Get as much frames as possible to
873 * prevent overflows.
874 */
875#if 0
876 {
877 static DWORD tc = 0;
878 DWORD now = GetTickCount();
879 DWORD i = 0;
880 DWORD bits = 0;
881
882 if (tc == 0) tc = now;
883
884 for (i = 0; i < wmme_strm->dwMaxBufIdx; ++i)
885 {
886 bits = bits << 4;
887 bits |= wmme_strm->WaveHdr[i].dwFlags & WHDR_DONE;
888 }
889 PJ_LOG(5,(THIS_FILE, "Record Signal> Index: %d, Delta: %4.4d, "
890 "Flags: %6.6x\n",
891 wmme_strm->dwBufIdx,
892 now - tc,
893 bits));
894 tc = now;
895 }
896#endif
897
Benny Prijonocf6b5902009-03-10 12:05:23 +0000898 for (burst=0; burst<MAX_BURST &&
899 (wmme_strm->WaveHdr[wmme_strm->dwBufIdx].dwFlags & WHDR_DONE);
900 ++burst)
Benny Prijono598b01d2009-02-18 13:55:03 +0000901 {
902 char* buffer = (char*)
903 wmme_strm->WaveHdr[wmme_strm->dwBufIdx].lpData;
904 unsigned cap_len =
905 wmme_strm->WaveHdr[wmme_strm->dwBufIdx].dwBytesRecorded;
Benny Prijono2058f472009-02-22 17:15:34 +0000906 pjmedia_frame pcm_frame, *frame;
Benny Prijono598b01d2009-02-18 13:55:03 +0000907
908 /*
909 PJ_LOG(5,(THIS_FILE, "Read %d bytes from buffer %d", cap_len,
910 wmme_strm->dwBufIdx));
911 */
Benny Prijono2058f472009-02-22 17:15:34 +0000912
Benny Prijono5fad7be2009-02-22 21:33:20 +0000913 if (strm->fmt_id == PJMEDIA_FORMAT_L16) {
Benny Prijono2058f472009-02-22 17:15:34 +0000914 /* PCM mode */
915 if (cap_len < strm->bytes_per_frame)
916 pj_bzero(buffer + cap_len,
917 strm->bytes_per_frame - cap_len);
Benny Prijono598b01d2009-02-18 13:55:03 +0000918
Benny Prijono2058f472009-02-22 17:15:34 +0000919 /* Copy the audio data out of the wave buffer. */
920 pj_memcpy(strm->buffer, buffer, strm->bytes_per_frame);
Benny Prijono598b01d2009-02-18 13:55:03 +0000921
Benny Prijono2058f472009-02-22 17:15:34 +0000922 /* Prepare frame */
923 frame = &pcm_frame;
924 frame->type = PJMEDIA_FRAME_TYPE_AUDIO;
925 frame->buf = strm->buffer;
926 frame->size = strm->bytes_per_frame;
927 frame->timestamp.u64 = wmme_strm->timestamp.u64;
928 frame->bit_info = 0;
929
930 } else {
931 /* Codec mode */
932 frame = &strm->xfrm->base;
933
934 frame->type = PJMEDIA_FRAME_TYPE_EXTENDED;
935 frame->buf = NULL;
936 frame->size = strm->bytes_per_frame;
937 frame->timestamp.u64 = wmme_strm->timestamp.u64;
938 frame->bit_info = 0;
939
940 strm->xfrm->samples_cnt = 0;
941 strm->xfrm->subframe_cnt = 0;
Benny Prijono7a380002009-03-09 12:55:29 +0000942 pjmedia_frame_ext_append_subframe(
943 strm->xfrm, buffer,
944 strm->bytes_per_frame *8,
945 strm->param.samples_per_frame
946 );
Benny Prijono2058f472009-02-22 17:15:34 +0000947 }
Benny Prijono598b01d2009-02-18 13:55:03 +0000948
949 /* Re-add the buffer to the device. */
950 mr = waveInAddBuffer(wmme_strm->hWave.In,
951 &(wmme_strm->WaveHdr[wmme_strm->dwBufIdx]),
952 sizeof(WAVEHDR));
953 if (mr != MMSYSERR_NOERROR) {
Benny Prijono8eeab0b2009-03-04 19:00:28 +0000954 status = PJMEDIA_AUDIODEV_ERRNO_FROM_WMME_IN(mr);
Benny Prijono598b01d2009-02-18 13:55:03 +0000955 break;
956 }
957
Benny Prijono598b01d2009-02-18 13:55:03 +0000958
959 /* Call callback */
Benny Prijono2e988792009-02-23 10:21:33 +0000960 //PJ_LOG(5,(THIS_FILE, "xxx %u rec_cb", rec_cnt++));
Benny Prijono2058f472009-02-22 17:15:34 +0000961 status = (*strm->rec_cb)(strm->user_data, frame);
Benny Prijono598b01d2009-02-18 13:55:03 +0000962 if (status != PJ_SUCCESS)
963 break;
964
965 /* Increment position. */
966 if (++wmme_strm->dwBufIdx >= wmme_strm->dwMaxBufIdx)
967 wmme_strm->dwBufIdx = 0;
Benny Prijono7a380002009-03-09 12:55:29 +0000968 wmme_strm->timestamp.u64 += strm->param.samples_per_frame /
969 strm->param.channel_count;
Benny Prijonocf6b5902009-03-10 12:05:23 +0000970 } /* for */
Benny Prijono598b01d2009-02-18 13:55:03 +0000971 }
972 }
973
974 PJ_LOG(5,(THIS_FILE, "WMME: thread stopping.."));
975 return 0;
976}
977
978
979/* API: create stream */
980static pj_status_t factory_create_stream(pjmedia_aud_dev_factory *f,
Benny Prijono10454dc2009-02-21 14:21:59 +0000981 const pjmedia_aud_param *param,
Benny Prijono598b01d2009-02-18 13:55:03 +0000982 pjmedia_aud_rec_cb rec_cb,
983 pjmedia_aud_play_cb play_cb,
984 void *user_data,
985 pjmedia_aud_stream **p_aud_strm)
986{
987 struct wmme_factory *wf = (struct wmme_factory*)f;
988 pj_pool_t *pool;
989 struct wmme_stream *strm;
Benny Prijono5fad7be2009-02-22 21:33:20 +0000990 pj_uint8_t silence_char;
Benny Prijono598b01d2009-02-18 13:55:03 +0000991 pj_status_t status;
992
Benny Prijono5fad7be2009-02-22 21:33:20 +0000993 switch (param->ext_fmt.id) {
994 case PJMEDIA_FORMAT_L16:
995 silence_char = '\0';
996 break;
997 case PJMEDIA_FORMAT_ALAW:
998 silence_char = (pj_uint8_t)'\xd5';
999 break;
1000 case PJMEDIA_FORMAT_ULAW:
1001 silence_char = (pj_uint8_t)'\xff';
1002 break;
1003 default:
1004 return PJMEDIA_EAUD_BADFORMAT;
1005 }
Benny Prijono598b01d2009-02-18 13:55:03 +00001006
1007 /* Create and Initialize stream descriptor */
1008 pool = pj_pool_create(wf->pf, "wmme-dev", 1000, 1000, NULL);
1009 PJ_ASSERT_RETURN(pool != NULL, PJ_ENOMEM);
1010
1011 strm = PJ_POOL_ZALLOC_T(pool, struct wmme_stream);
Benny Prijono7a380002009-03-09 12:55:29 +00001012 pj_memcpy(&strm->param, param, sizeof(*param));
Benny Prijono598b01d2009-02-18 13:55:03 +00001013 strm->pool = pool;
1014 strm->rec_cb = rec_cb;
1015 strm->play_cb = play_cb;
1016 strm->user_data = user_data;
Benny Prijono5fad7be2009-02-22 21:33:20 +00001017 strm->fmt_id = param->ext_fmt.id;
1018 strm->silence_char = silence_char;
Benny Prijono598b01d2009-02-18 13:55:03 +00001019
1020 /* Create player stream */
1021 if (param->dir & PJMEDIA_DIR_PLAYBACK) {
1022 unsigned buf_count;
1023
Benny Prijono7a380002009-03-09 12:55:29 +00001024 if ((param->flags & PJMEDIA_AUD_DEV_CAP_OUTPUT_LATENCY)==0) {
1025 strm->param.flags |= PJMEDIA_AUD_DEV_CAP_OUTPUT_LATENCY;
1026 strm->param.output_latency_ms = PJMEDIA_SND_DEFAULT_PLAY_LATENCY;
1027 }
Benny Prijono598b01d2009-02-18 13:55:03 +00001028
Benny Prijono7a380002009-03-09 12:55:29 +00001029 buf_count = strm->param.output_latency_ms * param->clock_rate *
Benny Prijono598b01d2009-02-18 13:55:03 +00001030 param->channel_count / param->samples_per_frame / 1000;
1031
1032 status = init_player_stream(wf, strm->pool,
Benny Prijono2058f472009-02-22 17:15:34 +00001033 strm,
Benny Prijono598b01d2009-02-18 13:55:03 +00001034 &strm->play_strm,
Benny Prijono2058f472009-02-22 17:15:34 +00001035 param,
Benny Prijono598b01d2009-02-18 13:55:03 +00001036 buf_count);
1037
1038 if (status != PJ_SUCCESS) {
1039 stream_destroy(&strm->base);
1040 return status;
1041 }
1042 }
1043
1044 /* Create capture stream */
1045 if (param->dir & PJMEDIA_DIR_CAPTURE) {
1046 unsigned buf_count;
1047
Benny Prijono7a380002009-03-09 12:55:29 +00001048 if ((param->flags & PJMEDIA_AUD_DEV_CAP_INPUT_LATENCY)==0) {
1049 strm->param.flags |= PJMEDIA_AUD_DEV_CAP_INPUT_LATENCY;
1050 strm->param.input_latency_ms = PJMEDIA_SND_DEFAULT_REC_LATENCY;
1051 }
Benny Prijono598b01d2009-02-18 13:55:03 +00001052
Benny Prijono7a380002009-03-09 12:55:29 +00001053 buf_count = strm->param.input_latency_ms * param->clock_rate *
Benny Prijono598b01d2009-02-18 13:55:03 +00001054 param->channel_count / param->samples_per_frame / 1000;
1055
1056 status = init_capture_stream(wf, strm->pool,
Benny Prijono2058f472009-02-22 17:15:34 +00001057 strm,
Benny Prijono598b01d2009-02-18 13:55:03 +00001058 &strm->rec_strm,
Benny Prijono2058f472009-02-22 17:15:34 +00001059 param,
Benny Prijono598b01d2009-02-18 13:55:03 +00001060 buf_count);
1061
1062 if (status != PJ_SUCCESS) {
1063 stream_destroy(&strm->base);
1064 return status;
1065 }
1066 }
1067
Benny Prijono5fad7be2009-02-22 21:33:20 +00001068 strm->buffer = pj_pool_alloc(pool, strm->bytes_per_frame);
1069 if (!strm->buffer) {
1070 pj_pool_release(pool);
1071 return PJ_ENOMEM;
1072 }
1073
Benny Prijono2058f472009-02-22 17:15:34 +00001074 /* If format is extended, must create buffer for the extended frame. */
Benny Prijono5fad7be2009-02-22 21:33:20 +00001075 if (strm->fmt_id != PJMEDIA_FORMAT_L16) {
Benny Prijono2058f472009-02-22 17:15:34 +00001076 strm->xfrm_size = sizeof(pjmedia_frame_ext) +
1077 32 * sizeof(pjmedia_frame_ext_subframe) +
Benny Prijono5fad7be2009-02-22 21:33:20 +00001078 strm->bytes_per_frame + 4;
Benny Prijono2058f472009-02-22 17:15:34 +00001079 strm->xfrm = (pjmedia_frame_ext*)
1080 pj_pool_alloc(pool, strm->xfrm_size);
1081 }
1082
Benny Prijono598b01d2009-02-18 13:55:03 +00001083 /* Create the stop event */
1084 strm->thread_quit_event = CreateEvent(NULL, FALSE, FALSE, NULL);
1085 if (strm->thread_quit_event == NULL) {
1086 status = pj_get_os_error();
1087 stream_destroy(&strm->base);
1088 return status;
1089 }
1090
1091 /* Create and start the thread */
1092 status = pj_thread_create(pool, "wmme", &wmme_dev_thread, strm, 0, 0,
1093 &strm->thread);
1094 if (status != PJ_SUCCESS) {
1095 stream_destroy(&strm->base);
1096 return status;
1097 }
1098
Benny Prijono7a380002009-03-09 12:55:29 +00001099 /* Apply the remaining settings */
1100 if (param->flags & PJMEDIA_AUD_DEV_CAP_OUTPUT_VOLUME_SETTING) {
1101 stream_set_cap(&strm->base, PJMEDIA_AUD_DEV_CAP_OUTPUT_VOLUME_SETTING,
1102 &param->output_vol);
1103 }
1104
1105
Benny Prijono598b01d2009-02-18 13:55:03 +00001106 /* Done */
1107 strm->base.op = &stream_op;
1108 *p_aud_strm = &strm->base;
1109
1110 return PJ_SUCCESS;
1111}
1112
1113/* API: Get stream info. */
1114static pj_status_t stream_get_param(pjmedia_aud_stream *s,
Benny Prijono10454dc2009-02-21 14:21:59 +00001115 pjmedia_aud_param *pi)
Benny Prijono598b01d2009-02-18 13:55:03 +00001116{
1117 struct wmme_stream *strm = (struct wmme_stream*)s;
1118
1119 PJ_ASSERT_RETURN(strm && pi, PJ_EINVAL);
1120
Benny Prijono7a380002009-03-09 12:55:29 +00001121 pj_memcpy(pi, &strm->param, sizeof(*pi));
Benny Prijono598b01d2009-02-18 13:55:03 +00001122
Benny Prijono85bdaa82009-03-09 13:22:01 +00001123 /* Update the volume setting */
1124 if (stream_get_cap(s, PJMEDIA_AUD_DEV_CAP_OUTPUT_VOLUME_SETTING,
1125 &pi->output_vol) == PJ_SUCCESS)
1126 {
1127 pi->flags |= PJMEDIA_AUD_DEV_CAP_OUTPUT_VOLUME_SETTING;
1128 }
1129
Benny Prijono598b01d2009-02-18 13:55:03 +00001130 return PJ_SUCCESS;
1131}
1132
1133/* API: get capability */
1134static pj_status_t stream_get_cap(pjmedia_aud_stream *s,
1135 pjmedia_aud_dev_cap cap,
1136 void *pval)
1137{
1138 struct wmme_stream *strm = (struct wmme_stream*)s;
1139
1140 PJ_ASSERT_RETURN(s && pval, PJ_EINVAL);
1141
1142 if (cap==PJMEDIA_AUD_DEV_CAP_INPUT_LATENCY &&
Benny Prijono7a380002009-03-09 12:55:29 +00001143 (strm->param.dir & PJMEDIA_DIR_CAPTURE))
Benny Prijono598b01d2009-02-18 13:55:03 +00001144 {
1145 /* Recording latency */
Benny Prijono7a380002009-03-09 12:55:29 +00001146 *(unsigned*)pval = strm->param.input_latency_ms;
Benny Prijono598b01d2009-02-18 13:55:03 +00001147 return PJ_SUCCESS;
1148 } else if (cap==PJMEDIA_AUD_DEV_CAP_OUTPUT_LATENCY &&
Benny Prijono7a380002009-03-09 12:55:29 +00001149 (strm->param.dir & PJMEDIA_DIR_PLAYBACK))
Benny Prijono598b01d2009-02-18 13:55:03 +00001150 {
1151 /* Playback latency */
Benny Prijono7a380002009-03-09 12:55:29 +00001152 *(unsigned*)pval = strm->param.output_latency_ms;
Benny Prijono598b01d2009-02-18 13:55:03 +00001153 return PJ_SUCCESS;
1154 } else if (cap==PJMEDIA_AUD_DEV_CAP_OUTPUT_VOLUME_SETTING &&
1155 strm->play_strm.hWave.Out)
1156 {
1157 /* Output volume setting */
Benny Prijono7a380002009-03-09 12:55:29 +00001158 DWORD waveVol;
Benny Prijono598b01d2009-02-18 13:55:03 +00001159 MMRESULT mr;
1160
Benny Prijono7a380002009-03-09 12:55:29 +00001161 mr = waveOutGetVolume(strm->play_strm.hWave.Out, &waveVol);
Benny Prijono598b01d2009-02-18 13:55:03 +00001162 if (mr != MMSYSERR_NOERROR) {
Benny Prijono8eeab0b2009-03-04 19:00:28 +00001163 return PJMEDIA_AUDIODEV_ERRNO_FROM_WMME_OUT(mr);
Benny Prijono598b01d2009-02-18 13:55:03 +00001164 }
1165
Benny Prijono7a380002009-03-09 12:55:29 +00001166 waveVol &= 0xFFFF;
1167 *(unsigned*)pval = (waveVol * 100) / 0xFFFF;
Benny Prijono598b01d2009-02-18 13:55:03 +00001168 return PJ_SUCCESS;
1169 } else {
Benny Prijono64f91382009-03-05 18:02:28 +00001170 return PJMEDIA_EAUD_INVCAP;
Benny Prijono598b01d2009-02-18 13:55:03 +00001171 }
1172}
1173
1174/* API: set capability */
1175static pj_status_t stream_set_cap(pjmedia_aud_stream *s,
1176 pjmedia_aud_dev_cap cap,
1177 const void *pval)
1178{
1179 struct wmme_stream *strm = (struct wmme_stream*)s;
1180
1181 PJ_ASSERT_RETURN(s && pval, PJ_EINVAL);
1182
1183 if (cap==PJMEDIA_AUD_DEV_CAP_OUTPUT_VOLUME_SETTING &&
1184 strm->play_strm.hWave.Out)
1185 {
1186 /* Output volume setting */
Benny Prijono7a380002009-03-09 12:55:29 +00001187 unsigned vol = *(unsigned*)pval;
1188 DWORD waveVol;
Benny Prijono598b01d2009-02-18 13:55:03 +00001189 MMRESULT mr;
Benny Prijono7a380002009-03-09 12:55:29 +00001190 pj_status_t status;
Benny Prijono598b01d2009-02-18 13:55:03 +00001191
Benny Prijono7a380002009-03-09 12:55:29 +00001192 if (vol > 100)
1193 vol = 100;
Benny Prijono598b01d2009-02-18 13:55:03 +00001194
Benny Prijono7a380002009-03-09 12:55:29 +00001195 waveVol = (vol * 0xFFFF) / 100;
1196 waveVol |= (waveVol << 16);
1197
1198 mr = waveOutSetVolume(strm->play_strm.hWave.Out, waveVol);
1199 status = (mr==MMSYSERR_NOERROR)? PJ_SUCCESS :
Benny Prijono8eeab0b2009-03-04 19:00:28 +00001200 PJMEDIA_AUDIODEV_ERRNO_FROM_WMME_OUT(mr);
Benny Prijono7a380002009-03-09 12:55:29 +00001201 if (status == PJ_SUCCESS) {
1202 strm->param.output_vol = *(unsigned*)pval;
1203 }
1204 return status;
Benny Prijono598b01d2009-02-18 13:55:03 +00001205 }
1206
Benny Prijono64f91382009-03-05 18:02:28 +00001207 return PJMEDIA_EAUD_INVCAP;
Benny Prijono598b01d2009-02-18 13:55:03 +00001208}
1209
1210/* API: Start stream. */
1211static pj_status_t stream_start(pjmedia_aud_stream *strm)
1212{
1213 struct wmme_stream *stream = (struct wmme_stream*)strm;
1214 MMRESULT mr;
1215
1216 if (stream->play_strm.hWave.Out != NULL)
1217 {
1218 mr = waveOutRestart(stream->play_strm.hWave.Out);
1219 if (mr != MMSYSERR_NOERROR) {
Benny Prijono8eeab0b2009-03-04 19:00:28 +00001220 return PJMEDIA_AUDIODEV_ERRNO_FROM_WMME_OUT(mr);
Benny Prijono598b01d2009-02-18 13:55:03 +00001221 }
Benny Prijono2e988792009-02-23 10:21:33 +00001222 PJ_LOG(4,(THIS_FILE, "WMME playback stream started"));
Benny Prijono598b01d2009-02-18 13:55:03 +00001223 }
1224
1225 if (stream->rec_strm.hWave.In != NULL)
1226 {
1227 mr = waveInStart(stream->rec_strm.hWave.In);
1228 if (mr != MMSYSERR_NOERROR) {
Benny Prijono8eeab0b2009-03-04 19:00:28 +00001229 return PJMEDIA_AUDIODEV_ERRNO_FROM_WMME_IN(mr);
Benny Prijono598b01d2009-02-18 13:55:03 +00001230 }
Benny Prijono2e988792009-02-23 10:21:33 +00001231 PJ_LOG(4,(THIS_FILE, "WMME capture stream started"));
Benny Prijono598b01d2009-02-18 13:55:03 +00001232 }
1233
1234 return PJ_SUCCESS;
1235}
1236
1237/* API: Stop stream. */
1238static pj_status_t stream_stop(pjmedia_aud_stream *strm)
1239{
1240 struct wmme_stream *stream = (struct wmme_stream*)strm;
1241 MMRESULT mr;
1242
1243 PJ_ASSERT_RETURN(stream != NULL, PJ_EINVAL);
1244
1245 if (stream->play_strm.hWave.Out != NULL)
1246 {
1247 mr = waveOutPause(stream->play_strm.hWave.Out);
1248 if (mr != MMSYSERR_NOERROR) {
Benny Prijono8eeab0b2009-03-04 19:00:28 +00001249 return PJMEDIA_AUDIODEV_ERRNO_FROM_WMME_OUT(mr);
Benny Prijono598b01d2009-02-18 13:55:03 +00001250 }
Benny Prijono2e988792009-02-23 10:21:33 +00001251 PJ_LOG(4,(THIS_FILE, "Stopped WMME playback stream"));
Benny Prijono598b01d2009-02-18 13:55:03 +00001252 }
1253
1254 if (stream->rec_strm.hWave.In != NULL)
1255 {
1256 mr = waveInStop(stream->rec_strm.hWave.In);
1257 if (mr != MMSYSERR_NOERROR) {
Benny Prijono8eeab0b2009-03-04 19:00:28 +00001258 return PJMEDIA_AUDIODEV_ERRNO_FROM_WMME_IN(mr);
Benny Prijono598b01d2009-02-18 13:55:03 +00001259 }
Benny Prijono2e988792009-02-23 10:21:33 +00001260 PJ_LOG(4,(THIS_FILE, "Stopped WMME capture stream"));
Benny Prijono598b01d2009-02-18 13:55:03 +00001261 }
1262
1263 return PJ_SUCCESS;
1264}
1265
1266
1267/* API: Destroy stream. */
1268static pj_status_t stream_destroy(pjmedia_aud_stream *strm)
1269{
1270 struct wmme_stream *stream = (struct wmme_stream*)strm;
1271 unsigned i;
1272
1273 PJ_ASSERT_RETURN(stream != NULL, PJ_EINVAL);
1274
1275 stream_stop(strm);
1276
Nanang Izzuddinf46a69b2009-03-27 15:36:18 +00001277 /* Stop the stream thread */
Benny Prijono598b01d2009-02-18 13:55:03 +00001278 if (stream->thread)
1279 {
1280 SetEvent(stream->thread_quit_event);
1281 pj_thread_join(stream->thread);
1282 pj_thread_destroy(stream->thread);
1283 stream->thread = NULL;
1284 }
1285
Nanang Izzuddinf46a69b2009-03-27 15:36:18 +00001286 /* Close the thread quit event */
1287 if (stream->thread_quit_event)
1288 {
1289 CloseHandle(stream->thread_quit_event);
1290 stream->thread_quit_event = NULL;
1291 }
1292
Benny Prijono598b01d2009-02-18 13:55:03 +00001293 /* Unprepare the headers and close the play device */
1294 if (stream->play_strm.hWave.Out)
1295 {
1296 waveOutReset(stream->play_strm.hWave.Out);
1297 for (i = 0; i < stream->play_strm.dwMaxBufIdx; ++i)
1298 waveOutUnprepareHeader(stream->play_strm.hWave.Out,
1299 &(stream->play_strm.WaveHdr[i]),
1300 sizeof(WAVEHDR));
1301 waveOutClose(stream->play_strm.hWave.Out);
1302 stream->play_strm.hWave.Out = NULL;
1303 }
1304
1305 /* Close the play event */
1306 if (stream->play_strm.hEvent)
1307 {
1308 CloseHandle(stream->play_strm.hEvent);
1309 stream->play_strm.hEvent = NULL;
1310 }
1311
1312 /* Unprepare the headers and close the record device */
1313 if (stream->rec_strm.hWave.In)
1314 {
1315 waveInReset(stream->rec_strm.hWave.In);
1316 for (i = 0; i < stream->play_strm.dwMaxBufIdx; ++i)
1317 waveInUnprepareHeader(stream->rec_strm.hWave.In,
1318 &(stream->rec_strm.WaveHdr[i]),
1319 sizeof(WAVEHDR));
1320 waveInClose(stream->rec_strm.hWave.In);
1321 stream->rec_strm.hWave.In = NULL;
1322 }
1323
1324 /* Close the record event */
1325 if (stream->rec_strm.hEvent)
1326 {
1327 CloseHandle(stream->rec_strm.hEvent);
1328 stream->rec_strm.hEvent = NULL;
1329 }
1330
1331 pj_pool_release(stream->pool);
1332
1333 return PJ_SUCCESS;
1334}
1335
Benny Prijono8eeab0b2009-03-04 19:00:28 +00001336#endif /* PJMEDIA_AUDIO_DEV_HAS_WMME */
1337