blob: 5d2bb3a59a17a6db7c7e07bd6ac00718ff08af11 [file] [log] [blame]
Shunkai Yao05b190a2022-12-22 00:21:31 +00001/*
2 * Copyright (C) 2022 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17#include "VisualizerContext.h"
18
19#include <algorithm>
Mikhail Naganov6352e822023-03-09 18:22:36 -080020#include <math.h>
21#include <time.h>
22
Shunkai Yao05b190a2022-12-22 00:21:31 +000023#include <android/binder_status.h>
24#include <audio_utils/primitives.h>
Shunkai Yao05b190a2022-12-22 00:21:31 +000025#include <system/audio.h>
Shunkai Yao05b190a2022-12-22 00:21:31 +000026#include <Utils.h>
27
28#ifndef BUILD_FLOAT
29 #error AIDL Visualizer only support float 32bits, make sure add cflags -DBUILD_FLOAT,
30#endif
31
Mikhail Naganov6352e822023-03-09 18:22:36 -080032using aidl::android::hardware::audio::common::getChannelCount;
Shunkai Yao05b190a2022-12-22 00:21:31 +000033
34namespace aidl::android::hardware::audio::effect {
35
36VisualizerContext::VisualizerContext(int statusDepth, const Parameter::Common& common)
37 : EffectContext(statusDepth, common) {
38}
39
40VisualizerContext::~VisualizerContext() {
41 std::lock_guard lg(mMutex);
42 LOG(DEBUG) << __func__;
43 mState = State::UNINITIALIZED;
44}
45
46RetCode VisualizerContext::initParams(const Parameter::Common& common) {
47 std::lock_guard lg(mMutex);
48 LOG(DEBUG) << __func__;
49 if (common.input != common.output) {
50 LOG(ERROR) << __func__ << " mismatch input: " << common.input.toString()
51 << " and output: " << common.output.toString();
52 return RetCode::ERROR_ILLEGAL_PARAMETER;
53 }
54
55 mState = State::INITIALIZED;
56 auto channelCount = getChannelCount(common.input.base.channelMask);
57#ifdef SUPPORT_MC
58 if (channelCount < 1 || channelCount > FCC_LIMIT) return RetCode::ERROR_ILLEGAL_PARAMETER;
59#else
60 if (channelCount != FCC_2) return RetCode::ERROR_ILLEGAL_PARAMETER;
61#endif
62 mChannelCount = channelCount;
63 mCommon = common;
Shunkai Yao22796602023-11-21 04:13:22 +000064 std::fill(mCaptureBuf.begin(), mCaptureBuf.end(), 0x80);
Shunkai Yao05b190a2022-12-22 00:21:31 +000065 return RetCode::SUCCESS;
66}
67
68RetCode VisualizerContext::enable() {
69 std::lock_guard lg(mMutex);
70 if (mState != State::INITIALIZED) {
71 return RetCode::ERROR_EFFECT_LIB_ERROR;
72 }
73 mState = State::ACTIVE;
74 return RetCode::SUCCESS;
75}
76
77RetCode VisualizerContext::disable() {
78 std::lock_guard lg(mMutex);
79 if (mState != State::ACTIVE) {
80 return RetCode::ERROR_EFFECT_LIB_ERROR;
81 }
82 mState = State::INITIALIZED;
83 return RetCode::SUCCESS;
84}
85
86void VisualizerContext::reset() {
87 std::lock_guard lg(mMutex);
Shunkai Yao22796602023-11-21 04:13:22 +000088 std::fill(mCaptureBuf.begin(), mCaptureBuf.end(), 0x80);
Shunkai Yao05b190a2022-12-22 00:21:31 +000089}
90
91RetCode VisualizerContext::setCaptureSamples(int samples) {
92 std::lock_guard lg(mMutex);
Shunkai Yao05b190a2022-12-22 00:21:31 +000093 mCaptureSamples = samples;
94 return RetCode::SUCCESS;
95}
96int VisualizerContext::getCaptureSamples() {
97 std::lock_guard lg(mMutex);
98 return mCaptureSamples;
99}
100
101RetCode VisualizerContext::setMeasurementMode(Visualizer::MeasurementMode mode) {
102 std::lock_guard lg(mMutex);
103 mMeasurementMode = mode;
104 return RetCode::SUCCESS;
105}
106Visualizer::MeasurementMode VisualizerContext::getMeasurementMode() {
107 std::lock_guard lg(mMutex);
108 return mMeasurementMode;
109}
110
111RetCode VisualizerContext::setScalingMode(Visualizer::ScalingMode mode) {
112 std::lock_guard lg(mMutex);
113 mScalingMode = mode;
114 return RetCode::SUCCESS;
115}
116Visualizer::ScalingMode VisualizerContext::getScalingMode() {
117 std::lock_guard lg(mMutex);
118 return mScalingMode;
119}
120
121RetCode VisualizerContext::setDownstreamLatency(int latency) {
Shunkai Yao05b190a2022-12-22 00:21:31 +0000122 std::lock_guard lg(mMutex);
123 mDownstreamLatency = latency;
124 return RetCode::SUCCESS;
125}
126
Shunkai Yao6b857c92023-02-13 17:44:52 +0000127int VisualizerContext::getDownstreamLatency() {
128 std::lock_guard lg(mMutex);
129 return mDownstreamLatency;
130}
131
Shunkai Yao05b190a2022-12-22 00:21:31 +0000132uint32_t VisualizerContext::getDeltaTimeMsFromUpdatedTime_l() {
133 uint32_t deltaMs = 0;
134 if (mBufferUpdateTime.tv_sec != 0) {
135 struct timespec ts;
136 if (clock_gettime(CLOCK_MONOTONIC, &ts) == 0) {
137 time_t secs = ts.tv_sec - mBufferUpdateTime.tv_sec;
138 long nsec = ts.tv_nsec - mBufferUpdateTime.tv_nsec;
139 if (nsec < 0) {
140 --secs;
141 nsec += 1000000000;
142 }
143 deltaMs = secs * 1000 + nsec / 1000000;
144 }
145 }
146 return deltaMs;
147}
148
Shunkai Yao6b857c92023-02-13 17:44:52 +0000149Visualizer::Measurement VisualizerContext::getMeasure() {
Shunkai Yao05b190a2022-12-22 00:21:31 +0000150 uint16_t peakU16 = 0;
151 float sumRmsSquared = 0.0f;
152 uint8_t nbValidMeasurements = 0;
153
154 {
155 std::lock_guard lg(mMutex);
156 // reset measurements if last measurement was too long ago (which implies stored
157 // measurements aren't relevant anymore and shouldn't bias the new one)
158 const uint32_t delayMs = getDeltaTimeMsFromUpdatedTime_l();
159 if (delayMs > kDiscardMeasurementsTimeMs) {
160 LOG(INFO) << __func__ << " Discarding " << delayMs << " ms old measurements";
161 for (uint32_t i = 0; i < mMeasurementWindowSizeInBuffers; i++) {
162 mPastMeasurements[i].mIsValid = false;
163 mPastMeasurements[i].mPeakU16 = 0;
164 mPastMeasurements[i].mRmsSquared = 0;
165 }
166 mMeasurementBufferIdx = 0;
167 } else {
168 // only use actual measurements, otherwise the first RMS measure happening before
169 // MEASUREMENT_WINDOW_MAX_SIZE_IN_BUFFERS have been played will always be artificially
170 // low
171 for (uint32_t i = 0; i < mMeasurementWindowSizeInBuffers; i++) {
172 if (mPastMeasurements[i].mIsValid) {
173 if (mPastMeasurements[i].mPeakU16 > peakU16) {
174 peakU16 = mPastMeasurements[i].mPeakU16;
175 }
176 sumRmsSquared += mPastMeasurements[i].mRmsSquared;
177 nbValidMeasurements++;
178 }
179 }
180 }
181 }
182
183 float rms = nbValidMeasurements == 0 ? 0.0f : sqrtf(sumRmsSquared / nbValidMeasurements);
Shunkai Yao6b857c92023-02-13 17:44:52 +0000184 Visualizer::Measurement measure;
Shunkai Yao05b190a2022-12-22 00:21:31 +0000185 // convert from I16 sample values to mB and write results
186 measure.rms = (rms < 0.000016f) ? -9600 : (int32_t)(2000 * log10(rms / 32767.0f));
187 measure.peak = (peakU16 == 0) ? -9600 : (int32_t)(2000 * log10(peakU16 / 32767.0f));
188 LOG(INFO) << __func__ << " peak " << peakU16 << " (" << measure.peak << "mB), rms " << rms
189 << " (" << measure.rms << "mB)";
190 return measure;
191}
192
193std::vector<uint8_t> VisualizerContext::capture() {
Shunkai Yao05b190a2022-12-22 00:21:31 +0000194 std::lock_guard lg(mMutex);
Shunkai Yao22796602023-11-21 04:13:22 +0000195 uint32_t captureSamples = mCaptureSamples;
196 std::vector<uint8_t> result(captureSamples, 0x80);
Shunkai Yaob851b3c2023-02-27 22:50:00 +0000197 // cts android.media.audio.cts.VisualizerTest expecting silence data when effect not running
198 // RETURN_VALUE_IF(mState != State::ACTIVE, result, "illegalState");
199 if (mState != State::ACTIVE) {
Shunkai Yaob851b3c2023-02-27 22:50:00 +0000200 return result;
201 }
Shunkai Yao05b190a2022-12-22 00:21:31 +0000202
Shunkai Yaob851b3c2023-02-27 22:50:00 +0000203 const uint32_t deltaMs = getDeltaTimeMsFromUpdatedTime_l();
Shunkai Yao05b190a2022-12-22 00:21:31 +0000204 // if audio framework has stopped playing audio although the effect is still active we must
205 // clear the capture buffer to return silence
206 if ((mLastCaptureIdx == mCaptureIdx) && (mBufferUpdateTime.tv_sec != 0) &&
207 (deltaMs > kMaxStallTimeMs)) {
208 LOG(INFO) << __func__ << " capture going to idle";
209 mBufferUpdateTime.tv_sec = 0;
210 return result;
211 }
212 int32_t latencyMs = mDownstreamLatency;
213 latencyMs -= deltaMs;
214 if (latencyMs < 0) {
215 latencyMs = 0;
216 }
Shunkai Yao22796602023-11-21 04:13:22 +0000217 uint32_t deltaSamples = captureSamples + mCommon.input.base.sampleRate * latencyMs / 1000;
Shunkai Yao05b190a2022-12-22 00:21:31 +0000218
219 // large sample rate, latency, or capture size, could cause overflow.
220 // do not offset more than the size of buffer.
221 if (deltaSamples > kMaxCaptureBufSize) {
222 android_errorWriteLog(0x534e4554, "31781965");
223 deltaSamples = kMaxCaptureBufSize;
224 }
225
226 int32_t capturePoint;
Shunkai Yao05b190a2022-12-22 00:21:31 +0000227 __builtin_sub_overflow((int32_t) mCaptureIdx, deltaSamples, &capturePoint);
228 // a negative capturePoint means we wrap the buffer.
229 if (capturePoint < 0) {
230 uint32_t size = -capturePoint;
Shunkai Yao22796602023-11-21 04:13:22 +0000231 if (size > captureSamples) {
232 size = captureSamples;
Shunkai Yao05b190a2022-12-22 00:21:31 +0000233 }
Shunkai Yao22796602023-11-21 04:13:22 +0000234 std::copy(std::begin(mCaptureBuf) + kMaxCaptureBufSize - size,
235 std::begin(mCaptureBuf) + kMaxCaptureBufSize, result.begin());
236 captureSamples -= size;
Shunkai Yao05b190a2022-12-22 00:21:31 +0000237 capturePoint = 0;
238 }
Shunkai Yao22796602023-11-21 04:13:22 +0000239 std::copy(std::begin(mCaptureBuf) + capturePoint,
240 std::begin(mCaptureBuf) + capturePoint + captureSamples,
241 result.begin() + mCaptureSamples - captureSamples);
Shunkai Yao05b190a2022-12-22 00:21:31 +0000242 mLastCaptureIdx = mCaptureIdx;
243 return result;
244}
245
246IEffect::Status VisualizerContext::process(float* in, float* out, int samples) {
247 IEffect::Status result = {STATUS_NOT_ENOUGH_DATA, 0, 0};
248 RETURN_VALUE_IF(in == nullptr || out == nullptr || samples == 0, result, "dataBufferError");
249
250 std::lock_guard lg(mMutex);
251 result.status = STATUS_INVALID_OPERATION;
252 RETURN_VALUE_IF(mState != State::ACTIVE, result, "stateNotActive");
253 LOG(DEBUG) << __func__ << " in " << in << " out " << out << " sample " << samples;
254 // perform measurements if needed
255 if (mMeasurementMode == Visualizer::MeasurementMode::PEAK_RMS) {
256 // find the peak and RMS squared for the new buffer
257 float rmsSqAcc = 0;
258 float maxSample = 0.f;
Shunkai Yao22796602023-11-21 04:13:22 +0000259 for (size_t inIdx = 0; inIdx < (unsigned) samples; ++inIdx) {
Shunkai Yao05b190a2022-12-22 00:21:31 +0000260 maxSample = fmax(maxSample, fabs(in[inIdx]));
261 rmsSqAcc += in[inIdx] * in[inIdx];
262 }
263 maxSample *= 1 << 15; // scale to int16_t, with exactly 1 << 15 representing positive num.
264 rmsSqAcc *= 1 << 30; // scale to int16_t * 2
Shunkai Yao22796602023-11-21 04:13:22 +0000265 mPastMeasurements[mMeasurementBufferIdx] = {.mIsValid = true,
266 .mPeakU16 = (uint16_t)maxSample,
267 .mRmsSquared = rmsSqAcc / samples};
Shunkai Yao05b190a2022-12-22 00:21:31 +0000268 if (++mMeasurementBufferIdx >= mMeasurementWindowSizeInBuffers) {
269 mMeasurementBufferIdx = 0;
270 }
271 }
272
273 float fscale; // multiplicative scale
274 if (mScalingMode == Visualizer::ScalingMode::NORMALIZED) {
275 // derive capture scaling factor from peak value in current buffer
276 // this gives more interesting captures for display.
277 float maxSample = 0.f;
278 for (size_t inIdx = 0; inIdx < (unsigned)samples; ) {
279 // we reconstruct the actual summed value to ensure proper normalization
280 // for multichannel outputs (channels > 2 may often be 0).
281 float smp = 0.f;
282 for (int i = 0; i < mChannelCount; ++i) {
283 smp += in[inIdx++];
284 }
285 maxSample = fmax(maxSample, fabs(smp));
286 }
287 if (maxSample > 0.f) {
288 fscale = 0.99f / maxSample;
289 int exp; // unused
290 const float significand = frexp(fscale, &exp);
291 if (significand == 0.5f) {
292 fscale *= 255.f / 256.f; // avoid returning unaltered PCM signal
293 }
294 } else {
295 // scale doesn't matter, the values are all 0.
296 fscale = 1.f;
297 }
298 } else {
299 assert(mScalingMode == Visualizer::ScalingMode::AS_PLAYED);
300 // Note: if channels are uncorrelated, 1/sqrt(N) could be used at the risk of clipping.
301 fscale = 1.f / mChannelCount; // account for summing all the channels together.
302 }
303
304 uint32_t captIdx;
305 uint32_t inIdx;
306 for (inIdx = 0, captIdx = mCaptureIdx; inIdx < (unsigned)samples; captIdx++) {
307 // wrap
308 if (captIdx >= kMaxCaptureBufSize) {
309 captIdx = 0;
310 }
311
312 float smp = 0.f;
313 for (uint32_t i = 0; i < mChannelCount; ++i) {
314 smp += in[inIdx++];
315 }
316 mCaptureBuf[captIdx] = clamp8_from_float(smp * fscale);
317 }
318
319 // the following two should really be atomic, though it probably doesn't
320 // matter much for visualization purposes
321 mCaptureIdx = captIdx;
322 // update last buffer update time stamp
323 if (clock_gettime(CLOCK_MONOTONIC, &mBufferUpdateTime) < 0) {
324 mBufferUpdateTime.tv_sec = 0;
325 }
326
327 // TODO: handle access_mode
328 memcpy(out, in, samples * sizeof(float));
329 return {STATUS_OK, samples, samples};
330}
331
332} // namespace aidl::android::hardware::audio::effect