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