blob: 5505f14144a9da10e0cb4a39818ab9c5b901d0de [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>
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;
Eric Laurent0fa449c2010-09-24 11:52:04 -070068 memset(pContext->mCaptureBuf[0], 0x80, VISUALIZER_CAPTURE_SIZE_MAX);
69 memset(pContext->mCaptureBuf[1], 0x80, VISUALIZER_CAPTURE_SIZE_MAX);
Eric Laurentda7581b2010-07-02 08:12:41 -070070}
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 }
Eric Laurentda7581b2010-07-02 08:12:41 -0700233
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
Eric Laurent0e75f0f2010-09-21 14:52:01 -0700242
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 for (size_t i = 0; i < inBuffer->frameCount; i++) {
247 int32_t smp = inBuffer->s16[i];
248 if (smp < 0) smp = -smp;
249 int32_t clz = __builtin_clz(smp);
250 if (shift > clz) shift = clz;
251 }
252 // never scale by less than 8 to avoid returning unaltered PCM signal.
253 // add one to combine the division by 2 needed after summing left and right channels below
254 if (20 > shift) {
255 shift = (31 - 8 + 1) - shift;
256 } else {
257 shift = (3 + 1);
258 }
259
Eric Laurentda7581b2010-07-02 08:12:41 -0700260 uint32_t captIdx;
261 uint32_t inIdx;
262 uint8_t *buf = pContext->mCaptureBuf[pContext->mCurrentBuf];
263 for (inIdx = 0, captIdx = pContext->mCaptureIdx;
264 inIdx < inBuffer->frameCount && captIdx < pContext->mCaptureSize;
265 inIdx++, captIdx++) {
266 int32_t smp = inBuffer->s16[2 * inIdx] + inBuffer->s16[2 * inIdx + 1];
Eric Laurent0e75f0f2010-09-21 14:52:01 -0700267 smp = (smp + (1 << (shift - 1))) >> shift;
Eric Laurentda7581b2010-07-02 08:12:41 -0700268 buf[captIdx] = ((uint8_t)smp)^0x80;
269 }
270 pContext->mCaptureIdx = captIdx;
271
272 // go to next buffer when buffer full
273 if (pContext->mCaptureIdx == pContext->mCaptureSize) {
274 pContext->mCurrentBuf ^= 1;
275 pContext->mCaptureIdx = 0;
276 }
277
278 if (inBuffer->raw != outBuffer->raw) {
279 if (pContext->mConfig.outputCfg.accessMode == EFFECT_BUFFER_ACCESS_ACCUMULATE) {
280 for (size_t i = 0; i < outBuffer->frameCount*2; i++) {
281 outBuffer->s16[i] = clamp16(outBuffer->s16[i] + inBuffer->s16[i]);
282 }
283 } else {
284 memcpy(outBuffer->raw, inBuffer->raw, outBuffer->frameCount * 2 * sizeof(int16_t));
285 }
286 }
Eric Laurentf997cab2010-07-19 06:24:46 -0700287 if (pContext->mState != VISUALIZER_STATE_ACTIVE) {
288 return -ENODATA;
289 }
Eric Laurentda7581b2010-07-02 08:12:41 -0700290 return 0;
291} // end Visualizer_process
292
Eric Laurent25f43952010-07-28 05:40:18 -0700293extern "C" int Visualizer_command(effect_interface_t self, uint32_t cmdCode, uint32_t cmdSize,
294 void *pCmdData, uint32_t *replySize, void *pReplyData) {
Eric Laurentda7581b2010-07-02 08:12:41 -0700295
296 android::VisualizerContext * pContext = (android::VisualizerContext *)self;
297 int retsize;
298
299 if (pContext == NULL || pContext->mState == VISUALIZER_STATE_UNINITIALIZED) {
300 return -EINVAL;
301 }
302
303// LOGV("Visualizer_command command %d cmdSize %d",cmdCode, cmdSize);
304
305 switch (cmdCode) {
306 case EFFECT_CMD_INIT:
307 if (pReplyData == NULL || *replySize != sizeof(int)) {
308 return -EINVAL;
309 }
310 *(int *) pReplyData = Visualizer_init(pContext);
311 break;
312 case EFFECT_CMD_CONFIGURE:
313 if (pCmdData == NULL || cmdSize != sizeof(effect_config_t)
314 || pReplyData == NULL || *replySize != sizeof(int)) {
315 return -EINVAL;
316 }
317 *(int *) pReplyData = Visualizer_configure(pContext,
318 (effect_config_t *) pCmdData);
319 break;
320 case EFFECT_CMD_RESET:
321 Visualizer_reset(pContext);
322 break;
323 case EFFECT_CMD_ENABLE:
324 if (pReplyData == NULL || *replySize != sizeof(int)) {
325 return -EINVAL;
326 }
327 if (pContext->mState != VISUALIZER_STATE_INITIALIZED) {
328 return -ENOSYS;
329 }
330 pContext->mState = VISUALIZER_STATE_ACTIVE;
331 LOGV("EFFECT_CMD_ENABLE() OK");
332 *(int *)pReplyData = 0;
333 break;
334 case EFFECT_CMD_DISABLE:
335 if (pReplyData == NULL || *replySize != sizeof(int)) {
336 return -EINVAL;
337 }
338 if (pContext->mState != VISUALIZER_STATE_ACTIVE) {
339 return -ENOSYS;
340 }
341 pContext->mState = VISUALIZER_STATE_INITIALIZED;
342 LOGV("EFFECT_CMD_DISABLE() OK");
343 *(int *)pReplyData = 0;
344 break;
345 case EFFECT_CMD_GET_PARAM: {
346 if (pCmdData == NULL ||
347 cmdSize != (int)(sizeof(effect_param_t) + sizeof(uint32_t)) ||
348 pReplyData == NULL ||
349 *replySize < (int)(sizeof(effect_param_t) + sizeof(uint32_t) + sizeof(uint32_t))) {
350 return -EINVAL;
351 }
352 memcpy(pReplyData, pCmdData, sizeof(effect_param_t) + sizeof(uint32_t));
353 effect_param_t *p = (effect_param_t *)pReplyData;
354 p->status = 0;
355 *replySize = sizeof(effect_param_t) + sizeof(uint32_t);
356 if (p->psize != sizeof(uint32_t) ||
357 *(uint32_t *)p->data != VISU_PARAM_CAPTURE_SIZE) {
358 p->status = -EINVAL;
359 break;
360 }
361 LOGV("get mCaptureSize = %d", pContext->mCaptureSize);
362 *((uint32_t *)p->data + 1) = pContext->mCaptureSize;
363 p->vsize = sizeof(uint32_t);
364 *replySize += sizeof(uint32_t);
365 } break;
366 case EFFECT_CMD_SET_PARAM: {
367 if (pCmdData == NULL ||
368 cmdSize != (int)(sizeof(effect_param_t) + sizeof(uint32_t) + sizeof(uint32_t)) ||
369 pReplyData == NULL || *replySize != sizeof(int32_t)) {
370 return -EINVAL;
371 }
372 *(int32_t *)pReplyData = 0;
373 effect_param_t *p = (effect_param_t *)pCmdData;
374 if (p->psize != sizeof(uint32_t) ||
375 p->vsize != sizeof(uint32_t) ||
376 *(uint32_t *)p->data != VISU_PARAM_CAPTURE_SIZE) {
377 *(int32_t *)pReplyData = -EINVAL;
378 break;;
379 }
380 pContext->mCaptureSize = *((uint32_t *)p->data + 1);
381 LOGV("set mCaptureSize = %d", pContext->mCaptureSize);
382 } break;
383 case EFFECT_CMD_SET_DEVICE:
384 case EFFECT_CMD_SET_VOLUME:
385 case EFFECT_CMD_SET_AUDIO_MODE:
386 break;
387
388
389 case VISU_CMD_CAPTURE:
Eric Laurent0e75f0f2010-09-21 14:52:01 -0700390 if (pReplyData == NULL || *replySize != pContext->mCaptureSize) {
Eric Laurentda7581b2010-07-02 08:12:41 -0700391 LOGV("VISU_CMD_CAPTURE() error *replySize %d pContext->mCaptureSize %d",
392 *replySize, pContext->mCaptureSize);
393 return -EINVAL;
394 }
395 if (pContext->mState == VISUALIZER_STATE_ACTIVE) {
396 memcpy(pReplyData,
397 pContext->mCaptureBuf[pContext->mCurrentBuf ^ 1],
398 pContext->mCaptureSize);
399 } else {
400 memset(pReplyData, 0x80, pContext->mCaptureSize);
401 }
402 break;
403
404 default:
405 LOGW("Visualizer_command invalid command %d",cmdCode);
406 return -EINVAL;
407 }
408
409 return 0;
410}
411
412// effect_interface_t interface implementation for visualizer effect
413const struct effect_interface_s gVisualizerInterface = {
414 Visualizer_process,
415 Visualizer_command
416};
417
418} // namespace
419