blob: 44baf937e772213dfdcb4b29e766d74598dad5a2 [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
Jean-Michel Trivi3476de62012-04-15 17:15:07 -070017#define LOG_TAG "EffectVisualizer"
Eric Laurentda7581b2010-07-02 08:12:41 -070018//#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 Laurent183dc772012-03-23 15:35:48 -070024#include <time.h>
Eric Laurent6d8b6942011-06-24 07:01:31 -070025#include <audio_effects/effect_visualizer.h>
Eric Laurentda7581b2010-07-02 08:12:41 -070026
Eric Laurentda7581b2010-07-02 08:12:41 -070027
Eric Laurente1315cf2011-05-17 19:16:02 -070028extern "C" {
29
30// effect_handle_t interface implementation for visualizer effect
31extern const struct effect_interface_s gVisualizerInterface;
Eric Laurentda7581b2010-07-02 08:12:41 -070032
33// Google Visualizer UUID: d069d9e0-8329-11df-9168-0002a5d5c51b
34const effect_descriptor_t gVisualizerDescriptor = {
35 {0xe46b26a0, 0xdddd, 0x11db, 0x8afd, {0x00, 0x02, 0xa5, 0xd5, 0xc5, 0x1b}}, // type
36 {0xd069d9e0, 0x8329, 0x11df, 0x9168, {0x00, 0x02, 0xa5, 0xd5, 0xc5, 0x1b}}, // uuid
Eric Laurente1315cf2011-05-17 19:16:02 -070037 EFFECT_CONTROL_API_VERSION,
Eric Laurentda7581b2010-07-02 08:12:41 -070038 (EFFECT_FLAG_TYPE_INSERT | EFFECT_FLAG_INSERT_FIRST),
39 0, // TODO
40 1,
41 "Visualizer",
Eric Laurente1315cf2011-05-17 19:16:02 -070042 "The Android Open Source Project",
Eric Laurentda7581b2010-07-02 08:12:41 -070043};
44
45enum visualizer_state_e {
46 VISUALIZER_STATE_UNINITIALIZED,
47 VISUALIZER_STATE_INITIALIZED,
48 VISUALIZER_STATE_ACTIVE,
49};
50
Eric Laurent183dc772012-03-23 15:35:48 -070051// maximum time since last capture buffer update before resetting capture buffer. This means
Eric Laurent3df40a02011-11-10 10:02:18 -080052// that the framework has stopped playing audio and we must start returning silence
Eric Laurent183dc772012-03-23 15:35:48 -070053#define MAX_STALL_TIME_MS 1000
Eric Laurent3df40a02011-11-10 10:02:18 -080054
Marco Nelissenf06c2ed2012-06-06 09:52:31 -070055#define CAPTURE_BUF_SIZE 65536 // "64k should be enough for everyone"
56
Eric Laurentda7581b2010-07-02 08:12:41 -070057struct VisualizerContext {
58 const struct effect_interface_s *mItfe;
59 effect_config_t mConfig;
Eric Laurentda7581b2010-07-02 08:12:41 -070060 uint32_t mCaptureIdx;
61 uint32_t mCaptureSize;
Jean-Michel Trivi3476de62012-04-15 17:15:07 -070062 uint32_t mScalingMode;
Eric Laurent3df40a02011-11-10 10:02:18 -080063 uint8_t mState;
Marco Nelissenf06c2ed2012-06-06 09:52:31 -070064 uint8_t mLastCaptureIdx;
65 uint32_t mLatency;
Eric Laurent183dc772012-03-23 15:35:48 -070066 struct timespec mBufferUpdateTime;
Marco Nelissenf06c2ed2012-06-06 09:52:31 -070067 uint8_t mCaptureBuf[CAPTURE_BUF_SIZE];
Eric Laurentda7581b2010-07-02 08:12:41 -070068};
69
Eric Laurentda7581b2010-07-02 08:12:41 -070070//
71//--- Local functions
72//
73
74void Visualizer_reset(VisualizerContext *pContext)
75{
76 pContext->mCaptureIdx = 0;
Marco Nelissenf06c2ed2012-06-06 09:52:31 -070077 pContext->mLastCaptureIdx = 0;
Eric Laurent183dc772012-03-23 15:35:48 -070078 pContext->mBufferUpdateTime.tv_sec = 0;
Marco Nelissenf06c2ed2012-06-06 09:52:31 -070079 pContext->mLatency = 0;
80 memset(pContext->mCaptureBuf, 0x80, CAPTURE_BUF_SIZE);
Eric Laurentda7581b2010-07-02 08:12:41 -070081}
82
83//----------------------------------------------------------------------------
Eric Laurent3d5188b2011-12-16 15:30:36 -080084// Visualizer_setConfig()
Eric Laurentda7581b2010-07-02 08:12:41 -070085//----------------------------------------------------------------------------
86// Purpose: Set input and output audio configuration.
87//
88// Inputs:
89// pContext: effect engine context
90// pConfig: pointer to effect_config_t structure holding input and output
91// configuration parameters
92//
93// Outputs:
94//
95//----------------------------------------------------------------------------
96
Eric Laurent3d5188b2011-12-16 15:30:36 -080097int Visualizer_setConfig(VisualizerContext *pContext, effect_config_t *pConfig)
Eric Laurentda7581b2010-07-02 08:12:41 -070098{
Eric Laurent3d5188b2011-12-16 15:30:36 -080099 ALOGV("Visualizer_setConfig start");
Eric Laurentda7581b2010-07-02 08:12:41 -0700100
101 if (pConfig->inputCfg.samplingRate != pConfig->outputCfg.samplingRate) return -EINVAL;
102 if (pConfig->inputCfg.channels != pConfig->outputCfg.channels) return -EINVAL;
103 if (pConfig->inputCfg.format != pConfig->outputCfg.format) return -EINVAL;
Eric Laurente1315cf2011-05-17 19:16:02 -0700104 if (pConfig->inputCfg.channels != AUDIO_CHANNEL_OUT_STEREO) return -EINVAL;
Eric Laurentda7581b2010-07-02 08:12:41 -0700105 if (pConfig->outputCfg.accessMode != EFFECT_BUFFER_ACCESS_WRITE &&
106 pConfig->outputCfg.accessMode != EFFECT_BUFFER_ACCESS_ACCUMULATE) return -EINVAL;
Eric Laurente1315cf2011-05-17 19:16:02 -0700107 if (pConfig->inputCfg.format != AUDIO_FORMAT_PCM_16_BIT) return -EINVAL;
Eric Laurentda7581b2010-07-02 08:12:41 -0700108
Glenn Kastena189a682012-02-20 12:16:30 -0800109 pContext->mConfig = *pConfig;
Eric Laurentda7581b2010-07-02 08:12:41 -0700110
111 Visualizer_reset(pContext);
112
113 return 0;
114}
115
116
117//----------------------------------------------------------------------------
Eric Laurent3d5188b2011-12-16 15:30:36 -0800118// Visualizer_getConfig()
119//----------------------------------------------------------------------------
120// Purpose: Get input and output audio configuration.
121//
122// Inputs:
123// pContext: effect engine context
124// pConfig: pointer to effect_config_t structure holding input and output
125// configuration parameters
126//
127// Outputs:
128//
129//----------------------------------------------------------------------------
130
131void Visualizer_getConfig(VisualizerContext *pContext, effect_config_t *pConfig)
132{
Glenn Kastena189a682012-02-20 12:16:30 -0800133 *pConfig = pContext->mConfig;
Eric Laurent3d5188b2011-12-16 15:30:36 -0800134}
135
136
137//----------------------------------------------------------------------------
Eric Laurentda7581b2010-07-02 08:12:41 -0700138// Visualizer_init()
139//----------------------------------------------------------------------------
140// Purpose: Initialize engine with default configuration.
141//
142// Inputs:
143// pContext: effect engine context
144//
145// Outputs:
146//
147//----------------------------------------------------------------------------
148
149int Visualizer_init(VisualizerContext *pContext)
150{
151 pContext->mConfig.inputCfg.accessMode = EFFECT_BUFFER_ACCESS_READ;
Eric Laurente1315cf2011-05-17 19:16:02 -0700152 pContext->mConfig.inputCfg.channels = AUDIO_CHANNEL_OUT_STEREO;
153 pContext->mConfig.inputCfg.format = AUDIO_FORMAT_PCM_16_BIT;
Eric Laurentda7581b2010-07-02 08:12:41 -0700154 pContext->mConfig.inputCfg.samplingRate = 44100;
155 pContext->mConfig.inputCfg.bufferProvider.getBuffer = NULL;
156 pContext->mConfig.inputCfg.bufferProvider.releaseBuffer = NULL;
157 pContext->mConfig.inputCfg.bufferProvider.cookie = NULL;
158 pContext->mConfig.inputCfg.mask = EFFECT_CONFIG_ALL;
159 pContext->mConfig.outputCfg.accessMode = EFFECT_BUFFER_ACCESS_ACCUMULATE;
Eric Laurente1315cf2011-05-17 19:16:02 -0700160 pContext->mConfig.outputCfg.channels = AUDIO_CHANNEL_OUT_STEREO;
161 pContext->mConfig.outputCfg.format = AUDIO_FORMAT_PCM_16_BIT;
Eric Laurentda7581b2010-07-02 08:12:41 -0700162 pContext->mConfig.outputCfg.samplingRate = 44100;
163 pContext->mConfig.outputCfg.bufferProvider.getBuffer = NULL;
164 pContext->mConfig.outputCfg.bufferProvider.releaseBuffer = NULL;
165 pContext->mConfig.outputCfg.bufferProvider.cookie = NULL;
166 pContext->mConfig.outputCfg.mask = EFFECT_CONFIG_ALL;
167
168 pContext->mCaptureSize = VISUALIZER_CAPTURE_SIZE_MAX;
Jean-Michel Trivi3476de62012-04-15 17:15:07 -0700169 pContext->mScalingMode = VISUALIZER_SCALING_MODE_NORMALIZED;
Eric Laurentda7581b2010-07-02 08:12:41 -0700170
Eric Laurent3d5188b2011-12-16 15:30:36 -0800171 Visualizer_setConfig(pContext, &pContext->mConfig);
Eric Laurentda7581b2010-07-02 08:12:41 -0700172
173 return 0;
174}
175
176//
177//--- Effect Library Interface Implementation
178//
179
Eric Laurente1315cf2011-05-17 19:16:02 -0700180int VisualizerLib_QueryNumberEffects(uint32_t *pNumEffects) {
Eric Laurentda7581b2010-07-02 08:12:41 -0700181 *pNumEffects = 1;
182 return 0;
183}
184
Eric Laurente1315cf2011-05-17 19:16:02 -0700185int VisualizerLib_QueryEffect(uint32_t index,
186 effect_descriptor_t *pDescriptor) {
Eric Laurentda7581b2010-07-02 08:12:41 -0700187 if (pDescriptor == NULL) {
188 return -EINVAL;
189 }
190 if (index > 0) {
191 return -EINVAL;
192 }
Glenn Kastena189a682012-02-20 12:16:30 -0800193 *pDescriptor = gVisualizerDescriptor;
Eric Laurentda7581b2010-07-02 08:12:41 -0700194 return 0;
195}
196
Glenn Kasten5e92a782012-01-30 07:40:52 -0800197int VisualizerLib_Create(const effect_uuid_t *uuid,
Eric Laurente1315cf2011-05-17 19:16:02 -0700198 int32_t sessionId,
199 int32_t ioId,
200 effect_handle_t *pHandle) {
Eric Laurentda7581b2010-07-02 08:12:41 -0700201 int ret;
202 int i;
203
Eric Laurente1315cf2011-05-17 19:16:02 -0700204 if (pHandle == NULL || uuid == NULL) {
Eric Laurentda7581b2010-07-02 08:12:41 -0700205 return -EINVAL;
206 }
207
208 if (memcmp(uuid, &gVisualizerDescriptor.uuid, sizeof(effect_uuid_t)) != 0) {
209 return -EINVAL;
210 }
211
212 VisualizerContext *pContext = new VisualizerContext;
213
214 pContext->mItfe = &gVisualizerInterface;
215 pContext->mState = VISUALIZER_STATE_UNINITIALIZED;
216
217 ret = Visualizer_init(pContext);
218 if (ret < 0) {
Steve Block5ff1dd52012-01-05 23:22:43 +0000219 ALOGW("VisualizerLib_Create() init failed");
Eric Laurentda7581b2010-07-02 08:12:41 -0700220 delete pContext;
221 return ret;
222 }
223
Eric Laurente1315cf2011-05-17 19:16:02 -0700224 *pHandle = (effect_handle_t)pContext;
Eric Laurentda7581b2010-07-02 08:12:41 -0700225
226 pContext->mState = VISUALIZER_STATE_INITIALIZED;
227
Steve Block3856b092011-10-20 11:56:00 +0100228 ALOGV("VisualizerLib_Create %p", pContext);
Eric Laurentda7581b2010-07-02 08:12:41 -0700229
230 return 0;
231
232}
233
Eric Laurente1315cf2011-05-17 19:16:02 -0700234int VisualizerLib_Release(effect_handle_t handle) {
235 VisualizerContext * pContext = (VisualizerContext *)handle;
Eric Laurentda7581b2010-07-02 08:12:41 -0700236
Steve Block3856b092011-10-20 11:56:00 +0100237 ALOGV("VisualizerLib_Release %p", handle);
Eric Laurentda7581b2010-07-02 08:12:41 -0700238 if (pContext == NULL) {
239 return -EINVAL;
240 }
241 pContext->mState = VISUALIZER_STATE_UNINITIALIZED;
242 delete pContext;
243
244 return 0;
245}
246
Glenn Kasten5e92a782012-01-30 07:40:52 -0800247int VisualizerLib_GetDescriptor(const effect_uuid_t *uuid,
Eric Laurente1315cf2011-05-17 19:16:02 -0700248 effect_descriptor_t *pDescriptor) {
249
250 if (pDescriptor == NULL || uuid == NULL){
Steve Block3856b092011-10-20 11:56:00 +0100251 ALOGV("VisualizerLib_GetDescriptor() called with NULL pointer");
Eric Laurente1315cf2011-05-17 19:16:02 -0700252 return -EINVAL;
253 }
254
255 if (memcmp(uuid, &gVisualizerDescriptor.uuid, sizeof(effect_uuid_t)) == 0) {
Glenn Kastena189a682012-02-20 12:16:30 -0800256 *pDescriptor = gVisualizerDescriptor;
Eric Laurente1315cf2011-05-17 19:16:02 -0700257 return 0;
258 }
259
260 return -EINVAL;
261} /* end VisualizerLib_GetDescriptor */
262
Eric Laurentda7581b2010-07-02 08:12:41 -0700263//
264//--- Effect Control Interface Implementation
265//
266
267static inline int16_t clamp16(int32_t sample)
268{
269 if ((sample>>15) ^ (sample>>31))
270 sample = 0x7FFF ^ (sample>>31);
271 return sample;
272}
273
Eric Laurente1315cf2011-05-17 19:16:02 -0700274int Visualizer_process(
275 effect_handle_t self,audio_buffer_t *inBuffer, audio_buffer_t *outBuffer)
Eric Laurentda7581b2010-07-02 08:12:41 -0700276{
Eric Laurente1315cf2011-05-17 19:16:02 -0700277 VisualizerContext * pContext = (VisualizerContext *)self;
Eric Laurentda7581b2010-07-02 08:12:41 -0700278
279 if (pContext == NULL) {
280 return -EINVAL;
281 }
Eric Laurentda7581b2010-07-02 08:12:41 -0700282
283 if (inBuffer == NULL || inBuffer->raw == NULL ||
284 outBuffer == NULL || outBuffer->raw == NULL ||
285 inBuffer->frameCount != outBuffer->frameCount ||
286 inBuffer->frameCount == 0) {
287 return -EINVAL;
288 }
289
290 // all code below assumes stereo 16 bit PCM output and input
Jean-Michel Trivi3476de62012-04-15 17:15:07 -0700291 int32_t shift;
Eric Laurent0e75f0f2010-09-21 14:52:01 -0700292
Jean-Michel Trivi3476de62012-04-15 17:15:07 -0700293 if (pContext->mScalingMode == VISUALIZER_SCALING_MODE_NORMALIZED) {
294 // derive capture scaling factor from peak value in current buffer
295 // this gives more interesting captures for display.
296 shift = 32;
297 int len = inBuffer->frameCount * 2;
298 for (int i = 0; i < len; i++) {
299 int32_t smp = inBuffer->s16[i];
300 if (smp < 0) smp = -smp - 1; // take care to keep the max negative in range
301 int32_t clz = __builtin_clz(smp);
302 if (shift > clz) shift = clz;
303 }
304 // A maximum amplitude signal will have 17 leading zeros, which we want to
305 // translate to a shift of 8 (for converting 16 bit to 8 bit)
306 shift = 25 - shift;
307 // Never scale by less than 8 to avoid returning unaltered PCM signal.
308 if (shift < 3) {
309 shift = 3;
310 }
311 // add one to combine the division by 2 needed after summing left and right channels below
312 shift++;
313 } else {
314 assert(pContext->mScalingMode == VISUALIZER_SCALING_MODE_AS_PLAYED);
315 shift = 9;
Eric Laurent0e75f0f2010-09-21 14:52:01 -0700316 }
Eric Laurent0e75f0f2010-09-21 14:52:01 -0700317
Eric Laurentda7581b2010-07-02 08:12:41 -0700318 uint32_t captIdx;
319 uint32_t inIdx;
Marco Nelissenf06c2ed2012-06-06 09:52:31 -0700320 uint8_t *buf = pContext->mCaptureBuf;
Eric Laurentda7581b2010-07-02 08:12:41 -0700321 for (inIdx = 0, captIdx = pContext->mCaptureIdx;
Marco Nelissenf06c2ed2012-06-06 09:52:31 -0700322 inIdx < inBuffer->frameCount;
Eric Laurentda7581b2010-07-02 08:12:41 -0700323 inIdx++, captIdx++) {
Marco Nelissenf06c2ed2012-06-06 09:52:31 -0700324 if (captIdx >= CAPTURE_BUF_SIZE) {
325 // wrap around
326 captIdx = 0;
327 }
Eric Laurentda7581b2010-07-02 08:12:41 -0700328 int32_t smp = inBuffer->s16[2 * inIdx] + inBuffer->s16[2 * inIdx + 1];
Marco Nelissen64c3bde2010-10-27 09:06:01 -0700329 smp = smp >> shift;
Eric Laurentda7581b2010-07-02 08:12:41 -0700330 buf[captIdx] = ((uint8_t)smp)^0x80;
331 }
Marco Nelissenf06c2ed2012-06-06 09:52:31 -0700332
333 // XXX the following two should really be atomic, though it probably doesn't
334 // matter much for visualization purposes
Eric Laurentda7581b2010-07-02 08:12:41 -0700335 pContext->mCaptureIdx = captIdx;
Marco Nelissenf06c2ed2012-06-06 09:52:31 -0700336 // update last buffer update time stamp
337 if (clock_gettime(CLOCK_MONOTONIC, &pContext->mBufferUpdateTime) < 0) {
338 pContext->mBufferUpdateTime.tv_sec = 0;
Eric Laurentda7581b2010-07-02 08:12:41 -0700339 }
340
341 if (inBuffer->raw != outBuffer->raw) {
342 if (pContext->mConfig.outputCfg.accessMode == EFFECT_BUFFER_ACCESS_ACCUMULATE) {
343 for (size_t i = 0; i < outBuffer->frameCount*2; i++) {
344 outBuffer->s16[i] = clamp16(outBuffer->s16[i] + inBuffer->s16[i]);
345 }
346 } else {
347 memcpy(outBuffer->raw, inBuffer->raw, outBuffer->frameCount * 2 * sizeof(int16_t));
348 }
349 }
Eric Laurentf997cab2010-07-19 06:24:46 -0700350 if (pContext->mState != VISUALIZER_STATE_ACTIVE) {
351 return -ENODATA;
352 }
Eric Laurentda7581b2010-07-02 08:12:41 -0700353 return 0;
354} // end Visualizer_process
355
Eric Laurente1315cf2011-05-17 19:16:02 -0700356int Visualizer_command(effect_handle_t self, uint32_t cmdCode, uint32_t cmdSize,
Eric Laurent25f43952010-07-28 05:40:18 -0700357 void *pCmdData, uint32_t *replySize, void *pReplyData) {
Eric Laurentda7581b2010-07-02 08:12:41 -0700358
Eric Laurente1315cf2011-05-17 19:16:02 -0700359 VisualizerContext * pContext = (VisualizerContext *)self;
Eric Laurentda7581b2010-07-02 08:12:41 -0700360 int retsize;
361
362 if (pContext == NULL || pContext->mState == VISUALIZER_STATE_UNINITIALIZED) {
363 return -EINVAL;
364 }
365
Steve Block3856b092011-10-20 11:56:00 +0100366// ALOGV("Visualizer_command command %d cmdSize %d",cmdCode, cmdSize);
Eric Laurentda7581b2010-07-02 08:12:41 -0700367
368 switch (cmdCode) {
369 case EFFECT_CMD_INIT:
370 if (pReplyData == NULL || *replySize != sizeof(int)) {
371 return -EINVAL;
372 }
373 *(int *) pReplyData = Visualizer_init(pContext);
374 break;
Eric Laurent3d5188b2011-12-16 15:30:36 -0800375 case EFFECT_CMD_SET_CONFIG:
Eric Laurentda7581b2010-07-02 08:12:41 -0700376 if (pCmdData == NULL || cmdSize != sizeof(effect_config_t)
377 || pReplyData == NULL || *replySize != sizeof(int)) {
378 return -EINVAL;
379 }
Eric Laurent3d5188b2011-12-16 15:30:36 -0800380 *(int *) pReplyData = Visualizer_setConfig(pContext,
Eric Laurentda7581b2010-07-02 08:12:41 -0700381 (effect_config_t *) pCmdData);
382 break;
Eric Laurent3d5188b2011-12-16 15:30:36 -0800383 case EFFECT_CMD_GET_CONFIG:
384 if (pReplyData == NULL ||
385 *replySize != sizeof(effect_config_t)) {
386 return -EINVAL;
387 }
388 Visualizer_getConfig(pContext, (effect_config_t *)pReplyData);
389 break;
Eric Laurentda7581b2010-07-02 08:12:41 -0700390 case EFFECT_CMD_RESET:
391 Visualizer_reset(pContext);
392 break;
393 case EFFECT_CMD_ENABLE:
394 if (pReplyData == NULL || *replySize != sizeof(int)) {
395 return -EINVAL;
396 }
397 if (pContext->mState != VISUALIZER_STATE_INITIALIZED) {
398 return -ENOSYS;
399 }
400 pContext->mState = VISUALIZER_STATE_ACTIVE;
Steve Block3856b092011-10-20 11:56:00 +0100401 ALOGV("EFFECT_CMD_ENABLE() OK");
Eric Laurentda7581b2010-07-02 08:12:41 -0700402 *(int *)pReplyData = 0;
403 break;
404 case EFFECT_CMD_DISABLE:
405 if (pReplyData == NULL || *replySize != sizeof(int)) {
406 return -EINVAL;
407 }
408 if (pContext->mState != VISUALIZER_STATE_ACTIVE) {
409 return -ENOSYS;
410 }
411 pContext->mState = VISUALIZER_STATE_INITIALIZED;
Steve Block3856b092011-10-20 11:56:00 +0100412 ALOGV("EFFECT_CMD_DISABLE() OK");
Eric Laurentda7581b2010-07-02 08:12:41 -0700413 *(int *)pReplyData = 0;
414 break;
415 case EFFECT_CMD_GET_PARAM: {
416 if (pCmdData == NULL ||
417 cmdSize != (int)(sizeof(effect_param_t) + sizeof(uint32_t)) ||
418 pReplyData == NULL ||
419 *replySize < (int)(sizeof(effect_param_t) + sizeof(uint32_t) + sizeof(uint32_t))) {
420 return -EINVAL;
421 }
422 memcpy(pReplyData, pCmdData, sizeof(effect_param_t) + sizeof(uint32_t));
423 effect_param_t *p = (effect_param_t *)pReplyData;
424 p->status = 0;
425 *replySize = sizeof(effect_param_t) + sizeof(uint32_t);
Jean-Michel Trivi3476de62012-04-15 17:15:07 -0700426 if (p->psize != sizeof(uint32_t)) {
Eric Laurentda7581b2010-07-02 08:12:41 -0700427 p->status = -EINVAL;
428 break;
429 }
Jean-Michel Trivi3476de62012-04-15 17:15:07 -0700430 switch (*(uint32_t *)p->data) {
431 case VISUALIZER_PARAM_CAPTURE_SIZE:
432 ALOGV("get mCaptureSize = %d", pContext->mCaptureSize);
433 *((uint32_t *)p->data + 1) = pContext->mCaptureSize;
434 p->vsize = sizeof(uint32_t);
435 *replySize += sizeof(uint32_t);
436 break;
437 case VISUALIZER_PARAM_SCALING_MODE:
438 ALOGV("get mScalingMode = %d", pContext->mScalingMode);
439 *((uint32_t *)p->data + 1) = pContext->mScalingMode;
440 p->vsize = sizeof(uint32_t);
441 *replySize += sizeof(uint32_t);
442 break;
443 default:
444 p->status = -EINVAL;
445 }
Eric Laurentda7581b2010-07-02 08:12:41 -0700446 } break;
447 case EFFECT_CMD_SET_PARAM: {
448 if (pCmdData == NULL ||
449 cmdSize != (int)(sizeof(effect_param_t) + sizeof(uint32_t) + sizeof(uint32_t)) ||
450 pReplyData == NULL || *replySize != sizeof(int32_t)) {
451 return -EINVAL;
452 }
453 *(int32_t *)pReplyData = 0;
454 effect_param_t *p = (effect_param_t *)pCmdData;
Jean-Michel Trivi3476de62012-04-15 17:15:07 -0700455 if (p->psize != sizeof(uint32_t) || p->vsize != sizeof(uint32_t)) {
Eric Laurentda7581b2010-07-02 08:12:41 -0700456 *(int32_t *)pReplyData = -EINVAL;
Jean-Michel Trivi3476de62012-04-15 17:15:07 -0700457 break;
Eric Laurentda7581b2010-07-02 08:12:41 -0700458 }
Jean-Michel Trivi3476de62012-04-15 17:15:07 -0700459 switch (*(uint32_t *)p->data) {
460 case VISUALIZER_PARAM_CAPTURE_SIZE:
461 pContext->mCaptureSize = *((uint32_t *)p->data + 1);
462 ALOGV("set mCaptureSize = %d", pContext->mCaptureSize);
463 break;
464 case VISUALIZER_PARAM_SCALING_MODE:
465 pContext->mScalingMode = *((uint32_t *)p->data + 1);
466 ALOGV("set mScalingMode = %d", pContext->mScalingMode);
467 break;
Marco Nelissenf06c2ed2012-06-06 09:52:31 -0700468 case VISUALIZER_PARAM_LATENCY:
469 pContext->mLatency = *((uint32_t *)p->data + 1);
470 ALOGV("set mLatency = %d", pContext->mLatency);
471 break;
Jean-Michel Trivi3476de62012-04-15 17:15:07 -0700472 default:
473 *(int32_t *)pReplyData = -EINVAL;
474 }
Eric Laurentda7581b2010-07-02 08:12:41 -0700475 } break;
476 case EFFECT_CMD_SET_DEVICE:
477 case EFFECT_CMD_SET_VOLUME:
478 case EFFECT_CMD_SET_AUDIO_MODE:
479 break;
480
481
Eric Laurent6d8b6942011-06-24 07:01:31 -0700482 case VISUALIZER_CMD_CAPTURE:
Eric Laurent0e75f0f2010-09-21 14:52:01 -0700483 if (pReplyData == NULL || *replySize != pContext->mCaptureSize) {
Steve Block3856b092011-10-20 11:56:00 +0100484 ALOGV("VISUALIZER_CMD_CAPTURE() error *replySize %d pContext->mCaptureSize %d",
Eric Laurentda7581b2010-07-02 08:12:41 -0700485 *replySize, pContext->mCaptureSize);
486 return -EINVAL;
487 }
488 if (pContext->mState == VISUALIZER_STATE_ACTIVE) {
Marco Nelissenf06c2ed2012-06-06 09:52:31 -0700489 int32_t latencyMs = pContext->mLatency;
490 uint32_t deltaMs = 0;
491 if (pContext->mBufferUpdateTime.tv_sec != 0) {
Eric Laurent183dc772012-03-23 15:35:48 -0700492 struct timespec ts;
493 if (clock_gettime(CLOCK_MONOTONIC, &ts) == 0) {
494 time_t secs = ts.tv_sec - pContext->mBufferUpdateTime.tv_sec;
495 long nsec = ts.tv_nsec - pContext->mBufferUpdateTime.tv_nsec;
496 if (nsec < 0) {
497 --secs;
498 nsec += 1000000000;
499 }
Marco Nelissenf06c2ed2012-06-06 09:52:31 -0700500 deltaMs = secs * 1000 + nsec / 1000000;
501 latencyMs -= deltaMs;
502 if (latencyMs < 0) {
503 latencyMs = 0;
Eric Laurent3df40a02011-11-10 10:02:18 -0800504 }
505 }
Eric Laurent3df40a02011-11-10 10:02:18 -0800506 }
Marco Nelissenf06c2ed2012-06-06 09:52:31 -0700507 uint32_t deltaSmpl = pContext->mConfig.inputCfg.samplingRate * latencyMs / 1000;
508
509 int32_t capturePoint = pContext->mCaptureIdx - pContext->mCaptureSize - deltaSmpl;
510 int32_t captureSize = pContext->mCaptureSize;
511 if (capturePoint < 0) {
512 int32_t size = -capturePoint;
513 if (size > captureSize) {
514 size = captureSize;
515 }
516 memcpy(pReplyData,
517 pContext->mCaptureBuf + CAPTURE_BUF_SIZE + capturePoint,
518 size);
519 pReplyData += size;
520 captureSize -= size;
521 capturePoint = 0;
522 }
523 memcpy(pReplyData,
524 pContext->mCaptureBuf + capturePoint,
525 captureSize);
526
527
528 // if audio framework has stopped playing audio although the effect is still
529 // active we must clear the capture buffer to return silence
530 if ((pContext->mLastCaptureIdx == pContext->mCaptureIdx) &&
531 (pContext->mBufferUpdateTime.tv_sec != 0)) {
532 if (deltaMs > MAX_STALL_TIME_MS) {
533 ALOGV("capture going to idle");
534 pContext->mBufferUpdateTime.tv_sec = 0;
535 memset(pReplyData, 0x80, pContext->mCaptureSize);
536 }
537 }
538 pContext->mLastCaptureIdx = pContext->mCaptureIdx;
Eric Laurentda7581b2010-07-02 08:12:41 -0700539 } else {
540 memset(pReplyData, 0x80, pContext->mCaptureSize);
541 }
Eric Laurent3df40a02011-11-10 10:02:18 -0800542
Eric Laurentda7581b2010-07-02 08:12:41 -0700543 break;
544
545 default:
Steve Block5ff1dd52012-01-05 23:22:43 +0000546 ALOGW("Visualizer_command invalid command %d",cmdCode);
Eric Laurentda7581b2010-07-02 08:12:41 -0700547 return -EINVAL;
548 }
549
550 return 0;
551}
552
Eric Laurente1315cf2011-05-17 19:16:02 -0700553/* Effect Control Interface Implementation: get_descriptor */
554int Visualizer_getDescriptor(effect_handle_t self,
555 effect_descriptor_t *pDescriptor)
556{
557 VisualizerContext * pContext = (VisualizerContext *) self;
558
559 if (pContext == NULL || pDescriptor == NULL) {
Steve Block3856b092011-10-20 11:56:00 +0100560 ALOGV("Visualizer_getDescriptor() invalid param");
Eric Laurente1315cf2011-05-17 19:16:02 -0700561 return -EINVAL;
562 }
563
Glenn Kastena189a682012-02-20 12:16:30 -0800564 *pDescriptor = gVisualizerDescriptor;
Eric Laurente1315cf2011-05-17 19:16:02 -0700565
566 return 0;
567} /* end Visualizer_getDescriptor */
568
569// effect_handle_t interface implementation for visualizer effect
Eric Laurentda7581b2010-07-02 08:12:41 -0700570const struct effect_interface_s gVisualizerInterface = {
571 Visualizer_process,
Eric Laurente1315cf2011-05-17 19:16:02 -0700572 Visualizer_command,
Eric Laurentba7b8f82011-06-17 18:54:16 -0700573 Visualizer_getDescriptor,
574 NULL,
Eric Laurentda7581b2010-07-02 08:12:41 -0700575};
576
Eric Laurentda7581b2010-07-02 08:12:41 -0700577
Eric Laurente1315cf2011-05-17 19:16:02 -0700578audio_effect_library_t AUDIO_EFFECT_LIBRARY_INFO_SYM = {
579 tag : AUDIO_EFFECT_LIBRARY_TAG,
580 version : EFFECT_LIBRARY_API_VERSION,
581 name : "Visualizer Library",
582 implementor : "The Android Open Source Project",
583 query_num_effects : VisualizerLib_QueryNumberEffects,
584 query_effect : VisualizerLib_QueryEffect,
585 create_effect : VisualizerLib_Create,
586 release_effect : VisualizerLib_Release,
587 get_descriptor : VisualizerLib_GetDescriptor,
588};
589
590}; // extern "C"