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