EffectVisualizer.cpp revision 3df40a093d8d3d211f693e0e3ef4076750cabfea
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_configure() 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_configure(VisualizerContext *pContext, effect_config_t *pConfig) 95{ 96 LOGV("Visualizer_configure 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_init() 116//---------------------------------------------------------------------------- 117// Purpose: Initialize engine with default configuration. 118// 119// Inputs: 120// pContext: effect engine context 121// 122// Outputs: 123// 124//---------------------------------------------------------------------------- 125 126int Visualizer_init(VisualizerContext *pContext) 127{ 128 pContext->mConfig.inputCfg.accessMode = EFFECT_BUFFER_ACCESS_READ; 129 pContext->mConfig.inputCfg.channels = AUDIO_CHANNEL_OUT_STEREO; 130 pContext->mConfig.inputCfg.format = AUDIO_FORMAT_PCM_16_BIT; 131 pContext->mConfig.inputCfg.samplingRate = 44100; 132 pContext->mConfig.inputCfg.bufferProvider.getBuffer = NULL; 133 pContext->mConfig.inputCfg.bufferProvider.releaseBuffer = NULL; 134 pContext->mConfig.inputCfg.bufferProvider.cookie = NULL; 135 pContext->mConfig.inputCfg.mask = EFFECT_CONFIG_ALL; 136 pContext->mConfig.outputCfg.accessMode = EFFECT_BUFFER_ACCESS_ACCUMULATE; 137 pContext->mConfig.outputCfg.channels = AUDIO_CHANNEL_OUT_STEREO; 138 pContext->mConfig.outputCfg.format = AUDIO_FORMAT_PCM_16_BIT; 139 pContext->mConfig.outputCfg.samplingRate = 44100; 140 pContext->mConfig.outputCfg.bufferProvider.getBuffer = NULL; 141 pContext->mConfig.outputCfg.bufferProvider.releaseBuffer = NULL; 142 pContext->mConfig.outputCfg.bufferProvider.cookie = NULL; 143 pContext->mConfig.outputCfg.mask = EFFECT_CONFIG_ALL; 144 145 pContext->mCaptureSize = VISUALIZER_CAPTURE_SIZE_MAX; 146 147 Visualizer_configure(pContext, &pContext->mConfig); 148 149 return 0; 150} 151 152// 153//--- Effect Library Interface Implementation 154// 155 156int VisualizerLib_QueryNumberEffects(uint32_t *pNumEffects) { 157 *pNumEffects = 1; 158 return 0; 159} 160 161int VisualizerLib_QueryEffect(uint32_t index, 162 effect_descriptor_t *pDescriptor) { 163 if (pDescriptor == NULL) { 164 return -EINVAL; 165 } 166 if (index > 0) { 167 return -EINVAL; 168 } 169 memcpy(pDescriptor, &gVisualizerDescriptor, sizeof(effect_descriptor_t)); 170 return 0; 171} 172 173int VisualizerLib_Create(effect_uuid_t *uuid, 174 int32_t sessionId, 175 int32_t ioId, 176 effect_handle_t *pHandle) { 177 int ret; 178 int i; 179 180 if (pHandle == NULL || uuid == NULL) { 181 return -EINVAL; 182 } 183 184 if (memcmp(uuid, &gVisualizerDescriptor.uuid, sizeof(effect_uuid_t)) != 0) { 185 return -EINVAL; 186 } 187 188 VisualizerContext *pContext = new VisualizerContext; 189 190 pContext->mItfe = &gVisualizerInterface; 191 pContext->mState = VISUALIZER_STATE_UNINITIALIZED; 192 193 ret = Visualizer_init(pContext); 194 if (ret < 0) { 195 LOGW("VisualizerLib_Create() init failed"); 196 delete pContext; 197 return ret; 198 } 199 200 *pHandle = (effect_handle_t)pContext; 201 202 pContext->mState = VISUALIZER_STATE_INITIALIZED; 203 204 LOGV("VisualizerLib_Create %p", pContext); 205 206 return 0; 207 208} 209 210int VisualizerLib_Release(effect_handle_t handle) { 211 VisualizerContext * pContext = (VisualizerContext *)handle; 212 213 LOGV("VisualizerLib_Release %p", handle); 214 if (pContext == NULL) { 215 return -EINVAL; 216 } 217 pContext->mState = VISUALIZER_STATE_UNINITIALIZED; 218 delete pContext; 219 220 return 0; 221} 222 223int VisualizerLib_GetDescriptor(effect_uuid_t *uuid, 224 effect_descriptor_t *pDescriptor) { 225 226 if (pDescriptor == NULL || uuid == NULL){ 227 LOGV("VisualizerLib_GetDescriptor() called with NULL pointer"); 228 return -EINVAL; 229 } 230 231 if (memcmp(uuid, &gVisualizerDescriptor.uuid, sizeof(effect_uuid_t)) == 0) { 232 memcpy(pDescriptor, &gVisualizerDescriptor, sizeof(effect_descriptor_t)); 233 return 0; 234 } 235 236 return -EINVAL; 237} /* end VisualizerLib_GetDescriptor */ 238 239// 240//--- Effect Control Interface Implementation 241// 242 243static inline int16_t clamp16(int32_t sample) 244{ 245 if ((sample>>15) ^ (sample>>31)) 246 sample = 0x7FFF ^ (sample>>31); 247 return sample; 248} 249 250int Visualizer_process( 251 effect_handle_t self,audio_buffer_t *inBuffer, audio_buffer_t *outBuffer) 252{ 253 VisualizerContext * pContext = (VisualizerContext *)self; 254 255 if (pContext == NULL) { 256 return -EINVAL; 257 } 258 259 if (inBuffer == NULL || inBuffer->raw == NULL || 260 outBuffer == NULL || outBuffer->raw == NULL || 261 inBuffer->frameCount != outBuffer->frameCount || 262 inBuffer->frameCount == 0) { 263 return -EINVAL; 264 } 265 266 // all code below assumes stereo 16 bit PCM output and input 267 268 // derive capture scaling factor from peak value in current buffer 269 // this gives more interesting captures for display. 270 int32_t shift = 32; 271 int len = inBuffer->frameCount * 2; 272 for (int i = 0; i < len; i++) { 273 int32_t smp = inBuffer->s16[i]; 274 if (smp < 0) smp = -smp - 1; // take care to keep the max negative in range 275 int32_t clz = __builtin_clz(smp); 276 if (shift > clz) shift = clz; 277 } 278 // A maximum amplitude signal will have 17 leading zeros, which we want to 279 // translate to a shift of 8 (for converting 16 bit to 8 bit) 280 shift = 25 - shift; 281 // Never scale by less than 8 to avoid returning unaltered PCM signal. 282 if (shift < 3) { 283 shift = 3; 284 } 285 // add one to combine the division by 2 needed after summing left and right channels below 286 shift++; 287 288 uint32_t captIdx; 289 uint32_t inIdx; 290 uint8_t *buf = pContext->mCaptureBuf[pContext->mCurrentBuf]; 291 for (inIdx = 0, captIdx = pContext->mCaptureIdx; 292 inIdx < inBuffer->frameCount && captIdx < pContext->mCaptureSize; 293 inIdx++, captIdx++) { 294 int32_t smp = inBuffer->s16[2 * inIdx] + inBuffer->s16[2 * inIdx + 1]; 295 smp = smp >> shift; 296 buf[captIdx] = ((uint8_t)smp)^0x80; 297 } 298 pContext->mCaptureIdx = captIdx; 299 300 // go to next buffer when buffer full 301 if (pContext->mCaptureIdx == pContext->mCaptureSize) { 302 pContext->mCurrentBuf ^= 1; 303 pContext->mCaptureIdx = 0; 304 } 305 306 if (inBuffer->raw != outBuffer->raw) { 307 if (pContext->mConfig.outputCfg.accessMode == EFFECT_BUFFER_ACCESS_ACCUMULATE) { 308 for (size_t i = 0; i < outBuffer->frameCount*2; i++) { 309 outBuffer->s16[i] = clamp16(outBuffer->s16[i] + inBuffer->s16[i]); 310 } 311 } else { 312 memcpy(outBuffer->raw, inBuffer->raw, outBuffer->frameCount * 2 * sizeof(int16_t)); 313 } 314 } 315 if (pContext->mState != VISUALIZER_STATE_ACTIVE) { 316 return -ENODATA; 317 } 318 return 0; 319} // end Visualizer_process 320 321int Visualizer_command(effect_handle_t self, uint32_t cmdCode, uint32_t cmdSize, 322 void *pCmdData, uint32_t *replySize, void *pReplyData) { 323 324 VisualizerContext * pContext = (VisualizerContext *)self; 325 int retsize; 326 327 if (pContext == NULL || pContext->mState == VISUALIZER_STATE_UNINITIALIZED) { 328 return -EINVAL; 329 } 330 331// LOGV("Visualizer_command command %d cmdSize %d",cmdCode, cmdSize); 332 333 switch (cmdCode) { 334 case EFFECT_CMD_INIT: 335 if (pReplyData == NULL || *replySize != sizeof(int)) { 336 return -EINVAL; 337 } 338 *(int *) pReplyData = Visualizer_init(pContext); 339 break; 340 case EFFECT_CMD_CONFIGURE: 341 if (pCmdData == NULL || cmdSize != sizeof(effect_config_t) 342 || pReplyData == NULL || *replySize != sizeof(int)) { 343 return -EINVAL; 344 } 345 *(int *) pReplyData = Visualizer_configure(pContext, 346 (effect_config_t *) pCmdData); 347 break; 348 case EFFECT_CMD_RESET: 349 Visualizer_reset(pContext); 350 break; 351 case EFFECT_CMD_ENABLE: 352 if (pReplyData == NULL || *replySize != sizeof(int)) { 353 return -EINVAL; 354 } 355 if (pContext->mState != VISUALIZER_STATE_INITIALIZED) { 356 return -ENOSYS; 357 } 358 pContext->mState = VISUALIZER_STATE_ACTIVE; 359 LOGV("EFFECT_CMD_ENABLE() OK"); 360 *(int *)pReplyData = 0; 361 break; 362 case EFFECT_CMD_DISABLE: 363 if (pReplyData == NULL || *replySize != sizeof(int)) { 364 return -EINVAL; 365 } 366 if (pContext->mState != VISUALIZER_STATE_ACTIVE) { 367 return -ENOSYS; 368 } 369 pContext->mState = VISUALIZER_STATE_INITIALIZED; 370 LOGV("EFFECT_CMD_DISABLE() OK"); 371 *(int *)pReplyData = 0; 372 break; 373 case EFFECT_CMD_GET_PARAM: { 374 if (pCmdData == NULL || 375 cmdSize != (int)(sizeof(effect_param_t) + sizeof(uint32_t)) || 376 pReplyData == NULL || 377 *replySize < (int)(sizeof(effect_param_t) + sizeof(uint32_t) + sizeof(uint32_t))) { 378 return -EINVAL; 379 } 380 memcpy(pReplyData, pCmdData, sizeof(effect_param_t) + sizeof(uint32_t)); 381 effect_param_t *p = (effect_param_t *)pReplyData; 382 p->status = 0; 383 *replySize = sizeof(effect_param_t) + sizeof(uint32_t); 384 if (p->psize != sizeof(uint32_t) || 385 *(uint32_t *)p->data != VISUALIZER_PARAM_CAPTURE_SIZE) { 386 p->status = -EINVAL; 387 break; 388 } 389 LOGV("get mCaptureSize = %d", pContext->mCaptureSize); 390 *((uint32_t *)p->data + 1) = pContext->mCaptureSize; 391 p->vsize = sizeof(uint32_t); 392 *replySize += sizeof(uint32_t); 393 } break; 394 case EFFECT_CMD_SET_PARAM: { 395 if (pCmdData == NULL || 396 cmdSize != (int)(sizeof(effect_param_t) + sizeof(uint32_t) + sizeof(uint32_t)) || 397 pReplyData == NULL || *replySize != sizeof(int32_t)) { 398 return -EINVAL; 399 } 400 *(int32_t *)pReplyData = 0; 401 effect_param_t *p = (effect_param_t *)pCmdData; 402 if (p->psize != sizeof(uint32_t) || 403 p->vsize != sizeof(uint32_t) || 404 *(uint32_t *)p->data != VISUALIZER_PARAM_CAPTURE_SIZE) { 405 *(int32_t *)pReplyData = -EINVAL; 406 break;; 407 } 408 pContext->mCaptureSize = *((uint32_t *)p->data + 1); 409 LOGV("set mCaptureSize = %d", pContext->mCaptureSize); 410 } break; 411 case EFFECT_CMD_SET_DEVICE: 412 case EFFECT_CMD_SET_VOLUME: 413 case EFFECT_CMD_SET_AUDIO_MODE: 414 break; 415 416 417 case VISUALIZER_CMD_CAPTURE: 418 if (pReplyData == NULL || *replySize != pContext->mCaptureSize) { 419 LOGV("VISUALIZER_CMD_CAPTURE() error *replySize %d pContext->mCaptureSize %d", 420 *replySize, pContext->mCaptureSize); 421 return -EINVAL; 422 } 423 if (pContext->mState == VISUALIZER_STATE_ACTIVE) { 424 memcpy(pReplyData, 425 pContext->mCaptureBuf[pContext->mCurrentBuf ^ 1], 426 pContext->mCaptureSize); 427 // if audio framework has stopped playing audio although the effect is still 428 // active we must clear the capture buffer to return silence 429 if (pContext->mLastBuf == pContext->mCurrentBuf) { 430 if (pContext->mStallCount < MAX_STALL_COUNT) { 431 if (++pContext->mStallCount == MAX_STALL_COUNT) { 432 memset(pContext->mCaptureBuf[pContext->mCurrentBuf ^ 1], 433 0x80, 434 pContext->mCaptureSize); 435 } 436 } 437 } else { 438 pContext->mStallCount = 0; 439 } 440 pContext->mLastBuf = pContext->mCurrentBuf; 441 } else { 442 memset(pReplyData, 0x80, pContext->mCaptureSize); 443 } 444 445 break; 446 447 default: 448 LOGW("Visualizer_command invalid command %d",cmdCode); 449 return -EINVAL; 450 } 451 452 return 0; 453} 454 455/* Effect Control Interface Implementation: get_descriptor */ 456int Visualizer_getDescriptor(effect_handle_t self, 457 effect_descriptor_t *pDescriptor) 458{ 459 VisualizerContext * pContext = (VisualizerContext *) self; 460 461 if (pContext == NULL || pDescriptor == NULL) { 462 LOGV("Visualizer_getDescriptor() invalid param"); 463 return -EINVAL; 464 } 465 466 memcpy(pDescriptor, &gVisualizerDescriptor, sizeof(effect_descriptor_t)); 467 468 return 0; 469} /* end Visualizer_getDescriptor */ 470 471// effect_handle_t interface implementation for visualizer effect 472const struct effect_interface_s gVisualizerInterface = { 473 Visualizer_process, 474 Visualizer_command, 475 Visualizer_getDescriptor, 476 NULL, 477}; 478 479 480audio_effect_library_t AUDIO_EFFECT_LIBRARY_INFO_SYM = { 481 tag : AUDIO_EFFECT_LIBRARY_TAG, 482 version : EFFECT_LIBRARY_API_VERSION, 483 name : "Visualizer Library", 484 implementor : "The Android Open Source Project", 485 query_num_effects : VisualizerLib_QueryNumberEffects, 486 query_effect : VisualizerLib_QueryEffect, 487 create_effect : VisualizerLib_Create, 488 release_effect : VisualizerLib_Release, 489 get_descriptor : VisualizerLib_GetDescriptor, 490}; 491 492}; // extern "C" 493