blob: 47e96e523372a450ccdd961bd3b8cc08b7b69818 [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
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