blob: 02448939dbf985caf84db69ae87d3ef632280dab [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;
253 wdi->info.ext_fmt[0].id = PJMEDIA_FORMAT_PCMU;
254 wdi->info.ext_fmt[0].bitrate = 64000;
255 wdi->info.ext_fmt[0].vad = 0;
256 wdi->info.ext_fmt[1].id = PJMEDIA_FORMAT_PCMA;
257 wdi->info.ext_fmt[1].bitrate = 64000;
258 wdi->info.ext_fmt[1].vad = 0;
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"));
286 return PJMEDIA_EAUD_NODEV;
287 }
288
289 wf->dev_info = (struct wmme_dev_info*)
290 pj_pool_calloc(wf->pool, devCount,
291 sizeof(struct wmme_dev_info));
292
Benny Prijono2058f472009-02-22 17:15:34 +0000293 if (inputDeviceCount && outputDeviceCount) {
Benny Prijono598b01d2009-02-18 13:55:03 +0000294 /* Attempt to add WAVE_MAPPER as input and output device */
295 WAVEINCAPS wic;
296 MMRESULT mr;
297
298 pj_bzero(&wic, sizeof(WAVEINCAPS));
299 mr = waveInGetDevCaps(WAVE_MAPPER, &wic, sizeof(WAVEINCAPS));
300
301 if (mr == MMSYSERR_NOERROR) {
302 WAVEOUTCAPS woc;
303
304 pj_bzero(&woc, sizeof(WAVEOUTCAPS));
305 mr = waveOutGetDevCaps(WAVE_MAPPER, &woc, sizeof(WAVEOUTCAPS));
306 if (mr == MMSYSERR_NOERROR) {
307 build_dev_info(WAVE_MAPPER, &wf->dev_info[wf->dev_count],
308 &wic, &woc);
309 ++wf->dev_count;
310 waveMapperAdded = PJ_TRUE;
311 }
312 }
313
314 }
315
316 if (inputDeviceCount > 0) {
317 /* -1 is the WAVE_MAPPER */
318 for (i = (waveMapperAdded? 0 : -1); i < inputDeviceCount; ++i) {
319 UINT uDeviceID = (UINT)((i==-1) ? WAVE_MAPPER : i);
320 WAVEINCAPS wic;
321 MMRESULT mr;
322
323 pj_bzero(&wic, sizeof(WAVEINCAPS));
324
325 mr = waveInGetDevCaps(uDeviceID, &wic, sizeof(WAVEINCAPS));
326
327 if (mr == MMSYSERR_NOMEM)
328 return PJ_ENOMEM;
329
330 if (mr != MMSYSERR_NOERROR)
331 continue;
332
333 build_dev_info(uDeviceID, &wf->dev_info[wf->dev_count],
334 &wic, NULL);
335 ++wf->dev_count;
336 }
337 }
338
339 if( outputDeviceCount > 0 )
340 {
341 /* -1 is the WAVE_MAPPER */
342 for (i = (waveMapperAdded? 0 : -1); i < outputDeviceCount; ++i) {
343 UINT uDeviceID = (UINT)((i==-1) ? WAVE_MAPPER : i);
344 WAVEOUTCAPS woc;
345 MMRESULT mr;
346
347 pj_bzero(&woc, sizeof(WAVEOUTCAPS));
348
349 mr = waveOutGetDevCaps(uDeviceID, &woc, sizeof(WAVEOUTCAPS));
350
351 if (mr == MMSYSERR_NOMEM)
352 return PJ_ENOMEM;
353
354 if (mr != MMSYSERR_NOERROR)
355 continue;
356
357 build_dev_info(uDeviceID, &wf->dev_info[wf->dev_count],
358 NULL, &woc);
359 ++wf->dev_count;
360 }
361 }
362
363 PJ_LOG(4, (THIS_FILE, "WMME initialized, found %d devices:",
364 wf->dev_count));
365 for (c = 0; c < wf->dev_count; ++c) {
366 PJ_LOG(4, (THIS_FILE, " dev_id %d: %s (in=%d, out=%d)",
367 c,
368 wf->dev_info[c].info.name,
369 wf->dev_info[c].info.input_count,
370 wf->dev_info[c].info.output_count));
371 }
372
373 return PJ_SUCCESS;
374}
375
376/* API: destroy factory */
377static pj_status_t factory_destroy(pjmedia_aud_dev_factory *f)
378{
379 struct wmme_factory *wf = (struct wmme_factory*)f;
380 pj_pool_t *pool = wf->pool;
381
382 wf->pool = NULL;
383 pj_pool_release(pool);
384
385 return PJ_SUCCESS;
386}
387
388/* API: get number of devices */
389static unsigned factory_get_dev_count(pjmedia_aud_dev_factory *f)
390{
391 struct wmme_factory *wf = (struct wmme_factory*)f;
392 return wf->dev_count;
393}
394
395/* API: get device info */
396static pj_status_t factory_get_dev_info(pjmedia_aud_dev_factory *f,
397 unsigned index,
398 pjmedia_aud_dev_info *info)
399{
400 struct wmme_factory *wf = (struct wmme_factory*)f;
401
402 PJ_ASSERT_RETURN(index < wf->dev_count, PJMEDIA_EAUD_INVDEV);
403
404 pj_memcpy(info, &wf->dev_info[index].info, sizeof(*info));
405
406 return PJ_SUCCESS;
407}
408
409/* API: create default device parameter */
410static pj_status_t factory_default_param(pjmedia_aud_dev_factory *f,
411 unsigned index,
Benny Prijono10454dc2009-02-21 14:21:59 +0000412 pjmedia_aud_param *param)
Benny Prijono598b01d2009-02-18 13:55:03 +0000413{
414 struct wmme_factory *wf = (struct wmme_factory*)f;
415 struct wmme_dev_info *di = &wf->dev_info[index];
416
417 PJ_ASSERT_RETURN(index < wf->dev_count, PJMEDIA_EAUD_INVDEV);
418
419 pj_bzero(param, sizeof(*param));
420 if (di->info.input_count && di->info.output_count) {
421 param->dir = PJMEDIA_DIR_CAPTURE_PLAYBACK;
422 param->rec_id = index;
423 param->play_id = index;
424 } else if (di->info.input_count) {
425 param->dir = PJMEDIA_DIR_CAPTURE;
426 param->rec_id = index;
Benny Prijono96e74f32009-02-22 12:00:12 +0000427 param->play_id = PJMEDIA_AUD_INVALID_DEV;
Benny Prijono598b01d2009-02-18 13:55:03 +0000428 } else if (di->info.output_count) {
429 param->dir = PJMEDIA_DIR_PLAYBACK;
430 param->play_id = index;
Benny Prijono96e74f32009-02-22 12:00:12 +0000431 param->rec_id = PJMEDIA_AUD_INVALID_DEV;
Benny Prijono598b01d2009-02-18 13:55:03 +0000432 } else {
433 return PJMEDIA_EAUD_INVDEV;
434 }
435
436 param->clock_rate = di->info.default_samples_per_sec;
437 param->channel_count = 1;
438 param->samples_per_frame = di->info.default_samples_per_sec * 20 / 1000;
439 param->bits_per_sample = 16;
Benny Prijono7a380002009-03-09 12:55:29 +0000440 param->flags = PJMEDIA_AUD_DEV_CAP_INPUT_LATENCY |
441 PJMEDIA_AUD_DEV_CAP_OUTPUT_LATENCY;
Benny Prijono598b01d2009-02-18 13:55:03 +0000442 param->input_latency_ms = PJMEDIA_SND_DEFAULT_REC_LATENCY;
443 param->output_latency_ms = PJMEDIA_SND_DEFAULT_PLAY_LATENCY;
444
445 return PJ_SUCCESS;
446}
447
448/* Internal: init WAVEFORMATEX */
Benny Prijono2058f472009-02-22 17:15:34 +0000449static pj_status_t init_waveformatex(LPWAVEFORMATEX wfx,
450 const pjmedia_aud_param *prm)
Benny Prijono598b01d2009-02-18 13:55:03 +0000451{
Benny Prijono2058f472009-02-22 17:15:34 +0000452
453 pj_bzero(wfx, sizeof(PCMWAVEFORMAT));
454 if (prm->ext_fmt.id == PJMEDIA_FORMAT_L16) {
Benny Prijono5fad7be2009-02-22 21:33:20 +0000455 enum { BYTES_PER_SAMPLE = 2 };
Benny Prijono2058f472009-02-22 17:15:34 +0000456 wfx->wFormatTag = WAVE_FORMAT_PCM;
457 wfx->nChannels = (pj_uint16_t)prm->channel_count;
458 wfx->nSamplesPerSec = prm->clock_rate;
459 wfx->nBlockAlign = (pj_uint16_t)(prm->channel_count *
460 BYTES_PER_SAMPLE);
461 wfx->nAvgBytesPerSec = prm->clock_rate * prm->channel_count *
462 BYTES_PER_SAMPLE;
Benny Prijono5fad7be2009-02-22 21:33:20 +0000463 wfx->wBitsPerSample = 16;
Benny Prijono2058f472009-02-22 17:15:34 +0000464
465 return PJ_SUCCESS;
466
467 } else if ((prm->flags & PJMEDIA_AUD_DEV_CAP_EXT_FORMAT) &&
468 (prm->ext_fmt.id == PJMEDIA_FORMAT_PCMA ||
469 prm->ext_fmt.id == PJMEDIA_FORMAT_PCMU))
470 {
471 unsigned ptime;
472
473 ptime = prm->samples_per_frame * 1000 /
474 (prm->clock_rate * prm->channel_count);
475 wfx->wFormatTag = (pj_uint16_t)
476 ((prm->ext_fmt.id==PJMEDIA_FORMAT_PCMA) ?
477 WAVE_FORMAT_ALAW : WAVE_FORMAT_MULAW);
478 wfx->nChannels = (pj_uint16_t)prm->channel_count;
479 wfx->nSamplesPerSec = prm->clock_rate;
480 wfx->nAvgBytesPerSec = prm->clock_rate * prm->channel_count;
481 wfx->nBlockAlign = (pj_uint16_t)(wfx->nAvgBytesPerSec * ptime /
482 1000);
483 wfx->wBitsPerSample = 8;
484 wfx->cbSize = 0;
485
486 return PJ_SUCCESS;
487
488 } else {
489
490 return PJMEDIA_EAUD_BADFORMAT;
491
492 }
Benny Prijono598b01d2009-02-18 13:55:03 +0000493}
494
Benny Prijono5fad7be2009-02-22 21:33:20 +0000495/* Get format name */
496static const char *get_fmt_name(pj_uint32_t id)
497{
498 static char name[8];
499
500 if (id == PJMEDIA_FORMAT_L16)
Benny Prijono7a380002009-03-09 12:55:29 +0000501 return "PCM";
Benny Prijono5fad7be2009-02-22 21:33:20 +0000502 pj_memcpy(name, &id, 4);
503 name[4] = '\0';
504 return name;
505}
Benny Prijono598b01d2009-02-18 13:55:03 +0000506
507/* Internal: create WMME player device. */
508static pj_status_t init_player_stream( struct wmme_factory *wf,
509 pj_pool_t *pool,
Benny Prijono2058f472009-02-22 17:15:34 +0000510 struct wmme_stream *parent,
Benny Prijono598b01d2009-02-18 13:55:03 +0000511 struct wmme_channel *wmme_strm,
Benny Prijono2058f472009-02-22 17:15:34 +0000512 const pjmedia_aud_param *prm,
Benny Prijono598b01d2009-02-18 13:55:03 +0000513 unsigned buffer_count)
514{
515 MMRESULT mr;
Benny Prijono2058f472009-02-22 17:15:34 +0000516 WAVEFORMATEX wfx;
517 unsigned i, ptime;
518 pj_status_t status;
Benny Prijono598b01d2009-02-18 13:55:03 +0000519
Benny Prijono2058f472009-02-22 17:15:34 +0000520 PJ_ASSERT_RETURN(prm->play_id < (int)wf->dev_count, PJ_EINVAL);
Benny Prijono598b01d2009-02-18 13:55:03 +0000521
522 /*
523 * Create a wait event.
524 */
525 wmme_strm->hEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
526 if (NULL == wmme_strm->hEvent)
527 return pj_get_os_error();
528
529 /*
530 * Set up wave format structure for opening the device.
531 */
Benny Prijono2058f472009-02-22 17:15:34 +0000532 status = init_waveformatex(&wfx, prm);
533 if (status != PJ_SUCCESS)
534 return status;
535
536 ptime = prm->samples_per_frame * 1000 /
537 (prm->clock_rate * prm->channel_count);
Benny Prijono5fad7be2009-02-22 21:33:20 +0000538 parent->bytes_per_frame = wfx.nAvgBytesPerSec * ptime / 1000;
Benny Prijono598b01d2009-02-18 13:55:03 +0000539
540 /*
541 * Open wave device.
542 */
Benny Prijono2058f472009-02-22 17:15:34 +0000543 mr = waveOutOpen(&wmme_strm->hWave.Out,
544 wf->dev_info[prm->play_id].deviceId,
545 &wfx, (DWORD)wmme_strm->hEvent, 0, CALLBACK_EVENT);
Benny Prijono598b01d2009-02-18 13:55:03 +0000546 if (mr != MMSYSERR_NOERROR) {
Benny Prijono8eeab0b2009-03-04 19:00:28 +0000547 return PJMEDIA_AUDIODEV_ERRNO_FROM_WMME_OUT(mr);
Benny Prijono598b01d2009-02-18 13:55:03 +0000548 }
549
550 /* Pause the wave out device */
551 mr = waveOutPause(wmme_strm->hWave.Out);
552 if (mr != MMSYSERR_NOERROR) {
Benny Prijono8eeab0b2009-03-04 19:00:28 +0000553 return PJMEDIA_AUDIODEV_ERRNO_FROM_WMME_OUT(mr);
Benny Prijono598b01d2009-02-18 13:55:03 +0000554 }
555
556 /*
557 * Create the buffers.
558 */
559 wmme_strm->WaveHdr = (WAVEHDR*)
560 pj_pool_zalloc(pool, sizeof(WAVEHDR) * buffer_count);
561 for (i = 0; i < buffer_count; ++i) {
Benny Prijono2058f472009-02-22 17:15:34 +0000562 wmme_strm->WaveHdr[i].lpData = pj_pool_zalloc(pool,
563 parent->bytes_per_frame);
564 wmme_strm->WaveHdr[i].dwBufferLength = parent->bytes_per_frame;
Benny Prijono598b01d2009-02-18 13:55:03 +0000565 mr = waveOutPrepareHeader(wmme_strm->hWave.Out,
566 &(wmme_strm->WaveHdr[i]),
567 sizeof(WAVEHDR));
568 if (mr != MMSYSERR_NOERROR) {
Benny Prijono8eeab0b2009-03-04 19:00:28 +0000569 return PJMEDIA_AUDIODEV_ERRNO_FROM_WMME_OUT(mr);
Benny Prijono598b01d2009-02-18 13:55:03 +0000570 }
571 mr = waveOutWrite(wmme_strm->hWave.Out, &(wmme_strm->WaveHdr[i]),
572 sizeof(WAVEHDR));
573 if (mr != MMSYSERR_NOERROR) {
Benny Prijono8eeab0b2009-03-04 19:00:28 +0000574 return PJMEDIA_AUDIODEV_ERRNO_FROM_WMME_OUT(mr);
Benny Prijono598b01d2009-02-18 13:55:03 +0000575 }
576 }
577
578 wmme_strm->dwBufIdx = 0;
579 wmme_strm->dwMaxBufIdx = buffer_count;
580 wmme_strm->timestamp.u64 = 0;
581
582 /* Done setting up play device. */
Benny Prijono2e988792009-02-23 10:21:33 +0000583 PJ_LOG(4, (THIS_FILE,
Benny Prijono5fad7be2009-02-22 21:33:20 +0000584 " WaveAPI Sound player \"%s\" initialized ("
585 "format=%s, clock_rate=%d, "
Benny Prijono598b01d2009-02-18 13:55:03 +0000586 "channel_count=%d, samples_per_frame=%d (%dms))",
Benny Prijono2058f472009-02-22 17:15:34 +0000587 wf->dev_info[prm->play_id].info.name,
Benny Prijono5fad7be2009-02-22 21:33:20 +0000588 get_fmt_name(prm->ext_fmt.id),
Benny Prijono2058f472009-02-22 17:15:34 +0000589 prm->clock_rate, prm->channel_count, prm->samples_per_frame,
590 prm->samples_per_frame * 1000 / prm->clock_rate));
Benny Prijono598b01d2009-02-18 13:55:03 +0000591
592 return PJ_SUCCESS;
593}
594
595
596/* Internal: create Windows Multimedia recorder device */
597static pj_status_t init_capture_stream( struct wmme_factory *wf,
598 pj_pool_t *pool,
Benny Prijono2058f472009-02-22 17:15:34 +0000599 struct wmme_stream *parent,
Benny Prijono598b01d2009-02-18 13:55:03 +0000600 struct wmme_channel *wmme_strm,
Benny Prijono2058f472009-02-22 17:15:34 +0000601 const pjmedia_aud_param *prm,
Benny Prijono598b01d2009-02-18 13:55:03 +0000602 unsigned buffer_count)
603{
604 MMRESULT mr;
Benny Prijono2058f472009-02-22 17:15:34 +0000605 WAVEFORMATEX wfx;
606 unsigned i, ptime;
Benny Prijono598b01d2009-02-18 13:55:03 +0000607
Benny Prijono2058f472009-02-22 17:15:34 +0000608 PJ_ASSERT_RETURN(prm->rec_id < (int)wf->dev_count, PJ_EINVAL);
Benny Prijono598b01d2009-02-18 13:55:03 +0000609
610 /*
611 * Create a wait event.
612 */
613 wmme_strm->hEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
614 if (NULL == wmme_strm->hEvent) {
615 return pj_get_os_error();
616 }
617
618 /*
619 * Set up wave format structure for opening the device.
620 */
Benny Prijono2058f472009-02-22 17:15:34 +0000621 init_waveformatex(&wfx, prm);
622 ptime = prm->samples_per_frame * 1000 /
623 (prm->clock_rate * prm->channel_count);
Benny Prijono5fad7be2009-02-22 21:33:20 +0000624 parent->bytes_per_frame = wfx.nAvgBytesPerSec * ptime / 1000;
Benny Prijono598b01d2009-02-18 13:55:03 +0000625
626 /*
627 * Open wave device.
628 */
Benny Prijono2058f472009-02-22 17:15:34 +0000629 mr = waveInOpen(&wmme_strm->hWave.In,
630 wf->dev_info[prm->rec_id].deviceId,
631 &wfx, (DWORD)wmme_strm->hEvent, 0, CALLBACK_EVENT);
Benny Prijono598b01d2009-02-18 13:55:03 +0000632 if (mr != MMSYSERR_NOERROR) {
Benny Prijono8eeab0b2009-03-04 19:00:28 +0000633 return PJMEDIA_AUDIODEV_ERRNO_FROM_WMME_IN(mr);
Benny Prijono598b01d2009-02-18 13:55:03 +0000634 }
635
636 /*
637 * Create the buffers.
638 */
639 wmme_strm->WaveHdr = (WAVEHDR*)
640 pj_pool_zalloc(pool, sizeof(WAVEHDR) * buffer_count);
641 for (i = 0; i < buffer_count; ++i) {
Benny Prijono2058f472009-02-22 17:15:34 +0000642 wmme_strm->WaveHdr[i].lpData = pj_pool_zalloc(pool,
643 parent->bytes_per_frame);
644 wmme_strm->WaveHdr[i].dwBufferLength = parent->bytes_per_frame;
Benny Prijono598b01d2009-02-18 13:55:03 +0000645 mr = waveInPrepareHeader(wmme_strm->hWave.In,
646 &(wmme_strm->WaveHdr[i]),
647 sizeof(WAVEHDR));
648 if (mr != MMSYSERR_NOERROR) {
Benny Prijono8eeab0b2009-03-04 19:00:28 +0000649 return PJMEDIA_AUDIODEV_ERRNO_FROM_WMME_IN(mr);
Benny Prijono598b01d2009-02-18 13:55:03 +0000650 }
651 mr = waveInAddBuffer(wmme_strm->hWave.In, &(wmme_strm->WaveHdr[i]),
652 sizeof(WAVEHDR));
653 if (mr != MMSYSERR_NOERROR) {
Benny Prijono8eeab0b2009-03-04 19:00:28 +0000654 return PJMEDIA_AUDIODEV_ERRNO_FROM_WMME_IN(mr);
Benny Prijono598b01d2009-02-18 13:55:03 +0000655 }
656 }
657
658 wmme_strm->dwBufIdx = 0;
659 wmme_strm->dwMaxBufIdx = buffer_count;
660 wmme_strm->timestamp.u64 = 0;
661
662 /* Done setting up play device. */
Benny Prijono2e988792009-02-23 10:21:33 +0000663 PJ_LOG(4,(THIS_FILE,
Benny Prijono5fad7be2009-02-22 21:33:20 +0000664 " WaveAPI Sound recorder \"%s\" initialized "
665 "(format=%s, clock_rate=%d, "
Benny Prijono598b01d2009-02-18 13:55:03 +0000666 "channel_count=%d, samples_per_frame=%d (%dms))",
Benny Prijono2058f472009-02-22 17:15:34 +0000667 wf->dev_info[prm->rec_id].info.name,
Benny Prijono5fad7be2009-02-22 21:33:20 +0000668 get_fmt_name(prm->ext_fmt.id),
Benny Prijono2058f472009-02-22 17:15:34 +0000669 prm->clock_rate, prm->channel_count, prm->samples_per_frame,
670 prm->samples_per_frame * 1000 / prm->clock_rate));
Benny Prijono598b01d2009-02-18 13:55:03 +0000671
672 return PJ_SUCCESS;
673}
674
675
676/* WMME capture and playback thread. */
677static int PJ_THREAD_FUNC wmme_dev_thread(void *arg)
678{
679 struct wmme_stream *strm = (struct wmme_stream*)arg;
680 HANDLE events[3];
681 unsigned eventCount;
Benny Prijono598b01d2009-02-18 13:55:03 +0000682 pj_status_t status = PJ_SUCCESS;
Benny Prijono5fad7be2009-02-22 21:33:20 +0000683 static unsigned rec_cnt, play_cnt;
Nanang Izzuddin719f7392009-03-27 15:22:35 +0000684 enum { MAX_BURST = 1000 };
Benny Prijono5fad7be2009-02-22 21:33:20 +0000685
686 rec_cnt = play_cnt = 0;
Benny Prijono598b01d2009-02-18 13:55:03 +0000687
Benny Prijono598b01d2009-02-18 13:55:03 +0000688 eventCount = 0;
689 events[eventCount++] = strm->thread_quit_event;
Benny Prijono7a380002009-03-09 12:55:29 +0000690 if (strm->param.dir & PJMEDIA_DIR_PLAYBACK)
Benny Prijono598b01d2009-02-18 13:55:03 +0000691 events[eventCount++] = strm->play_strm.hEvent;
Benny Prijono7a380002009-03-09 12:55:29 +0000692 if (strm->param.dir & PJMEDIA_DIR_CAPTURE)
Benny Prijono598b01d2009-02-18 13:55:03 +0000693 events[eventCount++] = strm->rec_strm.hEvent;
694
695
696 /* Raise self priority. We don't want the audio to be distorted by
697 * system activity.
698 */
699#if defined(PJ_WIN32_WINCE) && PJ_WIN32_WINCE != 0
Benny Prijono7a380002009-03-09 12:55:29 +0000700 if (strm->param.dir & PJMEDIA_DIR_PLAYBACK)
Benny Prijono598b01d2009-02-18 13:55:03 +0000701 CeSetThreadPriority(GetCurrentThread(), 153);
702 else
703 CeSetThreadPriority(GetCurrentThread(), 247);
704#else
705 SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_TIME_CRITICAL);
706#endif
707
Benny Prijono598b01d2009-02-18 13:55:03 +0000708 /*
709 * Loop while not signalled to quit, wait for event objects to be
710 * signalled by WMME capture and play buffer.
711 */
712 while (status == PJ_SUCCESS)
713 {
714
715 DWORD rc;
716 pjmedia_dir signalled_dir;
717
Benny Prijonocf6b5902009-03-10 12:05:23 +0000718 /* Swap hWaveIn and hWaveOut to get equal opportunity for both */
Benny Prijono5fad7be2009-02-22 21:33:20 +0000719 if (eventCount==3) {
720 HANDLE hTemp = events[2];
721 events[2] = events[1];
722 events[1] = hTemp;
723 }
724
Benny Prijono598b01d2009-02-18 13:55:03 +0000725 rc = WaitForMultipleObjects(eventCount, events, FALSE, INFINITE);
726 if (rc < WAIT_OBJECT_0 || rc >= WAIT_OBJECT_0 + eventCount)
727 continue;
728
729 if (rc == WAIT_OBJECT_0)
730 break;
731
732 if (rc == (WAIT_OBJECT_0 + 1))
733 {
734 if (events[1] == strm->play_strm.hEvent)
735 signalled_dir = PJMEDIA_DIR_PLAYBACK;
736 else
737 signalled_dir = PJMEDIA_DIR_CAPTURE;
738 }
739 else
740 {
741 if (events[2] == strm->play_strm.hEvent)
742 signalled_dir = PJMEDIA_DIR_PLAYBACK;
743 else
744 signalled_dir = PJMEDIA_DIR_CAPTURE;
745 }
746
747
748 if (signalled_dir == PJMEDIA_DIR_PLAYBACK)
749 {
750 struct wmme_channel *wmme_strm = &strm->play_strm;
Benny Prijonocf6b5902009-03-10 12:05:23 +0000751 unsigned burst;
Benny Prijono2058f472009-02-22 17:15:34 +0000752
Benny Prijono598b01d2009-02-18 13:55:03 +0000753 status = PJ_SUCCESS;
754
755 /*
756 * Windows Multimedia has requested us to feed some frames to
757 * playback buffer.
758 */
759
Benny Prijonocf6b5902009-03-10 12:05:23 +0000760 for (burst=0; burst<MAX_BURST &&
761 (wmme_strm->WaveHdr[wmme_strm->dwBufIdx].dwFlags & WHDR_DONE);
762 ++burst)
Benny Prijono598b01d2009-02-18 13:55:03 +0000763 {
764 void *buffer = wmme_strm->WaveHdr[wmme_strm->dwBufIdx].lpData;
Benny Prijono2058f472009-02-22 17:15:34 +0000765 pjmedia_frame pcm_frame, *frame;
Benny Prijono5fad7be2009-02-22 21:33:20 +0000766 MMRESULT mr = MMSYSERR_NOERROR;
Benny Prijono598b01d2009-02-18 13:55:03 +0000767
768 //PJ_LOG(5,(THIS_FILE, "Finished writing buffer %d",
769 // wmme_strm->dwBufIdx));
770
Benny Prijono5fad7be2009-02-22 21:33:20 +0000771 if (strm->fmt_id == PJMEDIA_FORMAT_L16) {
Benny Prijono2058f472009-02-22 17:15:34 +0000772 /* PCM mode */
773 frame = &pcm_frame;
774
775 frame->type = PJMEDIA_FRAME_TYPE_AUDIO;
776 frame->size = strm->bytes_per_frame;
777 frame->buf = buffer;
778 frame->timestamp.u64 = wmme_strm->timestamp.u64;
779 frame->bit_info = 0;
780 } else {
781 /* Codec mode */
782 frame = &strm->xfrm->base;
783
784 strm->xfrm->base.type = PJMEDIA_FRAME_TYPE_EXTENDED;
785 strm->xfrm->base.size = strm->bytes_per_frame;
786 strm->xfrm->base.buf = NULL;
787 strm->xfrm->base.timestamp.u64 = wmme_strm->timestamp.u64;
788 strm->xfrm->base.bit_info = 0;
789 }
Benny Prijono598b01d2009-02-18 13:55:03 +0000790
791 /* Get frame from application. */
Benny Prijono2e988792009-02-23 10:21:33 +0000792 //PJ_LOG(5,(THIS_FILE, "xxx %u play_cb", play_cnt++));
Benny Prijono2058f472009-02-22 17:15:34 +0000793 status = (*strm->play_cb)(strm->user_data, frame);
Benny Prijono598b01d2009-02-18 13:55:03 +0000794
795 if (status != PJ_SUCCESS)
796 break;
797
Benny Prijono5fad7be2009-02-22 21:33:20 +0000798 if (strm->fmt_id == PJMEDIA_FORMAT_L16) {
Benny Prijono2058f472009-02-22 17:15:34 +0000799 /* PCM mode */
800 if (frame->type == PJMEDIA_FRAME_TYPE_NONE) {
801 pj_bzero(buffer, strm->bytes_per_frame);
Benny Prijono2058f472009-02-22 17:15:34 +0000802 } else if (frame->type == PJMEDIA_FRAME_TYPE_EXTENDED) {
803 pj_assert(!"Frame type not supported");
804 } else if (frame->type == PJMEDIA_FRAME_TYPE_AUDIO) {
Benny Prijono5fad7be2009-02-22 21:33:20 +0000805 /* Nothing to do */
Benny Prijono2058f472009-02-22 17:15:34 +0000806 } else {
807 pj_assert(!"Frame type not supported");
808 }
809 } else {
810 /* Codec mode */
811 if (frame->type == PJMEDIA_FRAME_TYPE_NONE) {
Benny Prijono5fad7be2009-02-22 21:33:20 +0000812 pj_memset(buffer, strm->silence_char,
813 strm->bytes_per_frame);
Benny Prijono2058f472009-02-22 17:15:34 +0000814 } else if (frame->type == PJMEDIA_FRAME_TYPE_EXTENDED) {
815 unsigned sz;
816 sz = pjmedia_frame_ext_copy_payload(strm->xfrm,
817 buffer,
818 strm->bytes_per_frame);
Benny Prijono5fad7be2009-02-22 21:33:20 +0000819 if (sz < strm->bytes_per_frame) {
820 pj_memset((char*)buffer+sz,
821 strm->silence_char,
822 strm->bytes_per_frame - sz);
823 }
Benny Prijono2058f472009-02-22 17:15:34 +0000824 } else {
825 pj_assert(!"Frame type not supported");
826 }
Benny Prijono598b01d2009-02-18 13:55:03 +0000827 }
828
829 /* Write to the device. */
Benny Prijono5fad7be2009-02-22 21:33:20 +0000830 mr = waveOutWrite(wmme_strm->hWave.Out,
831 &(wmme_strm->WaveHdr[wmme_strm->dwBufIdx]),
832 sizeof(WAVEHDR));
833 if (mr != MMSYSERR_NOERROR) {
Benny Prijono8eeab0b2009-03-04 19:00:28 +0000834 status = PJMEDIA_AUDIODEV_ERRNO_FROM_WMME_OUT(mr);
Benny Prijono5fad7be2009-02-22 21:33:20 +0000835 break;
Benny Prijono598b01d2009-02-18 13:55:03 +0000836 }
837
838 /* Increment position. */
839 if (++wmme_strm->dwBufIdx >= wmme_strm->dwMaxBufIdx)
840 wmme_strm->dwBufIdx = 0;
Benny Prijono7a380002009-03-09 12:55:29 +0000841 wmme_strm->timestamp.u64 += strm->param.samples_per_frame /
842 strm->param.channel_count;
Benny Prijonocf6b5902009-03-10 12:05:23 +0000843 } /* for */
Benny Prijono598b01d2009-02-18 13:55:03 +0000844 }
845 else
846 {
847 struct wmme_channel *wmme_strm = &strm->rec_strm;
Benny Prijonocf6b5902009-03-10 12:05:23 +0000848 unsigned burst;
Benny Prijono598b01d2009-02-18 13:55:03 +0000849 MMRESULT mr = MMSYSERR_NOERROR;
850 status = PJ_SUCCESS;
851
852 /*
853 * Windows Multimedia has indicated that it has some frames ready
854 * in the capture buffer. Get as much frames as possible to
855 * prevent overflows.
856 */
857#if 0
858 {
859 static DWORD tc = 0;
860 DWORD now = GetTickCount();
861 DWORD i = 0;
862 DWORD bits = 0;
863
864 if (tc == 0) tc = now;
865
866 for (i = 0; i < wmme_strm->dwMaxBufIdx; ++i)
867 {
868 bits = bits << 4;
869 bits |= wmme_strm->WaveHdr[i].dwFlags & WHDR_DONE;
870 }
871 PJ_LOG(5,(THIS_FILE, "Record Signal> Index: %d, Delta: %4.4d, "
872 "Flags: %6.6x\n",
873 wmme_strm->dwBufIdx,
874 now - tc,
875 bits));
876 tc = now;
877 }
878#endif
879
Benny Prijonocf6b5902009-03-10 12:05:23 +0000880 for (burst=0; burst<MAX_BURST &&
881 (wmme_strm->WaveHdr[wmme_strm->dwBufIdx].dwFlags & WHDR_DONE);
882 ++burst)
Benny Prijono598b01d2009-02-18 13:55:03 +0000883 {
884 char* buffer = (char*)
885 wmme_strm->WaveHdr[wmme_strm->dwBufIdx].lpData;
886 unsigned cap_len =
887 wmme_strm->WaveHdr[wmme_strm->dwBufIdx].dwBytesRecorded;
Benny Prijono2058f472009-02-22 17:15:34 +0000888 pjmedia_frame pcm_frame, *frame;
Benny Prijono598b01d2009-02-18 13:55:03 +0000889
890 /*
891 PJ_LOG(5,(THIS_FILE, "Read %d bytes from buffer %d", cap_len,
892 wmme_strm->dwBufIdx));
893 */
Benny Prijono2058f472009-02-22 17:15:34 +0000894
Benny Prijono5fad7be2009-02-22 21:33:20 +0000895 if (strm->fmt_id == PJMEDIA_FORMAT_L16) {
Benny Prijono2058f472009-02-22 17:15:34 +0000896 /* PCM mode */
897 if (cap_len < strm->bytes_per_frame)
898 pj_bzero(buffer + cap_len,
899 strm->bytes_per_frame - cap_len);
Benny Prijono598b01d2009-02-18 13:55:03 +0000900
Benny Prijono2058f472009-02-22 17:15:34 +0000901 /* Copy the audio data out of the wave buffer. */
902 pj_memcpy(strm->buffer, buffer, strm->bytes_per_frame);
Benny Prijono598b01d2009-02-18 13:55:03 +0000903
Benny Prijono2058f472009-02-22 17:15:34 +0000904 /* Prepare frame */
905 frame = &pcm_frame;
906 frame->type = PJMEDIA_FRAME_TYPE_AUDIO;
907 frame->buf = strm->buffer;
908 frame->size = strm->bytes_per_frame;
909 frame->timestamp.u64 = wmme_strm->timestamp.u64;
910 frame->bit_info = 0;
911
912 } else {
913 /* Codec mode */
914 frame = &strm->xfrm->base;
915
916 frame->type = PJMEDIA_FRAME_TYPE_EXTENDED;
917 frame->buf = NULL;
918 frame->size = strm->bytes_per_frame;
919 frame->timestamp.u64 = wmme_strm->timestamp.u64;
920 frame->bit_info = 0;
921
922 strm->xfrm->samples_cnt = 0;
923 strm->xfrm->subframe_cnt = 0;
Benny Prijono7a380002009-03-09 12:55:29 +0000924 pjmedia_frame_ext_append_subframe(
925 strm->xfrm, buffer,
926 strm->bytes_per_frame *8,
927 strm->param.samples_per_frame
928 );
Benny Prijono2058f472009-02-22 17:15:34 +0000929 }
Benny Prijono598b01d2009-02-18 13:55:03 +0000930
931 /* Re-add the buffer to the device. */
932 mr = waveInAddBuffer(wmme_strm->hWave.In,
933 &(wmme_strm->WaveHdr[wmme_strm->dwBufIdx]),
934 sizeof(WAVEHDR));
935 if (mr != MMSYSERR_NOERROR) {
Benny Prijono8eeab0b2009-03-04 19:00:28 +0000936 status = PJMEDIA_AUDIODEV_ERRNO_FROM_WMME_IN(mr);
Benny Prijono598b01d2009-02-18 13:55:03 +0000937 break;
938 }
939
Benny Prijono598b01d2009-02-18 13:55:03 +0000940
941 /* Call callback */
Benny Prijono2e988792009-02-23 10:21:33 +0000942 //PJ_LOG(5,(THIS_FILE, "xxx %u rec_cb", rec_cnt++));
Benny Prijono2058f472009-02-22 17:15:34 +0000943 status = (*strm->rec_cb)(strm->user_data, frame);
Benny Prijono598b01d2009-02-18 13:55:03 +0000944 if (status != PJ_SUCCESS)
945 break;
946
947 /* Increment position. */
948 if (++wmme_strm->dwBufIdx >= wmme_strm->dwMaxBufIdx)
949 wmme_strm->dwBufIdx = 0;
Benny Prijono7a380002009-03-09 12:55:29 +0000950 wmme_strm->timestamp.u64 += strm->param.samples_per_frame /
951 strm->param.channel_count;
Benny Prijonocf6b5902009-03-10 12:05:23 +0000952 } /* for */
Benny Prijono598b01d2009-02-18 13:55:03 +0000953 }
954 }
955
956 PJ_LOG(5,(THIS_FILE, "WMME: thread stopping.."));
957 return 0;
958}
959
960
961/* API: create stream */
962static pj_status_t factory_create_stream(pjmedia_aud_dev_factory *f,
Benny Prijono10454dc2009-02-21 14:21:59 +0000963 const pjmedia_aud_param *param,
Benny Prijono598b01d2009-02-18 13:55:03 +0000964 pjmedia_aud_rec_cb rec_cb,
965 pjmedia_aud_play_cb play_cb,
966 void *user_data,
967 pjmedia_aud_stream **p_aud_strm)
968{
969 struct wmme_factory *wf = (struct wmme_factory*)f;
970 pj_pool_t *pool;
971 struct wmme_stream *strm;
Benny Prijono5fad7be2009-02-22 21:33:20 +0000972 pj_uint8_t silence_char;
Benny Prijono598b01d2009-02-18 13:55:03 +0000973 pj_status_t status;
974
Benny Prijono5fad7be2009-02-22 21:33:20 +0000975 switch (param->ext_fmt.id) {
976 case PJMEDIA_FORMAT_L16:
977 silence_char = '\0';
978 break;
979 case PJMEDIA_FORMAT_ALAW:
980 silence_char = (pj_uint8_t)'\xd5';
981 break;
982 case PJMEDIA_FORMAT_ULAW:
983 silence_char = (pj_uint8_t)'\xff';
984 break;
985 default:
986 return PJMEDIA_EAUD_BADFORMAT;
987 }
Benny Prijono598b01d2009-02-18 13:55:03 +0000988
989 /* Create and Initialize stream descriptor */
990 pool = pj_pool_create(wf->pf, "wmme-dev", 1000, 1000, NULL);
991 PJ_ASSERT_RETURN(pool != NULL, PJ_ENOMEM);
992
993 strm = PJ_POOL_ZALLOC_T(pool, struct wmme_stream);
Benny Prijono7a380002009-03-09 12:55:29 +0000994 pj_memcpy(&strm->param, param, sizeof(*param));
Benny Prijono598b01d2009-02-18 13:55:03 +0000995 strm->pool = pool;
996 strm->rec_cb = rec_cb;
997 strm->play_cb = play_cb;
998 strm->user_data = user_data;
Benny Prijono5fad7be2009-02-22 21:33:20 +0000999 strm->fmt_id = param->ext_fmt.id;
1000 strm->silence_char = silence_char;
Benny Prijono598b01d2009-02-18 13:55:03 +00001001
1002 /* Create player stream */
1003 if (param->dir & PJMEDIA_DIR_PLAYBACK) {
1004 unsigned buf_count;
1005
Benny Prijono7a380002009-03-09 12:55:29 +00001006 if ((param->flags & PJMEDIA_AUD_DEV_CAP_OUTPUT_LATENCY)==0) {
1007 strm->param.flags |= PJMEDIA_AUD_DEV_CAP_OUTPUT_LATENCY;
1008 strm->param.output_latency_ms = PJMEDIA_SND_DEFAULT_PLAY_LATENCY;
1009 }
Benny Prijono598b01d2009-02-18 13:55:03 +00001010
Benny Prijono7a380002009-03-09 12:55:29 +00001011 buf_count = strm->param.output_latency_ms * param->clock_rate *
Benny Prijono598b01d2009-02-18 13:55:03 +00001012 param->channel_count / param->samples_per_frame / 1000;
1013
1014 status = init_player_stream(wf, strm->pool,
Benny Prijono2058f472009-02-22 17:15:34 +00001015 strm,
Benny Prijono598b01d2009-02-18 13:55:03 +00001016 &strm->play_strm,
Benny Prijono2058f472009-02-22 17:15:34 +00001017 param,
Benny Prijono598b01d2009-02-18 13:55:03 +00001018 buf_count);
1019
1020 if (status != PJ_SUCCESS) {
1021 stream_destroy(&strm->base);
1022 return status;
1023 }
1024 }
1025
1026 /* Create capture stream */
1027 if (param->dir & PJMEDIA_DIR_CAPTURE) {
1028 unsigned buf_count;
1029
Benny Prijono7a380002009-03-09 12:55:29 +00001030 if ((param->flags & PJMEDIA_AUD_DEV_CAP_INPUT_LATENCY)==0) {
1031 strm->param.flags |= PJMEDIA_AUD_DEV_CAP_INPUT_LATENCY;
1032 strm->param.input_latency_ms = PJMEDIA_SND_DEFAULT_REC_LATENCY;
1033 }
Benny Prijono598b01d2009-02-18 13:55:03 +00001034
Benny Prijono7a380002009-03-09 12:55:29 +00001035 buf_count = strm->param.input_latency_ms * param->clock_rate *
Benny Prijono598b01d2009-02-18 13:55:03 +00001036 param->channel_count / param->samples_per_frame / 1000;
1037
1038 status = init_capture_stream(wf, strm->pool,
Benny Prijono2058f472009-02-22 17:15:34 +00001039 strm,
Benny Prijono598b01d2009-02-18 13:55:03 +00001040 &strm->rec_strm,
Benny Prijono2058f472009-02-22 17:15:34 +00001041 param,
Benny Prijono598b01d2009-02-18 13:55:03 +00001042 buf_count);
1043
1044 if (status != PJ_SUCCESS) {
1045 stream_destroy(&strm->base);
1046 return status;
1047 }
1048 }
1049
Benny Prijono5fad7be2009-02-22 21:33:20 +00001050 strm->buffer = pj_pool_alloc(pool, strm->bytes_per_frame);
1051 if (!strm->buffer) {
1052 pj_pool_release(pool);
1053 return PJ_ENOMEM;
1054 }
1055
Benny Prijono2058f472009-02-22 17:15:34 +00001056 /* If format is extended, must create buffer for the extended frame. */
Benny Prijono5fad7be2009-02-22 21:33:20 +00001057 if (strm->fmt_id != PJMEDIA_FORMAT_L16) {
Benny Prijono2058f472009-02-22 17:15:34 +00001058 strm->xfrm_size = sizeof(pjmedia_frame_ext) +
1059 32 * sizeof(pjmedia_frame_ext_subframe) +
Benny Prijono5fad7be2009-02-22 21:33:20 +00001060 strm->bytes_per_frame + 4;
Benny Prijono2058f472009-02-22 17:15:34 +00001061 strm->xfrm = (pjmedia_frame_ext*)
1062 pj_pool_alloc(pool, strm->xfrm_size);
1063 }
1064
Benny Prijono598b01d2009-02-18 13:55:03 +00001065 /* Create the stop event */
1066 strm->thread_quit_event = CreateEvent(NULL, FALSE, FALSE, NULL);
1067 if (strm->thread_quit_event == NULL) {
1068 status = pj_get_os_error();
1069 stream_destroy(&strm->base);
1070 return status;
1071 }
1072
1073 /* Create and start the thread */
1074 status = pj_thread_create(pool, "wmme", &wmme_dev_thread, strm, 0, 0,
1075 &strm->thread);
1076 if (status != PJ_SUCCESS) {
1077 stream_destroy(&strm->base);
1078 return status;
1079 }
1080
Benny Prijono7a380002009-03-09 12:55:29 +00001081 /* Apply the remaining settings */
1082 if (param->flags & PJMEDIA_AUD_DEV_CAP_OUTPUT_VOLUME_SETTING) {
1083 stream_set_cap(&strm->base, PJMEDIA_AUD_DEV_CAP_OUTPUT_VOLUME_SETTING,
1084 &param->output_vol);
1085 }
1086
1087
Benny Prijono598b01d2009-02-18 13:55:03 +00001088 /* Done */
1089 strm->base.op = &stream_op;
1090 *p_aud_strm = &strm->base;
1091
1092 return PJ_SUCCESS;
1093}
1094
1095/* API: Get stream info. */
1096static pj_status_t stream_get_param(pjmedia_aud_stream *s,
Benny Prijono10454dc2009-02-21 14:21:59 +00001097 pjmedia_aud_param *pi)
Benny Prijono598b01d2009-02-18 13:55:03 +00001098{
1099 struct wmme_stream *strm = (struct wmme_stream*)s;
1100
1101 PJ_ASSERT_RETURN(strm && pi, PJ_EINVAL);
1102
Benny Prijono7a380002009-03-09 12:55:29 +00001103 pj_memcpy(pi, &strm->param, sizeof(*pi));
Benny Prijono598b01d2009-02-18 13:55:03 +00001104
Benny Prijono85bdaa82009-03-09 13:22:01 +00001105 /* Update the volume setting */
1106 if (stream_get_cap(s, PJMEDIA_AUD_DEV_CAP_OUTPUT_VOLUME_SETTING,
1107 &pi->output_vol) == PJ_SUCCESS)
1108 {
1109 pi->flags |= PJMEDIA_AUD_DEV_CAP_OUTPUT_VOLUME_SETTING;
1110 }
1111
Benny Prijono598b01d2009-02-18 13:55:03 +00001112 return PJ_SUCCESS;
1113}
1114
1115/* API: get capability */
1116static pj_status_t stream_get_cap(pjmedia_aud_stream *s,
1117 pjmedia_aud_dev_cap cap,
1118 void *pval)
1119{
1120 struct wmme_stream *strm = (struct wmme_stream*)s;
1121
1122 PJ_ASSERT_RETURN(s && pval, PJ_EINVAL);
1123
1124 if (cap==PJMEDIA_AUD_DEV_CAP_INPUT_LATENCY &&
Benny Prijono7a380002009-03-09 12:55:29 +00001125 (strm->param.dir & PJMEDIA_DIR_CAPTURE))
Benny Prijono598b01d2009-02-18 13:55:03 +00001126 {
1127 /* Recording latency */
Benny Prijono7a380002009-03-09 12:55:29 +00001128 *(unsigned*)pval = strm->param.input_latency_ms;
Benny Prijono598b01d2009-02-18 13:55:03 +00001129 return PJ_SUCCESS;
1130 } else if (cap==PJMEDIA_AUD_DEV_CAP_OUTPUT_LATENCY &&
Benny Prijono7a380002009-03-09 12:55:29 +00001131 (strm->param.dir & PJMEDIA_DIR_PLAYBACK))
Benny Prijono598b01d2009-02-18 13:55:03 +00001132 {
1133 /* Playback latency */
Benny Prijono7a380002009-03-09 12:55:29 +00001134 *(unsigned*)pval = strm->param.output_latency_ms;
Benny Prijono598b01d2009-02-18 13:55:03 +00001135 return PJ_SUCCESS;
1136 } else if (cap==PJMEDIA_AUD_DEV_CAP_OUTPUT_VOLUME_SETTING &&
1137 strm->play_strm.hWave.Out)
1138 {
1139 /* Output volume setting */
Benny Prijono7a380002009-03-09 12:55:29 +00001140 DWORD waveVol;
Benny Prijono598b01d2009-02-18 13:55:03 +00001141 MMRESULT mr;
1142
Benny Prijono7a380002009-03-09 12:55:29 +00001143 mr = waveOutGetVolume(strm->play_strm.hWave.Out, &waveVol);
Benny Prijono598b01d2009-02-18 13:55:03 +00001144 if (mr != MMSYSERR_NOERROR) {
Benny Prijono8eeab0b2009-03-04 19:00:28 +00001145 return PJMEDIA_AUDIODEV_ERRNO_FROM_WMME_OUT(mr);
Benny Prijono598b01d2009-02-18 13:55:03 +00001146 }
1147
Benny Prijono7a380002009-03-09 12:55:29 +00001148 waveVol &= 0xFFFF;
1149 *(unsigned*)pval = (waveVol * 100) / 0xFFFF;
Benny Prijono598b01d2009-02-18 13:55:03 +00001150 return PJ_SUCCESS;
1151 } else {
Benny Prijono64f91382009-03-05 18:02:28 +00001152 return PJMEDIA_EAUD_INVCAP;
Benny Prijono598b01d2009-02-18 13:55:03 +00001153 }
1154}
1155
1156/* API: set capability */
1157static pj_status_t stream_set_cap(pjmedia_aud_stream *s,
1158 pjmedia_aud_dev_cap cap,
1159 const void *pval)
1160{
1161 struct wmme_stream *strm = (struct wmme_stream*)s;
1162
1163 PJ_ASSERT_RETURN(s && pval, PJ_EINVAL);
1164
1165 if (cap==PJMEDIA_AUD_DEV_CAP_OUTPUT_VOLUME_SETTING &&
1166 strm->play_strm.hWave.Out)
1167 {
1168 /* Output volume setting */
Benny Prijono7a380002009-03-09 12:55:29 +00001169 unsigned vol = *(unsigned*)pval;
1170 DWORD waveVol;
Benny Prijono598b01d2009-02-18 13:55:03 +00001171 MMRESULT mr;
Benny Prijono7a380002009-03-09 12:55:29 +00001172 pj_status_t status;
Benny Prijono598b01d2009-02-18 13:55:03 +00001173
Benny Prijono7a380002009-03-09 12:55:29 +00001174 if (vol > 100)
1175 vol = 100;
Benny Prijono598b01d2009-02-18 13:55:03 +00001176
Benny Prijono7a380002009-03-09 12:55:29 +00001177 waveVol = (vol * 0xFFFF) / 100;
1178 waveVol |= (waveVol << 16);
1179
1180 mr = waveOutSetVolume(strm->play_strm.hWave.Out, waveVol);
1181 status = (mr==MMSYSERR_NOERROR)? PJ_SUCCESS :
Benny Prijono8eeab0b2009-03-04 19:00:28 +00001182 PJMEDIA_AUDIODEV_ERRNO_FROM_WMME_OUT(mr);
Benny Prijono7a380002009-03-09 12:55:29 +00001183 if (status == PJ_SUCCESS) {
1184 strm->param.output_vol = *(unsigned*)pval;
1185 }
1186 return status;
Benny Prijono598b01d2009-02-18 13:55:03 +00001187 }
1188
Benny Prijono64f91382009-03-05 18:02:28 +00001189 return PJMEDIA_EAUD_INVCAP;
Benny Prijono598b01d2009-02-18 13:55:03 +00001190}
1191
1192/* API: Start stream. */
1193static pj_status_t stream_start(pjmedia_aud_stream *strm)
1194{
1195 struct wmme_stream *stream = (struct wmme_stream*)strm;
1196 MMRESULT mr;
1197
1198 if (stream->play_strm.hWave.Out != NULL)
1199 {
1200 mr = waveOutRestart(stream->play_strm.hWave.Out);
1201 if (mr != MMSYSERR_NOERROR) {
Benny Prijono8eeab0b2009-03-04 19:00:28 +00001202 return PJMEDIA_AUDIODEV_ERRNO_FROM_WMME_OUT(mr);
Benny Prijono598b01d2009-02-18 13:55:03 +00001203 }
Benny Prijono2e988792009-02-23 10:21:33 +00001204 PJ_LOG(4,(THIS_FILE, "WMME playback stream started"));
Benny Prijono598b01d2009-02-18 13:55:03 +00001205 }
1206
1207 if (stream->rec_strm.hWave.In != NULL)
1208 {
1209 mr = waveInStart(stream->rec_strm.hWave.In);
1210 if (mr != MMSYSERR_NOERROR) {
Benny Prijono8eeab0b2009-03-04 19:00:28 +00001211 return PJMEDIA_AUDIODEV_ERRNO_FROM_WMME_IN(mr);
Benny Prijono598b01d2009-02-18 13:55:03 +00001212 }
Benny Prijono2e988792009-02-23 10:21:33 +00001213 PJ_LOG(4,(THIS_FILE, "WMME capture stream started"));
Benny Prijono598b01d2009-02-18 13:55:03 +00001214 }
1215
1216 return PJ_SUCCESS;
1217}
1218
1219/* API: Stop stream. */
1220static pj_status_t stream_stop(pjmedia_aud_stream *strm)
1221{
1222 struct wmme_stream *stream = (struct wmme_stream*)strm;
1223 MMRESULT mr;
1224
1225 PJ_ASSERT_RETURN(stream != NULL, PJ_EINVAL);
1226
1227 if (stream->play_strm.hWave.Out != NULL)
1228 {
1229 mr = waveOutPause(stream->play_strm.hWave.Out);
1230 if (mr != MMSYSERR_NOERROR) {
Benny Prijono8eeab0b2009-03-04 19:00:28 +00001231 return PJMEDIA_AUDIODEV_ERRNO_FROM_WMME_OUT(mr);
Benny Prijono598b01d2009-02-18 13:55:03 +00001232 }
Benny Prijono2e988792009-02-23 10:21:33 +00001233 PJ_LOG(4,(THIS_FILE, "Stopped WMME playback stream"));
Benny Prijono598b01d2009-02-18 13:55:03 +00001234 }
1235
1236 if (stream->rec_strm.hWave.In != NULL)
1237 {
1238 mr = waveInStop(stream->rec_strm.hWave.In);
1239 if (mr != MMSYSERR_NOERROR) {
Benny Prijono8eeab0b2009-03-04 19:00:28 +00001240 return PJMEDIA_AUDIODEV_ERRNO_FROM_WMME_IN(mr);
Benny Prijono598b01d2009-02-18 13:55:03 +00001241 }
Benny Prijono2e988792009-02-23 10:21:33 +00001242 PJ_LOG(4,(THIS_FILE, "Stopped WMME capture stream"));
Benny Prijono598b01d2009-02-18 13:55:03 +00001243 }
1244
1245 return PJ_SUCCESS;
1246}
1247
1248
1249/* API: Destroy stream. */
1250static pj_status_t stream_destroy(pjmedia_aud_stream *strm)
1251{
1252 struct wmme_stream *stream = (struct wmme_stream*)strm;
1253 unsigned i;
1254
1255 PJ_ASSERT_RETURN(stream != NULL, PJ_EINVAL);
1256
1257 stream_stop(strm);
1258
Nanang Izzuddinf46a69b2009-03-27 15:36:18 +00001259 /* Stop the stream thread */
Benny Prijono598b01d2009-02-18 13:55:03 +00001260 if (stream->thread)
1261 {
1262 SetEvent(stream->thread_quit_event);
1263 pj_thread_join(stream->thread);
1264 pj_thread_destroy(stream->thread);
1265 stream->thread = NULL;
1266 }
1267
Nanang Izzuddinf46a69b2009-03-27 15:36:18 +00001268 /* Close the thread quit event */
1269 if (stream->thread_quit_event)
1270 {
1271 CloseHandle(stream->thread_quit_event);
1272 stream->thread_quit_event = NULL;
1273 }
1274
Benny Prijono598b01d2009-02-18 13:55:03 +00001275 /* Unprepare the headers and close the play device */
1276 if (stream->play_strm.hWave.Out)
1277 {
1278 waveOutReset(stream->play_strm.hWave.Out);
1279 for (i = 0; i < stream->play_strm.dwMaxBufIdx; ++i)
1280 waveOutUnprepareHeader(stream->play_strm.hWave.Out,
1281 &(stream->play_strm.WaveHdr[i]),
1282 sizeof(WAVEHDR));
1283 waveOutClose(stream->play_strm.hWave.Out);
1284 stream->play_strm.hWave.Out = NULL;
1285 }
1286
1287 /* Close the play event */
1288 if (stream->play_strm.hEvent)
1289 {
1290 CloseHandle(stream->play_strm.hEvent);
1291 stream->play_strm.hEvent = NULL;
1292 }
1293
1294 /* Unprepare the headers and close the record device */
1295 if (stream->rec_strm.hWave.In)
1296 {
1297 waveInReset(stream->rec_strm.hWave.In);
1298 for (i = 0; i < stream->play_strm.dwMaxBufIdx; ++i)
1299 waveInUnprepareHeader(stream->rec_strm.hWave.In,
1300 &(stream->rec_strm.WaveHdr[i]),
1301 sizeof(WAVEHDR));
1302 waveInClose(stream->rec_strm.hWave.In);
1303 stream->rec_strm.hWave.In = NULL;
1304 }
1305
1306 /* Close the record event */
1307 if (stream->rec_strm.hEvent)
1308 {
1309 CloseHandle(stream->rec_strm.hEvent);
1310 stream->rec_strm.hEvent = NULL;
1311 }
1312
1313 pj_pool_release(stream->pool);
1314
1315 return PJ_SUCCESS;
1316}
1317
Benny Prijono8eeab0b2009-03-04 19:00:28 +00001318#endif /* PJMEDIA_AUDIO_DEV_HAS_WMME */
1319