blob: 5b8461c6069c58dfd4b8e0c424c4665f63c940b3 [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;
Benny Prijonocdf2c9c2009-07-18 09:21:09 +0000518 DWORD flag;
Benny Prijono2058f472009-02-22 17:15:34 +0000519 pj_status_t status;
Benny Prijono598b01d2009-02-18 13:55:03 +0000520
Benny Prijono2058f472009-02-22 17:15:34 +0000521 PJ_ASSERT_RETURN(prm->play_id < (int)wf->dev_count, PJ_EINVAL);
Benny Prijono598b01d2009-02-18 13:55:03 +0000522
523 /*
524 * Create a wait event.
525 */
526 wmme_strm->hEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
527 if (NULL == wmme_strm->hEvent)
528 return pj_get_os_error();
529
530 /*
531 * Set up wave format structure for opening the device.
532 */
Benny Prijono2058f472009-02-22 17:15:34 +0000533 status = init_waveformatex(&wfx, prm);
534 if (status != PJ_SUCCESS)
535 return status;
536
537 ptime = prm->samples_per_frame * 1000 /
538 (prm->clock_rate * prm->channel_count);
Benny Prijono5fad7be2009-02-22 21:33:20 +0000539 parent->bytes_per_frame = wfx.nAvgBytesPerSec * ptime / 1000;
Benny Prijono598b01d2009-02-18 13:55:03 +0000540
Benny Prijonocdf2c9c2009-07-18 09:21:09 +0000541 flag = CALLBACK_EVENT;
542 if (prm->ext_fmt.id == PJMEDIA_FORMAT_L16)
543 flag |= WAVE_FORMAT_DIRECT;
544
Benny Prijono598b01d2009-02-18 13:55:03 +0000545 /*
546 * Open wave device.
547 */
Benny Prijono2058f472009-02-22 17:15:34 +0000548 mr = waveOutOpen(&wmme_strm->hWave.Out,
549 wf->dev_info[prm->play_id].deviceId,
Benny Prijonocdf2c9c2009-07-18 09:21:09 +0000550 &wfx, (DWORD)wmme_strm->hEvent, 0, flag);
Benny Prijono598b01d2009-02-18 13:55:03 +0000551 if (mr != MMSYSERR_NOERROR) {
Benny Prijono8eeab0b2009-03-04 19:00:28 +0000552 return PJMEDIA_AUDIODEV_ERRNO_FROM_WMME_OUT(mr);
Benny Prijono598b01d2009-02-18 13:55:03 +0000553 }
554
555 /* Pause the wave out device */
556 mr = waveOutPause(wmme_strm->hWave.Out);
557 if (mr != MMSYSERR_NOERROR) {
Benny Prijono8eeab0b2009-03-04 19:00:28 +0000558 return PJMEDIA_AUDIODEV_ERRNO_FROM_WMME_OUT(mr);
Benny Prijono598b01d2009-02-18 13:55:03 +0000559 }
560
561 /*
562 * Create the buffers.
563 */
564 wmme_strm->WaveHdr = (WAVEHDR*)
565 pj_pool_zalloc(pool, sizeof(WAVEHDR) * buffer_count);
566 for (i = 0; i < buffer_count; ++i) {
Benny Prijono2058f472009-02-22 17:15:34 +0000567 wmme_strm->WaveHdr[i].lpData = pj_pool_zalloc(pool,
568 parent->bytes_per_frame);
569 wmme_strm->WaveHdr[i].dwBufferLength = parent->bytes_per_frame;
Benny Prijono598b01d2009-02-18 13:55:03 +0000570 mr = waveOutPrepareHeader(wmme_strm->hWave.Out,
571 &(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 mr = waveOutWrite(wmme_strm->hWave.Out, &(wmme_strm->WaveHdr[i]),
577 sizeof(WAVEHDR));
578 if (mr != MMSYSERR_NOERROR) {
Benny Prijono8eeab0b2009-03-04 19:00:28 +0000579 return PJMEDIA_AUDIODEV_ERRNO_FROM_WMME_OUT(mr);
Benny Prijono598b01d2009-02-18 13:55:03 +0000580 }
581 }
582
583 wmme_strm->dwBufIdx = 0;
584 wmme_strm->dwMaxBufIdx = buffer_count;
585 wmme_strm->timestamp.u64 = 0;
586
587 /* Done setting up play device. */
Benny Prijono2e988792009-02-23 10:21:33 +0000588 PJ_LOG(4, (THIS_FILE,
Benny Prijono5fad7be2009-02-22 21:33:20 +0000589 " WaveAPI Sound player \"%s\" initialized ("
590 "format=%s, clock_rate=%d, "
Benny Prijono598b01d2009-02-18 13:55:03 +0000591 "channel_count=%d, samples_per_frame=%d (%dms))",
Benny Prijono2058f472009-02-22 17:15:34 +0000592 wf->dev_info[prm->play_id].info.name,
Benny Prijono5fad7be2009-02-22 21:33:20 +0000593 get_fmt_name(prm->ext_fmt.id),
Benny Prijono2058f472009-02-22 17:15:34 +0000594 prm->clock_rate, prm->channel_count, prm->samples_per_frame,
595 prm->samples_per_frame * 1000 / prm->clock_rate));
Benny Prijono598b01d2009-02-18 13:55:03 +0000596
597 return PJ_SUCCESS;
598}
599
600
601/* Internal: create Windows Multimedia recorder device */
602static pj_status_t init_capture_stream( struct wmme_factory *wf,
603 pj_pool_t *pool,
Benny Prijono2058f472009-02-22 17:15:34 +0000604 struct wmme_stream *parent,
Benny Prijono598b01d2009-02-18 13:55:03 +0000605 struct wmme_channel *wmme_strm,
Benny Prijono2058f472009-02-22 17:15:34 +0000606 const pjmedia_aud_param *prm,
Benny Prijono598b01d2009-02-18 13:55:03 +0000607 unsigned buffer_count)
608{
609 MMRESULT mr;
Benny Prijono2058f472009-02-22 17:15:34 +0000610 WAVEFORMATEX wfx;
Benny Prijonocdf2c9c2009-07-18 09:21:09 +0000611 DWORD flag;
Benny Prijono2058f472009-02-22 17:15:34 +0000612 unsigned i, ptime;
Benny Prijono598b01d2009-02-18 13:55:03 +0000613
Benny Prijono2058f472009-02-22 17:15:34 +0000614 PJ_ASSERT_RETURN(prm->rec_id < (int)wf->dev_count, PJ_EINVAL);
Benny Prijono598b01d2009-02-18 13:55:03 +0000615
616 /*
617 * Create a wait event.
618 */
619 wmme_strm->hEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
620 if (NULL == wmme_strm->hEvent) {
621 return pj_get_os_error();
622 }
623
624 /*
625 * Set up wave format structure for opening the device.
626 */
Benny Prijono2058f472009-02-22 17:15:34 +0000627 init_waveformatex(&wfx, prm);
628 ptime = prm->samples_per_frame * 1000 /
629 (prm->clock_rate * prm->channel_count);
Benny Prijono5fad7be2009-02-22 21:33:20 +0000630 parent->bytes_per_frame = wfx.nAvgBytesPerSec * ptime / 1000;
Benny Prijono598b01d2009-02-18 13:55:03 +0000631
Benny Prijonocdf2c9c2009-07-18 09:21:09 +0000632 flag = CALLBACK_EVENT;
633 if (prm->ext_fmt.id == PJMEDIA_FORMAT_L16)
634 flag |= WAVE_FORMAT_DIRECT;
635
Benny Prijono598b01d2009-02-18 13:55:03 +0000636 /*
637 * Open wave device.
638 */
Benny Prijono2058f472009-02-22 17:15:34 +0000639 mr = waveInOpen(&wmme_strm->hWave.In,
640 wf->dev_info[prm->rec_id].deviceId,
Benny Prijonocdf2c9c2009-07-18 09:21:09 +0000641 &wfx, (DWORD)wmme_strm->hEvent, 0, flag);
Benny Prijono598b01d2009-02-18 13:55:03 +0000642 if (mr != MMSYSERR_NOERROR) {
Benny Prijono8eeab0b2009-03-04 19:00:28 +0000643 return PJMEDIA_AUDIODEV_ERRNO_FROM_WMME_IN(mr);
Benny Prijono598b01d2009-02-18 13:55:03 +0000644 }
645
646 /*
647 * Create the buffers.
648 */
649 wmme_strm->WaveHdr = (WAVEHDR*)
650 pj_pool_zalloc(pool, sizeof(WAVEHDR) * buffer_count);
651 for (i = 0; i < buffer_count; ++i) {
Benny Prijono2058f472009-02-22 17:15:34 +0000652 wmme_strm->WaveHdr[i].lpData = pj_pool_zalloc(pool,
653 parent->bytes_per_frame);
654 wmme_strm->WaveHdr[i].dwBufferLength = parent->bytes_per_frame;
Benny Prijono598b01d2009-02-18 13:55:03 +0000655 mr = waveInPrepareHeader(wmme_strm->hWave.In,
656 &(wmme_strm->WaveHdr[i]),
657 sizeof(WAVEHDR));
658 if (mr != MMSYSERR_NOERROR) {
Benny Prijono8eeab0b2009-03-04 19:00:28 +0000659 return PJMEDIA_AUDIODEV_ERRNO_FROM_WMME_IN(mr);
Benny Prijono598b01d2009-02-18 13:55:03 +0000660 }
661 mr = waveInAddBuffer(wmme_strm->hWave.In, &(wmme_strm->WaveHdr[i]),
662 sizeof(WAVEHDR));
663 if (mr != MMSYSERR_NOERROR) {
Benny Prijono8eeab0b2009-03-04 19:00:28 +0000664 return PJMEDIA_AUDIODEV_ERRNO_FROM_WMME_IN(mr);
Benny Prijono598b01d2009-02-18 13:55:03 +0000665 }
666 }
667
668 wmme_strm->dwBufIdx = 0;
669 wmme_strm->dwMaxBufIdx = buffer_count;
670 wmme_strm->timestamp.u64 = 0;
671
672 /* Done setting up play device. */
Benny Prijono2e988792009-02-23 10:21:33 +0000673 PJ_LOG(4,(THIS_FILE,
Benny Prijono5fad7be2009-02-22 21:33:20 +0000674 " WaveAPI Sound recorder \"%s\" initialized "
675 "(format=%s, clock_rate=%d, "
Benny Prijono598b01d2009-02-18 13:55:03 +0000676 "channel_count=%d, samples_per_frame=%d (%dms))",
Benny Prijono2058f472009-02-22 17:15:34 +0000677 wf->dev_info[prm->rec_id].info.name,
Benny Prijono5fad7be2009-02-22 21:33:20 +0000678 get_fmt_name(prm->ext_fmt.id),
Benny Prijono2058f472009-02-22 17:15:34 +0000679 prm->clock_rate, prm->channel_count, prm->samples_per_frame,
680 prm->samples_per_frame * 1000 / prm->clock_rate));
Benny Prijono598b01d2009-02-18 13:55:03 +0000681
682 return PJ_SUCCESS;
683}
684
685
686/* WMME capture and playback thread. */
687static int PJ_THREAD_FUNC wmme_dev_thread(void *arg)
688{
689 struct wmme_stream *strm = (struct wmme_stream*)arg;
690 HANDLE events[3];
691 unsigned eventCount;
Benny Prijono598b01d2009-02-18 13:55:03 +0000692 pj_status_t status = PJ_SUCCESS;
Benny Prijono5fad7be2009-02-22 21:33:20 +0000693 static unsigned rec_cnt, play_cnt;
Nanang Izzuddin719f7392009-03-27 15:22:35 +0000694 enum { MAX_BURST = 1000 };
Benny Prijono5fad7be2009-02-22 21:33:20 +0000695
696 rec_cnt = play_cnt = 0;
Benny Prijono598b01d2009-02-18 13:55:03 +0000697
Benny Prijono598b01d2009-02-18 13:55:03 +0000698 eventCount = 0;
699 events[eventCount++] = strm->thread_quit_event;
Benny Prijono7a380002009-03-09 12:55:29 +0000700 if (strm->param.dir & PJMEDIA_DIR_PLAYBACK)
Benny Prijono598b01d2009-02-18 13:55:03 +0000701 events[eventCount++] = strm->play_strm.hEvent;
Benny Prijono7a380002009-03-09 12:55:29 +0000702 if (strm->param.dir & PJMEDIA_DIR_CAPTURE)
Benny Prijono598b01d2009-02-18 13:55:03 +0000703 events[eventCount++] = strm->rec_strm.hEvent;
704
705
706 /* Raise self priority. We don't want the audio to be distorted by
707 * system activity.
708 */
709#if defined(PJ_WIN32_WINCE) && PJ_WIN32_WINCE != 0
Benny Prijono7a380002009-03-09 12:55:29 +0000710 if (strm->param.dir & PJMEDIA_DIR_PLAYBACK)
Benny Prijono598b01d2009-02-18 13:55:03 +0000711 CeSetThreadPriority(GetCurrentThread(), 153);
712 else
713 CeSetThreadPriority(GetCurrentThread(), 247);
714#else
715 SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_TIME_CRITICAL);
716#endif
717
Benny Prijono598b01d2009-02-18 13:55:03 +0000718 /*
719 * Loop while not signalled to quit, wait for event objects to be
720 * signalled by WMME capture and play buffer.
721 */
722 while (status == PJ_SUCCESS)
723 {
724
725 DWORD rc;
726 pjmedia_dir signalled_dir;
727
Benny Prijonocf6b5902009-03-10 12:05:23 +0000728 /* Swap hWaveIn and hWaveOut to get equal opportunity for both */
Benny Prijono5fad7be2009-02-22 21:33:20 +0000729 if (eventCount==3) {
730 HANDLE hTemp = events[2];
731 events[2] = events[1];
732 events[1] = hTemp;
733 }
734
Benny Prijono598b01d2009-02-18 13:55:03 +0000735 rc = WaitForMultipleObjects(eventCount, events, FALSE, INFINITE);
736 if (rc < WAIT_OBJECT_0 || rc >= WAIT_OBJECT_0 + eventCount)
737 continue;
738
739 if (rc == WAIT_OBJECT_0)
740 break;
741
742 if (rc == (WAIT_OBJECT_0 + 1))
743 {
744 if (events[1] == strm->play_strm.hEvent)
745 signalled_dir = PJMEDIA_DIR_PLAYBACK;
746 else
747 signalled_dir = PJMEDIA_DIR_CAPTURE;
748 }
749 else
750 {
751 if (events[2] == strm->play_strm.hEvent)
752 signalled_dir = PJMEDIA_DIR_PLAYBACK;
753 else
754 signalled_dir = PJMEDIA_DIR_CAPTURE;
755 }
756
757
758 if (signalled_dir == PJMEDIA_DIR_PLAYBACK)
759 {
760 struct wmme_channel *wmme_strm = &strm->play_strm;
Benny Prijonocf6b5902009-03-10 12:05:23 +0000761 unsigned burst;
Benny Prijono2058f472009-02-22 17:15:34 +0000762
Benny Prijono598b01d2009-02-18 13:55:03 +0000763 status = PJ_SUCCESS;
764
765 /*
766 * Windows Multimedia has requested us to feed some frames to
767 * playback buffer.
768 */
769
Benny Prijonocf6b5902009-03-10 12:05:23 +0000770 for (burst=0; burst<MAX_BURST &&
771 (wmme_strm->WaveHdr[wmme_strm->dwBufIdx].dwFlags & WHDR_DONE);
772 ++burst)
Benny Prijono598b01d2009-02-18 13:55:03 +0000773 {
774 void *buffer = wmme_strm->WaveHdr[wmme_strm->dwBufIdx].lpData;
Benny Prijono2058f472009-02-22 17:15:34 +0000775 pjmedia_frame pcm_frame, *frame;
Benny Prijono5fad7be2009-02-22 21:33:20 +0000776 MMRESULT mr = MMSYSERR_NOERROR;
Benny Prijono598b01d2009-02-18 13:55:03 +0000777
778 //PJ_LOG(5,(THIS_FILE, "Finished writing buffer %d",
779 // wmme_strm->dwBufIdx));
780
Benny Prijono5fad7be2009-02-22 21:33:20 +0000781 if (strm->fmt_id == PJMEDIA_FORMAT_L16) {
Benny Prijono2058f472009-02-22 17:15:34 +0000782 /* PCM mode */
783 frame = &pcm_frame;
784
785 frame->type = PJMEDIA_FRAME_TYPE_AUDIO;
786 frame->size = strm->bytes_per_frame;
787 frame->buf = buffer;
788 frame->timestamp.u64 = wmme_strm->timestamp.u64;
789 frame->bit_info = 0;
790 } else {
791 /* Codec mode */
792 frame = &strm->xfrm->base;
793
794 strm->xfrm->base.type = PJMEDIA_FRAME_TYPE_EXTENDED;
795 strm->xfrm->base.size = strm->bytes_per_frame;
796 strm->xfrm->base.buf = NULL;
797 strm->xfrm->base.timestamp.u64 = wmme_strm->timestamp.u64;
798 strm->xfrm->base.bit_info = 0;
799 }
Benny Prijono598b01d2009-02-18 13:55:03 +0000800
801 /* Get frame from application. */
Benny Prijono2e988792009-02-23 10:21:33 +0000802 //PJ_LOG(5,(THIS_FILE, "xxx %u play_cb", play_cnt++));
Benny Prijono2058f472009-02-22 17:15:34 +0000803 status = (*strm->play_cb)(strm->user_data, frame);
Benny Prijono598b01d2009-02-18 13:55:03 +0000804
805 if (status != PJ_SUCCESS)
806 break;
807
Benny Prijono5fad7be2009-02-22 21:33:20 +0000808 if (strm->fmt_id == PJMEDIA_FORMAT_L16) {
Benny Prijono2058f472009-02-22 17:15:34 +0000809 /* PCM mode */
810 if (frame->type == PJMEDIA_FRAME_TYPE_NONE) {
811 pj_bzero(buffer, strm->bytes_per_frame);
Benny Prijono2058f472009-02-22 17:15:34 +0000812 } else if (frame->type == PJMEDIA_FRAME_TYPE_EXTENDED) {
813 pj_assert(!"Frame type not supported");
814 } else if (frame->type == PJMEDIA_FRAME_TYPE_AUDIO) {
Benny Prijono5fad7be2009-02-22 21:33:20 +0000815 /* Nothing to do */
Benny Prijono2058f472009-02-22 17:15:34 +0000816 } else {
817 pj_assert(!"Frame type not supported");
818 }
819 } else {
820 /* Codec mode */
821 if (frame->type == PJMEDIA_FRAME_TYPE_NONE) {
Benny Prijono5fad7be2009-02-22 21:33:20 +0000822 pj_memset(buffer, strm->silence_char,
823 strm->bytes_per_frame);
Benny Prijono2058f472009-02-22 17:15:34 +0000824 } else if (frame->type == PJMEDIA_FRAME_TYPE_EXTENDED) {
825 unsigned sz;
826 sz = pjmedia_frame_ext_copy_payload(strm->xfrm,
827 buffer,
828 strm->bytes_per_frame);
Benny Prijono5fad7be2009-02-22 21:33:20 +0000829 if (sz < strm->bytes_per_frame) {
830 pj_memset((char*)buffer+sz,
831 strm->silence_char,
832 strm->bytes_per_frame - sz);
833 }
Benny Prijono2058f472009-02-22 17:15:34 +0000834 } else {
835 pj_assert(!"Frame type not supported");
836 }
Benny Prijono598b01d2009-02-18 13:55:03 +0000837 }
838
839 /* Write to the device. */
Benny Prijono5fad7be2009-02-22 21:33:20 +0000840 mr = waveOutWrite(wmme_strm->hWave.Out,
841 &(wmme_strm->WaveHdr[wmme_strm->dwBufIdx]),
842 sizeof(WAVEHDR));
843 if (mr != MMSYSERR_NOERROR) {
Benny Prijono8eeab0b2009-03-04 19:00:28 +0000844 status = PJMEDIA_AUDIODEV_ERRNO_FROM_WMME_OUT(mr);
Benny Prijono5fad7be2009-02-22 21:33:20 +0000845 break;
Benny Prijono598b01d2009-02-18 13:55:03 +0000846 }
847
848 /* Increment position. */
849 if (++wmme_strm->dwBufIdx >= wmme_strm->dwMaxBufIdx)
850 wmme_strm->dwBufIdx = 0;
Benny Prijono7a380002009-03-09 12:55:29 +0000851 wmme_strm->timestamp.u64 += strm->param.samples_per_frame /
852 strm->param.channel_count;
Benny Prijonocf6b5902009-03-10 12:05:23 +0000853 } /* for */
Benny Prijono598b01d2009-02-18 13:55:03 +0000854 }
855 else
856 {
857 struct wmme_channel *wmme_strm = &strm->rec_strm;
Benny Prijonocf6b5902009-03-10 12:05:23 +0000858 unsigned burst;
Benny Prijono598b01d2009-02-18 13:55:03 +0000859 MMRESULT mr = MMSYSERR_NOERROR;
860 status = PJ_SUCCESS;
861
862 /*
863 * Windows Multimedia has indicated that it has some frames ready
864 * in the capture buffer. Get as much frames as possible to
865 * prevent overflows.
866 */
867#if 0
868 {
869 static DWORD tc = 0;
870 DWORD now = GetTickCount();
871 DWORD i = 0;
872 DWORD bits = 0;
873
874 if (tc == 0) tc = now;
875
876 for (i = 0; i < wmme_strm->dwMaxBufIdx; ++i)
877 {
878 bits = bits << 4;
879 bits |= wmme_strm->WaveHdr[i].dwFlags & WHDR_DONE;
880 }
881 PJ_LOG(5,(THIS_FILE, "Record Signal> Index: %d, Delta: %4.4d, "
882 "Flags: %6.6x\n",
883 wmme_strm->dwBufIdx,
884 now - tc,
885 bits));
886 tc = now;
887 }
888#endif
889
Benny Prijonocf6b5902009-03-10 12:05:23 +0000890 for (burst=0; burst<MAX_BURST &&
891 (wmme_strm->WaveHdr[wmme_strm->dwBufIdx].dwFlags & WHDR_DONE);
892 ++burst)
Benny Prijono598b01d2009-02-18 13:55:03 +0000893 {
894 char* buffer = (char*)
895 wmme_strm->WaveHdr[wmme_strm->dwBufIdx].lpData;
896 unsigned cap_len =
897 wmme_strm->WaveHdr[wmme_strm->dwBufIdx].dwBytesRecorded;
Benny Prijono2058f472009-02-22 17:15:34 +0000898 pjmedia_frame pcm_frame, *frame;
Benny Prijono598b01d2009-02-18 13:55:03 +0000899
900 /*
901 PJ_LOG(5,(THIS_FILE, "Read %d bytes from buffer %d", cap_len,
902 wmme_strm->dwBufIdx));
903 */
Benny Prijono2058f472009-02-22 17:15:34 +0000904
Benny Prijono5fad7be2009-02-22 21:33:20 +0000905 if (strm->fmt_id == PJMEDIA_FORMAT_L16) {
Benny Prijono2058f472009-02-22 17:15:34 +0000906 /* PCM mode */
907 if (cap_len < strm->bytes_per_frame)
908 pj_bzero(buffer + cap_len,
909 strm->bytes_per_frame - cap_len);
Benny Prijono598b01d2009-02-18 13:55:03 +0000910
Benny Prijono2058f472009-02-22 17:15:34 +0000911 /* Copy the audio data out of the wave buffer. */
912 pj_memcpy(strm->buffer, buffer, strm->bytes_per_frame);
Benny Prijono598b01d2009-02-18 13:55:03 +0000913
Benny Prijono2058f472009-02-22 17:15:34 +0000914 /* Prepare frame */
915 frame = &pcm_frame;
916 frame->type = PJMEDIA_FRAME_TYPE_AUDIO;
917 frame->buf = strm->buffer;
918 frame->size = strm->bytes_per_frame;
919 frame->timestamp.u64 = wmme_strm->timestamp.u64;
920 frame->bit_info = 0;
921
922 } else {
923 /* Codec mode */
924 frame = &strm->xfrm->base;
925
926 frame->type = PJMEDIA_FRAME_TYPE_EXTENDED;
927 frame->buf = NULL;
928 frame->size = strm->bytes_per_frame;
929 frame->timestamp.u64 = wmme_strm->timestamp.u64;
930 frame->bit_info = 0;
931
932 strm->xfrm->samples_cnt = 0;
933 strm->xfrm->subframe_cnt = 0;
Benny Prijono7a380002009-03-09 12:55:29 +0000934 pjmedia_frame_ext_append_subframe(
935 strm->xfrm, buffer,
936 strm->bytes_per_frame *8,
937 strm->param.samples_per_frame
938 );
Benny Prijono2058f472009-02-22 17:15:34 +0000939 }
Benny Prijono598b01d2009-02-18 13:55:03 +0000940
941 /* Re-add the buffer to the device. */
942 mr = waveInAddBuffer(wmme_strm->hWave.In,
943 &(wmme_strm->WaveHdr[wmme_strm->dwBufIdx]),
944 sizeof(WAVEHDR));
945 if (mr != MMSYSERR_NOERROR) {
Benny Prijono8eeab0b2009-03-04 19:00:28 +0000946 status = PJMEDIA_AUDIODEV_ERRNO_FROM_WMME_IN(mr);
Benny Prijono598b01d2009-02-18 13:55:03 +0000947 break;
948 }
949
Benny Prijono598b01d2009-02-18 13:55:03 +0000950
951 /* Call callback */
Benny Prijono2e988792009-02-23 10:21:33 +0000952 //PJ_LOG(5,(THIS_FILE, "xxx %u rec_cb", rec_cnt++));
Benny Prijono2058f472009-02-22 17:15:34 +0000953 status = (*strm->rec_cb)(strm->user_data, frame);
Benny Prijono598b01d2009-02-18 13:55:03 +0000954 if (status != PJ_SUCCESS)
955 break;
956
957 /* Increment position. */
958 if (++wmme_strm->dwBufIdx >= wmme_strm->dwMaxBufIdx)
959 wmme_strm->dwBufIdx = 0;
Benny Prijono7a380002009-03-09 12:55:29 +0000960 wmme_strm->timestamp.u64 += strm->param.samples_per_frame /
961 strm->param.channel_count;
Benny Prijonocf6b5902009-03-10 12:05:23 +0000962 } /* for */
Benny Prijono598b01d2009-02-18 13:55:03 +0000963 }
964 }
965
966 PJ_LOG(5,(THIS_FILE, "WMME: thread stopping.."));
967 return 0;
968}
969
970
971/* API: create stream */
972static pj_status_t factory_create_stream(pjmedia_aud_dev_factory *f,
Benny Prijono10454dc2009-02-21 14:21:59 +0000973 const pjmedia_aud_param *param,
Benny Prijono598b01d2009-02-18 13:55:03 +0000974 pjmedia_aud_rec_cb rec_cb,
975 pjmedia_aud_play_cb play_cb,
976 void *user_data,
977 pjmedia_aud_stream **p_aud_strm)
978{
979 struct wmme_factory *wf = (struct wmme_factory*)f;
980 pj_pool_t *pool;
981 struct wmme_stream *strm;
Benny Prijono5fad7be2009-02-22 21:33:20 +0000982 pj_uint8_t silence_char;
Benny Prijono598b01d2009-02-18 13:55:03 +0000983 pj_status_t status;
984
Benny Prijono5fad7be2009-02-22 21:33:20 +0000985 switch (param->ext_fmt.id) {
986 case PJMEDIA_FORMAT_L16:
987 silence_char = '\0';
988 break;
989 case PJMEDIA_FORMAT_ALAW:
990 silence_char = (pj_uint8_t)'\xd5';
991 break;
992 case PJMEDIA_FORMAT_ULAW:
993 silence_char = (pj_uint8_t)'\xff';
994 break;
995 default:
996 return PJMEDIA_EAUD_BADFORMAT;
997 }
Benny Prijono598b01d2009-02-18 13:55:03 +0000998
999 /* Create and Initialize stream descriptor */
1000 pool = pj_pool_create(wf->pf, "wmme-dev", 1000, 1000, NULL);
1001 PJ_ASSERT_RETURN(pool != NULL, PJ_ENOMEM);
1002
1003 strm = PJ_POOL_ZALLOC_T(pool, struct wmme_stream);
Benny Prijono7a380002009-03-09 12:55:29 +00001004 pj_memcpy(&strm->param, param, sizeof(*param));
Benny Prijono598b01d2009-02-18 13:55:03 +00001005 strm->pool = pool;
1006 strm->rec_cb = rec_cb;
1007 strm->play_cb = play_cb;
1008 strm->user_data = user_data;
Benny Prijono5fad7be2009-02-22 21:33:20 +00001009 strm->fmt_id = param->ext_fmt.id;
1010 strm->silence_char = silence_char;
Benny Prijono598b01d2009-02-18 13:55:03 +00001011
1012 /* Create player stream */
1013 if (param->dir & PJMEDIA_DIR_PLAYBACK) {
1014 unsigned buf_count;
1015
Benny Prijono7a380002009-03-09 12:55:29 +00001016 if ((param->flags & PJMEDIA_AUD_DEV_CAP_OUTPUT_LATENCY)==0) {
1017 strm->param.flags |= PJMEDIA_AUD_DEV_CAP_OUTPUT_LATENCY;
1018 strm->param.output_latency_ms = PJMEDIA_SND_DEFAULT_PLAY_LATENCY;
1019 }
Benny Prijono598b01d2009-02-18 13:55:03 +00001020
Benny Prijono7a380002009-03-09 12:55:29 +00001021 buf_count = strm->param.output_latency_ms * param->clock_rate *
Benny Prijono598b01d2009-02-18 13:55:03 +00001022 param->channel_count / param->samples_per_frame / 1000;
1023
1024 status = init_player_stream(wf, strm->pool,
Benny Prijono2058f472009-02-22 17:15:34 +00001025 strm,
Benny Prijono598b01d2009-02-18 13:55:03 +00001026 &strm->play_strm,
Benny Prijono2058f472009-02-22 17:15:34 +00001027 param,
Benny Prijono598b01d2009-02-18 13:55:03 +00001028 buf_count);
1029
1030 if (status != PJ_SUCCESS) {
1031 stream_destroy(&strm->base);
1032 return status;
1033 }
1034 }
1035
1036 /* Create capture stream */
1037 if (param->dir & PJMEDIA_DIR_CAPTURE) {
1038 unsigned buf_count;
1039
Benny Prijono7a380002009-03-09 12:55:29 +00001040 if ((param->flags & PJMEDIA_AUD_DEV_CAP_INPUT_LATENCY)==0) {
1041 strm->param.flags |= PJMEDIA_AUD_DEV_CAP_INPUT_LATENCY;
1042 strm->param.input_latency_ms = PJMEDIA_SND_DEFAULT_REC_LATENCY;
1043 }
Benny Prijono598b01d2009-02-18 13:55:03 +00001044
Benny Prijono7a380002009-03-09 12:55:29 +00001045 buf_count = strm->param.input_latency_ms * param->clock_rate *
Benny Prijono598b01d2009-02-18 13:55:03 +00001046 param->channel_count / param->samples_per_frame / 1000;
1047
1048 status = init_capture_stream(wf, strm->pool,
Benny Prijono2058f472009-02-22 17:15:34 +00001049 strm,
Benny Prijono598b01d2009-02-18 13:55:03 +00001050 &strm->rec_strm,
Benny Prijono2058f472009-02-22 17:15:34 +00001051 param,
Benny Prijono598b01d2009-02-18 13:55:03 +00001052 buf_count);
1053
1054 if (status != PJ_SUCCESS) {
1055 stream_destroy(&strm->base);
1056 return status;
1057 }
1058 }
1059
Benny Prijono5fad7be2009-02-22 21:33:20 +00001060 strm->buffer = pj_pool_alloc(pool, strm->bytes_per_frame);
1061 if (!strm->buffer) {
1062 pj_pool_release(pool);
1063 return PJ_ENOMEM;
1064 }
1065
Benny Prijono2058f472009-02-22 17:15:34 +00001066 /* If format is extended, must create buffer for the extended frame. */
Benny Prijono5fad7be2009-02-22 21:33:20 +00001067 if (strm->fmt_id != PJMEDIA_FORMAT_L16) {
Benny Prijono2058f472009-02-22 17:15:34 +00001068 strm->xfrm_size = sizeof(pjmedia_frame_ext) +
1069 32 * sizeof(pjmedia_frame_ext_subframe) +
Benny Prijono5fad7be2009-02-22 21:33:20 +00001070 strm->bytes_per_frame + 4;
Benny Prijono2058f472009-02-22 17:15:34 +00001071 strm->xfrm = (pjmedia_frame_ext*)
1072 pj_pool_alloc(pool, strm->xfrm_size);
1073 }
1074
Benny Prijono598b01d2009-02-18 13:55:03 +00001075 /* Create the stop event */
1076 strm->thread_quit_event = CreateEvent(NULL, FALSE, FALSE, NULL);
1077 if (strm->thread_quit_event == NULL) {
1078 status = pj_get_os_error();
1079 stream_destroy(&strm->base);
1080 return status;
1081 }
1082
1083 /* Create and start the thread */
1084 status = pj_thread_create(pool, "wmme", &wmme_dev_thread, strm, 0, 0,
1085 &strm->thread);
1086 if (status != PJ_SUCCESS) {
1087 stream_destroy(&strm->base);
1088 return status;
1089 }
1090
Benny Prijono7a380002009-03-09 12:55:29 +00001091 /* Apply the remaining settings */
1092 if (param->flags & PJMEDIA_AUD_DEV_CAP_OUTPUT_VOLUME_SETTING) {
1093 stream_set_cap(&strm->base, PJMEDIA_AUD_DEV_CAP_OUTPUT_VOLUME_SETTING,
1094 &param->output_vol);
1095 }
1096
1097
Benny Prijono598b01d2009-02-18 13:55:03 +00001098 /* Done */
1099 strm->base.op = &stream_op;
1100 *p_aud_strm = &strm->base;
1101
1102 return PJ_SUCCESS;
1103}
1104
1105/* API: Get stream info. */
1106static pj_status_t stream_get_param(pjmedia_aud_stream *s,
Benny Prijono10454dc2009-02-21 14:21:59 +00001107 pjmedia_aud_param *pi)
Benny Prijono598b01d2009-02-18 13:55:03 +00001108{
1109 struct wmme_stream *strm = (struct wmme_stream*)s;
1110
1111 PJ_ASSERT_RETURN(strm && pi, PJ_EINVAL);
1112
Benny Prijono7a380002009-03-09 12:55:29 +00001113 pj_memcpy(pi, &strm->param, sizeof(*pi));
Benny Prijono598b01d2009-02-18 13:55:03 +00001114
Benny Prijono85bdaa82009-03-09 13:22:01 +00001115 /* Update the volume setting */
1116 if (stream_get_cap(s, PJMEDIA_AUD_DEV_CAP_OUTPUT_VOLUME_SETTING,
1117 &pi->output_vol) == PJ_SUCCESS)
1118 {
1119 pi->flags |= PJMEDIA_AUD_DEV_CAP_OUTPUT_VOLUME_SETTING;
1120 }
1121
Benny Prijono598b01d2009-02-18 13:55:03 +00001122 return PJ_SUCCESS;
1123}
1124
1125/* API: get capability */
1126static pj_status_t stream_get_cap(pjmedia_aud_stream *s,
1127 pjmedia_aud_dev_cap cap,
1128 void *pval)
1129{
1130 struct wmme_stream *strm = (struct wmme_stream*)s;
1131
1132 PJ_ASSERT_RETURN(s && pval, PJ_EINVAL);
1133
1134 if (cap==PJMEDIA_AUD_DEV_CAP_INPUT_LATENCY &&
Benny Prijono7a380002009-03-09 12:55:29 +00001135 (strm->param.dir & PJMEDIA_DIR_CAPTURE))
Benny Prijono598b01d2009-02-18 13:55:03 +00001136 {
1137 /* Recording latency */
Benny Prijono7a380002009-03-09 12:55:29 +00001138 *(unsigned*)pval = strm->param.input_latency_ms;
Benny Prijono598b01d2009-02-18 13:55:03 +00001139 return PJ_SUCCESS;
1140 } else if (cap==PJMEDIA_AUD_DEV_CAP_OUTPUT_LATENCY &&
Benny Prijono7a380002009-03-09 12:55:29 +00001141 (strm->param.dir & PJMEDIA_DIR_PLAYBACK))
Benny Prijono598b01d2009-02-18 13:55:03 +00001142 {
1143 /* Playback latency */
Benny Prijono7a380002009-03-09 12:55:29 +00001144 *(unsigned*)pval = strm->param.output_latency_ms;
Benny Prijono598b01d2009-02-18 13:55:03 +00001145 return PJ_SUCCESS;
1146 } else if (cap==PJMEDIA_AUD_DEV_CAP_OUTPUT_VOLUME_SETTING &&
1147 strm->play_strm.hWave.Out)
1148 {
1149 /* Output volume setting */
Benny Prijono7a380002009-03-09 12:55:29 +00001150 DWORD waveVol;
Benny Prijono598b01d2009-02-18 13:55:03 +00001151 MMRESULT mr;
1152
Benny Prijono7a380002009-03-09 12:55:29 +00001153 mr = waveOutGetVolume(strm->play_strm.hWave.Out, &waveVol);
Benny Prijono598b01d2009-02-18 13:55:03 +00001154 if (mr != MMSYSERR_NOERROR) {
Benny Prijono8eeab0b2009-03-04 19:00:28 +00001155 return PJMEDIA_AUDIODEV_ERRNO_FROM_WMME_OUT(mr);
Benny Prijono598b01d2009-02-18 13:55:03 +00001156 }
1157
Benny Prijono7a380002009-03-09 12:55:29 +00001158 waveVol &= 0xFFFF;
1159 *(unsigned*)pval = (waveVol * 100) / 0xFFFF;
Benny Prijono598b01d2009-02-18 13:55:03 +00001160 return PJ_SUCCESS;
1161 } else {
Benny Prijono64f91382009-03-05 18:02:28 +00001162 return PJMEDIA_EAUD_INVCAP;
Benny Prijono598b01d2009-02-18 13:55:03 +00001163 }
1164}
1165
1166/* API: set capability */
1167static pj_status_t stream_set_cap(pjmedia_aud_stream *s,
1168 pjmedia_aud_dev_cap cap,
1169 const void *pval)
1170{
1171 struct wmme_stream *strm = (struct wmme_stream*)s;
1172
1173 PJ_ASSERT_RETURN(s && pval, PJ_EINVAL);
1174
1175 if (cap==PJMEDIA_AUD_DEV_CAP_OUTPUT_VOLUME_SETTING &&
1176 strm->play_strm.hWave.Out)
1177 {
1178 /* Output volume setting */
Benny Prijono7a380002009-03-09 12:55:29 +00001179 unsigned vol = *(unsigned*)pval;
1180 DWORD waveVol;
Benny Prijono598b01d2009-02-18 13:55:03 +00001181 MMRESULT mr;
Benny Prijono7a380002009-03-09 12:55:29 +00001182 pj_status_t status;
Benny Prijono598b01d2009-02-18 13:55:03 +00001183
Benny Prijono7a380002009-03-09 12:55:29 +00001184 if (vol > 100)
1185 vol = 100;
Benny Prijono598b01d2009-02-18 13:55:03 +00001186
Benny Prijono7a380002009-03-09 12:55:29 +00001187 waveVol = (vol * 0xFFFF) / 100;
1188 waveVol |= (waveVol << 16);
1189
1190 mr = waveOutSetVolume(strm->play_strm.hWave.Out, waveVol);
1191 status = (mr==MMSYSERR_NOERROR)? PJ_SUCCESS :
Benny Prijono8eeab0b2009-03-04 19:00:28 +00001192 PJMEDIA_AUDIODEV_ERRNO_FROM_WMME_OUT(mr);
Benny Prijono7a380002009-03-09 12:55:29 +00001193 if (status == PJ_SUCCESS) {
1194 strm->param.output_vol = *(unsigned*)pval;
1195 }
1196 return status;
Benny Prijono598b01d2009-02-18 13:55:03 +00001197 }
1198
Benny Prijono64f91382009-03-05 18:02:28 +00001199 return PJMEDIA_EAUD_INVCAP;
Benny Prijono598b01d2009-02-18 13:55:03 +00001200}
1201
1202/* API: Start stream. */
1203static pj_status_t stream_start(pjmedia_aud_stream *strm)
1204{
1205 struct wmme_stream *stream = (struct wmme_stream*)strm;
1206 MMRESULT mr;
1207
1208 if (stream->play_strm.hWave.Out != NULL)
1209 {
1210 mr = waveOutRestart(stream->play_strm.hWave.Out);
1211 if (mr != MMSYSERR_NOERROR) {
Benny Prijono8eeab0b2009-03-04 19:00:28 +00001212 return PJMEDIA_AUDIODEV_ERRNO_FROM_WMME_OUT(mr);
Benny Prijono598b01d2009-02-18 13:55:03 +00001213 }
Benny Prijono2e988792009-02-23 10:21:33 +00001214 PJ_LOG(4,(THIS_FILE, "WMME playback stream started"));
Benny Prijono598b01d2009-02-18 13:55:03 +00001215 }
1216
1217 if (stream->rec_strm.hWave.In != NULL)
1218 {
1219 mr = waveInStart(stream->rec_strm.hWave.In);
1220 if (mr != MMSYSERR_NOERROR) {
Benny Prijono8eeab0b2009-03-04 19:00:28 +00001221 return PJMEDIA_AUDIODEV_ERRNO_FROM_WMME_IN(mr);
Benny Prijono598b01d2009-02-18 13:55:03 +00001222 }
Benny Prijono2e988792009-02-23 10:21:33 +00001223 PJ_LOG(4,(THIS_FILE, "WMME capture stream started"));
Benny Prijono598b01d2009-02-18 13:55:03 +00001224 }
1225
1226 return PJ_SUCCESS;
1227}
1228
1229/* API: Stop stream. */
1230static pj_status_t stream_stop(pjmedia_aud_stream *strm)
1231{
1232 struct wmme_stream *stream = (struct wmme_stream*)strm;
1233 MMRESULT mr;
1234
1235 PJ_ASSERT_RETURN(stream != NULL, PJ_EINVAL);
1236
1237 if (stream->play_strm.hWave.Out != NULL)
1238 {
1239 mr = waveOutPause(stream->play_strm.hWave.Out);
1240 if (mr != MMSYSERR_NOERROR) {
Benny Prijono8eeab0b2009-03-04 19:00:28 +00001241 return PJMEDIA_AUDIODEV_ERRNO_FROM_WMME_OUT(mr);
Benny Prijono598b01d2009-02-18 13:55:03 +00001242 }
Benny Prijono2e988792009-02-23 10:21:33 +00001243 PJ_LOG(4,(THIS_FILE, "Stopped WMME playback stream"));
Benny Prijono598b01d2009-02-18 13:55:03 +00001244 }
1245
1246 if (stream->rec_strm.hWave.In != NULL)
1247 {
1248 mr = waveInStop(stream->rec_strm.hWave.In);
1249 if (mr != MMSYSERR_NOERROR) {
Benny Prijono8eeab0b2009-03-04 19:00:28 +00001250 return PJMEDIA_AUDIODEV_ERRNO_FROM_WMME_IN(mr);
Benny Prijono598b01d2009-02-18 13:55:03 +00001251 }
Benny Prijono2e988792009-02-23 10:21:33 +00001252 PJ_LOG(4,(THIS_FILE, "Stopped WMME capture stream"));
Benny Prijono598b01d2009-02-18 13:55:03 +00001253 }
1254
1255 return PJ_SUCCESS;
1256}
1257
1258
1259/* API: Destroy stream. */
1260static pj_status_t stream_destroy(pjmedia_aud_stream *strm)
1261{
1262 struct wmme_stream *stream = (struct wmme_stream*)strm;
1263 unsigned i;
1264
1265 PJ_ASSERT_RETURN(stream != NULL, PJ_EINVAL);
1266
1267 stream_stop(strm);
1268
Nanang Izzuddinf46a69b2009-03-27 15:36:18 +00001269 /* Stop the stream thread */
Benny Prijono598b01d2009-02-18 13:55:03 +00001270 if (stream->thread)
1271 {
1272 SetEvent(stream->thread_quit_event);
1273 pj_thread_join(stream->thread);
1274 pj_thread_destroy(stream->thread);
1275 stream->thread = NULL;
1276 }
1277
Nanang Izzuddinf46a69b2009-03-27 15:36:18 +00001278 /* Close the thread quit event */
1279 if (stream->thread_quit_event)
1280 {
1281 CloseHandle(stream->thread_quit_event);
1282 stream->thread_quit_event = NULL;
1283 }
1284
Benny Prijono598b01d2009-02-18 13:55:03 +00001285 /* Unprepare the headers and close the play device */
1286 if (stream->play_strm.hWave.Out)
1287 {
1288 waveOutReset(stream->play_strm.hWave.Out);
1289 for (i = 0; i < stream->play_strm.dwMaxBufIdx; ++i)
1290 waveOutUnprepareHeader(stream->play_strm.hWave.Out,
1291 &(stream->play_strm.WaveHdr[i]),
1292 sizeof(WAVEHDR));
1293 waveOutClose(stream->play_strm.hWave.Out);
1294 stream->play_strm.hWave.Out = NULL;
1295 }
1296
1297 /* Close the play event */
1298 if (stream->play_strm.hEvent)
1299 {
1300 CloseHandle(stream->play_strm.hEvent);
1301 stream->play_strm.hEvent = NULL;
1302 }
1303
1304 /* Unprepare the headers and close the record device */
1305 if (stream->rec_strm.hWave.In)
1306 {
1307 waveInReset(stream->rec_strm.hWave.In);
1308 for (i = 0; i < stream->play_strm.dwMaxBufIdx; ++i)
1309 waveInUnprepareHeader(stream->rec_strm.hWave.In,
1310 &(stream->rec_strm.WaveHdr[i]),
1311 sizeof(WAVEHDR));
1312 waveInClose(stream->rec_strm.hWave.In);
1313 stream->rec_strm.hWave.In = NULL;
1314 }
1315
1316 /* Close the record event */
1317 if (stream->rec_strm.hEvent)
1318 {
1319 CloseHandle(stream->rec_strm.hEvent);
1320 stream->rec_strm.hEvent = NULL;
1321 }
1322
1323 pj_pool_release(stream->pool);
1324
1325 return PJ_SUCCESS;
1326}
1327
Benny Prijono8eeab0b2009-03-04 19:00:28 +00001328#endif /* PJMEDIA_AUDIO_DEV_HAS_WMME */
1329