blob: 8bf1f79a488467265ab1846ed9d6a2fcd8703156 [file] [log] [blame]
Benny Prijono2cd64f82009-02-17 19:57:48 +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 Prijono64f91382009-03-05 18:02:28 +000021#include <pj/assert.h>
Benny Prijono2cd64f82009-02-17 19:57:48 +000022#include <pj/errno.h>
Benny Prijono10454dc2009-02-21 14:21:59 +000023#include <pj/log.h>
Benny Prijono2058f472009-02-22 17:15:34 +000024#include <pj/pool.h>
Benny Prijono10454dc2009-02-21 14:21:59 +000025#include <pj/string.h>
26
27#define THIS_FILE "audiodev.c"
28
Benny Prijono8eeab0b2009-03-04 19:00:28 +000029#define DEFINE_CAP(name, info) {name, info}
30
Benny Prijono10454dc2009-02-21 14:21:59 +000031/* Capability names */
32static struct cap_info
33{
34 const char *name;
35 const char *info;
36} cap_infos[] =
37{
Benny Prijono8eeab0b2009-03-04 19:00:28 +000038 DEFINE_CAP("ext-fmt", "Extended/non-PCM format"),
39 DEFINE_CAP("latency-in", "Input latency/buffer size setting"),
40 DEFINE_CAP("latency-out", "Output latency/buffer size setting"),
41 DEFINE_CAP("vol-in", "Input volume setting"),
42 DEFINE_CAP("vol-out", "Output volume setting"),
43 DEFINE_CAP("meter-in", "Input meter"),
44 DEFINE_CAP("meter-out", "Output meter"),
45 DEFINE_CAP("route-in", "Input routing"),
46 DEFINE_CAP("route-out", "Output routing"),
47 DEFINE_CAP("aec", "Accoustic echo cancellation"),
48 DEFINE_CAP("aec-tail", "Tail length setting for AEC"),
49 DEFINE_CAP("vad", "Voice activity detection"),
50 DEFINE_CAP("cng", "Comfort noise generation"),
51 DEFINE_CAP("plg", "Packet loss concealment")
Benny Prijono10454dc2009-02-21 14:21:59 +000052};
53
Benny Prijono2cd64f82009-02-17 19:57:48 +000054
Benny Prijono598b01d2009-02-18 13:55:03 +000055/*
Benny Prijono10454dc2009-02-21 14:21:59 +000056 * The device index seen by application and driver is different.
Benny Prijono598b01d2009-02-18 13:55:03 +000057 *
Benny Prijono10454dc2009-02-21 14:21:59 +000058 * At application level, device index is index to global list of device.
59 * At driver level, device index is index to device list on that particular
60 * factory only.
Benny Prijono598b01d2009-02-18 13:55:03 +000061 */
Benny Prijonoe3ebd552009-02-18 20:14:15 +000062#define MAKE_DEV_ID(f_id, index) (((f_id & 0xFFFF) << 16) | (index & 0xFFFF))
Benny Prijono2cd64f82009-02-17 19:57:48 +000063#define GET_INDEX(dev_id) ((dev_id) & 0xFFFF)
64#define GET_FID(dev_id) ((dev_id) >> 16)
Benny Prijono598b01d2009-02-18 13:55:03 +000065#define DEFAULT_DEV_ID 0
Benny Prijono2cd64f82009-02-17 19:57:48 +000066
67
Benny Prijono598b01d2009-02-18 13:55:03 +000068/* extern functions to create factories */
Benny Prijono2058f472009-02-22 17:15:34 +000069#if PJMEDIA_AUDIO_DEV_HAS_PORTAUDIO
Benny Prijono2cd64f82009-02-17 19:57:48 +000070pjmedia_aud_dev_factory* pjmedia_pa_factory(pj_pool_factory *pf);
Benny Prijono2058f472009-02-22 17:15:34 +000071#endif
72
73#if PJMEDIA_AUDIO_DEV_HAS_WMME
Benny Prijono598b01d2009-02-18 13:55:03 +000074pjmedia_aud_dev_factory* pjmedia_wmme_factory(pj_pool_factory *pf);
Benny Prijono2058f472009-02-22 17:15:34 +000075#endif
76
Nanang Izzuddina940b362009-02-23 13:53:30 +000077#if PJMEDIA_AUDIO_DEV_HAS_SYMB_APS
78pjmedia_aud_dev_factory* pjmedia_aps_factory(pj_pool_factory *pf);
79#endif
80
81#if PJMEDIA_AUDIO_DEV_HAS_SYMB_MDA
82pjmedia_aud_dev_factory* pjmedia_symb_mda_factory(pj_pool_factory *pf);
83#endif
Benny Prijono598b01d2009-02-18 13:55:03 +000084
Benny Prijonoe3ebd552009-02-18 20:14:15 +000085#define MAX_DRIVERS 16
Benny Prijono10454dc2009-02-21 14:21:59 +000086#define MAX_DEVS 64
87
88/* typedef for factory creation function */
89typedef pjmedia_aud_dev_factory* (*create_func_ptr)(pj_pool_factory*);
90
91/* driver structure */
92struct driver
93{
94 create_func_ptr create; /* Creation function. */
95 pjmedia_aud_dev_factory *f; /* Factory instance. */
96 char name[32]; /* Driver name */
97 unsigned dev_cnt; /* Number of devices */
98 unsigned start_idx; /* Start index in global list */
Benny Prijono96e74f32009-02-22 12:00:12 +000099 int rec_dev_idx;/* Default capture device. */
100 int play_dev_idx;/* Default playback device */
101 int dev_idx; /* Default device. */
Benny Prijono10454dc2009-02-21 14:21:59 +0000102};
Benny Prijono2cd64f82009-02-17 19:57:48 +0000103
Benny Prijonoe3ebd552009-02-18 20:14:15 +0000104/* The audio subsystem */
105static struct aud_subsys
Benny Prijono2cd64f82009-02-17 19:57:48 +0000106{
Benny Prijono10454dc2009-02-21 14:21:59 +0000107 unsigned init_count; /* How many times init() is called */
108 pj_pool_factory *pf; /* The pool factory. */
Benny Prijono2cd64f82009-02-17 19:57:48 +0000109
Benny Prijono10454dc2009-02-21 14:21:59 +0000110 unsigned drv_cnt; /* Number of drivers. */
111 struct driver drv[MAX_DRIVERS]; /* Array of drivers. */
Benny Prijonoe3ebd552009-02-18 20:14:15 +0000112
Benny Prijono10454dc2009-02-21 14:21:59 +0000113 unsigned dev_cnt; /* Total number of devices. */
114 pj_uint32_t dev_list[MAX_DEVS];/* Array of device IDs. */
Benny Prijonoe3ebd552009-02-18 20:14:15 +0000115
116} aud_subsys;
117
Benny Prijono7a380002009-03-09 12:55:29 +0000118/* API: get capability name/info */
119PJ_DEF(const char*) pjmedia_aud_dev_cap_name(pjmedia_aud_dev_cap cap,
120 const char **p_desc)
121{
122 const char *desc;
123 unsigned i;
124
125 if (p_desc==NULL) p_desc = &desc;
126
127 for (i=0; i<PJ_ARRAY_SIZE(cap_infos); ++i) {
128 if ((1 << i)==cap)
129 break;
130 }
131
132 if (i==32) {
133 *p_desc = "??";
134 return "??";
135 }
136
137 *p_desc = cap_infos[i].info;
138 return cap_infos[i].name;
139}
140
141static pj_status_t get_cap_pointer(const pjmedia_aud_param *param,
142 pjmedia_aud_dev_cap cap,
143 void **ptr,
144 unsigned *size)
145{
146#define FIELD_INFO(name) *ptr = (void*)&param->name; \
147 *size = sizeof(param->name)
148
149 switch (cap) {
150 case PJMEDIA_AUD_DEV_CAP_EXT_FORMAT:
151 FIELD_INFO(ext_fmt);
152 break;
153 case PJMEDIA_AUD_DEV_CAP_INPUT_LATENCY:
154 FIELD_INFO(input_latency_ms);
155 break;
156 case PJMEDIA_AUD_DEV_CAP_OUTPUT_LATENCY:
157 FIELD_INFO(output_latency_ms);
158 break;
159 case PJMEDIA_AUD_DEV_CAP_INPUT_VOLUME_SETTING:
160 FIELD_INFO(input_vol);
161 break;
162 case PJMEDIA_AUD_DEV_CAP_OUTPUT_VOLUME_SETTING:
163 FIELD_INFO(output_vol);
164 break;
165 case PJMEDIA_AUD_DEV_CAP_INPUT_ROUTE:
166 FIELD_INFO(input_route);
167 break;
168 case PJMEDIA_AUD_DEV_CAP_OUTPUT_ROUTE:
169 FIELD_INFO(output_route);
170 break;
171 case PJMEDIA_AUD_DEV_CAP_EC:
172 FIELD_INFO(ec_enabled);
173 break;
174 case PJMEDIA_AUD_DEV_CAP_EC_TAIL:
175 FIELD_INFO(ec_tail_ms);
176 break;
177 case PJMEDIA_AUD_DEV_CAP_VAD:
178 FIELD_INFO(ext_fmt.vad);
179 break;
180 case PJMEDIA_AUD_DEV_CAP_CNG:
181 FIELD_INFO(cng_enabled);
182 break;
183 case PJMEDIA_AUD_DEV_CAP_PLC:
184 FIELD_INFO(plc_enabled);
185 break;
186 default:
187 return PJMEDIA_EAUD_INVCAP;
188 }
189
190#undef FIELD_INFO
191
192 return PJ_SUCCESS;
193}
194
195/* API: set cap value to param */
196PJ_DEF(pj_status_t) pjmedia_aud_param_set_cap( pjmedia_aud_param *param,
197 pjmedia_aud_dev_cap cap,
198 const void *pval)
199{
200 void *cap_ptr;
201 unsigned cap_size;
202 pj_status_t status;
203
204 status = get_cap_pointer(param, cap, &cap_ptr, &cap_size);
205 if (status != PJ_SUCCESS)
206 return status;
207
208 pj_memcpy(cap_ptr, pval, cap_size);
209 param->flags |= cap;
210
211 return PJ_SUCCESS;
212}
213
214/* API: get cap value from param */
215PJ_DEF(pj_status_t) pjmedia_aud_param_get_cap( const pjmedia_aud_param *param,
216 pjmedia_aud_dev_cap cap,
217 void *pval)
218{
219 void *cap_ptr;
220 unsigned cap_size;
221 pj_status_t status;
222
223 status = get_cap_pointer(param, cap, &cap_ptr, &cap_size);
224 if (status != PJ_SUCCESS)
225 return status;
226
227 if ((param->flags & cap) == 0) {
228 pj_bzero(cap_ptr, cap_size);
229 return PJMEDIA_EAUD_INVCAP;
230 }
231
232 pj_memcpy(pval, cap_ptr, cap_size);
233 return PJ_SUCCESS;
234}
Benny Prijono2cd64f82009-02-17 19:57:48 +0000235
Benny Prijono96e74f32009-02-22 12:00:12 +0000236/* Internal: init driver */
237static pj_status_t init_driver(unsigned drv_idx)
238{
239 struct driver *drv = &aud_subsys.drv[drv_idx];
240 pjmedia_aud_dev_factory *f;
241 unsigned i, dev_cnt;
242 pj_status_t status;
243
244 /* Create the factory */
245 f = (*drv->create)(aud_subsys.pf);
246 if (!f)
247 return PJ_EUNKNOWN;
248
249 /* Call factory->init() */
250 status = f->op->init(f);
251 if (status != PJ_SUCCESS) {
252 f->op->destroy(f);
253 return status;
254 }
255
256 /* Get number of devices */
257 dev_cnt = f->op->get_dev_count(f);
258 if (dev_cnt + aud_subsys.dev_cnt > MAX_DEVS) {
259 PJ_LOG(4,(THIS_FILE, "%d device(s) cannot be registered because"
260 " there are too many devices",
261 aud_subsys.dev_cnt + dev_cnt - MAX_DEVS));
262 dev_cnt = MAX_DEVS - aud_subsys.dev_cnt;
263 }
264 if (dev_cnt == 0) {
265 f->op->destroy(f);
266 return PJMEDIA_EAUD_NODEV;
267 }
268
269 /* Fill in default devices */
270 drv->play_dev_idx = drv->rec_dev_idx = drv->dev_idx = -1;
271 for (i=0; i<dev_cnt; ++i) {
272 pjmedia_aud_dev_info info;
273
274 status = f->op->get_dev_info(f, i, &info);
275 if (status != PJ_SUCCESS) {
276 f->op->destroy(f);
277 return status;
278 }
279
280 if (drv->name[0]=='\0') {
281 /* Set driver name */
282 pj_ansi_strncpy(drv->name, info.driver, sizeof(drv->name));
283 drv->name[sizeof(drv->name)-1] = '\0';
284 }
285
286 if (drv->play_dev_idx < 0 && info.output_count) {
287 /* Set default playback device */
288 drv->play_dev_idx = i;
289 }
290 if (drv->rec_dev_idx < 0 && info.input_count) {
291 /* Set default capture device */
292 drv->rec_dev_idx = i;
293 }
294 if (drv->dev_idx < 0 && info.input_count &&
295 info.output_count)
296 {
297 /* Set default capture and playback device */
298 drv->dev_idx = i;
299 }
300
301 if (drv->play_dev_idx >= 0 && drv->rec_dev_idx >= 0 &&
302 drv->dev_idx >= 0)
303 {
304 /* Done. */
305 break;
306 }
307 }
308
309 /* Register the factory */
310 drv->f = f;
311 drv->f->sys.drv_idx = drv_idx;
312 drv->start_idx = aud_subsys.dev_cnt;
313 drv->dev_cnt = dev_cnt;
314
315 /* Register devices to global list */
316 for (i=0; i<dev_cnt; ++i) {
317 aud_subsys.dev_list[aud_subsys.dev_cnt++] = MAKE_DEV_ID(drv_idx, i);
318 }
319
320 return PJ_SUCCESS;
321}
322
323/* Internal: deinit driver */
324static void deinit_driver(unsigned drv_idx)
325{
326 struct driver *drv = &aud_subsys.drv[drv_idx];
327
328 if (drv->f) {
329 drv->f->op->destroy(drv->f);
330 drv->f = NULL;
331 }
332
333 drv->dev_cnt = 0;
334 drv->play_dev_idx = drv->rec_dev_idx = drv->dev_idx = -1;
335}
Benny Prijono2cd64f82009-02-17 19:57:48 +0000336
337/* API: Initialize the audio subsystem. */
338PJ_DEF(pj_status_t) pjmedia_aud_subsys_init(pj_pool_factory *pf)
339{
340 unsigned i;
341 pj_status_t status = PJ_ENOMEM;
342
Benny Prijono555139d2009-02-19 12:08:19 +0000343 /* Allow init() to be called multiple times as long as there is matching
344 * number of shutdown().
345 */
346 if (aud_subsys.init_count++ != 0) {
347 return PJ_SUCCESS;
348 }
349
Benny Prijono8eeab0b2009-03-04 19:00:28 +0000350 /* Register error subsystem */
351 pj_register_strerror(PJMEDIA_AUDIODEV_ERRNO_START,
352 PJ_ERRNO_SPACE_SIZE,
353 &pjmedia_audiodev_strerror);
354
355 /* Init */
Benny Prijonoe3ebd552009-02-18 20:14:15 +0000356 aud_subsys.pf = pf;
Benny Prijono10454dc2009-02-21 14:21:59 +0000357 aud_subsys.drv_cnt = 0;
358 aud_subsys.dev_cnt = 0;
Benny Prijono2cd64f82009-02-17 19:57:48 +0000359
Benny Prijono10454dc2009-02-21 14:21:59 +0000360 /* Register creation functions */
Benny Prijono2058f472009-02-22 17:15:34 +0000361#if PJMEDIA_AUDIO_DEV_HAS_PORTAUDIO
Benny Prijono10454dc2009-02-21 14:21:59 +0000362 aud_subsys.drv[aud_subsys.drv_cnt++].create = &pjmedia_pa_factory;
Benny Prijono2058f472009-02-22 17:15:34 +0000363#endif
364#if PJMEDIA_AUDIO_DEV_HAS_WMME
Benny Prijono10454dc2009-02-21 14:21:59 +0000365 aud_subsys.drv[aud_subsys.drv_cnt++].create = &pjmedia_wmme_factory;
Benny Prijono2058f472009-02-22 17:15:34 +0000366#endif
Nanang Izzuddina940b362009-02-23 13:53:30 +0000367#if PJMEDIA_AUDIO_DEV_HAS_SYMB_APS
368 aud_subsys.drv[aud_subsys.drv_cnt++].create = &pjmedia_aps_factory;
369#endif
370#if PJMEDIA_AUDIO_DEV_HAS_SYMB_MDA
371 aud_subsys.drv[aud_subsys.drv_cnt++].create = &pjmedia_symb_mda_factory;
372#endif
Benny Prijonoe3ebd552009-02-18 20:14:15 +0000373
Benny Prijono10454dc2009-02-21 14:21:59 +0000374 /* Initialize each factory and build the device ID list */
375 for (i=0; i<aud_subsys.drv_cnt; ++i) {
Benny Prijono96e74f32009-02-22 12:00:12 +0000376 status = init_driver(i);
Benny Prijono2cd64f82009-02-17 19:57:48 +0000377 if (status != PJ_SUCCESS) {
Benny Prijono96e74f32009-02-22 12:00:12 +0000378 deinit_driver(i);
Benny Prijonoe3ebd552009-02-18 20:14:15 +0000379 continue;
Benny Prijono2cd64f82009-02-17 19:57:48 +0000380 }
Benny Prijono2cd64f82009-02-17 19:57:48 +0000381 }
382
Benny Prijono96e74f32009-02-22 12:00:12 +0000383 return aud_subsys.dev_cnt ? PJ_SUCCESS : status;
Benny Prijonoe3ebd552009-02-18 20:14:15 +0000384}
385
386/* API: get the pool factory registered to the audio subsystem. */
387PJ_DEF(pj_pool_factory*) pjmedia_aud_subsys_get_pool_factory(void)
388{
389 return aud_subsys.pf;
Benny Prijono2cd64f82009-02-17 19:57:48 +0000390}
391
392/* API: Shutdown the audio subsystem. */
393PJ_DEF(pj_status_t) pjmedia_aud_subsys_shutdown(void)
394{
395 unsigned i;
396
Benny Prijono555139d2009-02-19 12:08:19 +0000397 /* Allow shutdown() to be called multiple times as long as there is matching
398 * number of init().
399 */
400 if (aud_subsys.init_count == 0) {
401 return PJ_SUCCESS;
402 }
403 --aud_subsys.init_count;
404
Benny Prijono10454dc2009-02-21 14:21:59 +0000405 for (i=0; i<aud_subsys.drv_cnt; ++i) {
Benny Prijono96e74f32009-02-22 12:00:12 +0000406 deinit_driver(i);
Benny Prijono2cd64f82009-02-17 19:57:48 +0000407 }
408
Benny Prijono96e74f32009-02-22 12:00:12 +0000409 aud_subsys.pf = NULL;
Benny Prijono2cd64f82009-02-17 19:57:48 +0000410 return PJ_SUCCESS;
411}
412
413/* API: Get the number of sound devices installed in the system. */
414PJ_DEF(unsigned) pjmedia_aud_dev_count(void)
415{
Benny Prijono10454dc2009-02-21 14:21:59 +0000416 return aud_subsys.dev_cnt;
Benny Prijono2cd64f82009-02-17 19:57:48 +0000417}
418
Benny Prijono96e74f32009-02-22 12:00:12 +0000419/* Internal: convert local index to global device index */
420static pj_status_t make_global_index(unsigned drv_idx,
421 pjmedia_aud_dev_index *id)
422{
423 if (*id < 0) {
424 return PJ_SUCCESS;
425 }
426
427 /* Check that factory still exists */
428 PJ_ASSERT_RETURN(aud_subsys.drv[drv_idx].f, PJ_EBUG);
429
430 /* Check that device index is valid */
431 PJ_ASSERT_RETURN(*id>=0 && *id<(int)aud_subsys.drv[drv_idx].dev_cnt,
432 PJ_EBUG);
433
434 *id += aud_subsys.drv[drv_idx].start_idx;
435 return PJ_SUCCESS;
436}
437
Benny Prijono10454dc2009-02-21 14:21:59 +0000438/* Internal: lookup device id */
439static pj_status_t lookup_dev(pjmedia_aud_dev_index id,
440 pjmedia_aud_dev_factory **p_f,
441 unsigned *p_local_index)
Benny Prijono2cd64f82009-02-17 19:57:48 +0000442{
Benny Prijono10454dc2009-02-21 14:21:59 +0000443 int f_id, index;
Benny Prijono2cd64f82009-02-17 19:57:48 +0000444
Benny Prijono96e74f32009-02-22 12:00:12 +0000445 if (id < 0) {
446 unsigned i;
Benny Prijono2cd64f82009-02-17 19:57:48 +0000447
Benny Prijono96e74f32009-02-22 12:00:12 +0000448 if (id == PJMEDIA_AUD_INVALID_DEV)
449 return PJMEDIA_EAUD_INVDEV;
450
451 for (i=0; i<aud_subsys.drv_cnt; ++i) {
452 struct driver *drv = &aud_subsys.drv[i];
453 if (drv->dev_idx >= 0) {
454 id = drv->dev_idx;
455 make_global_index(i, &id);
456 break;
457 } else if (id==PJMEDIA_AUD_DEFAULT_CAPTURE_DEV &&
458 drv->rec_dev_idx >= 0)
459 {
460 id = drv->rec_dev_idx;
461 make_global_index(i, &id);
462 break;
463 } else if (id==PJMEDIA_AUD_DEFAULT_PLAYBACK_DEV &&
464 drv->play_dev_idx >= 0)
465 {
466 id = drv->play_dev_idx;
467 make_global_index(i, &id);
468 break;
469 }
470 }
471
472 if (id < 0) {
473 return PJMEDIA_EAUD_NODEFDEV;
474 }
475 }
Benny Prijono2cd64f82009-02-17 19:57:48 +0000476
Benny Prijono10454dc2009-02-21 14:21:59 +0000477 f_id = GET_FID(aud_subsys.dev_list[id]);
478 index = GET_INDEX(aud_subsys.dev_list[id]);
Benny Prijono2cd64f82009-02-17 19:57:48 +0000479
Benny Prijono10454dc2009-02-21 14:21:59 +0000480 if (f_id < 0 || f_id >= (int)aud_subsys.drv_cnt)
481 return PJMEDIA_EAUD_INVDEV;
482
483 if (index < 0 || index >= (int)aud_subsys.drv[f_id].dev_cnt)
484 return PJMEDIA_EAUD_INVDEV;
485
486 *p_f = aud_subsys.drv[f_id].f;
487 *p_local_index = (unsigned)index;
488
489 return PJ_SUCCESS;
490
Benny Prijono2cd64f82009-02-17 19:57:48 +0000491}
492
Benny Prijono2cd64f82009-02-17 19:57:48 +0000493/* API: Get device information. */
Benny Prijono10454dc2009-02-21 14:21:59 +0000494PJ_DEF(pj_status_t) pjmedia_aud_dev_get_info(pjmedia_aud_dev_index id,
Benny Prijono2cd64f82009-02-17 19:57:48 +0000495 pjmedia_aud_dev_info *info)
496{
Benny Prijonoe3ebd552009-02-18 20:14:15 +0000497 pjmedia_aud_dev_factory *f;
Benny Prijono10454dc2009-02-21 14:21:59 +0000498 unsigned index;
499 pj_status_t status;
Benny Prijono2cd64f82009-02-17 19:57:48 +0000500
Benny Prijono96e74f32009-02-22 12:00:12 +0000501 PJ_ASSERT_RETURN(info && id!=PJMEDIA_AUD_INVALID_DEV, PJ_EINVAL);
502 PJ_ASSERT_RETURN(aud_subsys.pf, PJMEDIA_EAUD_INIT);
Benny Prijono598b01d2009-02-18 13:55:03 +0000503
Benny Prijono10454dc2009-02-21 14:21:59 +0000504 status = lookup_dev(id, &f, &index);
505 if (status != PJ_SUCCESS)
506 return status;
Benny Prijono2cd64f82009-02-17 19:57:48 +0000507
Benny Prijonoe3ebd552009-02-18 20:14:15 +0000508 return f->op->get_dev_info(f, index, info);
Benny Prijono2cd64f82009-02-17 19:57:48 +0000509}
510
Benny Prijono10454dc2009-02-21 14:21:59 +0000511/* API: find device */
512PJ_DEF(pj_status_t) pjmedia_aud_dev_lookup( const char *drv_name,
513 const char *dev_name,
514 pjmedia_aud_dev_index *id)
515{
516 pjmedia_aud_dev_factory *f = NULL;
Benny Prijono96e74f32009-02-22 12:00:12 +0000517 unsigned drv_idx, dev_idx;
Benny Prijono10454dc2009-02-21 14:21:59 +0000518
519 PJ_ASSERT_RETURN(drv_name && dev_name && id, PJ_EINVAL);
Benny Prijono96e74f32009-02-22 12:00:12 +0000520 PJ_ASSERT_RETURN(aud_subsys.pf, PJMEDIA_EAUD_INIT);
Benny Prijono10454dc2009-02-21 14:21:59 +0000521
Benny Prijono96e74f32009-02-22 12:00:12 +0000522 for (drv_idx=0; drv_idx<aud_subsys.drv_cnt; ++drv_idx) {
523 if (!pj_ansi_stricmp(drv_name, aud_subsys.drv[drv_idx].name)) {
524 f = aud_subsys.drv[drv_idx].f;
Benny Prijono10454dc2009-02-21 14:21:59 +0000525 break;
526 }
527 }
528
529 if (!f)
530 return PJ_ENOTFOUND;
531
Benny Prijono96e74f32009-02-22 12:00:12 +0000532 for (dev_idx=0; dev_idx<aud_subsys.drv[drv_idx].dev_cnt; ++dev_idx) {
Benny Prijono10454dc2009-02-21 14:21:59 +0000533 pjmedia_aud_dev_info info;
534 pj_status_t status;
535
Benny Prijono96e74f32009-02-22 12:00:12 +0000536 status = f->op->get_dev_info(f, dev_idx, &info);
Benny Prijono10454dc2009-02-21 14:21:59 +0000537 if (status != PJ_SUCCESS)
538 return status;
539
540 if (!pj_ansi_stricmp(dev_name, info.name))
541 break;
542 }
543
Benny Prijono96e74f32009-02-22 12:00:12 +0000544 if (dev_idx==aud_subsys.drv[drv_idx].dev_cnt)
Benny Prijono10454dc2009-02-21 14:21:59 +0000545 return PJ_ENOTFOUND;
546
Benny Prijono96e74f32009-02-22 12:00:12 +0000547 *id = dev_idx;
548 make_global_index(drv_idx, id);
Benny Prijono10454dc2009-02-21 14:21:59 +0000549
550 return PJ_SUCCESS;
551}
552
Benny Prijono2cd64f82009-02-17 19:57:48 +0000553/* API: Initialize the audio device parameters with default values for the
554 * specified device.
555 */
Benny Prijono10454dc2009-02-21 14:21:59 +0000556PJ_DEF(pj_status_t) pjmedia_aud_dev_default_param(pjmedia_aud_dev_index id,
557 pjmedia_aud_param *param)
Benny Prijono2cd64f82009-02-17 19:57:48 +0000558{
Benny Prijonoe3ebd552009-02-18 20:14:15 +0000559 pjmedia_aud_dev_factory *f;
Benny Prijono10454dc2009-02-21 14:21:59 +0000560 unsigned index;
Benny Prijono2cd64f82009-02-17 19:57:48 +0000561 pj_status_t status;
562
Benny Prijono96e74f32009-02-22 12:00:12 +0000563 PJ_ASSERT_RETURN(param && id!=PJMEDIA_AUD_INVALID_DEV, PJ_EINVAL);
564 PJ_ASSERT_RETURN(aud_subsys.pf, PJMEDIA_EAUD_INIT);
Benny Prijono598b01d2009-02-18 13:55:03 +0000565
Benny Prijono10454dc2009-02-21 14:21:59 +0000566 status = lookup_dev(id, &f, &index);
567 if (status != PJ_SUCCESS)
568 return status;
Benny Prijono2cd64f82009-02-17 19:57:48 +0000569
Benny Prijonoe3ebd552009-02-18 20:14:15 +0000570 status = f->op->default_param(f, index, param);
Benny Prijono2cd64f82009-02-17 19:57:48 +0000571 if (status != PJ_SUCCESS)
572 return status;
573
574 /* Normalize device IDs */
Benny Prijono96e74f32009-02-22 12:00:12 +0000575 make_global_index(f->sys.drv_idx, &param->rec_id);
576 make_global_index(f->sys.drv_idx, &param->play_id);
Benny Prijono2cd64f82009-02-17 19:57:48 +0000577
578 return PJ_SUCCESS;
579}
580
581/* API: Open audio stream object using the specified parameters. */
Benny Prijono10454dc2009-02-21 14:21:59 +0000582PJ_DEF(pj_status_t) pjmedia_aud_stream_create(const pjmedia_aud_param *prm,
Benny Prijono2cd64f82009-02-17 19:57:48 +0000583 pjmedia_aud_rec_cb rec_cb,
584 pjmedia_aud_play_cb play_cb,
585 void *user_data,
586 pjmedia_aud_stream **p_aud_strm)
587{
Benny Prijono10454dc2009-02-21 14:21:59 +0000588 pjmedia_aud_dev_factory *rec_f=NULL, *play_f=NULL, *f=NULL;
589 pjmedia_aud_param param;
Benny Prijono2cd64f82009-02-17 19:57:48 +0000590 pj_status_t status;
591
Benny Prijono10454dc2009-02-21 14:21:59 +0000592 PJ_ASSERT_RETURN(prm && prm->dir && p_aud_strm, PJ_EINVAL);
Benny Prijono96e74f32009-02-22 12:00:12 +0000593 PJ_ASSERT_RETURN(aud_subsys.pf, PJMEDIA_EAUD_INIT);
Benny Prijono10454dc2009-02-21 14:21:59 +0000594
Benny Prijono2cd64f82009-02-17 19:57:48 +0000595 /* Must make copy of param because we're changing device ID */
Benny Prijono10454dc2009-02-21 14:21:59 +0000596 pj_memcpy(&param, prm, sizeof(param));
Benny Prijono2cd64f82009-02-17 19:57:48 +0000597
Benny Prijono10454dc2009-02-21 14:21:59 +0000598 /* Normalize rec_id */
599 if (param.dir & PJMEDIA_DIR_CAPTURE) {
600 unsigned index;
601
Benny Prijono96e74f32009-02-22 12:00:12 +0000602 if (param.rec_id < 0)
603 param.rec_id = PJMEDIA_AUD_DEFAULT_CAPTURE_DEV;
604
Benny Prijono10454dc2009-02-21 14:21:59 +0000605 status = lookup_dev(param.rec_id, &rec_f, &index);
606 if (status != PJ_SUCCESS)
607 return status;
608
609 param.rec_id = index;
610 f = rec_f;
611 }
612
613 /* Normalize play_id */
614 if (param.dir & PJMEDIA_DIR_PLAYBACK) {
615 unsigned index;
616
Benny Prijono96e74f32009-02-22 12:00:12 +0000617 if (param.play_id < 0)
618 param.play_id = PJMEDIA_AUD_DEFAULT_PLAYBACK_DEV;
619
Benny Prijono10454dc2009-02-21 14:21:59 +0000620 status = lookup_dev(param.play_id, &play_f, &index);
621 if (status != PJ_SUCCESS)
622 return status;
623
624 param.play_id = index;
625 f = play_f;
Benny Prijono10454dc2009-02-21 14:21:59 +0000626 }
627
Nanang Izzuddin6f7d5a12009-03-16 16:29:39 +0000628 /* For now, rec_id and play_id must belong to the same factory */
629 PJ_ASSERT_RETURN((param.dir != PJMEDIA_DIR_CAPTURE_PLAYBACK) ||
630 (rec_f == play_f),
631 PJMEDIA_EAUD_INVDEV);
632
Benny Prijono10454dc2009-02-21 14:21:59 +0000633 /* Create the stream */
Benny Prijonoe3ebd552009-02-18 20:14:15 +0000634 status = f->op->create_stream(f, &param, rec_cb, play_cb,
635 user_data, p_aud_strm);
Benny Prijono2cd64f82009-02-17 19:57:48 +0000636 if (status != PJ_SUCCESS)
637 return status;
638
Benny Prijono10454dc2009-02-21 14:21:59 +0000639 /* Assign factory id to the stream */
Benny Prijono96e74f32009-02-22 12:00:12 +0000640 (*p_aud_strm)->sys.drv_idx = f->sys.drv_idx;
Benny Prijono2cd64f82009-02-17 19:57:48 +0000641 return PJ_SUCCESS;
642}
643
644/* API: Get the running parameters for the specified audio stream. */
645PJ_DEF(pj_status_t) pjmedia_aud_stream_get_param(pjmedia_aud_stream *strm,
Benny Prijono10454dc2009-02-21 14:21:59 +0000646 pjmedia_aud_param *param)
Benny Prijono2cd64f82009-02-17 19:57:48 +0000647{
648 pj_status_t status;
649
Benny Prijono96e74f32009-02-22 12:00:12 +0000650 PJ_ASSERT_RETURN(strm && param, PJ_EINVAL);
651 PJ_ASSERT_RETURN(aud_subsys.pf, PJMEDIA_EAUD_INIT);
652
Benny Prijono2cd64f82009-02-17 19:57:48 +0000653 status = strm->op->get_param(strm, param);
654 if (status != PJ_SUCCESS)
655 return status;
656
657 /* Normalize device id's */
Benny Prijono96e74f32009-02-22 12:00:12 +0000658 make_global_index(strm->sys.drv_idx, &param->rec_id);
659 make_global_index(strm->sys.drv_idx, &param->play_id);
Benny Prijono2cd64f82009-02-17 19:57:48 +0000660
661 return PJ_SUCCESS;
662}
663
664/* API: Get the value of a specific capability of the audio stream. */
665PJ_DEF(pj_status_t) pjmedia_aud_stream_get_cap(pjmedia_aud_stream *strm,
666 pjmedia_aud_dev_cap cap,
667 void *value)
668{
669 return strm->op->get_cap(strm, cap, value);
670}
671
672/* API: Set the value of a specific capability of the audio stream. */
673PJ_DEF(pj_status_t) pjmedia_aud_stream_set_cap(pjmedia_aud_stream *strm,
674 pjmedia_aud_dev_cap cap,
675 const void *value)
676{
677 return strm->op->set_cap(strm, cap, value);
678}
679
680/* API: Start the stream. */
681PJ_DEF(pj_status_t) pjmedia_aud_stream_start(pjmedia_aud_stream *strm)
682{
683 return strm->op->start(strm);
684}
685
686/* API: Stop the stream. */
687PJ_DEF(pj_status_t) pjmedia_aud_stream_stop(pjmedia_aud_stream *strm)
688{
689 return strm->op->stop(strm);
690}
691
692/* API: Destroy the stream. */
693PJ_DEF(pj_status_t) pjmedia_aud_stream_destroy(pjmedia_aud_stream *strm)
694{
695 return strm->op->destroy(strm);
696}
697
698