blob: 968ae0cdb8bc04707bdd70fd408b11650a9c41d3 [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>
21#include <pj/errno.h>
Benny Prijono10454dc2009-02-21 14:21:59 +000022#include <pj/log.h>
Benny Prijono2058f472009-02-22 17:15:34 +000023#include <pj/pool.h>
Benny Prijono10454dc2009-02-21 14:21:59 +000024#include <pj/string.h>
25
26#define THIS_FILE "audiodev.c"
27
Benny Prijono8eeab0b2009-03-04 19:00:28 +000028#define DEFINE_CAP(name, info) {name, info}
29
Benny Prijono10454dc2009-02-21 14:21:59 +000030/* Capability names */
31static struct cap_info
32{
33 const char *name;
34 const char *info;
35} cap_infos[] =
36{
Benny Prijono8eeab0b2009-03-04 19:00:28 +000037 DEFINE_CAP("ext-fmt", "Extended/non-PCM format"),
38 DEFINE_CAP("latency-in", "Input latency/buffer size setting"),
39 DEFINE_CAP("latency-out", "Output latency/buffer size setting"),
40 DEFINE_CAP("vol-in", "Input volume setting"),
41 DEFINE_CAP("vol-out", "Output volume setting"),
42 DEFINE_CAP("meter-in", "Input meter"),
43 DEFINE_CAP("meter-out", "Output meter"),
44 DEFINE_CAP("route-in", "Input routing"),
45 DEFINE_CAP("route-out", "Output routing"),
46 DEFINE_CAP("aec", "Accoustic echo cancellation"),
47 DEFINE_CAP("aec-tail", "Tail length setting for AEC"),
48 DEFINE_CAP("vad", "Voice activity detection"),
49 DEFINE_CAP("cng", "Comfort noise generation"),
50 DEFINE_CAP("plg", "Packet loss concealment")
Benny Prijono10454dc2009-02-21 14:21:59 +000051};
52
Benny Prijono2cd64f82009-02-17 19:57:48 +000053
Benny Prijono598b01d2009-02-18 13:55:03 +000054/*
Benny Prijono10454dc2009-02-21 14:21:59 +000055 * The device index seen by application and driver is different.
Benny Prijono598b01d2009-02-18 13:55:03 +000056 *
Benny Prijono10454dc2009-02-21 14:21:59 +000057 * At application level, device index is index to global list of device.
58 * At driver level, device index is index to device list on that particular
59 * factory only.
Benny Prijono598b01d2009-02-18 13:55:03 +000060 */
Benny Prijonoe3ebd552009-02-18 20:14:15 +000061#define MAKE_DEV_ID(f_id, index) (((f_id & 0xFFFF) << 16) | (index & 0xFFFF))
Benny Prijono2cd64f82009-02-17 19:57:48 +000062#define GET_INDEX(dev_id) ((dev_id) & 0xFFFF)
63#define GET_FID(dev_id) ((dev_id) >> 16)
Benny Prijono598b01d2009-02-18 13:55:03 +000064#define DEFAULT_DEV_ID 0
Benny Prijono2cd64f82009-02-17 19:57:48 +000065
66
Benny Prijono598b01d2009-02-18 13:55:03 +000067/* extern functions to create factories */
Benny Prijono2058f472009-02-22 17:15:34 +000068#if PJMEDIA_AUDIO_DEV_HAS_PORTAUDIO
Benny Prijono2cd64f82009-02-17 19:57:48 +000069pjmedia_aud_dev_factory* pjmedia_pa_factory(pj_pool_factory *pf);
Benny Prijono2058f472009-02-22 17:15:34 +000070#endif
71
72#if PJMEDIA_AUDIO_DEV_HAS_WMME
Benny Prijono598b01d2009-02-18 13:55:03 +000073pjmedia_aud_dev_factory* pjmedia_wmme_factory(pj_pool_factory *pf);
Benny Prijono2058f472009-02-22 17:15:34 +000074#endif
75
Nanang Izzuddina940b362009-02-23 13:53:30 +000076#if PJMEDIA_AUDIO_DEV_HAS_SYMB_APS
77pjmedia_aud_dev_factory* pjmedia_aps_factory(pj_pool_factory *pf);
78#endif
79
80#if PJMEDIA_AUDIO_DEV_HAS_SYMB_MDA
81pjmedia_aud_dev_factory* pjmedia_symb_mda_factory(pj_pool_factory *pf);
82#endif
Benny Prijono598b01d2009-02-18 13:55:03 +000083
Benny Prijonoe3ebd552009-02-18 20:14:15 +000084#define MAX_DRIVERS 16
Benny Prijono10454dc2009-02-21 14:21:59 +000085#define MAX_DEVS 64
86
87/* typedef for factory creation function */
88typedef pjmedia_aud_dev_factory* (*create_func_ptr)(pj_pool_factory*);
89
90/* driver structure */
91struct driver
92{
93 create_func_ptr create; /* Creation function. */
94 pjmedia_aud_dev_factory *f; /* Factory instance. */
95 char name[32]; /* Driver name */
96 unsigned dev_cnt; /* Number of devices */
97 unsigned start_idx; /* Start index in global list */
Benny Prijono96e74f32009-02-22 12:00:12 +000098 int rec_dev_idx;/* Default capture device. */
99 int play_dev_idx;/* Default playback device */
100 int dev_idx; /* Default device. */
Benny Prijono10454dc2009-02-21 14:21:59 +0000101};
Benny Prijono2cd64f82009-02-17 19:57:48 +0000102
Benny Prijonoe3ebd552009-02-18 20:14:15 +0000103/* The audio subsystem */
104static struct aud_subsys
Benny Prijono2cd64f82009-02-17 19:57:48 +0000105{
Benny Prijono10454dc2009-02-21 14:21:59 +0000106 unsigned init_count; /* How many times init() is called */
107 pj_pool_factory *pf; /* The pool factory. */
Benny Prijono2cd64f82009-02-17 19:57:48 +0000108
Benny Prijono10454dc2009-02-21 14:21:59 +0000109 unsigned drv_cnt; /* Number of drivers. */
110 struct driver drv[MAX_DRIVERS]; /* Array of drivers. */
Benny Prijonoe3ebd552009-02-18 20:14:15 +0000111
Benny Prijono10454dc2009-02-21 14:21:59 +0000112 unsigned dev_cnt; /* Total number of devices. */
113 pj_uint32_t dev_list[MAX_DEVS];/* Array of device IDs. */
Benny Prijonoe3ebd552009-02-18 20:14:15 +0000114
115} aud_subsys;
116
Benny Prijono2cd64f82009-02-17 19:57:48 +0000117
Benny Prijono96e74f32009-02-22 12:00:12 +0000118/* Internal: init driver */
119static pj_status_t init_driver(unsigned drv_idx)
120{
121 struct driver *drv = &aud_subsys.drv[drv_idx];
122 pjmedia_aud_dev_factory *f;
123 unsigned i, dev_cnt;
124 pj_status_t status;
125
126 /* Create the factory */
127 f = (*drv->create)(aud_subsys.pf);
128 if (!f)
129 return PJ_EUNKNOWN;
130
131 /* Call factory->init() */
132 status = f->op->init(f);
133 if (status != PJ_SUCCESS) {
134 f->op->destroy(f);
135 return status;
136 }
137
138 /* Get number of devices */
139 dev_cnt = f->op->get_dev_count(f);
140 if (dev_cnt + aud_subsys.dev_cnt > MAX_DEVS) {
141 PJ_LOG(4,(THIS_FILE, "%d device(s) cannot be registered because"
142 " there are too many devices",
143 aud_subsys.dev_cnt + dev_cnt - MAX_DEVS));
144 dev_cnt = MAX_DEVS - aud_subsys.dev_cnt;
145 }
146 if (dev_cnt == 0) {
147 f->op->destroy(f);
148 return PJMEDIA_EAUD_NODEV;
149 }
150
151 /* Fill in default devices */
152 drv->play_dev_idx = drv->rec_dev_idx = drv->dev_idx = -1;
153 for (i=0; i<dev_cnt; ++i) {
154 pjmedia_aud_dev_info info;
155
156 status = f->op->get_dev_info(f, i, &info);
157 if (status != PJ_SUCCESS) {
158 f->op->destroy(f);
159 return status;
160 }
161
162 if (drv->name[0]=='\0') {
163 /* Set driver name */
164 pj_ansi_strncpy(drv->name, info.driver, sizeof(drv->name));
165 drv->name[sizeof(drv->name)-1] = '\0';
166 }
167
168 if (drv->play_dev_idx < 0 && info.output_count) {
169 /* Set default playback device */
170 drv->play_dev_idx = i;
171 }
172 if (drv->rec_dev_idx < 0 && info.input_count) {
173 /* Set default capture device */
174 drv->rec_dev_idx = i;
175 }
176 if (drv->dev_idx < 0 && info.input_count &&
177 info.output_count)
178 {
179 /* Set default capture and playback device */
180 drv->dev_idx = i;
181 }
182
183 if (drv->play_dev_idx >= 0 && drv->rec_dev_idx >= 0 &&
184 drv->dev_idx >= 0)
185 {
186 /* Done. */
187 break;
188 }
189 }
190
191 /* Register the factory */
192 drv->f = f;
193 drv->f->sys.drv_idx = drv_idx;
194 drv->start_idx = aud_subsys.dev_cnt;
195 drv->dev_cnt = dev_cnt;
196
197 /* Register devices to global list */
198 for (i=0; i<dev_cnt; ++i) {
199 aud_subsys.dev_list[aud_subsys.dev_cnt++] = MAKE_DEV_ID(drv_idx, i);
200 }
201
202 return PJ_SUCCESS;
203}
204
205/* Internal: deinit driver */
206static void deinit_driver(unsigned drv_idx)
207{
208 struct driver *drv = &aud_subsys.drv[drv_idx];
209
210 if (drv->f) {
211 drv->f->op->destroy(drv->f);
212 drv->f = NULL;
213 }
214
215 drv->dev_cnt = 0;
216 drv->play_dev_idx = drv->rec_dev_idx = drv->dev_idx = -1;
217}
Benny Prijono2cd64f82009-02-17 19:57:48 +0000218
219/* API: Initialize the audio subsystem. */
220PJ_DEF(pj_status_t) pjmedia_aud_subsys_init(pj_pool_factory *pf)
221{
222 unsigned i;
223 pj_status_t status = PJ_ENOMEM;
224
Benny Prijono555139d2009-02-19 12:08:19 +0000225 /* Allow init() to be called multiple times as long as there is matching
226 * number of shutdown().
227 */
228 if (aud_subsys.init_count++ != 0) {
229 return PJ_SUCCESS;
230 }
231
Benny Prijono8eeab0b2009-03-04 19:00:28 +0000232 /* Register error subsystem */
233 pj_register_strerror(PJMEDIA_AUDIODEV_ERRNO_START,
234 PJ_ERRNO_SPACE_SIZE,
235 &pjmedia_audiodev_strerror);
236
237 /* Init */
Benny Prijonoe3ebd552009-02-18 20:14:15 +0000238 aud_subsys.pf = pf;
Benny Prijono10454dc2009-02-21 14:21:59 +0000239 aud_subsys.drv_cnt = 0;
240 aud_subsys.dev_cnt = 0;
Benny Prijono2cd64f82009-02-17 19:57:48 +0000241
Benny Prijono10454dc2009-02-21 14:21:59 +0000242 /* Register creation functions */
Benny Prijono2058f472009-02-22 17:15:34 +0000243#if PJMEDIA_AUDIO_DEV_HAS_PORTAUDIO
Benny Prijono10454dc2009-02-21 14:21:59 +0000244 aud_subsys.drv[aud_subsys.drv_cnt++].create = &pjmedia_pa_factory;
Benny Prijono2058f472009-02-22 17:15:34 +0000245#endif
246#if PJMEDIA_AUDIO_DEV_HAS_WMME
Benny Prijono10454dc2009-02-21 14:21:59 +0000247 aud_subsys.drv[aud_subsys.drv_cnt++].create = &pjmedia_wmme_factory;
Benny Prijono2058f472009-02-22 17:15:34 +0000248#endif
Nanang Izzuddina940b362009-02-23 13:53:30 +0000249#if PJMEDIA_AUDIO_DEV_HAS_SYMB_APS
250 aud_subsys.drv[aud_subsys.drv_cnt++].create = &pjmedia_aps_factory;
251#endif
252#if PJMEDIA_AUDIO_DEV_HAS_SYMB_MDA
253 aud_subsys.drv[aud_subsys.drv_cnt++].create = &pjmedia_symb_mda_factory;
254#endif
Benny Prijonoe3ebd552009-02-18 20:14:15 +0000255
Benny Prijono10454dc2009-02-21 14:21:59 +0000256 /* Initialize each factory and build the device ID list */
257 for (i=0; i<aud_subsys.drv_cnt; ++i) {
Benny Prijono96e74f32009-02-22 12:00:12 +0000258 status = init_driver(i);
Benny Prijono2cd64f82009-02-17 19:57:48 +0000259 if (status != PJ_SUCCESS) {
Benny Prijono96e74f32009-02-22 12:00:12 +0000260 deinit_driver(i);
Benny Prijonoe3ebd552009-02-18 20:14:15 +0000261 continue;
Benny Prijono2cd64f82009-02-17 19:57:48 +0000262 }
Benny Prijono2cd64f82009-02-17 19:57:48 +0000263 }
264
Benny Prijono96e74f32009-02-22 12:00:12 +0000265 return aud_subsys.dev_cnt ? PJ_SUCCESS : status;
Benny Prijonoe3ebd552009-02-18 20:14:15 +0000266}
267
268/* API: get the pool factory registered to the audio subsystem. */
269PJ_DEF(pj_pool_factory*) pjmedia_aud_subsys_get_pool_factory(void)
270{
271 return aud_subsys.pf;
Benny Prijono2cd64f82009-02-17 19:57:48 +0000272}
273
274/* API: Shutdown the audio subsystem. */
275PJ_DEF(pj_status_t) pjmedia_aud_subsys_shutdown(void)
276{
277 unsigned i;
278
Benny Prijono555139d2009-02-19 12:08:19 +0000279 /* Allow shutdown() to be called multiple times as long as there is matching
280 * number of init().
281 */
282 if (aud_subsys.init_count == 0) {
283 return PJ_SUCCESS;
284 }
285 --aud_subsys.init_count;
286
Benny Prijono10454dc2009-02-21 14:21:59 +0000287 for (i=0; i<aud_subsys.drv_cnt; ++i) {
Benny Prijono96e74f32009-02-22 12:00:12 +0000288 deinit_driver(i);
Benny Prijono2cd64f82009-02-17 19:57:48 +0000289 }
290
Benny Prijono96e74f32009-02-22 12:00:12 +0000291 aud_subsys.pf = NULL;
Benny Prijono2cd64f82009-02-17 19:57:48 +0000292 return PJ_SUCCESS;
293}
294
Benny Prijono10454dc2009-02-21 14:21:59 +0000295/* API: get capability name/info */
296PJ_DEF(const char*) pjmedia_aud_dev_cap_name(pjmedia_aud_dev_cap cap,
297 const char **p_desc)
298{
Benny Prijono689f8a82009-02-24 08:23:36 +0000299 const char *desc;
Benny Prijono10454dc2009-02-21 14:21:59 +0000300 unsigned i;
301
302 if (p_desc==NULL) p_desc = &desc;
303
304 for (i=0; i<PJ_ARRAY_SIZE(cap_infos); ++i) {
305 if ((1 << i)==cap)
306 break;
307 }
308
309 if (i==32) {
310 *p_desc = "??";
311 return "??";
312 }
313
314 *p_desc = cap_infos[i].info;
315 return cap_infos[i].name;
316}
317
Benny Prijono2cd64f82009-02-17 19:57:48 +0000318/* API: Get the number of sound devices installed in the system. */
319PJ_DEF(unsigned) pjmedia_aud_dev_count(void)
320{
Benny Prijono10454dc2009-02-21 14:21:59 +0000321 return aud_subsys.dev_cnt;
Benny Prijono2cd64f82009-02-17 19:57:48 +0000322}
323
Benny Prijono96e74f32009-02-22 12:00:12 +0000324/* Internal: convert local index to global device index */
325static pj_status_t make_global_index(unsigned drv_idx,
326 pjmedia_aud_dev_index *id)
327{
328 if (*id < 0) {
329 return PJ_SUCCESS;
330 }
331
332 /* Check that factory still exists */
333 PJ_ASSERT_RETURN(aud_subsys.drv[drv_idx].f, PJ_EBUG);
334
335 /* Check that device index is valid */
336 PJ_ASSERT_RETURN(*id>=0 && *id<(int)aud_subsys.drv[drv_idx].dev_cnt,
337 PJ_EBUG);
338
339 *id += aud_subsys.drv[drv_idx].start_idx;
340 return PJ_SUCCESS;
341}
342
Benny Prijono10454dc2009-02-21 14:21:59 +0000343/* Internal: lookup device id */
344static pj_status_t lookup_dev(pjmedia_aud_dev_index id,
345 pjmedia_aud_dev_factory **p_f,
346 unsigned *p_local_index)
Benny Prijono2cd64f82009-02-17 19:57:48 +0000347{
Benny Prijono10454dc2009-02-21 14:21:59 +0000348 int f_id, index;
Benny Prijono2cd64f82009-02-17 19:57:48 +0000349
Benny Prijono96e74f32009-02-22 12:00:12 +0000350 if (id < 0) {
351 unsigned i;
Benny Prijono2cd64f82009-02-17 19:57:48 +0000352
Benny Prijono96e74f32009-02-22 12:00:12 +0000353 if (id == PJMEDIA_AUD_INVALID_DEV)
354 return PJMEDIA_EAUD_INVDEV;
355
356 for (i=0; i<aud_subsys.drv_cnt; ++i) {
357 struct driver *drv = &aud_subsys.drv[i];
358 if (drv->dev_idx >= 0) {
359 id = drv->dev_idx;
360 make_global_index(i, &id);
361 break;
362 } else if (id==PJMEDIA_AUD_DEFAULT_CAPTURE_DEV &&
363 drv->rec_dev_idx >= 0)
364 {
365 id = drv->rec_dev_idx;
366 make_global_index(i, &id);
367 break;
368 } else if (id==PJMEDIA_AUD_DEFAULT_PLAYBACK_DEV &&
369 drv->play_dev_idx >= 0)
370 {
371 id = drv->play_dev_idx;
372 make_global_index(i, &id);
373 break;
374 }
375 }
376
377 if (id < 0) {
378 return PJMEDIA_EAUD_NODEFDEV;
379 }
380 }
Benny Prijono2cd64f82009-02-17 19:57:48 +0000381
Benny Prijono10454dc2009-02-21 14:21:59 +0000382 f_id = GET_FID(aud_subsys.dev_list[id]);
383 index = GET_INDEX(aud_subsys.dev_list[id]);
Benny Prijono2cd64f82009-02-17 19:57:48 +0000384
Benny Prijono10454dc2009-02-21 14:21:59 +0000385 if (f_id < 0 || f_id >= (int)aud_subsys.drv_cnt)
386 return PJMEDIA_EAUD_INVDEV;
387
388 if (index < 0 || index >= (int)aud_subsys.drv[f_id].dev_cnt)
389 return PJMEDIA_EAUD_INVDEV;
390
391 *p_f = aud_subsys.drv[f_id].f;
392 *p_local_index = (unsigned)index;
393
394 return PJ_SUCCESS;
395
Benny Prijono2cd64f82009-02-17 19:57:48 +0000396}
397
Benny Prijono2cd64f82009-02-17 19:57:48 +0000398/* API: Get device information. */
Benny Prijono10454dc2009-02-21 14:21:59 +0000399PJ_DEF(pj_status_t) pjmedia_aud_dev_get_info(pjmedia_aud_dev_index id,
Benny Prijono2cd64f82009-02-17 19:57:48 +0000400 pjmedia_aud_dev_info *info)
401{
Benny Prijonoe3ebd552009-02-18 20:14:15 +0000402 pjmedia_aud_dev_factory *f;
Benny Prijono10454dc2009-02-21 14:21:59 +0000403 unsigned index;
404 pj_status_t status;
Benny Prijono2cd64f82009-02-17 19:57:48 +0000405
Benny Prijono96e74f32009-02-22 12:00:12 +0000406 PJ_ASSERT_RETURN(info && id!=PJMEDIA_AUD_INVALID_DEV, PJ_EINVAL);
407 PJ_ASSERT_RETURN(aud_subsys.pf, PJMEDIA_EAUD_INIT);
Benny Prijono598b01d2009-02-18 13:55:03 +0000408
Benny Prijono10454dc2009-02-21 14:21:59 +0000409 status = lookup_dev(id, &f, &index);
410 if (status != PJ_SUCCESS)
411 return status;
Benny Prijono2cd64f82009-02-17 19:57:48 +0000412
Benny Prijonoe3ebd552009-02-18 20:14:15 +0000413 return f->op->get_dev_info(f, index, info);
Benny Prijono2cd64f82009-02-17 19:57:48 +0000414}
415
Benny Prijono10454dc2009-02-21 14:21:59 +0000416/* API: find device */
417PJ_DEF(pj_status_t) pjmedia_aud_dev_lookup( const char *drv_name,
418 const char *dev_name,
419 pjmedia_aud_dev_index *id)
420{
421 pjmedia_aud_dev_factory *f = NULL;
Benny Prijono96e74f32009-02-22 12:00:12 +0000422 unsigned drv_idx, dev_idx;
Benny Prijono10454dc2009-02-21 14:21:59 +0000423
424 PJ_ASSERT_RETURN(drv_name && dev_name && id, PJ_EINVAL);
Benny Prijono96e74f32009-02-22 12:00:12 +0000425 PJ_ASSERT_RETURN(aud_subsys.pf, PJMEDIA_EAUD_INIT);
Benny Prijono10454dc2009-02-21 14:21:59 +0000426
Benny Prijono96e74f32009-02-22 12:00:12 +0000427 for (drv_idx=0; drv_idx<aud_subsys.drv_cnt; ++drv_idx) {
428 if (!pj_ansi_stricmp(drv_name, aud_subsys.drv[drv_idx].name)) {
429 f = aud_subsys.drv[drv_idx].f;
Benny Prijono10454dc2009-02-21 14:21:59 +0000430 break;
431 }
432 }
433
434 if (!f)
435 return PJ_ENOTFOUND;
436
Benny Prijono96e74f32009-02-22 12:00:12 +0000437 for (dev_idx=0; dev_idx<aud_subsys.drv[drv_idx].dev_cnt; ++dev_idx) {
Benny Prijono10454dc2009-02-21 14:21:59 +0000438 pjmedia_aud_dev_info info;
439 pj_status_t status;
440
Benny Prijono96e74f32009-02-22 12:00:12 +0000441 status = f->op->get_dev_info(f, dev_idx, &info);
Benny Prijono10454dc2009-02-21 14:21:59 +0000442 if (status != PJ_SUCCESS)
443 return status;
444
445 if (!pj_ansi_stricmp(dev_name, info.name))
446 break;
447 }
448
Benny Prijono96e74f32009-02-22 12:00:12 +0000449 if (dev_idx==aud_subsys.drv[drv_idx].dev_cnt)
Benny Prijono10454dc2009-02-21 14:21:59 +0000450 return PJ_ENOTFOUND;
451
Benny Prijono96e74f32009-02-22 12:00:12 +0000452 *id = dev_idx;
453 make_global_index(drv_idx, id);
Benny Prijono10454dc2009-02-21 14:21:59 +0000454
455 return PJ_SUCCESS;
456}
457
Benny Prijono2cd64f82009-02-17 19:57:48 +0000458/* API: Initialize the audio device parameters with default values for the
459 * specified device.
460 */
Benny Prijono10454dc2009-02-21 14:21:59 +0000461PJ_DEF(pj_status_t) pjmedia_aud_dev_default_param(pjmedia_aud_dev_index id,
462 pjmedia_aud_param *param)
Benny Prijono2cd64f82009-02-17 19:57:48 +0000463{
Benny Prijonoe3ebd552009-02-18 20:14:15 +0000464 pjmedia_aud_dev_factory *f;
Benny Prijono10454dc2009-02-21 14:21:59 +0000465 unsigned index;
Benny Prijono2cd64f82009-02-17 19:57:48 +0000466 pj_status_t status;
467
Benny Prijono96e74f32009-02-22 12:00:12 +0000468 PJ_ASSERT_RETURN(param && id!=PJMEDIA_AUD_INVALID_DEV, PJ_EINVAL);
469 PJ_ASSERT_RETURN(aud_subsys.pf, PJMEDIA_EAUD_INIT);
Benny Prijono598b01d2009-02-18 13:55:03 +0000470
Benny Prijono10454dc2009-02-21 14:21:59 +0000471 status = lookup_dev(id, &f, &index);
472 if (status != PJ_SUCCESS)
473 return status;
Benny Prijono2cd64f82009-02-17 19:57:48 +0000474
Benny Prijonoe3ebd552009-02-18 20:14:15 +0000475 status = f->op->default_param(f, index, param);
Benny Prijono2cd64f82009-02-17 19:57:48 +0000476 if (status != PJ_SUCCESS)
477 return status;
478
479 /* Normalize device IDs */
Benny Prijono96e74f32009-02-22 12:00:12 +0000480 make_global_index(f->sys.drv_idx, &param->rec_id);
481 make_global_index(f->sys.drv_idx, &param->play_id);
Benny Prijono2cd64f82009-02-17 19:57:48 +0000482
483 return PJ_SUCCESS;
484}
485
486/* API: Open audio stream object using the specified parameters. */
Benny Prijono10454dc2009-02-21 14:21:59 +0000487PJ_DEF(pj_status_t) pjmedia_aud_stream_create(const pjmedia_aud_param *prm,
Benny Prijono2cd64f82009-02-17 19:57:48 +0000488 pjmedia_aud_rec_cb rec_cb,
489 pjmedia_aud_play_cb play_cb,
490 void *user_data,
491 pjmedia_aud_stream **p_aud_strm)
492{
Benny Prijono10454dc2009-02-21 14:21:59 +0000493 pjmedia_aud_dev_factory *rec_f=NULL, *play_f=NULL, *f=NULL;
494 pjmedia_aud_param param;
Benny Prijono2cd64f82009-02-17 19:57:48 +0000495 pj_status_t status;
496
Benny Prijono10454dc2009-02-21 14:21:59 +0000497 PJ_ASSERT_RETURN(prm && prm->dir && p_aud_strm, PJ_EINVAL);
Benny Prijono96e74f32009-02-22 12:00:12 +0000498 PJ_ASSERT_RETURN(aud_subsys.pf, PJMEDIA_EAUD_INIT);
Benny Prijono10454dc2009-02-21 14:21:59 +0000499
Benny Prijono2cd64f82009-02-17 19:57:48 +0000500 /* Must make copy of param because we're changing device ID */
Benny Prijono10454dc2009-02-21 14:21:59 +0000501 pj_memcpy(&param, prm, sizeof(param));
Benny Prijono2cd64f82009-02-17 19:57:48 +0000502
Benny Prijono10454dc2009-02-21 14:21:59 +0000503 /* Normalize rec_id */
504 if (param.dir & PJMEDIA_DIR_CAPTURE) {
505 unsigned index;
506
Benny Prijono96e74f32009-02-22 12:00:12 +0000507 if (param.rec_id < 0)
508 param.rec_id = PJMEDIA_AUD_DEFAULT_CAPTURE_DEV;
509
Benny Prijono10454dc2009-02-21 14:21:59 +0000510 status = lookup_dev(param.rec_id, &rec_f, &index);
511 if (status != PJ_SUCCESS)
512 return status;
513
514 param.rec_id = index;
515 f = rec_f;
516 }
517
518 /* Normalize play_id */
519 if (param.dir & PJMEDIA_DIR_PLAYBACK) {
520 unsigned index;
521
Benny Prijono96e74f32009-02-22 12:00:12 +0000522 if (param.play_id < 0)
523 param.play_id = PJMEDIA_AUD_DEFAULT_PLAYBACK_DEV;
524
Benny Prijono10454dc2009-02-21 14:21:59 +0000525 status = lookup_dev(param.play_id, &play_f, &index);
526 if (status != PJ_SUCCESS)
527 return status;
528
529 param.play_id = index;
530 f = play_f;
531
532 /* For now, rec_id and play_id must belong to the same factory */
Benny Prijono96e74f32009-02-22 12:00:12 +0000533 PJ_ASSERT_RETURN(rec_f == play_f, PJMEDIA_EAUD_INVDEV);
Benny Prijono10454dc2009-02-21 14:21:59 +0000534 }
535
Benny Prijono2cd64f82009-02-17 19:57:48 +0000536
Benny Prijono10454dc2009-02-21 14:21:59 +0000537 /* Create the stream */
Benny Prijonoe3ebd552009-02-18 20:14:15 +0000538 status = f->op->create_stream(f, &param, rec_cb, play_cb,
539 user_data, p_aud_strm);
Benny Prijono2cd64f82009-02-17 19:57:48 +0000540 if (status != PJ_SUCCESS)
541 return status;
542
Benny Prijono10454dc2009-02-21 14:21:59 +0000543 /* Assign factory id to the stream */
Benny Prijono96e74f32009-02-22 12:00:12 +0000544 (*p_aud_strm)->sys.drv_idx = f->sys.drv_idx;
Benny Prijono2cd64f82009-02-17 19:57:48 +0000545 return PJ_SUCCESS;
546}
547
548/* API: Get the running parameters for the specified audio stream. */
549PJ_DEF(pj_status_t) pjmedia_aud_stream_get_param(pjmedia_aud_stream *strm,
Benny Prijono10454dc2009-02-21 14:21:59 +0000550 pjmedia_aud_param *param)
Benny Prijono2cd64f82009-02-17 19:57:48 +0000551{
552 pj_status_t status;
553
Benny Prijono96e74f32009-02-22 12:00:12 +0000554 PJ_ASSERT_RETURN(strm && param, PJ_EINVAL);
555 PJ_ASSERT_RETURN(aud_subsys.pf, PJMEDIA_EAUD_INIT);
556
Benny Prijono2cd64f82009-02-17 19:57:48 +0000557 status = strm->op->get_param(strm, param);
558 if (status != PJ_SUCCESS)
559 return status;
560
561 /* Normalize device id's */
Benny Prijono96e74f32009-02-22 12:00:12 +0000562 make_global_index(strm->sys.drv_idx, &param->rec_id);
563 make_global_index(strm->sys.drv_idx, &param->play_id);
Benny Prijono2cd64f82009-02-17 19:57:48 +0000564
565 return PJ_SUCCESS;
566}
567
568/* API: Get the value of a specific capability of the audio stream. */
569PJ_DEF(pj_status_t) pjmedia_aud_stream_get_cap(pjmedia_aud_stream *strm,
570 pjmedia_aud_dev_cap cap,
571 void *value)
572{
573 return strm->op->get_cap(strm, cap, value);
574}
575
576/* API: Set the value of a specific capability of the audio stream. */
577PJ_DEF(pj_status_t) pjmedia_aud_stream_set_cap(pjmedia_aud_stream *strm,
578 pjmedia_aud_dev_cap cap,
579 const void *value)
580{
581 return strm->op->set_cap(strm, cap, value);
582}
583
584/* API: Start the stream. */
585PJ_DEF(pj_status_t) pjmedia_aud_stream_start(pjmedia_aud_stream *strm)
586{
587 return strm->op->start(strm);
588}
589
590/* API: Stop the stream. */
591PJ_DEF(pj_status_t) pjmedia_aud_stream_stop(pjmedia_aud_stream *strm)
592{
593 return strm->op->stop(strm);
594}
595
596/* API: Destroy the stream. */
597PJ_DEF(pj_status_t) pjmedia_aud_stream_destroy(pjmedia_aud_stream *strm)
598{
599 return strm->op->destroy(strm);
600}
601
602