blob: 93939aa668eff66ae5ffe918a1759d8930aba60a [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 Izzuddind687a502009-06-30 13:37:26 +000077#if PJMEDIA_AUDIO_DEV_HAS_SYMB_VAS
78pjmedia_aud_dev_factory* pjmedia_symb_vas_factory(pj_pool_factory *pf);
79#endif
80
Nanang Izzuddina940b362009-02-23 13:53:30 +000081#if PJMEDIA_AUDIO_DEV_HAS_SYMB_APS
82pjmedia_aud_dev_factory* pjmedia_aps_factory(pj_pool_factory *pf);
83#endif
84
85#if PJMEDIA_AUDIO_DEV_HAS_SYMB_MDA
86pjmedia_aud_dev_factory* pjmedia_symb_mda_factory(pj_pool_factory *pf);
87#endif
Benny Prijono598b01d2009-02-18 13:55:03 +000088
Benny Prijonoe3ebd552009-02-18 20:14:15 +000089#define MAX_DRIVERS 16
Benny Prijono10454dc2009-02-21 14:21:59 +000090#define MAX_DEVS 64
91
92/* typedef for factory creation function */
93typedef pjmedia_aud_dev_factory* (*create_func_ptr)(pj_pool_factory*);
94
95/* driver structure */
96struct driver
97{
98 create_func_ptr create; /* Creation function. */
99 pjmedia_aud_dev_factory *f; /* Factory instance. */
100 char name[32]; /* Driver name */
101 unsigned dev_cnt; /* Number of devices */
102 unsigned start_idx; /* Start index in global list */
Benny Prijono96e74f32009-02-22 12:00:12 +0000103 int rec_dev_idx;/* Default capture device. */
104 int play_dev_idx;/* Default playback device */
105 int dev_idx; /* Default device. */
Benny Prijono10454dc2009-02-21 14:21:59 +0000106};
Benny Prijono2cd64f82009-02-17 19:57:48 +0000107
Benny Prijonoe3ebd552009-02-18 20:14:15 +0000108/* The audio subsystem */
109static struct aud_subsys
Benny Prijono2cd64f82009-02-17 19:57:48 +0000110{
Benny Prijono10454dc2009-02-21 14:21:59 +0000111 unsigned init_count; /* How many times init() is called */
112 pj_pool_factory *pf; /* The pool factory. */
Benny Prijono2cd64f82009-02-17 19:57:48 +0000113
Benny Prijono10454dc2009-02-21 14:21:59 +0000114 unsigned drv_cnt; /* Number of drivers. */
115 struct driver drv[MAX_DRIVERS]; /* Array of drivers. */
Benny Prijonoe3ebd552009-02-18 20:14:15 +0000116
Benny Prijono10454dc2009-02-21 14:21:59 +0000117 unsigned dev_cnt; /* Total number of devices. */
118 pj_uint32_t dev_list[MAX_DEVS];/* Array of device IDs. */
Benny Prijonoe3ebd552009-02-18 20:14:15 +0000119
120} aud_subsys;
121
Benny Prijono7a380002009-03-09 12:55:29 +0000122/* API: get capability name/info */
123PJ_DEF(const char*) pjmedia_aud_dev_cap_name(pjmedia_aud_dev_cap cap,
124 const char **p_desc)
125{
126 const char *desc;
127 unsigned i;
128
129 if (p_desc==NULL) p_desc = &desc;
130
131 for (i=0; i<PJ_ARRAY_SIZE(cap_infos); ++i) {
132 if ((1 << i)==cap)
133 break;
134 }
135
136 if (i==32) {
137 *p_desc = "??";
138 return "??";
139 }
140
141 *p_desc = cap_infos[i].info;
142 return cap_infos[i].name;
143}
144
145static pj_status_t get_cap_pointer(const pjmedia_aud_param *param,
146 pjmedia_aud_dev_cap cap,
147 void **ptr,
148 unsigned *size)
149{
150#define FIELD_INFO(name) *ptr = (void*)&param->name; \
151 *size = sizeof(param->name)
152
153 switch (cap) {
154 case PJMEDIA_AUD_DEV_CAP_EXT_FORMAT:
155 FIELD_INFO(ext_fmt);
156 break;
157 case PJMEDIA_AUD_DEV_CAP_INPUT_LATENCY:
158 FIELD_INFO(input_latency_ms);
159 break;
160 case PJMEDIA_AUD_DEV_CAP_OUTPUT_LATENCY:
161 FIELD_INFO(output_latency_ms);
162 break;
163 case PJMEDIA_AUD_DEV_CAP_INPUT_VOLUME_SETTING:
164 FIELD_INFO(input_vol);
165 break;
166 case PJMEDIA_AUD_DEV_CAP_OUTPUT_VOLUME_SETTING:
167 FIELD_INFO(output_vol);
168 break;
169 case PJMEDIA_AUD_DEV_CAP_INPUT_ROUTE:
170 FIELD_INFO(input_route);
171 break;
172 case PJMEDIA_AUD_DEV_CAP_OUTPUT_ROUTE:
173 FIELD_INFO(output_route);
174 break;
175 case PJMEDIA_AUD_DEV_CAP_EC:
176 FIELD_INFO(ec_enabled);
177 break;
178 case PJMEDIA_AUD_DEV_CAP_EC_TAIL:
179 FIELD_INFO(ec_tail_ms);
180 break;
181 case PJMEDIA_AUD_DEV_CAP_VAD:
182 FIELD_INFO(ext_fmt.vad);
183 break;
184 case PJMEDIA_AUD_DEV_CAP_CNG:
185 FIELD_INFO(cng_enabled);
186 break;
187 case PJMEDIA_AUD_DEV_CAP_PLC:
188 FIELD_INFO(plc_enabled);
189 break;
190 default:
191 return PJMEDIA_EAUD_INVCAP;
192 }
193
194#undef FIELD_INFO
195
196 return PJ_SUCCESS;
197}
198
199/* API: set cap value to param */
200PJ_DEF(pj_status_t) pjmedia_aud_param_set_cap( pjmedia_aud_param *param,
201 pjmedia_aud_dev_cap cap,
202 const void *pval)
203{
204 void *cap_ptr;
205 unsigned cap_size;
206 pj_status_t status;
207
208 status = get_cap_pointer(param, cap, &cap_ptr, &cap_size);
209 if (status != PJ_SUCCESS)
210 return status;
211
212 pj_memcpy(cap_ptr, pval, cap_size);
213 param->flags |= cap;
214
215 return PJ_SUCCESS;
216}
217
218/* API: get cap value from param */
219PJ_DEF(pj_status_t) pjmedia_aud_param_get_cap( const pjmedia_aud_param *param,
220 pjmedia_aud_dev_cap cap,
221 void *pval)
222{
223 void *cap_ptr;
224 unsigned cap_size;
225 pj_status_t status;
226
227 status = get_cap_pointer(param, cap, &cap_ptr, &cap_size);
228 if (status != PJ_SUCCESS)
229 return status;
230
231 if ((param->flags & cap) == 0) {
232 pj_bzero(cap_ptr, cap_size);
233 return PJMEDIA_EAUD_INVCAP;
234 }
235
236 pj_memcpy(pval, cap_ptr, cap_size);
237 return PJ_SUCCESS;
238}
Benny Prijono2cd64f82009-02-17 19:57:48 +0000239
Benny Prijono96e74f32009-02-22 12:00:12 +0000240/* Internal: init driver */
241static pj_status_t init_driver(unsigned drv_idx)
242{
243 struct driver *drv = &aud_subsys.drv[drv_idx];
244 pjmedia_aud_dev_factory *f;
245 unsigned i, dev_cnt;
246 pj_status_t status;
247
248 /* Create the factory */
249 f = (*drv->create)(aud_subsys.pf);
250 if (!f)
251 return PJ_EUNKNOWN;
252
253 /* Call factory->init() */
254 status = f->op->init(f);
255 if (status != PJ_SUCCESS) {
256 f->op->destroy(f);
257 return status;
258 }
259
260 /* Get number of devices */
261 dev_cnt = f->op->get_dev_count(f);
262 if (dev_cnt + aud_subsys.dev_cnt > MAX_DEVS) {
263 PJ_LOG(4,(THIS_FILE, "%d device(s) cannot be registered because"
264 " there are too many devices",
265 aud_subsys.dev_cnt + dev_cnt - MAX_DEVS));
266 dev_cnt = MAX_DEVS - aud_subsys.dev_cnt;
267 }
Benny Prijonof87f67c2009-04-06 10:02:53 +0000268
269 /* enabling this will cause pjsua-lib initialization to fail when there
270 * is no sound device installed in the system, even when pjsua has been
271 * run with --null-audio
272 *
Benny Prijono96e74f32009-02-22 12:00:12 +0000273 if (dev_cnt == 0) {
274 f->op->destroy(f);
275 return PJMEDIA_EAUD_NODEV;
276 }
Benny Prijonof87f67c2009-04-06 10:02:53 +0000277 */
Benny Prijono96e74f32009-02-22 12:00:12 +0000278
279 /* Fill in default devices */
280 drv->play_dev_idx = drv->rec_dev_idx = drv->dev_idx = -1;
281 for (i=0; i<dev_cnt; ++i) {
282 pjmedia_aud_dev_info info;
283
284 status = f->op->get_dev_info(f, i, &info);
285 if (status != PJ_SUCCESS) {
286 f->op->destroy(f);
287 return status;
288 }
289
290 if (drv->name[0]=='\0') {
291 /* Set driver name */
292 pj_ansi_strncpy(drv->name, info.driver, sizeof(drv->name));
293 drv->name[sizeof(drv->name)-1] = '\0';
294 }
295
296 if (drv->play_dev_idx < 0 && info.output_count) {
297 /* Set default playback device */
298 drv->play_dev_idx = i;
299 }
300 if (drv->rec_dev_idx < 0 && info.input_count) {
301 /* Set default capture device */
302 drv->rec_dev_idx = i;
303 }
304 if (drv->dev_idx < 0 && info.input_count &&
305 info.output_count)
306 {
307 /* Set default capture and playback device */
308 drv->dev_idx = i;
309 }
310
311 if (drv->play_dev_idx >= 0 && drv->rec_dev_idx >= 0 &&
312 drv->dev_idx >= 0)
313 {
314 /* Done. */
315 break;
316 }
317 }
318
319 /* Register the factory */
320 drv->f = f;
321 drv->f->sys.drv_idx = drv_idx;
322 drv->start_idx = aud_subsys.dev_cnt;
323 drv->dev_cnt = dev_cnt;
324
325 /* Register devices to global list */
326 for (i=0; i<dev_cnt; ++i) {
327 aud_subsys.dev_list[aud_subsys.dev_cnt++] = MAKE_DEV_ID(drv_idx, i);
328 }
329
330 return PJ_SUCCESS;
331}
332
333/* Internal: deinit driver */
334static void deinit_driver(unsigned drv_idx)
335{
336 struct driver *drv = &aud_subsys.drv[drv_idx];
337
338 if (drv->f) {
339 drv->f->op->destroy(drv->f);
340 drv->f = NULL;
341 }
342
343 drv->dev_cnt = 0;
344 drv->play_dev_idx = drv->rec_dev_idx = drv->dev_idx = -1;
345}
Benny Prijono2cd64f82009-02-17 19:57:48 +0000346
347/* API: Initialize the audio subsystem. */
348PJ_DEF(pj_status_t) pjmedia_aud_subsys_init(pj_pool_factory *pf)
349{
350 unsigned i;
Benny Prijonodebe8e12009-06-03 12:29:35 +0000351 pj_status_t status = PJ_SUCCESS;
Benny Prijono2cd64f82009-02-17 19:57:48 +0000352
Benny Prijono555139d2009-02-19 12:08:19 +0000353 /* Allow init() to be called multiple times as long as there is matching
354 * number of shutdown().
355 */
356 if (aud_subsys.init_count++ != 0) {
357 return PJ_SUCCESS;
358 }
359
Benny Prijono8eeab0b2009-03-04 19:00:28 +0000360 /* Register error subsystem */
361 pj_register_strerror(PJMEDIA_AUDIODEV_ERRNO_START,
362 PJ_ERRNO_SPACE_SIZE,
363 &pjmedia_audiodev_strerror);
364
365 /* Init */
Benny Prijonoe3ebd552009-02-18 20:14:15 +0000366 aud_subsys.pf = pf;
Benny Prijono10454dc2009-02-21 14:21:59 +0000367 aud_subsys.drv_cnt = 0;
368 aud_subsys.dev_cnt = 0;
Benny Prijono2cd64f82009-02-17 19:57:48 +0000369
Benny Prijono10454dc2009-02-21 14:21:59 +0000370 /* Register creation functions */
Benny Prijono2058f472009-02-22 17:15:34 +0000371#if PJMEDIA_AUDIO_DEV_HAS_PORTAUDIO
Benny Prijono10454dc2009-02-21 14:21:59 +0000372 aud_subsys.drv[aud_subsys.drv_cnt++].create = &pjmedia_pa_factory;
Benny Prijono2058f472009-02-22 17:15:34 +0000373#endif
374#if PJMEDIA_AUDIO_DEV_HAS_WMME
Benny Prijono10454dc2009-02-21 14:21:59 +0000375 aud_subsys.drv[aud_subsys.drv_cnt++].create = &pjmedia_wmme_factory;
Benny Prijono2058f472009-02-22 17:15:34 +0000376#endif
Nanang Izzuddind687a502009-06-30 13:37:26 +0000377#if PJMEDIA_AUDIO_DEV_HAS_SYMB_VAS
378 aud_subsys.drv[aud_subsys.drv_cnt++].create = &pjmedia_symb_vas_factory;
379#endif
Nanang Izzuddina940b362009-02-23 13:53:30 +0000380#if PJMEDIA_AUDIO_DEV_HAS_SYMB_APS
381 aud_subsys.drv[aud_subsys.drv_cnt++].create = &pjmedia_aps_factory;
382#endif
383#if PJMEDIA_AUDIO_DEV_HAS_SYMB_MDA
384 aud_subsys.drv[aud_subsys.drv_cnt++].create = &pjmedia_symb_mda_factory;
385#endif
Benny Prijonoe3ebd552009-02-18 20:14:15 +0000386
Benny Prijono10454dc2009-02-21 14:21:59 +0000387 /* Initialize each factory and build the device ID list */
388 for (i=0; i<aud_subsys.drv_cnt; ++i) {
Benny Prijono96e74f32009-02-22 12:00:12 +0000389 status = init_driver(i);
Benny Prijono2cd64f82009-02-17 19:57:48 +0000390 if (status != PJ_SUCCESS) {
Benny Prijono96e74f32009-02-22 12:00:12 +0000391 deinit_driver(i);
Benny Prijonoe3ebd552009-02-18 20:14:15 +0000392 continue;
Benny Prijono2cd64f82009-02-17 19:57:48 +0000393 }
Benny Prijono2cd64f82009-02-17 19:57:48 +0000394 }
395
Benny Prijono96e74f32009-02-22 12:00:12 +0000396 return aud_subsys.dev_cnt ? PJ_SUCCESS : status;
Benny Prijonoe3ebd552009-02-18 20:14:15 +0000397}
398
399/* API: get the pool factory registered to the audio subsystem. */
400PJ_DEF(pj_pool_factory*) pjmedia_aud_subsys_get_pool_factory(void)
401{
402 return aud_subsys.pf;
Benny Prijono2cd64f82009-02-17 19:57:48 +0000403}
404
405/* API: Shutdown the audio subsystem. */
406PJ_DEF(pj_status_t) pjmedia_aud_subsys_shutdown(void)
407{
408 unsigned i;
409
Benny Prijono555139d2009-02-19 12:08:19 +0000410 /* Allow shutdown() to be called multiple times as long as there is matching
411 * number of init().
412 */
413 if (aud_subsys.init_count == 0) {
414 return PJ_SUCCESS;
415 }
416 --aud_subsys.init_count;
417
Benny Prijono10454dc2009-02-21 14:21:59 +0000418 for (i=0; i<aud_subsys.drv_cnt; ++i) {
Benny Prijono96e74f32009-02-22 12:00:12 +0000419 deinit_driver(i);
Benny Prijono2cd64f82009-02-17 19:57:48 +0000420 }
421
Benny Prijono96e74f32009-02-22 12:00:12 +0000422 aud_subsys.pf = NULL;
Benny Prijono2cd64f82009-02-17 19:57:48 +0000423 return PJ_SUCCESS;
424}
425
426/* API: Get the number of sound devices installed in the system. */
427PJ_DEF(unsigned) pjmedia_aud_dev_count(void)
428{
Benny Prijono10454dc2009-02-21 14:21:59 +0000429 return aud_subsys.dev_cnt;
Benny Prijono2cd64f82009-02-17 19:57:48 +0000430}
431
Benny Prijono96e74f32009-02-22 12:00:12 +0000432/* Internal: convert local index to global device index */
433static pj_status_t make_global_index(unsigned drv_idx,
434 pjmedia_aud_dev_index *id)
435{
436 if (*id < 0) {
437 return PJ_SUCCESS;
438 }
439
440 /* Check that factory still exists */
441 PJ_ASSERT_RETURN(aud_subsys.drv[drv_idx].f, PJ_EBUG);
442
443 /* Check that device index is valid */
444 PJ_ASSERT_RETURN(*id>=0 && *id<(int)aud_subsys.drv[drv_idx].dev_cnt,
445 PJ_EBUG);
446
447 *id += aud_subsys.drv[drv_idx].start_idx;
448 return PJ_SUCCESS;
449}
450
Benny Prijono10454dc2009-02-21 14:21:59 +0000451/* Internal: lookup device id */
452static pj_status_t lookup_dev(pjmedia_aud_dev_index id,
453 pjmedia_aud_dev_factory **p_f,
454 unsigned *p_local_index)
Benny Prijono2cd64f82009-02-17 19:57:48 +0000455{
Benny Prijono10454dc2009-02-21 14:21:59 +0000456 int f_id, index;
Benny Prijono2cd64f82009-02-17 19:57:48 +0000457
Benny Prijono96e74f32009-02-22 12:00:12 +0000458 if (id < 0) {
459 unsigned i;
Benny Prijono2cd64f82009-02-17 19:57:48 +0000460
Benny Prijono96e74f32009-02-22 12:00:12 +0000461 if (id == PJMEDIA_AUD_INVALID_DEV)
462 return PJMEDIA_EAUD_INVDEV;
463
464 for (i=0; i<aud_subsys.drv_cnt; ++i) {
465 struct driver *drv = &aud_subsys.drv[i];
466 if (drv->dev_idx >= 0) {
467 id = drv->dev_idx;
468 make_global_index(i, &id);
469 break;
470 } else if (id==PJMEDIA_AUD_DEFAULT_CAPTURE_DEV &&
471 drv->rec_dev_idx >= 0)
472 {
473 id = drv->rec_dev_idx;
474 make_global_index(i, &id);
475 break;
476 } else if (id==PJMEDIA_AUD_DEFAULT_PLAYBACK_DEV &&
477 drv->play_dev_idx >= 0)
478 {
479 id = drv->play_dev_idx;
480 make_global_index(i, &id);
481 break;
482 }
483 }
484
485 if (id < 0) {
486 return PJMEDIA_EAUD_NODEFDEV;
487 }
488 }
Benny Prijono2cd64f82009-02-17 19:57:48 +0000489
Benny Prijono10454dc2009-02-21 14:21:59 +0000490 f_id = GET_FID(aud_subsys.dev_list[id]);
491 index = GET_INDEX(aud_subsys.dev_list[id]);
Benny Prijono2cd64f82009-02-17 19:57:48 +0000492
Benny Prijono10454dc2009-02-21 14:21:59 +0000493 if (f_id < 0 || f_id >= (int)aud_subsys.drv_cnt)
494 return PJMEDIA_EAUD_INVDEV;
495
496 if (index < 0 || index >= (int)aud_subsys.drv[f_id].dev_cnt)
497 return PJMEDIA_EAUD_INVDEV;
498
499 *p_f = aud_subsys.drv[f_id].f;
500 *p_local_index = (unsigned)index;
501
502 return PJ_SUCCESS;
503
Benny Prijono2cd64f82009-02-17 19:57:48 +0000504}
505
Benny Prijono2cd64f82009-02-17 19:57:48 +0000506/* API: Get device information. */
Benny Prijono10454dc2009-02-21 14:21:59 +0000507PJ_DEF(pj_status_t) pjmedia_aud_dev_get_info(pjmedia_aud_dev_index id,
Benny Prijono2cd64f82009-02-17 19:57:48 +0000508 pjmedia_aud_dev_info *info)
509{
Benny Prijonoe3ebd552009-02-18 20:14:15 +0000510 pjmedia_aud_dev_factory *f;
Benny Prijono10454dc2009-02-21 14:21:59 +0000511 unsigned index;
512 pj_status_t status;
Benny Prijono2cd64f82009-02-17 19:57:48 +0000513
Benny Prijono96e74f32009-02-22 12:00:12 +0000514 PJ_ASSERT_RETURN(info && id!=PJMEDIA_AUD_INVALID_DEV, PJ_EINVAL);
515 PJ_ASSERT_RETURN(aud_subsys.pf, PJMEDIA_EAUD_INIT);
Benny Prijono598b01d2009-02-18 13:55:03 +0000516
Benny Prijono10454dc2009-02-21 14:21:59 +0000517 status = lookup_dev(id, &f, &index);
518 if (status != PJ_SUCCESS)
519 return status;
Benny Prijono2cd64f82009-02-17 19:57:48 +0000520
Benny Prijonoe3ebd552009-02-18 20:14:15 +0000521 return f->op->get_dev_info(f, index, info);
Benny Prijono2cd64f82009-02-17 19:57:48 +0000522}
523
Benny Prijono10454dc2009-02-21 14:21:59 +0000524/* API: find device */
525PJ_DEF(pj_status_t) pjmedia_aud_dev_lookup( const char *drv_name,
526 const char *dev_name,
527 pjmedia_aud_dev_index *id)
528{
529 pjmedia_aud_dev_factory *f = NULL;
Benny Prijono96e74f32009-02-22 12:00:12 +0000530 unsigned drv_idx, dev_idx;
Benny Prijono10454dc2009-02-21 14:21:59 +0000531
532 PJ_ASSERT_RETURN(drv_name && dev_name && id, PJ_EINVAL);
Benny Prijono96e74f32009-02-22 12:00:12 +0000533 PJ_ASSERT_RETURN(aud_subsys.pf, PJMEDIA_EAUD_INIT);
Benny Prijono10454dc2009-02-21 14:21:59 +0000534
Benny Prijono96e74f32009-02-22 12:00:12 +0000535 for (drv_idx=0; drv_idx<aud_subsys.drv_cnt; ++drv_idx) {
536 if (!pj_ansi_stricmp(drv_name, aud_subsys.drv[drv_idx].name)) {
537 f = aud_subsys.drv[drv_idx].f;
Benny Prijono10454dc2009-02-21 14:21:59 +0000538 break;
539 }
540 }
541
542 if (!f)
543 return PJ_ENOTFOUND;
544
Benny Prijono96e74f32009-02-22 12:00:12 +0000545 for (dev_idx=0; dev_idx<aud_subsys.drv[drv_idx].dev_cnt; ++dev_idx) {
Benny Prijono10454dc2009-02-21 14:21:59 +0000546 pjmedia_aud_dev_info info;
547 pj_status_t status;
548
Benny Prijono96e74f32009-02-22 12:00:12 +0000549 status = f->op->get_dev_info(f, dev_idx, &info);
Benny Prijono10454dc2009-02-21 14:21:59 +0000550 if (status != PJ_SUCCESS)
551 return status;
552
553 if (!pj_ansi_stricmp(dev_name, info.name))
554 break;
555 }
556
Benny Prijono96e74f32009-02-22 12:00:12 +0000557 if (dev_idx==aud_subsys.drv[drv_idx].dev_cnt)
Benny Prijono10454dc2009-02-21 14:21:59 +0000558 return PJ_ENOTFOUND;
559
Benny Prijono96e74f32009-02-22 12:00:12 +0000560 *id = dev_idx;
561 make_global_index(drv_idx, id);
Benny Prijono10454dc2009-02-21 14:21:59 +0000562
563 return PJ_SUCCESS;
564}
565
Benny Prijono2cd64f82009-02-17 19:57:48 +0000566/* API: Initialize the audio device parameters with default values for the
567 * specified device.
568 */
Benny Prijono10454dc2009-02-21 14:21:59 +0000569PJ_DEF(pj_status_t) pjmedia_aud_dev_default_param(pjmedia_aud_dev_index id,
570 pjmedia_aud_param *param)
Benny Prijono2cd64f82009-02-17 19:57:48 +0000571{
Benny Prijonoe3ebd552009-02-18 20:14:15 +0000572 pjmedia_aud_dev_factory *f;
Benny Prijono10454dc2009-02-21 14:21:59 +0000573 unsigned index;
Benny Prijono2cd64f82009-02-17 19:57:48 +0000574 pj_status_t status;
575
Benny Prijono96e74f32009-02-22 12:00:12 +0000576 PJ_ASSERT_RETURN(param && id!=PJMEDIA_AUD_INVALID_DEV, PJ_EINVAL);
577 PJ_ASSERT_RETURN(aud_subsys.pf, PJMEDIA_EAUD_INIT);
Benny Prijono598b01d2009-02-18 13:55:03 +0000578
Benny Prijono10454dc2009-02-21 14:21:59 +0000579 status = lookup_dev(id, &f, &index);
580 if (status != PJ_SUCCESS)
581 return status;
Benny Prijono2cd64f82009-02-17 19:57:48 +0000582
Benny Prijonoe3ebd552009-02-18 20:14:15 +0000583 status = f->op->default_param(f, index, param);
Benny Prijono2cd64f82009-02-17 19:57:48 +0000584 if (status != PJ_SUCCESS)
585 return status;
586
587 /* Normalize device IDs */
Benny Prijono96e74f32009-02-22 12:00:12 +0000588 make_global_index(f->sys.drv_idx, &param->rec_id);
589 make_global_index(f->sys.drv_idx, &param->play_id);
Benny Prijono2cd64f82009-02-17 19:57:48 +0000590
591 return PJ_SUCCESS;
592}
593
594/* API: Open audio stream object using the specified parameters. */
Benny Prijono10454dc2009-02-21 14:21:59 +0000595PJ_DEF(pj_status_t) pjmedia_aud_stream_create(const pjmedia_aud_param *prm,
Benny Prijono2cd64f82009-02-17 19:57:48 +0000596 pjmedia_aud_rec_cb rec_cb,
597 pjmedia_aud_play_cb play_cb,
598 void *user_data,
599 pjmedia_aud_stream **p_aud_strm)
600{
Benny Prijono10454dc2009-02-21 14:21:59 +0000601 pjmedia_aud_dev_factory *rec_f=NULL, *play_f=NULL, *f=NULL;
602 pjmedia_aud_param param;
Benny Prijono2cd64f82009-02-17 19:57:48 +0000603 pj_status_t status;
604
Benny Prijono10454dc2009-02-21 14:21:59 +0000605 PJ_ASSERT_RETURN(prm && prm->dir && p_aud_strm, PJ_EINVAL);
Benny Prijono96e74f32009-02-22 12:00:12 +0000606 PJ_ASSERT_RETURN(aud_subsys.pf, PJMEDIA_EAUD_INIT);
Benny Prijonod65f78c2009-06-03 18:59:37 +0000607 PJ_ASSERT_RETURN(prm->dir==PJMEDIA_DIR_CAPTURE ||
608 prm->dir==PJMEDIA_DIR_PLAYBACK ||
609 prm->dir==PJMEDIA_DIR_CAPTURE_PLAYBACK,
610 PJ_EINVAL);
Benny Prijono10454dc2009-02-21 14:21:59 +0000611
Benny Prijono2cd64f82009-02-17 19:57:48 +0000612 /* Must make copy of param because we're changing device ID */
Benny Prijono10454dc2009-02-21 14:21:59 +0000613 pj_memcpy(&param, prm, sizeof(param));
Benny Prijono2cd64f82009-02-17 19:57:48 +0000614
Benny Prijono10454dc2009-02-21 14:21:59 +0000615 /* Normalize rec_id */
616 if (param.dir & PJMEDIA_DIR_CAPTURE) {
617 unsigned index;
618
Benny Prijono96e74f32009-02-22 12:00:12 +0000619 if (param.rec_id < 0)
620 param.rec_id = PJMEDIA_AUD_DEFAULT_CAPTURE_DEV;
621
Benny Prijono10454dc2009-02-21 14:21:59 +0000622 status = lookup_dev(param.rec_id, &rec_f, &index);
623 if (status != PJ_SUCCESS)
624 return status;
625
626 param.rec_id = index;
627 f = rec_f;
628 }
629
630 /* Normalize play_id */
631 if (param.dir & PJMEDIA_DIR_PLAYBACK) {
632 unsigned index;
633
Benny Prijono96e74f32009-02-22 12:00:12 +0000634 if (param.play_id < 0)
635 param.play_id = PJMEDIA_AUD_DEFAULT_PLAYBACK_DEV;
636
Benny Prijono10454dc2009-02-21 14:21:59 +0000637 status = lookup_dev(param.play_id, &play_f, &index);
638 if (status != PJ_SUCCESS)
639 return status;
640
641 param.play_id = index;
642 f = play_f;
Benny Prijono10454dc2009-02-21 14:21:59 +0000643 }
644
Benny Prijonod65f78c2009-06-03 18:59:37 +0000645 PJ_ASSERT_RETURN(f != NULL, PJ_EBUG);
646
Nanang Izzuddin6f7d5a12009-03-16 16:29:39 +0000647 /* For now, rec_id and play_id must belong to the same factory */
648 PJ_ASSERT_RETURN((param.dir != PJMEDIA_DIR_CAPTURE_PLAYBACK) ||
649 (rec_f == play_f),
650 PJMEDIA_EAUD_INVDEV);
651
Benny Prijono10454dc2009-02-21 14:21:59 +0000652 /* Create the stream */
Benny Prijonoe3ebd552009-02-18 20:14:15 +0000653 status = f->op->create_stream(f, &param, rec_cb, play_cb,
654 user_data, p_aud_strm);
Benny Prijono2cd64f82009-02-17 19:57:48 +0000655 if (status != PJ_SUCCESS)
656 return status;
657
Benny Prijono10454dc2009-02-21 14:21:59 +0000658 /* Assign factory id to the stream */
Benny Prijono96e74f32009-02-22 12:00:12 +0000659 (*p_aud_strm)->sys.drv_idx = f->sys.drv_idx;
Benny Prijono2cd64f82009-02-17 19:57:48 +0000660 return PJ_SUCCESS;
661}
662
663/* API: Get the running parameters for the specified audio stream. */
664PJ_DEF(pj_status_t) pjmedia_aud_stream_get_param(pjmedia_aud_stream *strm,
Benny Prijono10454dc2009-02-21 14:21:59 +0000665 pjmedia_aud_param *param)
Benny Prijono2cd64f82009-02-17 19:57:48 +0000666{
667 pj_status_t status;
668
Benny Prijono96e74f32009-02-22 12:00:12 +0000669 PJ_ASSERT_RETURN(strm && param, PJ_EINVAL);
670 PJ_ASSERT_RETURN(aud_subsys.pf, PJMEDIA_EAUD_INIT);
671
Benny Prijono2cd64f82009-02-17 19:57:48 +0000672 status = strm->op->get_param(strm, param);
673 if (status != PJ_SUCCESS)
674 return status;
675
676 /* Normalize device id's */
Benny Prijono96e74f32009-02-22 12:00:12 +0000677 make_global_index(strm->sys.drv_idx, &param->rec_id);
678 make_global_index(strm->sys.drv_idx, &param->play_id);
Benny Prijono2cd64f82009-02-17 19:57:48 +0000679
680 return PJ_SUCCESS;
681}
682
683/* API: Get the value of a specific capability of the audio stream. */
684PJ_DEF(pj_status_t) pjmedia_aud_stream_get_cap(pjmedia_aud_stream *strm,
685 pjmedia_aud_dev_cap cap,
686 void *value)
687{
688 return strm->op->get_cap(strm, cap, value);
689}
690
691/* API: Set the value of a specific capability of the audio stream. */
692PJ_DEF(pj_status_t) pjmedia_aud_stream_set_cap(pjmedia_aud_stream *strm,
693 pjmedia_aud_dev_cap cap,
694 const void *value)
695{
696 return strm->op->set_cap(strm, cap, value);
697}
698
699/* API: Start the stream. */
700PJ_DEF(pj_status_t) pjmedia_aud_stream_start(pjmedia_aud_stream *strm)
701{
702 return strm->op->start(strm);
703}
704
705/* API: Stop the stream. */
706PJ_DEF(pj_status_t) pjmedia_aud_stream_stop(pjmedia_aud_stream *strm)
707{
708 return strm->op->stop(strm);
709}
710
711/* API: Destroy the stream. */
712PJ_DEF(pj_status_t) pjmedia_aud_stream_destroy(pjmedia_aud_stream *strm)
713{
714 return strm->op->destroy(strm);
715}
716
717