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