• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 //
Visualizer_getDeltaTimeMsFromUpdatedTime(VisualizerContext * pContext)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 
Visualizer_reset(VisualizerContext * pContext)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 
Visualizer_setConfig(VisualizerContext * pContext,effect_config_t * pConfig)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 
Visualizer_getConfig(VisualizerContext * pContext,effect_config_t * pConfig)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 
Visualizer_init(VisualizerContext * pContext)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 
VisualizerLib_Create(const effect_uuid_t * uuid,int32_t,int32_t,effect_handle_t * pHandle)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 
VisualizerLib_Release(effect_handle_t handle)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 
VisualizerLib_GetDescriptor(const effect_uuid_t * uuid,effect_descriptor_t * pDescriptor)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 
clamp16(int32_t sample)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 
Visualizer_process(effect_handle_t self,audio_buffer_t * inBuffer,audio_buffer_t * outBuffer)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 
Visualizer_command(effect_handle_t self,uint32_t cmdCode,uint32_t cmdSize,void * pCmdData,uint32_t * replySize,void * pReplyData)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 */
Visualizer_getDescriptor(effect_handle_t self,effect_descriptor_t * pDescriptor)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