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