Home | History | Annotate | Download | only in post_proc
      1 /*
      2  * Copyright (C) 2015 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 "volume_listener"
     18 //#define LOG_NDEBUG 0
     19 #include <stdlib.h>
     20 #include <dlfcn.h>
     21 
     22 #include <cutils/list.h>
     23 #include <cutils/log.h>
     24 #include <hardware/audio_effect.h>
     25 #include <cutils/properties.h>
     26 #include <platform_api.h>
     27 
     28 #define PRIMARY_HAL_PATH XSTR(LIB_AUDIO_HAL)
     29 #define XSTR(x) STR(x)
     30 #define STR(x) #x
     31 
     32 #define VOL_FLAG ( EFFECT_FLAG_TYPE_INSERT | \
     33                    EFFECT_FLAG_VOLUME_IND | \
     34                    EFFECT_FLAG_DEVICE_IND | \
     35                    EFFECT_FLAG_OFFLOAD_SUPPORTED | \
     36                    EFFECT_FLAG_NO_PROCESS)
     37 
     38 #define PRINT_STREAM_TYPE(i) ALOGV("descriptor found and is of stream type %s ",\
     39                                                             i == MUSIC?"MUSIC": \
     40                                                             i == RING?"RING": \
     41                                                             i == ALARM?"ALARM": \
     42                                                             i == VOICE_CALL?"Voice_call": \
     43                                                             i == NOTIFICATION?"Notification":\
     44                                                             "--INVALID--"); \
     45 
     46 #define MAX_GAIN_LEVELS 5
     47 
     48 #define AHAL_GAIN_DEPENDENT_INTERFACE_FUNCTION "audio_hw_send_gain_dep_calibration"
     49 #define AHAL_GAIN_GET_MAPPING_TABLE "audio_hw_get_gain_level_mapping"
     50 #define DEFAULT_CAL_STEP 0
     51 
     52 enum {
     53     VOL_LISTENER_STATE_UNINITIALIZED,
     54     VOL_LISTENER_STATE_INITIALIZED,
     55     VOL_LISTENER_STATE_ACTIVE,
     56 };
     57 
     58 typedef struct vol_listener_context_s vol_listener_context_t;
     59 static const struct effect_interface_s effect_interface;
     60 
     61 /* flag to avoid multiple initialization */
     62 static bool initialized = false;
     63 
     64 /* current gain dep cal level that was pushed succesfully */
     65 static int current_gain_dep_cal_level = -1;
     66 
     67 enum STREAM_TYPE {
     68     MUSIC,
     69     RING,
     70     ALARM,
     71     VOICE_CALL,
     72     NOTIFICATION,
     73     MAX_STREAM_TYPES,
     74 };
     75 
     76 struct vol_listener_context_s {
     77     const struct effect_interface_s *itfe;
     78     struct listnode effect_list_node;
     79     effect_config_t config;
     80     const effect_descriptor_t *desc;
     81     uint32_t stream_type;
     82     uint32_t session_id;
     83     uint32_t state;
     84     uint32_t dev_id;
     85     float left_vol;
     86     float right_vol;
     87 };
     88 
     89 /* volume listener, music UUID: 08b8b058-0590-11e5-ac71-0025b32654a0 */
     90 const effect_descriptor_t vol_listener_music_descriptor = {
     91     { 0x08b8b058, 0x0590, 0x11e5, 0xac71, { 0x00, 0x25, 0xb3, 0x26, 0x54, 0xa0 } },  // type
     92     { 0x08b8b058, 0x0590, 0x11e5, 0xac71, { 0x00, 0x25, 0xb3, 0x26, 0x54, 0xa0 } },  // uuid
     93     EFFECT_CONTROL_API_VERSION,
     94     VOL_FLAG,
     95     0, /* TODO */
     96     1,
     97     "Volume listener for Music",
     98     "Qualcomm Technologies Inc.",
     99 };
    100 
    101 /* volume listener, ring UUID: 0956df94-0590-11e5-bdbe-0025b32654a0 */
    102 const effect_descriptor_t vol_listener_ring_descriptor = {
    103     { 0x0956df94, 0x0590, 0x11e5, 0xbdbe, { 0x00, 0x25, 0xb3, 0x26, 0x54, 0xa0 } }, // type
    104     { 0x0956df94, 0x0590, 0x11e5, 0xbdbe, { 0x00, 0x25, 0xb3, 0x26, 0x54, 0xa0 } }, // uuid
    105     EFFECT_CONTROL_API_VERSION,
    106     VOL_FLAG,
    107     0, /* TODO */
    108     1,
    109     "Volume listener for ring",
    110     "Qualcomm Technologies Inc",
    111 };
    112 
    113 /* volume listener, alarm UUID: 09f303e2-0590-11e5-8fdb-0025b32654a0 */
    114 const effect_descriptor_t vol_listener_alarm_descriptor = {
    115     { 0x09f303e2, 0x0590, 0x11e5, 0x8fdb, { 0x00, 0x25, 0xb3, 0x26, 0x54, 0xa0 } }, // type
    116     { 0x09f303e2, 0x0590, 0x11e5, 0x8fdb, { 0x00, 0x25, 0xb3, 0x26, 0x54, 0xa0 } }, // uuid
    117     EFFECT_CONTROL_API_VERSION,
    118     VOL_FLAG,
    119     0, /* TODO */
    120     1,
    121     "Volume listener for alarm",
    122     "Qualcomm Technologies Inc",
    123 };
    124 
    125 /* volume listener, voice call UUID: 0ace5c08-0590-11e5-ae9e-0025b32654a0 */
    126 const effect_descriptor_t vol_listener_voice_call_descriptor = {
    127     { 0x0ace5c08, 0x0590, 0x11e5, 0xae9e, { 0x00, 0x25, 0xb3, 0x26, 0x54, 0xa0 } }, // type
    128     { 0x0ace5c08, 0x0590, 0x11e5, 0xae9e, { 0x00, 0x25, 0xb3, 0x26, 0x54, 0xa0 } }, // uuid
    129     EFFECT_CONTROL_API_VERSION,
    130     VOL_FLAG,
    131     0, /* TODO */
    132     1,
    133     "Volume listener for voice call",
    134     "Qualcomm Technologies Inc",
    135 };
    136 
    137 /* volume listener, notification UUID: 0b776dde-0590-11e5-81ba-0025b32654a0 */
    138 const effect_descriptor_t vol_listener_notification_descriptor = {
    139     { 0x0b776dde, 0x0590, 0x11e5, 0x81ba, { 0x00, 0x25, 0xb3, 0x26, 0x54, 0xa0 } }, // type
    140     { 0x0b776dde, 0x0590, 0x11e5, 0x81ba, { 0x00, 0x25, 0xb3, 0x26, 0x54, 0xa0 } }, // uuid
    141     EFFECT_CONTROL_API_VERSION,
    142     VOL_FLAG,
    143     0, /* TODO */
    144     1,
    145     "Volume listener for notification",
    146     "Qualcomm Technologies Inc",
    147 };
    148 
    149 static int total_volume_cal_step = MAX_GAIN_LEVELS;
    150 
    151 // using gain level for non-drc volume curve
    152 struct amp_db_and_gain_table  volume_curve_gain_mapping_table[MAX_VOLUME_CAL_STEPS] =
    153 {
    154     /* Level 0 in the calibration database contains default calibration */
    155     { 0.001774, -55, 5 },
    156     { 0.501187,  -6, 4 },
    157     { 0.630957,  -4, 3 },
    158     { 0.794328,  -2, 2 },
    159     { 1.0,        0, 1 },
    160     { 0, 0, -1 },
    161     { 0, 0, -1 },
    162     { 0, 0, -1 },
    163     { 0, 0, -1 },
    164     { 0, 0, -1 },
    165     { 0, 0, -1 },
    166     { 0, 0, -1 },
    167     { 0, 0, -1 },
    168     { 0, 0, -1 },
    169     { 0, 0, -1 }
    170 };
    171 
    172 static const effect_descriptor_t *descriptors[] = {
    173     &vol_listener_music_descriptor,
    174     &vol_listener_ring_descriptor,
    175     &vol_listener_alarm_descriptor,
    176     &vol_listener_voice_call_descriptor,
    177     &vol_listener_notification_descriptor,
    178     NULL,
    179 };
    180 
    181 pthread_once_t once = PTHREAD_ONCE_INIT;
    182 /* flag to indicate if init was success */
    183 static int init_status;
    184 
    185 /* current volume level for which gain dep cal level was selected */
    186 static float current_vol = 0.0;
    187 
    188 /* HAL interface to send calibration */
    189 static bool (*send_gain_dep_cal)(int);
    190 
    191 static int (*get_custom_gain_table)(struct amp_db_and_gain_table *, int);
    192 
    193 /* if dumping allowed */
    194 static bool dumping_enabled = false;
    195 
    196 /* list of created effects. */
    197 struct listnode vol_effect_list;
    198 
    199 /* lock must be held when modifying or accessing created_effects_list */
    200 pthread_mutex_t vol_listner_init_lock;
    201 
    202 /*
    203  *  Local functions
    204  */
    205 static void dump_list_l()
    206 {
    207     struct listnode *node;
    208     vol_listener_context_t *context;
    209 
    210     ALOGW("DUMP_START :: ===========");
    211 
    212     list_for_each(node, &vol_effect_list) {
    213         context = node_to_item(node, struct vol_listener_context_s, effect_list_node);
    214         // dump stream_type / Device / session_id / left / righ volume
    215         ALOGW("%s: streamType [%s] Device [%d] state [%d] sessionID [%d] volume (L/R) [%f / %f] ",
    216                 __func__,
    217                 context->stream_type == MUSIC ? "MUSIC" :
    218                 context->stream_type == RING ? "RING" :
    219                 context->stream_type == ALARM ? "ALARM" :
    220                 context->stream_type == VOICE_CALL ? "VOICE_CALL" :
    221                 context->stream_type == NOTIFICATION ? "NOTIFICATION" : "--INVALID--",
    222                 context->dev_id, context->state, context->session_id, context->left_vol,context->right_vol);
    223     }
    224 
    225     ALOGW("DUMP_END :: ===========");
    226 }
    227 
    228 static void check_and_set_gain_dep_cal()
    229 {
    230     // iterate through list and make decision to set new gain dep cal level for speaker device
    231     // 1. find all usecase active on speaker
    232     // 2. find average of left and right for each usecase
    233     // 3. find the highest of all the active usecase
    234     // 4. if new value is different than the current value then load new calibration
    235 
    236     struct listnode *node = NULL;
    237     float new_vol = -1.0;
    238     int max_level = 0;
    239     vol_listener_context_t *context = NULL;
    240     if (dumping_enabled) {
    241         dump_list_l();
    242     }
    243 
    244     ALOGV("%s ==> Start ...", __func__);
    245 
    246     // select the highest volume on speaker device
    247     list_for_each(node, &vol_effect_list) {
    248         context = node_to_item(node, struct vol_listener_context_s, effect_list_node);
    249         if ((context->state == VOL_LISTENER_STATE_ACTIVE) &&
    250             (context->dev_id & AUDIO_DEVICE_OUT_SPEAKER) &&
    251             (new_vol < (context->left_vol + context->right_vol) / 2)) {
    252             new_vol = (context->left_vol + context->right_vol) / 2;
    253         }
    254     }
    255 
    256     if (new_vol != current_vol) {
    257         ALOGV("%s:: Change in decision :: current volume is %f new volume is %f",
    258               __func__, current_vol, new_vol);
    259 
    260         if (send_gain_dep_cal != NULL) {
    261             // send Gain dep cal level
    262             int gain_dep_cal_level = -1;
    263 
    264             if (new_vol >= 1 && total_volume_cal_step > 0) { // max amplitude, use highest DRC level
    265                 gain_dep_cal_level = volume_curve_gain_mapping_table[total_volume_cal_step - 1].level;
    266             } else if (new_vol == -1) {
    267                 gain_dep_cal_level = DEFAULT_CAL_STEP;
    268             } else if (new_vol == 0) {
    269                 gain_dep_cal_level = volume_curve_gain_mapping_table[0].level;
    270             } else {
    271                 for (max_level = 0; max_level + 1 < total_volume_cal_step; max_level++) {
    272                     if (new_vol < volume_curve_gain_mapping_table[max_level + 1].amp &&
    273                         new_vol >= volume_curve_gain_mapping_table[max_level].amp) {
    274                         gain_dep_cal_level = volume_curve_gain_mapping_table[max_level].level;
    275                         ALOGV("%s: volume(%f), gain dep cal selcetd %d ",
    276                               __func__, current_vol, gain_dep_cal_level);
    277                         break;
    278                     }
    279                 }
    280             }
    281 
    282             // check here if previous gain dep cal level was not same
    283             if (gain_dep_cal_level != -1) {
    284                 if (gain_dep_cal_level != current_gain_dep_cal_level) {
    285                     // decision made .. send new level now
    286                     if (!send_gain_dep_cal(gain_dep_cal_level)) {
    287                         ALOGE("%s: Failed to set gain dep cal level", __func__);
    288                     } else {
    289                         // Success in setting the gain dep cal level, store new level and Volume
    290                         if (dumping_enabled) {
    291                             ALOGW("%s: (old/new) Volume (%f/%f) (old/new) level (%d/%d)",
    292                                   __func__, current_vol, new_vol, current_gain_dep_cal_level,
    293                                   gain_dep_cal_level);
    294                         } else {
    295                             ALOGV("%s: Change in Cal::(old/new) Volume (%f/%f) (old/new) level (%d/%d)",
    296                                   __func__, current_vol, new_vol, current_gain_dep_cal_level,
    297                                   gain_dep_cal_level);
    298                         }
    299                         current_gain_dep_cal_level = gain_dep_cal_level;
    300                         current_vol = new_vol;
    301                     }
    302                 } else {
    303                     if (dumping_enabled) {
    304                         ALOGW("%s: volume changed but gain dep cal level is still the same",
    305                               __func__);
    306                     } else {
    307                         ALOGV("%s: volume changed but gain dep cal level is still the same",
    308                               __func__);
    309                     }
    310                 }
    311             } else {
    312                 ALOGW("%s: Failed to find gain dep cal level for volume %f", __func__, new_vol);
    313             }
    314         } else {
    315             ALOGE("%s: not able to send calibration, NULL function pointer",
    316                   __func__);
    317         }
    318     } else {
    319         ALOGV("%s:: volume not changed, stick to same config ..... ", __func__);
    320     }
    321 
    322     ALOGV("check_and_set_gain_dep_cal ==> End ");
    323 }
    324 
    325 /*
    326  * Effect Control Interface Implementation
    327  */
    328 
    329 static inline int16_t clamp16(int32_t sample)
    330 {
    331     if ((sample>>15) ^ (sample>>31))
    332         sample = 0x7FFF ^ (sample>>31);
    333     return sample;
    334 }
    335 
    336 static int vol_effect_command(effect_handle_t self,
    337                               uint32_t cmd_code, uint32_t cmd_size,
    338                               void *p_cmd_data, uint32_t *reply_size,
    339                               void *p_reply_data)
    340 {
    341     vol_listener_context_t *context = (vol_listener_context_t *)self;
    342     int status = 0;
    343 
    344     ALOGV("%s Called ", __func__);
    345     pthread_mutex_lock(&vol_listner_init_lock);
    346 
    347     if (context == NULL || context->state == VOL_LISTENER_STATE_UNINITIALIZED) {
    348         ALOGE("%s: %s is NULL", __func__, (context == NULL) ?
    349               "context" : "context->state");
    350         status = -EINVAL;
    351         goto exit;
    352     }
    353 
    354     switch (cmd_code) {
    355     case EFFECT_CMD_INIT:
    356         ALOGV("%s :: cmd called EFFECT_CMD_INIT", __func__);
    357         if (p_reply_data == NULL || *reply_size != sizeof(int)) {
    358             ALOGE("%s: EFFECT_CMD_INIT: %s, sending -EINVAL", __func__,
    359                   (p_reply_data == NULL) ? "p_reply_data is NULL" :
    360                   "*reply_size != sizeof(int)");
    361             return -EINVAL;
    362         }
    363         *(int *)p_reply_data = 0;
    364         break;
    365 
    366     case EFFECT_CMD_SET_CONFIG:
    367         ALOGV("%s :: cmd called EFFECT_CMD_SET_CONFIG", __func__);
    368         if (p_cmd_data == NULL || cmd_size != sizeof(effect_config_t)
    369                 || p_reply_data == NULL || reply_size == NULL || *reply_size != sizeof(int)) {
    370             return -EINVAL;
    371         }
    372         context->config = *(effect_config_t *)p_cmd_data;
    373         *(int *)p_reply_data = 0;
    374         break;
    375 
    376     case EFFECT_CMD_GET_CONFIG:
    377         ALOGV("%s :: cmd called EFFECT_CMD_GET_CONFIG", __func__);
    378         break;
    379 
    380     case EFFECT_CMD_RESET:
    381         ALOGV("%s :: cmd called EFFECT_CMD_RESET", __func__);
    382         break;
    383 
    384     case EFFECT_CMD_SET_AUDIO_MODE:
    385         ALOGV("%s :: cmd called EFFECT_CMD_SET_AUDIO_MODE", __func__);
    386         break;
    387 
    388     case EFFECT_CMD_OFFLOAD:
    389         ALOGV("%s :: cmd called EFFECT_CMD_OFFLOAD", __func__);
    390         if (p_reply_data == NULL || *reply_size != sizeof(int)) {
    391             ALOGE("%s: EFFECT_CMD_OFFLOAD: %s, sending -EINVAL", __func__,
    392                   (p_reply_data == NULL) ? "p_reply_data is NULL" :
    393                   "*reply_size != sizeof(int)");
    394             return -EINVAL;
    395         }
    396         *(int *)p_reply_data = 0;
    397         break;
    398 
    399     case EFFECT_CMD_ENABLE:
    400         ALOGV("%s :: cmd called EFFECT_CMD_ENABLE", __func__);
    401         if (p_reply_data == NULL || *reply_size != sizeof(int)) {
    402             ALOGE("%s: EFFECT_CMD_ENABLE: %s, sending -EINVAL", __func__,
    403                    (p_reply_data == NULL) ? "p_reply_data is NULL" :
    404                    "*reply_size != sizeof(int)");
    405             status = -EINVAL;
    406             goto exit;
    407         }
    408 
    409         if (context->state != VOL_LISTENER_STATE_INITIALIZED) {
    410             ALOGE("%s: EFFECT_CMD_ENABLE : state not INITIALIZED", __func__);
    411             status = -ENOSYS;
    412             goto exit;
    413         }
    414 
    415         context->state = VOL_LISTENER_STATE_ACTIVE;
    416         *(int *)p_reply_data = 0;
    417 
    418         // After changing the state and if device is speaker
    419         // recalculate gain dep cal level
    420         if (context->dev_id == AUDIO_DEVICE_OUT_SPEAKER) {
    421                 check_and_set_gain_dep_cal();
    422         }
    423 
    424         break;
    425 
    426     case EFFECT_CMD_DISABLE:
    427         ALOGV("%s :: cmd called EFFECT_CMD_DISABLE", __func__);
    428         if (p_reply_data == NULL || *reply_size != sizeof(int)) {
    429             ALOGE("%s: EFFECT_CMD_DISABLE: %s, sending -EINVAL", __func__,
    430                   (p_reply_data == NULL) ? "p_reply_data is NULL" :
    431                   "*reply_size != sizeof(int)");
    432             status = -EINVAL;
    433             goto exit;
    434         }
    435 
    436         if (context->state != VOL_LISTENER_STATE_ACTIVE) {
    437             ALOGE("%s: EFFECT_CMD_ENABLE : state not ACTIVE", __func__);
    438             status = -ENOSYS;
    439             goto exit;
    440         }
    441 
    442         context->state = VOL_LISTENER_STATE_INITIALIZED;
    443         *(int *)p_reply_data = 0;
    444 
    445         // After changing the state and if device is speaker
    446         // recalculate gain dep cal level
    447         if (context->dev_id == AUDIO_DEVICE_OUT_SPEAKER) {
    448             check_and_set_gain_dep_cal();
    449         }
    450 
    451         break;
    452 
    453     case EFFECT_CMD_GET_PARAM:
    454         ALOGV("%s :: cmd called EFFECT_CMD_GET_PARAM", __func__);
    455         break;
    456 
    457     case EFFECT_CMD_SET_PARAM:
    458         ALOGV("%s :: cmd called EFFECT_CMD_SET_PARAM", __func__);
    459         break;
    460 
    461     case EFFECT_CMD_SET_DEVICE:
    462     {
    463         uint32_t new_device;
    464         bool recompute_gain_dep_cal_Level = false;
    465         ALOGV("cmd called EFFECT_CMD_SET_DEVICE ");
    466 
    467         if (p_cmd_data == NULL) {
    468             ALOGE("%s: EFFECT_CMD_SET_DEVICE: cmd data NULL", __func__);
    469             status = -EINVAL;
    470             goto exit;
    471         }
    472 
    473         new_device = *(uint32_t *)p_cmd_data;
    474         ALOGV("%s :: EFFECT_CMD_SET_DEVICE: (current/new) device (0x%x / 0x%x)",
    475                __func__, context->dev_id, new_device);
    476 
    477         // check if old or new device is speaker
    478         if ((context->dev_id ==  AUDIO_DEVICE_OUT_SPEAKER) ||
    479             (new_device == AUDIO_DEVICE_OUT_SPEAKER)) {
    480             recompute_gain_dep_cal_Level = true;
    481         }
    482 
    483         context->dev_id = new_device;
    484 
    485         if (recompute_gain_dep_cal_Level) {
    486             check_and_set_gain_dep_cal();
    487         }
    488     }
    489     break;
    490 
    491     case EFFECT_CMD_SET_VOLUME:
    492     {
    493         float left_vol = 0, right_vol = 0;
    494         bool recompute_gain_dep_cal_Level = false;
    495 
    496         ALOGV("cmd called EFFECT_CMD_SET_VOLUME");
    497         if (p_cmd_data == NULL || cmd_size != 2 * sizeof(uint32_t)) {
    498             ALOGE("%s: EFFECT_CMD_SET_VOLUME: %s", __func__, (p_cmd_data == NULL) ?
    499                   "p_cmd_data is NULL" : "cmd_size issue");
    500             status = -EINVAL;
    501             goto exit;
    502         }
    503 
    504         if (context->dev_id == AUDIO_DEVICE_OUT_SPEAKER) {
    505             recompute_gain_dep_cal_Level = true;
    506         }
    507 
    508         left_vol = (float)(*(uint32_t *)p_cmd_data) / (1 << 24);
    509         right_vol = (float)(*((uint32_t *)p_cmd_data + 1)) / (1 << 24);
    510         ALOGV("Current Volume (%f / %f ) new Volume (%f / %f)", context->left_vol,
    511               context->right_vol, left_vol, right_vol);
    512 
    513         context->left_vol = left_vol;
    514         context->right_vol = right_vol;
    515 
    516         // recompute gan dep cal level only if volume changed on speaker device
    517         if (recompute_gain_dep_cal_Level) {
    518             check_and_set_gain_dep_cal();
    519         }
    520     }
    521     break;
    522 
    523     default:
    524         ALOGW("volume_listener_command invalid command %d", cmd_code);
    525         status = -ENOSYS;
    526         break;
    527     }
    528 
    529 exit:
    530     pthread_mutex_unlock(&vol_listner_init_lock);
    531     return status;
    532 }
    533 
    534 /* Effect Control Interface Implementation: get_descriptor */
    535 static int vol_effect_get_descriptor(effect_handle_t   self,
    536                                      effect_descriptor_t *descriptor)
    537 {
    538     vol_listener_context_t *context = (vol_listener_context_t *)self;
    539     ALOGV("%s Called ", __func__);
    540 
    541     if (descriptor == NULL) {
    542         ALOGE("%s: descriptor is NULL", __func__);
    543         return -EINVAL;
    544     }
    545 
    546     *descriptor = *context->desc;
    547     return 0;
    548 }
    549 
    550 static void init_once()
    551 {
    552     int max_table_ent = 0;
    553     if (initialized) {
    554         ALOGV("%s : already init .. do nothing", __func__);
    555         return;
    556     }
    557 
    558     ALOGD("%s Called ", __func__);
    559     send_gain_dep_cal = NULL;
    560     get_custom_gain_table = NULL;
    561 
    562     pthread_mutex_init(&vol_listner_init_lock, NULL);
    563 
    564     // get hal function pointer
    565     if (access(PRIMARY_HAL_PATH, R_OK) == 0) {
    566         void *hal_lib_pointer = dlopen(PRIMARY_HAL_PATH, RTLD_NOW);
    567         if (hal_lib_pointer == NULL) {
    568             ALOGE("%s: DLOPEN failed for %s", __func__, PRIMARY_HAL_PATH);
    569         } else {
    570             ALOGV("%s: DLOPEN of %s Succes .. next get HAL entry function", __func__, PRIMARY_HAL_PATH);
    571             send_gain_dep_cal = (bool (*)(int))dlsym(hal_lib_pointer, AHAL_GAIN_DEPENDENT_INTERFACE_FUNCTION);
    572             if (send_gain_dep_cal == NULL) {
    573                 ALOGE("Couldnt able to get the function symbol");
    574             }
    575             get_custom_gain_table = (int (*) (struct amp_db_and_gain_table *, int))dlsym(hal_lib_pointer, AHAL_GAIN_GET_MAPPING_TABLE);
    576             if (get_custom_gain_table == NULL) {
    577                 ALOGE("Couldnt able to get the function AHAL_GAIN_GET_MAPPING_TABLE  symbol");
    578             } else {
    579                 max_table_ent = get_custom_gain_table(volume_curve_gain_mapping_table, MAX_VOLUME_CAL_STEPS);
    580                 // if number of entries is 0 use default
    581                 // if number of entries > MAX_VOLUME_CAL_STEPS (this should never happen) then in this case
    582                 // use only default number of steps but this will result in unexpected behaviour
    583 
    584                 if (max_table_ent > 0 && max_table_ent <= MAX_VOLUME_CAL_STEPS) {
    585                     if (max_table_ent < total_volume_cal_step) {
    586                         for (int i = max_table_ent; i < total_volume_cal_step; i++ ) {
    587                             volume_curve_gain_mapping_table[i].amp = 0;
    588                             volume_curve_gain_mapping_table[i].db = 0;
    589                             volume_curve_gain_mapping_table[i].level = -1;
    590                         }
    591                     }
    592                     total_volume_cal_step = max_table_ent;
    593                     ALOGD("%s: using custome volume table", __func__);
    594                 } else {
    595                     ALOGD("%s: using default volume table", __func__);
    596                 }
    597 
    598                 if (dumping_enabled) {
    599                     ALOGD("%s: dumping table here .. size of table received %d",
    600                            __func__, max_table_ent);
    601                     for (int i = 0; i < MAX_VOLUME_CAL_STEPS ; i++)
    602                         ALOGD("[%d]  %f %f %d", i, volume_curve_gain_mapping_table[i].amp,
    603                                                    volume_curve_gain_mapping_table[i].db,
    604                                                    volume_curve_gain_mapping_table[i].level);
    605                 }
    606             }
    607         }
    608     } else {
    609         ALOGE("%s: not able to acces lib %s ", __func__, PRIMARY_HAL_PATH);
    610     }
    611 
    612     // check system property to see if dumping is required
    613     char check_dump_val[PROPERTY_VALUE_MAX];
    614     property_get("audio.volume.listener.dump", check_dump_val, "0");
    615     if (atoi(check_dump_val)) {
    616         dumping_enabled = true;
    617     }
    618 
    619     init_status = 0;
    620     list_init(&vol_effect_list);
    621     initialized = true;
    622 }
    623 
    624 static int lib_init()
    625 {
    626     pthread_once(&once, init_once);
    627     ALOGV("%s Called ", __func__);
    628     return init_status;
    629 }
    630 
    631 static int vol_prc_lib_create(const effect_uuid_t *uuid,
    632                               int32_t session_id,
    633                               int32_t io_id __unused,
    634                               effect_handle_t *p_handle)
    635 {
    636     int itt = 0;
    637     vol_listener_context_t *context = NULL;
    638 
    639     ALOGV("volume_prc_lib_create .. called ..");
    640 
    641     if (lib_init() != 0) {
    642         return init_status;
    643     }
    644 
    645     if (p_handle == NULL || uuid == NULL) {
    646         ALOGE("%s: %s is NULL", __func__, (p_handle == NULL) ? "p_handle" : "uuid");
    647         return -EINVAL;
    648     }
    649 
    650     context = (vol_listener_context_t *)calloc(1, sizeof(vol_listener_context_t));
    651 
    652     if (context == NULL) {
    653         ALOGE("%s: failed to allocate for context .. oops !!", __func__);
    654         return -EINVAL;
    655     }
    656 
    657     // check if UUID is supported
    658     for (itt = 0; descriptors[itt] != NULL; itt++) {
    659         if (memcmp(uuid, &descriptors[itt]->uuid, sizeof(effect_uuid_t)) == 0) {
    660             // check if this correct .. very imp
    661             context->desc = descriptors[itt];
    662             context->stream_type = itt;
    663             PRINT_STREAM_TYPE(itt)
    664             break;
    665         }
    666     }
    667 
    668     if (descriptors[itt] == NULL) {
    669         ALOGE("%s .. couldnt find passed uuid, something wrong", __func__);
    670         free(context);
    671         return -EINVAL;
    672     }
    673 
    674     ALOGV("%s CREATED_CONTEXT %p", __func__, context);
    675 
    676     context->itfe = &effect_interface;
    677     context->state = VOL_LISTENER_STATE_INITIALIZED;
    678     context->dev_id = AUDIO_DEVICE_NONE;
    679     context->session_id = session_id;
    680 
    681     // Add this to master list
    682     pthread_mutex_lock(&vol_listner_init_lock);
    683     list_add_tail(&vol_effect_list, &context->effect_list_node);
    684 
    685     if (dumping_enabled) {
    686         dump_list_l();
    687     }
    688 
    689     pthread_mutex_unlock(&vol_listner_init_lock);
    690 
    691     *p_handle = (effect_handle_t)context;
    692     return 0;
    693 }
    694 
    695 static int vol_prc_lib_release(effect_handle_t handle)
    696 {
    697     struct listnode *node, *temp_node_next;
    698     vol_listener_context_t *context = NULL;
    699     vol_listener_context_t *recv_contex = (vol_listener_context_t *)handle;
    700     int status = -EINVAL;
    701     bool recompute_flag = false;
    702     int active_stream_count = 0;
    703     uint32_t session_id;
    704     uint32_t stream_type;
    705     effect_uuid_t uuid;
    706 
    707     ALOGV("%s context %p", __func__, handle);
    708 
    709     if (recv_contex == NULL) {
    710         return status;
    711     }
    712     pthread_mutex_lock(&vol_listner_init_lock);
    713     session_id = recv_contex->session_id;
    714     stream_type = recv_contex->stream_type;
    715     uuid = recv_contex->desc->uuid;
    716 
    717     // check if the handle/context provided is valid
    718     list_for_each_safe(node, temp_node_next, &vol_effect_list) {
    719         context = node_to_item(node, struct vol_listener_context_s, effect_list_node);
    720         if ((memcmp(&(context->desc->uuid), &uuid, sizeof(effect_uuid_t)) == 0)
    721             && (context->session_id == session_id)
    722             && (context->stream_type == stream_type)) {
    723             ALOGV("--- Found something to remove ---");
    724             list_remove(node);
    725             PRINT_STREAM_TYPE(context->stream_type);
    726             if (context->dev_id == AUDIO_DEVICE_OUT_SPEAKER) {
    727                 recompute_flag = true;
    728             }
    729             free(context);
    730             status = 0;
    731         } else {
    732             ++active_stream_count;
    733         }
    734     }
    735 
    736     if (status != 0) {
    737         ALOGE("something wrong ... <<<--- Found NOTHING to remove ... ???? --->>>>>");
    738         pthread_mutex_unlock(&vol_listner_init_lock);
    739         return status;
    740     }
    741 
    742     // if there are no active streams, reset cal and volume level
    743     if (active_stream_count == 0) {
    744         current_gain_dep_cal_level = -1;
    745         current_vol = 0.0;
    746     }
    747 
    748     if (recompute_flag) {
    749         check_and_set_gain_dep_cal();
    750     }
    751 
    752     if (dumping_enabled) {
    753         dump_list_l();
    754     }
    755     pthread_mutex_unlock(&vol_listner_init_lock);
    756     return status;
    757 }
    758 
    759 static int vol_prc_lib_get_descriptor(const effect_uuid_t *uuid,
    760                                       effect_descriptor_t *descriptor)
    761 {
    762     int i = 0;
    763     ALOGV("%s Called ", __func__);
    764     if (lib_init() != 0) {
    765         return init_status;
    766     }
    767 
    768     if (descriptor == NULL || uuid == NULL) {
    769         ALOGE("%s: %s is NULL", __func__, (descriptor == NULL) ? "descriptor" : "uuid");
    770         return -EINVAL;
    771     }
    772 
    773     for (i = 0; descriptors[i] != NULL; i++) {
    774         if (memcmp(uuid, &descriptors[i]->uuid, sizeof(effect_uuid_t)) == 0) {
    775             *descriptor = *descriptors[i];
    776             return 0;
    777         }
    778     }
    779 
    780     ALOGE("%s: couldnt found uuid passed, oops", __func__);
    781     return  -EINVAL;
    782 }
    783 
    784 
    785 /* effect_handle_t interface implementation for volume listener effect */
    786 static const struct effect_interface_s effect_interface = {
    787     NULL,
    788     vol_effect_command,
    789     vol_effect_get_descriptor,
    790     NULL,
    791 };
    792 
    793 __attribute__((visibility("default")))
    794 audio_effect_library_t AUDIO_EFFECT_LIBRARY_INFO_SYM = {
    795     .tag = AUDIO_EFFECT_LIBRARY_TAG,
    796     .version = EFFECT_LIBRARY_API_VERSION,
    797     .name = "Volume Listener Effect Library",
    798     .implementor = "Qualcomm Technologies Inc.",
    799     .create_effect = vol_prc_lib_create,
    800     .release_effect = vol_prc_lib_release,
    801     .get_descriptor = vol_prc_lib_get_descriptor,
    802 };
    803