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