1 /*
2 * Copyright (C) 2010 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17 #define LOG_TAG "EffectsFactory"
18 //#define LOG_NDEBUG 0
19
20 #include <stdlib.h>
21 #include <string.h>
22 #include <unistd.h>
23
24 #include <cutils/properties.h>
25 #include <log/log.h>
26
27 #include <media/EffectsFactoryApi.h>
28
29 #include "EffectsConfigLoader.h"
30 #include "EffectsFactoryState.h"
31 #include "EffectsXmlConfigLoader.h"
32
33 #include "EffectsFactory.h"
34
35 static list_elem_t *gEffectList; // list of effect_entry_t: all currently created effects
36 static uint32_t gNumEffects; // total number number of effects
37 static list_elem_t *gCurLib; // current library in enumeration process
38 static list_elem_t *gCurEffect; // current effect in enumeration process
39 static uint32_t gCurEffectIdx; // current effect index in enumeration process
40 /** Number of elements skipped during the effects configuration loading.
41 * -1 if the config loader failed
42 * -2 if config load was skipped
43 */
44 static ssize_t gConfigNbElemSkipped = -2;
45
46 static int gInitDone; // true is global initialization has been preformed
47 static int gCanQueryEffect; // indicates that call to EffectQueryEffect() is valid, i.e. that the list of effects
48 // was not modified since last call to EffectQueryNumberEffects()
49 /////////////////////////////////////////////////
50 // Local functions prototypes
51 /////////////////////////////////////////////////
52
53 static int init();
54 static void resetEffectEnumeration();
55 static uint32_t updateNumEffects();
56 // To search a subeffect in the gSubEffectList
57 static int findSubEffect(const effect_uuid_t *uuid,
58 lib_entry_t **lib,
59 effect_descriptor_t **desc);
60
61 /////////////////////////////////////////////////
62 // Effect Control Interface functions
63 /////////////////////////////////////////////////
64
Effect_Process(effect_handle_t self,audio_buffer_t * inBuffer,audio_buffer_t * outBuffer)65 int Effect_Process(effect_handle_t self, audio_buffer_t *inBuffer, audio_buffer_t *outBuffer)
66 {
67 int ret = init();
68 if (ret < 0) {
69 return ret;
70 }
71 effect_entry_t *fx = (effect_entry_t *)self;
72 pthread_mutex_lock(&gLibLock);
73 if (fx->lib == NULL) {
74 pthread_mutex_unlock(&gLibLock);
75 return -EPIPE;
76 }
77 pthread_mutex_lock(&fx->lib->lock);
78 pthread_mutex_unlock(&gLibLock);
79
80 ret = (*fx->subItfe)->process(fx->subItfe, inBuffer, outBuffer);
81 pthread_mutex_unlock(&fx->lib->lock);
82 return ret;
83 }
84
Effect_Command(effect_handle_t self,uint32_t cmdCode,uint32_t cmdSize,void * pCmdData,uint32_t * replySize,void * pReplyData)85 int Effect_Command(effect_handle_t self,
86 uint32_t cmdCode,
87 uint32_t cmdSize,
88 void *pCmdData,
89 uint32_t *replySize,
90 void *pReplyData)
91 {
92 int ret = init();
93 if (ret < 0) {
94 return ret;
95 }
96 effect_entry_t *fx = (effect_entry_t *)self;
97 pthread_mutex_lock(&gLibLock);
98 if (fx->lib == NULL) {
99 pthread_mutex_unlock(&gLibLock);
100 return -EPIPE;
101 }
102 pthread_mutex_lock(&fx->lib->lock);
103 pthread_mutex_unlock(&gLibLock);
104
105 ret = (*fx->subItfe)->command(fx->subItfe, cmdCode, cmdSize, pCmdData, replySize, pReplyData);
106 pthread_mutex_unlock(&fx->lib->lock);
107 return ret;
108 }
109
Effect_GetDescriptor(effect_handle_t self,effect_descriptor_t * desc)110 int Effect_GetDescriptor(effect_handle_t self,
111 effect_descriptor_t *desc)
112 {
113 int ret = init();
114 if (ret < 0) {
115 return ret;
116 }
117 effect_entry_t *fx = (effect_entry_t *)self;
118 pthread_mutex_lock(&gLibLock);
119 if (fx->lib == NULL) {
120 pthread_mutex_unlock(&gLibLock);
121 return -EPIPE;
122 }
123 pthread_mutex_lock(&fx->lib->lock);
124 pthread_mutex_unlock(&gLibLock);
125
126 ret = (*fx->subItfe)->get_descriptor(fx->subItfe, desc);
127 pthread_mutex_unlock(&fx->lib->lock);
128 return ret;
129 }
130
Effect_ProcessReverse(effect_handle_t self,audio_buffer_t * inBuffer,audio_buffer_t * outBuffer)131 int Effect_ProcessReverse(effect_handle_t self, audio_buffer_t *inBuffer, audio_buffer_t *outBuffer)
132 {
133 int ret = init();
134 if (ret < 0) {
135 return ret;
136 }
137 effect_entry_t *fx = (effect_entry_t *)self;
138 pthread_mutex_lock(&gLibLock);
139 if (fx->lib == NULL) {
140 pthread_mutex_unlock(&gLibLock);
141 return -EPIPE;
142 }
143 pthread_mutex_lock(&fx->lib->lock);
144 pthread_mutex_unlock(&gLibLock);
145
146 if ((*fx->subItfe)->process_reverse != NULL) {
147 ret = (*fx->subItfe)->process_reverse(fx->subItfe, inBuffer, outBuffer);
148 } else {
149 ret = -ENOSYS;
150 }
151 pthread_mutex_unlock(&fx->lib->lock);
152 return ret;
153 }
154
155
156 const struct effect_interface_s gInterface = {
157 Effect_Process,
158 Effect_Command,
159 Effect_GetDescriptor,
160 NULL
161 };
162
163 const struct effect_interface_s gInterfaceWithReverse = {
164 Effect_Process,
165 Effect_Command,
166 Effect_GetDescriptor,
167 Effect_ProcessReverse
168 };
169
170 /////////////////////////////////////////////////
171 // Effect Factory Interface functions
172 /////////////////////////////////////////////////
173
EffectQueryNumberEffects(uint32_t * pNumEffects)174 int EffectQueryNumberEffects(uint32_t *pNumEffects)
175 {
176 int ret = init();
177 if (ret < 0) {
178 return ret;
179 }
180 if (pNumEffects == NULL) {
181 return -EINVAL;
182 }
183
184 pthread_mutex_lock(&gLibLock);
185 *pNumEffects = gNumEffects;
186 gCanQueryEffect = 1;
187 pthread_mutex_unlock(&gLibLock);
188 ALOGV("EffectQueryNumberEffects(): %d", *pNumEffects);
189 return ret;
190 }
191
EffectQueryEffect(uint32_t index,effect_descriptor_t * pDescriptor)192 int EffectQueryEffect(uint32_t index, effect_descriptor_t *pDescriptor)
193 {
194 int ret = init();
195 if (ret < 0) {
196 return ret;
197 }
198 if (pDescriptor == NULL ||
199 index >= gNumEffects) {
200 return -EINVAL;
201 }
202 if (gCanQueryEffect == 0) {
203 return -ENOSYS;
204 }
205
206 pthread_mutex_lock(&gLibLock);
207 ret = -ENOENT;
208 if (index < gCurEffectIdx) {
209 resetEffectEnumeration();
210 }
211 while (gCurLib) {
212 if (gCurEffect) {
213 if (index == gCurEffectIdx) {
214 *pDescriptor = *(effect_descriptor_t *)gCurEffect->object;
215 ret = 0;
216 break;
217 } else {
218 gCurEffect = gCurEffect->next;
219 gCurEffectIdx++;
220 }
221 } else {
222 gCurLib = gCurLib->next;
223 gCurEffect = ((lib_entry_t *)gCurLib->object)->effects;
224 }
225 }
226
227 #if (LOG_NDEBUG == 0)
228 char str[512];
229 dumpEffectDescriptor(pDescriptor, str, sizeof(str), 0 /* indent */);
230 ALOGV("EffectQueryEffect() desc:%s", str);
231 #endif
232 pthread_mutex_unlock(&gLibLock);
233 return ret;
234 }
235
EffectGetDescriptor(const effect_uuid_t * uuid,effect_descriptor_t * pDescriptor)236 int EffectGetDescriptor(const effect_uuid_t *uuid, effect_descriptor_t *pDescriptor)
237 {
238 lib_entry_t *l = NULL;
239 effect_descriptor_t *d = NULL;
240
241 int ret = init();
242 if (ret < 0) {
243 return ret;
244 }
245 if (pDescriptor == NULL || uuid == NULL) {
246 return -EINVAL;
247 }
248 pthread_mutex_lock(&gLibLock);
249 ret = findEffect(NULL, uuid, &l, &d);
250 if (ret == 0) {
251 *pDescriptor = *d;
252 }
253 pthread_mutex_unlock(&gLibLock);
254 return ret;
255 }
256
doEffectCreate(const effect_uuid_t * uuid,int32_t sessionId,int32_t ioId,int32_t deviceId,effect_handle_t * pHandle)257 int doEffectCreate(const effect_uuid_t *uuid, int32_t sessionId, int32_t ioId, int32_t deviceId,
258 effect_handle_t *pHandle)
259 {
260 list_elem_t *e = gLibraryList;
261 lib_entry_t *l = NULL;
262 effect_descriptor_t *d = NULL;
263 effect_handle_t itfe;
264 effect_entry_t *fx;
265 int ret;
266
267 if (uuid == NULL || pHandle == NULL) {
268 return -EINVAL;
269 }
270
271 ALOGV("EffectCreate() UUID: %08X-%04X-%04X-%04X-%02X%02X%02X%02X%02X%02X\n",
272 uuid->timeLow, uuid->timeMid, uuid->timeHiAndVersion,
273 uuid->clockSeq, uuid->node[0], uuid->node[1], uuid->node[2],
274 uuid->node[3], uuid->node[4], uuid->node[5]);
275
276 ret = init();
277
278 if (ret < 0) {
279 ALOGW("EffectCreate() init error: %d", ret);
280 return ret;
281 }
282
283 pthread_mutex_lock(&gLibLock);
284
285 ret = findEffect(NULL, uuid, &l, &d);
286 if (ret < 0) {
287 // Sub effects are not associated with the library->effects,
288 // so, findEffect will fail. Search for the effect in gSubEffectList.
289 ret = findSubEffect(uuid, &l, &d);
290 if (ret < 0) {
291 goto exit;
292 }
293 }
294
295 // create effect in library
296 if (sessionId == AUDIO_SESSION_DEVICE) {
297 if (l->desc->version >= EFFECT_LIBRARY_API_VERSION_3_1) {
298 ALOGI("EffectCreate() create_effect_3_1");
299 ret = l->desc->create_effect_3_1(uuid, sessionId, ioId, deviceId, &itfe);
300 } else {
301 ALOGE("EffectCreate() cannot create device effect on library with API version < 3.1");
302 ret = -ENOSYS;
303 }
304 } else {
305 ALOGI("EffectCreate() create_effect");
306 ret = l->desc->create_effect(uuid, sessionId, ioId, &itfe);
307 }
308
309 if (ret != 0) {
310 ALOGW("EffectCreate() library %s: could not create fx %s, error %d", l->name, d->name, ret);
311 goto exit;
312 }
313
314 // add entry to effect list
315 fx = (effect_entry_t *)malloc(sizeof(effect_entry_t));
316 fx->subItfe = itfe;
317 if ((*itfe)->process_reverse != NULL) {
318 fx->itfe = (struct effect_interface_s *)&gInterfaceWithReverse;
319 ALOGV("EffectCreate() gInterfaceWithReverse");
320 } else {
321 fx->itfe = (struct effect_interface_s *)&gInterface;
322 ALOGV("EffectCreate() gInterface");
323 }
324 fx->lib = l;
325
326 e = (list_elem_t *)malloc(sizeof(list_elem_t));
327 e->object = fx;
328 e->next = gEffectList;
329 gEffectList = e;
330
331 *pHandle = (effect_handle_t)fx;
332
333 ALOGV("EffectCreate() created entry %p with sub itfe %p in library %s", *pHandle, itfe, l->name);
334
335 exit:
336 pthread_mutex_unlock(&gLibLock);
337 return ret;
338 }
339
EffectCreate(const effect_uuid_t * uuid,int32_t sessionId,int32_t ioId,effect_handle_t * pHandle)340 int EffectCreate(const effect_uuid_t *uuid, int32_t sessionId, int32_t ioId,
341 effect_handle_t *pHandle) {
342 return doEffectCreate(uuid, sessionId, ioId, AUDIO_PORT_HANDLE_NONE, pHandle);
343 }
344
EffectCreateOnDevice(const effect_uuid_t * uuid,int32_t deviceId,int32_t ioId,effect_handle_t * pHandle)345 int EffectCreateOnDevice(const effect_uuid_t *uuid, int32_t deviceId, int32_t ioId,
346 effect_handle_t *pHandle) {
347 return doEffectCreate(uuid, AUDIO_SESSION_DEVICE, ioId, deviceId, pHandle);
348 }
349
EffectRelease(effect_handle_t handle)350 int EffectRelease(effect_handle_t handle)
351 {
352 effect_entry_t *fx;
353 list_elem_t *e1;
354 list_elem_t *e2;
355
356 int ret = init();
357 if (ret < 0) {
358 return ret;
359 }
360
361 // remove effect from effect list
362 pthread_mutex_lock(&gLibLock);
363 e1 = gEffectList;
364 e2 = NULL;
365 while (e1) {
366 if (e1->object == handle) {
367 if (e2) {
368 e2->next = e1->next;
369 } else {
370 gEffectList = e1->next;
371 }
372 fx = (effect_entry_t *)e1->object;
373 free(e1);
374 break;
375 }
376 e2 = e1;
377 e1 = e1->next;
378 }
379 if (e1 == NULL) {
380 ret = -ENOENT;
381 goto exit;
382 }
383
384 // release effect in library
385 if (fx->lib == NULL) {
386 ALOGW("EffectRelease() fx %p library already unloaded", handle);
387 } else {
388 pthread_mutex_lock(&fx->lib->lock);
389 fx->lib->desc->release_effect(fx->subItfe);
390 pthread_mutex_unlock(&fx->lib->lock);
391 }
392 free(fx);
393
394 exit:
395 pthread_mutex_unlock(&gLibLock);
396 return ret;
397 }
398
EffectIsNullUuid(const effect_uuid_t * uuid)399 int EffectIsNullUuid(const effect_uuid_t *uuid)
400 {
401 if (memcmp(uuid, EFFECT_UUID_NULL, sizeof(effect_uuid_t))) {
402 return 0;
403 }
404 return 1;
405 }
406
407 // Function to get the sub effect descriptors of the effect whose uuid
408 // is pointed by the first argument. It searches the gSubEffectList for the
409 // matching uuid and then copies the corresponding sub effect descriptors
410 // to the inout param
EffectGetSubEffects(const effect_uuid_t * uuid,sub_effect_entry_t ** pSube,size_t size)411 int EffectGetSubEffects(const effect_uuid_t *uuid, sub_effect_entry_t **pSube,
412 size_t size)
413 {
414 ALOGV("EffectGetSubEffects() UUID: %08X-%04X-%04X-%04X-%02X%02X%02X%02X%02X"
415 "%02X\n",uuid->timeLow, uuid->timeMid, uuid->timeHiAndVersion,
416 uuid->clockSeq, uuid->node[0], uuid->node[1],uuid->node[2],
417 uuid->node[3],uuid->node[4],uuid->node[5]);
418
419 // Check if the size of the desc buffer is large enough for 2 subeffects
420 if ((uuid == NULL) || (pSube == NULL) || (size < 2)) {
421 ALOGW("NULL pointer or insufficient memory. Cannot query subeffects");
422 return -EINVAL;
423 }
424 int ret = init();
425 if (ret < 0)
426 return ret;
427 list_sub_elem_t *e = gSubEffectList;
428 sub_effect_entry_t *subeffect;
429 effect_descriptor_t *d;
430 int count = 0;
431 while (e != NULL) {
432 d = (effect_descriptor_t*)e->object;
433 if (memcmp(uuid, &d->uuid, sizeof(effect_uuid_t)) == 0) {
434 ALOGV("EffectGetSubEffects: effect found in the list");
435 list_elem_t *subefx = e->sub_elem;
436 while (subefx != NULL) {
437 subeffect = (sub_effect_entry_t*)subefx->object;
438 pSube[count++] = subeffect;
439 subefx = subefx->next;
440 }
441 ALOGV("EffectGetSubEffects end - copied the sub effect structures");
442 return count;
443 }
444 e = e->next;
445 }
446 return -ENOENT;
447 }
448 /////////////////////////////////////////////////
449 // Local functions
450 /////////////////////////////////////////////////
451
init()452 int init() {
453 if (gInitDone) {
454 return 0;
455 }
456
457 // ignore effects or not?
458 const bool ignoreFxConfFiles = property_get_bool(PROPERTY_IGNORE_EFFECTS, false);
459
460 pthread_mutex_init(&gLibLock, NULL);
461
462 if (ignoreFxConfFiles) {
463 ALOGI("Audio effects in configuration files will be ignored");
464 } else {
465 gConfigNbElemSkipped = EffectLoadXmlEffectConfig(NULL);
466 if (gConfigNbElemSkipped < 0) {
467 ALOGW("Failed to load XML effect configuration, fallback to .conf");
468 EffectLoadEffectConfig();
469 } else if (gConfigNbElemSkipped > 0) {
470 ALOGE("Effect config is partially invalid, skipped %zd elements", gConfigNbElemSkipped);
471 }
472 }
473
474 updateNumEffects();
475 gInitDone = 1;
476 ALOGV("init() done");
477 return 0;
478 }
479
480 // Searches the sub effect matching to the specified uuid
481 // in the gSubEffectList. It gets the lib_entry_t for
482 // the matched sub_effect . Used in EffectCreate of sub effects
findSubEffect(const effect_uuid_t * uuid,lib_entry_t ** lib,effect_descriptor_t ** desc)483 int findSubEffect(const effect_uuid_t *uuid,
484 lib_entry_t **lib,
485 effect_descriptor_t **desc)
486 {
487 list_sub_elem_t *e = gSubEffectList;
488 list_elem_t *subefx;
489 sub_effect_entry_t *effect;
490 lib_entry_t *l = NULL;
491 effect_descriptor_t *d = NULL;
492 int found = 0;
493 int ret = 0;
494
495 if (uuid == NULL)
496 return -EINVAL;
497
498 while (e != NULL && !found) {
499 subefx = (list_elem_t*)(e->sub_elem);
500 while (subefx != NULL) {
501 effect = (sub_effect_entry_t*)subefx->object;
502 l = (lib_entry_t *)effect->lib;
503 d = (effect_descriptor_t *)effect->object;
504 if (memcmp(&d->uuid, uuid, sizeof(effect_uuid_t)) == 0) {
505 ALOGV("uuid matched");
506 found = 1;
507 break;
508 }
509 subefx = subefx->next;
510 }
511 e = e->next;
512 }
513 if (!found) {
514 ALOGV("findSubEffect() effect not found");
515 ret = -ENOENT;
516 } else {
517 ALOGV("findSubEffect() found effect: %s in lib %s", d->name, l->name);
518 *lib = l;
519 if (desc != NULL) {
520 *desc = d;
521 }
522 }
523 return ret;
524 }
525
resetEffectEnumeration()526 void resetEffectEnumeration()
527 {
528 gCurLib = gLibraryList;
529 gCurEffect = NULL;
530 if (gCurLib) {
531 gCurEffect = ((lib_entry_t *)gCurLib->object)->effects;
532 }
533 gCurEffectIdx = 0;
534 }
535
updateNumEffects()536 uint32_t updateNumEffects() {
537 list_elem_t *e;
538 uint32_t cnt = 0;
539
540 resetEffectEnumeration();
541
542 e = gLibraryList;
543 while (e) {
544 lib_entry_t *l = (lib_entry_t *)e->object;
545 list_elem_t *efx = l->effects;
546 while (efx) {
547 cnt++;
548 efx = efx->next;
549 }
550 e = e->next;
551 }
552 gNumEffects = cnt;
553 gCanQueryEffect = 0;
554 return cnt;
555 }
556
EffectDumpEffects(int fd)557 int EffectDumpEffects(int fd) {
558 char s[512];
559
560 list_elem_t *fe = gLibraryFailedList;
561 lib_failed_entry_t *fl = NULL;
562
563 dprintf(fd, "Libraries NOT loaded:\n");
564
565 while (fe) {
566 fl = (lib_failed_entry_t *)fe->object;
567 dprintf(fd, " Library %s\n", fl->name);
568 dprintf(fd, " path: %s\n", fl->path);
569 fe = fe->next;
570 }
571
572 list_elem_t *e = gLibraryList;
573 lib_entry_t *l = NULL;
574 effect_descriptor_t *d = NULL;
575 int ret = 0;
576
577 dprintf(fd, "Libraries loaded:\n");
578 while (e) {
579 l = (lib_entry_t *)e->object;
580 list_elem_t *efx = l->effects;
581 dprintf(fd, " Library %s\n", l->name);
582 dprintf(fd, " path: %s\n", l->path);
583 if (!efx) {
584 dprintf(fd, " (no effects)\n");
585 }
586 while (efx) {
587 d = (effect_descriptor_t *)efx->object;
588 dumpEffectDescriptor(d, s, sizeof(s), 2);
589 dprintf(fd, "%s", s);
590 efx = efx->next;
591 }
592 e = e->next;
593 }
594
595 e = gSkippedEffects;
596 if (e) {
597 dprintf(fd, "Skipped effects\n");
598 while(e) {
599 d = (effect_descriptor_t *)e->object;
600 dumpEffectDescriptor(d, s, sizeof(s), 2 /* indent */);
601 dprintf(fd, "%s", s);
602 e = e->next;
603 }
604 }
605 switch (gConfigNbElemSkipped) {
606 case -2:
607 dprintf(fd, "Effect configuration loading skipped.\n");
608 break;
609 case -1:
610 dprintf(fd, "XML effect configuration failed to load.\n");
611 break;
612 case 0:
613 dprintf(fd, "XML effect configuration loaded successfully.\n");
614 break;
615 default:
616 dprintf(fd, "XML effect configuration partially loaded, skipped %zd elements.\n",
617 gConfigNbElemSkipped);
618 }
619 return ret;
620 }
621
622