EffectsFactory.c revision e1315cf0b63b4c14a77046519e6b01f6f60d74b0
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 "EffectsFactory.h"
21#include <string.h>
22#include <stdlib.h>
23#include <dlfcn.h>
24
25#include <cutils/misc.h>
26#include <cutils/config_utils.h>
27
28static list_elem_t *gEffectList; // list of effect_entry_t: all currently created effects
29static list_elem_t *gLibraryList; // list of lib_entry_t: all currently loaded libraries
30static pthread_mutex_t gLibLock = PTHREAD_MUTEX_INITIALIZER; // controls access to gLibraryList
31static uint32_t gNumEffects;         // total number number of effects
32static list_elem_t *gCurLib;    // current library in enumeration process
33static list_elem_t *gCurEffect; // current effect in enumeration process
34static uint32_t gCurEffectIdx;       // current effect index in enumeration process
35static lib_entry_t *gCachedLibrary;  // last library accessed by getLibrary()
36
37static int gInitDone; // true is global initialization has been preformed
38static int gCanQueryEffect; // indicates that call to EffectQueryEffect() is valid, i.e. that the list of effects
39                          // was not modified since last call to EffectQueryNumberEffects()
40
41
42/////////////////////////////////////////////////
43//      Local functions prototypes
44/////////////////////////////////////////////////
45
46static int init();
47static int loadEffectConfigFile(const char *path);
48static int loadLibraries(cnode *root);
49static int loadLibrary(cnode *root, const char *name);
50static int loadEffects(cnode *root);
51static int loadEffect(cnode *node);
52static lib_entry_t *getLibrary(const char *path);
53static void resetEffectEnumeration();
54static uint32_t updateNumEffects();
55static int findEffect(effect_uuid_t *type,
56               effect_uuid_t *uuid,
57               lib_entry_t **lib,
58               effect_descriptor_t **desc);
59static void dumpEffectDescriptor(effect_descriptor_t *desc, char *str, size_t len);
60static int stringToUuid(const char *str, effect_uuid_t *uuid);
61static int uuidToString(const effect_uuid_t *uuid, char *str, size_t maxLen);
62
63/////////////////////////////////////////////////
64//      Effect Control Interface functions
65/////////////////////////////////////////////////
66
67int Effect_Process(effect_handle_t self, audio_buffer_t *inBuffer, audio_buffer_t *outBuffer)
68{
69    int ret = init();
70    if (ret < 0) {
71        return ret;
72    }
73    effect_entry_t *fx = (effect_entry_t *)self;
74    pthread_mutex_lock(&gLibLock);
75    if (fx->lib == NULL) {
76        pthread_mutex_unlock(&gLibLock);
77        return -EPIPE;
78    }
79    pthread_mutex_lock(&fx->lib->lock);
80    pthread_mutex_unlock(&gLibLock);
81
82    ret = (*fx->subItfe)->process(fx->subItfe, inBuffer, outBuffer);
83    pthread_mutex_unlock(&fx->lib->lock);
84    return ret;
85}
86
87int Effect_Command(effect_handle_t self,
88                   uint32_t cmdCode,
89                   uint32_t cmdSize,
90                   void *pCmdData,
91                   uint32_t *replySize,
92                   void *pReplyData)
93{
94    int ret = init();
95    if (ret < 0) {
96        return ret;
97    }
98    effect_entry_t *fx = (effect_entry_t *)self;
99    pthread_mutex_lock(&gLibLock);
100    if (fx->lib == NULL) {
101        pthread_mutex_unlock(&gLibLock);
102        return -EPIPE;
103    }
104    pthread_mutex_lock(&fx->lib->lock);
105    pthread_mutex_unlock(&gLibLock);
106
107    ret = (*fx->subItfe)->command(fx->subItfe, cmdCode, cmdSize, pCmdData, replySize, pReplyData);
108    pthread_mutex_unlock(&fx->lib->lock);
109    return ret;
110}
111
112int Effect_GetDescriptor(effect_handle_t self,
113                         effect_descriptor_t *desc)
114{
115    int ret = init();
116    if (ret < 0) {
117        return ret;
118    }
119    effect_entry_t *fx = (effect_entry_t *)self;
120    pthread_mutex_lock(&gLibLock);
121    if (fx->lib == NULL) {
122        pthread_mutex_unlock(&gLibLock);
123        return -EPIPE;
124    }
125    pthread_mutex_lock(&fx->lib->lock);
126    pthread_mutex_unlock(&gLibLock);
127
128    ret = (*fx->subItfe)->get_descriptor(fx->subItfe, desc);
129    pthread_mutex_unlock(&fx->lib->lock);
130    return ret;
131}
132
133const struct effect_interface_s gInterface = {
134        Effect_Process,
135        Effect_Command,
136        Effect_GetDescriptor
137};
138
139/////////////////////////////////////////////////
140//      Effect Factory Interface functions
141/////////////////////////////////////////////////
142
143int EffectQueryNumberEffects(uint32_t *pNumEffects)
144{
145    int ret = init();
146    if (ret < 0) {
147        return ret;
148    }
149    if (pNumEffects == NULL) {
150        return -EINVAL;
151    }
152
153    pthread_mutex_lock(&gLibLock);
154    *pNumEffects = gNumEffects;
155    gCanQueryEffect = 1;
156    pthread_mutex_unlock(&gLibLock);
157    LOGV("EffectQueryNumberEffects(): %d", *pNumEffects);
158    return ret;
159}
160
161int EffectQueryEffect(uint32_t index, effect_descriptor_t *pDescriptor)
162{
163    int ret = init();
164    if (ret < 0) {
165        return ret;
166    }
167    if (pDescriptor == NULL ||
168        index >= gNumEffects) {
169        return -EINVAL;
170    }
171    if (gCanQueryEffect == 0) {
172        return -ENOSYS;
173    }
174
175    pthread_mutex_lock(&gLibLock);
176    ret = -ENOENT;
177    if (index < gCurEffectIdx) {
178        resetEffectEnumeration();
179    }
180    while (gCurLib) {
181        if (gCurEffect) {
182            if (index == gCurEffectIdx) {
183                memcpy(pDescriptor, gCurEffect->object, sizeof(effect_descriptor_t));
184                ret = 0;
185                break;
186            } else {
187                gCurEffect = gCurEffect->next;
188                gCurEffectIdx++;
189            }
190        } else {
191            gCurLib = gCurLib->next;
192            gCurEffect = ((lib_entry_t *)gCurLib->object)->effects;
193        }
194    }
195
196#if (LOG_NDEBUG == 0)
197    char str[256];
198    dumpEffectDescriptor(pDescriptor, str, 256);
199    LOGV("EffectQueryEffect() desc:%s", str);
200#endif
201    pthread_mutex_unlock(&gLibLock);
202    return ret;
203}
204
205int EffectGetDescriptor(effect_uuid_t *uuid, effect_descriptor_t *pDescriptor)
206{
207    lib_entry_t *l = NULL;
208    effect_descriptor_t *d = NULL;
209
210    int ret = init();
211    if (ret < 0) {
212        return ret;
213    }
214    if (pDescriptor == NULL || uuid == NULL) {
215        return -EINVAL;
216    }
217    pthread_mutex_lock(&gLibLock);
218    ret = findEffect(NULL, uuid, &l, &d);
219    if (ret == 0) {
220        memcpy(pDescriptor, d, sizeof(effect_descriptor_t));
221    }
222    pthread_mutex_unlock(&gLibLock);
223    return ret;
224}
225
226int EffectCreate(effect_uuid_t *uuid, int32_t sessionId, int32_t ioId, effect_handle_t *pHandle)
227{
228    list_elem_t *e = gLibraryList;
229    lib_entry_t *l = NULL;
230    effect_descriptor_t *d = NULL;
231    effect_handle_t itfe;
232    effect_entry_t *fx;
233    int found = 0;
234    int ret;
235
236    if (uuid == NULL || pHandle == NULL) {
237        return -EINVAL;
238    }
239
240    LOGV("EffectCreate() UUID: %08X-%04X-%04X-%04X-%02X%02X%02X%02X%02X%02X\n",
241            uuid->timeLow, uuid->timeMid, uuid->timeHiAndVersion,
242            uuid->clockSeq, uuid->node[0], uuid->node[1],uuid->node[2],
243            uuid->node[3],uuid->node[4],uuid->node[5]);
244
245    ret = init();
246
247    if (ret < 0) {
248        LOGW("EffectCreate() init error: %d", ret);
249        return ret;
250    }
251
252    pthread_mutex_lock(&gLibLock);
253
254    ret = findEffect(NULL, uuid, &l, &d);
255    if (ret < 0){
256        goto exit;
257    }
258
259    // create effect in library
260    l->desc->create_effect(uuid, sessionId, ioId, &itfe);
261    if (ret != 0) {
262        LOGW("EffectCreate() library %s: could not create fx %s, error %d", l->name, d->name, ret);
263        goto exit;
264    }
265
266    // add entry to effect list
267    fx = (effect_entry_t *)malloc(sizeof(effect_entry_t));
268    fx->subItfe = itfe;
269    fx->itfe = (struct effect_interface_s *)&gInterface;
270    fx->lib = l;
271
272    e = (list_elem_t *)malloc(sizeof(list_elem_t));
273    e->object = fx;
274    e->next = gEffectList;
275    gEffectList = e;
276
277    *pHandle = (effect_handle_t)fx;
278
279    LOGV("EffectCreate() created entry %p with sub itfe %p in library %s", *pHandle, itfe, l->name);
280
281exit:
282    pthread_mutex_unlock(&gLibLock);
283    return ret;
284}
285
286int EffectRelease(effect_handle_t handle)
287{
288    effect_entry_t *fx;
289    list_elem_t *e1;
290    list_elem_t *e2;
291
292    int ret = init();
293    if (ret < 0) {
294        return ret;
295    }
296
297    // remove effect from effect list
298    pthread_mutex_lock(&gLibLock);
299    e1 = gEffectList;
300    e2 = NULL;
301    while (e1) {
302        if (e1->object == handle) {
303            if (e2) {
304                e2->next = e1->next;
305            } else {
306                gEffectList = e1->next;
307            }
308            fx = (effect_entry_t *)e1->object;
309            free(e1);
310            break;
311        }
312        e2 = e1;
313        e1 = e1->next;
314    }
315    if (e1 == NULL) {
316        ret = -ENOENT;
317        goto exit;
318    }
319
320    // release effect in library
321    if (fx->lib == NULL) {
322        LOGW("EffectRelease() fx %p library already unloaded", handle);
323    } else {
324        pthread_mutex_lock(&fx->lib->lock);
325        fx->lib->desc->release_effect(fx->subItfe);
326        pthread_mutex_unlock(&fx->lib->lock);
327    }
328    free(fx);
329
330exit:
331    pthread_mutex_unlock(&gLibLock);
332    return ret;
333}
334
335int EffectLoadLibrary(const char *libPath, int *handle)
336{
337    // TODO: see if this interface still makes sense with the use of config files
338    return -ENOSYS;
339}
340
341int EffectUnloadLibrary(int handle)
342{
343    // TODO: see if this interface still makes sense with the use of config files
344    return -ENOSYS;
345}
346
347int EffectIsNullUuid(effect_uuid_t *uuid)
348{
349    if (memcmp(uuid, EFFECT_UUID_NULL, sizeof(effect_uuid_t))) {
350        return 0;
351    }
352    return 1;
353}
354
355/////////////////////////////////////////////////
356//      Local functions
357/////////////////////////////////////////////////
358
359int init() {
360    int hdl;
361
362    if (gInitDone) {
363        return 0;
364    }
365
366    pthread_mutex_init(&gLibLock, NULL);
367
368    if (access(AUDIO_EFFECT_VENDOR_CONFIG_FILE, R_OK) == 0) {
369        loadEffectConfigFile(AUDIO_EFFECT_VENDOR_CONFIG_FILE);
370    } else if (access(AUDIO_EFFECT_DEFAULT_CONFIG_FILE, R_OK) == 0) {
371        loadEffectConfigFile(AUDIO_EFFECT_DEFAULT_CONFIG_FILE);
372    }
373
374    updateNumEffects();
375    gInitDone = 1;
376    LOGV("init() done");
377    return 0;
378}
379
380int loadEffectConfigFile(const char *path)
381{
382    cnode *root;
383    char *data;
384
385    data = load_file(path, NULL);
386    if (data == NULL) {
387        return -ENODEV;
388    }
389    root = config_node("", "");
390    config_load(root, data);
391    loadLibraries(root);
392    loadEffects(root);
393    config_free(root);
394    free(root);
395    free(data);
396
397    return 0;
398}
399
400int loadLibraries(cnode *root)
401{
402    cnode *node;
403
404    node = config_find(root, LIBRARIES_TAG);
405    if (node == NULL) {
406        return -ENOENT;
407    }
408    node = node->first_child;
409    while (node) {
410        loadLibrary(node, node->name);
411        node = node->next;
412    }
413    return 0;
414}
415
416int loadLibrary(cnode *root, const char *name)
417{
418    cnode *node;
419    void *hdl;
420    audio_effect_library_t *desc;
421    list_elem_t *e;
422    lib_entry_t *l;
423
424    node = config_find(root, PATH_TAG);
425    if (node == NULL) {
426        return -EINVAL;
427    }
428
429    hdl = dlopen(node->value, RTLD_NOW);
430    if (hdl == NULL) {
431        LOGW("loadLibrary() failed to open %s", node->value);
432        goto error;
433    }
434
435    desc = (audio_effect_library_t *)dlsym(hdl, AUDIO_EFFECT_LIBRARY_INFO_SYM_AS_STR);
436    if (desc == NULL) {
437        LOGW("loadLibrary() could not find symbol %s", AUDIO_EFFECT_LIBRARY_INFO_SYM_AS_STR);
438        goto error;
439    }
440
441    if (AUDIO_EFFECT_LIBRARY_TAG != desc->tag) {
442        LOGW("getLibrary() bad tag %08x in lib info struct", desc->tag);
443        goto error;
444    }
445
446    if (EFFECT_API_VERSION_MAJOR(desc->version) !=
447            EFFECT_API_VERSION_MAJOR(EFFECT_LIBRARY_API_VERSION)) {
448        LOGW("loadLibrary() bad lib version %08x", desc->version);
449        goto error;
450    }
451
452    // add entry for library in gLibraryList
453    l = malloc(sizeof(lib_entry_t));
454    l->name = strndup(name, PATH_MAX);
455    l->path = strndup(node->value, PATH_MAX);
456    l->handle = hdl;
457    l->desc = desc;
458    l->effects = NULL;
459    pthread_mutex_init(&l->lock, NULL);
460
461    e = malloc(sizeof(list_elem_t));
462    e->object = l;
463    pthread_mutex_lock(&gLibLock);
464    e->next = gLibraryList;
465    gLibraryList = e;
466    pthread_mutex_unlock(&gLibLock);
467    LOGV("getLibrary() linked library %p for path %s", l, node->value);
468
469    return 0;
470
471error:
472    if (hdl != NULL) {
473        dlclose(hdl);
474    }
475    return -EINVAL;
476}
477
478int loadEffects(cnode *root)
479{
480    cnode *node;
481
482    node = config_find(root, EFFECTS_TAG);
483    if (node == NULL) {
484        return -ENOENT;
485    }
486    node = node->first_child;
487    while (node) {
488        loadEffect(node);
489        node = node->next;
490    }
491    return 0;
492}
493
494int loadEffect(cnode *root)
495{
496    cnode *node;
497    effect_uuid_t uuid;
498    lib_entry_t *l;
499    effect_descriptor_t *d;
500    list_elem_t *e;
501
502    node = config_find(root, LIBRARY_TAG);
503    if (node == NULL) {
504        return -EINVAL;
505    }
506
507    l = getLibrary(node->value);
508    if (l == NULL) {
509        LOGW("loadEffect() could not get library %s", node->value);
510        return -EINVAL;
511    }
512
513    node = config_find(root, UUID_TAG);
514    if (node == NULL) {
515        return -EINVAL;
516    }
517    if (stringToUuid(node->value, &uuid) != 0) {
518        LOGW("loadEffect() invalid uuid %s", node->value);
519        return -EINVAL;
520    }
521
522    d = malloc(sizeof(effect_descriptor_t));
523    if (l->desc->get_descriptor(&uuid, d) != 0) {
524        char s[40];
525        uuidToString(&uuid, s, 40);
526        LOGW("Error querying effect %s on lib %s", s, l->name);
527        free(d);
528        return -EINVAL;
529    }
530#if (LOG_NDEBUG==0)
531    char s[256];
532    dumpEffectDescriptor(d, s, 256);
533    LOGV("loadEffect() read descriptor %p:%s",d, s);
534#endif
535    if (EFFECT_API_VERSION_MAJOR(d->apiVersion) !=
536            EFFECT_API_VERSION_MAJOR(EFFECT_CONTROL_API_VERSION)) {
537        LOGW("Bad API version %08x on lib %s", d->apiVersion, l->name);
538        free(d);
539        return -EINVAL;
540    }
541    e = malloc(sizeof(list_elem_t));
542    e->object = d;
543    e->next = l->effects;
544    l->effects = e;
545
546    return 0;
547}
548
549lib_entry_t *getLibrary(const char *name)
550{
551    list_elem_t *e;
552
553    if (gCachedLibrary &&
554            !strncmp(gCachedLibrary->name, name, PATH_MAX)) {
555        return gCachedLibrary;
556    }
557
558    e = gLibraryList;
559    while (e) {
560        lib_entry_t *l = (lib_entry_t *)e->object;
561        if (!strcmp(l->name, name)) {
562            gCachedLibrary = l;
563            return l;
564        }
565        e = e->next;
566    }
567
568    return NULL;
569}
570
571
572void resetEffectEnumeration()
573{
574    gCurLib = gLibraryList;
575    gCurEffect = NULL;
576    if (gCurLib) {
577        gCurEffect = ((lib_entry_t *)gCurLib->object)->effects;
578    }
579    gCurEffectIdx = 0;
580}
581
582uint32_t updateNumEffects() {
583    list_elem_t *e;
584    uint32_t cnt = 0;
585
586    resetEffectEnumeration();
587
588    e = gLibraryList;
589    while (e) {
590        lib_entry_t *l = (lib_entry_t *)e->object;
591        list_elem_t *efx = l->effects;
592        while (efx) {
593            cnt++;
594            efx = efx->next;
595        }
596        e = e->next;
597    }
598    gNumEffects = cnt;
599    gCanQueryEffect = 0;
600    return cnt;
601}
602
603int findEffect(effect_uuid_t *type,
604               effect_uuid_t *uuid,
605               lib_entry_t **lib,
606               effect_descriptor_t **desc)
607{
608    list_elem_t *e = gLibraryList;
609    lib_entry_t *l = NULL;
610    effect_descriptor_t *d = NULL;
611    int found = 0;
612    int ret = 0;
613
614    while (e && !found) {
615        l = (lib_entry_t *)e->object;
616        list_elem_t *efx = l->effects;
617        while (efx) {
618            d = (effect_descriptor_t *)efx->object;
619            if (type != NULL && memcmp(&d->type, type, sizeof(effect_uuid_t)) == 0) {
620                found = 1;
621                break;
622            }
623            if (uuid != NULL && memcmp(&d->uuid, uuid, sizeof(effect_uuid_t)) == 0) {
624                found = 1;
625                break;
626            }
627            efx = efx->next;
628        }
629        e = e->next;
630    }
631    if (!found) {
632        LOGV("findEffect() effect not found");
633        ret = -ENOENT;
634    } else {
635        LOGV("findEffect() found effect: %s in lib %s", d->name, l->name);
636        *lib = l;
637        if (desc) {
638            *desc = d;
639        }
640    }
641
642    return ret;
643}
644
645void dumpEffectDescriptor(effect_descriptor_t *desc, char *str, size_t len) {
646    char s[256];
647
648    snprintf(str, len, "\nEffect Descriptor %p:\n", desc);
649    strncat(str, "- TYPE: ", len);
650    uuidToString(&desc->uuid, s, 256);
651    snprintf(str, len, "- UUID: %s\n", s);
652    uuidToString(&desc->type, s, 256);
653    snprintf(str, len, "- TYPE: %s\n", s);
654    sprintf(s, "- apiVersion: %08X\n- flags: %08X\n",
655            desc->apiVersion, desc->flags);
656    strncat(str, s, len);
657    sprintf(s, "- name: %s\n", desc->name);
658    strncat(str, s, len);
659    sprintf(s, "- implementor: %s\n", desc->implementor);
660    strncat(str, s, len);
661}
662
663int stringToUuid(const char *str, effect_uuid_t *uuid)
664{
665    int tmp[10];
666
667    if (sscanf(str, "%08x-%04x-%04x-%04x-%02x%02x%02x%02x%02x%02x",
668            tmp, tmp+1, tmp+2, tmp+3, tmp+4, tmp+5, tmp+6, tmp+7, tmp+8, tmp+9) < 10) {
669        return -EINVAL;
670    }
671    uuid->timeLow = (uint32_t)tmp[0];
672    uuid->timeMid = (uint16_t)tmp[1];
673    uuid->timeHiAndVersion = (uint16_t)tmp[2];
674    uuid->clockSeq = (uint16_t)tmp[3];
675    uuid->node[0] = (uint8_t)tmp[4];
676    uuid->node[1] = (uint8_t)tmp[5];
677    uuid->node[2] = (uint8_t)tmp[6];
678    uuid->node[3] = (uint8_t)tmp[7];
679    uuid->node[4] = (uint8_t)tmp[8];
680    uuid->node[5] = (uint8_t)tmp[9];
681
682    return 0;
683}
684
685int uuidToString(const effect_uuid_t *uuid, char *str, size_t maxLen)
686{
687
688    snprintf(str, maxLen, "%08x-%04x-%04x-%04x-%02x%02x%02x%02x%02x%02x",
689            uuid->timeLow,
690            uuid->timeMid,
691            uuid->timeHiAndVersion,
692            uuid->clockSeq,
693            uuid->node[0],
694            uuid->node[1],
695            uuid->node[2],
696            uuid->node[3],
697            uuid->node[4],
698            uuid->node[5]);
699
700    return 0;
701}
702
703