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