EffectVisualizer.cpp revision 25f4395b932fa9859a6e91ba77c5d20d009da64a
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], 0, VISUALIZER_CAPTURE_SIZE_MAX);
69    memset(pContext->mCaptureBuf[1], 0, 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    uint32_t captIdx;
243    uint32_t inIdx;
244    uint8_t *buf = pContext->mCaptureBuf[pContext->mCurrentBuf];
245    for (inIdx = 0, captIdx = pContext->mCaptureIdx;
246         inIdx < inBuffer->frameCount && captIdx < pContext->mCaptureSize;
247         inIdx++, captIdx++) {
248        int32_t smp = inBuffer->s16[2 * inIdx] + inBuffer->s16[2 * inIdx + 1];
249        smp = (smp + (1 << 8)) >> 9;
250        buf[captIdx] = ((uint8_t)smp)^0x80;
251    }
252    pContext->mCaptureIdx = captIdx;
253
254    // go to next buffer when buffer full
255    if (pContext->mCaptureIdx == pContext->mCaptureSize) {
256        pContext->mCurrentBuf ^= 1;
257        pContext->mCaptureIdx = 0;
258    }
259
260    if (inBuffer->raw != outBuffer->raw) {
261        if (pContext->mConfig.outputCfg.accessMode == EFFECT_BUFFER_ACCESS_ACCUMULATE) {
262            for (size_t i = 0; i < outBuffer->frameCount*2; i++) {
263                outBuffer->s16[i] = clamp16(outBuffer->s16[i] + inBuffer->s16[i]);
264            }
265        } else {
266            memcpy(outBuffer->raw, inBuffer->raw, outBuffer->frameCount * 2 * sizeof(int16_t));
267        }
268    }
269    if (pContext->mState != VISUALIZER_STATE_ACTIVE) {
270        return -ENODATA;
271    }
272    return 0;
273}   // end Visualizer_process
274
275extern "C" int Visualizer_command(effect_interface_t self, uint32_t cmdCode, uint32_t cmdSize,
276        void *pCmdData, uint32_t *replySize, void *pReplyData) {
277
278    android::VisualizerContext * pContext = (android::VisualizerContext *)self;
279    int retsize;
280
281    if (pContext == NULL || pContext->mState == VISUALIZER_STATE_UNINITIALIZED) {
282        return -EINVAL;
283    }
284
285//    LOGV("Visualizer_command command %d cmdSize %d",cmdCode, cmdSize);
286
287    switch (cmdCode) {
288    case EFFECT_CMD_INIT:
289        if (pReplyData == NULL || *replySize != sizeof(int)) {
290            return -EINVAL;
291        }
292        *(int *) pReplyData = Visualizer_init(pContext);
293        break;
294    case EFFECT_CMD_CONFIGURE:
295        if (pCmdData == NULL || cmdSize != sizeof(effect_config_t)
296                || pReplyData == NULL || *replySize != sizeof(int)) {
297            return -EINVAL;
298        }
299        *(int *) pReplyData = Visualizer_configure(pContext,
300                (effect_config_t *) pCmdData);
301        break;
302    case EFFECT_CMD_RESET:
303        Visualizer_reset(pContext);
304        break;
305    case EFFECT_CMD_ENABLE:
306        if (pReplyData == NULL || *replySize != sizeof(int)) {
307            return -EINVAL;
308        }
309        if (pContext->mState != VISUALIZER_STATE_INITIALIZED) {
310            return -ENOSYS;
311        }
312        pContext->mState = VISUALIZER_STATE_ACTIVE;
313        LOGV("EFFECT_CMD_ENABLE() OK");
314        *(int *)pReplyData = 0;
315        break;
316    case EFFECT_CMD_DISABLE:
317        if (pReplyData == NULL || *replySize != sizeof(int)) {
318            return -EINVAL;
319        }
320        if (pContext->mState != VISUALIZER_STATE_ACTIVE) {
321            return -ENOSYS;
322        }
323        pContext->mState = VISUALIZER_STATE_INITIALIZED;
324        LOGV("EFFECT_CMD_DISABLE() OK");
325        *(int *)pReplyData = 0;
326        break;
327    case EFFECT_CMD_GET_PARAM: {
328        if (pCmdData == NULL ||
329            cmdSize != (int)(sizeof(effect_param_t) + sizeof(uint32_t)) ||
330            pReplyData == NULL ||
331            *replySize < (int)(sizeof(effect_param_t) + sizeof(uint32_t) + sizeof(uint32_t))) {
332            return -EINVAL;
333        }
334        memcpy(pReplyData, pCmdData, sizeof(effect_param_t) + sizeof(uint32_t));
335        effect_param_t *p = (effect_param_t *)pReplyData;
336        p->status = 0;
337        *replySize = sizeof(effect_param_t) + sizeof(uint32_t);
338        if (p->psize != sizeof(uint32_t) ||
339            *(uint32_t *)p->data != VISU_PARAM_CAPTURE_SIZE) {
340            p->status = -EINVAL;
341            break;
342        }
343        LOGV("get mCaptureSize = %d", pContext->mCaptureSize);
344        *((uint32_t *)p->data + 1) = pContext->mCaptureSize;
345        p->vsize = sizeof(uint32_t);
346        *replySize += sizeof(uint32_t);
347        } break;
348    case EFFECT_CMD_SET_PARAM: {
349        if (pCmdData == NULL ||
350            cmdSize != (int)(sizeof(effect_param_t) + sizeof(uint32_t) + sizeof(uint32_t)) ||
351            pReplyData == NULL || *replySize != sizeof(int32_t)) {
352            return -EINVAL;
353        }
354        *(int32_t *)pReplyData = 0;
355        effect_param_t *p = (effect_param_t *)pCmdData;
356        if (p->psize != sizeof(uint32_t) ||
357            p->vsize != sizeof(uint32_t) ||
358            *(uint32_t *)p->data != VISU_PARAM_CAPTURE_SIZE) {
359            *(int32_t *)pReplyData = -EINVAL;
360            break;;
361        }
362        pContext->mCaptureSize = *((uint32_t *)p->data + 1);
363        LOGV("set mCaptureSize = %d", pContext->mCaptureSize);
364        } break;
365    case EFFECT_CMD_SET_DEVICE:
366    case EFFECT_CMD_SET_VOLUME:
367    case EFFECT_CMD_SET_AUDIO_MODE:
368        break;
369
370
371    case VISU_CMD_CAPTURE:
372        if (pReplyData == NULL || *replySize != (int)pContext->mCaptureSize) {
373            LOGV("VISU_CMD_CAPTURE() error *replySize %d pContext->mCaptureSize %d",
374                    *replySize, pContext->mCaptureSize);
375            return -EINVAL;
376        }
377        if (pContext->mState == VISUALIZER_STATE_ACTIVE) {
378            memcpy(pReplyData,
379                   pContext->mCaptureBuf[pContext->mCurrentBuf ^ 1],
380                   pContext->mCaptureSize);
381        } else {
382            memset(pReplyData, 0x80, pContext->mCaptureSize);
383        }
384        break;
385
386    default:
387        LOGW("Visualizer_command invalid command %d",cmdCode);
388        return -EINVAL;
389    }
390
391    return 0;
392}
393
394// effect_interface_t interface implementation for visualizer effect
395const struct effect_interface_s gVisualizerInterface = {
396        Visualizer_process,
397        Visualizer_command
398};
399
400} // namespace
401
402