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