Visualizer.cpp revision fce7a473248381cc83a01855f92581077d3c9ee2
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 <cutils/bitops.h>
28
29#include <media/Visualizer.h>
30
31extern void fixed_fft_real(int n, int32_t *v);
32
33namespace android {
34
35// ---------------------------------------------------------------------------
36
37Visualizer::Visualizer (int32_t priority,
38         effect_callback_t cbf,
39         void* user,
40         int sessionId)
41    :   AudioEffect(SL_IID_VISUALIZATION, NULL, priority, cbf, user, sessionId),
42        mCaptureRate(CAPTURE_RATE_DEF),
43        mCaptureSize(CAPTURE_SIZE_DEF),
44        mSampleRate(44100000),
45        mCaptureCallBack(NULL),
46        mCaptureCbkUser(NULL)
47{
48    initCaptureSize();
49}
50
51Visualizer::~Visualizer()
52{
53}
54
55status_t Visualizer::setEnabled(bool enabled)
56{
57    Mutex::Autolock _l(mLock);
58
59    sp<CaptureThread> t = mCaptureThread;
60    if (t != 0) {
61        if (enabled) {
62            if (t->exitPending()) {
63                if (t->requestExitAndWait() == WOULD_BLOCK) {
64                    LOGE("Visualizer::enable() called from thread");
65                    return INVALID_OPERATION;
66                }
67            }
68        }
69        t->mLock.lock();
70     }
71
72    status_t status = AudioEffect::setEnabled(enabled);
73
74    if (status == NO_ERROR) {
75        if (t != 0) {
76            if (enabled) {
77                t->run("AudioTrackThread");
78            } else {
79                t->requestExit();
80            }
81        }
82    }
83
84    if (t != 0) {
85        t->mLock.unlock();
86    }
87
88    return status;
89}
90
91status_t Visualizer::setCaptureCallBack(capture_cbk_t cbk, void* user, uint32_t flags, uint32_t rate)
92{
93    if (rate > CAPTURE_RATE_MAX) {
94        return BAD_VALUE;
95    }
96    Mutex::Autolock _l(mLock);
97
98    if (mEnabled) {
99        return INVALID_OPERATION;
100    }
101
102    sp<CaptureThread> t = mCaptureThread;
103    if (t != 0) {
104        t->mLock.lock();
105    }
106    mCaptureThread.clear();
107    mCaptureCallBack = cbk;
108    mCaptureCbkUser = user;
109    mCaptureFlags = flags;
110    mCaptureRate = rate;
111
112    if (t != 0) {
113        t->mLock.unlock();
114    }
115
116    if (cbk != NULL) {
117        mCaptureThread = new CaptureThread(*this, rate, ((flags & CAPTURE_CALL_JAVA) != 0));
118        if (mCaptureThread == 0) {
119            LOGE("Could not create callback thread");
120            return NO_INIT;
121        }
122    }
123    LOGV("setCaptureCallBack() rate: %d thread %p flags 0x%08x",
124            rate, mCaptureThread.get(), mCaptureFlags);
125    return NO_ERROR;
126}
127
128status_t Visualizer::setCaptureSize(uint32_t size)
129{
130    if (size > VISUALIZER_CAPTURE_SIZE_MAX ||
131        size < VISUALIZER_CAPTURE_SIZE_MIN ||
132        popcount(size) != 1) {
133        return BAD_VALUE;
134    }
135
136    Mutex::Autolock _l(mLock);
137    if (mEnabled) {
138        return INVALID_OPERATION;
139    }
140
141    uint32_t buf32[sizeof(effect_param_t) / sizeof(uint32_t) + 2];
142    effect_param_t *p = (effect_param_t *)buf32;
143
144    p->psize = sizeof(uint32_t);
145    p->vsize = sizeof(uint32_t);
146    *(int32_t *)p->data = VISU_PARAM_CAPTURE_SIZE;
147    *((int32_t *)p->data + 1)= size;
148    status_t status = setParameter(p);
149
150    LOGV("setCaptureSize size %d  status %d p->status %d", size, status, p->status);
151
152    if (status == NO_ERROR) {
153        status = p->status;
154    }
155    if (status == NO_ERROR) {
156        mCaptureSize = size;
157    }
158
159    return status;
160}
161
162status_t Visualizer::getWaveForm(uint8_t *waveform)
163{
164    if (waveform == NULL) {
165        return BAD_VALUE;
166    }
167    if (mCaptureSize == 0) {
168        return NO_INIT;
169    }
170
171    status_t status = NO_ERROR;
172    if (mEnabled) {
173        uint32_t replySize = mCaptureSize;
174        status = command(VISU_CMD_CAPTURE, 0, NULL, &replySize, waveform);
175        LOGV("getWaveForm() command returned %d", status);
176        if (replySize == 0) {
177            status = NOT_ENOUGH_DATA;
178        }
179    } else {
180        LOGV("getWaveForm() disabled");
181        memset(waveform, 0x80, mCaptureSize);
182    }
183    return status;
184}
185
186status_t Visualizer::getFft(uint8_t *fft)
187{
188    if (fft == NULL) {
189        return BAD_VALUE;
190    }
191    if (mCaptureSize == 0) {
192        return NO_INIT;
193    }
194
195    status_t status = NO_ERROR;
196    if (mEnabled) {
197        uint8_t buf[mCaptureSize];
198        status = getWaveForm(buf);
199        if (status == NO_ERROR) {
200            status = doFft(fft, buf);
201        }
202    } else {
203        memset(fft, 0, mCaptureSize);
204    }
205    return status;
206}
207
208status_t Visualizer::doFft(uint8_t *fft, uint8_t *waveform)
209{
210    int32_t workspace[mCaptureSize >> 1];
211    int32_t nonzero = 0;
212
213    for (uint32_t i = 0; i < mCaptureSize; i += 2) {
214        workspace[i >> 1] =
215                ((waveform[i] ^ 0x80) << 24) | ((waveform[i + 1] ^ 0x80) << 8);
216        nonzero |= workspace[i >> 1];
217    }
218
219    if (nonzero) {
220        fixed_fft_real(mCaptureSize >> 1, workspace);
221    }
222
223    for (uint32_t i = 0; i < mCaptureSize; i += 2) {
224        short tmp = workspace[i >> 1] >> 21;
225        while (tmp > 127 || tmp < -128) tmp >>= 1;
226        fft[i] = tmp;
227        tmp = workspace[i >> 1];
228        tmp >>= 5;
229        while (tmp > 127 || tmp < -128) tmp >>= 1;
230        fft[i + 1] = tmp;
231    }
232
233    return NO_ERROR;
234}
235
236void Visualizer::periodicCapture()
237{
238    Mutex::Autolock _l(mLock);
239    LOGV("periodicCapture() %p mCaptureCallBack %p mCaptureFlags 0x%08x",
240            this, mCaptureCallBack, mCaptureFlags);
241    if (mCaptureCallBack != NULL &&
242        (mCaptureFlags & (CAPTURE_WAVEFORM|CAPTURE_FFT)) &&
243        mCaptureSize != 0) {
244        uint8_t waveform[mCaptureSize];
245        status_t status = getWaveForm(waveform);
246        if (status != NO_ERROR) {
247            return;
248        }
249        uint8_t fft[mCaptureSize];
250        if (mCaptureFlags & CAPTURE_FFT) {
251            status = doFft(fft, waveform);
252        }
253        if (status != NO_ERROR) {
254            return;
255        }
256        uint8_t *wavePtr = NULL;
257        uint8_t *fftPtr = NULL;
258        uint32_t waveSize = 0;
259        uint32_t fftSize = 0;
260        if (mCaptureFlags & CAPTURE_WAVEFORM) {
261            wavePtr = waveform;
262            waveSize = mCaptureSize;
263        }
264        if (mCaptureFlags & CAPTURE_FFT) {
265            fftPtr = fft;
266            fftSize = mCaptureSize;
267        }
268        mCaptureCallBack(mCaptureCbkUser, waveSize, wavePtr, fftSize, fftPtr, mSampleRate);
269    }
270}
271
272uint32_t Visualizer::initCaptureSize()
273{
274    uint32_t buf32[sizeof(effect_param_t) / sizeof(uint32_t) + 2];
275    effect_param_t *p = (effect_param_t *)buf32;
276
277    p->psize = sizeof(uint32_t);
278    p->vsize = sizeof(uint32_t);
279    *(int32_t *)p->data = VISU_PARAM_CAPTURE_SIZE;
280    status_t status = getParameter(p);
281
282    if (status == NO_ERROR) {
283        status = p->status;
284    }
285
286    uint32_t size = 0;
287    if (status == NO_ERROR) {
288        size = *((int32_t *)p->data + 1);
289    }
290    mCaptureSize = size;
291
292    LOGV("initCaptureSize size %d status %d", mCaptureSize, status);
293
294    return size;
295}
296
297//-------------------------------------------------------------------------
298
299Visualizer::CaptureThread::CaptureThread(Visualizer& receiver, uint32_t captureRate, bool bCanCallJava)
300    : Thread(bCanCallJava), mReceiver(receiver)
301{
302    mSleepTimeUs = 1000000000 / captureRate;
303    LOGV("CaptureThread cstor %p captureRate %d mSleepTimeUs %d", this, captureRate, mSleepTimeUs);
304}
305
306bool Visualizer::CaptureThread::threadLoop()
307{
308    LOGV("CaptureThread %p enter", this);
309    while (!exitPending())
310    {
311        usleep(mSleepTimeUs);
312        mReceiver.periodicCapture();
313    }
314    LOGV("CaptureThread %p exiting", this);
315    return false;
316}
317
318status_t Visualizer::CaptureThread::readyToRun()
319{
320    return NO_ERROR;
321}
322
323void Visualizer::CaptureThread::onFirstRef()
324{
325}
326
327}; // namespace android
328
329