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 "EffectVisualizer"
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 <time.h>
25#include <audio_effects/effect_visualizer.h>
26
27
28extern "C" {
29
30// effect_handle_t interface implementation for visualizer effect
31extern const struct effect_interface_s gVisualizerInterface;
32
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
37        EFFECT_CONTROL_API_VERSION,
38        (EFFECT_FLAG_TYPE_INSERT | EFFECT_FLAG_INSERT_FIRST),
39        0, // TODO
40        1,
41        "Visualizer",
42        "The Android Open Source Project",
43};
44
45enum visualizer_state_e {
46    VISUALIZER_STATE_UNINITIALIZED,
47    VISUALIZER_STATE_INITIALIZED,
48    VISUALIZER_STATE_ACTIVE,
49};
50
51// maximum time since last capture buffer update before resetting capture buffer. This means
52// that the framework has stopped playing audio and we must start returning silence
53#define MAX_STALL_TIME_MS 1000
54
55#define CAPTURE_BUF_SIZE 65536 // "64k should be enough for everyone"
56
57struct VisualizerContext {
58    const struct effect_interface_s *mItfe;
59    effect_config_t mConfig;
60    uint32_t mCaptureIdx;
61    uint32_t mCaptureSize;
62    uint32_t mScalingMode;
63    uint8_t mState;
64    uint8_t mLastCaptureIdx;
65    uint32_t mLatency;
66    struct timespec mBufferUpdateTime;
67    uint8_t mCaptureBuf[CAPTURE_BUF_SIZE];
68};
69
70//
71//--- Local functions
72//
73
74void Visualizer_reset(VisualizerContext *pContext)
75{
76    pContext->mCaptureIdx = 0;
77    pContext->mLastCaptureIdx = 0;
78    pContext->mBufferUpdateTime.tv_sec = 0;
79    pContext->mLatency = 0;
80    memset(pContext->mCaptureBuf, 0x80, CAPTURE_BUF_SIZE);
81}
82
83//----------------------------------------------------------------------------
84// Visualizer_setConfig()
85//----------------------------------------------------------------------------
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
97int Visualizer_setConfig(VisualizerContext *pContext, effect_config_t *pConfig)
98{
99    ALOGV("Visualizer_setConfig start");
100
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;
104    if (pConfig->inputCfg.channels != AUDIO_CHANNEL_OUT_STEREO) return -EINVAL;
105    if (pConfig->outputCfg.accessMode != EFFECT_BUFFER_ACCESS_WRITE &&
106            pConfig->outputCfg.accessMode != EFFECT_BUFFER_ACCESS_ACCUMULATE) return -EINVAL;
107    if (pConfig->inputCfg.format != AUDIO_FORMAT_PCM_16_BIT) return -EINVAL;
108
109    pContext->mConfig = *pConfig;
110
111    Visualizer_reset(pContext);
112
113    return 0;
114}
115
116
117//----------------------------------------------------------------------------
118// 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{
133    *pConfig = pContext->mConfig;
134}
135
136
137//----------------------------------------------------------------------------
138// 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;
152    pContext->mConfig.inputCfg.channels = AUDIO_CHANNEL_OUT_STEREO;
153    pContext->mConfig.inputCfg.format = AUDIO_FORMAT_PCM_16_BIT;
154    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;
160    pContext->mConfig.outputCfg.channels = AUDIO_CHANNEL_OUT_STEREO;
161    pContext->mConfig.outputCfg.format = AUDIO_FORMAT_PCM_16_BIT;
162    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;
169    pContext->mScalingMode = VISUALIZER_SCALING_MODE_NORMALIZED;
170
171    Visualizer_setConfig(pContext, &pContext->mConfig);
172
173    return 0;
174}
175
176//
177//--- Effect Library Interface Implementation
178//
179
180int VisualizerLib_QueryNumberEffects(uint32_t *pNumEffects) {
181    *pNumEffects = 1;
182    return 0;
183}
184
185int VisualizerLib_QueryEffect(uint32_t index,
186                              effect_descriptor_t *pDescriptor) {
187    if (pDescriptor == NULL) {
188        return -EINVAL;
189    }
190    if (index > 0) {
191        return -EINVAL;
192    }
193    *pDescriptor = gVisualizerDescriptor;
194    return 0;
195}
196
197int VisualizerLib_Create(const effect_uuid_t *uuid,
198                         int32_t sessionId,
199                         int32_t ioId,
200                         effect_handle_t *pHandle) {
201    int ret;
202    int i;
203
204    if (pHandle == NULL || uuid == NULL) {
205        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) {
219        ALOGW("VisualizerLib_Create() init failed");
220        delete pContext;
221        return ret;
222    }
223
224    *pHandle = (effect_handle_t)pContext;
225
226    pContext->mState = VISUALIZER_STATE_INITIALIZED;
227
228    ALOGV("VisualizerLib_Create %p", pContext);
229
230    return 0;
231
232}
233
234int VisualizerLib_Release(effect_handle_t handle) {
235    VisualizerContext * pContext = (VisualizerContext *)handle;
236
237    ALOGV("VisualizerLib_Release %p", handle);
238    if (pContext == NULL) {
239        return -EINVAL;
240    }
241    pContext->mState = VISUALIZER_STATE_UNINITIALIZED;
242    delete pContext;
243
244    return 0;
245}
246
247int VisualizerLib_GetDescriptor(const effect_uuid_t *uuid,
248                                effect_descriptor_t *pDescriptor) {
249
250    if (pDescriptor == NULL || uuid == NULL){
251        ALOGV("VisualizerLib_GetDescriptor() called with NULL pointer");
252        return -EINVAL;
253    }
254
255    if (memcmp(uuid, &gVisualizerDescriptor.uuid, sizeof(effect_uuid_t)) == 0) {
256        *pDescriptor = gVisualizerDescriptor;
257        return 0;
258    }
259
260    return  -EINVAL;
261} /* end VisualizerLib_GetDescriptor */
262
263//
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
274int Visualizer_process(
275        effect_handle_t self,audio_buffer_t *inBuffer, audio_buffer_t *outBuffer)
276{
277    VisualizerContext * pContext = (VisualizerContext *)self;
278
279    if (pContext == NULL) {
280        return -EINVAL;
281    }
282
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
291    int32_t shift;
292
293    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;
316    }
317
318    uint32_t captIdx;
319    uint32_t inIdx;
320    uint8_t *buf = pContext->mCaptureBuf;
321    for (inIdx = 0, captIdx = pContext->mCaptureIdx;
322         inIdx < inBuffer->frameCount;
323         inIdx++, captIdx++) {
324        if (captIdx >= CAPTURE_BUF_SIZE) {
325            // wrap around
326            captIdx = 0;
327        }
328        int32_t smp = inBuffer->s16[2 * inIdx] + inBuffer->s16[2 * inIdx + 1];
329        smp = smp >> shift;
330        buf[captIdx] = ((uint8_t)smp)^0x80;
331    }
332
333    // XXX the following two should really be atomic, though it probably doesn't
334    // matter much for visualization purposes
335    pContext->mCaptureIdx = captIdx;
336    // update last buffer update time stamp
337    if (clock_gettime(CLOCK_MONOTONIC, &pContext->mBufferUpdateTime) < 0) {
338        pContext->mBufferUpdateTime.tv_sec = 0;
339    }
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    }
350    if (pContext->mState != VISUALIZER_STATE_ACTIVE) {
351        return -ENODATA;
352    }
353    return 0;
354}   // end Visualizer_process
355
356int Visualizer_command(effect_handle_t self, uint32_t cmdCode, uint32_t cmdSize,
357        void *pCmdData, uint32_t *replySize, void *pReplyData) {
358
359    VisualizerContext * pContext = (VisualizerContext *)self;
360    int retsize;
361
362    if (pContext == NULL || pContext->mState == VISUALIZER_STATE_UNINITIALIZED) {
363        return -EINVAL;
364    }
365
366//    ALOGV("Visualizer_command command %d cmdSize %d",cmdCode, cmdSize);
367
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;
375    case EFFECT_CMD_SET_CONFIG:
376        if (pCmdData == NULL || cmdSize != sizeof(effect_config_t)
377                || pReplyData == NULL || *replySize != sizeof(int)) {
378            return -EINVAL;
379        }
380        *(int *) pReplyData = Visualizer_setConfig(pContext,
381                (effect_config_t *) pCmdData);
382        break;
383    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;
390    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;
401        ALOGV("EFFECT_CMD_ENABLE() OK");
402        *(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;
412        ALOGV("EFFECT_CMD_DISABLE() OK");
413        *(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);
426        if (p->psize != sizeof(uint32_t)) {
427            p->status = -EINVAL;
428            break;
429        }
430        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        }
446        } 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;
455        if (p->psize != sizeof(uint32_t) || p->vsize != sizeof(uint32_t)) {
456            *(int32_t *)pReplyData = -EINVAL;
457            break;
458        }
459        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;
468        case VISUALIZER_PARAM_LATENCY:
469            pContext->mLatency = *((uint32_t *)p->data + 1);
470            ALOGV("set mLatency = %d", pContext->mLatency);
471            break;
472        default:
473            *(int32_t *)pReplyData = -EINVAL;
474        }
475        } break;
476    case EFFECT_CMD_SET_DEVICE:
477    case EFFECT_CMD_SET_VOLUME:
478    case EFFECT_CMD_SET_AUDIO_MODE:
479        break;
480
481
482    case VISUALIZER_CMD_CAPTURE:
483        if (pReplyData == NULL || *replySize != pContext->mCaptureSize) {
484            ALOGV("VISUALIZER_CMD_CAPTURE() error *replySize %d pContext->mCaptureSize %d",
485                    *replySize, pContext->mCaptureSize);
486            return -EINVAL;
487        }
488        if (pContext->mState == VISUALIZER_STATE_ACTIVE) {
489            int32_t latencyMs = pContext->mLatency;
490            uint32_t deltaMs = 0;
491            if (pContext->mBufferUpdateTime.tv_sec != 0) {
492                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                    }
500                    deltaMs = secs * 1000 + nsec / 1000000;
501                    latencyMs -= deltaMs;
502                    if (latencyMs < 0) {
503                        latencyMs = 0;
504                    }
505                }
506            }
507            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;
539        } else {
540            memset(pReplyData, 0x80, pContext->mCaptureSize);
541        }
542
543        break;
544
545    default:
546        ALOGW("Visualizer_command invalid command %d",cmdCode);
547        return -EINVAL;
548    }
549
550    return 0;
551}
552
553/* 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) {
560        ALOGV("Visualizer_getDescriptor() invalid param");
561        return -EINVAL;
562    }
563
564    *pDescriptor = gVisualizerDescriptor;
565
566    return 0;
567}   /* end Visualizer_getDescriptor */
568
569// effect_handle_t interface implementation for visualizer effect
570const struct effect_interface_s gVisualizerInterface = {
571        Visualizer_process,
572        Visualizer_command,
573        Visualizer_getDescriptor,
574        NULL,
575};
576
577
578audio_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"
591