1 /*
2 * Copyright 2017 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 // #define LOG_NDEBUG 0
18 #define LOG_TAG "audio_utils_power"
19 #include <log/log.h>
20
21 #include <audio_utils/power.h>
22
23 #include <audio_utils/intrinsic_utils.h>
24 #include <audio_utils/primitives.h>
25
26 #if defined(__aarch64__) || defined(__ARM_NEON__)
27 #define USE_NEON
28 #endif
29
30 namespace {
31
isFormatSupported(audio_format_t format)32 constexpr inline bool isFormatSupported(audio_format_t format) {
33 switch (format) {
34 case AUDIO_FORMAT_PCM_8_BIT:
35 case AUDIO_FORMAT_PCM_16_BIT:
36 case AUDIO_FORMAT_PCM_24_BIT_PACKED:
37 case AUDIO_FORMAT_PCM_8_24_BIT:
38 case AUDIO_FORMAT_PCM_32_BIT:
39 case AUDIO_FORMAT_PCM_FLOAT:
40 return true;
41 default:
42 return false;
43 }
44 }
45
46 template <typename T>
getPtrPtrValueAndIncrement(const void ** data)47 inline T getPtrPtrValueAndIncrement(const void **data)
48 {
49 return *(*reinterpret_cast<const T **>(data))++;
50 }
51
52 template <audio_format_t FORMAT>
convertToFloatAndIncrement(const void ** data)53 inline float convertToFloatAndIncrement(const void **data)
54 {
55 switch (FORMAT) {
56 case AUDIO_FORMAT_PCM_8_BIT:
57 return float_from_u8(getPtrPtrValueAndIncrement<uint8_t>(data));
58
59 case AUDIO_FORMAT_PCM_16_BIT:
60 return float_from_i16(getPtrPtrValueAndIncrement<int16_t>(data));
61
62 case AUDIO_FORMAT_PCM_24_BIT_PACKED: {
63 const uint8_t *uptr = reinterpret_cast<const uint8_t *>(*data);
64 *data = uptr + 3;
65 return float_from_p24(uptr);
66 }
67
68 case AUDIO_FORMAT_PCM_8_24_BIT:
69 return float_from_q8_23(getPtrPtrValueAndIncrement<int32_t>(data));
70
71 case AUDIO_FORMAT_PCM_32_BIT:
72 return float_from_i32(getPtrPtrValueAndIncrement<int32_t>(data));
73
74 case AUDIO_FORMAT_PCM_FLOAT:
75 return getPtrPtrValueAndIncrement<float>(data);
76
77 default:
78 // static_assert cannot use false because the compiler may interpret it
79 // even though this code path may never be taken.
80 static_assert(isFormatSupported(FORMAT), "unsupported format");
81 }
82 }
83
84 // used to normalize integer fixed point value to the floating point equivalent.
85 template <audio_format_t FORMAT>
normalizeAmplitude()86 constexpr inline float normalizeAmplitude()
87 {
88 switch (FORMAT) {
89 case AUDIO_FORMAT_PCM_8_BIT:
90 return 1.f / (1 << 7);
91
92 case AUDIO_FORMAT_PCM_16_BIT:
93 return 1.f / (1 << 15);
94
95 case AUDIO_FORMAT_PCM_24_BIT_PACKED: // fall through
96 case AUDIO_FORMAT_PCM_8_24_BIT:
97 return 1.f / (1 << 23);
98
99 case AUDIO_FORMAT_PCM_32_BIT:
100 return 1.f / (1U << 31);
101
102 case AUDIO_FORMAT_PCM_FLOAT:
103 return 1.f;
104
105 default:
106 // static_assert cannot use false because the compiler may interpret it
107 // even though this code path may never be taken.
108 static_assert(isFormatSupported(FORMAT), "unsupported format");
109 }
110 }
111
112 template <audio_format_t FORMAT>
normalizeEnergy()113 constexpr inline float normalizeEnergy()
114 {
115 const float val = normalizeAmplitude<FORMAT>();
116 return val * val;
117 }
118
119 template <audio_format_t FORMAT>
energyMonoRef(const void * amplitudes,size_t size)120 inline float energyMonoRef(const void *amplitudes, size_t size)
121 {
122 float accum(0.f);
123 for (size_t i = 0; i < size; ++i) {
124 const float amplitude = convertToFloatAndIncrement<FORMAT>(&litudes);
125 accum += amplitude * amplitude;
126 }
127 return accum;
128 }
129
130 template <audio_format_t FORMAT>
energyRef(const void * amplitudes,size_t size,size_t numChannels,float * out)131 inline void energyRef(const void *amplitudes, size_t size, size_t numChannels, float* out)
132 {
133 const size_t framesSize = size / numChannels;
134 for (size_t i = 0; i < framesSize; ++i) {
135 for (size_t c = 0; c < numChannels; ++c) {
136 const float amplitude = convertToFloatAndIncrement<FORMAT>(&litudes);
137 out[c] += amplitude * amplitude;
138 }
139 }
140 }
141
142 template <audio_format_t FORMAT>
energyMono(const void * amplitudes,size_t size)143 inline float energyMono(const void *amplitudes, size_t size)
144 {
145 return energyMonoRef<FORMAT>(amplitudes, size);
146 }
147
148 // TODO: optimize with NEON
149 template <audio_format_t FORMAT>
energy(const void * amplitudes,size_t size,size_t numChannels,float * out)150 inline void energy(const void *amplitudes, size_t size, size_t numChannels, float* out)
151 {
152 energyRef<FORMAT>(amplitudes, size, numChannels, out);
153 }
154
155 // TODO(b/323611666) in some cases having a large kVectorWidth generic internal array is
156 // faster than the NEON intrinsic version. Optimize this.
157 #ifdef USE_NEON
158 // The type conversion appears faster if we use a neon accumulator type.
159 // Using a vector length of 4 triggers the code below to use the neon type float32x4_t.
160 constexpr size_t kVectorWidth16 = 4; // neon float32x4_t
161 constexpr size_t kVectorWidth32 = 4; // neon float32x4_t
162 constexpr size_t kVectorWidthFloat = 8; // use generic intrinsics for float.
163 #else
164 constexpr size_t kVectorWidth16 = 8;
165 constexpr size_t kVectorWidth32 = 8;
166 constexpr size_t kVectorWidthFloat = 8;
167 #endif
168
169 template <typename Scalar, size_t N>
energyMonoVector(const void * amplitudes,size_t size)170 inline float energyMonoVector(const void *amplitudes, size_t size)
171 { // check pointer validity, must be aligned with scalar type.
172 const Scalar *samplitudes = reinterpret_cast<const Scalar *>(amplitudes);
173 LOG_ALWAYS_FATAL_IF((uintptr_t)samplitudes % alignof(Scalar) != 0,
174 "Non-element aligned address: %p %zu", samplitudes, alignof(Scalar));
175
176 float accumulator = 0;
177
178 #ifdef USE_NEON
179 using AccumulatorType = std::conditional_t<N == 4, float32x4_t,
180 android::audio_utils::intrinsics::internal_array_t<float, N>>;
181 #else
182 using AccumulatorType = android::audio_utils::intrinsics::internal_array_t<float, N>;
183 #endif
184
185 // seems that loading input data is fine using our generic intrinsic.
186 using Vector = android::audio_utils::intrinsics::internal_array_t<Scalar, N>;
187
188 // handle pointer unaligned to vector type.
189 while ((uintptr_t)samplitudes % sizeof(Vector) != 0 /* compiler optimized */ && size > 0) {
190 const float amp = (float)*samplitudes++;
191 accumulator += amp * amp;
192 --size;
193 }
194
195 // samplitudes is now adjusted for proper vector alignment, cast to Vector *
196 const Vector *vamplitudes = reinterpret_cast<const Vector *>(samplitudes);
197
198 // clear vector accumulator
199 AccumulatorType accum{};
200
201 // iterate over array getting sum of squares in vectorLength lanes.
202 size_t i;
203 const size_t limit = size - size % N;
204 for (i = 0; i < limit; i += N) {
205 const auto famplitude = vconvert<AccumulatorType>(*vamplitudes++);
206 accum = android::audio_utils::intrinsics::vmla(accum, famplitude, famplitude);
207 }
208
209 // add all components of the vector.
210 accumulator += android::audio_utils::intrinsics::vaddv(accum);
211
212 // accumulate any trailing elements too small for vector size
213 for (; i < size; ++i) {
214 const float amp = (float)samplitudes[i];
215 accumulator += amp * amp;
216 }
217 return accumulator;
218 }
219
220 template <>
energyMono(const void * amplitudes,size_t size)221 inline float energyMono<AUDIO_FORMAT_PCM_FLOAT>(const void *amplitudes, size_t size)
222 {
223 return energyMonoVector<float, kVectorWidthFloat>(amplitudes, size);
224 }
225
226 template <>
energyMono(const void * amplitudes,size_t size)227 inline float energyMono<AUDIO_FORMAT_PCM_16_BIT>(const void *amplitudes, size_t size)
228 {
229 return energyMonoVector<int16_t, kVectorWidth16>(amplitudes, size)
230 * normalizeEnergy<AUDIO_FORMAT_PCM_16_BIT>();
231 }
232
233 // fast int32_t power computation for PCM_32
234 template <>
energyMono(const void * amplitudes,size_t size)235 inline float energyMono<AUDIO_FORMAT_PCM_32_BIT>(const void *amplitudes, size_t size)
236 {
237 return energyMonoVector<int32_t, kVectorWidth32>(amplitudes, size)
238 * normalizeEnergy<AUDIO_FORMAT_PCM_32_BIT>();
239 }
240
241 // fast int32_t power computation for PCM_8_24 (essentially identical to PCM_32 above)
242 template <>
energyMono(const void * amplitudes,size_t size)243 inline float energyMono<AUDIO_FORMAT_PCM_8_24_BIT>(const void *amplitudes, size_t size)
244 {
245 return energyMonoVector<int32_t, kVectorWidth32>(amplitudes, size)
246 * normalizeEnergy<AUDIO_FORMAT_PCM_8_24_BIT>();
247 }
248
249 } // namespace
250
audio_utils_compute_energy_mono(const void * buffer,audio_format_t format,size_t samples)251 float audio_utils_compute_energy_mono(const void *buffer, audio_format_t format, size_t samples)
252 {
253 switch (format) {
254 case AUDIO_FORMAT_PCM_8_BIT:
255 return energyMono<AUDIO_FORMAT_PCM_8_BIT>(buffer, samples);
256
257 case AUDIO_FORMAT_PCM_16_BIT:
258 return energyMono<AUDIO_FORMAT_PCM_16_BIT>(buffer, samples);
259
260 case AUDIO_FORMAT_PCM_24_BIT_PACKED:
261 return energyMono<AUDIO_FORMAT_PCM_24_BIT_PACKED>(buffer, samples);
262
263 case AUDIO_FORMAT_PCM_8_24_BIT:
264 return energyMono<AUDIO_FORMAT_PCM_8_24_BIT>(buffer, samples);
265
266 case AUDIO_FORMAT_PCM_32_BIT:
267 return energyMono<AUDIO_FORMAT_PCM_32_BIT>(buffer, samples);
268
269 case AUDIO_FORMAT_PCM_FLOAT:
270 return energyMono<AUDIO_FORMAT_PCM_FLOAT>(buffer, samples);
271
272 default:
273 LOG_ALWAYS_FATAL("invalid format: %#x", format);
274 }
275 }
276
audio_utils_accumulate_energy(const void * buffer,audio_format_t format,size_t samples,size_t numChannels,float * out)277 void audio_utils_accumulate_energy(const void* buffer,
278 audio_format_t format,
279 size_t samples,
280 size_t numChannels,
281 float* out)
282 {
283 switch (format) {
284 case AUDIO_FORMAT_PCM_8_BIT:
285 energy<AUDIO_FORMAT_PCM_8_BIT>(buffer, samples, numChannels, out);
286 break;
287
288 case AUDIO_FORMAT_PCM_16_BIT:
289 energy<AUDIO_FORMAT_PCM_16_BIT>(buffer, samples, numChannels, out);
290 break;
291
292 case AUDIO_FORMAT_PCM_24_BIT_PACKED:
293 energy<AUDIO_FORMAT_PCM_24_BIT_PACKED>(buffer, samples, numChannels, out);
294 break;
295
296 case AUDIO_FORMAT_PCM_8_24_BIT:
297 energy<AUDIO_FORMAT_PCM_8_24_BIT>(buffer, samples, numChannels, out);
298 break;
299
300 case AUDIO_FORMAT_PCM_32_BIT:
301 energy<AUDIO_FORMAT_PCM_32_BIT>(buffer, samples, numChannels, out);
302 break;
303
304 case AUDIO_FORMAT_PCM_FLOAT:
305 energy<AUDIO_FORMAT_PCM_FLOAT>(buffer, samples, numChannels, out);
306 break;
307
308 default:
309 LOG_ALWAYS_FATAL("invalid format: %#x", format);
310 }
311 }
312
audio_utils_compute_power_mono(const void * buffer,audio_format_t format,size_t samples)313 float audio_utils_compute_power_mono(const void *buffer, audio_format_t format, size_t samples)
314 {
315 return audio_utils_power_from_energy(
316 audio_utils_compute_energy_mono(buffer, format, samples) / samples);
317 }
318
audio_utils_is_compute_power_format_supported(audio_format_t format)319 bool audio_utils_is_compute_power_format_supported(audio_format_t format)
320 {
321 return isFormatSupported(format);
322 }
323