blob: 9630ceb7bf8716d231217d1166336c7ecf3add01 [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
41#if defined(PJ_WIN32_WINCE) && PJ_WIN32_WINCE!=0
42# pragma comment(lib, "Coredll.lib")
43#elif defined(_MSC_VER)
44# pragma comment(lib, "winmm.lib")
45#endif
46
47
48#define THIS_FILE "wmme_dev.c"
Benny Prijono598b01d2009-02-18 13:55:03 +000049
50
51/* WMME device info */
52struct wmme_dev_info
53{
54 pjmedia_aud_dev_info info;
55 unsigned deviceId;
56};
57
58/* WMME factory */
59struct wmme_factory
60{
61 pjmedia_aud_dev_factory base;
62 pj_pool_t *pool;
63 pj_pool_factory *pf;
64
65 unsigned dev_count;
66 struct wmme_dev_info *dev_info;
67};
68
69
70/* Individual WMME capture/playback stream descriptor */
71struct wmme_channel
72{
73 union
74 {
75 HWAVEIN In;
76 HWAVEOUT Out;
77 } hWave;
78
79 WAVEHDR *WaveHdr;
80 HANDLE hEvent;
81 DWORD dwBufIdx;
82 DWORD dwMaxBufIdx;
83 unsigned latency_ms;
84 pj_timestamp timestamp;
85};
86
87
88/* Sound stream. */
89struct wmme_stream
90{
91 pjmedia_aud_stream base;
92 pjmedia_dir dir; /**< Sound direction. */
93 int play_id; /**< Playback dev id. */
94 int rec_id; /**< Recording dev id. */
95 pj_pool_t *pool; /**< Memory pool. */
96
97 pjmedia_aud_rec_cb rec_cb; /**< Capture callback. */
98 pjmedia_aud_play_cb play_cb; /**< Playback callback. */
99 void *user_data; /**< Application data. */
100
101 struct wmme_channel play_strm; /**< Playback stream. */
102 struct wmme_channel rec_strm; /**< Capture stream. */
103
104 void *buffer; /**< Temp. frame buffer. */
Benny Prijono5fad7be2009-02-22 21:33:20 +0000105 pjmedia_format_id fmt_id; /**< Frame format */
106 pj_uint8_t silence_char; /**< Silence pattern */
Benny Prijono598b01d2009-02-18 13:55:03 +0000107 unsigned clock_rate; /**< Clock rate. */
Benny Prijono2058f472009-02-22 17:15:34 +0000108 unsigned bytes_per_frame; /**< Bytes per frame */
Benny Prijono598b01d2009-02-18 13:55:03 +0000109 unsigned samples_per_frame; /**< Samples per frame. */
110 unsigned bits_per_sample; /**< Bits per sample. */
111 unsigned channel_count; /**< Channel count. */
112
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;
440 param->flags = di->info.caps;
441 param->input_latency_ms = PJMEDIA_SND_DEFAULT_REC_LATENCY;
442 param->output_latency_ms = PJMEDIA_SND_DEFAULT_PLAY_LATENCY;
443
444 return PJ_SUCCESS;
445}
446
447/* Internal: init WAVEFORMATEX */
Benny Prijono2058f472009-02-22 17:15:34 +0000448static pj_status_t init_waveformatex(LPWAVEFORMATEX wfx,
449 const pjmedia_aud_param *prm)
Benny Prijono598b01d2009-02-18 13:55:03 +0000450{
Benny Prijono2058f472009-02-22 17:15:34 +0000451
452 pj_bzero(wfx, sizeof(PCMWAVEFORMAT));
453 if (prm->ext_fmt.id == PJMEDIA_FORMAT_L16) {
Benny Prijono5fad7be2009-02-22 21:33:20 +0000454 enum { BYTES_PER_SAMPLE = 2 };
Benny Prijono2058f472009-02-22 17:15:34 +0000455 wfx->wFormatTag = WAVE_FORMAT_PCM;
456 wfx->nChannels = (pj_uint16_t)prm->channel_count;
457 wfx->nSamplesPerSec = prm->clock_rate;
458 wfx->nBlockAlign = (pj_uint16_t)(prm->channel_count *
459 BYTES_PER_SAMPLE);
460 wfx->nAvgBytesPerSec = prm->clock_rate * prm->channel_count *
461 BYTES_PER_SAMPLE;
Benny Prijono5fad7be2009-02-22 21:33:20 +0000462 wfx->wBitsPerSample = 16;
Benny Prijono2058f472009-02-22 17:15:34 +0000463
464 return PJ_SUCCESS;
465
466 } else if ((prm->flags & PJMEDIA_AUD_DEV_CAP_EXT_FORMAT) &&
467 (prm->ext_fmt.id == PJMEDIA_FORMAT_PCMA ||
468 prm->ext_fmt.id == PJMEDIA_FORMAT_PCMU))
469 {
470 unsigned ptime;
471
472 ptime = prm->samples_per_frame * 1000 /
473 (prm->clock_rate * prm->channel_count);
474 wfx->wFormatTag = (pj_uint16_t)
475 ((prm->ext_fmt.id==PJMEDIA_FORMAT_PCMA) ?
476 WAVE_FORMAT_ALAW : WAVE_FORMAT_MULAW);
477 wfx->nChannels = (pj_uint16_t)prm->channel_count;
478 wfx->nSamplesPerSec = prm->clock_rate;
479 wfx->nAvgBytesPerSec = prm->clock_rate * prm->channel_count;
480 wfx->nBlockAlign = (pj_uint16_t)(wfx->nAvgBytesPerSec * ptime /
481 1000);
482 wfx->wBitsPerSample = 8;
483 wfx->cbSize = 0;
484
485 return PJ_SUCCESS;
486
487 } else {
488
489 return PJMEDIA_EAUD_BADFORMAT;
490
491 }
Benny Prijono598b01d2009-02-18 13:55:03 +0000492}
493
Benny Prijono5fad7be2009-02-22 21:33:20 +0000494/* Get format name */
495static const char *get_fmt_name(pj_uint32_t id)
496{
497 static char name[8];
498
499 if (id == PJMEDIA_FORMAT_L16)
500 return "l16";
501 pj_memcpy(name, &id, 4);
502 name[4] = '\0';
503 return name;
504}
Benny Prijono598b01d2009-02-18 13:55:03 +0000505
506/* Internal: create WMME player device. */
507static pj_status_t init_player_stream( struct wmme_factory *wf,
508 pj_pool_t *pool,
Benny Prijono2058f472009-02-22 17:15:34 +0000509 struct wmme_stream *parent,
Benny Prijono598b01d2009-02-18 13:55:03 +0000510 struct wmme_channel *wmme_strm,
Benny Prijono2058f472009-02-22 17:15:34 +0000511 const pjmedia_aud_param *prm,
Benny Prijono598b01d2009-02-18 13:55:03 +0000512 unsigned buffer_count)
513{
514 MMRESULT mr;
Benny Prijono2058f472009-02-22 17:15:34 +0000515 WAVEFORMATEX wfx;
516 unsigned i, ptime;
517 pj_status_t status;
Benny Prijono598b01d2009-02-18 13:55:03 +0000518
Benny Prijono2058f472009-02-22 17:15:34 +0000519 PJ_ASSERT_RETURN(prm->play_id < (int)wf->dev_count, PJ_EINVAL);
Benny Prijono598b01d2009-02-18 13:55:03 +0000520
521 /*
522 * Create a wait event.
523 */
524 wmme_strm->hEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
525 if (NULL == wmme_strm->hEvent)
526 return pj_get_os_error();
527
528 /*
529 * Set up wave format structure for opening the device.
530 */
Benny Prijono2058f472009-02-22 17:15:34 +0000531 status = init_waveformatex(&wfx, prm);
532 if (status != PJ_SUCCESS)
533 return status;
534
535 ptime = prm->samples_per_frame * 1000 /
536 (prm->clock_rate * prm->channel_count);
Benny Prijono5fad7be2009-02-22 21:33:20 +0000537 parent->bytes_per_frame = wfx.nAvgBytesPerSec * ptime / 1000;
Benny Prijono598b01d2009-02-18 13:55:03 +0000538
539 /*
540 * Open wave device.
541 */
Benny Prijono2058f472009-02-22 17:15:34 +0000542 mr = waveOutOpen(&wmme_strm->hWave.Out,
543 wf->dev_info[prm->play_id].deviceId,
544 &wfx, (DWORD)wmme_strm->hEvent, 0, CALLBACK_EVENT);
Benny Prijono598b01d2009-02-18 13:55:03 +0000545 if (mr != MMSYSERR_NOERROR) {
Benny Prijono8eeab0b2009-03-04 19:00:28 +0000546 return PJMEDIA_AUDIODEV_ERRNO_FROM_WMME_OUT(mr);
Benny Prijono598b01d2009-02-18 13:55:03 +0000547 }
548
549 /* Pause the wave out device */
550 mr = waveOutPause(wmme_strm->hWave.Out);
551 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 /*
556 * Create the buffers.
557 */
558 wmme_strm->WaveHdr = (WAVEHDR*)
559 pj_pool_zalloc(pool, sizeof(WAVEHDR) * buffer_count);
560 for (i = 0; i < buffer_count; ++i) {
Benny Prijono2058f472009-02-22 17:15:34 +0000561 wmme_strm->WaveHdr[i].lpData = pj_pool_zalloc(pool,
562 parent->bytes_per_frame);
563 wmme_strm->WaveHdr[i].dwBufferLength = parent->bytes_per_frame;
Benny Prijono598b01d2009-02-18 13:55:03 +0000564 mr = waveOutPrepareHeader(wmme_strm->hWave.Out,
565 &(wmme_strm->WaveHdr[i]),
566 sizeof(WAVEHDR));
567 if (mr != MMSYSERR_NOERROR) {
Benny Prijono8eeab0b2009-03-04 19:00:28 +0000568 return PJMEDIA_AUDIODEV_ERRNO_FROM_WMME_OUT(mr);
Benny Prijono598b01d2009-02-18 13:55:03 +0000569 }
570 mr = waveOutWrite(wmme_strm->hWave.Out, &(wmme_strm->WaveHdr[i]),
571 sizeof(WAVEHDR));
572 if (mr != MMSYSERR_NOERROR) {
Benny Prijono8eeab0b2009-03-04 19:00:28 +0000573 return PJMEDIA_AUDIODEV_ERRNO_FROM_WMME_OUT(mr);
Benny Prijono598b01d2009-02-18 13:55:03 +0000574 }
575 }
576
577 wmme_strm->dwBufIdx = 0;
578 wmme_strm->dwMaxBufIdx = buffer_count;
579 wmme_strm->timestamp.u64 = 0;
580
581 /* Done setting up play device. */
Benny Prijono2e988792009-02-23 10:21:33 +0000582 PJ_LOG(4, (THIS_FILE,
Benny Prijono5fad7be2009-02-22 21:33:20 +0000583 " WaveAPI Sound player \"%s\" initialized ("
584 "format=%s, clock_rate=%d, "
Benny Prijono598b01d2009-02-18 13:55:03 +0000585 "channel_count=%d, samples_per_frame=%d (%dms))",
Benny Prijono2058f472009-02-22 17:15:34 +0000586 wf->dev_info[prm->play_id].info.name,
Benny Prijono5fad7be2009-02-22 21:33:20 +0000587 get_fmt_name(prm->ext_fmt.id),
Benny Prijono2058f472009-02-22 17:15:34 +0000588 prm->clock_rate, prm->channel_count, prm->samples_per_frame,
589 prm->samples_per_frame * 1000 / prm->clock_rate));
Benny Prijono598b01d2009-02-18 13:55:03 +0000590
591 return PJ_SUCCESS;
592}
593
594
595/* Internal: create Windows Multimedia recorder device */
596static pj_status_t init_capture_stream( struct wmme_factory *wf,
597 pj_pool_t *pool,
Benny Prijono2058f472009-02-22 17:15:34 +0000598 struct wmme_stream *parent,
Benny Prijono598b01d2009-02-18 13:55:03 +0000599 struct wmme_channel *wmme_strm,
Benny Prijono2058f472009-02-22 17:15:34 +0000600 const pjmedia_aud_param *prm,
Benny Prijono598b01d2009-02-18 13:55:03 +0000601 unsigned buffer_count)
602{
603 MMRESULT mr;
Benny Prijono2058f472009-02-22 17:15:34 +0000604 WAVEFORMATEX wfx;
605 unsigned i, ptime;
Benny Prijono598b01d2009-02-18 13:55:03 +0000606
Benny Prijono2058f472009-02-22 17:15:34 +0000607 PJ_ASSERT_RETURN(prm->rec_id < (int)wf->dev_count, PJ_EINVAL);
Benny Prijono598b01d2009-02-18 13:55:03 +0000608
609 /*
610 * Create a wait event.
611 */
612 wmme_strm->hEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
613 if (NULL == wmme_strm->hEvent) {
614 return pj_get_os_error();
615 }
616
617 /*
618 * Set up wave format structure for opening the device.
619 */
Benny Prijono2058f472009-02-22 17:15:34 +0000620 init_waveformatex(&wfx, prm);
621 ptime = prm->samples_per_frame * 1000 /
622 (prm->clock_rate * prm->channel_count);
Benny Prijono5fad7be2009-02-22 21:33:20 +0000623 parent->bytes_per_frame = wfx.nAvgBytesPerSec * ptime / 1000;
Benny Prijono598b01d2009-02-18 13:55:03 +0000624
625 /*
626 * Open wave device.
627 */
Benny Prijono2058f472009-02-22 17:15:34 +0000628 mr = waveInOpen(&wmme_strm->hWave.In,
629 wf->dev_info[prm->rec_id].deviceId,
630 &wfx, (DWORD)wmme_strm->hEvent, 0, CALLBACK_EVENT);
Benny Prijono598b01d2009-02-18 13:55:03 +0000631 if (mr != MMSYSERR_NOERROR) {
Benny Prijono8eeab0b2009-03-04 19:00:28 +0000632 return PJMEDIA_AUDIODEV_ERRNO_FROM_WMME_IN(mr);
Benny Prijono598b01d2009-02-18 13:55:03 +0000633 }
634
635 /*
636 * Create the buffers.
637 */
638 wmme_strm->WaveHdr = (WAVEHDR*)
639 pj_pool_zalloc(pool, sizeof(WAVEHDR) * buffer_count);
640 for (i = 0; i < buffer_count; ++i) {
Benny Prijono2058f472009-02-22 17:15:34 +0000641 wmme_strm->WaveHdr[i].lpData = pj_pool_zalloc(pool,
642 parent->bytes_per_frame);
643 wmme_strm->WaveHdr[i].dwBufferLength = parent->bytes_per_frame;
Benny Prijono598b01d2009-02-18 13:55:03 +0000644 mr = waveInPrepareHeader(wmme_strm->hWave.In,
645 &(wmme_strm->WaveHdr[i]),
646 sizeof(WAVEHDR));
647 if (mr != MMSYSERR_NOERROR) {
Benny Prijono8eeab0b2009-03-04 19:00:28 +0000648 return PJMEDIA_AUDIODEV_ERRNO_FROM_WMME_IN(mr);
Benny Prijono598b01d2009-02-18 13:55:03 +0000649 }
650 mr = waveInAddBuffer(wmme_strm->hWave.In, &(wmme_strm->WaveHdr[i]),
651 sizeof(WAVEHDR));
652 if (mr != MMSYSERR_NOERROR) {
Benny Prijono8eeab0b2009-03-04 19:00:28 +0000653 return PJMEDIA_AUDIODEV_ERRNO_FROM_WMME_IN(mr);
Benny Prijono598b01d2009-02-18 13:55:03 +0000654 }
655 }
656
657 wmme_strm->dwBufIdx = 0;
658 wmme_strm->dwMaxBufIdx = buffer_count;
659 wmme_strm->timestamp.u64 = 0;
660
661 /* Done setting up play device. */
Benny Prijono2e988792009-02-23 10:21:33 +0000662 PJ_LOG(4,(THIS_FILE,
Benny Prijono5fad7be2009-02-22 21:33:20 +0000663 " WaveAPI Sound recorder \"%s\" initialized "
664 "(format=%s, clock_rate=%d, "
Benny Prijono598b01d2009-02-18 13:55:03 +0000665 "channel_count=%d, samples_per_frame=%d (%dms))",
Benny Prijono2058f472009-02-22 17:15:34 +0000666 wf->dev_info[prm->rec_id].info.name,
Benny Prijono5fad7be2009-02-22 21:33:20 +0000667 get_fmt_name(prm->ext_fmt.id),
Benny Prijono2058f472009-02-22 17:15:34 +0000668 prm->clock_rate, prm->channel_count, prm->samples_per_frame,
669 prm->samples_per_frame * 1000 / prm->clock_rate));
Benny Prijono598b01d2009-02-18 13:55:03 +0000670
671 return PJ_SUCCESS;
672}
673
674
675/* WMME capture and playback thread. */
676static int PJ_THREAD_FUNC wmme_dev_thread(void *arg)
677{
678 struct wmme_stream *strm = (struct wmme_stream*)arg;
679 HANDLE events[3];
680 unsigned eventCount;
Benny Prijono598b01d2009-02-18 13:55:03 +0000681 pj_status_t status = PJ_SUCCESS;
Benny Prijono5fad7be2009-02-22 21:33:20 +0000682 static unsigned rec_cnt, play_cnt;
683
684 rec_cnt = play_cnt = 0;
Benny Prijono598b01d2009-02-18 13:55:03 +0000685
Benny Prijono598b01d2009-02-18 13:55:03 +0000686 eventCount = 0;
687 events[eventCount++] = strm->thread_quit_event;
688 if (strm->dir & PJMEDIA_DIR_PLAYBACK)
689 events[eventCount++] = strm->play_strm.hEvent;
690 if (strm->dir & PJMEDIA_DIR_CAPTURE)
691 events[eventCount++] = strm->rec_strm.hEvent;
692
693
694 /* Raise self priority. We don't want the audio to be distorted by
695 * system activity.
696 */
697#if defined(PJ_WIN32_WINCE) && PJ_WIN32_WINCE != 0
698 if (strm->dir & PJMEDIA_DIR_PLAYBACK)
699 CeSetThreadPriority(GetCurrentThread(), 153);
700 else
701 CeSetThreadPriority(GetCurrentThread(), 247);
702#else
703 SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_TIME_CRITICAL);
704#endif
705
Benny Prijono598b01d2009-02-18 13:55:03 +0000706 /*
707 * Loop while not signalled to quit, wait for event objects to be
708 * signalled by WMME capture and play buffer.
709 */
710 while (status == PJ_SUCCESS)
711 {
712
713 DWORD rc;
714 pjmedia_dir signalled_dir;
715
Benny Prijono5fad7be2009-02-22 21:33:20 +0000716 /* Swap */
717 if (eventCount==3) {
718 HANDLE hTemp = events[2];
719 events[2] = events[1];
720 events[1] = hTemp;
721 }
722
Benny Prijono598b01d2009-02-18 13:55:03 +0000723 rc = WaitForMultipleObjects(eventCount, events, FALSE, INFINITE);
724 if (rc < WAIT_OBJECT_0 || rc >= WAIT_OBJECT_0 + eventCount)
725 continue;
726
727 if (rc == WAIT_OBJECT_0)
728 break;
729
730 if (rc == (WAIT_OBJECT_0 + 1))
731 {
732 if (events[1] == strm->play_strm.hEvent)
733 signalled_dir = PJMEDIA_DIR_PLAYBACK;
734 else
735 signalled_dir = PJMEDIA_DIR_CAPTURE;
736 }
737 else
738 {
739 if (events[2] == strm->play_strm.hEvent)
740 signalled_dir = PJMEDIA_DIR_PLAYBACK;
741 else
742 signalled_dir = PJMEDIA_DIR_CAPTURE;
743 }
744
745
746 if (signalled_dir == PJMEDIA_DIR_PLAYBACK)
747 {
748 struct wmme_channel *wmme_strm = &strm->play_strm;
Benny Prijono2058f472009-02-22 17:15:34 +0000749
Benny Prijono598b01d2009-02-18 13:55:03 +0000750 status = PJ_SUCCESS;
751
752 /*
753 * Windows Multimedia has requested us to feed some frames to
754 * playback buffer.
755 */
756
757 while (wmme_strm->WaveHdr[wmme_strm->dwBufIdx].dwFlags & WHDR_DONE)
758 {
759 void *buffer = wmme_strm->WaveHdr[wmme_strm->dwBufIdx].lpData;
Benny Prijono2058f472009-02-22 17:15:34 +0000760 pjmedia_frame pcm_frame, *frame;
Benny Prijono5fad7be2009-02-22 21:33:20 +0000761 MMRESULT mr = MMSYSERR_NOERROR;
Benny Prijono598b01d2009-02-18 13:55:03 +0000762
763 //PJ_LOG(5,(THIS_FILE, "Finished writing buffer %d",
764 // wmme_strm->dwBufIdx));
765
Benny Prijono5fad7be2009-02-22 21:33:20 +0000766 if (strm->fmt_id == PJMEDIA_FORMAT_L16) {
Benny Prijono2058f472009-02-22 17:15:34 +0000767 /* PCM mode */
768 frame = &pcm_frame;
769
770 frame->type = PJMEDIA_FRAME_TYPE_AUDIO;
771 frame->size = strm->bytes_per_frame;
772 frame->buf = buffer;
773 frame->timestamp.u64 = wmme_strm->timestamp.u64;
774 frame->bit_info = 0;
775 } else {
776 /* Codec mode */
777 frame = &strm->xfrm->base;
778
779 strm->xfrm->base.type = PJMEDIA_FRAME_TYPE_EXTENDED;
780 strm->xfrm->base.size = strm->bytes_per_frame;
781 strm->xfrm->base.buf = NULL;
782 strm->xfrm->base.timestamp.u64 = wmme_strm->timestamp.u64;
783 strm->xfrm->base.bit_info = 0;
784 }
Benny Prijono598b01d2009-02-18 13:55:03 +0000785
786 /* Get frame from application. */
Benny Prijono2e988792009-02-23 10:21:33 +0000787 //PJ_LOG(5,(THIS_FILE, "xxx %u play_cb", play_cnt++));
Benny Prijono2058f472009-02-22 17:15:34 +0000788 status = (*strm->play_cb)(strm->user_data, frame);
Benny Prijono598b01d2009-02-18 13:55:03 +0000789
790 if (status != PJ_SUCCESS)
791 break;
792
Benny Prijono5fad7be2009-02-22 21:33:20 +0000793 if (strm->fmt_id == PJMEDIA_FORMAT_L16) {
Benny Prijono2058f472009-02-22 17:15:34 +0000794 /* PCM mode */
795 if (frame->type == PJMEDIA_FRAME_TYPE_NONE) {
796 pj_bzero(buffer, strm->bytes_per_frame);
Benny Prijono2058f472009-02-22 17:15:34 +0000797 } else if (frame->type == PJMEDIA_FRAME_TYPE_EXTENDED) {
798 pj_assert(!"Frame type not supported");
799 } else if (frame->type == PJMEDIA_FRAME_TYPE_AUDIO) {
Benny Prijono5fad7be2009-02-22 21:33:20 +0000800 /* Nothing to do */
Benny Prijono2058f472009-02-22 17:15:34 +0000801 } else {
802 pj_assert(!"Frame type not supported");
803 }
804 } else {
805 /* Codec mode */
806 if (frame->type == PJMEDIA_FRAME_TYPE_NONE) {
Benny Prijono5fad7be2009-02-22 21:33:20 +0000807 pj_memset(buffer, strm->silence_char,
808 strm->bytes_per_frame);
Benny Prijono2058f472009-02-22 17:15:34 +0000809 } else if (frame->type == PJMEDIA_FRAME_TYPE_EXTENDED) {
810 unsigned sz;
811 sz = pjmedia_frame_ext_copy_payload(strm->xfrm,
812 buffer,
813 strm->bytes_per_frame);
Benny Prijono5fad7be2009-02-22 21:33:20 +0000814 if (sz < strm->bytes_per_frame) {
815 pj_memset((char*)buffer+sz,
816 strm->silence_char,
817 strm->bytes_per_frame - sz);
818 }
Benny Prijono2058f472009-02-22 17:15:34 +0000819 } else {
820 pj_assert(!"Frame type not supported");
821 }
Benny Prijono598b01d2009-02-18 13:55:03 +0000822 }
823
824 /* Write to the device. */
Benny Prijono5fad7be2009-02-22 21:33:20 +0000825 mr = waveOutWrite(wmme_strm->hWave.Out,
826 &(wmme_strm->WaveHdr[wmme_strm->dwBufIdx]),
827 sizeof(WAVEHDR));
828 if (mr != MMSYSERR_NOERROR) {
Benny Prijono8eeab0b2009-03-04 19:00:28 +0000829 status = PJMEDIA_AUDIODEV_ERRNO_FROM_WMME_OUT(mr);
Benny Prijono5fad7be2009-02-22 21:33:20 +0000830 break;
Benny Prijono598b01d2009-02-18 13:55:03 +0000831 }
832
833 /* Increment position. */
834 if (++wmme_strm->dwBufIdx >= wmme_strm->dwMaxBufIdx)
835 wmme_strm->dwBufIdx = 0;
836 wmme_strm->timestamp.u64 += strm->samples_per_frame /
837 strm->channel_count;
838 }
839 }
840 else
841 {
842 struct wmme_channel *wmme_strm = &strm->rec_strm;
843 MMRESULT mr = MMSYSERR_NOERROR;
844 status = PJ_SUCCESS;
845
846 /*
847 * Windows Multimedia has indicated that it has some frames ready
848 * in the capture buffer. Get as much frames as possible to
849 * prevent overflows.
850 */
851#if 0
852 {
853 static DWORD tc = 0;
854 DWORD now = GetTickCount();
855 DWORD i = 0;
856 DWORD bits = 0;
857
858 if (tc == 0) tc = now;
859
860 for (i = 0; i < wmme_strm->dwMaxBufIdx; ++i)
861 {
862 bits = bits << 4;
863 bits |= wmme_strm->WaveHdr[i].dwFlags & WHDR_DONE;
864 }
865 PJ_LOG(5,(THIS_FILE, "Record Signal> Index: %d, Delta: %4.4d, "
866 "Flags: %6.6x\n",
867 wmme_strm->dwBufIdx,
868 now - tc,
869 bits));
870 tc = now;
871 }
872#endif
873
874 while (wmme_strm->WaveHdr[wmme_strm->dwBufIdx].dwFlags & WHDR_DONE)
875 {
876 char* buffer = (char*)
877 wmme_strm->WaveHdr[wmme_strm->dwBufIdx].lpData;
878 unsigned cap_len =
879 wmme_strm->WaveHdr[wmme_strm->dwBufIdx].dwBytesRecorded;
Benny Prijono2058f472009-02-22 17:15:34 +0000880 pjmedia_frame pcm_frame, *frame;
Benny Prijono598b01d2009-02-18 13:55:03 +0000881
882 /*
883 PJ_LOG(5,(THIS_FILE, "Read %d bytes from buffer %d", cap_len,
884 wmme_strm->dwBufIdx));
885 */
Benny Prijono2058f472009-02-22 17:15:34 +0000886
Benny Prijono5fad7be2009-02-22 21:33:20 +0000887 if (strm->fmt_id == PJMEDIA_FORMAT_L16) {
Benny Prijono2058f472009-02-22 17:15:34 +0000888 /* PCM mode */
889 if (cap_len < strm->bytes_per_frame)
890 pj_bzero(buffer + cap_len,
891 strm->bytes_per_frame - cap_len);
Benny Prijono598b01d2009-02-18 13:55:03 +0000892
Benny Prijono2058f472009-02-22 17:15:34 +0000893 /* Copy the audio data out of the wave buffer. */
894 pj_memcpy(strm->buffer, buffer, strm->bytes_per_frame);
Benny Prijono598b01d2009-02-18 13:55:03 +0000895
Benny Prijono2058f472009-02-22 17:15:34 +0000896 /* Prepare frame */
897 frame = &pcm_frame;
898 frame->type = PJMEDIA_FRAME_TYPE_AUDIO;
899 frame->buf = strm->buffer;
900 frame->size = strm->bytes_per_frame;
901 frame->timestamp.u64 = wmme_strm->timestamp.u64;
902 frame->bit_info = 0;
903
904 } else {
905 /* Codec mode */
906 frame = &strm->xfrm->base;
907
908 frame->type = PJMEDIA_FRAME_TYPE_EXTENDED;
909 frame->buf = NULL;
910 frame->size = strm->bytes_per_frame;
911 frame->timestamp.u64 = wmme_strm->timestamp.u64;
912 frame->bit_info = 0;
913
914 strm->xfrm->samples_cnt = 0;
915 strm->xfrm->subframe_cnt = 0;
916 pjmedia_frame_ext_append_subframe(strm->xfrm, buffer,
917 strm->bytes_per_frame *8,
918 strm->samples_per_frame);
919 }
Benny Prijono598b01d2009-02-18 13:55:03 +0000920
921 /* Re-add the buffer to the device. */
922 mr = waveInAddBuffer(wmme_strm->hWave.In,
923 &(wmme_strm->WaveHdr[wmme_strm->dwBufIdx]),
924 sizeof(WAVEHDR));
925 if (mr != MMSYSERR_NOERROR) {
Benny Prijono8eeab0b2009-03-04 19:00:28 +0000926 status = PJMEDIA_AUDIODEV_ERRNO_FROM_WMME_IN(mr);
Benny Prijono598b01d2009-02-18 13:55:03 +0000927 break;
928 }
929
Benny Prijono598b01d2009-02-18 13:55:03 +0000930
931 /* Call callback */
Benny Prijono2e988792009-02-23 10:21:33 +0000932 //PJ_LOG(5,(THIS_FILE, "xxx %u rec_cb", rec_cnt++));
Benny Prijono2058f472009-02-22 17:15:34 +0000933 status = (*strm->rec_cb)(strm->user_data, frame);
Benny Prijono598b01d2009-02-18 13:55:03 +0000934 if (status != PJ_SUCCESS)
935 break;
936
937 /* Increment position. */
938 if (++wmme_strm->dwBufIdx >= wmme_strm->dwMaxBufIdx)
939 wmme_strm->dwBufIdx = 0;
940 wmme_strm->timestamp.u64 += strm->samples_per_frame /
941 strm->channel_count;
942 }
943 }
944 }
945
946 PJ_LOG(5,(THIS_FILE, "WMME: thread stopping.."));
947 return 0;
948}
949
950
951/* API: create stream */
952static pj_status_t factory_create_stream(pjmedia_aud_dev_factory *f,
Benny Prijono10454dc2009-02-21 14:21:59 +0000953 const pjmedia_aud_param *param,
Benny Prijono598b01d2009-02-18 13:55:03 +0000954 pjmedia_aud_rec_cb rec_cb,
955 pjmedia_aud_play_cb play_cb,
956 void *user_data,
957 pjmedia_aud_stream **p_aud_strm)
958{
959 struct wmme_factory *wf = (struct wmme_factory*)f;
960 pj_pool_t *pool;
961 struct wmme_stream *strm;
Benny Prijono5fad7be2009-02-22 21:33:20 +0000962 pj_uint8_t silence_char;
Benny Prijono598b01d2009-02-18 13:55:03 +0000963 pj_status_t status;
964
Benny Prijono5fad7be2009-02-22 21:33:20 +0000965 switch (param->ext_fmt.id) {
966 case PJMEDIA_FORMAT_L16:
967 silence_char = '\0';
968 break;
969 case PJMEDIA_FORMAT_ALAW:
970 silence_char = (pj_uint8_t)'\xd5';
971 break;
972 case PJMEDIA_FORMAT_ULAW:
973 silence_char = (pj_uint8_t)'\xff';
974 break;
975 default:
976 return PJMEDIA_EAUD_BADFORMAT;
977 }
Benny Prijono598b01d2009-02-18 13:55:03 +0000978
979 /* Create and Initialize stream descriptor */
980 pool = pj_pool_create(wf->pf, "wmme-dev", 1000, 1000, NULL);
981 PJ_ASSERT_RETURN(pool != NULL, PJ_ENOMEM);
982
983 strm = PJ_POOL_ZALLOC_T(pool, struct wmme_stream);
984 strm->dir = param->dir;
985 strm->play_id = param->play_id;
986 strm->rec_id = param->rec_id;
987 strm->pool = pool;
988 strm->rec_cb = rec_cb;
989 strm->play_cb = play_cb;
990 strm->user_data = user_data;
Benny Prijono5fad7be2009-02-22 21:33:20 +0000991 strm->fmt_id = param->ext_fmt.id;
992 strm->silence_char = silence_char;
Benny Prijono598b01d2009-02-18 13:55:03 +0000993 strm->clock_rate = param->clock_rate;
994 strm->samples_per_frame = param->samples_per_frame;
995 strm->bits_per_sample = param->bits_per_sample;
996 strm->channel_count = param->channel_count;
Benny Prijono598b01d2009-02-18 13:55:03 +0000997
998 /* Create player stream */
999 if (param->dir & PJMEDIA_DIR_PLAYBACK) {
1000 unsigned buf_count;
1001
1002 if (param->flags & PJMEDIA_AUD_DEV_CAP_OUTPUT_LATENCY)
1003 strm->play_strm.latency_ms = param->output_latency_ms;
1004 else
1005 strm->play_strm.latency_ms = PJMEDIA_SND_DEFAULT_PLAY_LATENCY;
1006
1007 buf_count = strm->play_strm.latency_ms * param->clock_rate *
1008 param->channel_count / param->samples_per_frame / 1000;
1009
1010 status = init_player_stream(wf, strm->pool,
Benny Prijono2058f472009-02-22 17:15:34 +00001011 strm,
Benny Prijono598b01d2009-02-18 13:55:03 +00001012 &strm->play_strm,
Benny Prijono2058f472009-02-22 17:15:34 +00001013 param,
Benny Prijono598b01d2009-02-18 13:55:03 +00001014 buf_count);
1015
1016 if (status != PJ_SUCCESS) {
1017 stream_destroy(&strm->base);
1018 return status;
1019 }
1020 }
1021
1022 /* Create capture stream */
1023 if (param->dir & PJMEDIA_DIR_CAPTURE) {
1024 unsigned buf_count;
1025
1026 if (param->flags & PJMEDIA_AUD_DEV_CAP_INPUT_LATENCY)
1027 strm->rec_strm.latency_ms = param->input_latency_ms;
1028 else
1029 strm->rec_strm.latency_ms = PJMEDIA_SND_DEFAULT_REC_LATENCY;
1030
1031 buf_count = strm->rec_strm.latency_ms * param->clock_rate *
1032 param->channel_count / param->samples_per_frame / 1000;
1033
1034 status = init_capture_stream(wf, strm->pool,
Benny Prijono2058f472009-02-22 17:15:34 +00001035 strm,
Benny Prijono598b01d2009-02-18 13:55:03 +00001036 &strm->rec_strm,
Benny Prijono2058f472009-02-22 17:15:34 +00001037 param,
Benny Prijono598b01d2009-02-18 13:55:03 +00001038 buf_count);
1039
1040 if (status != PJ_SUCCESS) {
1041 stream_destroy(&strm->base);
1042 return status;
1043 }
1044 }
1045
Benny Prijono5fad7be2009-02-22 21:33:20 +00001046 strm->buffer = pj_pool_alloc(pool, strm->bytes_per_frame);
1047 if (!strm->buffer) {
1048 pj_pool_release(pool);
1049 return PJ_ENOMEM;
1050 }
1051
Benny Prijono2058f472009-02-22 17:15:34 +00001052 /* If format is extended, must create buffer for the extended frame. */
Benny Prijono5fad7be2009-02-22 21:33:20 +00001053 if (strm->fmt_id != PJMEDIA_FORMAT_L16) {
Benny Prijono2058f472009-02-22 17:15:34 +00001054 strm->xfrm_size = sizeof(pjmedia_frame_ext) +
1055 32 * sizeof(pjmedia_frame_ext_subframe) +
Benny Prijono5fad7be2009-02-22 21:33:20 +00001056 strm->bytes_per_frame + 4;
Benny Prijono2058f472009-02-22 17:15:34 +00001057 strm->xfrm = (pjmedia_frame_ext*)
1058 pj_pool_alloc(pool, strm->xfrm_size);
1059 }
1060
Benny Prijono598b01d2009-02-18 13:55:03 +00001061 /* Create the stop event */
1062 strm->thread_quit_event = CreateEvent(NULL, FALSE, FALSE, NULL);
1063 if (strm->thread_quit_event == NULL) {
1064 status = pj_get_os_error();
1065 stream_destroy(&strm->base);
1066 return status;
1067 }
1068
1069 /* Create and start the thread */
1070 status = pj_thread_create(pool, "wmme", &wmme_dev_thread, strm, 0, 0,
1071 &strm->thread);
1072 if (status != PJ_SUCCESS) {
1073 stream_destroy(&strm->base);
1074 return status;
1075 }
1076
1077 /* Done */
1078 strm->base.op = &stream_op;
1079 *p_aud_strm = &strm->base;
1080
1081 return PJ_SUCCESS;
1082}
1083
1084/* API: Get stream info. */
1085static pj_status_t stream_get_param(pjmedia_aud_stream *s,
Benny Prijono10454dc2009-02-21 14:21:59 +00001086 pjmedia_aud_param *pi)
Benny Prijono598b01d2009-02-18 13:55:03 +00001087{
1088 struct wmme_stream *strm = (struct wmme_stream*)s;
1089
1090 PJ_ASSERT_RETURN(strm && pi, PJ_EINVAL);
1091
1092 pj_bzero(pi, sizeof(*pi));
1093 pi->dir = strm->dir;
1094 pi->play_id = strm->play_id;
1095 pi->rec_id = strm->rec_id;
1096 pi->clock_rate = strm->clock_rate;
1097 pi->channel_count = strm->channel_count;
1098 pi->samples_per_frame = strm->samples_per_frame;
1099 pi->bits_per_sample = strm->bits_per_sample;
1100
1101 if (pi->dir & PJMEDIA_DIR_CAPTURE) {
1102 pi->flags |= PJMEDIA_AUD_DEV_CAP_INPUT_LATENCY;
1103 pi->input_latency_ms = strm->rec_strm.latency_ms;
1104 }
1105
1106 if (pi->dir & PJMEDIA_DIR_PLAYBACK) {
1107 /* TODO: report the actual latency? */
1108 pi->flags |= PJMEDIA_AUD_DEV_CAP_OUTPUT_LATENCY;
1109 pi->output_latency_ms = strm->play_strm.latency_ms;
1110 }
1111
1112 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 &&
1125 (strm->dir & PJMEDIA_DIR_CAPTURE))
1126 {
1127 /* Recording latency */
1128 *(unsigned*)pval = strm->rec_strm.latency_ms;
1129 return PJ_SUCCESS;
1130 } else if (cap==PJMEDIA_AUD_DEV_CAP_OUTPUT_LATENCY &&
1131 (strm->dir & PJMEDIA_DIR_PLAYBACK))
1132 {
1133 /* Playback latency */
1134 *(unsigned*)pval = strm->play_strm.latency_ms;
1135 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 */
1140 DWORD dwVol;
1141 MMRESULT mr;
1142
1143 mr = waveOutGetVolume(strm->play_strm.hWave.Out, &dwVol);
1144 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
1148 dwVol &= 0xFFFF;
1149 *(unsigned*)pval = (dwVol * 100) / 0xFFFF;
1150 return PJ_SUCCESS;
1151 } else {
1152 return PJ_ENOTSUP;
1153 }
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 */
1169 DWORD dwVol;
1170 MMRESULT mr;
1171
1172 dwVol = ((*(unsigned*)pval) * 0xFFFF) / 100;
1173 dwVol |= (dwVol << 16);
1174
1175 mr = waveOutSetVolume(strm->play_strm.hWave.Out, dwVol);
Benny Prijono8eeab0b2009-03-04 19:00:28 +00001176 return (mr==MMSYSERR_NOERROR)? PJ_SUCCESS :
1177 PJMEDIA_AUDIODEV_ERRNO_FROM_WMME_OUT(mr);
Benny Prijono598b01d2009-02-18 13:55:03 +00001178 } else {
1179 return PJ_ENOTSUP;
1180 }
1181
1182 return PJ_ENOTSUP;
1183}
1184
1185/* API: Start stream. */
1186static pj_status_t stream_start(pjmedia_aud_stream *strm)
1187{
1188 struct wmme_stream *stream = (struct wmme_stream*)strm;
1189 MMRESULT mr;
1190
1191 if (stream->play_strm.hWave.Out != NULL)
1192 {
1193 mr = waveOutRestart(stream->play_strm.hWave.Out);
1194 if (mr != MMSYSERR_NOERROR) {
Benny Prijono8eeab0b2009-03-04 19:00:28 +00001195 return PJMEDIA_AUDIODEV_ERRNO_FROM_WMME_OUT(mr);
Benny Prijono598b01d2009-02-18 13:55:03 +00001196 }
Benny Prijono2e988792009-02-23 10:21:33 +00001197 PJ_LOG(4,(THIS_FILE, "WMME playback stream started"));
Benny Prijono598b01d2009-02-18 13:55:03 +00001198 }
1199
1200 if (stream->rec_strm.hWave.In != NULL)
1201 {
1202 mr = waveInStart(stream->rec_strm.hWave.In);
1203 if (mr != MMSYSERR_NOERROR) {
Benny Prijono8eeab0b2009-03-04 19:00:28 +00001204 return PJMEDIA_AUDIODEV_ERRNO_FROM_WMME_IN(mr);
Benny Prijono598b01d2009-02-18 13:55:03 +00001205 }
Benny Prijono2e988792009-02-23 10:21:33 +00001206 PJ_LOG(4,(THIS_FILE, "WMME capture stream started"));
Benny Prijono598b01d2009-02-18 13:55:03 +00001207 }
1208
1209 return PJ_SUCCESS;
1210}
1211
1212/* API: Stop stream. */
1213static pj_status_t stream_stop(pjmedia_aud_stream *strm)
1214{
1215 struct wmme_stream *stream = (struct wmme_stream*)strm;
1216 MMRESULT mr;
1217
1218 PJ_ASSERT_RETURN(stream != NULL, PJ_EINVAL);
1219
1220 if (stream->play_strm.hWave.Out != NULL)
1221 {
1222 mr = waveOutPause(stream->play_strm.hWave.Out);
1223 if (mr != MMSYSERR_NOERROR) {
Benny Prijono8eeab0b2009-03-04 19:00:28 +00001224 return PJMEDIA_AUDIODEV_ERRNO_FROM_WMME_OUT(mr);
Benny Prijono598b01d2009-02-18 13:55:03 +00001225 }
Benny Prijono2e988792009-02-23 10:21:33 +00001226 PJ_LOG(4,(THIS_FILE, "Stopped WMME playback stream"));
Benny Prijono598b01d2009-02-18 13:55:03 +00001227 }
1228
1229 if (stream->rec_strm.hWave.In != NULL)
1230 {
1231 mr = waveInStop(stream->rec_strm.hWave.In);
1232 if (mr != MMSYSERR_NOERROR) {
Benny Prijono8eeab0b2009-03-04 19:00:28 +00001233 return PJMEDIA_AUDIODEV_ERRNO_FROM_WMME_IN(mr);
Benny Prijono598b01d2009-02-18 13:55:03 +00001234 }
Benny Prijono2e988792009-02-23 10:21:33 +00001235 PJ_LOG(4,(THIS_FILE, "Stopped WMME capture stream"));
Benny Prijono598b01d2009-02-18 13:55:03 +00001236 }
1237
1238 return PJ_SUCCESS;
1239}
1240
1241
1242/* API: Destroy stream. */
1243static pj_status_t stream_destroy(pjmedia_aud_stream *strm)
1244{
1245 struct wmme_stream *stream = (struct wmme_stream*)strm;
1246 unsigned i;
1247
1248 PJ_ASSERT_RETURN(stream != NULL, PJ_EINVAL);
1249
1250 stream_stop(strm);
1251
1252 if (stream->thread)
1253 {
1254 SetEvent(stream->thread_quit_event);
1255 pj_thread_join(stream->thread);
1256 pj_thread_destroy(stream->thread);
1257 stream->thread = NULL;
1258 }
1259
1260 /* Unprepare the headers and close the play device */
1261 if (stream->play_strm.hWave.Out)
1262 {
1263 waveOutReset(stream->play_strm.hWave.Out);
1264 for (i = 0; i < stream->play_strm.dwMaxBufIdx; ++i)
1265 waveOutUnprepareHeader(stream->play_strm.hWave.Out,
1266 &(stream->play_strm.WaveHdr[i]),
1267 sizeof(WAVEHDR));
1268 waveOutClose(stream->play_strm.hWave.Out);
1269 stream->play_strm.hWave.Out = NULL;
1270 }
1271
1272 /* Close the play event */
1273 if (stream->play_strm.hEvent)
1274 {
1275 CloseHandle(stream->play_strm.hEvent);
1276 stream->play_strm.hEvent = NULL;
1277 }
1278
1279 /* Unprepare the headers and close the record device */
1280 if (stream->rec_strm.hWave.In)
1281 {
1282 waveInReset(stream->rec_strm.hWave.In);
1283 for (i = 0; i < stream->play_strm.dwMaxBufIdx; ++i)
1284 waveInUnprepareHeader(stream->rec_strm.hWave.In,
1285 &(stream->rec_strm.WaveHdr[i]),
1286 sizeof(WAVEHDR));
1287 waveInClose(stream->rec_strm.hWave.In);
1288 stream->rec_strm.hWave.In = NULL;
1289 }
1290
1291 /* Close the record event */
1292 if (stream->rec_strm.hEvent)
1293 {
1294 CloseHandle(stream->rec_strm.hEvent);
1295 stream->rec_strm.hEvent = NULL;
1296 }
1297
1298 pj_pool_release(stream->pool);
1299
1300 return PJ_SUCCESS;
1301}
1302
Benny Prijono8eeab0b2009-03-04 19:00:28 +00001303#endif /* PJMEDIA_AUDIO_DEV_HAS_WMME */
1304