blob: 39552b64a85ce98f12292184ea6df8f40475415f [file] [log] [blame]
Eric Laurentda7581b2010-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
27#include <media/Visualizer.h>
28
Chia-chi Yehdbd2b7e2010-08-19 15:34:10 +080029extern void fixed_fft_real(int n, int32_t *v);
Eric Laurentda7581b2010-07-02 08:12:41 -070030
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();
Eric Laurentda7581b2010-07-02 08:12:41 -070047}
48
49Visualizer::~Visualizer()
50{
Eric Laurentda7581b2010-07-02 08:12:41 -070051}
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;
Eric Laurentda7581b2010-07-02 08:12:41 -0700155 }
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) {
Eric Laurent25f43952010-07-28 05:40:18 -0700171 uint32_t replySize = mCaptureSize;
Eric Laurentda7581b2010-07-02 08:12:41 -0700172 status_t status = command(VISU_CMD_CAPTURE, 0, NULL, &replySize, waveform);
173 if (replySize == 0) {
174 status = NOT_ENOUGH_DATA;
175 }
176 } else {
177 memset(waveform, 0x80, mCaptureSize);
178 }
179 return status;
180}
181
182status_t Visualizer::getFft(uint8_t *fft)
183{
184 if (fft == NULL) {
185 return BAD_VALUE;
186 }
187 if (mCaptureSize == 0) {
188 return NO_INIT;
189 }
190
191 status_t status = NO_ERROR;
192 if (mEnabled) {
193 uint8_t buf[mCaptureSize];
194 status_t status = getWaveForm(buf);
195 if (status == NO_ERROR) {
196 status = doFft(fft, buf);
197 }
198 } else {
199 memset(fft, 0, mCaptureSize);
200 }
201 return status;
202}
203
204status_t Visualizer::doFft(uint8_t *fft, uint8_t *waveform)
205{
Chia-chi Yehdbd2b7e2010-08-19 15:34:10 +0800206 int32_t workspace[mCaptureSize >> 1];
207 int32_t nonzero = 0;
208
209 for (uint32_t i = 0; i < mCaptureSize; i += 2) {
210 workspace[i >> 1] = (waveform[i] ^ 0x80) << 23;
211 workspace[i >> 1] |= (waveform[i + 1] ^ 0x80) << 7;
212 nonzero |= workspace[i >> 1];
Eric Laurentda7581b2010-07-02 08:12:41 -0700213 }
214
Chia-chi Yehdbd2b7e2010-08-19 15:34:10 +0800215 if (nonzero) {
216 fixed_fft_real(mCaptureSize >> 1, workspace);
Eric Laurentda7581b2010-07-02 08:12:41 -0700217 }
Chia-chi Yehdbd2b7e2010-08-19 15:34:10 +0800218
219 for (uint32_t i = 0; i < mCaptureSize; i += 2) {
220 fft[i] = workspace[i >> 1] >> 23;
221 fft[i + 1] = workspace[i >> 1] >> 7;
Eric Laurentda7581b2010-07-02 08:12:41 -0700222 }
Chia-chi Yehdbd2b7e2010-08-19 15:34:10 +0800223
Eric Laurentda7581b2010-07-02 08:12:41 -0700224 return NO_ERROR;
225}
226
227void Visualizer::periodicCapture()
228{
229 Mutex::Autolock _l(mLock);
230 LOGV("periodicCapture() %p mCaptureCallBack %p mCaptureFlags 0x%08x",
231 this, mCaptureCallBack, mCaptureFlags);
232 if (mCaptureCallBack != NULL &&
233 (mCaptureFlags & (CAPTURE_WAVEFORM|CAPTURE_FFT)) &&
234 mCaptureSize != 0) {
235 uint8_t waveform[mCaptureSize];
236 status_t status = getWaveForm(waveform);
237 if (status != NO_ERROR) {
238 return;
239 }
240 uint8_t fft[mCaptureSize];
241 if (mCaptureFlags & CAPTURE_FFT) {
242 status = doFft(fft, waveform);
243 }
244 if (status != NO_ERROR) {
245 return;
246 }
247 uint8_t *wavePtr = NULL;
248 uint8_t *fftPtr = NULL;
249 uint32_t waveSize = 0;
250 uint32_t fftSize = 0;
251 if (mCaptureFlags & CAPTURE_WAVEFORM) {
252 wavePtr = waveform;
253 waveSize = mCaptureSize;
254 }
255 if (mCaptureFlags & CAPTURE_FFT) {
256 fftPtr = fft;
257 fftSize = mCaptureSize;
258 }
259 mCaptureCallBack(mCaptureCbkUser, waveSize, wavePtr, fftSize, fftPtr, mSampleRate);
260 }
261}
262
263uint32_t Visualizer::initCaptureSize()
264{
265 uint32_t buf32[sizeof(effect_param_t) / sizeof(uint32_t) + 2];
266 effect_param_t *p = (effect_param_t *)buf32;
267
268 p->psize = sizeof(uint32_t);
269 p->vsize = sizeof(uint32_t);
270 *(int32_t *)p->data = VISU_PARAM_CAPTURE_SIZE;
271 status_t status = getParameter(p);
272
273 if (status == NO_ERROR) {
274 status = p->status;
275 }
276
277 uint32_t size = 0;
278 if (status == NO_ERROR) {
279 size = *((int32_t *)p->data + 1);
280 }
281 mCaptureSize = size;
282
283 LOGV("initCaptureSize size %d status %d", mCaptureSize, status);
284
285 return size;
286}
287
288//-------------------------------------------------------------------------
289
290Visualizer::CaptureThread::CaptureThread(Visualizer& receiver, uint32_t captureRate, bool bCanCallJava)
291 : Thread(bCanCallJava), mReceiver(receiver)
292{
293 mSleepTimeUs = 1000000000 / captureRate;
294 LOGV("CaptureThread cstor %p captureRate %d mSleepTimeUs %d", this, captureRate, mSleepTimeUs);
295}
296
297bool Visualizer::CaptureThread::threadLoop()
298{
299 LOGV("CaptureThread %p enter", this);
300 while (!exitPending())
301 {
302 usleep(mSleepTimeUs);
303 mReceiver.periodicCapture();
304 }
305 LOGV("CaptureThread %p exiting", this);
306 return false;
307}
308
309status_t Visualizer::CaptureThread::readyToRun()
310{
311 return NO_ERROR;
312}
313
314void Visualizer::CaptureThread::onFirstRef()
315{
316}
317
318}; // namespace android
319