1/*
2 * Copyright (C) 2014 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 "offload_effect_bundle"
18//#define LOG_NDEBUG 0
19
20#include <stdlib.h>
21#include <cutils/list.h>
22#include <cutils/log.h>
23#include <system/thread_defs.h>
24#include <tinyalsa/asoundlib.h>
25#include <hardware/audio_effect.h>
26
27#include "bundle.h"
28#include "equalizer.h"
29#include "bass_boost.h"
30#include "virtualizer.h"
31#include "reverb.h"
32
33enum {
34    EFFECT_STATE_UNINITIALIZED,
35    EFFECT_STATE_INITIALIZED,
36    EFFECT_STATE_ACTIVE,
37};
38
39const effect_descriptor_t *descriptors[] = {
40        &equalizer_descriptor,
41        &bassboost_descriptor,
42        &virtualizer_descriptor,
43        &aux_env_reverb_descriptor,
44        &ins_env_reverb_descriptor,
45        &aux_preset_reverb_descriptor,
46        &ins_preset_reverb_descriptor,
47        NULL,
48};
49
50pthread_once_t once = PTHREAD_ONCE_INIT;
51int init_status;
52/*
53 * list of created effects.
54 * Updated by offload_effects_bundle_hal_start_output()
55 * and offload_effects_bundle_hal_stop_output()
56 */
57struct listnode created_effects_list;
58/*
59 * list of active output streams.
60 * Updated by offload_effects_bundle_hal_start_output()
61 * and offload_effects_bundle_hal_stop_output()
62 */
63struct listnode active_outputs_list;
64/*
65 * lock must be held when modifying or accessing
66 * created_effects_list or active_outputs_list
67 */
68pthread_mutex_t lock;
69
70
71/*
72 *  Local functions
73 */
74static void init_once() {
75    list_init(&created_effects_list);
76    list_init(&active_outputs_list);
77
78    pthread_mutex_init(&lock, NULL);
79
80    init_status = 0;
81}
82
83int lib_init()
84{
85    pthread_once(&once, init_once);
86    return init_status;
87}
88
89bool effect_exists(effect_context_t *context)
90{
91    struct listnode *node;
92
93    list_for_each(node, &created_effects_list) {
94        effect_context_t *fx_ctxt = node_to_item(node,
95                                                 effect_context_t,
96                                                 effects_list_node);
97        if (fx_ctxt == context) {
98            return true;
99        }
100    }
101    return false;
102}
103
104output_context_t *get_output(audio_io_handle_t output)
105{
106    struct listnode *node;
107
108    list_for_each(node, &active_outputs_list) {
109        output_context_t *out_ctxt = node_to_item(node,
110                                                  output_context_t,
111                                                  outputs_list_node);
112        if (out_ctxt->handle == output)
113            return out_ctxt;
114    }
115    return NULL;
116}
117
118void add_effect_to_output(output_context_t * output, effect_context_t *context)
119{
120    struct listnode *fx_node;
121
122    list_for_each(fx_node, &output->effects_list) {
123        effect_context_t *fx_ctxt = node_to_item(fx_node,
124                                                 effect_context_t,
125                                                 output_node);
126        if (fx_ctxt == context)
127            return;
128    }
129    list_add_tail(&output->effects_list, &context->output_node);
130    if (context->ops.start)
131        context->ops.start(context, output);
132
133}
134
135void remove_effect_from_output(output_context_t * output,
136                               effect_context_t *context)
137{
138    struct listnode *fx_node;
139
140    list_for_each(fx_node, &output->effects_list) {
141        effect_context_t *fx_ctxt = node_to_item(fx_node,
142                                                 effect_context_t,
143                                                 output_node);
144        if (fx_ctxt == context) {
145            if (context->ops.stop)
146                context->ops.stop(context, output);
147            list_remove(&context->output_node);
148            return;
149        }
150    }
151}
152
153bool effects_enabled()
154{
155    struct listnode *out_node;
156
157    list_for_each(out_node, &active_outputs_list) {
158        struct listnode *fx_node;
159        output_context_t *out_ctxt = node_to_item(out_node,
160                                                  output_context_t,
161                                                  outputs_list_node);
162
163        list_for_each(fx_node, &out_ctxt->effects_list) {
164            effect_context_t *fx_ctxt = node_to_item(fx_node,
165                                                     effect_context_t,
166                                                     output_node);
167            if ((fx_ctxt->state == EFFECT_STATE_ACTIVE) &&
168                (fx_ctxt->ops.process != NULL))
169                return true;
170        }
171    }
172    return false;
173}
174
175
176/*
177 * Interface from audio HAL
178 */
179__attribute__ ((visibility ("default")))
180int offload_effects_bundle_hal_start_output(audio_io_handle_t output, int pcm_id)
181{
182    int ret = 0;
183    struct listnode *node;
184    char mixer_string[128];
185    output_context_t * out_ctxt = NULL;
186
187    ALOGV("%s output %d pcm_id %d", __func__, output, pcm_id);
188
189    if (lib_init() != 0)
190        return init_status;
191
192    pthread_mutex_lock(&lock);
193    if (get_output(output) != NULL) {
194        ALOGW("%s output already started", __func__);
195        ret = -ENOSYS;
196        goto exit;
197    }
198
199    out_ctxt = (output_context_t *)
200                                 malloc(sizeof(output_context_t));
201    out_ctxt->handle = output;
202    out_ctxt->pcm_device_id = pcm_id;
203
204    /* populate the mixer control to send offload parameters */
205    snprintf(mixer_string, sizeof(mixer_string),
206             "%s %d", "Audio Effects Config", out_ctxt->pcm_device_id);
207    out_ctxt->mixer = mixer_open(MIXER_CARD);
208    if (!out_ctxt->mixer) {
209        ALOGE("Failed to open mixer");
210        out_ctxt->ctl = NULL;
211        ret = -EINVAL;
212        free(out_ctxt);
213        goto exit;
214    } else {
215        out_ctxt->ctl = mixer_get_ctl_by_name(out_ctxt->mixer, mixer_string);
216        if (!out_ctxt->ctl) {
217            ALOGE("mixer_get_ctl_by_name failed");
218            mixer_close(out_ctxt->mixer);
219            out_ctxt->mixer = NULL;
220            ret = -EINVAL;
221            free(out_ctxt);
222            goto exit;
223        }
224    }
225
226    list_init(&out_ctxt->effects_list);
227
228    list_for_each(node, &created_effects_list) {
229        effect_context_t *fx_ctxt = node_to_item(node,
230                                                 effect_context_t,
231                                                 effects_list_node);
232        if (fx_ctxt->out_handle == output) {
233            if (fx_ctxt->ops.start)
234                fx_ctxt->ops.start(fx_ctxt, out_ctxt);
235            list_add_tail(&out_ctxt->effects_list, &fx_ctxt->output_node);
236        }
237    }
238    list_add_tail(&active_outputs_list, &out_ctxt->outputs_list_node);
239exit:
240    pthread_mutex_unlock(&lock);
241    return ret;
242}
243
244__attribute__ ((visibility ("default")))
245int offload_effects_bundle_hal_stop_output(audio_io_handle_t output, int pcm_id)
246{
247    int ret;
248    struct listnode *node;
249    struct listnode *fx_node;
250    output_context_t *out_ctxt;
251
252    ALOGV("%s output %d pcm_id %d", __func__, output, pcm_id);
253
254    if (lib_init() != 0)
255        return init_status;
256
257    pthread_mutex_lock(&lock);
258
259    out_ctxt = get_output(output);
260    if (out_ctxt == NULL) {
261        ALOGW("%s output not started", __func__);
262        ret = -ENOSYS;
263        goto exit;
264    }
265
266    if (out_ctxt->mixer)
267        mixer_close(out_ctxt->mixer);
268
269    list_for_each(fx_node, &out_ctxt->effects_list) {
270        effect_context_t *fx_ctxt = node_to_item(fx_node,
271                                                 effect_context_t,
272                                                 output_node);
273        if (fx_ctxt->ops.stop)
274            fx_ctxt->ops.stop(fx_ctxt, out_ctxt);
275    }
276
277    list_remove(&out_ctxt->outputs_list_node);
278
279    free(out_ctxt);
280
281exit:
282    pthread_mutex_unlock(&lock);
283    return ret;
284}
285
286
287/*
288 * Effect operations
289 */
290int set_config(effect_context_t *context, effect_config_t *config)
291{
292    context->config = *config;
293
294    if (context->ops.reset)
295        context->ops.reset(context);
296
297    return 0;
298}
299
300void get_config(effect_context_t *context, effect_config_t *config)
301{
302    *config = context->config;
303}
304
305
306/*
307 * Effect Library Interface Implementation
308 */
309int effect_lib_create(const effect_uuid_t *uuid,
310                         int32_t sessionId,
311                         int32_t ioId,
312                         effect_handle_t *pHandle) {
313    int ret;
314    int i;
315
316    ALOGV("%s: sessionId: %d, ioId: %d", __func__, sessionId, ioId);
317    if (lib_init() != 0)
318        return init_status;
319
320    if (pHandle == NULL || uuid == NULL)
321        return -EINVAL;
322
323    for (i = 0; descriptors[i] != NULL; i++) {
324        if (memcmp(uuid, &descriptors[i]->uuid, sizeof(effect_uuid_t)) == 0)
325            break;
326    }
327
328    if (descriptors[i] == NULL)
329        return -EINVAL;
330
331    effect_context_t *context;
332    if (memcmp(uuid, &equalizer_descriptor.uuid,
333        sizeof(effect_uuid_t)) == 0) {
334        equalizer_context_t *eq_ctxt = (equalizer_context_t *)
335                                       calloc(1, sizeof(equalizer_context_t));
336        context = (effect_context_t *)eq_ctxt;
337        context->ops.init = equalizer_init;
338        context->ops.reset = equalizer_reset;
339        context->ops.set_parameter = equalizer_set_parameter;
340        context->ops.get_parameter = equalizer_get_parameter;
341        context->ops.set_device = equalizer_set_device;
342        context->ops.enable = equalizer_enable;
343        context->ops.disable = equalizer_disable;
344        context->ops.start = equalizer_start;
345        context->ops.stop = equalizer_stop;
346
347        context->desc = &equalizer_descriptor;
348        eq_ctxt->ctl = NULL;
349    } else if (memcmp(uuid, &bassboost_descriptor.uuid,
350               sizeof(effect_uuid_t)) == 0) {
351        bassboost_context_t *bass_ctxt = (bassboost_context_t *)
352                                         calloc(1, sizeof(bassboost_context_t));
353        context = (effect_context_t *)bass_ctxt;
354        context->ops.init = bassboost_init;
355        context->ops.reset = bassboost_reset;
356        context->ops.set_parameter = bassboost_set_parameter;
357        context->ops.get_parameter = bassboost_get_parameter;
358        context->ops.set_device = bassboost_set_device;
359        context->ops.enable = bassboost_enable;
360        context->ops.disable = bassboost_disable;
361        context->ops.start = bassboost_start;
362        context->ops.stop = bassboost_stop;
363
364        context->desc = &bassboost_descriptor;
365        bass_ctxt->ctl = NULL;
366    } else if (memcmp(uuid, &virtualizer_descriptor.uuid,
367               sizeof(effect_uuid_t)) == 0) {
368        virtualizer_context_t *virt_ctxt = (virtualizer_context_t *)
369                                           calloc(1, sizeof(virtualizer_context_t));
370        context = (effect_context_t *)virt_ctxt;
371        context->ops.init = virtualizer_init;
372        context->ops.reset = virtualizer_reset;
373        context->ops.set_parameter = virtualizer_set_parameter;
374        context->ops.get_parameter = virtualizer_get_parameter;
375        context->ops.set_device = virtualizer_set_device;
376        context->ops.enable = virtualizer_enable;
377        context->ops.disable = virtualizer_disable;
378        context->ops.start = virtualizer_start;
379        context->ops.stop = virtualizer_stop;
380
381        context->desc = &virtualizer_descriptor;
382        virt_ctxt->ctl = NULL;
383    } else if ((memcmp(uuid, &aux_env_reverb_descriptor.uuid,
384                sizeof(effect_uuid_t)) == 0) ||
385               (memcmp(uuid, &ins_env_reverb_descriptor.uuid,
386                sizeof(effect_uuid_t)) == 0) ||
387               (memcmp(uuid, &aux_preset_reverb_descriptor.uuid,
388                sizeof(effect_uuid_t)) == 0) ||
389               (memcmp(uuid, &ins_preset_reverb_descriptor.uuid,
390                sizeof(effect_uuid_t)) == 0)) {
391        reverb_context_t *reverb_ctxt = (reverb_context_t *)
392                                        calloc(1, sizeof(reverb_context_t));
393        context = (effect_context_t *)reverb_ctxt;
394        context->ops.init = reverb_init;
395        context->ops.reset = reverb_reset;
396        context->ops.set_parameter = reverb_set_parameter;
397        context->ops.get_parameter = reverb_get_parameter;
398        context->ops.set_device = reverb_set_device;
399        context->ops.enable = reverb_enable;
400        context->ops.disable = reverb_disable;
401        context->ops.start = reverb_start;
402        context->ops.stop = reverb_stop;
403
404        if (memcmp(uuid, &aux_env_reverb_descriptor.uuid,
405                   sizeof(effect_uuid_t)) == 0) {
406            context->desc = &aux_env_reverb_descriptor;
407            reverb_auxiliary_init(reverb_ctxt);
408        } else if (memcmp(uuid, &ins_env_reverb_descriptor.uuid,
409                   sizeof(effect_uuid_t)) == 0) {
410            context->desc = &ins_env_reverb_descriptor;
411            reverb_insert_init(reverb_ctxt);
412        } else if (memcmp(uuid, &aux_preset_reverb_descriptor.uuid,
413                   sizeof(effect_uuid_t)) == 0) {
414            context->desc = &aux_preset_reverb_descriptor;
415            reverb_auxiliary_init(reverb_ctxt);
416        } else if (memcmp(uuid, &ins_preset_reverb_descriptor.uuid,
417                   sizeof(effect_uuid_t)) == 0) {
418            context->desc = &ins_preset_reverb_descriptor;
419            reverb_preset_init(reverb_ctxt);
420        }
421        reverb_ctxt->ctl = NULL;
422    } else {
423        return -EINVAL;
424    }
425
426    context->itfe = &effect_interface;
427    context->state = EFFECT_STATE_UNINITIALIZED;
428    context->out_handle = (audio_io_handle_t)ioId;
429
430    ret = context->ops.init(context);
431    if (ret < 0) {
432        ALOGW("%s init failed", __func__);
433        free(context);
434        return ret;
435    }
436
437    context->state = EFFECT_STATE_INITIALIZED;
438
439    pthread_mutex_lock(&lock);
440    list_add_tail(&created_effects_list, &context->effects_list_node);
441    output_context_t *out_ctxt = get_output(ioId);
442    if (out_ctxt != NULL)
443        add_effect_to_output(out_ctxt, context);
444    pthread_mutex_unlock(&lock);
445
446    *pHandle = (effect_handle_t)context;
447
448    ALOGV("%s created context %p", __func__, context);
449
450    return 0;
451
452}
453
454int effect_lib_release(effect_handle_t handle)
455{
456    effect_context_t *context = (effect_context_t *)handle;
457    int status;
458
459    if (lib_init() != 0)
460        return init_status;
461
462    ALOGV("%s context %p", __func__, handle);
463    pthread_mutex_lock(&lock);
464    status = -EINVAL;
465    if (effect_exists(context)) {
466        output_context_t *out_ctxt = get_output(context->out_handle);
467        if (out_ctxt != NULL)
468            remove_effect_from_output(out_ctxt, context);
469        list_remove(&context->effects_list_node);
470        if (context->ops.release)
471            context->ops.release(context);
472        free(context);
473        status = 0;
474    }
475    pthread_mutex_unlock(&lock);
476
477    return status;
478}
479
480int effect_lib_get_descriptor(const effect_uuid_t *uuid,
481                              effect_descriptor_t *descriptor)
482{
483    int i;
484
485    if (lib_init() != 0)
486        return init_status;
487
488    if (descriptor == NULL || uuid == NULL) {
489        ALOGV("%s called with NULL pointer", __func__);
490        return -EINVAL;
491    }
492
493    for (i = 0; descriptors[i] != NULL; i++) {
494        if (memcmp(uuid, &descriptors[i]->uuid, sizeof(effect_uuid_t)) == 0) {
495            *descriptor = *descriptors[i];
496            return 0;
497        }
498    }
499
500    return  -EINVAL;
501}
502
503
504/*
505 * Effect Control Interface Implementation
506 */
507
508/* Stub function for effect interface: never called for offloaded effects */
509int effect_process(effect_handle_t self,
510                       audio_buffer_t *inBuffer __unused,
511                       audio_buffer_t *outBuffer __unused)
512{
513    effect_context_t * context = (effect_context_t *)self;
514    int status = 0;
515
516    ALOGW("%s Called ?????", __func__);
517
518    pthread_mutex_lock(&lock);
519    if (!effect_exists(context)) {
520        status = -ENOSYS;
521        goto exit;
522    }
523
524    if (context->state != EFFECT_STATE_ACTIVE) {
525        status = -ENODATA;
526        goto exit;
527    }
528
529exit:
530    pthread_mutex_unlock(&lock);
531    return status;
532}
533
534int effect_command(effect_handle_t self, uint32_t cmdCode, uint32_t cmdSize,
535                   void *pCmdData, uint32_t *replySize, void *pReplyData)
536{
537
538    effect_context_t * context = (effect_context_t *)self;
539    int retsize;
540    int status = 0;
541
542    pthread_mutex_lock(&lock);
543
544    if (!effect_exists(context)) {
545        status = -ENOSYS;
546        goto exit;
547    }
548
549    if (context == NULL || context->state == EFFECT_STATE_UNINITIALIZED) {
550        status = -ENOSYS;
551        goto exit;
552    }
553
554    switch (cmdCode) {
555    case EFFECT_CMD_INIT:
556        if (pReplyData == NULL || *replySize != sizeof(int)) {
557            status = -EINVAL;
558            goto exit;
559        }
560        if (context->ops.init)
561            *(int *) pReplyData = context->ops.init(context);
562        else
563            *(int *) pReplyData = 0;
564        break;
565    case EFFECT_CMD_SET_CONFIG:
566        if (pCmdData == NULL || cmdSize != sizeof(effect_config_t)
567                || pReplyData == NULL || *replySize != sizeof(int)) {
568            status = -EINVAL;
569            goto exit;
570        }
571        *(int *) pReplyData = set_config(context, (effect_config_t *) pCmdData);
572        break;
573    case EFFECT_CMD_GET_CONFIG:
574        if (pReplyData == NULL ||
575            *replySize != sizeof(effect_config_t)) {
576            status = -EINVAL;
577            goto exit;
578        }
579        if (!context->offload_enabled) {
580            status = -EINVAL;
581            goto exit;
582        }
583
584        get_config(context, (effect_config_t *)pReplyData);
585        break;
586    case EFFECT_CMD_RESET:
587        if (context->ops.reset)
588            context->ops.reset(context);
589        break;
590    case EFFECT_CMD_ENABLE:
591        if (pReplyData == NULL || *replySize != sizeof(int)) {
592            status = -EINVAL;
593            goto exit;
594        }
595        if (context->state != EFFECT_STATE_INITIALIZED) {
596            status = -ENOSYS;
597            goto exit;
598        }
599        context->state = EFFECT_STATE_ACTIVE;
600        if (context->ops.enable)
601            context->ops.enable(context);
602        ALOGV("%s EFFECT_CMD_ENABLE", __func__);
603        *(int *)pReplyData = 0;
604        break;
605    case EFFECT_CMD_DISABLE:
606        if (pReplyData == NULL || *replySize != sizeof(int)) {
607            status = -EINVAL;
608            goto exit;
609        }
610        if (context->state != EFFECT_STATE_ACTIVE) {
611            status = -ENOSYS;
612            goto exit;
613        }
614        context->state = EFFECT_STATE_INITIALIZED;
615        if (context->ops.disable)
616            context->ops.disable(context);
617        ALOGV("%s EFFECT_CMD_DISABLE", __func__);
618        *(int *)pReplyData = 0;
619        break;
620    case EFFECT_CMD_GET_PARAM: {
621        if (pCmdData == NULL ||
622            cmdSize < (int)(sizeof(effect_param_t) + sizeof(uint32_t)) ||
623            pReplyData == NULL ||
624            *replySize < (int)(sizeof(effect_param_t) + sizeof(uint32_t) + sizeof(uint16_t)) ||
625            // constrain memcpy below
626            ((effect_param_t *)pCmdData)->psize > *replySize - sizeof(effect_param_t)) {
627            status = -EINVAL;
628            ALOGV("EFFECT_CMD_GET_PARAM invalid command cmdSize %d *replySize %d",
629                  cmdSize, *replySize);
630            goto exit;
631        }
632        if (!context->offload_enabled) {
633            status = -EINVAL;
634            goto exit;
635        }
636        effect_param_t *q = (effect_param_t *)pCmdData;
637        memcpy(pReplyData, pCmdData, sizeof(effect_param_t) + q->psize);
638        effect_param_t *p = (effect_param_t *)pReplyData;
639        if (context->ops.get_parameter)
640            context->ops.get_parameter(context, p, replySize);
641        } break;
642    case EFFECT_CMD_SET_PARAM: {
643        if (pCmdData == NULL ||
644            cmdSize < (int)(sizeof(effect_param_t) + sizeof(uint32_t) +
645                            sizeof(uint16_t)) ||
646            pReplyData == NULL || *replySize != sizeof(int32_t)) {
647            status = -EINVAL;
648            ALOGV("EFFECT_CMD_SET_PARAM invalid command cmdSize %d *replySize %d",
649                  cmdSize, *replySize);
650            goto exit;
651        }
652        *(int32_t *)pReplyData = 0;
653        effect_param_t *p = (effect_param_t *)pCmdData;
654        if (context->ops.set_parameter)
655            *(int32_t *)pReplyData = context->ops.set_parameter(context, p,
656                                                                *replySize);
657
658        } break;
659    case EFFECT_CMD_SET_DEVICE: {
660        uint32_t device;
661        ALOGV("\t EFFECT_CMD_SET_DEVICE start");
662        if (pCmdData == NULL || cmdSize < sizeof(uint32_t)) {
663            status = -EINVAL;
664            ALOGV("EFFECT_CMD_SET_DEVICE invalid command cmdSize %d", cmdSize);
665            goto exit;
666        }
667        device = *(uint32_t *)pCmdData;
668        if (context->ops.set_device)
669            context->ops.set_device(context, device);
670        } break;
671    case EFFECT_CMD_SET_VOLUME:
672    case EFFECT_CMD_SET_AUDIO_MODE:
673        break;
674
675    case EFFECT_CMD_OFFLOAD: {
676        output_context_t *out_ctxt;
677
678        if (cmdSize != sizeof(effect_offload_param_t) || pCmdData == NULL
679                || pReplyData == NULL || *replySize != sizeof(int)) {
680            ALOGV("%s EFFECT_CMD_OFFLOAD bad format", __func__);
681            status = -EINVAL;
682            break;
683        }
684
685        effect_offload_param_t* offload_param = (effect_offload_param_t*)pCmdData;
686
687        ALOGV("%s EFFECT_CMD_OFFLOAD offload %d output %d", __func__,
688              offload_param->isOffload, offload_param->ioHandle);
689
690        *(int *)pReplyData = 0;
691
692        context->offload_enabled = offload_param->isOffload;
693        if (context->out_handle == offload_param->ioHandle)
694            break;
695
696        out_ctxt = get_output(context->out_handle);
697        if (out_ctxt != NULL)
698            remove_effect_from_output(out_ctxt, context);
699
700        context->out_handle = offload_param->ioHandle;
701        out_ctxt = get_output(context->out_handle);
702        if (out_ctxt != NULL)
703            add_effect_to_output(out_ctxt, context);
704
705        } break;
706
707
708    default:
709        if (cmdCode >= EFFECT_CMD_FIRST_PROPRIETARY && context->ops.command)
710            status = context->ops.command(context, cmdCode, cmdSize,
711                                          pCmdData, replySize, pReplyData);
712        else {
713            ALOGW("%s invalid command %d", __func__, cmdCode);
714            status = -EINVAL;
715        }
716        break;
717    }
718
719exit:
720    pthread_mutex_unlock(&lock);
721
722    return status;
723}
724
725/* Effect Control Interface Implementation: get_descriptor */
726int effect_get_descriptor(effect_handle_t   self,
727                          effect_descriptor_t *descriptor)
728{
729    effect_context_t *context = (effect_context_t *)self;
730
731    if (!effect_exists(context) || (descriptor == NULL))
732        return -EINVAL;
733
734    *descriptor = *context->desc;
735
736    return 0;
737}
738
739bool effect_is_active(effect_context_t * ctxt) {
740    return ctxt->state == EFFECT_STATE_ACTIVE;
741}
742
743/* effect_handle_t interface implementation for offload effects */
744const struct effect_interface_s effect_interface = {
745    effect_process,
746    effect_command,
747    effect_get_descriptor,
748    NULL,
749};
750
751__attribute__ ((visibility ("default")))
752audio_effect_library_t AUDIO_EFFECT_LIBRARY_INFO_SYM = {
753    tag : AUDIO_EFFECT_LIBRARY_TAG,
754    version : EFFECT_LIBRARY_API_VERSION,
755    name : "Offload Effects Bundle Library",
756    implementor : "The Android Open Source Project",
757    create_effect : effect_lib_create,
758    release_effect : effect_lib_release,
759    get_descriptor : effect_lib_get_descriptor,
760};
761