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