blob: d0f1ec6064c1b349b3b690c69af4beb33cda97b1 [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) {
145 mCaptureThread = new CaptureThread(this, rate, ((flags & CAPTURE_CALL_JAVA) != 0));
146 }
147 ALOGV("setCaptureCallBack() rate: %d thread %p flags 0x%08x",
148 rate, mCaptureThread.get(), mCaptureFlags);
149 return NO_ERROR;
150}
151
152status_t Visualizer::setCaptureSize(uint32_t size)
153{
154 if (size > VISUALIZER_CAPTURE_SIZE_MAX ||
155 size < VISUALIZER_CAPTURE_SIZE_MIN ||
156 popcount(size) != 1) {
157 return BAD_VALUE;
158 }
159
160 Mutex::Autolock _l(mCaptureLock);
161 if (mEnabled) {
162 return INVALID_OPERATION;
163 }
164
165 uint32_t buf32[sizeof(effect_param_t) / sizeof(uint32_t) + 2];
166 effect_param_t *p = (effect_param_t *)buf32;
167
168 p->psize = sizeof(uint32_t);
169 p->vsize = sizeof(uint32_t);
170 *(int32_t *)p->data = VISUALIZER_PARAM_CAPTURE_SIZE;
171 *((int32_t *)p->data + 1)= size;
172 status_t status = setParameter(p);
173
174 ALOGV("setCaptureSize size %d status %d p->status %d", size, status, p->status);
175
176 if (status == NO_ERROR) {
177 status = p->status;
178 if (status == NO_ERROR) {
179 mCaptureSize = size;
180 }
181 }
182
183 return status;
184}
185
186status_t Visualizer::setScalingMode(uint32_t mode) {
187 if ((mode != VISUALIZER_SCALING_MODE_NORMALIZED)
188 && (mode != VISUALIZER_SCALING_MODE_AS_PLAYED)) {
189 return BAD_VALUE;
190 }
191
192 Mutex::Autolock _l(mCaptureLock);
193
194 uint32_t buf32[sizeof(effect_param_t) / sizeof(uint32_t) + 2];
195 effect_param_t *p = (effect_param_t *)buf32;
196
197 p->psize = sizeof(uint32_t);
198 p->vsize = sizeof(uint32_t);
199 *(int32_t *)p->data = VISUALIZER_PARAM_SCALING_MODE;
200 *((int32_t *)p->data + 1)= mode;
201 status_t status = setParameter(p);
202
203 ALOGV("setScalingMode mode %d status %d p->status %d", mode, status, p->status);
204
205 if (status == NO_ERROR) {
206 status = p->status;
207 if (status == NO_ERROR) {
208 mScalingMode = mode;
209 }
210 }
211
212 return status;
213}
214
215status_t Visualizer::setMeasurementMode(uint32_t mode) {
216 if ((mode != MEASUREMENT_MODE_NONE)
217 //Note: needs to be handled as a mask when more measurement modes are added
218 && ((mode & MEASUREMENT_MODE_PEAK_RMS) != mode)) {
219 return BAD_VALUE;
220 }
221
222 Mutex::Autolock _l(mCaptureLock);
223
224 uint32_t buf32[sizeof(effect_param_t) / sizeof(uint32_t) + 2];
225 effect_param_t *p = (effect_param_t *)buf32;
226
227 p->psize = sizeof(uint32_t);
228 p->vsize = sizeof(uint32_t);
229 *(int32_t *)p->data = VISUALIZER_PARAM_MEASUREMENT_MODE;
230 *((int32_t *)p->data + 1)= mode;
231 status_t status = setParameter(p);
232
233 ALOGV("setMeasurementMode mode %d status %d p->status %d", mode, status, p->status);
234
235 if (status == NO_ERROR) {
236 status = p->status;
237 if (status == NO_ERROR) {
238 mMeasurementMode = mode;
239 }
240 }
241 return status;
242}
243
244status_t Visualizer::getIntMeasurements(uint32_t type, uint32_t number, int32_t *measurements) {
245 if (mMeasurementMode == MEASUREMENT_MODE_NONE) {
246 ALOGE("Cannot retrieve int measurements, no measurement mode set");
247 return INVALID_OPERATION;
248 }
249 if (!(mMeasurementMode & type)) {
250 // measurement type has not been set on this Visualizer
251 ALOGE("Cannot retrieve int measurements, requested measurement mode 0x%x not set(0x%x)",
252 type, mMeasurementMode);
253 return INVALID_OPERATION;
254 }
255 // only peak+RMS measurement supported
256 if ((type != MEASUREMENT_MODE_PEAK_RMS)
257 // for peak+RMS measurement, the results are 2 int32_t values
258 || (number != 2)) {
259 ALOGE("Cannot retrieve int measurements, MEASUREMENT_MODE_PEAK_RMS returns 2 ints, not %d",
260 number);
261 return BAD_VALUE;
262 }
263
264 status_t status = NO_ERROR;
265 if (mEnabled) {
266 uint32_t replySize = number * sizeof(int32_t);
267 status = command(VISUALIZER_CMD_MEASURE,
268 sizeof(uint32_t) /*cmdSize*/,
269 &type /*cmdData*/,
270 &replySize, measurements);
271 ALOGV("getMeasurements() command returned %d", status);
272 if ((status == NO_ERROR) && (replySize == 0)) {
273 status = NOT_ENOUGH_DATA;
274 }
275 } else {
276 ALOGV("getMeasurements() disabled");
277 return INVALID_OPERATION;
278 }
279 return status;
280}
281
282status_t Visualizer::getWaveForm(uint8_t *waveform)
283{
284 if (waveform == NULL) {
285 return BAD_VALUE;
286 }
287 if (mCaptureSize == 0) {
288 return NO_INIT;
289 }
290
291 status_t status = NO_ERROR;
292 if (mEnabled) {
293 uint32_t replySize = mCaptureSize;
294 status = command(VISUALIZER_CMD_CAPTURE, 0, NULL, &replySize, waveform);
295 ALOGV("getWaveForm() command returned %d", status);
296 if ((status == NO_ERROR) && (replySize == 0)) {
297 status = NOT_ENOUGH_DATA;
298 }
299 } else {
300 ALOGV("getWaveForm() disabled");
301 memset(waveform, 0x80, mCaptureSize);
302 }
303 return status;
304}
305
306status_t Visualizer::getFft(uint8_t *fft)
307{
308 if (fft == NULL) {
309 return BAD_VALUE;
310 }
311 if (mCaptureSize == 0) {
312 return NO_INIT;
313 }
314
315 status_t status = NO_ERROR;
316 if (mEnabled) {
317 uint8_t buf[mCaptureSize];
318 status = getWaveForm(buf);
319 if (status == NO_ERROR) {
320 status = doFft(fft, buf);
321 }
322 } else {
323 memset(fft, 0, mCaptureSize);
324 }
325 return status;
326}
327
328status_t Visualizer::doFft(uint8_t *fft, uint8_t *waveform)
329{
330 int32_t workspace[mCaptureSize >> 1];
331 int32_t nonzero = 0;
332
333 for (uint32_t i = 0; i < mCaptureSize; i += 2) {
334 workspace[i >> 1] =
335 ((waveform[i] ^ 0x80) << 24) | ((waveform[i + 1] ^ 0x80) << 8);
336 nonzero |= workspace[i >> 1];
337 }
338
339 if (nonzero) {
340 fixed_fft_real(mCaptureSize >> 1, workspace);
341 }
342
343 for (uint32_t i = 0; i < mCaptureSize; i += 2) {
344 short tmp = workspace[i >> 1] >> 21;
345 while (tmp > 127 || tmp < -128) tmp >>= 1;
346 fft[i] = tmp;
347 tmp = workspace[i >> 1];
348 tmp >>= 5;
349 while (tmp > 127 || tmp < -128) tmp >>= 1;
350 fft[i + 1] = tmp;
351 }
352
353 return NO_ERROR;
354}
355
356void Visualizer::periodicCapture()
357{
358 Mutex::Autolock _l(mCaptureLock);
359 ALOGV("periodicCapture() %p mCaptureCallBack %p mCaptureFlags 0x%08x",
360 this, mCaptureCallBack, mCaptureFlags);
361 if (mCaptureCallBack != NULL &&
362 (mCaptureFlags & (CAPTURE_WAVEFORM|CAPTURE_FFT)) &&
363 mCaptureSize != 0) {
364 uint8_t waveform[mCaptureSize];
365 status_t status = getWaveForm(waveform);
366 if (status != NO_ERROR) {
367 return;
368 }
369 uint8_t fft[mCaptureSize];
370 if (mCaptureFlags & CAPTURE_FFT) {
371 status = doFft(fft, waveform);
372 }
373 if (status != NO_ERROR) {
374 return;
375 }
376 uint8_t *wavePtr = NULL;
377 uint8_t *fftPtr = NULL;
378 uint32_t waveSize = 0;
379 uint32_t fftSize = 0;
380 if (mCaptureFlags & CAPTURE_WAVEFORM) {
381 wavePtr = waveform;
382 waveSize = mCaptureSize;
383 }
384 if (mCaptureFlags & CAPTURE_FFT) {
385 fftPtr = fft;
386 fftSize = mCaptureSize;
387 }
388 mCaptureCallBack(mCaptureCbkUser, waveSize, wavePtr, fftSize, fftPtr, mSampleRate);
389 }
390}
391
392uint32_t Visualizer::initCaptureSize()
393{
394 uint32_t buf32[sizeof(effect_param_t) / sizeof(uint32_t) + 2];
395 effect_param_t *p = (effect_param_t *)buf32;
396
397 p->psize = sizeof(uint32_t);
398 p->vsize = sizeof(uint32_t);
399 *(int32_t *)p->data = VISUALIZER_PARAM_CAPTURE_SIZE;
400 status_t status = getParameter(p);
401
402 if (status == NO_ERROR) {
403 status = p->status;
404 }
405
406 uint32_t size = 0;
407 if (status == NO_ERROR) {
408 size = *((int32_t *)p->data + 1);
409 }
410 mCaptureSize = size;
411
412 ALOGV("initCaptureSize size %d status %d", mCaptureSize, status);
413
414 return size;
415}
416
Mikhail Naganov97f6e722022-04-19 21:24:30 +0000417void Visualizer::initSampleRate()
418{
419 audio_config_base_t inputConfig, outputConfig;
420 status_t status = getConfigs(&inputConfig, &outputConfig);
421 if (status == NO_ERROR) {
422 mSampleRate = outputConfig.sample_rate * 1000;
423 }
424 ALOGV("%s sample rate %d status %d", __func__, mSampleRate, status);
425}
426
Marco Nelissen1a7b4152019-10-23 09:21:55 -0700427void Visualizer::controlStatusChanged(bool controlGranted) {
428 if (controlGranted) {
429 // this Visualizer instance regained control of the effect, reset the scaling mode
430 // and capture size as has been cached through it.
431 ALOGV("controlStatusChanged(true) causes effect parameter reset:");
432 ALOGV(" scaling mode reset to %d", mScalingMode);
433 setScalingMode(mScalingMode);
434 ALOGV(" capture size reset to %d", mCaptureSize);
435 setCaptureSize(mCaptureSize);
436 }
437 AudioEffect::controlStatusChanged(controlGranted);
438}
439
440//-------------------------------------------------------------------------
441
442Visualizer::CaptureThread::CaptureThread(Visualizer* receiver, uint32_t captureRate,
443 bool bCanCallJava)
444 : Thread(bCanCallJava), mReceiver(receiver)
445{
446 mSleepTimeUs = 1000000000 / captureRate;
447 ALOGV("CaptureThread cstor %p captureRate %d mSleepTimeUs %d", this, captureRate, mSleepTimeUs);
448}
449
450bool Visualizer::CaptureThread::threadLoop()
451{
452 ALOGV("CaptureThread %p enter", this);
453 sp<Visualizer> receiver = mReceiver.promote();
454 if (receiver == NULL) {
455 return false;
456 }
457 while (!exitPending())
458 {
459 usleep(mSleepTimeUs);
460 receiver->periodicCapture();
461 }
462 ALOGV("CaptureThread %p exiting", this);
463 return false;
464}
465
466} // namespace android