blob: 8025e04d5aefb2dee41866b7dbcc4b624002bcc6 [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 }
Benny Prijonof87f67c2009-04-06 10:02:53 +0000264
265 /* enabling this will cause pjsua-lib initialization to fail when there
266 * is no sound device installed in the system, even when pjsua has been
267 * run with --null-audio
268 *
Benny Prijono96e74f32009-02-22 12:00:12 +0000269 if (dev_cnt == 0) {
270 f->op->destroy(f);
271 return PJMEDIA_EAUD_NODEV;
272 }
Benny Prijonof87f67c2009-04-06 10:02:53 +0000273 */
Benny Prijono96e74f32009-02-22 12:00:12 +0000274
275 /* Fill in default devices */
276 drv->play_dev_idx = drv->rec_dev_idx = drv->dev_idx = -1;
277 for (i=0; i<dev_cnt; ++i) {
278 pjmedia_aud_dev_info info;
279
280 status = f->op->get_dev_info(f, i, &info);
281 if (status != PJ_SUCCESS) {
282 f->op->destroy(f);
283 return status;
284 }
285
286 if (drv->name[0]=='\0') {
287 /* Set driver name */
288 pj_ansi_strncpy(drv->name, info.driver, sizeof(drv->name));
289 drv->name[sizeof(drv->name)-1] = '\0';
290 }
291
292 if (drv->play_dev_idx < 0 && info.output_count) {
293 /* Set default playback device */
294 drv->play_dev_idx = i;
295 }
296 if (drv->rec_dev_idx < 0 && info.input_count) {
297 /* Set default capture device */
298 drv->rec_dev_idx = i;
299 }
300 if (drv->dev_idx < 0 && info.input_count &&
301 info.output_count)
302 {
303 /* Set default capture and playback device */
304 drv->dev_idx = i;
305 }
306
307 if (drv->play_dev_idx >= 0 && drv->rec_dev_idx >= 0 &&
308 drv->dev_idx >= 0)
309 {
310 /* Done. */
311 break;
312 }
313 }
314
315 /* Register the factory */
316 drv->f = f;
317 drv->f->sys.drv_idx = drv_idx;
318 drv->start_idx = aud_subsys.dev_cnt;
319 drv->dev_cnt = dev_cnt;
320
321 /* Register devices to global list */
322 for (i=0; i<dev_cnt; ++i) {
323 aud_subsys.dev_list[aud_subsys.dev_cnt++] = MAKE_DEV_ID(drv_idx, i);
324 }
325
326 return PJ_SUCCESS;
327}
328
329/* Internal: deinit driver */
330static void deinit_driver(unsigned drv_idx)
331{
332 struct driver *drv = &aud_subsys.drv[drv_idx];
333
334 if (drv->f) {
335 drv->f->op->destroy(drv->f);
336 drv->f = NULL;
337 }
338
339 drv->dev_cnt = 0;
340 drv->play_dev_idx = drv->rec_dev_idx = drv->dev_idx = -1;
341}
Benny Prijono2cd64f82009-02-17 19:57:48 +0000342
343/* API: Initialize the audio subsystem. */
344PJ_DEF(pj_status_t) pjmedia_aud_subsys_init(pj_pool_factory *pf)
345{
346 unsigned i;
Benny Prijonodebe8e12009-06-03 12:29:35 +0000347 pj_status_t status = PJ_SUCCESS;
Benny Prijono2cd64f82009-02-17 19:57:48 +0000348
Benny Prijono555139d2009-02-19 12:08:19 +0000349 /* Allow init() to be called multiple times as long as there is matching
350 * number of shutdown().
351 */
352 if (aud_subsys.init_count++ != 0) {
353 return PJ_SUCCESS;
354 }
355
Benny Prijono8eeab0b2009-03-04 19:00:28 +0000356 /* Register error subsystem */
357 pj_register_strerror(PJMEDIA_AUDIODEV_ERRNO_START,
358 PJ_ERRNO_SPACE_SIZE,
359 &pjmedia_audiodev_strerror);
360
361 /* Init */
Benny Prijonoe3ebd552009-02-18 20:14:15 +0000362 aud_subsys.pf = pf;
Benny Prijono10454dc2009-02-21 14:21:59 +0000363 aud_subsys.drv_cnt = 0;
364 aud_subsys.dev_cnt = 0;
Benny Prijono2cd64f82009-02-17 19:57:48 +0000365
Benny Prijono10454dc2009-02-21 14:21:59 +0000366 /* Register creation functions */
Benny Prijono2058f472009-02-22 17:15:34 +0000367#if PJMEDIA_AUDIO_DEV_HAS_PORTAUDIO
Benny Prijono10454dc2009-02-21 14:21:59 +0000368 aud_subsys.drv[aud_subsys.drv_cnt++].create = &pjmedia_pa_factory;
Benny Prijono2058f472009-02-22 17:15:34 +0000369#endif
370#if PJMEDIA_AUDIO_DEV_HAS_WMME
Benny Prijono10454dc2009-02-21 14:21:59 +0000371 aud_subsys.drv[aud_subsys.drv_cnt++].create = &pjmedia_wmme_factory;
Benny Prijono2058f472009-02-22 17:15:34 +0000372#endif
Nanang Izzuddina940b362009-02-23 13:53:30 +0000373#if PJMEDIA_AUDIO_DEV_HAS_SYMB_APS
374 aud_subsys.drv[aud_subsys.drv_cnt++].create = &pjmedia_aps_factory;
375#endif
376#if PJMEDIA_AUDIO_DEV_HAS_SYMB_MDA
377 aud_subsys.drv[aud_subsys.drv_cnt++].create = &pjmedia_symb_mda_factory;
378#endif
Benny Prijonoe3ebd552009-02-18 20:14:15 +0000379
Benny Prijono10454dc2009-02-21 14:21:59 +0000380 /* Initialize each factory and build the device ID list */
381 for (i=0; i<aud_subsys.drv_cnt; ++i) {
Benny Prijono96e74f32009-02-22 12:00:12 +0000382 status = init_driver(i);
Benny Prijono2cd64f82009-02-17 19:57:48 +0000383 if (status != PJ_SUCCESS) {
Benny Prijono96e74f32009-02-22 12:00:12 +0000384 deinit_driver(i);
Benny Prijonoe3ebd552009-02-18 20:14:15 +0000385 continue;
Benny Prijono2cd64f82009-02-17 19:57:48 +0000386 }
Benny Prijono2cd64f82009-02-17 19:57:48 +0000387 }
388
Benny Prijono96e74f32009-02-22 12:00:12 +0000389 return aud_subsys.dev_cnt ? PJ_SUCCESS : status;
Benny Prijonoe3ebd552009-02-18 20:14:15 +0000390}
391
392/* API: get the pool factory registered to the audio subsystem. */
393PJ_DEF(pj_pool_factory*) pjmedia_aud_subsys_get_pool_factory(void)
394{
395 return aud_subsys.pf;
Benny Prijono2cd64f82009-02-17 19:57:48 +0000396}
397
398/* API: Shutdown the audio subsystem. */
399PJ_DEF(pj_status_t) pjmedia_aud_subsys_shutdown(void)
400{
401 unsigned i;
402
Benny Prijono555139d2009-02-19 12:08:19 +0000403 /* Allow shutdown() to be called multiple times as long as there is matching
404 * number of init().
405 */
406 if (aud_subsys.init_count == 0) {
407 return PJ_SUCCESS;
408 }
409 --aud_subsys.init_count;
410
Benny Prijono10454dc2009-02-21 14:21:59 +0000411 for (i=0; i<aud_subsys.drv_cnt; ++i) {
Benny Prijono96e74f32009-02-22 12:00:12 +0000412 deinit_driver(i);
Benny Prijono2cd64f82009-02-17 19:57:48 +0000413 }
414
Benny Prijono96e74f32009-02-22 12:00:12 +0000415 aud_subsys.pf = NULL;
Benny Prijono2cd64f82009-02-17 19:57:48 +0000416 return PJ_SUCCESS;
417}
418
419/* API: Get the number of sound devices installed in the system. */
420PJ_DEF(unsigned) pjmedia_aud_dev_count(void)
421{
Benny Prijono10454dc2009-02-21 14:21:59 +0000422 return aud_subsys.dev_cnt;
Benny Prijono2cd64f82009-02-17 19:57:48 +0000423}
424
Benny Prijono96e74f32009-02-22 12:00:12 +0000425/* Internal: convert local index to global device index */
426static pj_status_t make_global_index(unsigned drv_idx,
427 pjmedia_aud_dev_index *id)
428{
429 if (*id < 0) {
430 return PJ_SUCCESS;
431 }
432
433 /* Check that factory still exists */
434 PJ_ASSERT_RETURN(aud_subsys.drv[drv_idx].f, PJ_EBUG);
435
436 /* Check that device index is valid */
437 PJ_ASSERT_RETURN(*id>=0 && *id<(int)aud_subsys.drv[drv_idx].dev_cnt,
438 PJ_EBUG);
439
440 *id += aud_subsys.drv[drv_idx].start_idx;
441 return PJ_SUCCESS;
442}
443
Benny Prijono10454dc2009-02-21 14:21:59 +0000444/* Internal: lookup device id */
445static pj_status_t lookup_dev(pjmedia_aud_dev_index id,
446 pjmedia_aud_dev_factory **p_f,
447 unsigned *p_local_index)
Benny Prijono2cd64f82009-02-17 19:57:48 +0000448{
Benny Prijono10454dc2009-02-21 14:21:59 +0000449 int f_id, index;
Benny Prijono2cd64f82009-02-17 19:57:48 +0000450
Benny Prijono96e74f32009-02-22 12:00:12 +0000451 if (id < 0) {
452 unsigned i;
Benny Prijono2cd64f82009-02-17 19:57:48 +0000453
Benny Prijono96e74f32009-02-22 12:00:12 +0000454 if (id == PJMEDIA_AUD_INVALID_DEV)
455 return PJMEDIA_EAUD_INVDEV;
456
457 for (i=0; i<aud_subsys.drv_cnt; ++i) {
458 struct driver *drv = &aud_subsys.drv[i];
459 if (drv->dev_idx >= 0) {
460 id = drv->dev_idx;
461 make_global_index(i, &id);
462 break;
463 } else if (id==PJMEDIA_AUD_DEFAULT_CAPTURE_DEV &&
464 drv->rec_dev_idx >= 0)
465 {
466 id = drv->rec_dev_idx;
467 make_global_index(i, &id);
468 break;
469 } else if (id==PJMEDIA_AUD_DEFAULT_PLAYBACK_DEV &&
470 drv->play_dev_idx >= 0)
471 {
472 id = drv->play_dev_idx;
473 make_global_index(i, &id);
474 break;
475 }
476 }
477
478 if (id < 0) {
479 return PJMEDIA_EAUD_NODEFDEV;
480 }
481 }
Benny Prijono2cd64f82009-02-17 19:57:48 +0000482
Benny Prijono10454dc2009-02-21 14:21:59 +0000483 f_id = GET_FID(aud_subsys.dev_list[id]);
484 index = GET_INDEX(aud_subsys.dev_list[id]);
Benny Prijono2cd64f82009-02-17 19:57:48 +0000485
Benny Prijono10454dc2009-02-21 14:21:59 +0000486 if (f_id < 0 || f_id >= (int)aud_subsys.drv_cnt)
487 return PJMEDIA_EAUD_INVDEV;
488
489 if (index < 0 || index >= (int)aud_subsys.drv[f_id].dev_cnt)
490 return PJMEDIA_EAUD_INVDEV;
491
492 *p_f = aud_subsys.drv[f_id].f;
493 *p_local_index = (unsigned)index;
494
495 return PJ_SUCCESS;
496
Benny Prijono2cd64f82009-02-17 19:57:48 +0000497}
498
Benny Prijono2cd64f82009-02-17 19:57:48 +0000499/* API: Get device information. */
Benny Prijono10454dc2009-02-21 14:21:59 +0000500PJ_DEF(pj_status_t) pjmedia_aud_dev_get_info(pjmedia_aud_dev_index id,
Benny Prijono2cd64f82009-02-17 19:57:48 +0000501 pjmedia_aud_dev_info *info)
502{
Benny Prijonoe3ebd552009-02-18 20:14:15 +0000503 pjmedia_aud_dev_factory *f;
Benny Prijono10454dc2009-02-21 14:21:59 +0000504 unsigned index;
505 pj_status_t status;
Benny Prijono2cd64f82009-02-17 19:57:48 +0000506
Benny Prijono96e74f32009-02-22 12:00:12 +0000507 PJ_ASSERT_RETURN(info && id!=PJMEDIA_AUD_INVALID_DEV, PJ_EINVAL);
508 PJ_ASSERT_RETURN(aud_subsys.pf, PJMEDIA_EAUD_INIT);
Benny Prijono598b01d2009-02-18 13:55:03 +0000509
Benny Prijono10454dc2009-02-21 14:21:59 +0000510 status = lookup_dev(id, &f, &index);
511 if (status != PJ_SUCCESS)
512 return status;
Benny Prijono2cd64f82009-02-17 19:57:48 +0000513
Benny Prijonoe3ebd552009-02-18 20:14:15 +0000514 return f->op->get_dev_info(f, index, info);
Benny Prijono2cd64f82009-02-17 19:57:48 +0000515}
516
Benny Prijono10454dc2009-02-21 14:21:59 +0000517/* API: find device */
518PJ_DEF(pj_status_t) pjmedia_aud_dev_lookup( const char *drv_name,
519 const char *dev_name,
520 pjmedia_aud_dev_index *id)
521{
522 pjmedia_aud_dev_factory *f = NULL;
Benny Prijono96e74f32009-02-22 12:00:12 +0000523 unsigned drv_idx, dev_idx;
Benny Prijono10454dc2009-02-21 14:21:59 +0000524
525 PJ_ASSERT_RETURN(drv_name && dev_name && id, PJ_EINVAL);
Benny Prijono96e74f32009-02-22 12:00:12 +0000526 PJ_ASSERT_RETURN(aud_subsys.pf, PJMEDIA_EAUD_INIT);
Benny Prijono10454dc2009-02-21 14:21:59 +0000527
Benny Prijono96e74f32009-02-22 12:00:12 +0000528 for (drv_idx=0; drv_idx<aud_subsys.drv_cnt; ++drv_idx) {
529 if (!pj_ansi_stricmp(drv_name, aud_subsys.drv[drv_idx].name)) {
530 f = aud_subsys.drv[drv_idx].f;
Benny Prijono10454dc2009-02-21 14:21:59 +0000531 break;
532 }
533 }
534
535 if (!f)
536 return PJ_ENOTFOUND;
537
Benny Prijono96e74f32009-02-22 12:00:12 +0000538 for (dev_idx=0; dev_idx<aud_subsys.drv[drv_idx].dev_cnt; ++dev_idx) {
Benny Prijono10454dc2009-02-21 14:21:59 +0000539 pjmedia_aud_dev_info info;
540 pj_status_t status;
541
Benny Prijono96e74f32009-02-22 12:00:12 +0000542 status = f->op->get_dev_info(f, dev_idx, &info);
Benny Prijono10454dc2009-02-21 14:21:59 +0000543 if (status != PJ_SUCCESS)
544 return status;
545
546 if (!pj_ansi_stricmp(dev_name, info.name))
547 break;
548 }
549
Benny Prijono96e74f32009-02-22 12:00:12 +0000550 if (dev_idx==aud_subsys.drv[drv_idx].dev_cnt)
Benny Prijono10454dc2009-02-21 14:21:59 +0000551 return PJ_ENOTFOUND;
552
Benny Prijono96e74f32009-02-22 12:00:12 +0000553 *id = dev_idx;
554 make_global_index(drv_idx, id);
Benny Prijono10454dc2009-02-21 14:21:59 +0000555
556 return PJ_SUCCESS;
557}
558
Benny Prijono2cd64f82009-02-17 19:57:48 +0000559/* API: Initialize the audio device parameters with default values for the
560 * specified device.
561 */
Benny Prijono10454dc2009-02-21 14:21:59 +0000562PJ_DEF(pj_status_t) pjmedia_aud_dev_default_param(pjmedia_aud_dev_index id,
563 pjmedia_aud_param *param)
Benny Prijono2cd64f82009-02-17 19:57:48 +0000564{
Benny Prijonoe3ebd552009-02-18 20:14:15 +0000565 pjmedia_aud_dev_factory *f;
Benny Prijono10454dc2009-02-21 14:21:59 +0000566 unsigned index;
Benny Prijono2cd64f82009-02-17 19:57:48 +0000567 pj_status_t status;
568
Benny Prijono96e74f32009-02-22 12:00:12 +0000569 PJ_ASSERT_RETURN(param && id!=PJMEDIA_AUD_INVALID_DEV, PJ_EINVAL);
570 PJ_ASSERT_RETURN(aud_subsys.pf, PJMEDIA_EAUD_INIT);
Benny Prijono598b01d2009-02-18 13:55:03 +0000571
Benny Prijono10454dc2009-02-21 14:21:59 +0000572 status = lookup_dev(id, &f, &index);
573 if (status != PJ_SUCCESS)
574 return status;
Benny Prijono2cd64f82009-02-17 19:57:48 +0000575
Benny Prijonoe3ebd552009-02-18 20:14:15 +0000576 status = f->op->default_param(f, index, param);
Benny Prijono2cd64f82009-02-17 19:57:48 +0000577 if (status != PJ_SUCCESS)
578 return status;
579
580 /* Normalize device IDs */
Benny Prijono96e74f32009-02-22 12:00:12 +0000581 make_global_index(f->sys.drv_idx, &param->rec_id);
582 make_global_index(f->sys.drv_idx, &param->play_id);
Benny Prijono2cd64f82009-02-17 19:57:48 +0000583
584 return PJ_SUCCESS;
585}
586
587/* API: Open audio stream object using the specified parameters. */
Benny Prijono10454dc2009-02-21 14:21:59 +0000588PJ_DEF(pj_status_t) pjmedia_aud_stream_create(const pjmedia_aud_param *prm,
Benny Prijono2cd64f82009-02-17 19:57:48 +0000589 pjmedia_aud_rec_cb rec_cb,
590 pjmedia_aud_play_cb play_cb,
591 void *user_data,
592 pjmedia_aud_stream **p_aud_strm)
593{
Benny Prijono10454dc2009-02-21 14:21:59 +0000594 pjmedia_aud_dev_factory *rec_f=NULL, *play_f=NULL, *f=NULL;
595 pjmedia_aud_param param;
Benny Prijono2cd64f82009-02-17 19:57:48 +0000596 pj_status_t status;
597
Benny Prijono10454dc2009-02-21 14:21:59 +0000598 PJ_ASSERT_RETURN(prm && prm->dir && p_aud_strm, PJ_EINVAL);
Benny Prijono96e74f32009-02-22 12:00:12 +0000599 PJ_ASSERT_RETURN(aud_subsys.pf, PJMEDIA_EAUD_INIT);
Benny Prijonod65f78c2009-06-03 18:59:37 +0000600 PJ_ASSERT_RETURN(prm->dir==PJMEDIA_DIR_CAPTURE ||
601 prm->dir==PJMEDIA_DIR_PLAYBACK ||
602 prm->dir==PJMEDIA_DIR_CAPTURE_PLAYBACK,
603 PJ_EINVAL);
Benny Prijono10454dc2009-02-21 14:21:59 +0000604
Benny Prijono2cd64f82009-02-17 19:57:48 +0000605 /* Must make copy of param because we're changing device ID */
Benny Prijono10454dc2009-02-21 14:21:59 +0000606 pj_memcpy(&param, prm, sizeof(param));
Benny Prijono2cd64f82009-02-17 19:57:48 +0000607
Benny Prijono10454dc2009-02-21 14:21:59 +0000608 /* Normalize rec_id */
609 if (param.dir & PJMEDIA_DIR_CAPTURE) {
610 unsigned index;
611
Benny Prijono96e74f32009-02-22 12:00:12 +0000612 if (param.rec_id < 0)
613 param.rec_id = PJMEDIA_AUD_DEFAULT_CAPTURE_DEV;
614
Benny Prijono10454dc2009-02-21 14:21:59 +0000615 status = lookup_dev(param.rec_id, &rec_f, &index);
616 if (status != PJ_SUCCESS)
617 return status;
618
619 param.rec_id = index;
620 f = rec_f;
621 }
622
623 /* Normalize play_id */
624 if (param.dir & PJMEDIA_DIR_PLAYBACK) {
625 unsigned index;
626
Benny Prijono96e74f32009-02-22 12:00:12 +0000627 if (param.play_id < 0)
628 param.play_id = PJMEDIA_AUD_DEFAULT_PLAYBACK_DEV;
629
Benny Prijono10454dc2009-02-21 14:21:59 +0000630 status = lookup_dev(param.play_id, &play_f, &index);
631 if (status != PJ_SUCCESS)
632 return status;
633
634 param.play_id = index;
635 f = play_f;
Benny Prijono10454dc2009-02-21 14:21:59 +0000636 }
637
Benny Prijonod65f78c2009-06-03 18:59:37 +0000638 PJ_ASSERT_RETURN(f != NULL, PJ_EBUG);
639
Nanang Izzuddin6f7d5a12009-03-16 16:29:39 +0000640 /* For now, rec_id and play_id must belong to the same factory */
641 PJ_ASSERT_RETURN((param.dir != PJMEDIA_DIR_CAPTURE_PLAYBACK) ||
642 (rec_f == play_f),
643 PJMEDIA_EAUD_INVDEV);
644
Benny Prijono10454dc2009-02-21 14:21:59 +0000645 /* Create the stream */
Benny Prijonoe3ebd552009-02-18 20:14:15 +0000646 status = f->op->create_stream(f, &param, rec_cb, play_cb,
647 user_data, p_aud_strm);
Benny Prijono2cd64f82009-02-17 19:57:48 +0000648 if (status != PJ_SUCCESS)
649 return status;
650
Benny Prijono10454dc2009-02-21 14:21:59 +0000651 /* Assign factory id to the stream */
Benny Prijono96e74f32009-02-22 12:00:12 +0000652 (*p_aud_strm)->sys.drv_idx = f->sys.drv_idx;
Benny Prijono2cd64f82009-02-17 19:57:48 +0000653 return PJ_SUCCESS;
654}
655
656/* API: Get the running parameters for the specified audio stream. */
657PJ_DEF(pj_status_t) pjmedia_aud_stream_get_param(pjmedia_aud_stream *strm,
Benny Prijono10454dc2009-02-21 14:21:59 +0000658 pjmedia_aud_param *param)
Benny Prijono2cd64f82009-02-17 19:57:48 +0000659{
660 pj_status_t status;
661
Benny Prijono96e74f32009-02-22 12:00:12 +0000662 PJ_ASSERT_RETURN(strm && param, PJ_EINVAL);
663 PJ_ASSERT_RETURN(aud_subsys.pf, PJMEDIA_EAUD_INIT);
664
Benny Prijono2cd64f82009-02-17 19:57:48 +0000665 status = strm->op->get_param(strm, param);
666 if (status != PJ_SUCCESS)
667 return status;
668
669 /* Normalize device id's */
Benny Prijono96e74f32009-02-22 12:00:12 +0000670 make_global_index(strm->sys.drv_idx, &param->rec_id);
671 make_global_index(strm->sys.drv_idx, &param->play_id);
Benny Prijono2cd64f82009-02-17 19:57:48 +0000672
673 return PJ_SUCCESS;
674}
675
676/* API: Get the value of a specific capability of the audio stream. */
677PJ_DEF(pj_status_t) pjmedia_aud_stream_get_cap(pjmedia_aud_stream *strm,
678 pjmedia_aud_dev_cap cap,
679 void *value)
680{
681 return strm->op->get_cap(strm, cap, value);
682}
683
684/* API: Set the value of a specific capability of the audio stream. */
685PJ_DEF(pj_status_t) pjmedia_aud_stream_set_cap(pjmedia_aud_stream *strm,
686 pjmedia_aud_dev_cap cap,
687 const void *value)
688{
689 return strm->op->set_cap(strm, cap, value);
690}
691
692/* API: Start the stream. */
693PJ_DEF(pj_status_t) pjmedia_aud_stream_start(pjmedia_aud_stream *strm)
694{
695 return strm->op->start(strm);
696}
697
698/* API: Stop the stream. */
699PJ_DEF(pj_status_t) pjmedia_aud_stream_stop(pjmedia_aud_stream *strm)
700{
701 return strm->op->stop(strm);
702}
703
704/* API: Destroy the stream. */
705PJ_DEF(pj_status_t) pjmedia_aud_stream_destroy(pjmedia_aud_stream *strm)
706{
707 return strm->op->destroy(strm);
708}
709
710