blob: 38c3b4ca13fba3239a4443c16a2086f4e40255e8 [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
Benny Prijono19a87c72010-01-27 17:22:17 +000073#if PJMEDIA_AUDIO_DEV_HAS_ALSA
74pjmedia_aud_dev_factory* pjmedia_alsa_factory(pj_pool_factory *pf);
75#endif
76
Benny Prijono2058f472009-02-22 17:15:34 +000077#if PJMEDIA_AUDIO_DEV_HAS_WMME
Benny Prijono598b01d2009-02-18 13:55:03 +000078pjmedia_aud_dev_factory* pjmedia_wmme_factory(pj_pool_factory *pf);
Benny Prijono2058f472009-02-22 17:15:34 +000079#endif
80
Nanang Izzuddind687a502009-06-30 13:37:26 +000081#if PJMEDIA_AUDIO_DEV_HAS_SYMB_VAS
82pjmedia_aud_dev_factory* pjmedia_symb_vas_factory(pj_pool_factory *pf);
83#endif
84
Nanang Izzuddina940b362009-02-23 13:53:30 +000085#if PJMEDIA_AUDIO_DEV_HAS_SYMB_APS
86pjmedia_aud_dev_factory* pjmedia_aps_factory(pj_pool_factory *pf);
87#endif
88
89#if PJMEDIA_AUDIO_DEV_HAS_SYMB_MDA
90pjmedia_aud_dev_factory* pjmedia_symb_mda_factory(pj_pool_factory *pf);
91#endif
Benny Prijono598b01d2009-02-18 13:55:03 +000092
Sauw Ming60a0c9b2010-02-19 09:57:48 +000093#if PJMEDIA_AUDIO_DEV_HAS_NULL_AUDIO
94pjmedia_aud_dev_factory* pjmedia_null_audio_factory(pj_pool_factory *pf);
95#endif
96
Benny Prijonoe3ebd552009-02-18 20:14:15 +000097#define MAX_DRIVERS 16
Benny Prijono10454dc2009-02-21 14:21:59 +000098#define MAX_DEVS 64
99
100/* typedef for factory creation function */
101typedef pjmedia_aud_dev_factory* (*create_func_ptr)(pj_pool_factory*);
102
103/* driver structure */
104struct driver
105{
106 create_func_ptr create; /* Creation function. */
107 pjmedia_aud_dev_factory *f; /* Factory instance. */
108 char name[32]; /* Driver name */
109 unsigned dev_cnt; /* Number of devices */
110 unsigned start_idx; /* Start index in global list */
Benny Prijono96e74f32009-02-22 12:00:12 +0000111 int rec_dev_idx;/* Default capture device. */
112 int play_dev_idx;/* Default playback device */
113 int dev_idx; /* Default device. */
Benny Prijono10454dc2009-02-21 14:21:59 +0000114};
Benny Prijono2cd64f82009-02-17 19:57:48 +0000115
Benny Prijonoe3ebd552009-02-18 20:14:15 +0000116/* The audio subsystem */
117static struct aud_subsys
Benny Prijono2cd64f82009-02-17 19:57:48 +0000118{
Benny Prijono10454dc2009-02-21 14:21:59 +0000119 unsigned init_count; /* How many times init() is called */
120 pj_pool_factory *pf; /* The pool factory. */
Benny Prijono2cd64f82009-02-17 19:57:48 +0000121
Benny Prijono10454dc2009-02-21 14:21:59 +0000122 unsigned drv_cnt; /* Number of drivers. */
123 struct driver drv[MAX_DRIVERS]; /* Array of drivers. */
Benny Prijonoe3ebd552009-02-18 20:14:15 +0000124
Benny Prijono10454dc2009-02-21 14:21:59 +0000125 unsigned dev_cnt; /* Total number of devices. */
126 pj_uint32_t dev_list[MAX_DEVS];/* Array of device IDs. */
Benny Prijonoe3ebd552009-02-18 20:14:15 +0000127
128} aud_subsys;
129
Benny Prijono7a380002009-03-09 12:55:29 +0000130/* API: get capability name/info */
131PJ_DEF(const char*) pjmedia_aud_dev_cap_name(pjmedia_aud_dev_cap cap,
132 const char **p_desc)
133{
134 const char *desc;
135 unsigned i;
136
137 if (p_desc==NULL) p_desc = &desc;
138
139 for (i=0; i<PJ_ARRAY_SIZE(cap_infos); ++i) {
140 if ((1 << i)==cap)
141 break;
142 }
143
144 if (i==32) {
145 *p_desc = "??";
146 return "??";
147 }
148
149 *p_desc = cap_infos[i].info;
150 return cap_infos[i].name;
151}
152
153static pj_status_t get_cap_pointer(const pjmedia_aud_param *param,
154 pjmedia_aud_dev_cap cap,
155 void **ptr,
156 unsigned *size)
157{
158#define FIELD_INFO(name) *ptr = (void*)&param->name; \
159 *size = sizeof(param->name)
160
161 switch (cap) {
162 case PJMEDIA_AUD_DEV_CAP_EXT_FORMAT:
163 FIELD_INFO(ext_fmt);
164 break;
165 case PJMEDIA_AUD_DEV_CAP_INPUT_LATENCY:
166 FIELD_INFO(input_latency_ms);
167 break;
168 case PJMEDIA_AUD_DEV_CAP_OUTPUT_LATENCY:
169 FIELD_INFO(output_latency_ms);
170 break;
171 case PJMEDIA_AUD_DEV_CAP_INPUT_VOLUME_SETTING:
172 FIELD_INFO(input_vol);
173 break;
174 case PJMEDIA_AUD_DEV_CAP_OUTPUT_VOLUME_SETTING:
175 FIELD_INFO(output_vol);
176 break;
177 case PJMEDIA_AUD_DEV_CAP_INPUT_ROUTE:
178 FIELD_INFO(input_route);
179 break;
180 case PJMEDIA_AUD_DEV_CAP_OUTPUT_ROUTE:
181 FIELD_INFO(output_route);
182 break;
183 case PJMEDIA_AUD_DEV_CAP_EC:
184 FIELD_INFO(ec_enabled);
185 break;
186 case PJMEDIA_AUD_DEV_CAP_EC_TAIL:
187 FIELD_INFO(ec_tail_ms);
188 break;
189 case PJMEDIA_AUD_DEV_CAP_VAD:
190 FIELD_INFO(ext_fmt.vad);
191 break;
192 case PJMEDIA_AUD_DEV_CAP_CNG:
193 FIELD_INFO(cng_enabled);
194 break;
195 case PJMEDIA_AUD_DEV_CAP_PLC:
196 FIELD_INFO(plc_enabled);
197 break;
198 default:
199 return PJMEDIA_EAUD_INVCAP;
200 }
201
202#undef FIELD_INFO
203
204 return PJ_SUCCESS;
205}
206
207/* API: set cap value to param */
208PJ_DEF(pj_status_t) pjmedia_aud_param_set_cap( pjmedia_aud_param *param,
209 pjmedia_aud_dev_cap cap,
210 const void *pval)
211{
212 void *cap_ptr;
213 unsigned cap_size;
214 pj_status_t status;
215
216 status = get_cap_pointer(param, cap, &cap_ptr, &cap_size);
217 if (status != PJ_SUCCESS)
218 return status;
219
220 pj_memcpy(cap_ptr, pval, cap_size);
221 param->flags |= cap;
222
223 return PJ_SUCCESS;
224}
225
226/* API: get cap value from param */
227PJ_DEF(pj_status_t) pjmedia_aud_param_get_cap( const pjmedia_aud_param *param,
228 pjmedia_aud_dev_cap cap,
229 void *pval)
230{
231 void *cap_ptr;
232 unsigned cap_size;
233 pj_status_t status;
234
235 status = get_cap_pointer(param, cap, &cap_ptr, &cap_size);
236 if (status != PJ_SUCCESS)
237 return status;
238
239 if ((param->flags & cap) == 0) {
240 pj_bzero(cap_ptr, cap_size);
241 return PJMEDIA_EAUD_INVCAP;
242 }
243
244 pj_memcpy(pval, cap_ptr, cap_size);
245 return PJ_SUCCESS;
246}
Benny Prijono2cd64f82009-02-17 19:57:48 +0000247
Benny Prijono96e74f32009-02-22 12:00:12 +0000248/* Internal: init driver */
249static pj_status_t init_driver(unsigned drv_idx)
250{
251 struct driver *drv = &aud_subsys.drv[drv_idx];
252 pjmedia_aud_dev_factory *f;
253 unsigned i, dev_cnt;
254 pj_status_t status;
255
256 /* Create the factory */
257 f = (*drv->create)(aud_subsys.pf);
258 if (!f)
259 return PJ_EUNKNOWN;
260
261 /* Call factory->init() */
262 status = f->op->init(f);
263 if (status != PJ_SUCCESS) {
264 f->op->destroy(f);
265 return status;
266 }
267
268 /* Get number of devices */
269 dev_cnt = f->op->get_dev_count(f);
270 if (dev_cnt + aud_subsys.dev_cnt > MAX_DEVS) {
271 PJ_LOG(4,(THIS_FILE, "%d device(s) cannot be registered because"
272 " there are too many devices",
273 aud_subsys.dev_cnt + dev_cnt - MAX_DEVS));
274 dev_cnt = MAX_DEVS - aud_subsys.dev_cnt;
275 }
Benny Prijonof87f67c2009-04-06 10:02:53 +0000276
277 /* enabling this will cause pjsua-lib initialization to fail when there
278 * is no sound device installed in the system, even when pjsua has been
279 * run with --null-audio
280 *
Benny Prijono96e74f32009-02-22 12:00:12 +0000281 if (dev_cnt == 0) {
282 f->op->destroy(f);
283 return PJMEDIA_EAUD_NODEV;
284 }
Benny Prijonof87f67c2009-04-06 10:02:53 +0000285 */
Benny Prijono96e74f32009-02-22 12:00:12 +0000286
287 /* Fill in default devices */
288 drv->play_dev_idx = drv->rec_dev_idx = drv->dev_idx = -1;
289 for (i=0; i<dev_cnt; ++i) {
290 pjmedia_aud_dev_info info;
291
292 status = f->op->get_dev_info(f, i, &info);
293 if (status != PJ_SUCCESS) {
294 f->op->destroy(f);
295 return status;
296 }
297
298 if (drv->name[0]=='\0') {
299 /* Set driver name */
300 pj_ansi_strncpy(drv->name, info.driver, sizeof(drv->name));
301 drv->name[sizeof(drv->name)-1] = '\0';
302 }
303
304 if (drv->play_dev_idx < 0 && info.output_count) {
305 /* Set default playback device */
306 drv->play_dev_idx = i;
307 }
308 if (drv->rec_dev_idx < 0 && info.input_count) {
309 /* Set default capture device */
310 drv->rec_dev_idx = i;
311 }
312 if (drv->dev_idx < 0 && info.input_count &&
313 info.output_count)
314 {
315 /* Set default capture and playback device */
316 drv->dev_idx = i;
317 }
318
319 if (drv->play_dev_idx >= 0 && drv->rec_dev_idx >= 0 &&
320 drv->dev_idx >= 0)
321 {
322 /* Done. */
323 break;
324 }
325 }
326
327 /* Register the factory */
328 drv->f = f;
329 drv->f->sys.drv_idx = drv_idx;
330 drv->start_idx = aud_subsys.dev_cnt;
331 drv->dev_cnt = dev_cnt;
332
333 /* Register devices to global list */
334 for (i=0; i<dev_cnt; ++i) {
335 aud_subsys.dev_list[aud_subsys.dev_cnt++] = MAKE_DEV_ID(drv_idx, i);
336 }
337
338 return PJ_SUCCESS;
339}
340
341/* Internal: deinit driver */
342static void deinit_driver(unsigned drv_idx)
343{
344 struct driver *drv = &aud_subsys.drv[drv_idx];
345
346 if (drv->f) {
347 drv->f->op->destroy(drv->f);
348 drv->f = NULL;
349 }
350
351 drv->dev_cnt = 0;
352 drv->play_dev_idx = drv->rec_dev_idx = drv->dev_idx = -1;
353}
Benny Prijono2cd64f82009-02-17 19:57:48 +0000354
355/* API: Initialize the audio subsystem. */
356PJ_DEF(pj_status_t) pjmedia_aud_subsys_init(pj_pool_factory *pf)
357{
358 unsigned i;
Benny Prijonodebe8e12009-06-03 12:29:35 +0000359 pj_status_t status = PJ_SUCCESS;
Benny Prijono2cd64f82009-02-17 19:57:48 +0000360
Benny Prijono555139d2009-02-19 12:08:19 +0000361 /* Allow init() to be called multiple times as long as there is matching
362 * number of shutdown().
363 */
364 if (aud_subsys.init_count++ != 0) {
365 return PJ_SUCCESS;
366 }
367
Benny Prijono8eeab0b2009-03-04 19:00:28 +0000368 /* Register error subsystem */
369 pj_register_strerror(PJMEDIA_AUDIODEV_ERRNO_START,
370 PJ_ERRNO_SPACE_SIZE,
371 &pjmedia_audiodev_strerror);
372
373 /* Init */
Benny Prijonoe3ebd552009-02-18 20:14:15 +0000374 aud_subsys.pf = pf;
Benny Prijono10454dc2009-02-21 14:21:59 +0000375 aud_subsys.drv_cnt = 0;
376 aud_subsys.dev_cnt = 0;
Benny Prijono2cd64f82009-02-17 19:57:48 +0000377
Benny Prijono10454dc2009-02-21 14:21:59 +0000378 /* Register creation functions */
Benny Prijono19a87c72010-01-27 17:22:17 +0000379#if PJMEDIA_AUDIO_DEV_HAS_ALSA
380 aud_subsys.drv[aud_subsys.drv_cnt++].create = &pjmedia_alsa_factory;
381#endif
Benny Prijono2058f472009-02-22 17:15:34 +0000382#if PJMEDIA_AUDIO_DEV_HAS_PORTAUDIO
Benny Prijono10454dc2009-02-21 14:21:59 +0000383 aud_subsys.drv[aud_subsys.drv_cnt++].create = &pjmedia_pa_factory;
Benny Prijono2058f472009-02-22 17:15:34 +0000384#endif
385#if PJMEDIA_AUDIO_DEV_HAS_WMME
Benny Prijono10454dc2009-02-21 14:21:59 +0000386 aud_subsys.drv[aud_subsys.drv_cnt++].create = &pjmedia_wmme_factory;
Benny Prijono2058f472009-02-22 17:15:34 +0000387#endif
Nanang Izzuddind687a502009-06-30 13:37:26 +0000388#if PJMEDIA_AUDIO_DEV_HAS_SYMB_VAS
389 aud_subsys.drv[aud_subsys.drv_cnt++].create = &pjmedia_symb_vas_factory;
390#endif
Nanang Izzuddina940b362009-02-23 13:53:30 +0000391#if PJMEDIA_AUDIO_DEV_HAS_SYMB_APS
392 aud_subsys.drv[aud_subsys.drv_cnt++].create = &pjmedia_aps_factory;
393#endif
394#if PJMEDIA_AUDIO_DEV_HAS_SYMB_MDA
395 aud_subsys.drv[aud_subsys.drv_cnt++].create = &pjmedia_symb_mda_factory;
396#endif
Sauw Ming60a0c9b2010-02-19 09:57:48 +0000397#if PJMEDIA_AUDIO_DEV_HAS_NULL_AUDIO
398 aud_subsys.drv[aud_subsys.drv_cnt++].create = &pjmedia_null_audio_factory;
399#endif
Benny Prijonoe3ebd552009-02-18 20:14:15 +0000400
Benny Prijono10454dc2009-02-21 14:21:59 +0000401 /* Initialize each factory and build the device ID list */
402 for (i=0; i<aud_subsys.drv_cnt; ++i) {
Benny Prijono96e74f32009-02-22 12:00:12 +0000403 status = init_driver(i);
Benny Prijono2cd64f82009-02-17 19:57:48 +0000404 if (status != PJ_SUCCESS) {
Benny Prijono96e74f32009-02-22 12:00:12 +0000405 deinit_driver(i);
Benny Prijonoe3ebd552009-02-18 20:14:15 +0000406 continue;
Benny Prijono2cd64f82009-02-17 19:57:48 +0000407 }
Benny Prijono2cd64f82009-02-17 19:57:48 +0000408 }
409
Benny Prijono96e74f32009-02-22 12:00:12 +0000410 return aud_subsys.dev_cnt ? PJ_SUCCESS : status;
Benny Prijonoe3ebd552009-02-18 20:14:15 +0000411}
412
413/* API: get the pool factory registered to the audio subsystem. */
414PJ_DEF(pj_pool_factory*) pjmedia_aud_subsys_get_pool_factory(void)
415{
416 return aud_subsys.pf;
Benny Prijono2cd64f82009-02-17 19:57:48 +0000417}
418
419/* API: Shutdown the audio subsystem. */
420PJ_DEF(pj_status_t) pjmedia_aud_subsys_shutdown(void)
421{
422 unsigned i;
423
Benny Prijono555139d2009-02-19 12:08:19 +0000424 /* Allow shutdown() to be called multiple times as long as there is matching
425 * number of init().
426 */
427 if (aud_subsys.init_count == 0) {
428 return PJ_SUCCESS;
429 }
430 --aud_subsys.init_count;
431
Benny Prijono10454dc2009-02-21 14:21:59 +0000432 for (i=0; i<aud_subsys.drv_cnt; ++i) {
Benny Prijono96e74f32009-02-22 12:00:12 +0000433 deinit_driver(i);
Benny Prijono2cd64f82009-02-17 19:57:48 +0000434 }
435
Benny Prijono96e74f32009-02-22 12:00:12 +0000436 aud_subsys.pf = NULL;
Benny Prijono2cd64f82009-02-17 19:57:48 +0000437 return PJ_SUCCESS;
438}
439
440/* API: Get the number of sound devices installed in the system. */
441PJ_DEF(unsigned) pjmedia_aud_dev_count(void)
442{
Benny Prijono10454dc2009-02-21 14:21:59 +0000443 return aud_subsys.dev_cnt;
Benny Prijono2cd64f82009-02-17 19:57:48 +0000444}
445
Benny Prijono96e74f32009-02-22 12:00:12 +0000446/* Internal: convert local index to global device index */
447static pj_status_t make_global_index(unsigned drv_idx,
448 pjmedia_aud_dev_index *id)
449{
450 if (*id < 0) {
451 return PJ_SUCCESS;
452 }
453
454 /* Check that factory still exists */
455 PJ_ASSERT_RETURN(aud_subsys.drv[drv_idx].f, PJ_EBUG);
456
457 /* Check that device index is valid */
458 PJ_ASSERT_RETURN(*id>=0 && *id<(int)aud_subsys.drv[drv_idx].dev_cnt,
459 PJ_EBUG);
460
461 *id += aud_subsys.drv[drv_idx].start_idx;
462 return PJ_SUCCESS;
463}
464
Benny Prijono10454dc2009-02-21 14:21:59 +0000465/* Internal: lookup device id */
466static pj_status_t lookup_dev(pjmedia_aud_dev_index id,
467 pjmedia_aud_dev_factory **p_f,
468 unsigned *p_local_index)
Benny Prijono2cd64f82009-02-17 19:57:48 +0000469{
Benny Prijono10454dc2009-02-21 14:21:59 +0000470 int f_id, index;
Benny Prijono2cd64f82009-02-17 19:57:48 +0000471
Benny Prijono96e74f32009-02-22 12:00:12 +0000472 if (id < 0) {
473 unsigned i;
Benny Prijono2cd64f82009-02-17 19:57:48 +0000474
Benny Prijono96e74f32009-02-22 12:00:12 +0000475 if (id == PJMEDIA_AUD_INVALID_DEV)
476 return PJMEDIA_EAUD_INVDEV;
477
478 for (i=0; i<aud_subsys.drv_cnt; ++i) {
479 struct driver *drv = &aud_subsys.drv[i];
480 if (drv->dev_idx >= 0) {
481 id = drv->dev_idx;
482 make_global_index(i, &id);
483 break;
484 } else if (id==PJMEDIA_AUD_DEFAULT_CAPTURE_DEV &&
485 drv->rec_dev_idx >= 0)
486 {
487 id = drv->rec_dev_idx;
488 make_global_index(i, &id);
489 break;
490 } else if (id==PJMEDIA_AUD_DEFAULT_PLAYBACK_DEV &&
491 drv->play_dev_idx >= 0)
492 {
493 id = drv->play_dev_idx;
494 make_global_index(i, &id);
495 break;
496 }
497 }
498
499 if (id < 0) {
500 return PJMEDIA_EAUD_NODEFDEV;
501 }
502 }
Benny Prijono2cd64f82009-02-17 19:57:48 +0000503
Benny Prijono10454dc2009-02-21 14:21:59 +0000504 f_id = GET_FID(aud_subsys.dev_list[id]);
505 index = GET_INDEX(aud_subsys.dev_list[id]);
Benny Prijono2cd64f82009-02-17 19:57:48 +0000506
Benny Prijono10454dc2009-02-21 14:21:59 +0000507 if (f_id < 0 || f_id >= (int)aud_subsys.drv_cnt)
508 return PJMEDIA_EAUD_INVDEV;
509
510 if (index < 0 || index >= (int)aud_subsys.drv[f_id].dev_cnt)
511 return PJMEDIA_EAUD_INVDEV;
512
513 *p_f = aud_subsys.drv[f_id].f;
514 *p_local_index = (unsigned)index;
515
516 return PJ_SUCCESS;
517
Benny Prijono2cd64f82009-02-17 19:57:48 +0000518}
519
Benny Prijono2cd64f82009-02-17 19:57:48 +0000520/* API: Get device information. */
Benny Prijono10454dc2009-02-21 14:21:59 +0000521PJ_DEF(pj_status_t) pjmedia_aud_dev_get_info(pjmedia_aud_dev_index id,
Benny Prijono2cd64f82009-02-17 19:57:48 +0000522 pjmedia_aud_dev_info *info)
523{
Benny Prijonoe3ebd552009-02-18 20:14:15 +0000524 pjmedia_aud_dev_factory *f;
Benny Prijono10454dc2009-02-21 14:21:59 +0000525 unsigned index;
526 pj_status_t status;
Benny Prijono2cd64f82009-02-17 19:57:48 +0000527
Benny Prijono96e74f32009-02-22 12:00:12 +0000528 PJ_ASSERT_RETURN(info && id!=PJMEDIA_AUD_INVALID_DEV, PJ_EINVAL);
529 PJ_ASSERT_RETURN(aud_subsys.pf, PJMEDIA_EAUD_INIT);
Benny Prijono598b01d2009-02-18 13:55:03 +0000530
Benny Prijono10454dc2009-02-21 14:21:59 +0000531 status = lookup_dev(id, &f, &index);
532 if (status != PJ_SUCCESS)
533 return status;
Benny Prijono2cd64f82009-02-17 19:57:48 +0000534
Benny Prijonoe3ebd552009-02-18 20:14:15 +0000535 return f->op->get_dev_info(f, index, info);
Benny Prijono2cd64f82009-02-17 19:57:48 +0000536}
537
Benny Prijono10454dc2009-02-21 14:21:59 +0000538/* API: find device */
539PJ_DEF(pj_status_t) pjmedia_aud_dev_lookup( const char *drv_name,
540 const char *dev_name,
541 pjmedia_aud_dev_index *id)
542{
543 pjmedia_aud_dev_factory *f = NULL;
Benny Prijono96e74f32009-02-22 12:00:12 +0000544 unsigned drv_idx, dev_idx;
Benny Prijono10454dc2009-02-21 14:21:59 +0000545
546 PJ_ASSERT_RETURN(drv_name && dev_name && id, PJ_EINVAL);
Benny Prijono96e74f32009-02-22 12:00:12 +0000547 PJ_ASSERT_RETURN(aud_subsys.pf, PJMEDIA_EAUD_INIT);
Benny Prijono10454dc2009-02-21 14:21:59 +0000548
Benny Prijono96e74f32009-02-22 12:00:12 +0000549 for (drv_idx=0; drv_idx<aud_subsys.drv_cnt; ++drv_idx) {
550 if (!pj_ansi_stricmp(drv_name, aud_subsys.drv[drv_idx].name)) {
551 f = aud_subsys.drv[drv_idx].f;
Benny Prijono10454dc2009-02-21 14:21:59 +0000552 break;
553 }
554 }
555
556 if (!f)
557 return PJ_ENOTFOUND;
558
Benny Prijono96e74f32009-02-22 12:00:12 +0000559 for (dev_idx=0; dev_idx<aud_subsys.drv[drv_idx].dev_cnt; ++dev_idx) {
Benny Prijono10454dc2009-02-21 14:21:59 +0000560 pjmedia_aud_dev_info info;
561 pj_status_t status;
562
Benny Prijono96e74f32009-02-22 12:00:12 +0000563 status = f->op->get_dev_info(f, dev_idx, &info);
Benny Prijono10454dc2009-02-21 14:21:59 +0000564 if (status != PJ_SUCCESS)
565 return status;
566
567 if (!pj_ansi_stricmp(dev_name, info.name))
568 break;
569 }
570
Benny Prijono96e74f32009-02-22 12:00:12 +0000571 if (dev_idx==aud_subsys.drv[drv_idx].dev_cnt)
Benny Prijono10454dc2009-02-21 14:21:59 +0000572 return PJ_ENOTFOUND;
573
Benny Prijono96e74f32009-02-22 12:00:12 +0000574 *id = dev_idx;
575 make_global_index(drv_idx, id);
Benny Prijono10454dc2009-02-21 14:21:59 +0000576
577 return PJ_SUCCESS;
578}
579
Benny Prijono2cd64f82009-02-17 19:57:48 +0000580/* API: Initialize the audio device parameters with default values for the
581 * specified device.
582 */
Benny Prijono10454dc2009-02-21 14:21:59 +0000583PJ_DEF(pj_status_t) pjmedia_aud_dev_default_param(pjmedia_aud_dev_index id,
584 pjmedia_aud_param *param)
Benny Prijono2cd64f82009-02-17 19:57:48 +0000585{
Benny Prijonoe3ebd552009-02-18 20:14:15 +0000586 pjmedia_aud_dev_factory *f;
Benny Prijono10454dc2009-02-21 14:21:59 +0000587 unsigned index;
Benny Prijono2cd64f82009-02-17 19:57:48 +0000588 pj_status_t status;
589
Benny Prijono96e74f32009-02-22 12:00:12 +0000590 PJ_ASSERT_RETURN(param && id!=PJMEDIA_AUD_INVALID_DEV, PJ_EINVAL);
591 PJ_ASSERT_RETURN(aud_subsys.pf, PJMEDIA_EAUD_INIT);
Benny Prijono598b01d2009-02-18 13:55:03 +0000592
Benny Prijono10454dc2009-02-21 14:21:59 +0000593 status = lookup_dev(id, &f, &index);
594 if (status != PJ_SUCCESS)
595 return status;
Benny Prijono2cd64f82009-02-17 19:57:48 +0000596
Benny Prijonoe3ebd552009-02-18 20:14:15 +0000597 status = f->op->default_param(f, index, param);
Benny Prijono2cd64f82009-02-17 19:57:48 +0000598 if (status != PJ_SUCCESS)
599 return status;
600
601 /* Normalize device IDs */
Benny Prijono96e74f32009-02-22 12:00:12 +0000602 make_global_index(f->sys.drv_idx, &param->rec_id);
603 make_global_index(f->sys.drv_idx, &param->play_id);
Benny Prijono2cd64f82009-02-17 19:57:48 +0000604
605 return PJ_SUCCESS;
606}
607
608/* API: Open audio stream object using the specified parameters. */
Benny Prijono10454dc2009-02-21 14:21:59 +0000609PJ_DEF(pj_status_t) pjmedia_aud_stream_create(const pjmedia_aud_param *prm,
Benny Prijono2cd64f82009-02-17 19:57:48 +0000610 pjmedia_aud_rec_cb rec_cb,
611 pjmedia_aud_play_cb play_cb,
612 void *user_data,
613 pjmedia_aud_stream **p_aud_strm)
614{
Benny Prijono10454dc2009-02-21 14:21:59 +0000615 pjmedia_aud_dev_factory *rec_f=NULL, *play_f=NULL, *f=NULL;
616 pjmedia_aud_param param;
Benny Prijono2cd64f82009-02-17 19:57:48 +0000617 pj_status_t status;
618
Benny Prijono10454dc2009-02-21 14:21:59 +0000619 PJ_ASSERT_RETURN(prm && prm->dir && p_aud_strm, PJ_EINVAL);
Benny Prijono96e74f32009-02-22 12:00:12 +0000620 PJ_ASSERT_RETURN(aud_subsys.pf, PJMEDIA_EAUD_INIT);
Benny Prijonod65f78c2009-06-03 18:59:37 +0000621 PJ_ASSERT_RETURN(prm->dir==PJMEDIA_DIR_CAPTURE ||
622 prm->dir==PJMEDIA_DIR_PLAYBACK ||
623 prm->dir==PJMEDIA_DIR_CAPTURE_PLAYBACK,
624 PJ_EINVAL);
Benny Prijono10454dc2009-02-21 14:21:59 +0000625
Benny Prijono2cd64f82009-02-17 19:57:48 +0000626 /* Must make copy of param because we're changing device ID */
Benny Prijono10454dc2009-02-21 14:21:59 +0000627 pj_memcpy(&param, prm, sizeof(param));
Benny Prijono2cd64f82009-02-17 19:57:48 +0000628
Benny Prijono10454dc2009-02-21 14:21:59 +0000629 /* Normalize rec_id */
630 if (param.dir & PJMEDIA_DIR_CAPTURE) {
631 unsigned index;
632
Benny Prijono96e74f32009-02-22 12:00:12 +0000633 if (param.rec_id < 0)
634 param.rec_id = PJMEDIA_AUD_DEFAULT_CAPTURE_DEV;
635
Benny Prijono10454dc2009-02-21 14:21:59 +0000636 status = lookup_dev(param.rec_id, &rec_f, &index);
637 if (status != PJ_SUCCESS)
638 return status;
639
640 param.rec_id = index;
641 f = rec_f;
642 }
643
644 /* Normalize play_id */
645 if (param.dir & PJMEDIA_DIR_PLAYBACK) {
646 unsigned index;
647
Benny Prijono96e74f32009-02-22 12:00:12 +0000648 if (param.play_id < 0)
649 param.play_id = PJMEDIA_AUD_DEFAULT_PLAYBACK_DEV;
650
Benny Prijono10454dc2009-02-21 14:21:59 +0000651 status = lookup_dev(param.play_id, &play_f, &index);
652 if (status != PJ_SUCCESS)
653 return status;
654
655 param.play_id = index;
656 f = play_f;
Benny Prijono10454dc2009-02-21 14:21:59 +0000657 }
658
Benny Prijonod65f78c2009-06-03 18:59:37 +0000659 PJ_ASSERT_RETURN(f != NULL, PJ_EBUG);
660
Nanang Izzuddin6f7d5a12009-03-16 16:29:39 +0000661 /* For now, rec_id and play_id must belong to the same factory */
662 PJ_ASSERT_RETURN((param.dir != PJMEDIA_DIR_CAPTURE_PLAYBACK) ||
663 (rec_f == play_f),
664 PJMEDIA_EAUD_INVDEV);
665
Benny Prijono10454dc2009-02-21 14:21:59 +0000666 /* Create the stream */
Benny Prijonoe3ebd552009-02-18 20:14:15 +0000667 status = f->op->create_stream(f, &param, rec_cb, play_cb,
668 user_data, p_aud_strm);
Benny Prijono2cd64f82009-02-17 19:57:48 +0000669 if (status != PJ_SUCCESS)
670 return status;
671
Benny Prijono10454dc2009-02-21 14:21:59 +0000672 /* Assign factory id to the stream */
Benny Prijono96e74f32009-02-22 12:00:12 +0000673 (*p_aud_strm)->sys.drv_idx = f->sys.drv_idx;
Benny Prijono2cd64f82009-02-17 19:57:48 +0000674 return PJ_SUCCESS;
675}
676
677/* API: Get the running parameters for the specified audio stream. */
678PJ_DEF(pj_status_t) pjmedia_aud_stream_get_param(pjmedia_aud_stream *strm,
Benny Prijono10454dc2009-02-21 14:21:59 +0000679 pjmedia_aud_param *param)
Benny Prijono2cd64f82009-02-17 19:57:48 +0000680{
681 pj_status_t status;
682
Benny Prijono96e74f32009-02-22 12:00:12 +0000683 PJ_ASSERT_RETURN(strm && param, PJ_EINVAL);
684 PJ_ASSERT_RETURN(aud_subsys.pf, PJMEDIA_EAUD_INIT);
685
Benny Prijono2cd64f82009-02-17 19:57:48 +0000686 status = strm->op->get_param(strm, param);
687 if (status != PJ_SUCCESS)
688 return status;
689
690 /* Normalize device id's */
Benny Prijono96e74f32009-02-22 12:00:12 +0000691 make_global_index(strm->sys.drv_idx, &param->rec_id);
692 make_global_index(strm->sys.drv_idx, &param->play_id);
Benny Prijono2cd64f82009-02-17 19:57:48 +0000693
694 return PJ_SUCCESS;
695}
696
697/* API: Get the value of a specific capability of the audio stream. */
698PJ_DEF(pj_status_t) pjmedia_aud_stream_get_cap(pjmedia_aud_stream *strm,
699 pjmedia_aud_dev_cap cap,
700 void *value)
701{
702 return strm->op->get_cap(strm, cap, value);
703}
704
705/* API: Set the value of a specific capability of the audio stream. */
706PJ_DEF(pj_status_t) pjmedia_aud_stream_set_cap(pjmedia_aud_stream *strm,
707 pjmedia_aud_dev_cap cap,
708 const void *value)
709{
710 return strm->op->set_cap(strm, cap, value);
711}
712
713/* API: Start the stream. */
714PJ_DEF(pj_status_t) pjmedia_aud_stream_start(pjmedia_aud_stream *strm)
715{
716 return strm->op->start(strm);
717}
718
719/* API: Stop the stream. */
720PJ_DEF(pj_status_t) pjmedia_aud_stream_stop(pjmedia_aud_stream *strm)
721{
722 return strm->op->stop(strm);
723}
724
725/* API: Destroy the stream. */
726PJ_DEF(pj_status_t) pjmedia_aud_stream_destroy(pjmedia_aud_stream *strm)
727{
728 return strm->op->destroy(strm);
729}
730
731