blob: a74ae5307a361d7123f956645df5815359cb7ee1 [file] [log] [blame]
Marco Nelissen1a7b4152019-10-23 09:21:55 -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 <audio_utils/fixedfft.h>
Glenn Kastena0ab0de2020-06-30 12:14:25 -070028#include <cutils/bitops.h>
Marco Nelissen1a7b4152019-10-23 09:21:55 -070029#include <utils/Thread.h>
30
31#include "Visualizer.h"
32
33namespace android {
34
35// ---------------------------------------------------------------------------
36
Mikhail Naganov12223f02020-07-31 17:42:20 -070037Visualizer::Visualizer (const String16& opPackageName)
38 : AudioEffect(opPackageName)
Marco Nelissen1a7b4152019-10-23 09:21:55 -070039{
Marco Nelissen1a7b4152019-10-23 09:21:55 -070040}
41
42Visualizer::~Visualizer()
43{
44 ALOGV("Visualizer::~Visualizer()");
45 setEnabled(false);
46 setCaptureCallBack(NULL, NULL, 0, 0);
47}
48
Mikhail Naganov12223f02020-07-31 17:42:20 -070049status_t Visualizer::set(int32_t priority,
50 effect_callback_t cbf,
51 void* user,
52 audio_session_t sessionId,
53 audio_io_handle_t io,
54 const AudioDeviceTypeAddr& device,
55 bool probe)
56{
57 status_t status = AudioEffect::set(
58 SL_IID_VISUALIZATION, nullptr, priority, cbf, user, sessionId, io, device, probe);
59 if (status == NO_ERROR || status == ALREADY_EXISTS) {
60 initCaptureSize();
61 }
62 return status;
63}
64
65
Marco Nelissen1a7b4152019-10-23 09:21:55 -070066void Visualizer::release()
67{
68 ALOGV("Visualizer::release()");
69 setEnabled(false);
70 Mutex::Autolock _l(mCaptureLock);
71
72 mCaptureThread.clear();
73 mCaptureCallBack = NULL;
74 mCaptureCbkUser = NULL;
75 mCaptureFlags = 0;
76 mCaptureRate = 0;
77}
78
79status_t Visualizer::setEnabled(bool enabled)
80{
81 Mutex::Autolock _l(mCaptureLock);
82
83 sp<CaptureThread> t = mCaptureThread;
84 if (t != 0) {
85 if (enabled) {
86 if (t->exitPending()) {
87 mCaptureLock.unlock();
88 if (t->requestExitAndWait() == WOULD_BLOCK) {
89 mCaptureLock.lock();
90 ALOGE("Visualizer::enable() called from thread");
91 return INVALID_OPERATION;
92 }
93 mCaptureLock.lock();
94 }
95 }
96 t->mLock.lock();
97 }
98
99 status_t status = AudioEffect::setEnabled(enabled);
100
101 if (t != 0) {
102 if (enabled && status == NO_ERROR) {
103 t->run("Visualizer");
104 } else {
105 t->requestExit();
106 }
107 }
108
109 if (t != 0) {
110 t->mLock.unlock();
111 }
112
113 return status;
114}
115
116status_t Visualizer::setCaptureCallBack(capture_cbk_t cbk, void* user, uint32_t flags,
117 uint32_t rate)
118{
119 if (rate > CAPTURE_RATE_MAX) {
120 return BAD_VALUE;
121 }
122 Mutex::Autolock _l(mCaptureLock);
123
124 if (mEnabled) {
125 return INVALID_OPERATION;
126 }
127
128 if (mCaptureThread != 0) {
Mikhail Naganov61c109c2020-03-12 17:55:29 -0700129 sp<CaptureThread> t = mCaptureThread;
Marco Nelissen1a7b4152019-10-23 09:21:55 -0700130 mCaptureLock.unlock();
Mikhail Naganov61c109c2020-03-12 17:55:29 -0700131 t->requestExitAndWait();
Marco Nelissen1a7b4152019-10-23 09:21:55 -0700132 mCaptureLock.lock();
133 }
134
135 mCaptureThread.clear();
136 mCaptureCallBack = cbk;
137 mCaptureCbkUser = user;
138 mCaptureFlags = flags;
139 mCaptureRate = rate;
140
141 if (cbk != NULL) {
142 mCaptureThread = new CaptureThread(this, rate, ((flags & CAPTURE_CALL_JAVA) != 0));
143 }
144 ALOGV("setCaptureCallBack() rate: %d thread %p flags 0x%08x",
145 rate, mCaptureThread.get(), mCaptureFlags);
146 return NO_ERROR;
147}
148
149status_t Visualizer::setCaptureSize(uint32_t size)
150{
151 if (size > VISUALIZER_CAPTURE_SIZE_MAX ||
152 size < VISUALIZER_CAPTURE_SIZE_MIN ||
153 popcount(size) != 1) {
154 return BAD_VALUE;
155 }
156
157 Mutex::Autolock _l(mCaptureLock);
158 if (mEnabled) {
159 return INVALID_OPERATION;
160 }
161
162 uint32_t buf32[sizeof(effect_param_t) / sizeof(uint32_t) + 2];
163 effect_param_t *p = (effect_param_t *)buf32;
164
165 p->psize = sizeof(uint32_t);
166 p->vsize = sizeof(uint32_t);
167 *(int32_t *)p->data = VISUALIZER_PARAM_CAPTURE_SIZE;
168 *((int32_t *)p->data + 1)= size;
169 status_t status = setParameter(p);
170
171 ALOGV("setCaptureSize size %d status %d p->status %d", size, status, p->status);
172
173 if (status == NO_ERROR) {
174 status = p->status;
175 if (status == NO_ERROR) {
176 mCaptureSize = size;
177 }
178 }
179
180 return status;
181}
182
183status_t Visualizer::setScalingMode(uint32_t mode) {
184 if ((mode != VISUALIZER_SCALING_MODE_NORMALIZED)
185 && (mode != VISUALIZER_SCALING_MODE_AS_PLAYED)) {
186 return BAD_VALUE;
187 }
188
189 Mutex::Autolock _l(mCaptureLock);
190
191 uint32_t buf32[sizeof(effect_param_t) / sizeof(uint32_t) + 2];
192 effect_param_t *p = (effect_param_t *)buf32;
193
194 p->psize = sizeof(uint32_t);
195 p->vsize = sizeof(uint32_t);
196 *(int32_t *)p->data = VISUALIZER_PARAM_SCALING_MODE;
197 *((int32_t *)p->data + 1)= mode;
198 status_t status = setParameter(p);
199
200 ALOGV("setScalingMode mode %d status %d p->status %d", mode, status, p->status);
201
202 if (status == NO_ERROR) {
203 status = p->status;
204 if (status == NO_ERROR) {
205 mScalingMode = mode;
206 }
207 }
208
209 return status;
210}
211
212status_t Visualizer::setMeasurementMode(uint32_t mode) {
213 if ((mode != MEASUREMENT_MODE_NONE)
214 //Note: needs to be handled as a mask when more measurement modes are added
215 && ((mode & MEASUREMENT_MODE_PEAK_RMS) != mode)) {
216 return BAD_VALUE;
217 }
218
219 Mutex::Autolock _l(mCaptureLock);
220
221 uint32_t buf32[sizeof(effect_param_t) / sizeof(uint32_t) + 2];
222 effect_param_t *p = (effect_param_t *)buf32;
223
224 p->psize = sizeof(uint32_t);
225 p->vsize = sizeof(uint32_t);
226 *(int32_t *)p->data = VISUALIZER_PARAM_MEASUREMENT_MODE;
227 *((int32_t *)p->data + 1)= mode;
228 status_t status = setParameter(p);
229
230 ALOGV("setMeasurementMode mode %d status %d p->status %d", mode, status, p->status);
231
232 if (status == NO_ERROR) {
233 status = p->status;
234 if (status == NO_ERROR) {
235 mMeasurementMode = mode;
236 }
237 }
238 return status;
239}
240
241status_t Visualizer::getIntMeasurements(uint32_t type, uint32_t number, int32_t *measurements) {
242 if (mMeasurementMode == MEASUREMENT_MODE_NONE) {
243 ALOGE("Cannot retrieve int measurements, no measurement mode set");
244 return INVALID_OPERATION;
245 }
246 if (!(mMeasurementMode & type)) {
247 // measurement type has not been set on this Visualizer
248 ALOGE("Cannot retrieve int measurements, requested measurement mode 0x%x not set(0x%x)",
249 type, mMeasurementMode);
250 return INVALID_OPERATION;
251 }
252 // only peak+RMS measurement supported
253 if ((type != MEASUREMENT_MODE_PEAK_RMS)
254 // for peak+RMS measurement, the results are 2 int32_t values
255 || (number != 2)) {
256 ALOGE("Cannot retrieve int measurements, MEASUREMENT_MODE_PEAK_RMS returns 2 ints, not %d",
257 number);
258 return BAD_VALUE;
259 }
260
261 status_t status = NO_ERROR;
262 if (mEnabled) {
263 uint32_t replySize = number * sizeof(int32_t);
264 status = command(VISUALIZER_CMD_MEASURE,
265 sizeof(uint32_t) /*cmdSize*/,
266 &type /*cmdData*/,
267 &replySize, measurements);
268 ALOGV("getMeasurements() command returned %d", status);
269 if ((status == NO_ERROR) && (replySize == 0)) {
270 status = NOT_ENOUGH_DATA;
271 }
272 } else {
273 ALOGV("getMeasurements() disabled");
274 return INVALID_OPERATION;
275 }
276 return status;
277}
278
279status_t Visualizer::getWaveForm(uint8_t *waveform)
280{
281 if (waveform == NULL) {
282 return BAD_VALUE;
283 }
284 if (mCaptureSize == 0) {
285 return NO_INIT;
286 }
287
288 status_t status = NO_ERROR;
289 if (mEnabled) {
290 uint32_t replySize = mCaptureSize;
291 status = command(VISUALIZER_CMD_CAPTURE, 0, NULL, &replySize, waveform);
292 ALOGV("getWaveForm() command returned %d", status);
293 if ((status == NO_ERROR) && (replySize == 0)) {
294 status = NOT_ENOUGH_DATA;
295 }
296 } else {
297 ALOGV("getWaveForm() disabled");
298 memset(waveform, 0x80, mCaptureSize);
299 }
300 return status;
301}
302
303status_t Visualizer::getFft(uint8_t *fft)
304{
305 if (fft == NULL) {
306 return BAD_VALUE;
307 }
308 if (mCaptureSize == 0) {
309 return NO_INIT;
310 }
311
312 status_t status = NO_ERROR;
313 if (mEnabled) {
314 uint8_t buf[mCaptureSize];
315 status = getWaveForm(buf);
316 if (status == NO_ERROR) {
317 status = doFft(fft, buf);
318 }
319 } else {
320 memset(fft, 0, mCaptureSize);
321 }
322 return status;
323}
324
325status_t Visualizer::doFft(uint8_t *fft, uint8_t *waveform)
326{
327 int32_t workspace[mCaptureSize >> 1];
328 int32_t nonzero = 0;
329
330 for (uint32_t i = 0; i < mCaptureSize; i += 2) {
331 workspace[i >> 1] =
332 ((waveform[i] ^ 0x80) << 24) | ((waveform[i + 1] ^ 0x80) << 8);
333 nonzero |= workspace[i >> 1];
334 }
335
336 if (nonzero) {
337 fixed_fft_real(mCaptureSize >> 1, workspace);
338 }
339
340 for (uint32_t i = 0; i < mCaptureSize; i += 2) {
341 short tmp = workspace[i >> 1] >> 21;
342 while (tmp > 127 || tmp < -128) tmp >>= 1;
343 fft[i] = tmp;
344 tmp = workspace[i >> 1];
345 tmp >>= 5;
346 while (tmp > 127 || tmp < -128) tmp >>= 1;
347 fft[i + 1] = tmp;
348 }
349
350 return NO_ERROR;
351}
352
353void Visualizer::periodicCapture()
354{
355 Mutex::Autolock _l(mCaptureLock);
356 ALOGV("periodicCapture() %p mCaptureCallBack %p mCaptureFlags 0x%08x",
357 this, mCaptureCallBack, mCaptureFlags);
358 if (mCaptureCallBack != NULL &&
359 (mCaptureFlags & (CAPTURE_WAVEFORM|CAPTURE_FFT)) &&
360 mCaptureSize != 0) {
361 uint8_t waveform[mCaptureSize];
362 status_t status = getWaveForm(waveform);
363 if (status != NO_ERROR) {
364 return;
365 }
366 uint8_t fft[mCaptureSize];
367 if (mCaptureFlags & CAPTURE_FFT) {
368 status = doFft(fft, waveform);
369 }
370 if (status != NO_ERROR) {
371 return;
372 }
373 uint8_t *wavePtr = NULL;
374 uint8_t *fftPtr = NULL;
375 uint32_t waveSize = 0;
376 uint32_t fftSize = 0;
377 if (mCaptureFlags & CAPTURE_WAVEFORM) {
378 wavePtr = waveform;
379 waveSize = mCaptureSize;
380 }
381 if (mCaptureFlags & CAPTURE_FFT) {
382 fftPtr = fft;
383 fftSize = mCaptureSize;
384 }
385 mCaptureCallBack(mCaptureCbkUser, waveSize, wavePtr, fftSize, fftPtr, mSampleRate);
386 }
387}
388
389uint32_t Visualizer::initCaptureSize()
390{
391 uint32_t buf32[sizeof(effect_param_t) / sizeof(uint32_t) + 2];
392 effect_param_t *p = (effect_param_t *)buf32;
393
394 p->psize = sizeof(uint32_t);
395 p->vsize = sizeof(uint32_t);
396 *(int32_t *)p->data = VISUALIZER_PARAM_CAPTURE_SIZE;
397 status_t status = getParameter(p);
398
399 if (status == NO_ERROR) {
400 status = p->status;
401 }
402
403 uint32_t size = 0;
404 if (status == NO_ERROR) {
405 size = *((int32_t *)p->data + 1);
406 }
407 mCaptureSize = size;
408
409 ALOGV("initCaptureSize size %d status %d", mCaptureSize, status);
410
411 return size;
412}
413
414void Visualizer::controlStatusChanged(bool controlGranted) {
415 if (controlGranted) {
416 // this Visualizer instance regained control of the effect, reset the scaling mode
417 // and capture size as has been cached through it.
418 ALOGV("controlStatusChanged(true) causes effect parameter reset:");
419 ALOGV(" scaling mode reset to %d", mScalingMode);
420 setScalingMode(mScalingMode);
421 ALOGV(" capture size reset to %d", mCaptureSize);
422 setCaptureSize(mCaptureSize);
423 }
424 AudioEffect::controlStatusChanged(controlGranted);
425}
426
427//-------------------------------------------------------------------------
428
429Visualizer::CaptureThread::CaptureThread(Visualizer* receiver, uint32_t captureRate,
430 bool bCanCallJava)
431 : Thread(bCanCallJava), mReceiver(receiver)
432{
433 mSleepTimeUs = 1000000000 / captureRate;
434 ALOGV("CaptureThread cstor %p captureRate %d mSleepTimeUs %d", this, captureRate, mSleepTimeUs);
435}
436
437bool Visualizer::CaptureThread::threadLoop()
438{
439 ALOGV("CaptureThread %p enter", this);
440 sp<Visualizer> receiver = mReceiver.promote();
441 if (receiver == NULL) {
442 return false;
443 }
444 while (!exitPending())
445 {
446 usleep(mSleepTimeUs);
447 receiver->periodicCapture();
448 }
449 ALOGV("CaptureThread %p exiting", this);
450 return false;
451}
452
453} // namespace android