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 = 211 audio_channel_count_from_out_mask(pContext->mConfig.inputCfg.channels); 212 pContext->mMeasurementMode = MEASUREMENT_MODE_NONE; 213 pContext->mMeasurementWindowSizeInBuffers = MEASUREMENT_WINDOW_MAX_SIZE_IN_BUFFERS; 214 pContext->mMeasurementBufferIdx = 0; 215 for (uint32_t i=0 ; i<pContext->mMeasurementWindowSizeInBuffers ; i++) { 216 pContext->mPastMeasurements[i].mIsValid = false; 217 pContext->mPastMeasurements[i].mPeakU16 = 0; 218 pContext->mPastMeasurements[i].mRmsSquared = 0; 219 } 220 221 Visualizer_setConfig(pContext, &pContext->mConfig); 222 223 return 0; 224} 225 226// 227//--- Effect Library Interface Implementation 228// 229 230int VisualizerLib_Create(const effect_uuid_t *uuid, 231 int32_t /*sessionId*/, 232 int32_t /*ioId*/, 233 effect_handle_t *pHandle) { 234 int ret; 235 int i; 236 237 if (pHandle == NULL || uuid == NULL) { 238 return -EINVAL; 239 } 240 241 if (memcmp(uuid, &gVisualizerDescriptor.uuid, sizeof(effect_uuid_t)) != 0) { 242 return -EINVAL; 243 } 244 245 VisualizerContext *pContext = new VisualizerContext; 246 247 pContext->mItfe = &gVisualizerInterface; 248 pContext->mState = VISUALIZER_STATE_UNINITIALIZED; 249 250 ret = Visualizer_init(pContext); 251 if (ret < 0) { 252 ALOGW("VisualizerLib_Create() init failed"); 253 delete pContext; 254 return ret; 255 } 256 257 *pHandle = (effect_handle_t)pContext; 258 259 pContext->mState = VISUALIZER_STATE_INITIALIZED; 260 261 ALOGV("VisualizerLib_Create %p", pContext); 262 263 return 0; 264 265} 266 267int VisualizerLib_Release(effect_handle_t handle) { 268 VisualizerContext * pContext = (VisualizerContext *)handle; 269 270 ALOGV("VisualizerLib_Release %p", handle); 271 if (pContext == NULL) { 272 return -EINVAL; 273 } 274 pContext->mState = VISUALIZER_STATE_UNINITIALIZED; 275 delete pContext; 276 277 return 0; 278} 279 280int VisualizerLib_GetDescriptor(const effect_uuid_t *uuid, 281 effect_descriptor_t *pDescriptor) { 282 283 if (pDescriptor == NULL || uuid == NULL){ 284 ALOGV("VisualizerLib_GetDescriptor() called with NULL pointer"); 285 return -EINVAL; 286 } 287 288 if (memcmp(uuid, &gVisualizerDescriptor.uuid, sizeof(effect_uuid_t)) == 0) { 289 *pDescriptor = gVisualizerDescriptor; 290 return 0; 291 } 292 293 return -EINVAL; 294} /* end VisualizerLib_GetDescriptor */ 295 296// 297//--- Effect Control Interface Implementation 298// 299 300static inline int16_t clamp16(int32_t sample) 301{ 302 if ((sample>>15) ^ (sample>>31)) 303 sample = 0x7FFF ^ (sample>>31); 304 return sample; 305} 306 307int Visualizer_process( 308 effect_handle_t self,audio_buffer_t *inBuffer, audio_buffer_t *outBuffer) 309{ 310 VisualizerContext * pContext = (VisualizerContext *)self; 311 312 if (pContext == NULL) { 313 return -EINVAL; 314 } 315 316 if (inBuffer == NULL || inBuffer->raw == NULL || 317 outBuffer == NULL || outBuffer->raw == NULL || 318 inBuffer->frameCount != outBuffer->frameCount || 319 inBuffer->frameCount == 0) { 320 return -EINVAL; 321 } 322 323 // perform measurements if needed 324 if (pContext->mMeasurementMode & MEASUREMENT_MODE_PEAK_RMS) { 325 // find the peak and RMS squared for the new buffer 326 uint32_t inIdx; 327 int16_t maxSample = 0; 328 float rmsSqAcc = 0; 329 for (inIdx = 0 ; inIdx < inBuffer->frameCount * pContext->mChannelCount ; inIdx++) { 330 if (inBuffer->s16[inIdx] > maxSample) { 331 maxSample = inBuffer->s16[inIdx]; 332 } else if (-inBuffer->s16[inIdx] > maxSample) { 333 maxSample = -inBuffer->s16[inIdx]; 334 } 335 rmsSqAcc += (inBuffer->s16[inIdx] * inBuffer->s16[inIdx]); 336 } 337 // store the measurement 338 pContext->mPastMeasurements[pContext->mMeasurementBufferIdx].mPeakU16 = (uint16_t)maxSample; 339 pContext->mPastMeasurements[pContext->mMeasurementBufferIdx].mRmsSquared = 340 rmsSqAcc / (inBuffer->frameCount * pContext->mChannelCount); 341 pContext->mPastMeasurements[pContext->mMeasurementBufferIdx].mIsValid = true; 342 if (++pContext->mMeasurementBufferIdx >= pContext->mMeasurementWindowSizeInBuffers) { 343 pContext->mMeasurementBufferIdx = 0; 344 } 345 } 346 347 // all code below assumes stereo 16 bit PCM output and input 348 int32_t shift; 349 350 if (pContext->mScalingMode == VISUALIZER_SCALING_MODE_NORMALIZED) { 351 // derive capture scaling factor from peak value in current buffer 352 // this gives more interesting captures for display. 353 shift = 32; 354 int len = inBuffer->frameCount * 2; 355 for (int i = 0; i < len; i++) { 356 int32_t smp = inBuffer->s16[i]; 357 if (smp < 0) smp = -smp - 1; // take care to keep the max negative in range 358 int32_t clz = __builtin_clz(smp); 359 if (shift > clz) shift = clz; 360 } 361 // A maximum amplitude signal will have 17 leading zeros, which we want to 362 // translate to a shift of 8 (for converting 16 bit to 8 bit) 363 shift = 25 - shift; 364 // Never scale by less than 8 to avoid returning unaltered PCM signal. 365 if (shift < 3) { 366 shift = 3; 367 } 368 // add one to combine the division by 2 needed after summing left and right channels below 369 shift++; 370 } else { 371 assert(pContext->mScalingMode == VISUALIZER_SCALING_MODE_AS_PLAYED); 372 shift = 9; 373 } 374 375 uint32_t captIdx; 376 uint32_t inIdx; 377 uint8_t *buf = pContext->mCaptureBuf; 378 for (inIdx = 0, captIdx = pContext->mCaptureIdx; 379 inIdx < inBuffer->frameCount; 380 inIdx++, captIdx++) { 381 if (captIdx >= CAPTURE_BUF_SIZE) { 382 // wrap around 383 captIdx = 0; 384 } 385 int32_t smp = inBuffer->s16[2 * inIdx] + inBuffer->s16[2 * inIdx + 1]; 386 smp = smp >> shift; 387 buf[captIdx] = ((uint8_t)smp)^0x80; 388 } 389 390 // XXX the following two should really be atomic, though it probably doesn't 391 // matter much for visualization purposes 392 pContext->mCaptureIdx = captIdx; 393 // update last buffer update time stamp 394 if (clock_gettime(CLOCK_MONOTONIC, &pContext->mBufferUpdateTime) < 0) { 395 pContext->mBufferUpdateTime.tv_sec = 0; 396 } 397 398 if (inBuffer->raw != outBuffer->raw) { 399 if (pContext->mConfig.outputCfg.accessMode == EFFECT_BUFFER_ACCESS_ACCUMULATE) { 400 for (size_t i = 0; i < outBuffer->frameCount*2; i++) { 401 outBuffer->s16[i] = clamp16(outBuffer->s16[i] + inBuffer->s16[i]); 402 } 403 } else { 404 memcpy(outBuffer->raw, inBuffer->raw, outBuffer->frameCount * 2 * sizeof(int16_t)); 405 } 406 } 407 if (pContext->mState != VISUALIZER_STATE_ACTIVE) { 408 return -ENODATA; 409 } 410 return 0; 411} // end Visualizer_process 412 413int Visualizer_command(effect_handle_t self, uint32_t cmdCode, uint32_t cmdSize, 414 void *pCmdData, uint32_t *replySize, void *pReplyData) { 415 416 VisualizerContext * pContext = (VisualizerContext *)self; 417 int retsize; 418 419 if (pContext == NULL || pContext->mState == VISUALIZER_STATE_UNINITIALIZED) { 420 return -EINVAL; 421 } 422 423// ALOGV("Visualizer_command command %" PRIu32 " cmdSize %" PRIu32, cmdCode, cmdSize); 424 425 switch (cmdCode) { 426 case EFFECT_CMD_INIT: 427 if (pReplyData == NULL || replySize == NULL || *replySize != sizeof(int)) { 428 return -EINVAL; 429 } 430 *(int *) pReplyData = Visualizer_init(pContext); 431 break; 432 case EFFECT_CMD_SET_CONFIG: 433 if (pCmdData == NULL || cmdSize != sizeof(effect_config_t) 434 || pReplyData == NULL || replySize == NULL || *replySize != sizeof(int)) { 435 return -EINVAL; 436 } 437 *(int *) pReplyData = Visualizer_setConfig(pContext, 438 (effect_config_t *) pCmdData); 439 break; 440 case EFFECT_CMD_GET_CONFIG: 441 if (pReplyData == NULL || replySize == NULL || 442 *replySize != sizeof(effect_config_t)) { 443 return -EINVAL; 444 } 445 Visualizer_getConfig(pContext, (effect_config_t *)pReplyData); 446 break; 447 case EFFECT_CMD_RESET: 448 Visualizer_reset(pContext); 449 break; 450 case EFFECT_CMD_ENABLE: 451 if (pReplyData == NULL || replySize == NULL || *replySize != sizeof(int)) { 452 return -EINVAL; 453 } 454 if (pContext->mState != VISUALIZER_STATE_INITIALIZED) { 455 return -ENOSYS; 456 } 457 pContext->mState = VISUALIZER_STATE_ACTIVE; 458 ALOGV("EFFECT_CMD_ENABLE() OK"); 459 *(int *)pReplyData = 0; 460 break; 461 case EFFECT_CMD_DISABLE: 462 if (pReplyData == NULL || replySize == NULL || *replySize != sizeof(int)) { 463 return -EINVAL; 464 } 465 if (pContext->mState != VISUALIZER_STATE_ACTIVE) { 466 return -ENOSYS; 467 } 468 pContext->mState = VISUALIZER_STATE_INITIALIZED; 469 ALOGV("EFFECT_CMD_DISABLE() OK"); 470 *(int *)pReplyData = 0; 471 break; 472 case EFFECT_CMD_GET_PARAM: { 473 if (pCmdData == NULL || 474 cmdSize != (int)(sizeof(effect_param_t) + sizeof(uint32_t)) || 475 pReplyData == NULL || replySize == NULL || 476 *replySize < (int)(sizeof(effect_param_t) + sizeof(uint32_t) + sizeof(uint32_t))) { 477 return -EINVAL; 478 } 479 memcpy(pReplyData, pCmdData, sizeof(effect_param_t) + sizeof(uint32_t)); 480 effect_param_t *p = (effect_param_t *)pReplyData; 481 p->status = 0; 482 *replySize = sizeof(effect_param_t) + sizeof(uint32_t); 483 if (p->psize != sizeof(uint32_t)) { 484 p->status = -EINVAL; 485 break; 486 } 487 switch (*(uint32_t *)p->data) { 488 case VISUALIZER_PARAM_CAPTURE_SIZE: 489 ALOGV("get mCaptureSize = %" PRIu32, pContext->mCaptureSize); 490 *((uint32_t *)p->data + 1) = pContext->mCaptureSize; 491 p->vsize = sizeof(uint32_t); 492 *replySize += sizeof(uint32_t); 493 break; 494 case VISUALIZER_PARAM_SCALING_MODE: 495 ALOGV("get mScalingMode = %" PRIu32, pContext->mScalingMode); 496 *((uint32_t *)p->data + 1) = pContext->mScalingMode; 497 p->vsize = sizeof(uint32_t); 498 *replySize += sizeof(uint32_t); 499 break; 500 case VISUALIZER_PARAM_MEASUREMENT_MODE: 501 ALOGV("get mMeasurementMode = %" PRIu32, pContext->mMeasurementMode); 502 *((uint32_t *)p->data + 1) = pContext->mMeasurementMode; 503 p->vsize = sizeof(uint32_t); 504 *replySize += sizeof(uint32_t); 505 break; 506 default: 507 p->status = -EINVAL; 508 } 509 } break; 510 case EFFECT_CMD_SET_PARAM: { 511 if (pCmdData == NULL || 512 cmdSize != (int)(sizeof(effect_param_t) + sizeof(uint32_t) + sizeof(uint32_t)) || 513 pReplyData == NULL || replySize == NULL || *replySize != sizeof(int32_t)) { 514 return -EINVAL; 515 } 516 *(int32_t *)pReplyData = 0; 517 effect_param_t *p = (effect_param_t *)pCmdData; 518 if (p->psize != sizeof(uint32_t) || p->vsize != sizeof(uint32_t)) { 519 *(int32_t *)pReplyData = -EINVAL; 520 break; 521 } 522 switch (*(uint32_t *)p->data) { 523 case VISUALIZER_PARAM_CAPTURE_SIZE: 524 pContext->mCaptureSize = *((uint32_t *)p->data + 1); 525 ALOGV("set mCaptureSize = %" PRIu32, pContext->mCaptureSize); 526 break; 527 case VISUALIZER_PARAM_SCALING_MODE: 528 pContext->mScalingMode = *((uint32_t *)p->data + 1); 529 ALOGV("set mScalingMode = %" PRIu32, pContext->mScalingMode); 530 break; 531 case VISUALIZER_PARAM_LATENCY: 532 pContext->mLatency = *((uint32_t *)p->data + 1); 533 ALOGV("set mLatency = %" PRIu32, pContext->mLatency); 534 break; 535 case VISUALIZER_PARAM_MEASUREMENT_MODE: 536 pContext->mMeasurementMode = *((uint32_t *)p->data + 1); 537 ALOGV("set mMeasurementMode = %" PRIu32, pContext->mMeasurementMode); 538 break; 539 default: 540 *(int32_t *)pReplyData = -EINVAL; 541 } 542 } break; 543 case EFFECT_CMD_SET_DEVICE: 544 case EFFECT_CMD_SET_VOLUME: 545 case EFFECT_CMD_SET_AUDIO_MODE: 546 break; 547 548 549 case VISUALIZER_CMD_CAPTURE: { 550 uint32_t captureSize = pContext->mCaptureSize; 551 if (pReplyData == NULL || replySize == NULL || *replySize != captureSize) { 552 ALOGV("VISUALIZER_CMD_CAPTURE() error *replySize %" PRIu32 " captureSize %" PRIu32, 553 *replySize, captureSize); 554 return -EINVAL; 555 } 556 if (pContext->mState == VISUALIZER_STATE_ACTIVE) { 557 const uint32_t deltaMs = Visualizer_getDeltaTimeMsFromUpdatedTime(pContext); 558 559 // if audio framework has stopped playing audio although the effect is still 560 // active we must clear the capture buffer to return silence 561 if ((pContext->mLastCaptureIdx == pContext->mCaptureIdx) && 562 (pContext->mBufferUpdateTime.tv_sec != 0) && 563 (deltaMs > MAX_STALL_TIME_MS)) { 564 ALOGV("capture going to idle"); 565 pContext->mBufferUpdateTime.tv_sec = 0; 566 memset(pReplyData, 0x80, captureSize); 567 } else { 568 int32_t latencyMs = pContext->mLatency; 569 latencyMs -= deltaMs; 570 if (latencyMs < 0) { 571 latencyMs = 0; 572 } 573 const uint32_t deltaSmpl = 574 pContext->mConfig.inputCfg.samplingRate * latencyMs / 1000; 575 int32_t capturePoint = pContext->mCaptureIdx - captureSize - deltaSmpl; 576 577 if (capturePoint < 0) { 578 uint32_t size = -capturePoint; 579 if (size > captureSize) { 580 size = captureSize; 581 } 582 memcpy(pReplyData, 583 pContext->mCaptureBuf + CAPTURE_BUF_SIZE + capturePoint, 584 size); 585 pReplyData = (char *)pReplyData + size; 586 captureSize -= size; 587 capturePoint = 0; 588 } 589 memcpy(pReplyData, 590 pContext->mCaptureBuf + capturePoint, 591 captureSize); 592 } 593 594 pContext->mLastCaptureIdx = pContext->mCaptureIdx; 595 } else { 596 memset(pReplyData, 0x80, captureSize); 597 } 598 599 } break; 600 601 case VISUALIZER_CMD_MEASURE: { 602 uint16_t peakU16 = 0; 603 float sumRmsSquared = 0.0f; 604 uint8_t nbValidMeasurements = 0; 605 // reset measurements if last measurement was too long ago (which implies stored 606 // measurements aren't relevant anymore and shouldn't bias the new one) 607 const int32_t delayMs = Visualizer_getDeltaTimeMsFromUpdatedTime(pContext); 608 if (delayMs > DISCARD_MEASUREMENTS_TIME_MS) { 609 ALOGV("Discarding measurements, last measurement is %" PRId32 "ms old", delayMs); 610 for (uint32_t i=0 ; i<pContext->mMeasurementWindowSizeInBuffers ; i++) { 611 pContext->mPastMeasurements[i].mIsValid = false; 612 pContext->mPastMeasurements[i].mPeakU16 = 0; 613 pContext->mPastMeasurements[i].mRmsSquared = 0; 614 } 615 pContext->mMeasurementBufferIdx = 0; 616 } else { 617 // only use actual measurements, otherwise the first RMS measure happening before 618 // MEASUREMENT_WINDOW_MAX_SIZE_IN_BUFFERS have been played will always be artificially 619 // low 620 for (uint32_t i=0 ; i < pContext->mMeasurementWindowSizeInBuffers ; i++) { 621 if (pContext->mPastMeasurements[i].mIsValid) { 622 if (pContext->mPastMeasurements[i].mPeakU16 > peakU16) { 623 peakU16 = pContext->mPastMeasurements[i].mPeakU16; 624 } 625 sumRmsSquared += pContext->mPastMeasurements[i].mRmsSquared; 626 nbValidMeasurements++; 627 } 628 } 629 } 630 float rms = nbValidMeasurements == 0 ? 0.0f : sqrtf(sumRmsSquared / nbValidMeasurements); 631 int32_t* pIntReplyData = (int32_t*)pReplyData; 632 // convert from I16 sample values to mB and write results 633 if (rms < 0.000016f) { 634 pIntReplyData[MEASUREMENT_IDX_RMS] = -9600; //-96dB 635 } else { 636 pIntReplyData[MEASUREMENT_IDX_RMS] = (int32_t) (2000 * log10(rms / 32767.0f)); 637 } 638 if (peakU16 == 0) { 639 pIntReplyData[MEASUREMENT_IDX_PEAK] = -9600; //-96dB 640 } else { 641 pIntReplyData[MEASUREMENT_IDX_PEAK] = (int32_t) (2000 * log10(peakU16 / 32767.0f)); 642 } 643 ALOGV("VISUALIZER_CMD_MEASURE peak=%" PRIu16 " (%" PRId32 "mB), rms=%.1f (%" PRId32 "mB)", 644 peakU16, pIntReplyData[MEASUREMENT_IDX_PEAK], 645 rms, pIntReplyData[MEASUREMENT_IDX_RMS]); 646 } 647 break; 648 649 default: 650 ALOGW("Visualizer_command invalid command %" PRIu32, cmdCode); 651 return -EINVAL; 652 } 653 654 return 0; 655} 656 657/* Effect Control Interface Implementation: get_descriptor */ 658int Visualizer_getDescriptor(effect_handle_t self, 659 effect_descriptor_t *pDescriptor) 660{ 661 VisualizerContext * pContext = (VisualizerContext *) self; 662 663 if (pContext == NULL || pDescriptor == NULL) { 664 ALOGV("Visualizer_getDescriptor() invalid param"); 665 return -EINVAL; 666 } 667 668 *pDescriptor = gVisualizerDescriptor; 669 670 return 0; 671} /* end Visualizer_getDescriptor */ 672 673// effect_handle_t interface implementation for visualizer effect 674const struct effect_interface_s gVisualizerInterface = { 675 Visualizer_process, 676 Visualizer_command, 677 Visualizer_getDescriptor, 678 NULL, 679}; 680 681// This is the only symbol that needs to be exported 682__attribute__ ((visibility ("default"))) 683audio_effect_library_t AUDIO_EFFECT_LIBRARY_INFO_SYM = { 684 .tag = AUDIO_EFFECT_LIBRARY_TAG, 685 .version = EFFECT_LIBRARY_API_VERSION, 686 .name = "Visualizer Library", 687 .implementor = "The Android Open Source Project", 688 .create_effect = VisualizerLib_Create, 689 .release_effect = VisualizerLib_Release, 690 .get_descriptor = VisualizerLib_GetDescriptor, 691}; 692 693}; // extern "C" 694