1/*
2**
3** Copyright 2010, The Android Open Source Project
4**
5** Licensed under the Apache License, Version 2.0 (the "License");
6** you may not use this file except in compliance with the License.
7** You may obtain a copy of the License at
8**
9**     http://www.apache.org/licenses/LICENSE-2.0
10**
11** Unless required by applicable law or agreed to in writing, software
12** distributed under the License is distributed on an "AS IS" BASIS,
13** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14** See the License for the specific language governing permissions and
15** limitations under the License.
16*/
17
18
19//#define LOG_NDEBUG 0
20#define LOG_TAG "Visualizer"
21#include <utils/Log.h>
22
23#include <stdint.h>
24#include <sys/types.h>
25#include <limits.h>
26
27#include <media/Visualizer.h>
28#include <audio_utils/fixedfft.h>
29#include <utils/Thread.h>
30
31namespace android {
32
33// ---------------------------------------------------------------------------
34
35Visualizer::Visualizer (const String16& opPackageName,
36         int32_t priority,
37         effect_callback_t cbf,
38         void* user,
39         audio_session_t sessionId)
40    :   AudioEffect(SL_IID_VISUALIZATION, opPackageName, NULL, priority, cbf, user, sessionId),
41        mCaptureRate(CAPTURE_RATE_DEF),
42        mCaptureSize(CAPTURE_SIZE_DEF),
43        mSampleRate(44100000),
44        mScalingMode(VISUALIZER_SCALING_MODE_NORMALIZED),
45        mMeasurementMode(MEASUREMENT_MODE_NONE),
46        mCaptureCallBack(NULL),
47        mCaptureCbkUser(NULL)
48{
49    initCaptureSize();
50}
51
52Visualizer::~Visualizer()
53{
54    ALOGV("Visualizer::~Visualizer()");
55    setEnabled(false);
56    setCaptureCallBack(NULL, NULL, 0, 0);
57}
58
59status_t Visualizer::setEnabled(bool enabled)
60{
61    Mutex::Autolock _l(mCaptureLock);
62
63    sp<CaptureThread> t = mCaptureThread;
64    if (t != 0) {
65        if (enabled) {
66            if (t->exitPending()) {
67                if (t->requestExitAndWait() == WOULD_BLOCK) {
68                    ALOGE("Visualizer::enable() called from thread");
69                    return INVALID_OPERATION;
70                }
71            }
72        }
73        t->mLock.lock();
74    }
75
76    status_t status = AudioEffect::setEnabled(enabled);
77
78    if (t != 0) {
79        if (enabled && status == NO_ERROR) {
80            t->run("Visualizer");
81        } else {
82            t->requestExit();
83        }
84    }
85
86    if (t != 0) {
87        t->mLock.unlock();
88    }
89
90    return status;
91}
92
93status_t Visualizer::setCaptureCallBack(capture_cbk_t cbk, void* user, uint32_t flags,
94        uint32_t rate)
95{
96    if (rate > CAPTURE_RATE_MAX) {
97        return BAD_VALUE;
98    }
99    Mutex::Autolock _l(mCaptureLock);
100
101    if (mEnabled) {
102        return INVALID_OPERATION;
103    }
104
105    if (mCaptureThread != 0) {
106        mCaptureLock.unlock();
107        mCaptureThread->requestExitAndWait();
108        mCaptureLock.lock();
109    }
110
111    mCaptureThread.clear();
112    mCaptureCallBack = cbk;
113    mCaptureCbkUser = user;
114    mCaptureFlags = flags;
115    mCaptureRate = rate;
116
117    if (cbk != NULL) {
118        mCaptureThread = new CaptureThread(*this, rate, ((flags & CAPTURE_CALL_JAVA) != 0));
119    }
120    ALOGV("setCaptureCallBack() rate: %d thread %p flags 0x%08x",
121            rate, mCaptureThread.get(), mCaptureFlags);
122    return NO_ERROR;
123}
124
125status_t Visualizer::setCaptureSize(uint32_t size)
126{
127    if (size > VISUALIZER_CAPTURE_SIZE_MAX ||
128        size < VISUALIZER_CAPTURE_SIZE_MIN ||
129        popcount(size) != 1) {
130        return BAD_VALUE;
131    }
132
133    Mutex::Autolock _l(mCaptureLock);
134    if (mEnabled) {
135        return INVALID_OPERATION;
136    }
137
138    uint32_t buf32[sizeof(effect_param_t) / sizeof(uint32_t) + 2];
139    effect_param_t *p = (effect_param_t *)buf32;
140
141    p->psize = sizeof(uint32_t);
142    p->vsize = sizeof(uint32_t);
143    *(int32_t *)p->data = VISUALIZER_PARAM_CAPTURE_SIZE;
144    *((int32_t *)p->data + 1)= size;
145    status_t status = setParameter(p);
146
147    ALOGV("setCaptureSize size %d  status %d p->status %d", size, status, p->status);
148
149    if (status == NO_ERROR) {
150        status = p->status;
151        if (status == NO_ERROR) {
152            mCaptureSize = size;
153        }
154    }
155
156    return status;
157}
158
159status_t Visualizer::setScalingMode(uint32_t mode) {
160    if ((mode != VISUALIZER_SCALING_MODE_NORMALIZED)
161            && (mode != VISUALIZER_SCALING_MODE_AS_PLAYED)) {
162        return BAD_VALUE;
163    }
164
165    Mutex::Autolock _l(mCaptureLock);
166
167    uint32_t buf32[sizeof(effect_param_t) / sizeof(uint32_t) + 2];
168    effect_param_t *p = (effect_param_t *)buf32;
169
170    p->psize = sizeof(uint32_t);
171    p->vsize = sizeof(uint32_t);
172    *(int32_t *)p->data = VISUALIZER_PARAM_SCALING_MODE;
173    *((int32_t *)p->data + 1)= mode;
174    status_t status = setParameter(p);
175
176    ALOGV("setScalingMode mode %d  status %d p->status %d", mode, status, p->status);
177
178    if (status == NO_ERROR) {
179        status = p->status;
180        if (status == NO_ERROR) {
181            mScalingMode = mode;
182        }
183    }
184
185    return status;
186}
187
188status_t Visualizer::setMeasurementMode(uint32_t mode) {
189    if ((mode != MEASUREMENT_MODE_NONE)
190            //Note: needs to be handled as a mask when more measurement modes are added
191            && ((mode & MEASUREMENT_MODE_PEAK_RMS) != mode)) {
192        return BAD_VALUE;
193    }
194
195    Mutex::Autolock _l(mCaptureLock);
196
197    uint32_t buf32[sizeof(effect_param_t) / sizeof(uint32_t) + 2];
198    effect_param_t *p = (effect_param_t *)buf32;
199
200    p->psize = sizeof(uint32_t);
201    p->vsize = sizeof(uint32_t);
202    *(int32_t *)p->data = VISUALIZER_PARAM_MEASUREMENT_MODE;
203    *((int32_t *)p->data + 1)= mode;
204    status_t status = setParameter(p);
205
206    ALOGV("setMeasurementMode mode %d  status %d p->status %d", mode, status, p->status);
207
208    if (status == NO_ERROR) {
209        status = p->status;
210        if (status == NO_ERROR) {
211            mMeasurementMode = mode;
212        }
213    }
214    return status;
215}
216
217status_t Visualizer::getIntMeasurements(uint32_t type, uint32_t number, int32_t *measurements) {
218    if (mMeasurementMode == MEASUREMENT_MODE_NONE) {
219        ALOGE("Cannot retrieve int measurements, no measurement mode set");
220        return INVALID_OPERATION;
221    }
222    if (!(mMeasurementMode & type)) {
223        // measurement type has not been set on this Visualizer
224        ALOGE("Cannot retrieve int measurements, requested measurement mode 0x%x not set(0x%x)",
225                type, mMeasurementMode);
226        return INVALID_OPERATION;
227    }
228    // only peak+RMS measurement supported
229    if ((type != MEASUREMENT_MODE_PEAK_RMS)
230            // for peak+RMS measurement, the results are 2 int32_t values
231            || (number != 2)) {
232        ALOGE("Cannot retrieve int measurements, MEASUREMENT_MODE_PEAK_RMS returns 2 ints, not %d",
233                        number);
234        return BAD_VALUE;
235    }
236
237    status_t status = NO_ERROR;
238    if (mEnabled) {
239        uint32_t replySize = number * sizeof(int32_t);
240        status = command(VISUALIZER_CMD_MEASURE,
241                sizeof(uint32_t)  /*cmdSize*/,
242                &type /*cmdData*/,
243                &replySize, measurements);
244        ALOGV("getMeasurements() command returned %d", status);
245        if ((status == NO_ERROR) && (replySize == 0)) {
246            status = NOT_ENOUGH_DATA;
247        }
248    } else {
249        ALOGV("getMeasurements() disabled");
250        return INVALID_OPERATION;
251    }
252    return status;
253}
254
255status_t Visualizer::getWaveForm(uint8_t *waveform)
256{
257    if (waveform == NULL) {
258        return BAD_VALUE;
259    }
260    if (mCaptureSize == 0) {
261        return NO_INIT;
262    }
263
264    status_t status = NO_ERROR;
265    if (mEnabled) {
266        uint32_t replySize = mCaptureSize;
267        status = command(VISUALIZER_CMD_CAPTURE, 0, NULL, &replySize, waveform);
268        ALOGV("getWaveForm() command returned %d", status);
269        if ((status == NO_ERROR) && (replySize == 0)) {
270            status = NOT_ENOUGH_DATA;
271        }
272    } else {
273        ALOGV("getWaveForm() disabled");
274        memset(waveform, 0x80, mCaptureSize);
275    }
276    return status;
277}
278
279status_t Visualizer::getFft(uint8_t *fft)
280{
281    if (fft == NULL) {
282        return BAD_VALUE;
283    }
284    if (mCaptureSize == 0) {
285        return NO_INIT;
286    }
287
288    status_t status = NO_ERROR;
289    if (mEnabled) {
290        uint8_t buf[mCaptureSize];
291        status = getWaveForm(buf);
292        if (status == NO_ERROR) {
293            status = doFft(fft, buf);
294        }
295    } else {
296        memset(fft, 0, mCaptureSize);
297    }
298    return status;
299}
300
301status_t Visualizer::doFft(uint8_t *fft, uint8_t *waveform)
302{
303    int32_t workspace[mCaptureSize >> 1];
304    int32_t nonzero = 0;
305
306    for (uint32_t i = 0; i < mCaptureSize; i += 2) {
307        workspace[i >> 1] =
308                ((waveform[i] ^ 0x80) << 24) | ((waveform[i + 1] ^ 0x80) << 8);
309        nonzero |= workspace[i >> 1];
310    }
311
312    if (nonzero) {
313        fixed_fft_real(mCaptureSize >> 1, workspace);
314    }
315
316    for (uint32_t i = 0; i < mCaptureSize; i += 2) {
317        short tmp = workspace[i >> 1] >> 21;
318        while (tmp > 127 || tmp < -128) tmp >>= 1;
319        fft[i] = tmp;
320        tmp = workspace[i >> 1];
321        tmp >>= 5;
322        while (tmp > 127 || tmp < -128) tmp >>= 1;
323        fft[i + 1] = tmp;
324    }
325
326    return NO_ERROR;
327}
328
329void Visualizer::periodicCapture()
330{
331    Mutex::Autolock _l(mCaptureLock);
332    ALOGV("periodicCapture() %p mCaptureCallBack %p mCaptureFlags 0x%08x",
333            this, mCaptureCallBack, mCaptureFlags);
334    if (mCaptureCallBack != NULL &&
335        (mCaptureFlags & (CAPTURE_WAVEFORM|CAPTURE_FFT)) &&
336        mCaptureSize != 0) {
337        uint8_t waveform[mCaptureSize];
338        status_t status = getWaveForm(waveform);
339        if (status != NO_ERROR) {
340            return;
341        }
342        uint8_t fft[mCaptureSize];
343        if (mCaptureFlags & CAPTURE_FFT) {
344            status = doFft(fft, waveform);
345        }
346        if (status != NO_ERROR) {
347            return;
348        }
349        uint8_t *wavePtr = NULL;
350        uint8_t *fftPtr = NULL;
351        uint32_t waveSize = 0;
352        uint32_t fftSize = 0;
353        if (mCaptureFlags & CAPTURE_WAVEFORM) {
354            wavePtr = waveform;
355            waveSize = mCaptureSize;
356        }
357        if (mCaptureFlags & CAPTURE_FFT) {
358            fftPtr = fft;
359            fftSize = mCaptureSize;
360        }
361        mCaptureCallBack(mCaptureCbkUser, waveSize, wavePtr, fftSize, fftPtr, mSampleRate);
362    }
363}
364
365uint32_t Visualizer::initCaptureSize()
366{
367    uint32_t buf32[sizeof(effect_param_t) / sizeof(uint32_t) + 2];
368    effect_param_t *p = (effect_param_t *)buf32;
369
370    p->psize = sizeof(uint32_t);
371    p->vsize = sizeof(uint32_t);
372    *(int32_t *)p->data = VISUALIZER_PARAM_CAPTURE_SIZE;
373    status_t status = getParameter(p);
374
375    if (status == NO_ERROR) {
376        status = p->status;
377    }
378
379    uint32_t size = 0;
380    if (status == NO_ERROR) {
381        size = *((int32_t *)p->data + 1);
382    }
383    mCaptureSize = size;
384
385    ALOGV("initCaptureSize size %d status %d", mCaptureSize, status);
386
387    return size;
388}
389
390void Visualizer::controlStatusChanged(bool controlGranted) {
391    if (controlGranted) {
392        // this Visualizer instance regained control of the effect, reset the scaling mode
393        //   and capture size as has been cached through it.
394        ALOGV("controlStatusChanged(true) causes effect parameter reset:");
395        ALOGV("    scaling mode reset to %d", mScalingMode);
396        setScalingMode(mScalingMode);
397        ALOGV("    capture size reset to %d", mCaptureSize);
398        setCaptureSize(mCaptureSize);
399    }
400    AudioEffect::controlStatusChanged(controlGranted);
401}
402
403//-------------------------------------------------------------------------
404
405Visualizer::CaptureThread::CaptureThread(Visualizer& receiver, uint32_t captureRate,
406        bool bCanCallJava)
407    : Thread(bCanCallJava), mReceiver(receiver)
408{
409    mSleepTimeUs = 1000000000 / captureRate;
410    ALOGV("CaptureThread cstor %p captureRate %d mSleepTimeUs %d", this, captureRate, mSleepTimeUs);
411}
412
413bool Visualizer::CaptureThread::threadLoop()
414{
415    ALOGV("CaptureThread %p enter", this);
416    while (!exitPending())
417    {
418        usleep(mSleepTimeUs);
419        mReceiver.periodicCapture();
420    }
421    ALOGV("CaptureThread %p exiting", this);
422    return false;
423}
424
425} // namespace android
426