EffectVisualizer.cpp revision 3df40a093d8d3d211f693e0e3ef4076750cabfea
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 <audio_effects/effect_visualizer.h>
25
26
27extern "C" {
28
29// effect_handle_t interface implementation for visualizer effect
30extern const struct effect_interface_s gVisualizerInterface;
31
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
36        EFFECT_CONTROL_API_VERSION,
37        (EFFECT_FLAG_TYPE_INSERT | EFFECT_FLAG_INSERT_FIRST),
38        0, // TODO
39        1,
40        "Visualizer",
41        "The Android Open Source Project",
42};
43
44enum visualizer_state_e {
45    VISUALIZER_STATE_UNINITIALIZED,
46    VISUALIZER_STATE_INITIALIZED,
47    VISUALIZER_STATE_ACTIVE,
48};
49
50// maximum number of reads from same buffer before resetting capture buffer. This means
51// that the framework has stopped playing audio and we must start returning silence
52#define MAX_STALL_COUNT 10
53
54struct VisualizerContext {
55    const struct effect_interface_s *mItfe;
56    effect_config_t mConfig;
57    uint32_t mCaptureIdx;
58    uint32_t mCaptureSize;
59    uint8_t mState;
60    uint8_t mCurrentBuf;
61    uint8_t mLastBuf;
62    uint8_t mStallCount;
63    uint8_t mCaptureBuf[2][VISUALIZER_CAPTURE_SIZE_MAX];
64};
65
66//
67//--- Local functions
68//
69
70void Visualizer_reset(VisualizerContext *pContext)
71{
72    pContext->mCaptureIdx = 0;
73    pContext->mCurrentBuf = 0;
74    pContext->mLastBuf = 1;
75    pContext->mStallCount = 0;
76    memset(pContext->mCaptureBuf[0], 0x80, VISUALIZER_CAPTURE_SIZE_MAX);
77    memset(pContext->mCaptureBuf[1], 0x80, VISUALIZER_CAPTURE_SIZE_MAX);
78}
79
80//----------------------------------------------------------------------------
81// Visualizer_configure()
82//----------------------------------------------------------------------------
83// Purpose: Set input and output audio configuration.
84//
85// Inputs:
86//  pContext:   effect engine context
87//  pConfig:    pointer to effect_config_t structure holding input and output
88//      configuration parameters
89//
90// Outputs:
91//
92//----------------------------------------------------------------------------
93
94int Visualizer_configure(VisualizerContext *pContext, effect_config_t *pConfig)
95{
96    LOGV("Visualizer_configure start");
97
98    if (pConfig->inputCfg.samplingRate != pConfig->outputCfg.samplingRate) return -EINVAL;
99    if (pConfig->inputCfg.channels != pConfig->outputCfg.channels) return -EINVAL;
100    if (pConfig->inputCfg.format != pConfig->outputCfg.format) return -EINVAL;
101    if (pConfig->inputCfg.channels != AUDIO_CHANNEL_OUT_STEREO) return -EINVAL;
102    if (pConfig->outputCfg.accessMode != EFFECT_BUFFER_ACCESS_WRITE &&
103            pConfig->outputCfg.accessMode != EFFECT_BUFFER_ACCESS_ACCUMULATE) return -EINVAL;
104    if (pConfig->inputCfg.format != AUDIO_FORMAT_PCM_16_BIT) return -EINVAL;
105
106    memcpy(&pContext->mConfig, pConfig, sizeof(effect_config_t));
107
108    Visualizer_reset(pContext);
109
110    return 0;
111}
112
113
114//----------------------------------------------------------------------------
115// Visualizer_init()
116//----------------------------------------------------------------------------
117// Purpose: Initialize engine with default configuration.
118//
119// Inputs:
120//  pContext:   effect engine context
121//
122// Outputs:
123//
124//----------------------------------------------------------------------------
125
126int Visualizer_init(VisualizerContext *pContext)
127{
128    pContext->mConfig.inputCfg.accessMode = EFFECT_BUFFER_ACCESS_READ;
129    pContext->mConfig.inputCfg.channels = AUDIO_CHANNEL_OUT_STEREO;
130    pContext->mConfig.inputCfg.format = AUDIO_FORMAT_PCM_16_BIT;
131    pContext->mConfig.inputCfg.samplingRate = 44100;
132    pContext->mConfig.inputCfg.bufferProvider.getBuffer = NULL;
133    pContext->mConfig.inputCfg.bufferProvider.releaseBuffer = NULL;
134    pContext->mConfig.inputCfg.bufferProvider.cookie = NULL;
135    pContext->mConfig.inputCfg.mask = EFFECT_CONFIG_ALL;
136    pContext->mConfig.outputCfg.accessMode = EFFECT_BUFFER_ACCESS_ACCUMULATE;
137    pContext->mConfig.outputCfg.channels = AUDIO_CHANNEL_OUT_STEREO;
138    pContext->mConfig.outputCfg.format = AUDIO_FORMAT_PCM_16_BIT;
139    pContext->mConfig.outputCfg.samplingRate = 44100;
140    pContext->mConfig.outputCfg.bufferProvider.getBuffer = NULL;
141    pContext->mConfig.outputCfg.bufferProvider.releaseBuffer = NULL;
142    pContext->mConfig.outputCfg.bufferProvider.cookie = NULL;
143    pContext->mConfig.outputCfg.mask = EFFECT_CONFIG_ALL;
144
145    pContext->mCaptureSize = VISUALIZER_CAPTURE_SIZE_MAX;
146
147    Visualizer_configure(pContext, &pContext->mConfig);
148
149    return 0;
150}
151
152//
153//--- Effect Library Interface Implementation
154//
155
156int VisualizerLib_QueryNumberEffects(uint32_t *pNumEffects) {
157    *pNumEffects = 1;
158    return 0;
159}
160
161int VisualizerLib_QueryEffect(uint32_t index,
162                              effect_descriptor_t *pDescriptor) {
163    if (pDescriptor == NULL) {
164        return -EINVAL;
165    }
166    if (index > 0) {
167        return -EINVAL;
168    }
169    memcpy(pDescriptor, &gVisualizerDescriptor, sizeof(effect_descriptor_t));
170    return 0;
171}
172
173int VisualizerLib_Create(effect_uuid_t *uuid,
174                         int32_t sessionId,
175                         int32_t ioId,
176                         effect_handle_t *pHandle) {
177    int ret;
178    int i;
179
180    if (pHandle == NULL || uuid == NULL) {
181        return -EINVAL;
182    }
183
184    if (memcmp(uuid, &gVisualizerDescriptor.uuid, sizeof(effect_uuid_t)) != 0) {
185        return -EINVAL;
186    }
187
188    VisualizerContext *pContext = new VisualizerContext;
189
190    pContext->mItfe = &gVisualizerInterface;
191    pContext->mState = VISUALIZER_STATE_UNINITIALIZED;
192
193    ret = Visualizer_init(pContext);
194    if (ret < 0) {
195        LOGW("VisualizerLib_Create() init failed");
196        delete pContext;
197        return ret;
198    }
199
200    *pHandle = (effect_handle_t)pContext;
201
202    pContext->mState = VISUALIZER_STATE_INITIALIZED;
203
204    LOGV("VisualizerLib_Create %p", pContext);
205
206    return 0;
207
208}
209
210int VisualizerLib_Release(effect_handle_t handle) {
211    VisualizerContext * pContext = (VisualizerContext *)handle;
212
213    LOGV("VisualizerLib_Release %p", handle);
214    if (pContext == NULL) {
215        return -EINVAL;
216    }
217    pContext->mState = VISUALIZER_STATE_UNINITIALIZED;
218    delete pContext;
219
220    return 0;
221}
222
223int VisualizerLib_GetDescriptor(effect_uuid_t       *uuid,
224                                effect_descriptor_t *pDescriptor) {
225
226    if (pDescriptor == NULL || uuid == NULL){
227        LOGV("VisualizerLib_GetDescriptor() called with NULL pointer");
228        return -EINVAL;
229    }
230
231    if (memcmp(uuid, &gVisualizerDescriptor.uuid, sizeof(effect_uuid_t)) == 0) {
232        memcpy(pDescriptor, &gVisualizerDescriptor, sizeof(effect_descriptor_t));
233        return 0;
234    }
235
236    return  -EINVAL;
237} /* end VisualizerLib_GetDescriptor */
238
239//
240//--- Effect Control Interface Implementation
241//
242
243static inline int16_t clamp16(int32_t sample)
244{
245    if ((sample>>15) ^ (sample>>31))
246        sample = 0x7FFF ^ (sample>>31);
247    return sample;
248}
249
250int Visualizer_process(
251        effect_handle_t self,audio_buffer_t *inBuffer, audio_buffer_t *outBuffer)
252{
253    VisualizerContext * pContext = (VisualizerContext *)self;
254
255    if (pContext == NULL) {
256        return -EINVAL;
257    }
258
259    if (inBuffer == NULL || inBuffer->raw == NULL ||
260        outBuffer == NULL || outBuffer->raw == NULL ||
261        inBuffer->frameCount != outBuffer->frameCount ||
262        inBuffer->frameCount == 0) {
263        return -EINVAL;
264    }
265
266    // all code below assumes stereo 16 bit PCM output and input
267
268    // derive capture scaling factor from peak value in current buffer
269    // this gives more interesting captures for display.
270    int32_t shift = 32;
271    int len = inBuffer->frameCount * 2;
272    for (int i = 0; i < len; i++) {
273        int32_t smp = inBuffer->s16[i];
274        if (smp < 0) smp = -smp - 1; // take care to keep the max negative in range
275        int32_t clz = __builtin_clz(smp);
276        if (shift > clz) shift = clz;
277    }
278    // A maximum amplitude signal will have 17 leading zeros, which we want to
279    // translate to a shift of 8 (for converting 16 bit to 8 bit)
280     shift = 25 - shift;
281    // Never scale by less than 8 to avoid returning unaltered PCM signal.
282    if (shift < 3) {
283        shift = 3;
284    }
285    // add one to combine the division by 2 needed after summing left and right channels below
286    shift++;
287
288    uint32_t captIdx;
289    uint32_t inIdx;
290    uint8_t *buf = pContext->mCaptureBuf[pContext->mCurrentBuf];
291    for (inIdx = 0, captIdx = pContext->mCaptureIdx;
292         inIdx < inBuffer->frameCount && captIdx < pContext->mCaptureSize;
293         inIdx++, captIdx++) {
294        int32_t smp = inBuffer->s16[2 * inIdx] + inBuffer->s16[2 * inIdx + 1];
295        smp = smp >> shift;
296        buf[captIdx] = ((uint8_t)smp)^0x80;
297    }
298    pContext->mCaptureIdx = captIdx;
299
300    // go to next buffer when buffer full
301    if (pContext->mCaptureIdx == pContext->mCaptureSize) {
302        pContext->mCurrentBuf ^= 1;
303        pContext->mCaptureIdx = 0;
304    }
305
306    if (inBuffer->raw != outBuffer->raw) {
307        if (pContext->mConfig.outputCfg.accessMode == EFFECT_BUFFER_ACCESS_ACCUMULATE) {
308            for (size_t i = 0; i < outBuffer->frameCount*2; i++) {
309                outBuffer->s16[i] = clamp16(outBuffer->s16[i] + inBuffer->s16[i]);
310            }
311        } else {
312            memcpy(outBuffer->raw, inBuffer->raw, outBuffer->frameCount * 2 * sizeof(int16_t));
313        }
314    }
315    if (pContext->mState != VISUALIZER_STATE_ACTIVE) {
316        return -ENODATA;
317    }
318    return 0;
319}   // end Visualizer_process
320
321int Visualizer_command(effect_handle_t self, uint32_t cmdCode, uint32_t cmdSize,
322        void *pCmdData, uint32_t *replySize, void *pReplyData) {
323
324    VisualizerContext * pContext = (VisualizerContext *)self;
325    int retsize;
326
327    if (pContext == NULL || pContext->mState == VISUALIZER_STATE_UNINITIALIZED) {
328        return -EINVAL;
329    }
330
331//    LOGV("Visualizer_command command %d cmdSize %d",cmdCode, cmdSize);
332
333    switch (cmdCode) {
334    case EFFECT_CMD_INIT:
335        if (pReplyData == NULL || *replySize != sizeof(int)) {
336            return -EINVAL;
337        }
338        *(int *) pReplyData = Visualizer_init(pContext);
339        break;
340    case EFFECT_CMD_CONFIGURE:
341        if (pCmdData == NULL || cmdSize != sizeof(effect_config_t)
342                || pReplyData == NULL || *replySize != sizeof(int)) {
343            return -EINVAL;
344        }
345        *(int *) pReplyData = Visualizer_configure(pContext,
346                (effect_config_t *) pCmdData);
347        break;
348    case EFFECT_CMD_RESET:
349        Visualizer_reset(pContext);
350        break;
351    case EFFECT_CMD_ENABLE:
352        if (pReplyData == NULL || *replySize != sizeof(int)) {
353            return -EINVAL;
354        }
355        if (pContext->mState != VISUALIZER_STATE_INITIALIZED) {
356            return -ENOSYS;
357        }
358        pContext->mState = VISUALIZER_STATE_ACTIVE;
359        LOGV("EFFECT_CMD_ENABLE() OK");
360        *(int *)pReplyData = 0;
361        break;
362    case EFFECT_CMD_DISABLE:
363        if (pReplyData == NULL || *replySize != sizeof(int)) {
364            return -EINVAL;
365        }
366        if (pContext->mState != VISUALIZER_STATE_ACTIVE) {
367            return -ENOSYS;
368        }
369        pContext->mState = VISUALIZER_STATE_INITIALIZED;
370        LOGV("EFFECT_CMD_DISABLE() OK");
371        *(int *)pReplyData = 0;
372        break;
373    case EFFECT_CMD_GET_PARAM: {
374        if (pCmdData == NULL ||
375            cmdSize != (int)(sizeof(effect_param_t) + sizeof(uint32_t)) ||
376            pReplyData == NULL ||
377            *replySize < (int)(sizeof(effect_param_t) + sizeof(uint32_t) + sizeof(uint32_t))) {
378            return -EINVAL;
379        }
380        memcpy(pReplyData, pCmdData, sizeof(effect_param_t) + sizeof(uint32_t));
381        effect_param_t *p = (effect_param_t *)pReplyData;
382        p->status = 0;
383        *replySize = sizeof(effect_param_t) + sizeof(uint32_t);
384        if (p->psize != sizeof(uint32_t) ||
385            *(uint32_t *)p->data != VISUALIZER_PARAM_CAPTURE_SIZE) {
386            p->status = -EINVAL;
387            break;
388        }
389        LOGV("get mCaptureSize = %d", pContext->mCaptureSize);
390        *((uint32_t *)p->data + 1) = pContext->mCaptureSize;
391        p->vsize = sizeof(uint32_t);
392        *replySize += sizeof(uint32_t);
393        } break;
394    case EFFECT_CMD_SET_PARAM: {
395        if (pCmdData == NULL ||
396            cmdSize != (int)(sizeof(effect_param_t) + sizeof(uint32_t) + sizeof(uint32_t)) ||
397            pReplyData == NULL || *replySize != sizeof(int32_t)) {
398            return -EINVAL;
399        }
400        *(int32_t *)pReplyData = 0;
401        effect_param_t *p = (effect_param_t *)pCmdData;
402        if (p->psize != sizeof(uint32_t) ||
403            p->vsize != sizeof(uint32_t) ||
404            *(uint32_t *)p->data != VISUALIZER_PARAM_CAPTURE_SIZE) {
405            *(int32_t *)pReplyData = -EINVAL;
406            break;;
407        }
408        pContext->mCaptureSize = *((uint32_t *)p->data + 1);
409        LOGV("set mCaptureSize = %d", pContext->mCaptureSize);
410        } break;
411    case EFFECT_CMD_SET_DEVICE:
412    case EFFECT_CMD_SET_VOLUME:
413    case EFFECT_CMD_SET_AUDIO_MODE:
414        break;
415
416
417    case VISUALIZER_CMD_CAPTURE:
418        if (pReplyData == NULL || *replySize != pContext->mCaptureSize) {
419            LOGV("VISUALIZER_CMD_CAPTURE() error *replySize %d pContext->mCaptureSize %d",
420                    *replySize, pContext->mCaptureSize);
421            return -EINVAL;
422        }
423        if (pContext->mState == VISUALIZER_STATE_ACTIVE) {
424            memcpy(pReplyData,
425                   pContext->mCaptureBuf[pContext->mCurrentBuf ^ 1],
426                   pContext->mCaptureSize);
427            // if audio framework has stopped playing audio although the effect is still
428            // active we must clear the capture buffer to return silence
429            if (pContext->mLastBuf == pContext->mCurrentBuf) {
430                if (pContext->mStallCount < MAX_STALL_COUNT) {
431                    if (++pContext->mStallCount == MAX_STALL_COUNT) {
432                        memset(pContext->mCaptureBuf[pContext->mCurrentBuf ^ 1],
433                                0x80,
434                                pContext->mCaptureSize);
435                    }
436                }
437            } else {
438                pContext->mStallCount = 0;
439            }
440            pContext->mLastBuf = pContext->mCurrentBuf;
441        } else {
442            memset(pReplyData, 0x80, pContext->mCaptureSize);
443        }
444
445        break;
446
447    default:
448        LOGW("Visualizer_command invalid command %d",cmdCode);
449        return -EINVAL;
450    }
451
452    return 0;
453}
454
455/* Effect Control Interface Implementation: get_descriptor */
456int Visualizer_getDescriptor(effect_handle_t   self,
457                                    effect_descriptor_t *pDescriptor)
458{
459    VisualizerContext * pContext = (VisualizerContext *) self;
460
461    if (pContext == NULL || pDescriptor == NULL) {
462        LOGV("Visualizer_getDescriptor() invalid param");
463        return -EINVAL;
464    }
465
466    memcpy(pDescriptor, &gVisualizerDescriptor, sizeof(effect_descriptor_t));
467
468    return 0;
469}   /* end Visualizer_getDescriptor */
470
471// effect_handle_t interface implementation for visualizer effect
472const struct effect_interface_s gVisualizerInterface = {
473        Visualizer_process,
474        Visualizer_command,
475        Visualizer_getDescriptor,
476        NULL,
477};
478
479
480audio_effect_library_t AUDIO_EFFECT_LIBRARY_INFO_SYM = {
481    tag : AUDIO_EFFECT_LIBRARY_TAG,
482    version : EFFECT_LIBRARY_API_VERSION,
483    name : "Visualizer Library",
484    implementor : "The Android Open Source Project",
485    query_num_effects : VisualizerLib_QueryNumberEffects,
486    query_effect : VisualizerLib_QueryEffect,
487    create_effect : VisualizerLib_Create,
488    release_effect : VisualizerLib_Release,
489    get_descriptor : VisualizerLib_GetDescriptor,
490};
491
492}; // extern "C"
493