EffectVisualizer.cpp revision a189a6883ee55cf62da1d7bf5bf5a8ab501938a4
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 "EffectVisualizer" 18//#define LOG_NDEBUG 0 19#include <cutils/log.h> 20#include <assert.h> 21#include <stdlib.h> 22#include <string.h> 23#include <new> 24#include <time.h> 25#include <audio_effects/effect_visualizer.h> 26 27 28extern "C" { 29 30// effect_handle_t interface implementation for visualizer effect 31extern const struct effect_interface_s gVisualizerInterface; 32 33// Google Visualizer UUID: d069d9e0-8329-11df-9168-0002a5d5c51b 34const effect_descriptor_t gVisualizerDescriptor = { 35 {0xe46b26a0, 0xdddd, 0x11db, 0x8afd, {0x00, 0x02, 0xa5, 0xd5, 0xc5, 0x1b}}, // type 36 {0xd069d9e0, 0x8329, 0x11df, 0x9168, {0x00, 0x02, 0xa5, 0xd5, 0xc5, 0x1b}}, // uuid 37 EFFECT_CONTROL_API_VERSION, 38 (EFFECT_FLAG_TYPE_INSERT | EFFECT_FLAG_INSERT_FIRST), 39 0, // TODO 40 1, 41 "Visualizer", 42 "The Android Open Source Project", 43}; 44 45enum visualizer_state_e { 46 VISUALIZER_STATE_UNINITIALIZED, 47 VISUALIZER_STATE_INITIALIZED, 48 VISUALIZER_STATE_ACTIVE, 49}; 50 51// maximum time since last capture buffer update before resetting capture buffer. This means 52// that the framework has stopped playing audio and we must start returning silence 53#define MAX_STALL_TIME_MS 1000 54 55#define CAPTURE_BUF_SIZE 65536 // "64k should be enough for everyone" 56 57struct VisualizerContext { 58 const struct effect_interface_s *mItfe; 59 effect_config_t mConfig; 60 uint32_t mCaptureIdx; 61 uint32_t mCaptureSize; 62 uint32_t mScalingMode; 63 uint8_t mState; 64 uint8_t mLastCaptureIdx; 65 uint32_t mLatency; 66 struct timespec mBufferUpdateTime; 67 uint8_t mCaptureBuf[CAPTURE_BUF_SIZE]; 68}; 69 70// 71//--- Local functions 72// 73 74void Visualizer_reset(VisualizerContext *pContext) 75{ 76 pContext->mCaptureIdx = 0; 77 pContext->mLastCaptureIdx = 0; 78 pContext->mBufferUpdateTime.tv_sec = 0; 79 pContext->mLatency = 0; 80 memset(pContext->mCaptureBuf, 0x80, CAPTURE_BUF_SIZE); 81} 82 83//---------------------------------------------------------------------------- 84// Visualizer_setConfig() 85//---------------------------------------------------------------------------- 86// Purpose: Set input and output audio configuration. 87// 88// Inputs: 89// pContext: effect engine context 90// pConfig: pointer to effect_config_t structure holding input and output 91// configuration parameters 92// 93// Outputs: 94// 95//---------------------------------------------------------------------------- 96 97int Visualizer_setConfig(VisualizerContext *pContext, effect_config_t *pConfig) 98{ 99 ALOGV("Visualizer_setConfig start"); 100 101 if (pConfig->inputCfg.samplingRate != pConfig->outputCfg.samplingRate) return -EINVAL; 102 if (pConfig->inputCfg.channels != pConfig->outputCfg.channels) return -EINVAL; 103 if (pConfig->inputCfg.format != pConfig->outputCfg.format) return -EINVAL; 104 if (pConfig->inputCfg.channels != AUDIO_CHANNEL_OUT_STEREO) return -EINVAL; 105 if (pConfig->outputCfg.accessMode != EFFECT_BUFFER_ACCESS_WRITE && 106 pConfig->outputCfg.accessMode != EFFECT_BUFFER_ACCESS_ACCUMULATE) return -EINVAL; 107 if (pConfig->inputCfg.format != AUDIO_FORMAT_PCM_16_BIT) return -EINVAL; 108 109 pContext->mConfig = *pConfig; 110 111 Visualizer_reset(pContext); 112 113 return 0; 114} 115 116 117//---------------------------------------------------------------------------- 118// Visualizer_getConfig() 119//---------------------------------------------------------------------------- 120// Purpose: Get input and output audio configuration. 121// 122// Inputs: 123// pContext: effect engine context 124// pConfig: pointer to effect_config_t structure holding input and output 125// configuration parameters 126// 127// Outputs: 128// 129//---------------------------------------------------------------------------- 130 131void Visualizer_getConfig(VisualizerContext *pContext, effect_config_t *pConfig) 132{ 133 *pConfig = pContext->mConfig; 134} 135 136 137//---------------------------------------------------------------------------- 138// Visualizer_init() 139//---------------------------------------------------------------------------- 140// Purpose: Initialize engine with default configuration. 141// 142// Inputs: 143// pContext: effect engine context 144// 145// Outputs: 146// 147//---------------------------------------------------------------------------- 148 149int Visualizer_init(VisualizerContext *pContext) 150{ 151 pContext->mConfig.inputCfg.accessMode = EFFECT_BUFFER_ACCESS_READ; 152 pContext->mConfig.inputCfg.channels = AUDIO_CHANNEL_OUT_STEREO; 153 pContext->mConfig.inputCfg.format = AUDIO_FORMAT_PCM_16_BIT; 154 pContext->mConfig.inputCfg.samplingRate = 44100; 155 pContext->mConfig.inputCfg.bufferProvider.getBuffer = NULL; 156 pContext->mConfig.inputCfg.bufferProvider.releaseBuffer = NULL; 157 pContext->mConfig.inputCfg.bufferProvider.cookie = NULL; 158 pContext->mConfig.inputCfg.mask = EFFECT_CONFIG_ALL; 159 pContext->mConfig.outputCfg.accessMode = EFFECT_BUFFER_ACCESS_ACCUMULATE; 160 pContext->mConfig.outputCfg.channels = AUDIO_CHANNEL_OUT_STEREO; 161 pContext->mConfig.outputCfg.format = AUDIO_FORMAT_PCM_16_BIT; 162 pContext->mConfig.outputCfg.samplingRate = 44100; 163 pContext->mConfig.outputCfg.bufferProvider.getBuffer = NULL; 164 pContext->mConfig.outputCfg.bufferProvider.releaseBuffer = NULL; 165 pContext->mConfig.outputCfg.bufferProvider.cookie = NULL; 166 pContext->mConfig.outputCfg.mask = EFFECT_CONFIG_ALL; 167 168 pContext->mCaptureSize = VISUALIZER_CAPTURE_SIZE_MAX; 169 pContext->mScalingMode = VISUALIZER_SCALING_MODE_NORMALIZED; 170 171 Visualizer_setConfig(pContext, &pContext->mConfig); 172 173 return 0; 174} 175 176// 177//--- Effect Library Interface Implementation 178// 179 180int VisualizerLib_QueryNumberEffects(uint32_t *pNumEffects) { 181 *pNumEffects = 1; 182 return 0; 183} 184 185int VisualizerLib_QueryEffect(uint32_t index, 186 effect_descriptor_t *pDescriptor) { 187 if (pDescriptor == NULL) { 188 return -EINVAL; 189 } 190 if (index > 0) { 191 return -EINVAL; 192 } 193 *pDescriptor = gVisualizerDescriptor; 194 return 0; 195} 196 197int VisualizerLib_Create(const effect_uuid_t *uuid, 198 int32_t sessionId, 199 int32_t ioId, 200 effect_handle_t *pHandle) { 201 int ret; 202 int i; 203 204 if (pHandle == NULL || uuid == NULL) { 205 return -EINVAL; 206 } 207 208 if (memcmp(uuid, &gVisualizerDescriptor.uuid, sizeof(effect_uuid_t)) != 0) { 209 return -EINVAL; 210 } 211 212 VisualizerContext *pContext = new VisualizerContext; 213 214 pContext->mItfe = &gVisualizerInterface; 215 pContext->mState = VISUALIZER_STATE_UNINITIALIZED; 216 217 ret = Visualizer_init(pContext); 218 if (ret < 0) { 219 ALOGW("VisualizerLib_Create() init failed"); 220 delete pContext; 221 return ret; 222 } 223 224 *pHandle = (effect_handle_t)pContext; 225 226 pContext->mState = VISUALIZER_STATE_INITIALIZED; 227 228 ALOGV("VisualizerLib_Create %p", pContext); 229 230 return 0; 231 232} 233 234int VisualizerLib_Release(effect_handle_t handle) { 235 VisualizerContext * pContext = (VisualizerContext *)handle; 236 237 ALOGV("VisualizerLib_Release %p", handle); 238 if (pContext == NULL) { 239 return -EINVAL; 240 } 241 pContext->mState = VISUALIZER_STATE_UNINITIALIZED; 242 delete pContext; 243 244 return 0; 245} 246 247int VisualizerLib_GetDescriptor(const effect_uuid_t *uuid, 248 effect_descriptor_t *pDescriptor) { 249 250 if (pDescriptor == NULL || uuid == NULL){ 251 ALOGV("VisualizerLib_GetDescriptor() called with NULL pointer"); 252 return -EINVAL; 253 } 254 255 if (memcmp(uuid, &gVisualizerDescriptor.uuid, sizeof(effect_uuid_t)) == 0) { 256 *pDescriptor = gVisualizerDescriptor; 257 return 0; 258 } 259 260 return -EINVAL; 261} /* end VisualizerLib_GetDescriptor */ 262 263// 264//--- Effect Control Interface Implementation 265// 266 267static inline int16_t clamp16(int32_t sample) 268{ 269 if ((sample>>15) ^ (sample>>31)) 270 sample = 0x7FFF ^ (sample>>31); 271 return sample; 272} 273 274int Visualizer_process( 275 effect_handle_t self,audio_buffer_t *inBuffer, audio_buffer_t *outBuffer) 276{ 277 VisualizerContext * pContext = (VisualizerContext *)self; 278 279 if (pContext == NULL) { 280 return -EINVAL; 281 } 282 283 if (inBuffer == NULL || inBuffer->raw == NULL || 284 outBuffer == NULL || outBuffer->raw == NULL || 285 inBuffer->frameCount != outBuffer->frameCount || 286 inBuffer->frameCount == 0) { 287 return -EINVAL; 288 } 289 290 // all code below assumes stereo 16 bit PCM output and input 291 int32_t shift; 292 293 if (pContext->mScalingMode == VISUALIZER_SCALING_MODE_NORMALIZED) { 294 // derive capture scaling factor from peak value in current buffer 295 // this gives more interesting captures for display. 296 shift = 32; 297 int len = inBuffer->frameCount * 2; 298 for (int i = 0; i < len; i++) { 299 int32_t smp = inBuffer->s16[i]; 300 if (smp < 0) smp = -smp - 1; // take care to keep the max negative in range 301 int32_t clz = __builtin_clz(smp); 302 if (shift > clz) shift = clz; 303 } 304 // A maximum amplitude signal will have 17 leading zeros, which we want to 305 // translate to a shift of 8 (for converting 16 bit to 8 bit) 306 shift = 25 - shift; 307 // Never scale by less than 8 to avoid returning unaltered PCM signal. 308 if (shift < 3) { 309 shift = 3; 310 } 311 // add one to combine the division by 2 needed after summing left and right channels below 312 shift++; 313 } else { 314 assert(pContext->mScalingMode == VISUALIZER_SCALING_MODE_AS_PLAYED); 315 shift = 9; 316 } 317 318 uint32_t captIdx; 319 uint32_t inIdx; 320 uint8_t *buf = pContext->mCaptureBuf; 321 for (inIdx = 0, captIdx = pContext->mCaptureIdx; 322 inIdx < inBuffer->frameCount; 323 inIdx++, captIdx++) { 324 if (captIdx >= CAPTURE_BUF_SIZE) { 325 // wrap around 326 captIdx = 0; 327 } 328 int32_t smp = inBuffer->s16[2 * inIdx] + inBuffer->s16[2 * inIdx + 1]; 329 smp = smp >> shift; 330 buf[captIdx] = ((uint8_t)smp)^0x80; 331 } 332 333 // XXX the following two should really be atomic, though it probably doesn't 334 // matter much for visualization purposes 335 pContext->mCaptureIdx = captIdx; 336 // update last buffer update time stamp 337 if (clock_gettime(CLOCK_MONOTONIC, &pContext->mBufferUpdateTime) < 0) { 338 pContext->mBufferUpdateTime.tv_sec = 0; 339 } 340 341 if (inBuffer->raw != outBuffer->raw) { 342 if (pContext->mConfig.outputCfg.accessMode == EFFECT_BUFFER_ACCESS_ACCUMULATE) { 343 for (size_t i = 0; i < outBuffer->frameCount*2; i++) { 344 outBuffer->s16[i] = clamp16(outBuffer->s16[i] + inBuffer->s16[i]); 345 } 346 } else { 347 memcpy(outBuffer->raw, inBuffer->raw, outBuffer->frameCount * 2 * sizeof(int16_t)); 348 } 349 } 350 if (pContext->mState != VISUALIZER_STATE_ACTIVE) { 351 return -ENODATA; 352 } 353 return 0; 354} // end Visualizer_process 355 356int Visualizer_command(effect_handle_t self, uint32_t cmdCode, uint32_t cmdSize, 357 void *pCmdData, uint32_t *replySize, void *pReplyData) { 358 359 VisualizerContext * pContext = (VisualizerContext *)self; 360 int retsize; 361 362 if (pContext == NULL || pContext->mState == VISUALIZER_STATE_UNINITIALIZED) { 363 return -EINVAL; 364 } 365 366// ALOGV("Visualizer_command command %d cmdSize %d",cmdCode, cmdSize); 367 368 switch (cmdCode) { 369 case EFFECT_CMD_INIT: 370 if (pReplyData == NULL || *replySize != sizeof(int)) { 371 return -EINVAL; 372 } 373 *(int *) pReplyData = Visualizer_init(pContext); 374 break; 375 case EFFECT_CMD_SET_CONFIG: 376 if (pCmdData == NULL || cmdSize != sizeof(effect_config_t) 377 || pReplyData == NULL || *replySize != sizeof(int)) { 378 return -EINVAL; 379 } 380 *(int *) pReplyData = Visualizer_setConfig(pContext, 381 (effect_config_t *) pCmdData); 382 break; 383 case EFFECT_CMD_GET_CONFIG: 384 if (pReplyData == NULL || 385 *replySize != sizeof(effect_config_t)) { 386 return -EINVAL; 387 } 388 Visualizer_getConfig(pContext, (effect_config_t *)pReplyData); 389 break; 390 case EFFECT_CMD_RESET: 391 Visualizer_reset(pContext); 392 break; 393 case EFFECT_CMD_ENABLE: 394 if (pReplyData == NULL || *replySize != sizeof(int)) { 395 return -EINVAL; 396 } 397 if (pContext->mState != VISUALIZER_STATE_INITIALIZED) { 398 return -ENOSYS; 399 } 400 pContext->mState = VISUALIZER_STATE_ACTIVE; 401 ALOGV("EFFECT_CMD_ENABLE() OK"); 402 *(int *)pReplyData = 0; 403 break; 404 case EFFECT_CMD_DISABLE: 405 if (pReplyData == NULL || *replySize != sizeof(int)) { 406 return -EINVAL; 407 } 408 if (pContext->mState != VISUALIZER_STATE_ACTIVE) { 409 return -ENOSYS; 410 } 411 pContext->mState = VISUALIZER_STATE_INITIALIZED; 412 ALOGV("EFFECT_CMD_DISABLE() OK"); 413 *(int *)pReplyData = 0; 414 break; 415 case EFFECT_CMD_GET_PARAM: { 416 if (pCmdData == NULL || 417 cmdSize != (int)(sizeof(effect_param_t) + sizeof(uint32_t)) || 418 pReplyData == NULL || 419 *replySize < (int)(sizeof(effect_param_t) + sizeof(uint32_t) + sizeof(uint32_t))) { 420 return -EINVAL; 421 } 422 memcpy(pReplyData, pCmdData, sizeof(effect_param_t) + sizeof(uint32_t)); 423 effect_param_t *p = (effect_param_t *)pReplyData; 424 p->status = 0; 425 *replySize = sizeof(effect_param_t) + sizeof(uint32_t); 426 if (p->psize != sizeof(uint32_t)) { 427 p->status = -EINVAL; 428 break; 429 } 430 switch (*(uint32_t *)p->data) { 431 case VISUALIZER_PARAM_CAPTURE_SIZE: 432 ALOGV("get mCaptureSize = %d", pContext->mCaptureSize); 433 *((uint32_t *)p->data + 1) = pContext->mCaptureSize; 434 p->vsize = sizeof(uint32_t); 435 *replySize += sizeof(uint32_t); 436 break; 437 case VISUALIZER_PARAM_SCALING_MODE: 438 ALOGV("get mScalingMode = %d", pContext->mScalingMode); 439 *((uint32_t *)p->data + 1) = pContext->mScalingMode; 440 p->vsize = sizeof(uint32_t); 441 *replySize += sizeof(uint32_t); 442 break; 443 default: 444 p->status = -EINVAL; 445 } 446 } break; 447 case EFFECT_CMD_SET_PARAM: { 448 if (pCmdData == NULL || 449 cmdSize != (int)(sizeof(effect_param_t) + sizeof(uint32_t) + sizeof(uint32_t)) || 450 pReplyData == NULL || *replySize != sizeof(int32_t)) { 451 return -EINVAL; 452 } 453 *(int32_t *)pReplyData = 0; 454 effect_param_t *p = (effect_param_t *)pCmdData; 455 if (p->psize != sizeof(uint32_t) || p->vsize != sizeof(uint32_t)) { 456 *(int32_t *)pReplyData = -EINVAL; 457 break; 458 } 459 switch (*(uint32_t *)p->data) { 460 case VISUALIZER_PARAM_CAPTURE_SIZE: 461 pContext->mCaptureSize = *((uint32_t *)p->data + 1); 462 ALOGV("set mCaptureSize = %d", pContext->mCaptureSize); 463 break; 464 case VISUALIZER_PARAM_SCALING_MODE: 465 pContext->mScalingMode = *((uint32_t *)p->data + 1); 466 ALOGV("set mScalingMode = %d", pContext->mScalingMode); 467 break; 468 case VISUALIZER_PARAM_LATENCY: 469 pContext->mLatency = *((uint32_t *)p->data + 1); 470 ALOGV("set mLatency = %d", pContext->mLatency); 471 break; 472 default: 473 *(int32_t *)pReplyData = -EINVAL; 474 } 475 } break; 476 case EFFECT_CMD_SET_DEVICE: 477 case EFFECT_CMD_SET_VOLUME: 478 case EFFECT_CMD_SET_AUDIO_MODE: 479 break; 480 481 482 case VISUALIZER_CMD_CAPTURE: 483 if (pReplyData == NULL || *replySize != pContext->mCaptureSize) { 484 ALOGV("VISUALIZER_CMD_CAPTURE() error *replySize %d pContext->mCaptureSize %d", 485 *replySize, pContext->mCaptureSize); 486 return -EINVAL; 487 } 488 if (pContext->mState == VISUALIZER_STATE_ACTIVE) { 489 int32_t latencyMs = pContext->mLatency; 490 uint32_t deltaMs = 0; 491 if (pContext->mBufferUpdateTime.tv_sec != 0) { 492 struct timespec ts; 493 if (clock_gettime(CLOCK_MONOTONIC, &ts) == 0) { 494 time_t secs = ts.tv_sec - pContext->mBufferUpdateTime.tv_sec; 495 long nsec = ts.tv_nsec - pContext->mBufferUpdateTime.tv_nsec; 496 if (nsec < 0) { 497 --secs; 498 nsec += 1000000000; 499 } 500 deltaMs = secs * 1000 + nsec / 1000000; 501 latencyMs -= deltaMs; 502 if (latencyMs < 0) { 503 latencyMs = 0; 504 } 505 } 506 } 507 uint32_t deltaSmpl = pContext->mConfig.inputCfg.samplingRate * latencyMs / 1000; 508 509 int32_t capturePoint = pContext->mCaptureIdx - pContext->mCaptureSize - deltaSmpl; 510 int32_t captureSize = pContext->mCaptureSize; 511 if (capturePoint < 0) { 512 int32_t size = -capturePoint; 513 if (size > captureSize) { 514 size = captureSize; 515 } 516 memcpy(pReplyData, 517 pContext->mCaptureBuf + CAPTURE_BUF_SIZE + capturePoint, 518 size); 519 pReplyData += size; 520 captureSize -= size; 521 capturePoint = 0; 522 } 523 memcpy(pReplyData, 524 pContext->mCaptureBuf + capturePoint, 525 captureSize); 526 527 528 // if audio framework has stopped playing audio although the effect is still 529 // active we must clear the capture buffer to return silence 530 if ((pContext->mLastCaptureIdx == pContext->mCaptureIdx) && 531 (pContext->mBufferUpdateTime.tv_sec != 0)) { 532 if (deltaMs > MAX_STALL_TIME_MS) { 533 ALOGV("capture going to idle"); 534 pContext->mBufferUpdateTime.tv_sec = 0; 535 memset(pReplyData, 0x80, pContext->mCaptureSize); 536 } 537 } 538 pContext->mLastCaptureIdx = pContext->mCaptureIdx; 539 } else { 540 memset(pReplyData, 0x80, pContext->mCaptureSize); 541 } 542 543 break; 544 545 default: 546 ALOGW("Visualizer_command invalid command %d",cmdCode); 547 return -EINVAL; 548 } 549 550 return 0; 551} 552 553/* Effect Control Interface Implementation: get_descriptor */ 554int Visualizer_getDescriptor(effect_handle_t self, 555 effect_descriptor_t *pDescriptor) 556{ 557 VisualizerContext * pContext = (VisualizerContext *) self; 558 559 if (pContext == NULL || pDescriptor == NULL) { 560 ALOGV("Visualizer_getDescriptor() invalid param"); 561 return -EINVAL; 562 } 563 564 *pDescriptor = gVisualizerDescriptor; 565 566 return 0; 567} /* end Visualizer_getDescriptor */ 568 569// effect_handle_t interface implementation for visualizer effect 570const struct effect_interface_s gVisualizerInterface = { 571 Visualizer_process, 572 Visualizer_command, 573 Visualizer_getDescriptor, 574 NULL, 575}; 576 577 578audio_effect_library_t AUDIO_EFFECT_LIBRARY_INFO_SYM = { 579 tag : AUDIO_EFFECT_LIBRARY_TAG, 580 version : EFFECT_LIBRARY_API_VERSION, 581 name : "Visualizer Library", 582 implementor : "The Android Open Source Project", 583 query_num_effects : VisualizerLib_QueryNumberEffects, 584 query_effect : VisualizerLib_QueryEffect, 585 create_effect : VisualizerLib_Create, 586 release_effect : VisualizerLib_Release, 587 get_descriptor : VisualizerLib_GetDescriptor, 588}; 589 590}; // extern "C" 591