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