1 /* 2 * Copyright (C) 2010 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 "EffectsFactory" 18 //#define LOG_NDEBUG 0 19 20 #include <stdlib.h> 21 #include <string.h> 22 #include <unistd.h> 23 24 #include <cutils/properties.h> 25 #include <log/log.h> 26 27 #include <media/EffectsFactoryApi.h> 28 29 #include "EffectsConfigLoader.h" 30 #include "EffectsFactoryState.h" 31 #include "EffectsXmlConfigLoader.h" 32 33 #include "EffectsFactory.h" 34 35 static list_elem_t *gEffectList; // list of effect_entry_t: all currently created effects 36 static uint32_t gNumEffects; // total number number of effects 37 static list_elem_t *gCurLib; // current library in enumeration process 38 static list_elem_t *gCurEffect; // current effect in enumeration process 39 static uint32_t gCurEffectIdx; // current effect index in enumeration process 40 /** Number of elements skipped during the effects configuration loading. 41 * -1 if the config loader failed 42 * -2 if config load was skipped 43 */ 44 static ssize_t gConfigNbElemSkipped = -2; 45 46 static int gInitDone; // true is global initialization has been preformed 47 static int gCanQueryEffect; // indicates that call to EffectQueryEffect() is valid, i.e. that the list of effects 48 // was not modified since last call to EffectQueryNumberEffects() 49 ///////////////////////////////////////////////// 50 // Local functions prototypes 51 ///////////////////////////////////////////////// 52 53 static int init(); 54 static void resetEffectEnumeration(); 55 static uint32_t updateNumEffects(); 56 // To search a subeffect in the gSubEffectList 57 static int findSubEffect(const effect_uuid_t *uuid, 58 lib_entry_t **lib, 59 effect_descriptor_t **desc); 60 61 ///////////////////////////////////////////////// 62 // Effect Control Interface functions 63 ///////////////////////////////////////////////// 64 65 int Effect_Process(effect_handle_t self, audio_buffer_t *inBuffer, audio_buffer_t *outBuffer) 66 { 67 int ret = init(); 68 if (ret < 0) { 69 return ret; 70 } 71 effect_entry_t *fx = (effect_entry_t *)self; 72 pthread_mutex_lock(&gLibLock); 73 if (fx->lib == NULL) { 74 pthread_mutex_unlock(&gLibLock); 75 return -EPIPE; 76 } 77 pthread_mutex_lock(&fx->lib->lock); 78 pthread_mutex_unlock(&gLibLock); 79 80 ret = (*fx->subItfe)->process(fx->subItfe, inBuffer, outBuffer); 81 pthread_mutex_unlock(&fx->lib->lock); 82 return ret; 83 } 84 85 int Effect_Command(effect_handle_t self, 86 uint32_t cmdCode, 87 uint32_t cmdSize, 88 void *pCmdData, 89 uint32_t *replySize, 90 void *pReplyData) 91 { 92 int ret = init(); 93 if (ret < 0) { 94 return ret; 95 } 96 effect_entry_t *fx = (effect_entry_t *)self; 97 pthread_mutex_lock(&gLibLock); 98 if (fx->lib == NULL) { 99 pthread_mutex_unlock(&gLibLock); 100 return -EPIPE; 101 } 102 pthread_mutex_lock(&fx->lib->lock); 103 pthread_mutex_unlock(&gLibLock); 104 105 ret = (*fx->subItfe)->command(fx->subItfe, cmdCode, cmdSize, pCmdData, replySize, pReplyData); 106 pthread_mutex_unlock(&fx->lib->lock); 107 return ret; 108 } 109 110 int Effect_GetDescriptor(effect_handle_t self, 111 effect_descriptor_t *desc) 112 { 113 int ret = init(); 114 if (ret < 0) { 115 return ret; 116 } 117 effect_entry_t *fx = (effect_entry_t *)self; 118 pthread_mutex_lock(&gLibLock); 119 if (fx->lib == NULL) { 120 pthread_mutex_unlock(&gLibLock); 121 return -EPIPE; 122 } 123 pthread_mutex_lock(&fx->lib->lock); 124 pthread_mutex_unlock(&gLibLock); 125 126 ret = (*fx->subItfe)->get_descriptor(fx->subItfe, desc); 127 pthread_mutex_unlock(&fx->lib->lock); 128 return ret; 129 } 130 131 int Effect_ProcessReverse(effect_handle_t self, audio_buffer_t *inBuffer, audio_buffer_t *outBuffer) 132 { 133 int ret = init(); 134 if (ret < 0) { 135 return ret; 136 } 137 effect_entry_t *fx = (effect_entry_t *)self; 138 pthread_mutex_lock(&gLibLock); 139 if (fx->lib == NULL) { 140 pthread_mutex_unlock(&gLibLock); 141 return -EPIPE; 142 } 143 pthread_mutex_lock(&fx->lib->lock); 144 pthread_mutex_unlock(&gLibLock); 145 146 if ((*fx->subItfe)->process_reverse != NULL) { 147 ret = (*fx->subItfe)->process_reverse(fx->subItfe, inBuffer, outBuffer); 148 } else { 149 ret = -ENOSYS; 150 } 151 pthread_mutex_unlock(&fx->lib->lock); 152 return ret; 153 } 154 155 156 const struct effect_interface_s gInterface = { 157 Effect_Process, 158 Effect_Command, 159 Effect_GetDescriptor, 160 NULL 161 }; 162 163 const struct effect_interface_s gInterfaceWithReverse = { 164 Effect_Process, 165 Effect_Command, 166 Effect_GetDescriptor, 167 Effect_ProcessReverse 168 }; 169 170 ///////////////////////////////////////////////// 171 // Effect Factory Interface functions 172 ///////////////////////////////////////////////// 173 174 int EffectQueryNumberEffects(uint32_t *pNumEffects) 175 { 176 int ret = init(); 177 if (ret < 0) { 178 return ret; 179 } 180 if (pNumEffects == NULL) { 181 return -EINVAL; 182 } 183 184 pthread_mutex_lock(&gLibLock); 185 *pNumEffects = gNumEffects; 186 gCanQueryEffect = 1; 187 pthread_mutex_unlock(&gLibLock); 188 ALOGV("EffectQueryNumberEffects(): %d", *pNumEffects); 189 return ret; 190 } 191 192 int EffectQueryEffect(uint32_t index, effect_descriptor_t *pDescriptor) 193 { 194 int ret = init(); 195 if (ret < 0) { 196 return ret; 197 } 198 if (pDescriptor == NULL || 199 index >= gNumEffects) { 200 return -EINVAL; 201 } 202 if (gCanQueryEffect == 0) { 203 return -ENOSYS; 204 } 205 206 pthread_mutex_lock(&gLibLock); 207 ret = -ENOENT; 208 if (index < gCurEffectIdx) { 209 resetEffectEnumeration(); 210 } 211 while (gCurLib) { 212 if (gCurEffect) { 213 if (index == gCurEffectIdx) { 214 *pDescriptor = *(effect_descriptor_t *)gCurEffect->object; 215 ret = 0; 216 break; 217 } else { 218 gCurEffect = gCurEffect->next; 219 gCurEffectIdx++; 220 } 221 } else { 222 gCurLib = gCurLib->next; 223 gCurEffect = ((lib_entry_t *)gCurLib->object)->effects; 224 } 225 } 226 227 #if (LOG_NDEBUG == 0) 228 char str[512]; 229 dumpEffectDescriptor(pDescriptor, str, sizeof(str), 0 /* indent */); 230 ALOGV("EffectQueryEffect() desc:%s", str); 231 #endif 232 pthread_mutex_unlock(&gLibLock); 233 return ret; 234 } 235 236 int EffectGetDescriptor(const effect_uuid_t *uuid, effect_descriptor_t *pDescriptor) 237 { 238 lib_entry_t *l = NULL; 239 effect_descriptor_t *d = NULL; 240 241 int ret = init(); 242 if (ret < 0) { 243 return ret; 244 } 245 if (pDescriptor == NULL || uuid == NULL) { 246 return -EINVAL; 247 } 248 pthread_mutex_lock(&gLibLock); 249 ret = findEffect(NULL, uuid, &l, &d); 250 if (ret == 0) { 251 *pDescriptor = *d; 252 } 253 pthread_mutex_unlock(&gLibLock); 254 return ret; 255 } 256 257 int EffectCreate(const effect_uuid_t *uuid, int32_t sessionId, int32_t ioId, effect_handle_t *pHandle) 258 { 259 list_elem_t *e = gLibraryList; 260 lib_entry_t *l = NULL; 261 effect_descriptor_t *d = NULL; 262 effect_handle_t itfe; 263 effect_entry_t *fx; 264 int found = 0; 265 int ret; 266 267 if (uuid == NULL || pHandle == NULL) { 268 return -EINVAL; 269 } 270 271 ALOGV("EffectCreate() UUID: %08X-%04X-%04X-%04X-%02X%02X%02X%02X%02X%02X\n", 272 uuid->timeLow, uuid->timeMid, uuid->timeHiAndVersion, 273 uuid->clockSeq, uuid->node[0], uuid->node[1],uuid->node[2], 274 uuid->node[3],uuid->node[4],uuid->node[5]); 275 276 ret = init(); 277 278 if (ret < 0) { 279 ALOGW("EffectCreate() init error: %d", ret); 280 return ret; 281 } 282 283 pthread_mutex_lock(&gLibLock); 284 285 ret = findEffect(NULL, uuid, &l, &d); 286 if (ret < 0){ 287 // Sub effects are not associated with the library->effects, 288 // so, findEffect will fail. Search for the effect in gSubEffectList. 289 ret = findSubEffect(uuid, &l, &d); 290 if (ret < 0 ) { 291 goto exit; 292 } 293 } 294 295 // create effect in library 296 ret = l->desc->create_effect(uuid, sessionId, ioId, &itfe); 297 if (ret != 0) { 298 ALOGW("EffectCreate() library %s: could not create fx %s, error %d", l->name, d->name, ret); 299 goto exit; 300 } 301 302 // add entry to effect list 303 fx = (effect_entry_t *)malloc(sizeof(effect_entry_t)); 304 fx->subItfe = itfe; 305 if ((*itfe)->process_reverse != NULL) { 306 fx->itfe = (struct effect_interface_s *)&gInterfaceWithReverse; 307 ALOGV("EffectCreate() gInterfaceWithReverse"); 308 } else { 309 fx->itfe = (struct effect_interface_s *)&gInterface; 310 ALOGV("EffectCreate() gInterface"); 311 } 312 fx->lib = l; 313 314 e = (list_elem_t *)malloc(sizeof(list_elem_t)); 315 e->object = fx; 316 e->next = gEffectList; 317 gEffectList = e; 318 319 *pHandle = (effect_handle_t)fx; 320 321 ALOGV("EffectCreate() created entry %p with sub itfe %p in library %s", *pHandle, itfe, l->name); 322 323 exit: 324 pthread_mutex_unlock(&gLibLock); 325 return ret; 326 } 327 328 int EffectRelease(effect_handle_t handle) 329 { 330 effect_entry_t *fx; 331 list_elem_t *e1; 332 list_elem_t *e2; 333 334 int ret = init(); 335 if (ret < 0) { 336 return ret; 337 } 338 339 // remove effect from effect list 340 pthread_mutex_lock(&gLibLock); 341 e1 = gEffectList; 342 e2 = NULL; 343 while (e1) { 344 if (e1->object == handle) { 345 if (e2) { 346 e2->next = e1->next; 347 } else { 348 gEffectList = e1->next; 349 } 350 fx = (effect_entry_t *)e1->object; 351 free(e1); 352 break; 353 } 354 e2 = e1; 355 e1 = e1->next; 356 } 357 if (e1 == NULL) { 358 ret = -ENOENT; 359 goto exit; 360 } 361 362 // release effect in library 363 if (fx->lib == NULL) { 364 ALOGW("EffectRelease() fx %p library already unloaded", handle); 365 } else { 366 pthread_mutex_lock(&fx->lib->lock); 367 fx->lib->desc->release_effect(fx->subItfe); 368 pthread_mutex_unlock(&fx->lib->lock); 369 } 370 free(fx); 371 372 exit: 373 pthread_mutex_unlock(&gLibLock); 374 return ret; 375 } 376 377 int EffectIsNullUuid(const effect_uuid_t *uuid) 378 { 379 if (memcmp(uuid, EFFECT_UUID_NULL, sizeof(effect_uuid_t))) { 380 return 0; 381 } 382 return 1; 383 } 384 385 // Function to get the sub effect descriptors of the effect whose uuid 386 // is pointed by the first argument. It searches the gSubEffectList for the 387 // matching uuid and then copies the corresponding sub effect descriptors 388 // to the inout param 389 int EffectGetSubEffects(const effect_uuid_t *uuid, sub_effect_entry_t **pSube, 390 size_t size) 391 { 392 ALOGV("EffectGetSubEffects() UUID: %08X-%04X-%04X-%04X-%02X%02X%02X%02X%02X" 393 "%02X\n",uuid->timeLow, uuid->timeMid, uuid->timeHiAndVersion, 394 uuid->clockSeq, uuid->node[0], uuid->node[1],uuid->node[2], 395 uuid->node[3],uuid->node[4],uuid->node[5]); 396 397 // Check if the size of the desc buffer is large enough for 2 subeffects 398 if ((uuid == NULL) || (pSube == NULL) || (size < 2)) { 399 ALOGW("NULL pointer or insufficient memory. Cannot query subeffects"); 400 return -EINVAL; 401 } 402 int ret = init(); 403 if (ret < 0) 404 return ret; 405 list_sub_elem_t *e = gSubEffectList; 406 sub_effect_entry_t *subeffect; 407 effect_descriptor_t *d; 408 int count = 0; 409 while (e != NULL) { 410 d = (effect_descriptor_t*)e->object; 411 if (memcmp(uuid, &d->uuid, sizeof(effect_uuid_t)) == 0) { 412 ALOGV("EffectGetSubEffects: effect found in the list"); 413 list_elem_t *subefx = e->sub_elem; 414 while (subefx != NULL) { 415 subeffect = (sub_effect_entry_t*)subefx->object; 416 pSube[count++] = subeffect; 417 subefx = subefx->next; 418 } 419 ALOGV("EffectGetSubEffects end - copied the sub effect structures"); 420 return count; 421 } 422 e = e->next; 423 } 424 return -ENOENT; 425 } 426 ///////////////////////////////////////////////// 427 // Local functions 428 ///////////////////////////////////////////////// 429 430 int init() { 431 int hdl; 432 433 if (gInitDone) { 434 return 0; 435 } 436 437 // ignore effects or not? 438 const bool ignoreFxConfFiles = property_get_bool(PROPERTY_IGNORE_EFFECTS, false); 439 440 pthread_mutex_init(&gLibLock, NULL); 441 442 if (ignoreFxConfFiles) { 443 ALOGI("Audio effects in configuration files will be ignored"); 444 } else { 445 gConfigNbElemSkipped = EffectLoadXmlEffectConfig(NULL); 446 if (gConfigNbElemSkipped < 0) { 447 ALOGW("Failed to load XML effect configuration, fallback to .conf"); 448 EffectLoadEffectConfig(); 449 } else if (gConfigNbElemSkipped > 0) { 450 ALOGE("Effect config is partially invalid, skipped %zd elements", gConfigNbElemSkipped); 451 } 452 } 453 454 updateNumEffects(); 455 gInitDone = 1; 456 ALOGV("init() done"); 457 return 0; 458 } 459 460 // Searches the sub effect matching to the specified uuid 461 // in the gSubEffectList. It gets the lib_entry_t for 462 // the matched sub_effect . Used in EffectCreate of sub effects 463 int findSubEffect(const effect_uuid_t *uuid, 464 lib_entry_t **lib, 465 effect_descriptor_t **desc) 466 { 467 list_sub_elem_t *e = gSubEffectList; 468 list_elem_t *subefx; 469 sub_effect_entry_t *effect; 470 lib_entry_t *l = NULL; 471 effect_descriptor_t *d = NULL; 472 int found = 0; 473 int ret = 0; 474 475 if (uuid == NULL) 476 return -EINVAL; 477 478 while (e != NULL && !found) { 479 subefx = (list_elem_t*)(e->sub_elem); 480 while (subefx != NULL) { 481 effect = (sub_effect_entry_t*)subefx->object; 482 l = (lib_entry_t *)effect->lib; 483 d = (effect_descriptor_t *)effect->object; 484 if (memcmp(&d->uuid, uuid, sizeof(effect_uuid_t)) == 0) { 485 ALOGV("uuid matched"); 486 found = 1; 487 break; 488 } 489 subefx = subefx->next; 490 } 491 e = e->next; 492 } 493 if (!found) { 494 ALOGV("findSubEffect() effect not found"); 495 ret = -ENOENT; 496 } else { 497 ALOGV("findSubEffect() found effect: %s in lib %s", d->name, l->name); 498 *lib = l; 499 if (desc != NULL) { 500 *desc = d; 501 } 502 } 503 return ret; 504 } 505 506 void resetEffectEnumeration() 507 { 508 gCurLib = gLibraryList; 509 gCurEffect = NULL; 510 if (gCurLib) { 511 gCurEffect = ((lib_entry_t *)gCurLib->object)->effects; 512 } 513 gCurEffectIdx = 0; 514 } 515 516 uint32_t updateNumEffects() { 517 list_elem_t *e; 518 uint32_t cnt = 0; 519 520 resetEffectEnumeration(); 521 522 e = gLibraryList; 523 while (e) { 524 lib_entry_t *l = (lib_entry_t *)e->object; 525 list_elem_t *efx = l->effects; 526 while (efx) { 527 cnt++; 528 efx = efx->next; 529 } 530 e = e->next; 531 } 532 gNumEffects = cnt; 533 gCanQueryEffect = 0; 534 return cnt; 535 } 536 537 int EffectDumpEffects(int fd) { 538 char s[512]; 539 540 list_elem_t *fe = gLibraryFailedList; 541 lib_failed_entry_t *fl = NULL; 542 543 dprintf(fd, "Libraries NOT loaded:\n"); 544 545 while (fe) { 546 fl = (lib_failed_entry_t *)fe->object; 547 dprintf(fd, " Library %s\n", fl->name); 548 dprintf(fd, " path: %s\n", fl->path); 549 fe = fe->next; 550 } 551 552 list_elem_t *e = gLibraryList; 553 lib_entry_t *l = NULL; 554 effect_descriptor_t *d = NULL; 555 int found = 0; 556 int ret = 0; 557 558 dprintf(fd, "Libraries loaded:\n"); 559 while (e) { 560 l = (lib_entry_t *)e->object; 561 list_elem_t *efx = l->effects; 562 dprintf(fd, " Library %s\n", l->name); 563 dprintf(fd, " path: %s\n", l->path); 564 if (!efx) { 565 dprintf(fd, " (no effects)\n"); 566 } 567 while (efx) { 568 d = (effect_descriptor_t *)efx->object; 569 dumpEffectDescriptor(d, s, sizeof(s), 2); 570 dprintf(fd, "%s", s); 571 efx = efx->next; 572 } 573 e = e->next; 574 } 575 576 e = gSkippedEffects; 577 if (e) { 578 dprintf(fd, "Skipped effects\n"); 579 while(e) { 580 d = (effect_descriptor_t *)e->object; 581 dumpEffectDescriptor(d, s, sizeof(s), 2 /* indent */); 582 dprintf(fd, "%s", s); 583 e = e->next; 584 } 585 } 586 switch (gConfigNbElemSkipped) { 587 case -2: 588 dprintf(fd, "Effect configuration loading skipped.\n"); 589 break; 590 case -1: 591 dprintf(fd, "XML effect configuration failed to load.\n"); 592 break; 593 case 0: 594 dprintf(fd, "XML effect configuration loaded successfully.\n"); 595 break; 596 default: 597 dprintf(fd, "XML effect configuration partially loaded, skipped %zd elements.\n", 598 gConfigNbElemSkipped); 599 } 600 return ret; 601 } 602 603