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 ret; 265 266 if (uuid == NULL || pHandle == NULL) { 267 return -EINVAL; 268 } 269 270 ALOGV("EffectCreate() UUID: %08X-%04X-%04X-%04X-%02X%02X%02X%02X%02X%02X\n", 271 uuid->timeLow, uuid->timeMid, uuid->timeHiAndVersion, 272 uuid->clockSeq, uuid->node[0], uuid->node[1],uuid->node[2], 273 uuid->node[3],uuid->node[4],uuid->node[5]); 274 275 ret = init(); 276 277 if (ret < 0) { 278 ALOGW("EffectCreate() init error: %d", ret); 279 return ret; 280 } 281 282 pthread_mutex_lock(&gLibLock); 283 284 ret = findEffect(NULL, uuid, &l, &d); 285 if (ret < 0){ 286 // Sub effects are not associated with the library->effects, 287 // so, findEffect will fail. Search for the effect in gSubEffectList. 288 ret = findSubEffect(uuid, &l, &d); 289 if (ret < 0 ) { 290 goto exit; 291 } 292 } 293 294 // create effect in library 295 ret = l->desc->create_effect(uuid, sessionId, ioId, &itfe); 296 if (ret != 0) { 297 ALOGW("EffectCreate() library %s: could not create fx %s, error %d", l->name, d->name, ret); 298 goto exit; 299 } 300 301 // add entry to effect list 302 fx = (effect_entry_t *)malloc(sizeof(effect_entry_t)); 303 fx->subItfe = itfe; 304 if ((*itfe)->process_reverse != NULL) { 305 fx->itfe = (struct effect_interface_s *)&gInterfaceWithReverse; 306 ALOGV("EffectCreate() gInterfaceWithReverse"); 307 } else { 308 fx->itfe = (struct effect_interface_s *)&gInterface; 309 ALOGV("EffectCreate() gInterface"); 310 } 311 fx->lib = l; 312 313 e = (list_elem_t *)malloc(sizeof(list_elem_t)); 314 e->object = fx; 315 e->next = gEffectList; 316 gEffectList = e; 317 318 *pHandle = (effect_handle_t)fx; 319 320 ALOGV("EffectCreate() created entry %p with sub itfe %p in library %s", *pHandle, itfe, l->name); 321 322 exit: 323 pthread_mutex_unlock(&gLibLock); 324 return ret; 325 } 326 327 int EffectRelease(effect_handle_t handle) 328 { 329 effect_entry_t *fx; 330 list_elem_t *e1; 331 list_elem_t *e2; 332 333 int ret = init(); 334 if (ret < 0) { 335 return ret; 336 } 337 338 // remove effect from effect list 339 pthread_mutex_lock(&gLibLock); 340 e1 = gEffectList; 341 e2 = NULL; 342 while (e1) { 343 if (e1->object == handle) { 344 if (e2) { 345 e2->next = e1->next; 346 } else { 347 gEffectList = e1->next; 348 } 349 fx = (effect_entry_t *)e1->object; 350 free(e1); 351 break; 352 } 353 e2 = e1; 354 e1 = e1->next; 355 } 356 if (e1 == NULL) { 357 ret = -ENOENT; 358 goto exit; 359 } 360 361 // release effect in library 362 if (fx->lib == NULL) { 363 ALOGW("EffectRelease() fx %p library already unloaded", handle); 364 } else { 365 pthread_mutex_lock(&fx->lib->lock); 366 fx->lib->desc->release_effect(fx->subItfe); 367 pthread_mutex_unlock(&fx->lib->lock); 368 } 369 free(fx); 370 371 exit: 372 pthread_mutex_unlock(&gLibLock); 373 return ret; 374 } 375 376 int EffectIsNullUuid(const effect_uuid_t *uuid) 377 { 378 if (memcmp(uuid, EFFECT_UUID_NULL, sizeof(effect_uuid_t))) { 379 return 0; 380 } 381 return 1; 382 } 383 384 // Function to get the sub effect descriptors of the effect whose uuid 385 // is pointed by the first argument. It searches the gSubEffectList for the 386 // matching uuid and then copies the corresponding sub effect descriptors 387 // to the inout param 388 int EffectGetSubEffects(const effect_uuid_t *uuid, sub_effect_entry_t **pSube, 389 size_t size) 390 { 391 ALOGV("EffectGetSubEffects() UUID: %08X-%04X-%04X-%04X-%02X%02X%02X%02X%02X" 392 "%02X\n",uuid->timeLow, uuid->timeMid, uuid->timeHiAndVersion, 393 uuid->clockSeq, uuid->node[0], uuid->node[1],uuid->node[2], 394 uuid->node[3],uuid->node[4],uuid->node[5]); 395 396 // Check if the size of the desc buffer is large enough for 2 subeffects 397 if ((uuid == NULL) || (pSube == NULL) || (size < 2)) { 398 ALOGW("NULL pointer or insufficient memory. Cannot query subeffects"); 399 return -EINVAL; 400 } 401 int ret = init(); 402 if (ret < 0) 403 return ret; 404 list_sub_elem_t *e = gSubEffectList; 405 sub_effect_entry_t *subeffect; 406 effect_descriptor_t *d; 407 int count = 0; 408 while (e != NULL) { 409 d = (effect_descriptor_t*)e->object; 410 if (memcmp(uuid, &d->uuid, sizeof(effect_uuid_t)) == 0) { 411 ALOGV("EffectGetSubEffects: effect found in the list"); 412 list_elem_t *subefx = e->sub_elem; 413 while (subefx != NULL) { 414 subeffect = (sub_effect_entry_t*)subefx->object; 415 pSube[count++] = subeffect; 416 subefx = subefx->next; 417 } 418 ALOGV("EffectGetSubEffects end - copied the sub effect structures"); 419 return count; 420 } 421 e = e->next; 422 } 423 return -ENOENT; 424 } 425 ///////////////////////////////////////////////// 426 // Local functions 427 ///////////////////////////////////////////////// 428 429 int init() { 430 if (gInitDone) { 431 return 0; 432 } 433 434 // ignore effects or not? 435 const bool ignoreFxConfFiles = property_get_bool(PROPERTY_IGNORE_EFFECTS, false); 436 437 pthread_mutex_init(&gLibLock, NULL); 438 439 if (ignoreFxConfFiles) { 440 ALOGI("Audio effects in configuration files will be ignored"); 441 } else { 442 gConfigNbElemSkipped = EffectLoadXmlEffectConfig(NULL); 443 if (gConfigNbElemSkipped < 0) { 444 ALOGW("Failed to load XML effect configuration, fallback to .conf"); 445 EffectLoadEffectConfig(); 446 } else if (gConfigNbElemSkipped > 0) { 447 ALOGE("Effect config is partially invalid, skipped %zd elements", gConfigNbElemSkipped); 448 } 449 } 450 451 updateNumEffects(); 452 gInitDone = 1; 453 ALOGV("init() done"); 454 return 0; 455 } 456 457 // Searches the sub effect matching to the specified uuid 458 // in the gSubEffectList. It gets the lib_entry_t for 459 // the matched sub_effect . Used in EffectCreate of sub effects 460 int findSubEffect(const effect_uuid_t *uuid, 461 lib_entry_t **lib, 462 effect_descriptor_t **desc) 463 { 464 list_sub_elem_t *e = gSubEffectList; 465 list_elem_t *subefx; 466 sub_effect_entry_t *effect; 467 lib_entry_t *l = NULL; 468 effect_descriptor_t *d = NULL; 469 int found = 0; 470 int ret = 0; 471 472 if (uuid == NULL) 473 return -EINVAL; 474 475 while (e != NULL && !found) { 476 subefx = (list_elem_t*)(e->sub_elem); 477 while (subefx != NULL) { 478 effect = (sub_effect_entry_t*)subefx->object; 479 l = (lib_entry_t *)effect->lib; 480 d = (effect_descriptor_t *)effect->object; 481 if (memcmp(&d->uuid, uuid, sizeof(effect_uuid_t)) == 0) { 482 ALOGV("uuid matched"); 483 found = 1; 484 break; 485 } 486 subefx = subefx->next; 487 } 488 e = e->next; 489 } 490 if (!found) { 491 ALOGV("findSubEffect() effect not found"); 492 ret = -ENOENT; 493 } else { 494 ALOGV("findSubEffect() found effect: %s in lib %s", d->name, l->name); 495 *lib = l; 496 if (desc != NULL) { 497 *desc = d; 498 } 499 } 500 return ret; 501 } 502 503 void resetEffectEnumeration() 504 { 505 gCurLib = gLibraryList; 506 gCurEffect = NULL; 507 if (gCurLib) { 508 gCurEffect = ((lib_entry_t *)gCurLib->object)->effects; 509 } 510 gCurEffectIdx = 0; 511 } 512 513 uint32_t updateNumEffects() { 514 list_elem_t *e; 515 uint32_t cnt = 0; 516 517 resetEffectEnumeration(); 518 519 e = gLibraryList; 520 while (e) { 521 lib_entry_t *l = (lib_entry_t *)e->object; 522 list_elem_t *efx = l->effects; 523 while (efx) { 524 cnt++; 525 efx = efx->next; 526 } 527 e = e->next; 528 } 529 gNumEffects = cnt; 530 gCanQueryEffect = 0; 531 return cnt; 532 } 533 534 int EffectDumpEffects(int fd) { 535 char s[512]; 536 537 list_elem_t *fe = gLibraryFailedList; 538 lib_failed_entry_t *fl = NULL; 539 540 dprintf(fd, "Libraries NOT loaded:\n"); 541 542 while (fe) { 543 fl = (lib_failed_entry_t *)fe->object; 544 dprintf(fd, " Library %s\n", fl->name); 545 dprintf(fd, " path: %s\n", fl->path); 546 fe = fe->next; 547 } 548 549 list_elem_t *e = gLibraryList; 550 lib_entry_t *l = NULL; 551 effect_descriptor_t *d = NULL; 552 int ret = 0; 553 554 dprintf(fd, "Libraries loaded:\n"); 555 while (e) { 556 l = (lib_entry_t *)e->object; 557 list_elem_t *efx = l->effects; 558 dprintf(fd, " Library %s\n", l->name); 559 dprintf(fd, " path: %s\n", l->path); 560 if (!efx) { 561 dprintf(fd, " (no effects)\n"); 562 } 563 while (efx) { 564 d = (effect_descriptor_t *)efx->object; 565 dumpEffectDescriptor(d, s, sizeof(s), 2); 566 dprintf(fd, "%s", s); 567 efx = efx->next; 568 } 569 e = e->next; 570 } 571 572 e = gSkippedEffects; 573 if (e) { 574 dprintf(fd, "Skipped effects\n"); 575 while(e) { 576 d = (effect_descriptor_t *)e->object; 577 dumpEffectDescriptor(d, s, sizeof(s), 2 /* indent */); 578 dprintf(fd, "%s", s); 579 e = e->next; 580 } 581 } 582 switch (gConfigNbElemSkipped) { 583 case -2: 584 dprintf(fd, "Effect configuration loading skipped.\n"); 585 break; 586 case -1: 587 dprintf(fd, "XML effect configuration failed to load.\n"); 588 break; 589 case 0: 590 dprintf(fd, "XML effect configuration loaded successfully.\n"); 591 break; 592 default: 593 dprintf(fd, "XML effect configuration partially loaded, skipped %zd elements.\n", 594 gConfigNbElemSkipped); 595 } 596 return ret; 597 } 598 599