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