blob: 5bf22f20e4dc53507db89dad43a4fc695b871a16 [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>
20#include <android/binder_status.h>
21#include <audio_utils/primitives.h>
22#include <math.h>
23#include <system/audio.h>
24#include <time.h>
25#include <Utils.h>
26
27#ifndef BUILD_FLOAT
28 #error AIDL Visualizer only support float 32bits, make sure add cflags -DBUILD_FLOAT,
29#endif
30
31using android::hardware::audio::common::getChannelCount;
32
33namespace aidl::android::hardware::audio::effect {
34
35VisualizerContext::VisualizerContext(int statusDepth, const Parameter::Common& common)
36 : EffectContext(statusDepth, common) {
37}
38
39VisualizerContext::~VisualizerContext() {
40 std::lock_guard lg(mMutex);
41 LOG(DEBUG) << __func__;
42 mState = State::UNINITIALIZED;
43}
44
45RetCode VisualizerContext::initParams(const Parameter::Common& common) {
46 std::lock_guard lg(mMutex);
47 LOG(DEBUG) << __func__;
48 if (common.input != common.output) {
49 LOG(ERROR) << __func__ << " mismatch input: " << common.input.toString()
50 << " and output: " << common.output.toString();
51 return RetCode::ERROR_ILLEGAL_PARAMETER;
52 }
53
54 mState = State::INITIALIZED;
55 auto channelCount = getChannelCount(common.input.base.channelMask);
56#ifdef SUPPORT_MC
57 if (channelCount < 1 || channelCount > FCC_LIMIT) return RetCode::ERROR_ILLEGAL_PARAMETER;
58#else
59 if (channelCount != FCC_2) return RetCode::ERROR_ILLEGAL_PARAMETER;
60#endif
61 mChannelCount = channelCount;
62 mCommon = common;
63 return RetCode::SUCCESS;
64}
65
66RetCode VisualizerContext::enable() {
67 std::lock_guard lg(mMutex);
68 if (mState != State::INITIALIZED) {
69 return RetCode::ERROR_EFFECT_LIB_ERROR;
70 }
71 mState = State::ACTIVE;
72 return RetCode::SUCCESS;
73}
74
75RetCode VisualizerContext::disable() {
76 std::lock_guard lg(mMutex);
77 if (mState != State::ACTIVE) {
78 return RetCode::ERROR_EFFECT_LIB_ERROR;
79 }
80 mState = State::INITIALIZED;
81 return RetCode::SUCCESS;
82}
83
84void VisualizerContext::reset() {
85 std::lock_guard lg(mMutex);
86 std::fill_n(mCaptureBuf.begin(), kMaxCaptureBufSize, 0x80);
87}
88
89RetCode VisualizerContext::setCaptureSamples(int samples) {
90 std::lock_guard lg(mMutex);
Shunkai Yao05b190a2022-12-22 00:21:31 +000091 mCaptureSamples = samples;
92 return RetCode::SUCCESS;
93}
94int VisualizerContext::getCaptureSamples() {
95 std::lock_guard lg(mMutex);
96 return mCaptureSamples;
97}
98
99RetCode VisualizerContext::setMeasurementMode(Visualizer::MeasurementMode mode) {
100 std::lock_guard lg(mMutex);
101 mMeasurementMode = mode;
102 return RetCode::SUCCESS;
103}
104Visualizer::MeasurementMode VisualizerContext::getMeasurementMode() {
105 std::lock_guard lg(mMutex);
106 return mMeasurementMode;
107}
108
109RetCode VisualizerContext::setScalingMode(Visualizer::ScalingMode mode) {
110 std::lock_guard lg(mMutex);
111 mScalingMode = mode;
112 return RetCode::SUCCESS;
113}
114Visualizer::ScalingMode VisualizerContext::getScalingMode() {
115 std::lock_guard lg(mMutex);
116 return mScalingMode;
117}
118
119RetCode VisualizerContext::setDownstreamLatency(int latency) {
Shunkai Yao05b190a2022-12-22 00:21:31 +0000120 std::lock_guard lg(mMutex);
121 mDownstreamLatency = latency;
122 return RetCode::SUCCESS;
123}
124
Shunkai Yao6b857c92023-02-13 17:44:52 +0000125int VisualizerContext::getDownstreamLatency() {
126 std::lock_guard lg(mMutex);
127 return mDownstreamLatency;
128}
129
Shunkai Yao05b190a2022-12-22 00:21:31 +0000130uint32_t VisualizerContext::getDeltaTimeMsFromUpdatedTime_l() {
131 uint32_t deltaMs = 0;
132 if (mBufferUpdateTime.tv_sec != 0) {
133 struct timespec ts;
134 if (clock_gettime(CLOCK_MONOTONIC, &ts) == 0) {
135 time_t secs = ts.tv_sec - mBufferUpdateTime.tv_sec;
136 long nsec = ts.tv_nsec - mBufferUpdateTime.tv_nsec;
137 if (nsec < 0) {
138 --secs;
139 nsec += 1000000000;
140 }
141 deltaMs = secs * 1000 + nsec / 1000000;
142 }
143 }
144 return deltaMs;
145}
146
Shunkai Yao6b857c92023-02-13 17:44:52 +0000147Visualizer::Measurement VisualizerContext::getMeasure() {
Shunkai Yao05b190a2022-12-22 00:21:31 +0000148 uint16_t peakU16 = 0;
149 float sumRmsSquared = 0.0f;
150 uint8_t nbValidMeasurements = 0;
151
152 {
153 std::lock_guard lg(mMutex);
154 // reset measurements if last measurement was too long ago (which implies stored
155 // measurements aren't relevant anymore and shouldn't bias the new one)
156 const uint32_t delayMs = getDeltaTimeMsFromUpdatedTime_l();
157 if (delayMs > kDiscardMeasurementsTimeMs) {
158 LOG(INFO) << __func__ << " Discarding " << delayMs << " ms old measurements";
159 for (uint32_t i = 0; i < mMeasurementWindowSizeInBuffers; i++) {
160 mPastMeasurements[i].mIsValid = false;
161 mPastMeasurements[i].mPeakU16 = 0;
162 mPastMeasurements[i].mRmsSquared = 0;
163 }
164 mMeasurementBufferIdx = 0;
165 } else {
166 // only use actual measurements, otherwise the first RMS measure happening before
167 // MEASUREMENT_WINDOW_MAX_SIZE_IN_BUFFERS have been played will always be artificially
168 // low
169 for (uint32_t i = 0; i < mMeasurementWindowSizeInBuffers; i++) {
170 if (mPastMeasurements[i].mIsValid) {
171 if (mPastMeasurements[i].mPeakU16 > peakU16) {
172 peakU16 = mPastMeasurements[i].mPeakU16;
173 }
174 sumRmsSquared += mPastMeasurements[i].mRmsSquared;
175 nbValidMeasurements++;
176 }
177 }
178 }
179 }
180
181 float rms = nbValidMeasurements == 0 ? 0.0f : sqrtf(sumRmsSquared / nbValidMeasurements);
Shunkai Yao6b857c92023-02-13 17:44:52 +0000182 Visualizer::Measurement measure;
Shunkai Yao05b190a2022-12-22 00:21:31 +0000183 // convert from I16 sample values to mB and write results
184 measure.rms = (rms < 0.000016f) ? -9600 : (int32_t)(2000 * log10(rms / 32767.0f));
185 measure.peak = (peakU16 == 0) ? -9600 : (int32_t)(2000 * log10(peakU16 / 32767.0f));
186 LOG(INFO) << __func__ << " peak " << peakU16 << " (" << measure.peak << "mB), rms " << rms
187 << " (" << measure.rms << "mB)";
188 return measure;
189}
190
191std::vector<uint8_t> VisualizerContext::capture() {
192 std::vector<uint8_t> result;
193 std::lock_guard lg(mMutex);
Shunkai Yaob851b3c2023-02-27 22:50:00 +0000194 // cts android.media.audio.cts.VisualizerTest expecting silence data when effect not running
195 // RETURN_VALUE_IF(mState != State::ACTIVE, result, "illegalState");
196 if (mState != State::ACTIVE) {
197 result.resize(mCaptureSamples);
198 memset(result.data(), 0x80, mCaptureSamples);
199 return result;
200 }
Shunkai Yao05b190a2022-12-22 00:21:31 +0000201
Shunkai Yaob851b3c2023-02-27 22:50:00 +0000202 const uint32_t deltaMs = getDeltaTimeMsFromUpdatedTime_l();
Shunkai Yao05b190a2022-12-22 00:21:31 +0000203 // if audio framework has stopped playing audio although the effect is still active we must
204 // clear the capture buffer to return silence
205 if ((mLastCaptureIdx == mCaptureIdx) && (mBufferUpdateTime.tv_sec != 0) &&
206 (deltaMs > kMaxStallTimeMs)) {
207 LOG(INFO) << __func__ << " capture going to idle";
208 mBufferUpdateTime.tv_sec = 0;
209 return result;
210 }
211 int32_t latencyMs = mDownstreamLatency;
212 latencyMs -= deltaMs;
213 if (latencyMs < 0) {
214 latencyMs = 0;
215 }
216 uint32_t deltaSamples = mCaptureSamples + mCommon.input.base.sampleRate * latencyMs / 1000;
217
218 // large sample rate, latency, or capture size, could cause overflow.
219 // do not offset more than the size of buffer.
220 if (deltaSamples > kMaxCaptureBufSize) {
221 android_errorWriteLog(0x534e4554, "31781965");
222 deltaSamples = kMaxCaptureBufSize;
223 }
224
225 int32_t capturePoint;
226 //capturePoint = (int32_t)mCaptureIdx - deltaSamples;
227 __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;
231 if (size > mCaptureSamples) {
232 size = mCaptureSamples;
233 }
234 result.insert(result.end(), &mCaptureBuf[kMaxCaptureBufSize + capturePoint],
235 &mCaptureBuf[kMaxCaptureBufSize + capturePoint + size]);
236 mCaptureSamples -= size;
237 capturePoint = 0;
238 }
239 result.insert(result.end(), &mCaptureBuf[capturePoint],
240 &mCaptureBuf[capturePoint + mCaptureSamples]);
241 mLastCaptureIdx = mCaptureIdx;
242 return result;
243}
244
245IEffect::Status VisualizerContext::process(float* in, float* out, int samples) {
246 IEffect::Status result = {STATUS_NOT_ENOUGH_DATA, 0, 0};
247 RETURN_VALUE_IF(in == nullptr || out == nullptr || samples == 0, result, "dataBufferError");
248
249 std::lock_guard lg(mMutex);
250 result.status = STATUS_INVALID_OPERATION;
251 RETURN_VALUE_IF(mState != State::ACTIVE, result, "stateNotActive");
252 LOG(DEBUG) << __func__ << " in " << in << " out " << out << " sample " << samples;
253 // perform measurements if needed
254 if (mMeasurementMode == Visualizer::MeasurementMode::PEAK_RMS) {
255 // find the peak and RMS squared for the new buffer
256 float rmsSqAcc = 0;
257 float maxSample = 0.f;
258 for (size_t inIdx = 0; inIdx < (unsigned)samples; ++inIdx) {
259 maxSample = fmax(maxSample, fabs(in[inIdx]));
260 rmsSqAcc += in[inIdx] * in[inIdx];
261 }
262 maxSample *= 1 << 15; // scale to int16_t, with exactly 1 << 15 representing positive num.
263 rmsSqAcc *= 1 << 30; // scale to int16_t * 2
264 mPastMeasurements[mMeasurementBufferIdx] = {
265 .mPeakU16 = (uint16_t)maxSample,
266 .mRmsSquared = rmsSqAcc / samples,
267 .mIsValid = true };
268 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