blob: 0689cc943161cebeb12a778bb8ead9d4483fa079 [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
Sauw Ming55a73cd2010-05-17 12:51:06 +000073#if PJMEDIA_AUDIO_DEV_HAS_COREAUDIO
74pjmedia_aud_dev_factory* pjmedia_coreaudio_factory(pj_pool_factory *pf);
75#endif
76
Benny Prijono19a87c72010-01-27 17:22:17 +000077#if PJMEDIA_AUDIO_DEV_HAS_ALSA
78pjmedia_aud_dev_factory* pjmedia_alsa_factory(pj_pool_factory *pf);
79#endif
80
Benny Prijono2058f472009-02-22 17:15:34 +000081#if PJMEDIA_AUDIO_DEV_HAS_WMME
Benny Prijono598b01d2009-02-18 13:55:03 +000082pjmedia_aud_dev_factory* pjmedia_wmme_factory(pj_pool_factory *pf);
Benny Prijono2058f472009-02-22 17:15:34 +000083#endif
84
Nanang Izzuddind687a502009-06-30 13:37:26 +000085#if PJMEDIA_AUDIO_DEV_HAS_SYMB_VAS
86pjmedia_aud_dev_factory* pjmedia_symb_vas_factory(pj_pool_factory *pf);
87#endif
88
Nanang Izzuddina940b362009-02-23 13:53:30 +000089#if PJMEDIA_AUDIO_DEV_HAS_SYMB_APS
90pjmedia_aud_dev_factory* pjmedia_aps_factory(pj_pool_factory *pf);
91#endif
92
93#if PJMEDIA_AUDIO_DEV_HAS_SYMB_MDA
94pjmedia_aud_dev_factory* pjmedia_symb_mda_factory(pj_pool_factory *pf);
95#endif
Benny Prijono598b01d2009-02-18 13:55:03 +000096
Sauw Ming60a0c9b2010-02-19 09:57:48 +000097#if PJMEDIA_AUDIO_DEV_HAS_NULL_AUDIO
98pjmedia_aud_dev_factory* pjmedia_null_audio_factory(pj_pool_factory *pf);
99#endif
100
Benny Prijonoe3ebd552009-02-18 20:14:15 +0000101#define MAX_DRIVERS 16
Benny Prijono10454dc2009-02-21 14:21:59 +0000102#define MAX_DEVS 64
103
Benny Prijono10454dc2009-02-21 14:21:59 +0000104
105/* driver structure */
106struct driver
107{
Sauw Ming8fd16932010-05-05 04:23:27 +0000108 /* Creation function */
109 pjmedia_aud_dev_factory_create_func_ptr create;
110 /* Factory instance */
111 pjmedia_aud_dev_factory *f;
Benny Prijono10454dc2009-02-21 14:21:59 +0000112 char name[32]; /* Driver name */
113 unsigned dev_cnt; /* Number of devices */
114 unsigned start_idx; /* Start index in global list */
Benny Prijono96e74f32009-02-22 12:00:12 +0000115 int rec_dev_idx;/* Default capture device. */
116 int play_dev_idx;/* Default playback device */
117 int dev_idx; /* Default device. */
Benny Prijono10454dc2009-02-21 14:21:59 +0000118};
Benny Prijono2cd64f82009-02-17 19:57:48 +0000119
Benny Prijonoe3ebd552009-02-18 20:14:15 +0000120/* The audio subsystem */
121static struct aud_subsys
Benny Prijono2cd64f82009-02-17 19:57:48 +0000122{
Benny Prijono10454dc2009-02-21 14:21:59 +0000123 unsigned init_count; /* How many times init() is called */
124 pj_pool_factory *pf; /* The pool factory. */
Benny Prijono2cd64f82009-02-17 19:57:48 +0000125
Benny Prijono10454dc2009-02-21 14:21:59 +0000126 unsigned drv_cnt; /* Number of drivers. */
127 struct driver drv[MAX_DRIVERS]; /* Array of drivers. */
Benny Prijonoe3ebd552009-02-18 20:14:15 +0000128
Benny Prijono10454dc2009-02-21 14:21:59 +0000129 unsigned dev_cnt; /* Total number of devices. */
130 pj_uint32_t dev_list[MAX_DEVS];/* Array of device IDs. */
Benny Prijonoe3ebd552009-02-18 20:14:15 +0000131
132} aud_subsys;
133
Benny Prijono7a380002009-03-09 12:55:29 +0000134/* API: get capability name/info */
135PJ_DEF(const char*) pjmedia_aud_dev_cap_name(pjmedia_aud_dev_cap cap,
136 const char **p_desc)
137{
138 const char *desc;
139 unsigned i;
140
141 if (p_desc==NULL) p_desc = &desc;
142
143 for (i=0; i<PJ_ARRAY_SIZE(cap_infos); ++i) {
144 if ((1 << i)==cap)
145 break;
146 }
147
148 if (i==32) {
149 *p_desc = "??";
150 return "??";
151 }
152
153 *p_desc = cap_infos[i].info;
154 return cap_infos[i].name;
155}
156
157static pj_status_t get_cap_pointer(const pjmedia_aud_param *param,
158 pjmedia_aud_dev_cap cap,
159 void **ptr,
160 unsigned *size)
161{
162#define FIELD_INFO(name) *ptr = (void*)&param->name; \
163 *size = sizeof(param->name)
164
165 switch (cap) {
166 case PJMEDIA_AUD_DEV_CAP_EXT_FORMAT:
167 FIELD_INFO(ext_fmt);
168 break;
169 case PJMEDIA_AUD_DEV_CAP_INPUT_LATENCY:
170 FIELD_INFO(input_latency_ms);
171 break;
172 case PJMEDIA_AUD_DEV_CAP_OUTPUT_LATENCY:
173 FIELD_INFO(output_latency_ms);
174 break;
175 case PJMEDIA_AUD_DEV_CAP_INPUT_VOLUME_SETTING:
176 FIELD_INFO(input_vol);
177 break;
178 case PJMEDIA_AUD_DEV_CAP_OUTPUT_VOLUME_SETTING:
179 FIELD_INFO(output_vol);
180 break;
181 case PJMEDIA_AUD_DEV_CAP_INPUT_ROUTE:
182 FIELD_INFO(input_route);
183 break;
184 case PJMEDIA_AUD_DEV_CAP_OUTPUT_ROUTE:
185 FIELD_INFO(output_route);
186 break;
187 case PJMEDIA_AUD_DEV_CAP_EC:
188 FIELD_INFO(ec_enabled);
189 break;
190 case PJMEDIA_AUD_DEV_CAP_EC_TAIL:
191 FIELD_INFO(ec_tail_ms);
192 break;
193 case PJMEDIA_AUD_DEV_CAP_VAD:
194 FIELD_INFO(ext_fmt.vad);
195 break;
196 case PJMEDIA_AUD_DEV_CAP_CNG:
197 FIELD_INFO(cng_enabled);
198 break;
199 case PJMEDIA_AUD_DEV_CAP_PLC:
200 FIELD_INFO(plc_enabled);
201 break;
202 default:
203 return PJMEDIA_EAUD_INVCAP;
204 }
205
206#undef FIELD_INFO
207
208 return PJ_SUCCESS;
209}
210
211/* API: set cap value to param */
212PJ_DEF(pj_status_t) pjmedia_aud_param_set_cap( pjmedia_aud_param *param,
213 pjmedia_aud_dev_cap cap,
214 const void *pval)
215{
216 void *cap_ptr;
217 unsigned cap_size;
218 pj_status_t status;
219
220 status = get_cap_pointer(param, cap, &cap_ptr, &cap_size);
221 if (status != PJ_SUCCESS)
222 return status;
223
224 pj_memcpy(cap_ptr, pval, cap_size);
225 param->flags |= cap;
226
227 return PJ_SUCCESS;
228}
229
230/* API: get cap value from param */
231PJ_DEF(pj_status_t) pjmedia_aud_param_get_cap( const pjmedia_aud_param *param,
232 pjmedia_aud_dev_cap cap,
233 void *pval)
234{
235 void *cap_ptr;
236 unsigned cap_size;
237 pj_status_t status;
238
239 status = get_cap_pointer(param, cap, &cap_ptr, &cap_size);
240 if (status != PJ_SUCCESS)
241 return status;
242
243 if ((param->flags & cap) == 0) {
244 pj_bzero(cap_ptr, cap_size);
245 return PJMEDIA_EAUD_INVCAP;
246 }
247
248 pj_memcpy(pval, cap_ptr, cap_size);
249 return PJ_SUCCESS;
250}
Benny Prijono2cd64f82009-02-17 19:57:48 +0000251
Benny Prijono96e74f32009-02-22 12:00:12 +0000252/* Internal: init driver */
253static pj_status_t init_driver(unsigned drv_idx)
254{
255 struct driver *drv = &aud_subsys.drv[drv_idx];
256 pjmedia_aud_dev_factory *f;
257 unsigned i, dev_cnt;
258 pj_status_t status;
259
260 /* Create the factory */
261 f = (*drv->create)(aud_subsys.pf);
262 if (!f)
263 return PJ_EUNKNOWN;
264
265 /* Call factory->init() */
266 status = f->op->init(f);
267 if (status != PJ_SUCCESS) {
268 f->op->destroy(f);
269 return status;
270 }
271
272 /* Get number of devices */
273 dev_cnt = f->op->get_dev_count(f);
274 if (dev_cnt + aud_subsys.dev_cnt > MAX_DEVS) {
275 PJ_LOG(4,(THIS_FILE, "%d device(s) cannot be registered because"
276 " there are too many devices",
277 aud_subsys.dev_cnt + dev_cnt - MAX_DEVS));
278 dev_cnt = MAX_DEVS - aud_subsys.dev_cnt;
279 }
Benny Prijonof87f67c2009-04-06 10:02:53 +0000280
281 /* enabling this will cause pjsua-lib initialization to fail when there
282 * is no sound device installed in the system, even when pjsua has been
283 * run with --null-audio
284 *
Benny Prijono96e74f32009-02-22 12:00:12 +0000285 if (dev_cnt == 0) {
286 f->op->destroy(f);
287 return PJMEDIA_EAUD_NODEV;
288 }
Benny Prijonof87f67c2009-04-06 10:02:53 +0000289 */
Benny Prijono96e74f32009-02-22 12:00:12 +0000290
291 /* Fill in default devices */
292 drv->play_dev_idx = drv->rec_dev_idx = drv->dev_idx = -1;
293 for (i=0; i<dev_cnt; ++i) {
294 pjmedia_aud_dev_info info;
295
296 status = f->op->get_dev_info(f, i, &info);
297 if (status != PJ_SUCCESS) {
298 f->op->destroy(f);
299 return status;
300 }
301
302 if (drv->name[0]=='\0') {
303 /* Set driver name */
304 pj_ansi_strncpy(drv->name, info.driver, sizeof(drv->name));
305 drv->name[sizeof(drv->name)-1] = '\0';
306 }
307
308 if (drv->play_dev_idx < 0 && info.output_count) {
309 /* Set default playback device */
310 drv->play_dev_idx = i;
311 }
312 if (drv->rec_dev_idx < 0 && info.input_count) {
313 /* Set default capture device */
314 drv->rec_dev_idx = i;
315 }
316 if (drv->dev_idx < 0 && info.input_count &&
317 info.output_count)
318 {
319 /* Set default capture and playback device */
320 drv->dev_idx = i;
321 }
322
323 if (drv->play_dev_idx >= 0 && drv->rec_dev_idx >= 0 &&
324 drv->dev_idx >= 0)
325 {
326 /* Done. */
327 break;
328 }
329 }
330
331 /* Register the factory */
332 drv->f = f;
333 drv->f->sys.drv_idx = drv_idx;
334 drv->start_idx = aud_subsys.dev_cnt;
335 drv->dev_cnt = dev_cnt;
336
337 /* Register devices to global list */
338 for (i=0; i<dev_cnt; ++i) {
339 aud_subsys.dev_list[aud_subsys.dev_cnt++] = MAKE_DEV_ID(drv_idx, i);
340 }
341
342 return PJ_SUCCESS;
343}
344
345/* Internal: deinit driver */
346static void deinit_driver(unsigned drv_idx)
347{
348 struct driver *drv = &aud_subsys.drv[drv_idx];
349
350 if (drv->f) {
351 drv->f->op->destroy(drv->f);
352 drv->f = NULL;
353 }
354
355 drv->dev_cnt = 0;
356 drv->play_dev_idx = drv->rec_dev_idx = drv->dev_idx = -1;
357}
Benny Prijono2cd64f82009-02-17 19:57:48 +0000358
359/* API: Initialize the audio subsystem. */
360PJ_DEF(pj_status_t) pjmedia_aud_subsys_init(pj_pool_factory *pf)
361{
362 unsigned i;
Benny Prijonodebe8e12009-06-03 12:29:35 +0000363 pj_status_t status = PJ_SUCCESS;
Benny Prijono2cd64f82009-02-17 19:57:48 +0000364
Benny Prijono555139d2009-02-19 12:08:19 +0000365 /* Allow init() to be called multiple times as long as there is matching
366 * number of shutdown().
367 */
368 if (aud_subsys.init_count++ != 0) {
369 return PJ_SUCCESS;
370 }
371
Benny Prijono8eeab0b2009-03-04 19:00:28 +0000372 /* Register error subsystem */
373 pj_register_strerror(PJMEDIA_AUDIODEV_ERRNO_START,
374 PJ_ERRNO_SPACE_SIZE,
375 &pjmedia_audiodev_strerror);
376
377 /* Init */
Benny Prijonoe3ebd552009-02-18 20:14:15 +0000378 aud_subsys.pf = pf;
Benny Prijono10454dc2009-02-21 14:21:59 +0000379 aud_subsys.drv_cnt = 0;
380 aud_subsys.dev_cnt = 0;
Benny Prijono2cd64f82009-02-17 19:57:48 +0000381
Benny Prijono10454dc2009-02-21 14:21:59 +0000382 /* Register creation functions */
Benny Prijono19a87c72010-01-27 17:22:17 +0000383#if PJMEDIA_AUDIO_DEV_HAS_ALSA
384 aud_subsys.drv[aud_subsys.drv_cnt++].create = &pjmedia_alsa_factory;
385#endif
Sauw Ming55a73cd2010-05-17 12:51:06 +0000386#if PJMEDIA_AUDIO_DEV_HAS_COREAUDIO
387 aud_subsys.drv[aud_subsys.drv_cnt++].create = &pjmedia_coreaudio_factory;
388#endif
Benny Prijono2058f472009-02-22 17:15:34 +0000389#if PJMEDIA_AUDIO_DEV_HAS_PORTAUDIO
Benny Prijono10454dc2009-02-21 14:21:59 +0000390 aud_subsys.drv[aud_subsys.drv_cnt++].create = &pjmedia_pa_factory;
Benny Prijono2058f472009-02-22 17:15:34 +0000391#endif
392#if PJMEDIA_AUDIO_DEV_HAS_WMME
Benny Prijono10454dc2009-02-21 14:21:59 +0000393 aud_subsys.drv[aud_subsys.drv_cnt++].create = &pjmedia_wmme_factory;
Benny Prijono2058f472009-02-22 17:15:34 +0000394#endif
Nanang Izzuddind687a502009-06-30 13:37:26 +0000395#if PJMEDIA_AUDIO_DEV_HAS_SYMB_VAS
396 aud_subsys.drv[aud_subsys.drv_cnt++].create = &pjmedia_symb_vas_factory;
397#endif
Nanang Izzuddina940b362009-02-23 13:53:30 +0000398#if PJMEDIA_AUDIO_DEV_HAS_SYMB_APS
399 aud_subsys.drv[aud_subsys.drv_cnt++].create = &pjmedia_aps_factory;
400#endif
401#if PJMEDIA_AUDIO_DEV_HAS_SYMB_MDA
402 aud_subsys.drv[aud_subsys.drv_cnt++].create = &pjmedia_symb_mda_factory;
403#endif
Sauw Ming60a0c9b2010-02-19 09:57:48 +0000404#if PJMEDIA_AUDIO_DEV_HAS_NULL_AUDIO
405 aud_subsys.drv[aud_subsys.drv_cnt++].create = &pjmedia_null_audio_factory;
406#endif
Benny Prijonoe3ebd552009-02-18 20:14:15 +0000407
Benny Prijono10454dc2009-02-21 14:21:59 +0000408 /* Initialize each factory and build the device ID list */
409 for (i=0; i<aud_subsys.drv_cnt; ++i) {
Benny Prijono96e74f32009-02-22 12:00:12 +0000410 status = init_driver(i);
Benny Prijono2cd64f82009-02-17 19:57:48 +0000411 if (status != PJ_SUCCESS) {
Benny Prijono96e74f32009-02-22 12:00:12 +0000412 deinit_driver(i);
Benny Prijonoe3ebd552009-02-18 20:14:15 +0000413 continue;
Benny Prijono2cd64f82009-02-17 19:57:48 +0000414 }
Benny Prijono2cd64f82009-02-17 19:57:48 +0000415 }
416
Benny Prijono96e74f32009-02-22 12:00:12 +0000417 return aud_subsys.dev_cnt ? PJ_SUCCESS : status;
Benny Prijonoe3ebd552009-02-18 20:14:15 +0000418}
419
Sauw Ming8fd16932010-05-05 04:23:27 +0000420/* API: register an audio device factory to the audio subsystem. */
421PJ_DEF(pj_status_t)
422pjmedia_aud_register_factory(pjmedia_aud_dev_factory_create_func_ptr adf)
423{
424 pj_status_t status;
425
426 if (aud_subsys.init_count == 0)
427 return PJMEDIA_EAUD_INIT;
428
429 aud_subsys.drv[aud_subsys.drv_cnt].create = adf;
430 status = init_driver(aud_subsys.drv_cnt);
431 if (status == PJ_SUCCESS) {
432 aud_subsys.drv_cnt++;
433 } else {
434 deinit_driver(aud_subsys.drv_cnt);
435 }
436
437 return status;
438}
439
440/* API: unregister an audio device factory from the audio subsystem. */
441PJ_DEF(pj_status_t)
442pjmedia_aud_unregister_factory(pjmedia_aud_dev_factory_create_func_ptr adf)
443{
444 unsigned i, j;
445
446 if (aud_subsys.init_count == 0)
447 return PJMEDIA_EAUD_INIT;
448
449 for (i=0; i<aud_subsys.drv_cnt; ++i) {
450 struct driver *drv = &aud_subsys.drv[i];
451
452 if (drv->create == adf) {
453 for (j = drv->start_idx; j < drv->start_idx + drv->dev_cnt; j++)
454 {
455 aud_subsys.dev_list[j] = PJMEDIA_AUD_INVALID_DEV;
456 }
457
458 deinit_driver(i);
459 pj_bzero(drv, sizeof(*drv));
460 return PJ_SUCCESS;
461 }
462 }
463
464 return PJMEDIA_EAUD_ERR;
465}
466
Benny Prijonoe3ebd552009-02-18 20:14:15 +0000467/* API: get the pool factory registered to the audio subsystem. */
468PJ_DEF(pj_pool_factory*) pjmedia_aud_subsys_get_pool_factory(void)
469{
470 return aud_subsys.pf;
Benny Prijono2cd64f82009-02-17 19:57:48 +0000471}
472
473/* API: Shutdown the audio subsystem. */
474PJ_DEF(pj_status_t) pjmedia_aud_subsys_shutdown(void)
475{
476 unsigned i;
477
Benny Prijono555139d2009-02-19 12:08:19 +0000478 /* Allow shutdown() to be called multiple times as long as there is matching
479 * number of init().
480 */
481 if (aud_subsys.init_count == 0) {
482 return PJ_SUCCESS;
483 }
484 --aud_subsys.init_count;
485
Benny Prijono10454dc2009-02-21 14:21:59 +0000486 for (i=0; i<aud_subsys.drv_cnt; ++i) {
Benny Prijono96e74f32009-02-22 12:00:12 +0000487 deinit_driver(i);
Benny Prijono2cd64f82009-02-17 19:57:48 +0000488 }
489
Benny Prijono96e74f32009-02-22 12:00:12 +0000490 aud_subsys.pf = NULL;
Benny Prijono2cd64f82009-02-17 19:57:48 +0000491 return PJ_SUCCESS;
492}
493
494/* API: Get the number of sound devices installed in the system. */
495PJ_DEF(unsigned) pjmedia_aud_dev_count(void)
496{
Benny Prijono10454dc2009-02-21 14:21:59 +0000497 return aud_subsys.dev_cnt;
Benny Prijono2cd64f82009-02-17 19:57:48 +0000498}
499
Benny Prijono96e74f32009-02-22 12:00:12 +0000500/* Internal: convert local index to global device index */
501static pj_status_t make_global_index(unsigned drv_idx,
502 pjmedia_aud_dev_index *id)
503{
504 if (*id < 0) {
505 return PJ_SUCCESS;
506 }
507
508 /* Check that factory still exists */
509 PJ_ASSERT_RETURN(aud_subsys.drv[drv_idx].f, PJ_EBUG);
510
511 /* Check that device index is valid */
512 PJ_ASSERT_RETURN(*id>=0 && *id<(int)aud_subsys.drv[drv_idx].dev_cnt,
513 PJ_EBUG);
514
515 *id += aud_subsys.drv[drv_idx].start_idx;
516 return PJ_SUCCESS;
517}
518
Benny Prijono10454dc2009-02-21 14:21:59 +0000519/* Internal: lookup device id */
520static pj_status_t lookup_dev(pjmedia_aud_dev_index id,
521 pjmedia_aud_dev_factory **p_f,
522 unsigned *p_local_index)
Benny Prijono2cd64f82009-02-17 19:57:48 +0000523{
Benny Prijono10454dc2009-02-21 14:21:59 +0000524 int f_id, index;
Benny Prijono2cd64f82009-02-17 19:57:48 +0000525
Benny Prijono96e74f32009-02-22 12:00:12 +0000526 if (id < 0) {
527 unsigned i;
Benny Prijono2cd64f82009-02-17 19:57:48 +0000528
Benny Prijono96e74f32009-02-22 12:00:12 +0000529 if (id == PJMEDIA_AUD_INVALID_DEV)
530 return PJMEDIA_EAUD_INVDEV;
531
532 for (i=0; i<aud_subsys.drv_cnt; ++i) {
533 struct driver *drv = &aud_subsys.drv[i];
534 if (drv->dev_idx >= 0) {
535 id = drv->dev_idx;
536 make_global_index(i, &id);
537 break;
538 } else if (id==PJMEDIA_AUD_DEFAULT_CAPTURE_DEV &&
539 drv->rec_dev_idx >= 0)
540 {
541 id = drv->rec_dev_idx;
542 make_global_index(i, &id);
543 break;
544 } else if (id==PJMEDIA_AUD_DEFAULT_PLAYBACK_DEV &&
545 drv->play_dev_idx >= 0)
546 {
547 id = drv->play_dev_idx;
548 make_global_index(i, &id);
549 break;
550 }
551 }
552
553 if (id < 0) {
554 return PJMEDIA_EAUD_NODEFDEV;
555 }
556 }
Benny Prijono2cd64f82009-02-17 19:57:48 +0000557
Benny Prijono10454dc2009-02-21 14:21:59 +0000558 f_id = GET_FID(aud_subsys.dev_list[id]);
559 index = GET_INDEX(aud_subsys.dev_list[id]);
Benny Prijono2cd64f82009-02-17 19:57:48 +0000560
Benny Prijono10454dc2009-02-21 14:21:59 +0000561 if (f_id < 0 || f_id >= (int)aud_subsys.drv_cnt)
562 return PJMEDIA_EAUD_INVDEV;
563
564 if (index < 0 || index >= (int)aud_subsys.drv[f_id].dev_cnt)
565 return PJMEDIA_EAUD_INVDEV;
566
567 *p_f = aud_subsys.drv[f_id].f;
568 *p_local_index = (unsigned)index;
569
570 return PJ_SUCCESS;
571
Benny Prijono2cd64f82009-02-17 19:57:48 +0000572}
573
Benny Prijono2cd64f82009-02-17 19:57:48 +0000574/* API: Get device information. */
Benny Prijono10454dc2009-02-21 14:21:59 +0000575PJ_DEF(pj_status_t) pjmedia_aud_dev_get_info(pjmedia_aud_dev_index id,
Benny Prijono2cd64f82009-02-17 19:57:48 +0000576 pjmedia_aud_dev_info *info)
577{
Benny Prijonoe3ebd552009-02-18 20:14:15 +0000578 pjmedia_aud_dev_factory *f;
Benny Prijono10454dc2009-02-21 14:21:59 +0000579 unsigned index;
580 pj_status_t status;
Benny Prijono2cd64f82009-02-17 19:57:48 +0000581
Benny Prijono96e74f32009-02-22 12:00:12 +0000582 PJ_ASSERT_RETURN(info && id!=PJMEDIA_AUD_INVALID_DEV, PJ_EINVAL);
583 PJ_ASSERT_RETURN(aud_subsys.pf, PJMEDIA_EAUD_INIT);
Benny Prijono598b01d2009-02-18 13:55:03 +0000584
Benny Prijono10454dc2009-02-21 14:21:59 +0000585 status = lookup_dev(id, &f, &index);
586 if (status != PJ_SUCCESS)
587 return status;
Benny Prijono2cd64f82009-02-17 19:57:48 +0000588
Benny Prijonoe3ebd552009-02-18 20:14:15 +0000589 return f->op->get_dev_info(f, index, info);
Benny Prijono2cd64f82009-02-17 19:57:48 +0000590}
591
Benny Prijono10454dc2009-02-21 14:21:59 +0000592/* API: find device */
593PJ_DEF(pj_status_t) pjmedia_aud_dev_lookup( const char *drv_name,
594 const char *dev_name,
595 pjmedia_aud_dev_index *id)
596{
597 pjmedia_aud_dev_factory *f = NULL;
Benny Prijono96e74f32009-02-22 12:00:12 +0000598 unsigned drv_idx, dev_idx;
Benny Prijono10454dc2009-02-21 14:21:59 +0000599
600 PJ_ASSERT_RETURN(drv_name && dev_name && id, PJ_EINVAL);
Benny Prijono96e74f32009-02-22 12:00:12 +0000601 PJ_ASSERT_RETURN(aud_subsys.pf, PJMEDIA_EAUD_INIT);
Benny Prijono10454dc2009-02-21 14:21:59 +0000602
Benny Prijono96e74f32009-02-22 12:00:12 +0000603 for (drv_idx=0; drv_idx<aud_subsys.drv_cnt; ++drv_idx) {
604 if (!pj_ansi_stricmp(drv_name, aud_subsys.drv[drv_idx].name)) {
605 f = aud_subsys.drv[drv_idx].f;
Benny Prijono10454dc2009-02-21 14:21:59 +0000606 break;
607 }
608 }
609
610 if (!f)
611 return PJ_ENOTFOUND;
612
Benny Prijono96e74f32009-02-22 12:00:12 +0000613 for (dev_idx=0; dev_idx<aud_subsys.drv[drv_idx].dev_cnt; ++dev_idx) {
Benny Prijono10454dc2009-02-21 14:21:59 +0000614 pjmedia_aud_dev_info info;
615 pj_status_t status;
616
Benny Prijono96e74f32009-02-22 12:00:12 +0000617 status = f->op->get_dev_info(f, dev_idx, &info);
Benny Prijono10454dc2009-02-21 14:21:59 +0000618 if (status != PJ_SUCCESS)
619 return status;
620
621 if (!pj_ansi_stricmp(dev_name, info.name))
622 break;
623 }
624
Benny Prijono96e74f32009-02-22 12:00:12 +0000625 if (dev_idx==aud_subsys.drv[drv_idx].dev_cnt)
Benny Prijono10454dc2009-02-21 14:21:59 +0000626 return PJ_ENOTFOUND;
627
Benny Prijono96e74f32009-02-22 12:00:12 +0000628 *id = dev_idx;
629 make_global_index(drv_idx, id);
Benny Prijono10454dc2009-02-21 14:21:59 +0000630
631 return PJ_SUCCESS;
632}
633
Benny Prijono2cd64f82009-02-17 19:57:48 +0000634/* API: Initialize the audio device parameters with default values for the
635 * specified device.
636 */
Benny Prijono10454dc2009-02-21 14:21:59 +0000637PJ_DEF(pj_status_t) pjmedia_aud_dev_default_param(pjmedia_aud_dev_index id,
638 pjmedia_aud_param *param)
Benny Prijono2cd64f82009-02-17 19:57:48 +0000639{
Benny Prijonoe3ebd552009-02-18 20:14:15 +0000640 pjmedia_aud_dev_factory *f;
Benny Prijono10454dc2009-02-21 14:21:59 +0000641 unsigned index;
Benny Prijono2cd64f82009-02-17 19:57:48 +0000642 pj_status_t status;
643
Benny Prijono96e74f32009-02-22 12:00:12 +0000644 PJ_ASSERT_RETURN(param && id!=PJMEDIA_AUD_INVALID_DEV, PJ_EINVAL);
645 PJ_ASSERT_RETURN(aud_subsys.pf, PJMEDIA_EAUD_INIT);
Benny Prijono598b01d2009-02-18 13:55:03 +0000646
Benny Prijono10454dc2009-02-21 14:21:59 +0000647 status = lookup_dev(id, &f, &index);
648 if (status != PJ_SUCCESS)
649 return status;
Benny Prijono2cd64f82009-02-17 19:57:48 +0000650
Benny Prijonoe3ebd552009-02-18 20:14:15 +0000651 status = f->op->default_param(f, index, param);
Benny Prijono2cd64f82009-02-17 19:57:48 +0000652 if (status != PJ_SUCCESS)
653 return status;
654
655 /* Normalize device IDs */
Benny Prijono96e74f32009-02-22 12:00:12 +0000656 make_global_index(f->sys.drv_idx, &param->rec_id);
657 make_global_index(f->sys.drv_idx, &param->play_id);
Benny Prijono2cd64f82009-02-17 19:57:48 +0000658
659 return PJ_SUCCESS;
660}
661
662/* API: Open audio stream object using the specified parameters. */
Benny Prijono10454dc2009-02-21 14:21:59 +0000663PJ_DEF(pj_status_t) pjmedia_aud_stream_create(const pjmedia_aud_param *prm,
Benny Prijono2cd64f82009-02-17 19:57:48 +0000664 pjmedia_aud_rec_cb rec_cb,
665 pjmedia_aud_play_cb play_cb,
666 void *user_data,
667 pjmedia_aud_stream **p_aud_strm)
668{
Benny Prijono10454dc2009-02-21 14:21:59 +0000669 pjmedia_aud_dev_factory *rec_f=NULL, *play_f=NULL, *f=NULL;
670 pjmedia_aud_param param;
Benny Prijono2cd64f82009-02-17 19:57:48 +0000671 pj_status_t status;
672
Benny Prijono10454dc2009-02-21 14:21:59 +0000673 PJ_ASSERT_RETURN(prm && prm->dir && p_aud_strm, PJ_EINVAL);
Benny Prijono96e74f32009-02-22 12:00:12 +0000674 PJ_ASSERT_RETURN(aud_subsys.pf, PJMEDIA_EAUD_INIT);
Benny Prijonod65f78c2009-06-03 18:59:37 +0000675 PJ_ASSERT_RETURN(prm->dir==PJMEDIA_DIR_CAPTURE ||
676 prm->dir==PJMEDIA_DIR_PLAYBACK ||
677 prm->dir==PJMEDIA_DIR_CAPTURE_PLAYBACK,
678 PJ_EINVAL);
Benny Prijono10454dc2009-02-21 14:21:59 +0000679
Benny Prijono2cd64f82009-02-17 19:57:48 +0000680 /* Must make copy of param because we're changing device ID */
Benny Prijono10454dc2009-02-21 14:21:59 +0000681 pj_memcpy(&param, prm, sizeof(param));
Benny Prijono2cd64f82009-02-17 19:57:48 +0000682
Benny Prijono10454dc2009-02-21 14:21:59 +0000683 /* Normalize rec_id */
684 if (param.dir & PJMEDIA_DIR_CAPTURE) {
685 unsigned index;
686
Benny Prijono96e74f32009-02-22 12:00:12 +0000687 if (param.rec_id < 0)
688 param.rec_id = PJMEDIA_AUD_DEFAULT_CAPTURE_DEV;
689
Benny Prijono10454dc2009-02-21 14:21:59 +0000690 status = lookup_dev(param.rec_id, &rec_f, &index);
691 if (status != PJ_SUCCESS)
692 return status;
693
694 param.rec_id = index;
695 f = rec_f;
696 }
697
698 /* Normalize play_id */
699 if (param.dir & PJMEDIA_DIR_PLAYBACK) {
700 unsigned index;
701
Benny Prijono96e74f32009-02-22 12:00:12 +0000702 if (param.play_id < 0)
703 param.play_id = PJMEDIA_AUD_DEFAULT_PLAYBACK_DEV;
704
Benny Prijono10454dc2009-02-21 14:21:59 +0000705 status = lookup_dev(param.play_id, &play_f, &index);
706 if (status != PJ_SUCCESS)
707 return status;
708
709 param.play_id = index;
710 f = play_f;
Benny Prijono10454dc2009-02-21 14:21:59 +0000711 }
712
Benny Prijonod65f78c2009-06-03 18:59:37 +0000713 PJ_ASSERT_RETURN(f != NULL, PJ_EBUG);
714
Nanang Izzuddin6f7d5a12009-03-16 16:29:39 +0000715 /* For now, rec_id and play_id must belong to the same factory */
716 PJ_ASSERT_RETURN((param.dir != PJMEDIA_DIR_CAPTURE_PLAYBACK) ||
717 (rec_f == play_f),
718 PJMEDIA_EAUD_INVDEV);
719
Benny Prijono10454dc2009-02-21 14:21:59 +0000720 /* Create the stream */
Benny Prijonoe3ebd552009-02-18 20:14:15 +0000721 status = f->op->create_stream(f, &param, rec_cb, play_cb,
722 user_data, p_aud_strm);
Benny Prijono2cd64f82009-02-17 19:57:48 +0000723 if (status != PJ_SUCCESS)
724 return status;
725
Benny Prijono10454dc2009-02-21 14:21:59 +0000726 /* Assign factory id to the stream */
Benny Prijono96e74f32009-02-22 12:00:12 +0000727 (*p_aud_strm)->sys.drv_idx = f->sys.drv_idx;
Benny Prijono2cd64f82009-02-17 19:57:48 +0000728 return PJ_SUCCESS;
729}
730
731/* API: Get the running parameters for the specified audio stream. */
732PJ_DEF(pj_status_t) pjmedia_aud_stream_get_param(pjmedia_aud_stream *strm,
Benny Prijono10454dc2009-02-21 14:21:59 +0000733 pjmedia_aud_param *param)
Benny Prijono2cd64f82009-02-17 19:57:48 +0000734{
735 pj_status_t status;
736
Benny Prijono96e74f32009-02-22 12:00:12 +0000737 PJ_ASSERT_RETURN(strm && param, PJ_EINVAL);
738 PJ_ASSERT_RETURN(aud_subsys.pf, PJMEDIA_EAUD_INIT);
739
Benny Prijono2cd64f82009-02-17 19:57:48 +0000740 status = strm->op->get_param(strm, param);
741 if (status != PJ_SUCCESS)
742 return status;
743
744 /* Normalize device id's */
Benny Prijono96e74f32009-02-22 12:00:12 +0000745 make_global_index(strm->sys.drv_idx, &param->rec_id);
746 make_global_index(strm->sys.drv_idx, &param->play_id);
Benny Prijono2cd64f82009-02-17 19:57:48 +0000747
748 return PJ_SUCCESS;
749}
750
751/* API: Get the value of a specific capability of the audio stream. */
752PJ_DEF(pj_status_t) pjmedia_aud_stream_get_cap(pjmedia_aud_stream *strm,
753 pjmedia_aud_dev_cap cap,
754 void *value)
755{
756 return strm->op->get_cap(strm, cap, value);
757}
758
759/* API: Set the value of a specific capability of the audio stream. */
760PJ_DEF(pj_status_t) pjmedia_aud_stream_set_cap(pjmedia_aud_stream *strm,
761 pjmedia_aud_dev_cap cap,
762 const void *value)
763{
764 return strm->op->set_cap(strm, cap, value);
765}
766
767/* API: Start the stream. */
768PJ_DEF(pj_status_t) pjmedia_aud_stream_start(pjmedia_aud_stream *strm)
769{
770 return strm->op->start(strm);
771}
772
773/* API: Stop the stream. */
774PJ_DEF(pj_status_t) pjmedia_aud_stream_stop(pjmedia_aud_stream *strm)
775{
776 return strm->op->stop(strm);
777}
778
779/* API: Destroy the stream. */
780PJ_DEF(pj_status_t) pjmedia_aud_stream_destroy(pjmedia_aud_stream *strm)
781{
782 return strm->op->destroy(strm);
783}
784
785