Home | History | Annotate | Download | only in visualizer
      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 <log/log.h>
     20 #include <assert.h>
     21 #include <inttypes.h>
     22 #include <stdlib.h>
     23 #include <string.h>
     24 #include <new>
     25 #include <time.h>
     26 #include <math.h>
     27 #include <audio_effects/effect_visualizer.h>
     28 
     29 
     30 extern "C" {
     31 
     32 // effect_handle_t interface implementation for visualizer effect
     33 extern const struct effect_interface_s gVisualizerInterface;
     34 
     35 // Google Visualizer UUID: d069d9e0-8329-11df-9168-0002a5d5c51b
     36 const effect_descriptor_t gVisualizerDescriptor = {
     37         {0xe46b26a0, 0xdddd, 0x11db, 0x8afd, {0x00, 0x02, 0xa5, 0xd5, 0xc5, 0x1b}}, // type
     38         {0xd069d9e0, 0x8329, 0x11df, 0x9168, {0x00, 0x02, 0xa5, 0xd5, 0xc5, 0x1b}}, // uuid
     39         EFFECT_CONTROL_API_VERSION,
     40         (EFFECT_FLAG_TYPE_INSERT | EFFECT_FLAG_INSERT_FIRST),
     41         0, // TODO
     42         1,
     43         "Visualizer",
     44         "The Android Open Source Project",
     45 };
     46 
     47 enum visualizer_state_e {
     48     VISUALIZER_STATE_UNINITIALIZED,
     49     VISUALIZER_STATE_INITIALIZED,
     50     VISUALIZER_STATE_ACTIVE,
     51 };
     52 
     53 // maximum time since last capture buffer update before resetting capture buffer. This means
     54 // that the framework has stopped playing audio and we must start returning silence
     55 #define MAX_STALL_TIME_MS 1000
     56 
     57 #define CAPTURE_BUF_SIZE 65536 // "64k should be enough for everyone"
     58 
     59 #define DISCARD_MEASUREMENTS_TIME_MS 2000 // discard measurements older than this number of ms
     60 
     61 // maximum number of buffers for which we keep track of the measurements
     62 #define MEASUREMENT_WINDOW_MAX_SIZE_IN_BUFFERS 25 // note: buffer index is stored in uint8_t
     63 
     64 
     65 struct BufferStats {
     66     bool mIsValid;
     67     uint16_t mPeakU16; // the positive peak of the absolute value of the samples in a buffer
     68     float mRmsSquared; // the average square of the samples in a buffer
     69 };
     70 
     71 struct VisualizerContext {
     72     const struct effect_interface_s *mItfe;
     73     effect_config_t mConfig;
     74     uint32_t mCaptureIdx;
     75     uint32_t mCaptureSize;
     76     uint32_t mScalingMode;
     77     uint8_t mState;
     78     uint32_t mLastCaptureIdx;
     79     uint32_t mLatency;
     80     struct timespec mBufferUpdateTime;
     81     uint8_t mCaptureBuf[CAPTURE_BUF_SIZE];
     82     // for measurements
     83     uint8_t mChannelCount; // to avoid recomputing it every time a buffer is processed
     84     uint32_t mMeasurementMode;
     85     uint8_t mMeasurementWindowSizeInBuffers;
     86     uint8_t mMeasurementBufferIdx;
     87     BufferStats mPastMeasurements[MEASUREMENT_WINDOW_MAX_SIZE_IN_BUFFERS];
     88 };
     89 
     90 //
     91 //--- Local functions
     92 //
     93 uint32_t Visualizer_getDeltaTimeMsFromUpdatedTime(VisualizerContext* pContext) {
     94     uint32_t deltaMs = 0;
     95     if (pContext->mBufferUpdateTime.tv_sec != 0) {
     96         struct timespec ts;
     97         if (clock_gettime(CLOCK_MONOTONIC, &ts) == 0) {
     98             time_t secs = ts.tv_sec - pContext->mBufferUpdateTime.tv_sec;
     99             long nsec = ts.tv_nsec - pContext->mBufferUpdateTime.tv_nsec;
    100             if (nsec < 0) {
    101                 --secs;
    102                 nsec += 1000000000;
    103             }
    104             deltaMs = secs * 1000 + nsec / 1000000;
    105         }
    106     }
    107     return deltaMs;
    108 }
    109 
    110 
    111 void Visualizer_reset(VisualizerContext *pContext)
    112 {
    113     pContext->mCaptureIdx = 0;
    114     pContext->mLastCaptureIdx = 0;
    115     pContext->mBufferUpdateTime.tv_sec = 0;
    116     pContext->mLatency = 0;
    117     memset(pContext->mCaptureBuf, 0x80, CAPTURE_BUF_SIZE);
    118 }
    119 
    120 //----------------------------------------------------------------------------
    121 // Visualizer_setConfig()
    122 //----------------------------------------------------------------------------
    123 // Purpose: Set input and output audio configuration.
    124 //
    125 // Inputs:
    126 //  pContext:   effect engine context
    127 //  pConfig:    pointer to effect_config_t structure holding input and output
    128 //      configuration parameters
    129 //
    130 // Outputs:
    131 //
    132 //----------------------------------------------------------------------------
    133 
    134 int Visualizer_setConfig(VisualizerContext *pContext, effect_config_t *pConfig)
    135 {
    136     ALOGV("Visualizer_setConfig start");
    137 
    138     if (pConfig->inputCfg.samplingRate != pConfig->outputCfg.samplingRate) return -EINVAL;
    139     if (pConfig->inputCfg.channels != pConfig->outputCfg.channels) return -EINVAL;
    140     if (pConfig->inputCfg.format != pConfig->outputCfg.format) return -EINVAL;
    141     if (pConfig->inputCfg.channels != AUDIO_CHANNEL_OUT_STEREO) return -EINVAL;
    142     if (pConfig->outputCfg.accessMode != EFFECT_BUFFER_ACCESS_WRITE &&
    143             pConfig->outputCfg.accessMode != EFFECT_BUFFER_ACCESS_ACCUMULATE) return -EINVAL;
    144     if (pConfig->inputCfg.format != AUDIO_FORMAT_PCM_16_BIT) return -EINVAL;
    145 
    146     pContext->mConfig = *pConfig;
    147 
    148     Visualizer_reset(pContext);
    149 
    150     return 0;
    151 }
    152 
    153 
    154 //----------------------------------------------------------------------------
    155 // Visualizer_getConfig()
    156 //----------------------------------------------------------------------------
    157 // Purpose: Get input and output audio configuration.
    158 //
    159 // Inputs:
    160 //  pContext:   effect engine context
    161 //  pConfig:    pointer to effect_config_t structure holding input and output
    162 //      configuration parameters
    163 //
    164 // Outputs:
    165 //
    166 //----------------------------------------------------------------------------
    167 
    168 void Visualizer_getConfig(VisualizerContext *pContext, effect_config_t *pConfig)
    169 {
    170     *pConfig = pContext->mConfig;
    171 }
    172 
    173 
    174 //----------------------------------------------------------------------------
    175 // Visualizer_init()
    176 //----------------------------------------------------------------------------
    177 // Purpose: Initialize engine with default configuration.
    178 //
    179 // Inputs:
    180 //  pContext:   effect engine context
    181 //
    182 // Outputs:
    183 //
    184 //----------------------------------------------------------------------------
    185 
    186 int Visualizer_init(VisualizerContext *pContext)
    187 {
    188     pContext->mConfig.inputCfg.accessMode = EFFECT_BUFFER_ACCESS_READ;
    189     pContext->mConfig.inputCfg.channels = AUDIO_CHANNEL_OUT_STEREO;
    190     pContext->mConfig.inputCfg.format = AUDIO_FORMAT_PCM_16_BIT;
    191     pContext->mConfig.inputCfg.samplingRate = 44100;
    192     pContext->mConfig.inputCfg.bufferProvider.getBuffer = NULL;
    193     pContext->mConfig.inputCfg.bufferProvider.releaseBuffer = NULL;
    194     pContext->mConfig.inputCfg.bufferProvider.cookie = NULL;
    195     pContext->mConfig.inputCfg.mask = EFFECT_CONFIG_ALL;
    196     pContext->mConfig.outputCfg.accessMode = EFFECT_BUFFER_ACCESS_ACCUMULATE;
    197     pContext->mConfig.outputCfg.channels = AUDIO_CHANNEL_OUT_STEREO;
    198     pContext->mConfig.outputCfg.format = AUDIO_FORMAT_PCM_16_BIT;
    199     pContext->mConfig.outputCfg.samplingRate = 44100;
    200     pContext->mConfig.outputCfg.bufferProvider.getBuffer = NULL;
    201     pContext->mConfig.outputCfg.bufferProvider.releaseBuffer = NULL;
    202     pContext->mConfig.outputCfg.bufferProvider.cookie = NULL;
    203     pContext->mConfig.outputCfg.mask = EFFECT_CONFIG_ALL;
    204 
    205     // visualization initialization
    206     pContext->mCaptureSize = VISUALIZER_CAPTURE_SIZE_MAX;
    207     pContext->mScalingMode = VISUALIZER_SCALING_MODE_NORMALIZED;
    208 
    209     // measurement initialization
    210     pContext->mChannelCount =
    211             audio_channel_count_from_out_mask(pContext->mConfig.inputCfg.channels);
    212     pContext->mMeasurementMode = MEASUREMENT_MODE_NONE;
    213     pContext->mMeasurementWindowSizeInBuffers = MEASUREMENT_WINDOW_MAX_SIZE_IN_BUFFERS;
    214     pContext->mMeasurementBufferIdx = 0;
    215     for (uint32_t i=0 ; i<pContext->mMeasurementWindowSizeInBuffers ; i++) {
    216         pContext->mPastMeasurements[i].mIsValid = false;
    217         pContext->mPastMeasurements[i].mPeakU16 = 0;
    218         pContext->mPastMeasurements[i].mRmsSquared = 0;
    219     }
    220 
    221     Visualizer_setConfig(pContext, &pContext->mConfig);
    222 
    223     return 0;
    224 }
    225 
    226 //
    227 //--- Effect Library Interface Implementation
    228 //
    229 
    230 int VisualizerLib_Create(const effect_uuid_t *uuid,
    231                          int32_t /*sessionId*/,
    232                          int32_t /*ioId*/,
    233                          effect_handle_t *pHandle) {
    234     int ret;
    235     int i;
    236 
    237     if (pHandle == NULL || uuid == NULL) {
    238         return -EINVAL;
    239     }
    240 
    241     if (memcmp(uuid, &gVisualizerDescriptor.uuid, sizeof(effect_uuid_t)) != 0) {
    242         return -EINVAL;
    243     }
    244 
    245     VisualizerContext *pContext = new VisualizerContext;
    246 
    247     pContext->mItfe = &gVisualizerInterface;
    248     pContext->mState = VISUALIZER_STATE_UNINITIALIZED;
    249 
    250     ret = Visualizer_init(pContext);
    251     if (ret < 0) {
    252         ALOGW("VisualizerLib_Create() init failed");
    253         delete pContext;
    254         return ret;
    255     }
    256 
    257     *pHandle = (effect_handle_t)pContext;
    258 
    259     pContext->mState = VISUALIZER_STATE_INITIALIZED;
    260 
    261     ALOGV("VisualizerLib_Create %p", pContext);
    262 
    263     return 0;
    264 
    265 }
    266 
    267 int VisualizerLib_Release(effect_handle_t handle) {
    268     VisualizerContext * pContext = (VisualizerContext *)handle;
    269 
    270     ALOGV("VisualizerLib_Release %p", handle);
    271     if (pContext == NULL) {
    272         return -EINVAL;
    273     }
    274     pContext->mState = VISUALIZER_STATE_UNINITIALIZED;
    275     delete pContext;
    276 
    277     return 0;
    278 }
    279 
    280 int VisualizerLib_GetDescriptor(const effect_uuid_t *uuid,
    281                                 effect_descriptor_t *pDescriptor) {
    282 
    283     if (pDescriptor == NULL || uuid == NULL){
    284         ALOGV("VisualizerLib_GetDescriptor() called with NULL pointer");
    285         return -EINVAL;
    286     }
    287 
    288     if (memcmp(uuid, &gVisualizerDescriptor.uuid, sizeof(effect_uuid_t)) == 0) {
    289         *pDescriptor = gVisualizerDescriptor;
    290         return 0;
    291     }
    292 
    293     return  -EINVAL;
    294 } /* end VisualizerLib_GetDescriptor */
    295 
    296 //
    297 //--- Effect Control Interface Implementation
    298 //
    299 
    300 static inline int16_t clamp16(int32_t sample)
    301 {
    302     if ((sample>>15) ^ (sample>>31))
    303         sample = 0x7FFF ^ (sample>>31);
    304     return sample;
    305 }
    306 
    307 int Visualizer_process(
    308         effect_handle_t self,audio_buffer_t *inBuffer, audio_buffer_t *outBuffer)
    309 {
    310     VisualizerContext * pContext = (VisualizerContext *)self;
    311 
    312     if (pContext == NULL) {
    313         return -EINVAL;
    314     }
    315 
    316     if (inBuffer == NULL || inBuffer->raw == NULL ||
    317         outBuffer == NULL || outBuffer->raw == NULL ||
    318         inBuffer->frameCount != outBuffer->frameCount ||
    319         inBuffer->frameCount == 0) {
    320         return -EINVAL;
    321     }
    322 
    323     // perform measurements if needed
    324     if (pContext->mMeasurementMode & MEASUREMENT_MODE_PEAK_RMS) {
    325         // find the peak and RMS squared for the new buffer
    326         uint32_t inIdx;
    327         int16_t maxSample = 0;
    328         float rmsSqAcc = 0;
    329         for (inIdx = 0 ; inIdx < inBuffer->frameCount * pContext->mChannelCount ; inIdx++) {
    330             if (inBuffer->s16[inIdx] > maxSample) {
    331                 maxSample = inBuffer->s16[inIdx];
    332             } else if (-inBuffer->s16[inIdx] > maxSample) {
    333                 maxSample = -inBuffer->s16[inIdx];
    334             }
    335             rmsSqAcc += (inBuffer->s16[inIdx] * inBuffer->s16[inIdx]);
    336         }
    337         // store the measurement
    338         pContext->mPastMeasurements[pContext->mMeasurementBufferIdx].mPeakU16 = (uint16_t)maxSample;
    339         pContext->mPastMeasurements[pContext->mMeasurementBufferIdx].mRmsSquared =
    340                 rmsSqAcc / (inBuffer->frameCount * pContext->mChannelCount);
    341         pContext->mPastMeasurements[pContext->mMeasurementBufferIdx].mIsValid = true;
    342         if (++pContext->mMeasurementBufferIdx >= pContext->mMeasurementWindowSizeInBuffers) {
    343             pContext->mMeasurementBufferIdx = 0;
    344         }
    345     }
    346 
    347     // all code below assumes stereo 16 bit PCM output and input
    348     int32_t shift;
    349 
    350     if (pContext->mScalingMode == VISUALIZER_SCALING_MODE_NORMALIZED) {
    351         // derive capture scaling factor from peak value in current buffer
    352         // this gives more interesting captures for display.
    353         shift = 32;
    354         int len = inBuffer->frameCount * 2;
    355         for (int i = 0; i < len; i++) {
    356             int32_t smp = inBuffer->s16[i];
    357             if (smp < 0) smp = -smp - 1; // take care to keep the max negative in range
    358             int32_t clz = __builtin_clz(smp);
    359             if (shift > clz) shift = clz;
    360         }
    361         // A maximum amplitude signal will have 17 leading zeros, which we want to
    362         // translate to a shift of 8 (for converting 16 bit to 8 bit)
    363         shift = 25 - shift;
    364         // Never scale by less than 8 to avoid returning unaltered PCM signal.
    365         if (shift < 3) {
    366             shift = 3;
    367         }
    368         // add one to combine the division by 2 needed after summing left and right channels below
    369         shift++;
    370     } else {
    371         assert(pContext->mScalingMode == VISUALIZER_SCALING_MODE_AS_PLAYED);
    372         shift = 9;
    373     }
    374 
    375     uint32_t captIdx;
    376     uint32_t inIdx;
    377     uint8_t *buf = pContext->mCaptureBuf;
    378     for (inIdx = 0, captIdx = pContext->mCaptureIdx;
    379          inIdx < inBuffer->frameCount;
    380          inIdx++, captIdx++) {
    381         if (captIdx >= CAPTURE_BUF_SIZE) {
    382             // wrap around
    383             captIdx = 0;
    384         }
    385         int32_t smp = inBuffer->s16[2 * inIdx] + inBuffer->s16[2 * inIdx + 1];
    386         smp = smp >> shift;
    387         buf[captIdx] = ((uint8_t)smp)^0x80;
    388     }
    389 
    390     // XXX the following two should really be atomic, though it probably doesn't
    391     // matter much for visualization purposes
    392     pContext->mCaptureIdx = captIdx;
    393     // update last buffer update time stamp
    394     if (clock_gettime(CLOCK_MONOTONIC, &pContext->mBufferUpdateTime) < 0) {
    395         pContext->mBufferUpdateTime.tv_sec = 0;
    396     }
    397 
    398     if (inBuffer->raw != outBuffer->raw) {
    399         if (pContext->mConfig.outputCfg.accessMode == EFFECT_BUFFER_ACCESS_ACCUMULATE) {
    400             for (size_t i = 0; i < outBuffer->frameCount*2; i++) {
    401                 outBuffer->s16[i] = clamp16(outBuffer->s16[i] + inBuffer->s16[i]);
    402             }
    403         } else {
    404             memcpy(outBuffer->raw, inBuffer->raw, outBuffer->frameCount * 2 * sizeof(int16_t));
    405         }
    406     }
    407     if (pContext->mState != VISUALIZER_STATE_ACTIVE) {
    408         return -ENODATA;
    409     }
    410     return 0;
    411 }   // end Visualizer_process
    412 
    413 int Visualizer_command(effect_handle_t self, uint32_t cmdCode, uint32_t cmdSize,
    414         void *pCmdData, uint32_t *replySize, void *pReplyData) {
    415 
    416     VisualizerContext * pContext = (VisualizerContext *)self;
    417     int retsize;
    418 
    419     if (pContext == NULL || pContext->mState == VISUALIZER_STATE_UNINITIALIZED) {
    420         return -EINVAL;
    421     }
    422 
    423 //    ALOGV("Visualizer_command command %" PRIu32 " cmdSize %" PRIu32, cmdCode, cmdSize);
    424 
    425     switch (cmdCode) {
    426     case EFFECT_CMD_INIT:
    427         if (pReplyData == NULL || *replySize != sizeof(int)) {
    428             return -EINVAL;
    429         }
    430         *(int *) pReplyData = Visualizer_init(pContext);
    431         break;
    432     case EFFECT_CMD_SET_CONFIG:
    433         if (pCmdData == NULL || cmdSize != sizeof(effect_config_t)
    434                 || pReplyData == NULL || *replySize != sizeof(int)) {
    435             return -EINVAL;
    436         }
    437         *(int *) pReplyData = Visualizer_setConfig(pContext,
    438                 (effect_config_t *) pCmdData);
    439         break;
    440     case EFFECT_CMD_GET_CONFIG:
    441         if (pReplyData == NULL ||
    442             *replySize != sizeof(effect_config_t)) {
    443             return -EINVAL;
    444         }
    445         Visualizer_getConfig(pContext, (effect_config_t *)pReplyData);
    446         break;
    447     case EFFECT_CMD_RESET:
    448         Visualizer_reset(pContext);
    449         break;
    450     case EFFECT_CMD_ENABLE:
    451         if (pReplyData == NULL || *replySize != sizeof(int)) {
    452             return -EINVAL;
    453         }
    454         if (pContext->mState != VISUALIZER_STATE_INITIALIZED) {
    455             return -ENOSYS;
    456         }
    457         pContext->mState = VISUALIZER_STATE_ACTIVE;
    458         ALOGV("EFFECT_CMD_ENABLE() OK");
    459         *(int *)pReplyData = 0;
    460         break;
    461     case EFFECT_CMD_DISABLE:
    462         if (pReplyData == NULL || *replySize != sizeof(int)) {
    463             return -EINVAL;
    464         }
    465         if (pContext->mState != VISUALIZER_STATE_ACTIVE) {
    466             return -ENOSYS;
    467         }
    468         pContext->mState = VISUALIZER_STATE_INITIALIZED;
    469         ALOGV("EFFECT_CMD_DISABLE() OK");
    470         *(int *)pReplyData = 0;
    471         break;
    472     case EFFECT_CMD_GET_PARAM: {
    473         if (pCmdData == NULL ||
    474             cmdSize != (int)(sizeof(effect_param_t) + sizeof(uint32_t)) ||
    475             pReplyData == NULL ||
    476             *replySize < (int)(sizeof(effect_param_t) + sizeof(uint32_t) + sizeof(uint32_t))) {
    477             return -EINVAL;
    478         }
    479         memcpy(pReplyData, pCmdData, sizeof(effect_param_t) + sizeof(uint32_t));
    480         effect_param_t *p = (effect_param_t *)pReplyData;
    481         p->status = 0;
    482         *replySize = sizeof(effect_param_t) + sizeof(uint32_t);
    483         if (p->psize != sizeof(uint32_t)) {
    484             p->status = -EINVAL;
    485             break;
    486         }
    487         switch (*(uint32_t *)p->data) {
    488         case VISUALIZER_PARAM_CAPTURE_SIZE:
    489             ALOGV("get mCaptureSize = %" PRIu32, pContext->mCaptureSize);
    490             *((uint32_t *)p->data + 1) = pContext->mCaptureSize;
    491             p->vsize = sizeof(uint32_t);
    492             *replySize += sizeof(uint32_t);
    493             break;
    494         case VISUALIZER_PARAM_SCALING_MODE:
    495             ALOGV("get mScalingMode = %" PRIu32, pContext->mScalingMode);
    496             *((uint32_t *)p->data + 1) = pContext->mScalingMode;
    497             p->vsize = sizeof(uint32_t);
    498             *replySize += sizeof(uint32_t);
    499             break;
    500         case VISUALIZER_PARAM_MEASUREMENT_MODE:
    501             ALOGV("get mMeasurementMode = %" PRIu32, pContext->mMeasurementMode);
    502             *((uint32_t *)p->data + 1) = pContext->mMeasurementMode;
    503             p->vsize = sizeof(uint32_t);
    504             *replySize += sizeof(uint32_t);
    505             break;
    506         default:
    507             p->status = -EINVAL;
    508         }
    509         } break;
    510     case EFFECT_CMD_SET_PARAM: {
    511         if (pCmdData == NULL ||
    512             cmdSize != (int)(sizeof(effect_param_t) + sizeof(uint32_t) + sizeof(uint32_t)) ||
    513             pReplyData == NULL || *replySize != sizeof(int32_t)) {
    514             return -EINVAL;
    515         }
    516         *(int32_t *)pReplyData = 0;
    517         effect_param_t *p = (effect_param_t *)pCmdData;
    518         if (p->psize != sizeof(uint32_t) || p->vsize != sizeof(uint32_t)) {
    519             *(int32_t *)pReplyData = -EINVAL;
    520             break;
    521         }
    522         switch (*(uint32_t *)p->data) {
    523         case VISUALIZER_PARAM_CAPTURE_SIZE:
    524             pContext->mCaptureSize = *((uint32_t *)p->data + 1);
    525             ALOGV("set mCaptureSize = %" PRIu32, pContext->mCaptureSize);
    526             break;
    527         case VISUALIZER_PARAM_SCALING_MODE:
    528             pContext->mScalingMode = *((uint32_t *)p->data + 1);
    529             ALOGV("set mScalingMode = %" PRIu32, pContext->mScalingMode);
    530             break;
    531         case VISUALIZER_PARAM_LATENCY:
    532             pContext->mLatency = *((uint32_t *)p->data + 1);
    533             ALOGV("set mLatency = %" PRIu32, pContext->mLatency);
    534             break;
    535         case VISUALIZER_PARAM_MEASUREMENT_MODE:
    536             pContext->mMeasurementMode = *((uint32_t *)p->data + 1);
    537             ALOGV("set mMeasurementMode = %" PRIu32, pContext->mMeasurementMode);
    538             break;
    539         default:
    540             *(int32_t *)pReplyData = -EINVAL;
    541         }
    542         } break;
    543     case EFFECT_CMD_SET_DEVICE:
    544     case EFFECT_CMD_SET_VOLUME:
    545     case EFFECT_CMD_SET_AUDIO_MODE:
    546         break;
    547 
    548 
    549     case VISUALIZER_CMD_CAPTURE: {
    550         uint32_t captureSize = pContext->mCaptureSize;
    551         if (pReplyData == NULL || *replySize != captureSize) {
    552             ALOGV("VISUALIZER_CMD_CAPTURE() error *replySize %" PRIu32 " captureSize %" PRIu32,
    553                     *replySize, captureSize);
    554             return -EINVAL;
    555         }
    556         if (pContext->mState == VISUALIZER_STATE_ACTIVE) {
    557             const uint32_t deltaMs = Visualizer_getDeltaTimeMsFromUpdatedTime(pContext);
    558 
    559             // if audio framework has stopped playing audio although the effect is still
    560             // active we must clear the capture buffer to return silence
    561             if ((pContext->mLastCaptureIdx == pContext->mCaptureIdx) &&
    562                     (pContext->mBufferUpdateTime.tv_sec != 0) &&
    563                     (deltaMs > MAX_STALL_TIME_MS)) {
    564                     ALOGV("capture going to idle");
    565                     pContext->mBufferUpdateTime.tv_sec = 0;
    566                     memset(pReplyData, 0x80, captureSize);
    567             } else {
    568                 int32_t latencyMs = pContext->mLatency;
    569                 latencyMs -= deltaMs;
    570                 if (latencyMs < 0) {
    571                     latencyMs = 0;
    572                 }
    573                 const uint32_t deltaSmpl =
    574                     pContext->mConfig.inputCfg.samplingRate * latencyMs / 1000;
    575                 int32_t capturePoint = pContext->mCaptureIdx - captureSize - deltaSmpl;
    576 
    577                 if (capturePoint < 0) {
    578                     uint32_t size = -capturePoint;
    579                     if (size > captureSize) {
    580                         size = captureSize;
    581                     }
    582                     memcpy(pReplyData,
    583                            pContext->mCaptureBuf + CAPTURE_BUF_SIZE + capturePoint,
    584                            size);
    585                     pReplyData = (char *)pReplyData + size;
    586                     captureSize -= size;
    587                     capturePoint = 0;
    588                 }
    589                 memcpy(pReplyData,
    590                        pContext->mCaptureBuf + capturePoint,
    591                        captureSize);
    592             }
    593 
    594             pContext->mLastCaptureIdx = pContext->mCaptureIdx;
    595         } else {
    596             memset(pReplyData, 0x80, captureSize);
    597         }
    598 
    599         } break;
    600 
    601     case VISUALIZER_CMD_MEASURE: {
    602         uint16_t peakU16 = 0;
    603         float sumRmsSquared = 0.0f;
    604         uint8_t nbValidMeasurements = 0;
    605         // reset measurements if last measurement was too long ago (which implies stored
    606         // measurements aren't relevant anymore and shouldn't bias the new one)
    607         const int32_t delayMs = Visualizer_getDeltaTimeMsFromUpdatedTime(pContext);
    608         if (delayMs > DISCARD_MEASUREMENTS_TIME_MS) {
    609             ALOGV("Discarding measurements, last measurement is %" PRId32 "ms old", delayMs);
    610             for (uint32_t i=0 ; i<pContext->mMeasurementWindowSizeInBuffers ; i++) {
    611                 pContext->mPastMeasurements[i].mIsValid = false;
    612                 pContext->mPastMeasurements[i].mPeakU16 = 0;
    613                 pContext->mPastMeasurements[i].mRmsSquared = 0;
    614             }
    615             pContext->mMeasurementBufferIdx = 0;
    616         } else {
    617             // only use actual measurements, otherwise the first RMS measure happening before
    618             // MEASUREMENT_WINDOW_MAX_SIZE_IN_BUFFERS have been played will always be artificially
    619             // low
    620             for (uint32_t i=0 ; i < pContext->mMeasurementWindowSizeInBuffers ; i++) {
    621                 if (pContext->mPastMeasurements[i].mIsValid) {
    622                     if (pContext->mPastMeasurements[i].mPeakU16 > peakU16) {
    623                         peakU16 = pContext->mPastMeasurements[i].mPeakU16;
    624                     }
    625                     sumRmsSquared += pContext->mPastMeasurements[i].mRmsSquared;
    626                     nbValidMeasurements++;
    627                 }
    628             }
    629         }
    630         float rms = nbValidMeasurements == 0 ? 0.0f : sqrtf(sumRmsSquared / nbValidMeasurements);
    631         int32_t* pIntReplyData = (int32_t*)pReplyData;
    632         // convert from I16 sample values to mB and write results
    633         if (rms < 0.000016f) {
    634             pIntReplyData[MEASUREMENT_IDX_RMS] = -9600; //-96dB
    635         } else {
    636             pIntReplyData[MEASUREMENT_IDX_RMS] = (int32_t) (2000 * log10(rms / 32767.0f));
    637         }
    638         if (peakU16 == 0) {
    639             pIntReplyData[MEASUREMENT_IDX_PEAK] = -9600; //-96dB
    640         } else {
    641             pIntReplyData[MEASUREMENT_IDX_PEAK] = (int32_t) (2000 * log10(peakU16 / 32767.0f));
    642         }
    643         ALOGV("VISUALIZER_CMD_MEASURE peak=%" PRIu16 " (%" PRId32 "mB), rms=%.1f (%" PRId32 "mB)",
    644                 peakU16, pIntReplyData[MEASUREMENT_IDX_PEAK],
    645                 rms, pIntReplyData[MEASUREMENT_IDX_RMS]);
    646         }
    647         break;
    648 
    649     default:
    650         ALOGW("Visualizer_command invalid command %" PRIu32, cmdCode);
    651         return -EINVAL;
    652     }
    653 
    654     return 0;
    655 }
    656 
    657 /* Effect Control Interface Implementation: get_descriptor */
    658 int Visualizer_getDescriptor(effect_handle_t   self,
    659                                     effect_descriptor_t *pDescriptor)
    660 {
    661     VisualizerContext * pContext = (VisualizerContext *) self;
    662 
    663     if (pContext == NULL || pDescriptor == NULL) {
    664         ALOGV("Visualizer_getDescriptor() invalid param");
    665         return -EINVAL;
    666     }
    667 
    668     *pDescriptor = gVisualizerDescriptor;
    669 
    670     return 0;
    671 }   /* end Visualizer_getDescriptor */
    672 
    673 // effect_handle_t interface implementation for visualizer effect
    674 const struct effect_interface_s gVisualizerInterface = {
    675         Visualizer_process,
    676         Visualizer_command,
    677         Visualizer_getDescriptor,
    678         NULL,
    679 };
    680 
    681 // This is the only symbol that needs to be exported
    682 __attribute__ ((visibility ("default")))
    683 audio_effect_library_t AUDIO_EFFECT_LIBRARY_INFO_SYM = {
    684     .tag = AUDIO_EFFECT_LIBRARY_TAG,
    685     .version = EFFECT_LIBRARY_API_VERSION,
    686     .name = "Visualizer Library",
    687     .implementor = "The Android Open Source Project",
    688     .create_effect = VisualizerLib_Create,
    689     .release_effect = VisualizerLib_Release,
    690     .get_descriptor = VisualizerLib_GetDescriptor,
    691 };
    692 
    693 }; // extern "C"
    694