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 <media/EffectVisualizerApi.h> 25 26namespace android { 27 28// effect_interface_t interface implementation for visualizer effect 29extern "C" const struct effect_interface_s gVisualizerInterface; 30 31// Google Visualizer UUID: d069d9e0-8329-11df-9168-0002a5d5c51b 32const effect_descriptor_t gVisualizerDescriptor = { 33 {0xe46b26a0, 0xdddd, 0x11db, 0x8afd, {0x00, 0x02, 0xa5, 0xd5, 0xc5, 0x1b}}, // type 34 {0xd069d9e0, 0x8329, 0x11df, 0x9168, {0x00, 0x02, 0xa5, 0xd5, 0xc5, 0x1b}}, // uuid 35 EFFECT_API_VERSION, 36 (EFFECT_FLAG_TYPE_INSERT | EFFECT_FLAG_INSERT_FIRST), 37 0, // TODO 38 1, 39 "Visualizer", 40 "Google Inc.", 41}; 42 43enum visualizer_state_e { 44 VISUALIZER_STATE_UNINITIALIZED, 45 VISUALIZER_STATE_INITIALIZED, 46 VISUALIZER_STATE_ACTIVE, 47}; 48 49struct VisualizerContext { 50 const struct effect_interface_s *mItfe; 51 effect_config_t mConfig; 52 uint32_t mState; 53 uint32_t mCaptureIdx; 54 uint32_t mCaptureSize; 55 uint32_t mCurrentBuf; 56 uint8_t mCaptureBuf[2][VISUALIZER_CAPTURE_SIZE_MAX]; 57}; 58 59 60// 61//--- Local functions 62// 63 64void Visualizer_reset(VisualizerContext *pContext) 65{ 66 pContext->mCaptureIdx = 0; 67 pContext->mCurrentBuf = 0; 68 memset(pContext->mCaptureBuf[0], 0x80, VISUALIZER_CAPTURE_SIZE_MAX); 69 memset(pContext->mCaptureBuf[1], 0x80, VISUALIZER_CAPTURE_SIZE_MAX); 70} 71 72//---------------------------------------------------------------------------- 73// Visualizer_configure() 74//---------------------------------------------------------------------------- 75// Purpose: Set input and output audio configuration. 76// 77// Inputs: 78// pContext: effect engine context 79// pConfig: pointer to effect_config_t structure holding input and output 80// configuration parameters 81// 82// Outputs: 83// 84//---------------------------------------------------------------------------- 85 86int Visualizer_configure(VisualizerContext *pContext, effect_config_t *pConfig) 87{ 88 LOGV("Visualizer_configure start"); 89 90 if (pConfig->inputCfg.samplingRate != pConfig->outputCfg.samplingRate) return -EINVAL; 91 if (pConfig->inputCfg.channels != pConfig->outputCfg.channels) return -EINVAL; 92 if (pConfig->inputCfg.format != pConfig->outputCfg.format) return -EINVAL; 93 if (pConfig->inputCfg.channels != CHANNEL_STEREO) return -EINVAL; 94 if (pConfig->outputCfg.accessMode != EFFECT_BUFFER_ACCESS_WRITE && 95 pConfig->outputCfg.accessMode != EFFECT_BUFFER_ACCESS_ACCUMULATE) return -EINVAL; 96 if (pConfig->inputCfg.format != SAMPLE_FORMAT_PCM_S15) return -EINVAL; 97 98 memcpy(&pContext->mConfig, pConfig, sizeof(effect_config_t)); 99 100 Visualizer_reset(pContext); 101 102 return 0; 103} 104 105 106//---------------------------------------------------------------------------- 107// Visualizer_init() 108//---------------------------------------------------------------------------- 109// Purpose: Initialize engine with default configuration. 110// 111// Inputs: 112// pContext: effect engine context 113// 114// Outputs: 115// 116//---------------------------------------------------------------------------- 117 118int Visualizer_init(VisualizerContext *pContext) 119{ 120 pContext->mConfig.inputCfg.accessMode = EFFECT_BUFFER_ACCESS_READ; 121 pContext->mConfig.inputCfg.channels = CHANNEL_STEREO; 122 pContext->mConfig.inputCfg.format = SAMPLE_FORMAT_PCM_S15; 123 pContext->mConfig.inputCfg.samplingRate = 44100; 124 pContext->mConfig.inputCfg.bufferProvider.getBuffer = NULL; 125 pContext->mConfig.inputCfg.bufferProvider.releaseBuffer = NULL; 126 pContext->mConfig.inputCfg.bufferProvider.cookie = NULL; 127 pContext->mConfig.inputCfg.mask = EFFECT_CONFIG_ALL; 128 pContext->mConfig.outputCfg.accessMode = EFFECT_BUFFER_ACCESS_ACCUMULATE; 129 pContext->mConfig.outputCfg.channels = CHANNEL_STEREO; 130 pContext->mConfig.outputCfg.format = SAMPLE_FORMAT_PCM_S15; 131 pContext->mConfig.outputCfg.samplingRate = 44100; 132 pContext->mConfig.outputCfg.bufferProvider.getBuffer = NULL; 133 pContext->mConfig.outputCfg.bufferProvider.releaseBuffer = NULL; 134 pContext->mConfig.outputCfg.bufferProvider.cookie = NULL; 135 pContext->mConfig.outputCfg.mask = EFFECT_CONFIG_ALL; 136 137 pContext->mCaptureSize = VISUALIZER_CAPTURE_SIZE_MAX; 138 139 Visualizer_configure(pContext, &pContext->mConfig); 140 141 return 0; 142} 143 144// 145//--- Effect Library Interface Implementation 146// 147 148extern "C" int EffectQueryNumberEffects(uint32_t *pNumEffects) { 149 *pNumEffects = 1; 150 return 0; 151} 152 153extern "C" int EffectQueryEffect(uint32_t index, effect_descriptor_t *pDescriptor) { 154 if (pDescriptor == NULL) { 155 return -EINVAL; 156 } 157 if (index > 0) { 158 return -EINVAL; 159 } 160 memcpy(pDescriptor, &gVisualizerDescriptor, sizeof(effect_descriptor_t)); 161 return 0; 162} 163 164extern "C" int EffectCreate(effect_uuid_t *uuid, 165 int32_t sessionId, 166 int32_t ioId, 167 effect_interface_t *pInterface) { 168 int ret; 169 int i; 170 171 if (pInterface == NULL || uuid == NULL) { 172 return -EINVAL; 173 } 174 175 if (memcmp(uuid, &gVisualizerDescriptor.uuid, sizeof(effect_uuid_t)) != 0) { 176 return -EINVAL; 177 } 178 179 VisualizerContext *pContext = new VisualizerContext; 180 181 pContext->mItfe = &gVisualizerInterface; 182 pContext->mState = VISUALIZER_STATE_UNINITIALIZED; 183 184 ret = Visualizer_init(pContext); 185 if (ret < 0) { 186 LOGW("EffectCreate() init failed"); 187 delete pContext; 188 return ret; 189 } 190 191 *pInterface = (effect_interface_t)pContext; 192 193 pContext->mState = VISUALIZER_STATE_INITIALIZED; 194 195 LOGV("EffectCreate %p", pContext); 196 197 return 0; 198 199} 200 201extern "C" int EffectRelease(effect_interface_t interface) { 202 VisualizerContext * pContext = (VisualizerContext *)interface; 203 204 LOGV("EffectRelease %p", interface); 205 if (pContext == NULL) { 206 return -EINVAL; 207 } 208 pContext->mState = VISUALIZER_STATE_UNINITIALIZED; 209 delete pContext; 210 211 return 0; 212} 213 214// 215//--- Effect Control Interface Implementation 216// 217 218static inline int16_t clamp16(int32_t sample) 219{ 220 if ((sample>>15) ^ (sample>>31)) 221 sample = 0x7FFF ^ (sample>>31); 222 return sample; 223} 224 225extern "C" int Visualizer_process( 226 effect_interface_t self,audio_buffer_t *inBuffer, audio_buffer_t *outBuffer) 227{ 228 android::VisualizerContext * pContext = (android::VisualizerContext *)self; 229 230 if (pContext == NULL) { 231 return -EINVAL; 232 } 233 234 if (inBuffer == NULL || inBuffer->raw == NULL || 235 outBuffer == NULL || outBuffer->raw == NULL || 236 inBuffer->frameCount != outBuffer->frameCount || 237 inBuffer->frameCount == 0) { 238 return -EINVAL; 239 } 240 241 // all code below assumes stereo 16 bit PCM output and input 242 243 // derive capture scaling factor from peak value in current buffer 244 // this gives more interesting captures for display. 245 int32_t shift = 32; 246 int len = inBuffer->frameCount * 2; 247 for (size_t i = 0; i < len; i++) { 248 int32_t smp = inBuffer->s16[i]; 249 if (smp < 0) smp = -smp - 1; // take care to keep the max negative in range 250 int32_t clz = __builtin_clz(smp); 251 if (shift > clz) shift = clz; 252 } 253 // A maximum amplitude signal will have 17 leading zeros, which we want to 254 // translate to a shift of 8 (for converting 16 bit to 8 bit) 255 shift = 25 - shift; 256 // Never scale by less than 8 to avoid returning unaltered PCM signal. 257 if (shift < 3) { 258 shift = 3; 259 } 260 // add one to combine the division by 2 needed after summing left and right channels below 261 shift++; 262 263 uint32_t captIdx; 264 uint32_t inIdx; 265 uint8_t *buf = pContext->mCaptureBuf[pContext->mCurrentBuf]; 266 for (inIdx = 0, captIdx = pContext->mCaptureIdx; 267 inIdx < inBuffer->frameCount && captIdx < pContext->mCaptureSize; 268 inIdx++, captIdx++) { 269 int32_t smp = inBuffer->s16[2 * inIdx] + inBuffer->s16[2 * inIdx + 1]; 270 smp = smp >> shift; 271 buf[captIdx] = ((uint8_t)smp)^0x80; 272 } 273 pContext->mCaptureIdx = captIdx; 274 275 // go to next buffer when buffer full 276 if (pContext->mCaptureIdx == pContext->mCaptureSize) { 277 pContext->mCurrentBuf ^= 1; 278 pContext->mCaptureIdx = 0; 279 } 280 281 if (inBuffer->raw != outBuffer->raw) { 282 if (pContext->mConfig.outputCfg.accessMode == EFFECT_BUFFER_ACCESS_ACCUMULATE) { 283 for (size_t i = 0; i < outBuffer->frameCount*2; i++) { 284 outBuffer->s16[i] = clamp16(outBuffer->s16[i] + inBuffer->s16[i]); 285 } 286 } else { 287 memcpy(outBuffer->raw, inBuffer->raw, outBuffer->frameCount * 2 * sizeof(int16_t)); 288 } 289 } 290 if (pContext->mState != VISUALIZER_STATE_ACTIVE) { 291 return -ENODATA; 292 } 293 return 0; 294} // end Visualizer_process 295 296extern "C" int Visualizer_command(effect_interface_t self, uint32_t cmdCode, uint32_t cmdSize, 297 void *pCmdData, uint32_t *replySize, void *pReplyData) { 298 299 android::VisualizerContext * pContext = (android::VisualizerContext *)self; 300 int retsize; 301 302 if (pContext == NULL || pContext->mState == VISUALIZER_STATE_UNINITIALIZED) { 303 return -EINVAL; 304 } 305 306// LOGV("Visualizer_command command %d cmdSize %d",cmdCode, cmdSize); 307 308 switch (cmdCode) { 309 case EFFECT_CMD_INIT: 310 if (pReplyData == NULL || *replySize != sizeof(int)) { 311 return -EINVAL; 312 } 313 *(int *) pReplyData = Visualizer_init(pContext); 314 break; 315 case EFFECT_CMD_CONFIGURE: 316 if (pCmdData == NULL || cmdSize != sizeof(effect_config_t) 317 || pReplyData == NULL || *replySize != sizeof(int)) { 318 return -EINVAL; 319 } 320 *(int *) pReplyData = Visualizer_configure(pContext, 321 (effect_config_t *) pCmdData); 322 break; 323 case EFFECT_CMD_RESET: 324 Visualizer_reset(pContext); 325 break; 326 case EFFECT_CMD_ENABLE: 327 if (pReplyData == NULL || *replySize != sizeof(int)) { 328 return -EINVAL; 329 } 330 if (pContext->mState != VISUALIZER_STATE_INITIALIZED) { 331 return -ENOSYS; 332 } 333 pContext->mState = VISUALIZER_STATE_ACTIVE; 334 LOGV("EFFECT_CMD_ENABLE() OK"); 335 *(int *)pReplyData = 0; 336 break; 337 case EFFECT_CMD_DISABLE: 338 if (pReplyData == NULL || *replySize != sizeof(int)) { 339 return -EINVAL; 340 } 341 if (pContext->mState != VISUALIZER_STATE_ACTIVE) { 342 return -ENOSYS; 343 } 344 pContext->mState = VISUALIZER_STATE_INITIALIZED; 345 LOGV("EFFECT_CMD_DISABLE() OK"); 346 *(int *)pReplyData = 0; 347 break; 348 case EFFECT_CMD_GET_PARAM: { 349 if (pCmdData == NULL || 350 cmdSize != (int)(sizeof(effect_param_t) + sizeof(uint32_t)) || 351 pReplyData == NULL || 352 *replySize < (int)(sizeof(effect_param_t) + sizeof(uint32_t) + sizeof(uint32_t))) { 353 return -EINVAL; 354 } 355 memcpy(pReplyData, pCmdData, sizeof(effect_param_t) + sizeof(uint32_t)); 356 effect_param_t *p = (effect_param_t *)pReplyData; 357 p->status = 0; 358 *replySize = sizeof(effect_param_t) + sizeof(uint32_t); 359 if (p->psize != sizeof(uint32_t) || 360 *(uint32_t *)p->data != VISU_PARAM_CAPTURE_SIZE) { 361 p->status = -EINVAL; 362 break; 363 } 364 LOGV("get mCaptureSize = %d", pContext->mCaptureSize); 365 *((uint32_t *)p->data + 1) = pContext->mCaptureSize; 366 p->vsize = sizeof(uint32_t); 367 *replySize += sizeof(uint32_t); 368 } break; 369 case EFFECT_CMD_SET_PARAM: { 370 if (pCmdData == NULL || 371 cmdSize != (int)(sizeof(effect_param_t) + sizeof(uint32_t) + sizeof(uint32_t)) || 372 pReplyData == NULL || *replySize != sizeof(int32_t)) { 373 return -EINVAL; 374 } 375 *(int32_t *)pReplyData = 0; 376 effect_param_t *p = (effect_param_t *)pCmdData; 377 if (p->psize != sizeof(uint32_t) || 378 p->vsize != sizeof(uint32_t) || 379 *(uint32_t *)p->data != VISU_PARAM_CAPTURE_SIZE) { 380 *(int32_t *)pReplyData = -EINVAL; 381 break;; 382 } 383 pContext->mCaptureSize = *((uint32_t *)p->data + 1); 384 LOGV("set mCaptureSize = %d", pContext->mCaptureSize); 385 } break; 386 case EFFECT_CMD_SET_DEVICE: 387 case EFFECT_CMD_SET_VOLUME: 388 case EFFECT_CMD_SET_AUDIO_MODE: 389 break; 390 391 392 case VISU_CMD_CAPTURE: 393 if (pReplyData == NULL || *replySize != pContext->mCaptureSize) { 394 LOGV("VISU_CMD_CAPTURE() error *replySize %d pContext->mCaptureSize %d", 395 *replySize, pContext->mCaptureSize); 396 return -EINVAL; 397 } 398 if (pContext->mState == VISUALIZER_STATE_ACTIVE) { 399 memcpy(pReplyData, 400 pContext->mCaptureBuf[pContext->mCurrentBuf ^ 1], 401 pContext->mCaptureSize); 402 } else { 403 memset(pReplyData, 0x80, pContext->mCaptureSize); 404 } 405 break; 406 407 default: 408 LOGW("Visualizer_command invalid command %d",cmdCode); 409 return -EINVAL; 410 } 411 412 return 0; 413} 414 415// effect_interface_t interface implementation for visualizer effect 416const struct effect_interface_s gVisualizerInterface = { 417 Visualizer_process, 418 Visualizer_command 419}; 420 421} // namespace 422 423