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