blob: 33e333ccfbe1e2970fcbc00c3acde861f1b284ea [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>
23#include <pj/string.h>
24
25#define THIS_FILE "audiodev.c"
26
27/* Capability names */
28static struct cap_info
29{
30 const char *name;
31 const char *info;
32} cap_infos[] =
33{
34 {"ext-fmt", "Extended/non-PCM format"},
35 {"latency-in", "Input latency/buffer size setting"},
36 {"latency-out", "Output latency/buffer size setting"},
37 {"vol-in", "Input volume setting"},
38 {"vol-out", "Output volume setting"},
39 {"meter-in", "Input meter"},
40 {"meter-out", "Output meter"},
41 {"route-in", "Input routing"},
42 {"route-out", "Output routing"},
43 {"aec", "Accoustic echo cancellation"},
44 {"aec-tail", "Tail length setting for AEC"},
45 {"vad", "Voice activity detection"},
46 {"cng", "Comfort noise generation"},
47 {"plg", "Packet loss concealment"}
48};
49
Benny Prijono2cd64f82009-02-17 19:57:48 +000050
Benny Prijono598b01d2009-02-18 13:55:03 +000051/*
Benny Prijono10454dc2009-02-21 14:21:59 +000052 * The device index seen by application and driver is different.
Benny Prijono598b01d2009-02-18 13:55:03 +000053 *
Benny Prijono10454dc2009-02-21 14:21:59 +000054 * At application level, device index is index to global list of device.
55 * At driver level, device index is index to device list on that particular
56 * factory only.
Benny Prijono598b01d2009-02-18 13:55:03 +000057 */
Benny Prijonoe3ebd552009-02-18 20:14:15 +000058#define MAKE_DEV_ID(f_id, index) (((f_id & 0xFFFF) << 16) | (index & 0xFFFF))
Benny Prijono2cd64f82009-02-17 19:57:48 +000059#define GET_INDEX(dev_id) ((dev_id) & 0xFFFF)
60#define GET_FID(dev_id) ((dev_id) >> 16)
Benny Prijono598b01d2009-02-18 13:55:03 +000061#define DEFAULT_DEV_ID 0
Benny Prijono2cd64f82009-02-17 19:57:48 +000062
63
Benny Prijono598b01d2009-02-18 13:55:03 +000064/* extern functions to create factories */
Benny Prijono2cd64f82009-02-17 19:57:48 +000065pjmedia_aud_dev_factory* pjmedia_pa_factory(pj_pool_factory *pf);
Benny Prijono598b01d2009-02-18 13:55:03 +000066pjmedia_aud_dev_factory* pjmedia_wmme_factory(pj_pool_factory *pf);
67
Benny Prijonoe3ebd552009-02-18 20:14:15 +000068#define MAX_DRIVERS 16
Benny Prijono10454dc2009-02-21 14:21:59 +000069#define MAX_DEVS 64
70
71/* typedef for factory creation function */
72typedef pjmedia_aud_dev_factory* (*create_func_ptr)(pj_pool_factory*);
73
74/* driver structure */
75struct driver
76{
77 create_func_ptr create; /* Creation function. */
78 pjmedia_aud_dev_factory *f; /* Factory instance. */
79 char name[32]; /* Driver name */
80 unsigned dev_cnt; /* Number of devices */
81 unsigned start_idx; /* Start index in global list */
82};
Benny Prijono2cd64f82009-02-17 19:57:48 +000083
Benny Prijonoe3ebd552009-02-18 20:14:15 +000084/* The audio subsystem */
85static struct aud_subsys
Benny Prijono2cd64f82009-02-17 19:57:48 +000086{
Benny Prijono10454dc2009-02-21 14:21:59 +000087 unsigned init_count; /* How many times init() is called */
88 pj_pool_factory *pf; /* The pool factory. */
Benny Prijono2cd64f82009-02-17 19:57:48 +000089
Benny Prijono10454dc2009-02-21 14:21:59 +000090 unsigned drv_cnt; /* Number of drivers. */
91 struct driver drv[MAX_DRIVERS]; /* Array of drivers. */
Benny Prijonoe3ebd552009-02-18 20:14:15 +000092
Benny Prijono10454dc2009-02-21 14:21:59 +000093 unsigned dev_cnt; /* Total number of devices. */
94 pj_uint32_t dev_list[MAX_DEVS];/* Array of device IDs. */
Benny Prijonoe3ebd552009-02-18 20:14:15 +000095
96} aud_subsys;
97
Benny Prijono2cd64f82009-02-17 19:57:48 +000098
99
100/* API: Initialize the audio subsystem. */
101PJ_DEF(pj_status_t) pjmedia_aud_subsys_init(pj_pool_factory *pf)
102{
103 unsigned i;
104 pj_status_t status = PJ_ENOMEM;
105
Benny Prijono555139d2009-02-19 12:08:19 +0000106 /* Allow init() to be called multiple times as long as there is matching
107 * number of shutdown().
108 */
109 if (aud_subsys.init_count++ != 0) {
110 return PJ_SUCCESS;
111 }
112
Benny Prijonoe3ebd552009-02-18 20:14:15 +0000113 aud_subsys.pf = pf;
Benny Prijono10454dc2009-02-21 14:21:59 +0000114 aud_subsys.drv_cnt = 0;
115 aud_subsys.dev_cnt = 0;
Benny Prijono2cd64f82009-02-17 19:57:48 +0000116
Benny Prijono10454dc2009-02-21 14:21:59 +0000117 /* Register creation functions */
118 aud_subsys.drv[aud_subsys.drv_cnt++].create = &pjmedia_pa_factory;
119 aud_subsys.drv[aud_subsys.drv_cnt++].create = &pjmedia_wmme_factory;
Benny Prijonoe3ebd552009-02-18 20:14:15 +0000120
Benny Prijono10454dc2009-02-21 14:21:59 +0000121 /* Initialize each factory and build the device ID list */
122 for (i=0; i<aud_subsys.drv_cnt; ++i) {
Benny Prijonoe3ebd552009-02-18 20:14:15 +0000123 pjmedia_aud_dev_factory *f;
Benny Prijono10454dc2009-02-21 14:21:59 +0000124 pjmedia_aud_dev_info info;
125 unsigned j, dev_cnt;
Benny Prijonoe3ebd552009-02-18 20:14:15 +0000126
Benny Prijono10454dc2009-02-21 14:21:59 +0000127 /* Create the factory */
128 f = (*aud_subsys.drv[i].create)(pf);
Benny Prijonoe3ebd552009-02-18 20:14:15 +0000129 if (!f)
Benny Prijono2cd64f82009-02-17 19:57:48 +0000130 continue;
131
Benny Prijono10454dc2009-02-21 14:21:59 +0000132 /* Call factory->init() */
Benny Prijonoe3ebd552009-02-18 20:14:15 +0000133 status = f->op->init(f);
Benny Prijono2cd64f82009-02-17 19:57:48 +0000134 if (status != PJ_SUCCESS) {
Benny Prijonoe3ebd552009-02-18 20:14:15 +0000135 f->op->destroy(f);
136 continue;
Benny Prijono2cd64f82009-02-17 19:57:48 +0000137 }
138
Benny Prijono10454dc2009-02-21 14:21:59 +0000139 /* Build device list */
140 dev_cnt = f->op->get_dev_count(f);
141 if (dev_cnt == 0) {
142 f->op->destroy(f);
143 continue;
144 }
145
146 /* Get one device info */
147 status = f->op->get_dev_info(f, 0, &info);
148 if (status != PJ_SUCCESS) {
149 f->op->destroy(f);
150 continue;
151 }
152
153 /* Register the factory */
154 aud_subsys.drv[i].f = f;
155 aud_subsys.drv[i].f->internal.id = i;
156 aud_subsys.drv[i].start_idx = aud_subsys.dev_cnt;
157 pj_ansi_strncpy(aud_subsys.drv[i].name, info.driver,
158 sizeof(aud_subsys.drv[i].name));
159 aud_subsys.drv[i].name[sizeof(aud_subsys.drv[i].name)-1] = '\0';
160
161 /* Register devices */
162 if (aud_subsys.dev_cnt + dev_cnt > MAX_DEVS) {
163 PJ_LOG(4,(THIS_FILE, "%d device(s) cannot be registered because"
164 " there are too many sound devices",
165 aud_subsys.dev_cnt + dev_cnt - MAX_DEVS));
166 dev_cnt = MAX_DEVS - aud_subsys.dev_cnt;
167 }
168 for (j=0; j<dev_cnt; ++j) {
169 aud_subsys.dev_list[aud_subsys.dev_cnt++] = MAKE_DEV_ID(i, j);
170 }
171
Benny Prijono2cd64f82009-02-17 19:57:48 +0000172 }
173
Benny Prijono10454dc2009-02-21 14:21:59 +0000174 return aud_subsys.drv_cnt ? PJ_SUCCESS : status;
Benny Prijonoe3ebd552009-02-18 20:14:15 +0000175}
176
177/* API: get the pool factory registered to the audio subsystem. */
178PJ_DEF(pj_pool_factory*) pjmedia_aud_subsys_get_pool_factory(void)
179{
180 return aud_subsys.pf;
Benny Prijono2cd64f82009-02-17 19:57:48 +0000181}
182
183/* API: Shutdown the audio subsystem. */
184PJ_DEF(pj_status_t) pjmedia_aud_subsys_shutdown(void)
185{
186 unsigned i;
187
Benny Prijono555139d2009-02-19 12:08:19 +0000188 /* Allow shutdown() to be called multiple times as long as there is matching
189 * number of init().
190 */
191 if (aud_subsys.init_count == 0) {
192 return PJ_SUCCESS;
193 }
194 --aud_subsys.init_count;
195
Benny Prijono10454dc2009-02-21 14:21:59 +0000196 for (i=0; i<aud_subsys.drv_cnt; ++i) {
197 pjmedia_aud_dev_factory *f = aud_subsys.drv[i].f;
Benny Prijonoe3ebd552009-02-18 20:14:15 +0000198
199 if (!f)
Benny Prijono2cd64f82009-02-17 19:57:48 +0000200 continue;
201
Benny Prijonoe3ebd552009-02-18 20:14:15 +0000202 f->op->destroy(f);
Benny Prijono10454dc2009-02-21 14:21:59 +0000203 aud_subsys.drv[i].f = NULL;
Benny Prijono2cd64f82009-02-17 19:57:48 +0000204 }
205
206 return PJ_SUCCESS;
207}
208
Benny Prijono10454dc2009-02-21 14:21:59 +0000209/* API: get capability name/info */
210PJ_DEF(const char*) pjmedia_aud_dev_cap_name(pjmedia_aud_dev_cap cap,
211 const char **p_desc)
212{
213 char *desc;
214 unsigned i;
215
216 if (p_desc==NULL) p_desc = &desc;
217
218 for (i=0; i<PJ_ARRAY_SIZE(cap_infos); ++i) {
219 if ((1 << i)==cap)
220 break;
221 }
222
223 if (i==32) {
224 *p_desc = "??";
225 return "??";
226 }
227
228 *p_desc = cap_infos[i].info;
229 return cap_infos[i].name;
230}
231
Benny Prijono2cd64f82009-02-17 19:57:48 +0000232/* API: Get the number of sound devices installed in the system. */
233PJ_DEF(unsigned) pjmedia_aud_dev_count(void)
234{
Benny Prijono10454dc2009-02-21 14:21:59 +0000235 return aud_subsys.dev_cnt;
Benny Prijono2cd64f82009-02-17 19:57:48 +0000236}
237
Benny Prijono10454dc2009-02-21 14:21:59 +0000238/* Internal: lookup device id */
239static pj_status_t lookup_dev(pjmedia_aud_dev_index id,
240 pjmedia_aud_dev_factory **p_f,
241 unsigned *p_local_index)
Benny Prijono2cd64f82009-02-17 19:57:48 +0000242{
Benny Prijono10454dc2009-02-21 14:21:59 +0000243 int f_id, index;
Benny Prijono2cd64f82009-02-17 19:57:48 +0000244
Benny Prijono10454dc2009-02-21 14:21:59 +0000245 if (id == PJMEDIA_AUD_DEV_DEFAULT)
246 id = DEFAULT_DEV_ID;
Benny Prijono2cd64f82009-02-17 19:57:48 +0000247
Benny Prijono10454dc2009-02-21 14:21:59 +0000248 PJ_ASSERT_RETURN(id>=0 && id<(int)aud_subsys.dev_cnt,
249 PJMEDIA_EAUD_INVDEV);
Benny Prijono2cd64f82009-02-17 19:57:48 +0000250
Benny Prijono10454dc2009-02-21 14:21:59 +0000251 f_id = GET_FID(aud_subsys.dev_list[id]);
252 index = GET_INDEX(aud_subsys.dev_list[id]);
Benny Prijono2cd64f82009-02-17 19:57:48 +0000253
Benny Prijono10454dc2009-02-21 14:21:59 +0000254 if (f_id < 0 || f_id >= (int)aud_subsys.drv_cnt)
255 return PJMEDIA_EAUD_INVDEV;
256
257 if (index < 0 || index >= (int)aud_subsys.drv[f_id].dev_cnt)
258 return PJMEDIA_EAUD_INVDEV;
259
260 *p_f = aud_subsys.drv[f_id].f;
261 *p_local_index = (unsigned)index;
262
263 return PJ_SUCCESS;
264
Benny Prijono2cd64f82009-02-17 19:57:48 +0000265}
266
Benny Prijono10454dc2009-02-21 14:21:59 +0000267/* Internal: convert local index to global device index */
268static pj_status_t make_global_index(pjmedia_aud_dev_factory *f,
269 pjmedia_aud_dev_index *id)
270{
271 unsigned f_id = f->internal.id;
272
273 if (*id == PJMEDIA_AUD_DEV_DEFAULT)
274 return PJ_SUCCESS;
275
276 /* Check that factory still exists */
277 PJ_ASSERT_RETURN(f, PJ_EBUG);
278
279 /* Check that device index is valid */
280 PJ_ASSERT_RETURN(*id>=0 && *id<(int)aud_subsys.drv[f_id].dev_cnt, PJ_EBUG);
281
282 *id += aud_subsys.drv[f_id].start_idx;
283 return PJ_SUCCESS;
284}
Benny Prijono2cd64f82009-02-17 19:57:48 +0000285
286/* API: Get device information. */
Benny Prijono10454dc2009-02-21 14:21:59 +0000287PJ_DEF(pj_status_t) pjmedia_aud_dev_get_info(pjmedia_aud_dev_index id,
Benny Prijono2cd64f82009-02-17 19:57:48 +0000288 pjmedia_aud_dev_info *info)
289{
Benny Prijonoe3ebd552009-02-18 20:14:15 +0000290 pjmedia_aud_dev_factory *f;
Benny Prijono10454dc2009-02-21 14:21:59 +0000291 unsigned index;
292 pj_status_t status;
Benny Prijono2cd64f82009-02-17 19:57:48 +0000293
Benny Prijono10454dc2009-02-21 14:21:59 +0000294 PJ_ASSERT_RETURN(info, PJ_EINVAL);
Benny Prijono598b01d2009-02-18 13:55:03 +0000295
Benny Prijono10454dc2009-02-21 14:21:59 +0000296 status = lookup_dev(id, &f, &index);
297 if (status != PJ_SUCCESS)
298 return status;
Benny Prijono2cd64f82009-02-17 19:57:48 +0000299
Benny Prijonoe3ebd552009-02-18 20:14:15 +0000300 return f->op->get_dev_info(f, index, info);
Benny Prijono2cd64f82009-02-17 19:57:48 +0000301}
302
Benny Prijono10454dc2009-02-21 14:21:59 +0000303/* API: find device */
304PJ_DEF(pj_status_t) pjmedia_aud_dev_lookup( const char *drv_name,
305 const char *dev_name,
306 pjmedia_aud_dev_index *id)
307{
308 pjmedia_aud_dev_factory *f = NULL;
309 unsigned i, j;
310
311 PJ_ASSERT_RETURN(drv_name && dev_name && id, PJ_EINVAL);
312
313 for (i=0; i<aud_subsys.drv_cnt; ++i) {
314 if (!pj_ansi_stricmp(drv_name, aud_subsys.drv[i].name)) {
315 f = aud_subsys.drv[i].f;
316 break;
317 }
318 }
319
320 if (!f)
321 return PJ_ENOTFOUND;
322
323 for (j=0; j<aud_subsys.drv[i].dev_cnt; ++j) {
324 pjmedia_aud_dev_info info;
325 pj_status_t status;
326
327 status = f->op->get_dev_info(f, j, &info);
328 if (status != PJ_SUCCESS)
329 return status;
330
331 if (!pj_ansi_stricmp(dev_name, info.name))
332 break;
333 }
334
335 if (j==aud_subsys.drv[i].dev_cnt)
336 return PJ_ENOTFOUND;
337
338 *id = j;
339 make_global_index(f, id);
340
341 return PJ_SUCCESS;
342}
343
Benny Prijono2cd64f82009-02-17 19:57:48 +0000344/* API: Initialize the audio device parameters with default values for the
345 * specified device.
346 */
Benny Prijono10454dc2009-02-21 14:21:59 +0000347PJ_DEF(pj_status_t) pjmedia_aud_dev_default_param(pjmedia_aud_dev_index id,
348 pjmedia_aud_param *param)
Benny Prijono2cd64f82009-02-17 19:57:48 +0000349{
Benny Prijonoe3ebd552009-02-18 20:14:15 +0000350 pjmedia_aud_dev_factory *f;
Benny Prijono10454dc2009-02-21 14:21:59 +0000351 unsigned index;
Benny Prijono2cd64f82009-02-17 19:57:48 +0000352 pj_status_t status;
353
Benny Prijono10454dc2009-02-21 14:21:59 +0000354 PJ_ASSERT_RETURN(param, PJ_EINVAL);
Benny Prijono598b01d2009-02-18 13:55:03 +0000355
Benny Prijono10454dc2009-02-21 14:21:59 +0000356 status = lookup_dev(id, &f, &index);
357 if (status != PJ_SUCCESS)
358 return status;
Benny Prijono2cd64f82009-02-17 19:57:48 +0000359
Benny Prijonoe3ebd552009-02-18 20:14:15 +0000360 status = f->op->default_param(f, index, param);
Benny Prijono2cd64f82009-02-17 19:57:48 +0000361 if (status != PJ_SUCCESS)
362 return status;
363
364 /* Normalize device IDs */
Benny Prijono10454dc2009-02-21 14:21:59 +0000365 make_global_index(f, &param->rec_id);
366 make_global_index(f, &param->play_id);
Benny Prijono2cd64f82009-02-17 19:57:48 +0000367
368 return PJ_SUCCESS;
369}
370
371/* API: Open audio stream object using the specified parameters. */
Benny Prijono10454dc2009-02-21 14:21:59 +0000372PJ_DEF(pj_status_t) pjmedia_aud_stream_create(const pjmedia_aud_param *prm,
Benny Prijono2cd64f82009-02-17 19:57:48 +0000373 pjmedia_aud_rec_cb rec_cb,
374 pjmedia_aud_play_cb play_cb,
375 void *user_data,
376 pjmedia_aud_stream **p_aud_strm)
377{
Benny Prijono10454dc2009-02-21 14:21:59 +0000378 pjmedia_aud_dev_factory *rec_f=NULL, *play_f=NULL, *f=NULL;
379 pjmedia_aud_param param;
Benny Prijono2cd64f82009-02-17 19:57:48 +0000380 pj_status_t status;
381
Benny Prijono10454dc2009-02-21 14:21:59 +0000382 PJ_ASSERT_RETURN(prm && prm->dir && p_aud_strm, PJ_EINVAL);
383
Benny Prijono2cd64f82009-02-17 19:57:48 +0000384 /* Must make copy of param because we're changing device ID */
Benny Prijono10454dc2009-02-21 14:21:59 +0000385 pj_memcpy(&param, prm, sizeof(param));
Benny Prijono2cd64f82009-02-17 19:57:48 +0000386
Benny Prijono10454dc2009-02-21 14:21:59 +0000387 /* Normalize rec_id */
388 if (param.dir & PJMEDIA_DIR_CAPTURE) {
389 unsigned index;
390
391 status = lookup_dev(param.rec_id, &rec_f, &index);
392 if (status != PJ_SUCCESS)
393 return status;
394
395 param.rec_id = index;
396 f = rec_f;
397 }
398
399 /* Normalize play_id */
400 if (param.dir & PJMEDIA_DIR_PLAYBACK) {
401 unsigned index;
402
403 status = lookup_dev(param.play_id, &play_f, &index);
404 if (status != PJ_SUCCESS)
405 return status;
406
407 param.play_id = index;
408 f = play_f;
409
410 /* For now, rec_id and play_id must belong to the same factory */
411 PJ_ASSERT_RETURN(rec_f == play_f, PJ_EINVAL);
412 }
413
Benny Prijono2cd64f82009-02-17 19:57:48 +0000414
Benny Prijono10454dc2009-02-21 14:21:59 +0000415 /* Create the stream */
Benny Prijonoe3ebd552009-02-18 20:14:15 +0000416 status = f->op->create_stream(f, &param, rec_cb, play_cb,
417 user_data, p_aud_strm);
Benny Prijono2cd64f82009-02-17 19:57:48 +0000418 if (status != PJ_SUCCESS)
419 return status;
420
Benny Prijono10454dc2009-02-21 14:21:59 +0000421 /* Assign factory id to the stream */
422 (*p_aud_strm)->factory_id = f->internal.id;
Benny Prijono2cd64f82009-02-17 19:57:48 +0000423 return PJ_SUCCESS;
424}
425
426/* API: Get the running parameters for the specified audio stream. */
427PJ_DEF(pj_status_t) pjmedia_aud_stream_get_param(pjmedia_aud_stream *strm,
Benny Prijono10454dc2009-02-21 14:21:59 +0000428 pjmedia_aud_param *param)
Benny Prijono2cd64f82009-02-17 19:57:48 +0000429{
430 pj_status_t status;
431
432 status = strm->op->get_param(strm, param);
433 if (status != PJ_SUCCESS)
434 return status;
435
436 /* Normalize device id's */
Benny Prijono10454dc2009-02-21 14:21:59 +0000437 make_global_index(aud_subsys.drv[strm->factory_id].f, &param->rec_id);
438 make_global_index(aud_subsys.drv[strm->factory_id].f, &param->play_id);
Benny Prijono2cd64f82009-02-17 19:57:48 +0000439
440 return PJ_SUCCESS;
441}
442
443/* API: Get the value of a specific capability of the audio stream. */
444PJ_DEF(pj_status_t) pjmedia_aud_stream_get_cap(pjmedia_aud_stream *strm,
445 pjmedia_aud_dev_cap cap,
446 void *value)
447{
448 return strm->op->get_cap(strm, cap, value);
449}
450
451/* API: Set the value of a specific capability of the audio stream. */
452PJ_DEF(pj_status_t) pjmedia_aud_stream_set_cap(pjmedia_aud_stream *strm,
453 pjmedia_aud_dev_cap cap,
454 const void *value)
455{
456 return strm->op->set_cap(strm, cap, value);
457}
458
459/* API: Start the stream. */
460PJ_DEF(pj_status_t) pjmedia_aud_stream_start(pjmedia_aud_stream *strm)
461{
462 return strm->op->start(strm);
463}
464
465/* API: Stop the stream. */
466PJ_DEF(pj_status_t) pjmedia_aud_stream_stop(pjmedia_aud_stream *strm)
467{
468 return strm->op->stop(strm);
469}
470
471/* API: Destroy the stream. */
472PJ_DEF(pj_status_t) pjmedia_aud_stream_destroy(pjmedia_aud_stream *strm)
473{
474 return strm->op->destroy(strm);
475}
476
477