blob: aeebd4db91937651d5d0ec77c4ce9458a4c03e29 [file] [log] [blame]
Eric Laurentda7581b2010-07-02 08:12:41 -07001/*
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>
Eric Laurent6d8b6942011-06-24 07:01:31 -070024#include <audio_effects/effect_visualizer.h>
Eric Laurentda7581b2010-07-02 08:12:41 -070025
Eric Laurentda7581b2010-07-02 08:12:41 -070026
Eric Laurente1315cf2011-05-17 19:16:02 -070027extern "C" {
28
29// effect_handle_t interface implementation for visualizer effect
30extern const struct effect_interface_s gVisualizerInterface;
Eric Laurentda7581b2010-07-02 08:12:41 -070031
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
Eric Laurente1315cf2011-05-17 19:16:02 -070036 EFFECT_CONTROL_API_VERSION,
Eric Laurentda7581b2010-07-02 08:12:41 -070037 (EFFECT_FLAG_TYPE_INSERT | EFFECT_FLAG_INSERT_FIRST),
38 0, // TODO
39 1,
40 "Visualizer",
Eric Laurente1315cf2011-05-17 19:16:02 -070041 "The Android Open Source Project",
Eric Laurentda7581b2010-07-02 08:12:41 -070042};
43
44enum visualizer_state_e {
45 VISUALIZER_STATE_UNINITIALIZED,
46 VISUALIZER_STATE_INITIALIZED,
47 VISUALIZER_STATE_ACTIVE,
48};
49
50struct VisualizerContext {
51 const struct effect_interface_s *mItfe;
52 effect_config_t mConfig;
53 uint32_t mState;
54 uint32_t mCaptureIdx;
55 uint32_t mCaptureSize;
56 uint32_t mCurrentBuf;
57 uint8_t mCaptureBuf[2][VISUALIZER_CAPTURE_SIZE_MAX];
58};
59
60
61//
62//--- Local functions
63//
64
65void Visualizer_reset(VisualizerContext *pContext)
66{
67 pContext->mCaptureIdx = 0;
68 pContext->mCurrentBuf = 0;
Eric Laurent0fa449c2010-09-24 11:52:04 -070069 memset(pContext->mCaptureBuf[0], 0x80, VISUALIZER_CAPTURE_SIZE_MAX);
70 memset(pContext->mCaptureBuf[1], 0x80, VISUALIZER_CAPTURE_SIZE_MAX);
Eric Laurentda7581b2010-07-02 08:12:41 -070071}
72
73//----------------------------------------------------------------------------
74// Visualizer_configure()
75//----------------------------------------------------------------------------
76// Purpose: Set input and output audio configuration.
77//
78// Inputs:
79// pContext: effect engine context
80// pConfig: pointer to effect_config_t structure holding input and output
81// configuration parameters
82//
83// Outputs:
84//
85//----------------------------------------------------------------------------
86
87int Visualizer_configure(VisualizerContext *pContext, effect_config_t *pConfig)
88{
89 LOGV("Visualizer_configure start");
90
91 if (pConfig->inputCfg.samplingRate != pConfig->outputCfg.samplingRate) return -EINVAL;
92 if (pConfig->inputCfg.channels != pConfig->outputCfg.channels) return -EINVAL;
93 if (pConfig->inputCfg.format != pConfig->outputCfg.format) return -EINVAL;
Eric Laurente1315cf2011-05-17 19:16:02 -070094 if (pConfig->inputCfg.channels != AUDIO_CHANNEL_OUT_STEREO) return -EINVAL;
Eric Laurentda7581b2010-07-02 08:12:41 -070095 if (pConfig->outputCfg.accessMode != EFFECT_BUFFER_ACCESS_WRITE &&
96 pConfig->outputCfg.accessMode != EFFECT_BUFFER_ACCESS_ACCUMULATE) return -EINVAL;
Eric Laurente1315cf2011-05-17 19:16:02 -070097 if (pConfig->inputCfg.format != AUDIO_FORMAT_PCM_16_BIT) return -EINVAL;
Eric Laurentda7581b2010-07-02 08:12:41 -070098
99 memcpy(&pContext->mConfig, pConfig, sizeof(effect_config_t));
100
101 Visualizer_reset(pContext);
102
103 return 0;
104}
105
106
107//----------------------------------------------------------------------------
108// Visualizer_init()
109//----------------------------------------------------------------------------
110// Purpose: Initialize engine with default configuration.
111//
112// Inputs:
113// pContext: effect engine context
114//
115// Outputs:
116//
117//----------------------------------------------------------------------------
118
119int Visualizer_init(VisualizerContext *pContext)
120{
121 pContext->mConfig.inputCfg.accessMode = EFFECT_BUFFER_ACCESS_READ;
Eric Laurente1315cf2011-05-17 19:16:02 -0700122 pContext->mConfig.inputCfg.channels = AUDIO_CHANNEL_OUT_STEREO;
123 pContext->mConfig.inputCfg.format = AUDIO_FORMAT_PCM_16_BIT;
Eric Laurentda7581b2010-07-02 08:12:41 -0700124 pContext->mConfig.inputCfg.samplingRate = 44100;
125 pContext->mConfig.inputCfg.bufferProvider.getBuffer = NULL;
126 pContext->mConfig.inputCfg.bufferProvider.releaseBuffer = NULL;
127 pContext->mConfig.inputCfg.bufferProvider.cookie = NULL;
128 pContext->mConfig.inputCfg.mask = EFFECT_CONFIG_ALL;
129 pContext->mConfig.outputCfg.accessMode = EFFECT_BUFFER_ACCESS_ACCUMULATE;
Eric Laurente1315cf2011-05-17 19:16:02 -0700130 pContext->mConfig.outputCfg.channels = AUDIO_CHANNEL_OUT_STEREO;
131 pContext->mConfig.outputCfg.format = AUDIO_FORMAT_PCM_16_BIT;
Eric Laurentda7581b2010-07-02 08:12:41 -0700132 pContext->mConfig.outputCfg.samplingRate = 44100;
133 pContext->mConfig.outputCfg.bufferProvider.getBuffer = NULL;
134 pContext->mConfig.outputCfg.bufferProvider.releaseBuffer = NULL;
135 pContext->mConfig.outputCfg.bufferProvider.cookie = NULL;
136 pContext->mConfig.outputCfg.mask = EFFECT_CONFIG_ALL;
137
138 pContext->mCaptureSize = VISUALIZER_CAPTURE_SIZE_MAX;
139
140 Visualizer_configure(pContext, &pContext->mConfig);
141
142 return 0;
143}
144
145//
146//--- Effect Library Interface Implementation
147//
148
Eric Laurente1315cf2011-05-17 19:16:02 -0700149int VisualizerLib_QueryNumberEffects(uint32_t *pNumEffects) {
Eric Laurentda7581b2010-07-02 08:12:41 -0700150 *pNumEffects = 1;
151 return 0;
152}
153
Eric Laurente1315cf2011-05-17 19:16:02 -0700154int VisualizerLib_QueryEffect(uint32_t index,
155 effect_descriptor_t *pDescriptor) {
Eric Laurentda7581b2010-07-02 08:12:41 -0700156 if (pDescriptor == NULL) {
157 return -EINVAL;
158 }
159 if (index > 0) {
160 return -EINVAL;
161 }
162 memcpy(pDescriptor, &gVisualizerDescriptor, sizeof(effect_descriptor_t));
163 return 0;
164}
165
Eric Laurente1315cf2011-05-17 19:16:02 -0700166int VisualizerLib_Create(effect_uuid_t *uuid,
167 int32_t sessionId,
168 int32_t ioId,
169 effect_handle_t *pHandle) {
Eric Laurentda7581b2010-07-02 08:12:41 -0700170 int ret;
171 int i;
172
Eric Laurente1315cf2011-05-17 19:16:02 -0700173 if (pHandle == NULL || uuid == NULL) {
Eric Laurentda7581b2010-07-02 08:12:41 -0700174 return -EINVAL;
175 }
176
177 if (memcmp(uuid, &gVisualizerDescriptor.uuid, sizeof(effect_uuid_t)) != 0) {
178 return -EINVAL;
179 }
180
181 VisualizerContext *pContext = new VisualizerContext;
182
183 pContext->mItfe = &gVisualizerInterface;
184 pContext->mState = VISUALIZER_STATE_UNINITIALIZED;
185
186 ret = Visualizer_init(pContext);
187 if (ret < 0) {
Eric Laurente1315cf2011-05-17 19:16:02 -0700188 LOGW("VisualizerLib_Create() init failed");
Eric Laurentda7581b2010-07-02 08:12:41 -0700189 delete pContext;
190 return ret;
191 }
192
Eric Laurente1315cf2011-05-17 19:16:02 -0700193 *pHandle = (effect_handle_t)pContext;
Eric Laurentda7581b2010-07-02 08:12:41 -0700194
195 pContext->mState = VISUALIZER_STATE_INITIALIZED;
196
Eric Laurente1315cf2011-05-17 19:16:02 -0700197 LOGV("VisualizerLib_Create %p", pContext);
Eric Laurentda7581b2010-07-02 08:12:41 -0700198
199 return 0;
200
201}
202
Eric Laurente1315cf2011-05-17 19:16:02 -0700203int VisualizerLib_Release(effect_handle_t handle) {
204 VisualizerContext * pContext = (VisualizerContext *)handle;
Eric Laurentda7581b2010-07-02 08:12:41 -0700205
Eric Laurente1315cf2011-05-17 19:16:02 -0700206 LOGV("VisualizerLib_Release %p", handle);
Eric Laurentda7581b2010-07-02 08:12:41 -0700207 if (pContext == NULL) {
208 return -EINVAL;
209 }
210 pContext->mState = VISUALIZER_STATE_UNINITIALIZED;
211 delete pContext;
212
213 return 0;
214}
215
Eric Laurente1315cf2011-05-17 19:16:02 -0700216int VisualizerLib_GetDescriptor(effect_uuid_t *uuid,
217 effect_descriptor_t *pDescriptor) {
218
219 if (pDescriptor == NULL || uuid == NULL){
220 LOGV("VisualizerLib_GetDescriptor() called with NULL pointer");
221 return -EINVAL;
222 }
223
224 if (memcmp(uuid, &gVisualizerDescriptor.uuid, sizeof(effect_uuid_t)) == 0) {
225 memcpy(pDescriptor, &gVisualizerDescriptor, sizeof(effect_descriptor_t));
226 return 0;
227 }
228
229 return -EINVAL;
230} /* end VisualizerLib_GetDescriptor */
231
Eric Laurentda7581b2010-07-02 08:12:41 -0700232//
233//--- Effect Control Interface Implementation
234//
235
236static inline int16_t clamp16(int32_t sample)
237{
238 if ((sample>>15) ^ (sample>>31))
239 sample = 0x7FFF ^ (sample>>31);
240 return sample;
241}
242
Eric Laurente1315cf2011-05-17 19:16:02 -0700243int Visualizer_process(
244 effect_handle_t self,audio_buffer_t *inBuffer, audio_buffer_t *outBuffer)
Eric Laurentda7581b2010-07-02 08:12:41 -0700245{
Eric Laurente1315cf2011-05-17 19:16:02 -0700246 VisualizerContext * pContext = (VisualizerContext *)self;
Eric Laurentda7581b2010-07-02 08:12:41 -0700247
248 if (pContext == NULL) {
249 return -EINVAL;
250 }
Eric Laurentda7581b2010-07-02 08:12:41 -0700251
252 if (inBuffer == NULL || inBuffer->raw == NULL ||
253 outBuffer == NULL || outBuffer->raw == NULL ||
254 inBuffer->frameCount != outBuffer->frameCount ||
255 inBuffer->frameCount == 0) {
256 return -EINVAL;
257 }
258
259 // all code below assumes stereo 16 bit PCM output and input
Eric Laurent0e75f0f2010-09-21 14:52:01 -0700260
261 // derive capture scaling factor from peak value in current buffer
262 // this gives more interesting captures for display.
263 int32_t shift = 32;
Marco Nelissen64c3bde2010-10-27 09:06:01 -0700264 int len = inBuffer->frameCount * 2;
Eric Laurente1315cf2011-05-17 19:16:02 -0700265 for (int i = 0; i < len; i++) {
Eric Laurent0e75f0f2010-09-21 14:52:01 -0700266 int32_t smp = inBuffer->s16[i];
Marco Nelissen64c3bde2010-10-27 09:06:01 -0700267 if (smp < 0) smp = -smp - 1; // take care to keep the max negative in range
Eric Laurent0e75f0f2010-09-21 14:52:01 -0700268 int32_t clz = __builtin_clz(smp);
269 if (shift > clz) shift = clz;
270 }
Marco Nelissen64c3bde2010-10-27 09:06:01 -0700271 // A maximum amplitude signal will have 17 leading zeros, which we want to
272 // translate to a shift of 8 (for converting 16 bit to 8 bit)
273 shift = 25 - shift;
274 // Never scale by less than 8 to avoid returning unaltered PCM signal.
275 if (shift < 3) {
276 shift = 3;
Eric Laurent0e75f0f2010-09-21 14:52:01 -0700277 }
Marco Nelissen64c3bde2010-10-27 09:06:01 -0700278 // add one to combine the division by 2 needed after summing left and right channels below
279 shift++;
Eric Laurent0e75f0f2010-09-21 14:52:01 -0700280
Eric Laurentda7581b2010-07-02 08:12:41 -0700281 uint32_t captIdx;
282 uint32_t inIdx;
283 uint8_t *buf = pContext->mCaptureBuf[pContext->mCurrentBuf];
284 for (inIdx = 0, captIdx = pContext->mCaptureIdx;
285 inIdx < inBuffer->frameCount && captIdx < pContext->mCaptureSize;
286 inIdx++, captIdx++) {
287 int32_t smp = inBuffer->s16[2 * inIdx] + inBuffer->s16[2 * inIdx + 1];
Marco Nelissen64c3bde2010-10-27 09:06:01 -0700288 smp = smp >> shift;
Eric Laurentda7581b2010-07-02 08:12:41 -0700289 buf[captIdx] = ((uint8_t)smp)^0x80;
290 }
291 pContext->mCaptureIdx = captIdx;
292
293 // go to next buffer when buffer full
294 if (pContext->mCaptureIdx == pContext->mCaptureSize) {
295 pContext->mCurrentBuf ^= 1;
296 pContext->mCaptureIdx = 0;
297 }
298
299 if (inBuffer->raw != outBuffer->raw) {
300 if (pContext->mConfig.outputCfg.accessMode == EFFECT_BUFFER_ACCESS_ACCUMULATE) {
301 for (size_t i = 0; i < outBuffer->frameCount*2; i++) {
302 outBuffer->s16[i] = clamp16(outBuffer->s16[i] + inBuffer->s16[i]);
303 }
304 } else {
305 memcpy(outBuffer->raw, inBuffer->raw, outBuffer->frameCount * 2 * sizeof(int16_t));
306 }
307 }
Eric Laurentf997cab2010-07-19 06:24:46 -0700308 if (pContext->mState != VISUALIZER_STATE_ACTIVE) {
309 return -ENODATA;
310 }
Eric Laurentda7581b2010-07-02 08:12:41 -0700311 return 0;
312} // end Visualizer_process
313
Eric Laurente1315cf2011-05-17 19:16:02 -0700314int Visualizer_command(effect_handle_t self, uint32_t cmdCode, uint32_t cmdSize,
Eric Laurent25f43952010-07-28 05:40:18 -0700315 void *pCmdData, uint32_t *replySize, void *pReplyData) {
Eric Laurentda7581b2010-07-02 08:12:41 -0700316
Eric Laurente1315cf2011-05-17 19:16:02 -0700317 VisualizerContext * pContext = (VisualizerContext *)self;
Eric Laurentda7581b2010-07-02 08:12:41 -0700318 int retsize;
319
320 if (pContext == NULL || pContext->mState == VISUALIZER_STATE_UNINITIALIZED) {
321 return -EINVAL;
322 }
323
324// LOGV("Visualizer_command command %d cmdSize %d",cmdCode, cmdSize);
325
326 switch (cmdCode) {
327 case EFFECT_CMD_INIT:
328 if (pReplyData == NULL || *replySize != sizeof(int)) {
329 return -EINVAL;
330 }
331 *(int *) pReplyData = Visualizer_init(pContext);
332 break;
333 case EFFECT_CMD_CONFIGURE:
334 if (pCmdData == NULL || cmdSize != sizeof(effect_config_t)
335 || pReplyData == NULL || *replySize != sizeof(int)) {
336 return -EINVAL;
337 }
338 *(int *) pReplyData = Visualizer_configure(pContext,
339 (effect_config_t *) pCmdData);
340 break;
341 case EFFECT_CMD_RESET:
342 Visualizer_reset(pContext);
343 break;
344 case EFFECT_CMD_ENABLE:
345 if (pReplyData == NULL || *replySize != sizeof(int)) {
346 return -EINVAL;
347 }
348 if (pContext->mState != VISUALIZER_STATE_INITIALIZED) {
349 return -ENOSYS;
350 }
351 pContext->mState = VISUALIZER_STATE_ACTIVE;
352 LOGV("EFFECT_CMD_ENABLE() OK");
353 *(int *)pReplyData = 0;
354 break;
355 case EFFECT_CMD_DISABLE:
356 if (pReplyData == NULL || *replySize != sizeof(int)) {
357 return -EINVAL;
358 }
359 if (pContext->mState != VISUALIZER_STATE_ACTIVE) {
360 return -ENOSYS;
361 }
362 pContext->mState = VISUALIZER_STATE_INITIALIZED;
363 LOGV("EFFECT_CMD_DISABLE() OK");
364 *(int *)pReplyData = 0;
365 break;
366 case EFFECT_CMD_GET_PARAM: {
367 if (pCmdData == NULL ||
368 cmdSize != (int)(sizeof(effect_param_t) + sizeof(uint32_t)) ||
369 pReplyData == NULL ||
370 *replySize < (int)(sizeof(effect_param_t) + sizeof(uint32_t) + sizeof(uint32_t))) {
371 return -EINVAL;
372 }
373 memcpy(pReplyData, pCmdData, sizeof(effect_param_t) + sizeof(uint32_t));
374 effect_param_t *p = (effect_param_t *)pReplyData;
375 p->status = 0;
376 *replySize = sizeof(effect_param_t) + sizeof(uint32_t);
377 if (p->psize != sizeof(uint32_t) ||
Eric Laurent6d8b6942011-06-24 07:01:31 -0700378 *(uint32_t *)p->data != VISUALIZER_PARAM_CAPTURE_SIZE) {
Eric Laurentda7581b2010-07-02 08:12:41 -0700379 p->status = -EINVAL;
380 break;
381 }
382 LOGV("get mCaptureSize = %d", pContext->mCaptureSize);
383 *((uint32_t *)p->data + 1) = pContext->mCaptureSize;
384 p->vsize = sizeof(uint32_t);
385 *replySize += sizeof(uint32_t);
386 } break;
387 case EFFECT_CMD_SET_PARAM: {
388 if (pCmdData == NULL ||
389 cmdSize != (int)(sizeof(effect_param_t) + sizeof(uint32_t) + sizeof(uint32_t)) ||
390 pReplyData == NULL || *replySize != sizeof(int32_t)) {
391 return -EINVAL;
392 }
393 *(int32_t *)pReplyData = 0;
394 effect_param_t *p = (effect_param_t *)pCmdData;
395 if (p->psize != sizeof(uint32_t) ||
396 p->vsize != sizeof(uint32_t) ||
Eric Laurent6d8b6942011-06-24 07:01:31 -0700397 *(uint32_t *)p->data != VISUALIZER_PARAM_CAPTURE_SIZE) {
Eric Laurentda7581b2010-07-02 08:12:41 -0700398 *(int32_t *)pReplyData = -EINVAL;
399 break;;
400 }
401 pContext->mCaptureSize = *((uint32_t *)p->data + 1);
402 LOGV("set mCaptureSize = %d", pContext->mCaptureSize);
403 } break;
404 case EFFECT_CMD_SET_DEVICE:
405 case EFFECT_CMD_SET_VOLUME:
406 case EFFECT_CMD_SET_AUDIO_MODE:
407 break;
408
409
Eric Laurent6d8b6942011-06-24 07:01:31 -0700410 case VISUALIZER_CMD_CAPTURE:
Eric Laurent0e75f0f2010-09-21 14:52:01 -0700411 if (pReplyData == NULL || *replySize != pContext->mCaptureSize) {
Eric Laurent6d8b6942011-06-24 07:01:31 -0700412 LOGV("VISUALIZER_CMD_CAPTURE() error *replySize %d pContext->mCaptureSize %d",
Eric Laurentda7581b2010-07-02 08:12:41 -0700413 *replySize, pContext->mCaptureSize);
414 return -EINVAL;
415 }
416 if (pContext->mState == VISUALIZER_STATE_ACTIVE) {
417 memcpy(pReplyData,
418 pContext->mCaptureBuf[pContext->mCurrentBuf ^ 1],
419 pContext->mCaptureSize);
420 } else {
421 memset(pReplyData, 0x80, pContext->mCaptureSize);
422 }
423 break;
424
425 default:
426 LOGW("Visualizer_command invalid command %d",cmdCode);
427 return -EINVAL;
428 }
429
430 return 0;
431}
432
Eric Laurente1315cf2011-05-17 19:16:02 -0700433/* Effect Control Interface Implementation: get_descriptor */
434int Visualizer_getDescriptor(effect_handle_t self,
435 effect_descriptor_t *pDescriptor)
436{
437 VisualizerContext * pContext = (VisualizerContext *) self;
438
439 if (pContext == NULL || pDescriptor == NULL) {
440 LOGV("Visualizer_getDescriptor() invalid param");
441 return -EINVAL;
442 }
443
444 memcpy(pDescriptor, &gVisualizerDescriptor, sizeof(effect_descriptor_t));
445
446 return 0;
447} /* end Visualizer_getDescriptor */
448
449// effect_handle_t interface implementation for visualizer effect
Eric Laurentda7581b2010-07-02 08:12:41 -0700450const struct effect_interface_s gVisualizerInterface = {
451 Visualizer_process,
Eric Laurente1315cf2011-05-17 19:16:02 -0700452 Visualizer_command,
453 Visualizer_getDescriptor
Eric Laurentda7581b2010-07-02 08:12:41 -0700454};
455
Eric Laurentda7581b2010-07-02 08:12:41 -0700456
Eric Laurente1315cf2011-05-17 19:16:02 -0700457audio_effect_library_t AUDIO_EFFECT_LIBRARY_INFO_SYM = {
458 tag : AUDIO_EFFECT_LIBRARY_TAG,
459 version : EFFECT_LIBRARY_API_VERSION,
460 name : "Visualizer Library",
461 implementor : "The Android Open Source Project",
462 query_num_effects : VisualizerLib_QueryNumberEffects,
463 query_effect : VisualizerLib_QueryEffect,
464 create_effect : VisualizerLib_Create,
465 release_effect : VisualizerLib_Release,
466 get_descriptor : VisualizerLib_GetDescriptor,
467};
468
469}; // extern "C"