blob: 366707c97dc41dea4a2287f8753d77e1dcd31e95 [file] [log] [blame]
Eric Laurentdf9b81c2010-07-02 08:12:41 -07001/*
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
Dima Zavin24fc2fb2011-04-19 22:30:36 -070027#include <cutils/bitops.h>
28
Eric Laurentdf9b81c2010-07-02 08:12:41 -070029#include <media/Visualizer.h>
30
Chia-chi Yeh58d3bd02010-08-19 15:34:10 +080031extern void fixed_fft_real(int n, int32_t *v);
Eric Laurentdf9b81c2010-07-02 08:12:41 -070032
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();
Eric Laurentdf9b81c2010-07-02 08:12:41 -070049}
50
51Visualizer::~Visualizer()
52{
Eric Laurentdf9b81c2010-07-02 08:12:41 -070053}
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 ||
Dima Zavin24fc2fb2011-04-19 22:30:36 -0700132 popcount(size) != 1) {
Eric Laurentdf9b81c2010-07-02 08:12:41 -0700133 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;
Eric Laurentdf9b81c2010-07-02 08:12:41 -0700157 }
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) {
Eric Laurenta4c72ac2010-07-28 05:40:18 -0700173 uint32_t replySize = mCaptureSize;
Eric Laurent4d3fb502010-09-24 11:52:04 -0700174 status = command(VISU_CMD_CAPTURE, 0, NULL, &replySize, waveform);
175 LOGV("getWaveForm() command returned %d", status);
Eric Laurentdf9b81c2010-07-02 08:12:41 -0700176 if (replySize == 0) {
177 status = NOT_ENOUGH_DATA;
178 }
179 } else {
Eric Laurent4d3fb502010-09-24 11:52:04 -0700180 LOGV("getWaveForm() disabled");
Eric Laurentdf9b81c2010-07-02 08:12:41 -0700181 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];
Eric Laurent4d3fb502010-09-24 11:52:04 -0700198 status = getWaveForm(buf);
Eric Laurentdf9b81c2010-07-02 08:12:41 -0700199 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{
Chia-chi Yeh58d3bd02010-08-19 15:34:10 +0800210 int32_t workspace[mCaptureSize >> 1];
211 int32_t nonzero = 0;
212
213 for (uint32_t i = 0; i < mCaptureSize; i += 2) {
Chia-chi Yeh67f41772010-11-01 10:56:45 +0800214 workspace[i >> 1] =
215 ((waveform[i] ^ 0x80) << 24) | ((waveform[i + 1] ^ 0x80) << 8);
Chia-chi Yeh58d3bd02010-08-19 15:34:10 +0800216 nonzero |= workspace[i >> 1];
Eric Laurentdf9b81c2010-07-02 08:12:41 -0700217 }
218
Chia-chi Yeh58d3bd02010-08-19 15:34:10 +0800219 if (nonzero) {
220 fixed_fft_real(mCaptureSize >> 1, workspace);
Eric Laurentdf9b81c2010-07-02 08:12:41 -0700221 }
Chia-chi Yeh58d3bd02010-08-19 15:34:10 +0800222
223 for (uint32_t i = 0; i < mCaptureSize; i += 2) {
Marco Nelissendbc0fe92011-01-18 16:44:28 -0800224 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;
Eric Laurentdf9b81c2010-07-02 08:12:41 -0700231 }
Chia-chi Yeh58d3bd02010-08-19 15:34:10 +0800232
Eric Laurentdf9b81c2010-07-02 08:12:41 -0700233 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