blob: 1965e0e8a0023144be942eb71760fbcb4999cf03 [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);
91 if (samples < 0 || (unsigned)samples > kMaxCaptureBufSize) {
92 LOG(ERROR) << __func__ << " captureSamples " << samples << " exceed valid range: 0 - "
93 << kMaxCaptureBufSize;
94 return RetCode::ERROR_ILLEGAL_PARAMETER;
95 }
96 mCaptureSamples = samples;
97 return RetCode::SUCCESS;
98}
99int VisualizerContext::getCaptureSamples() {
100 std::lock_guard lg(mMutex);
101 return mCaptureSamples;
102}
103
104RetCode VisualizerContext::setMeasurementMode(Visualizer::MeasurementMode mode) {
105 std::lock_guard lg(mMutex);
106 mMeasurementMode = mode;
107 return RetCode::SUCCESS;
108}
109Visualizer::MeasurementMode VisualizerContext::getMeasurementMode() {
110 std::lock_guard lg(mMutex);
111 return mMeasurementMode;
112}
113
114RetCode VisualizerContext::setScalingMode(Visualizer::ScalingMode mode) {
115 std::lock_guard lg(mMutex);
116 mScalingMode = mode;
117 return RetCode::SUCCESS;
118}
119Visualizer::ScalingMode VisualizerContext::getScalingMode() {
120 std::lock_guard lg(mMutex);
121 return mScalingMode;
122}
123
124RetCode VisualizerContext::setDownstreamLatency(int latency) {
Shunkai Yaoc045a032022-12-27 21:33:17 +0000125 if (latency < 0 || (unsigned)latency > kMaxLatencyMs) {
126 LOG(ERROR) << __func__ << " latency " << latency << " exceed valid range: 0 - "
127 << kMaxLatencyMs;
128 return RetCode::ERROR_ILLEGAL_PARAMETER;
129 }
Shunkai Yao05b190a2022-12-22 00:21:31 +0000130 std::lock_guard lg(mMutex);
131 mDownstreamLatency = latency;
132 return RetCode::SUCCESS;
133}
134
135uint32_t VisualizerContext::getDeltaTimeMsFromUpdatedTime_l() {
136 uint32_t deltaMs = 0;
137 if (mBufferUpdateTime.tv_sec != 0) {
138 struct timespec ts;
139 if (clock_gettime(CLOCK_MONOTONIC, &ts) == 0) {
140 time_t secs = ts.tv_sec - mBufferUpdateTime.tv_sec;
141 long nsec = ts.tv_nsec - mBufferUpdateTime.tv_nsec;
142 if (nsec < 0) {
143 --secs;
144 nsec += 1000000000;
145 }
146 deltaMs = secs * 1000 + nsec / 1000000;
147 }
148 }
149 return deltaMs;
150}
151
152Visualizer::GetOnlyParameters::Measurement VisualizerContext::getMeasure() {
153 uint16_t peakU16 = 0;
154 float sumRmsSquared = 0.0f;
155 uint8_t nbValidMeasurements = 0;
156
157 {
158 std::lock_guard lg(mMutex);
159 // reset measurements if last measurement was too long ago (which implies stored
160 // measurements aren't relevant anymore and shouldn't bias the new one)
161 const uint32_t delayMs = getDeltaTimeMsFromUpdatedTime_l();
162 if (delayMs > kDiscardMeasurementsTimeMs) {
163 LOG(INFO) << __func__ << " Discarding " << delayMs << " ms old measurements";
164 for (uint32_t i = 0; i < mMeasurementWindowSizeInBuffers; i++) {
165 mPastMeasurements[i].mIsValid = false;
166 mPastMeasurements[i].mPeakU16 = 0;
167 mPastMeasurements[i].mRmsSquared = 0;
168 }
169 mMeasurementBufferIdx = 0;
170 } else {
171 // only use actual measurements, otherwise the first RMS measure happening before
172 // MEASUREMENT_WINDOW_MAX_SIZE_IN_BUFFERS have been played will always be artificially
173 // low
174 for (uint32_t i = 0; i < mMeasurementWindowSizeInBuffers; i++) {
175 if (mPastMeasurements[i].mIsValid) {
176 if (mPastMeasurements[i].mPeakU16 > peakU16) {
177 peakU16 = mPastMeasurements[i].mPeakU16;
178 }
179 sumRmsSquared += mPastMeasurements[i].mRmsSquared;
180 nbValidMeasurements++;
181 }
182 }
183 }
184 }
185
186 float rms = nbValidMeasurements == 0 ? 0.0f : sqrtf(sumRmsSquared / nbValidMeasurements);
187 Visualizer::GetOnlyParameters::Measurement measure;
188 // convert from I16 sample values to mB and write results
189 measure.rms = (rms < 0.000016f) ? -9600 : (int32_t)(2000 * log10(rms / 32767.0f));
190 measure.peak = (peakU16 == 0) ? -9600 : (int32_t)(2000 * log10(peakU16 / 32767.0f));
191 LOG(INFO) << __func__ << " peak " << peakU16 << " (" << measure.peak << "mB), rms " << rms
192 << " (" << measure.rms << "mB)";
193 return measure;
194}
195
196std::vector<uint8_t> VisualizerContext::capture() {
197 std::vector<uint8_t> result;
198 std::lock_guard lg(mMutex);
199 RETURN_VALUE_IF(mState != State::ACTIVE, result, "illegalState");
200 const uint32_t deltaMs = getDeltaTimeMsFromUpdatedTime_l();
201
202 // if audio framework has stopped playing audio although the effect is still active we must
203 // clear the capture buffer to return silence
204 if ((mLastCaptureIdx == mCaptureIdx) && (mBufferUpdateTime.tv_sec != 0) &&
205 (deltaMs > kMaxStallTimeMs)) {
206 LOG(INFO) << __func__ << " capture going to idle";
207 mBufferUpdateTime.tv_sec = 0;
208 return result;
209 }
210 int32_t latencyMs = mDownstreamLatency;
211 latencyMs -= deltaMs;
212 if (latencyMs < 0) {
213 latencyMs = 0;
214 }
215 uint32_t deltaSamples = mCaptureSamples + mCommon.input.base.sampleRate * latencyMs / 1000;
216
217 // large sample rate, latency, or capture size, could cause overflow.
218 // do not offset more than the size of buffer.
219 if (deltaSamples > kMaxCaptureBufSize) {
220 android_errorWriteLog(0x534e4554, "31781965");
221 deltaSamples = kMaxCaptureBufSize;
222 }
223
224 int32_t capturePoint;
225 //capturePoint = (int32_t)mCaptureIdx - deltaSamples;
226 __builtin_sub_overflow((int32_t) mCaptureIdx, deltaSamples, &capturePoint);
227 // a negative capturePoint means we wrap the buffer.
228 if (capturePoint < 0) {
229 uint32_t size = -capturePoint;
230 if (size > mCaptureSamples) {
231 size = mCaptureSamples;
232 }
233 result.insert(result.end(), &mCaptureBuf[kMaxCaptureBufSize + capturePoint],
234 &mCaptureBuf[kMaxCaptureBufSize + capturePoint + size]);
235 mCaptureSamples -= size;
236 capturePoint = 0;
237 }
238 result.insert(result.end(), &mCaptureBuf[capturePoint],
239 &mCaptureBuf[capturePoint + mCaptureSamples]);
240 mLastCaptureIdx = mCaptureIdx;
241 return result;
242}
243
244IEffect::Status VisualizerContext::process(float* in, float* out, int samples) {
245 IEffect::Status result = {STATUS_NOT_ENOUGH_DATA, 0, 0};
246 RETURN_VALUE_IF(in == nullptr || out == nullptr || samples == 0, result, "dataBufferError");
247
248 std::lock_guard lg(mMutex);
249 result.status = STATUS_INVALID_OPERATION;
250 RETURN_VALUE_IF(mState != State::ACTIVE, result, "stateNotActive");
251 LOG(DEBUG) << __func__ << " in " << in << " out " << out << " sample " << samples;
252 // perform measurements if needed
253 if (mMeasurementMode == Visualizer::MeasurementMode::PEAK_RMS) {
254 // find the peak and RMS squared for the new buffer
255 float rmsSqAcc = 0;
256 float maxSample = 0.f;
257 for (size_t inIdx = 0; inIdx < (unsigned)samples; ++inIdx) {
258 maxSample = fmax(maxSample, fabs(in[inIdx]));
259 rmsSqAcc += in[inIdx] * in[inIdx];
260 }
261 maxSample *= 1 << 15; // scale to int16_t, with exactly 1 << 15 representing positive num.
262 rmsSqAcc *= 1 << 30; // scale to int16_t * 2
263 mPastMeasurements[mMeasurementBufferIdx] = {
264 .mPeakU16 = (uint16_t)maxSample,
265 .mRmsSquared = rmsSqAcc / samples,
266 .mIsValid = true };
267 if (++mMeasurementBufferIdx >= mMeasurementWindowSizeInBuffers) {
268 mMeasurementBufferIdx = 0;
269 }
270 }
271
272 float fscale; // multiplicative scale
273 if (mScalingMode == Visualizer::ScalingMode::NORMALIZED) {
274 // derive capture scaling factor from peak value in current buffer
275 // this gives more interesting captures for display.
276 float maxSample = 0.f;
277 for (size_t inIdx = 0; inIdx < (unsigned)samples; ) {
278 // we reconstruct the actual summed value to ensure proper normalization
279 // for multichannel outputs (channels > 2 may often be 0).
280 float smp = 0.f;
281 for (int i = 0; i < mChannelCount; ++i) {
282 smp += in[inIdx++];
283 }
284 maxSample = fmax(maxSample, fabs(smp));
285 }
286 if (maxSample > 0.f) {
287 fscale = 0.99f / maxSample;
288 int exp; // unused
289 const float significand = frexp(fscale, &exp);
290 if (significand == 0.5f) {
291 fscale *= 255.f / 256.f; // avoid returning unaltered PCM signal
292 }
293 } else {
294 // scale doesn't matter, the values are all 0.
295 fscale = 1.f;
296 }
297 } else {
298 assert(mScalingMode == Visualizer::ScalingMode::AS_PLAYED);
299 // Note: if channels are uncorrelated, 1/sqrt(N) could be used at the risk of clipping.
300 fscale = 1.f / mChannelCount; // account for summing all the channels together.
301 }
302
303 uint32_t captIdx;
304 uint32_t inIdx;
305 for (inIdx = 0, captIdx = mCaptureIdx; inIdx < (unsigned)samples; captIdx++) {
306 // wrap
307 if (captIdx >= kMaxCaptureBufSize) {
308 captIdx = 0;
309 }
310
311 float smp = 0.f;
312 for (uint32_t i = 0; i < mChannelCount; ++i) {
313 smp += in[inIdx++];
314 }
315 mCaptureBuf[captIdx] = clamp8_from_float(smp * fscale);
316 }
317
318 // the following two should really be atomic, though it probably doesn't
319 // matter much for visualization purposes
320 mCaptureIdx = captIdx;
321 // update last buffer update time stamp
322 if (clock_gettime(CLOCK_MONOTONIC, &mBufferUpdateTime) < 0) {
323 mBufferUpdateTime.tv_sec = 0;
324 }
325
326 // TODO: handle access_mode
327 memcpy(out, in, samples * sizeof(float));
328 return {STATUS_OK, samples, samples};
329}
330
331} // namespace aidl::android::hardware::audio::effect