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