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 <math.h>
22
23 #include <audio_utils/power.h>
24 #include <audio_utils/primitives.h>
25
26 #if defined(__aarch64__) || defined(__ARM_NEON__)
27 #include <arm_neon.h>
28 #define USE_NEON
29 #endif
30
31 namespace {
32
isFormatSupported(audio_format_t format)33 constexpr inline bool isFormatSupported(audio_format_t format) {
34 switch (format) {
35 case AUDIO_FORMAT_PCM_8_BIT:
36 case AUDIO_FORMAT_PCM_16_BIT:
37 case AUDIO_FORMAT_PCM_24_BIT_PACKED:
38 case AUDIO_FORMAT_PCM_8_24_BIT:
39 case AUDIO_FORMAT_PCM_32_BIT:
40 case AUDIO_FORMAT_PCM_FLOAT:
41 return true;
42 default:
43 return false;
44 }
45 }
46
47 template <typename T>
getPtrPtrValueAndIncrement(const void ** data)48 inline T getPtrPtrValueAndIncrement(const void **data)
49 {
50 return *(*reinterpret_cast<const T **>(data))++;
51 }
52
53 template <audio_format_t FORMAT>
convertToFloatAndIncrement(const void ** data)54 inline float convertToFloatAndIncrement(const void **data)
55 {
56 switch (FORMAT) {
57 case AUDIO_FORMAT_PCM_8_BIT:
58 return float_from_u8(getPtrPtrValueAndIncrement<uint8_t>(data));
59
60 case AUDIO_FORMAT_PCM_16_BIT:
61 return float_from_i16(getPtrPtrValueAndIncrement<int16_t>(data));
62
63 case AUDIO_FORMAT_PCM_24_BIT_PACKED: {
64 const uint8_t *uptr = reinterpret_cast<const uint8_t *>(*data);
65 *data = uptr + 3;
66 return float_from_p24(uptr);
67 }
68
69 case AUDIO_FORMAT_PCM_8_24_BIT:
70 return float_from_q8_23(getPtrPtrValueAndIncrement<int32_t>(data));
71
72 case AUDIO_FORMAT_PCM_32_BIT:
73 return float_from_i32(getPtrPtrValueAndIncrement<int32_t>(data));
74
75 case AUDIO_FORMAT_PCM_FLOAT:
76 return getPtrPtrValueAndIncrement<float>(data);
77
78 default:
79 // static_assert cannot use false because the compiler may interpret it
80 // even though this code path may never be taken.
81 static_assert(isFormatSupported(FORMAT), "unsupported format");
82 }
83 }
84
85 // used to normalize integer fixed point value to the floating point equivalent.
86 template <audio_format_t FORMAT>
normalizeAmplitude()87 constexpr inline float normalizeAmplitude()
88 {
89 switch (FORMAT) {
90 case AUDIO_FORMAT_PCM_8_BIT:
91 return 1.f / (1 << 7);
92
93 case AUDIO_FORMAT_PCM_16_BIT:
94 return 1.f / (1 << 15);
95
96 case AUDIO_FORMAT_PCM_24_BIT_PACKED: // fall through
97 case AUDIO_FORMAT_PCM_8_24_BIT:
98 return 1.f / (1 << 23);
99
100 case AUDIO_FORMAT_PCM_32_BIT:
101 return 1.f / (1U << 31);
102
103 case AUDIO_FORMAT_PCM_FLOAT:
104 return 1.f;
105
106 default:
107 // static_assert cannot use false because the compiler may interpret it
108 // even though this code path may never be taken.
109 static_assert(isFormatSupported(FORMAT), "unsupported format");
110 }
111 }
112
113 template <audio_format_t FORMAT>
normalizeEnergy()114 constexpr inline float normalizeEnergy()
115 {
116 const float val = normalizeAmplitude<FORMAT>();
117 return val * val;
118 }
119
120 template <audio_format_t FORMAT>
energyMonoRef(const void * amplitudes,size_t size)121 inline float energyMonoRef(const void *amplitudes, size_t size)
122 {
123 float accum(0.f);
124 for (size_t i = 0; i < size; ++i) {
125 const float amplitude = convertToFloatAndIncrement<FORMAT>(&litudes);
126 accum += amplitude * amplitude;
127 }
128 return accum;
129 }
130
131 template <audio_format_t FORMAT>
energyMono(const void * amplitudes,size_t size)132 inline float energyMono(const void *amplitudes, size_t size)
133 {
134 return energyMonoRef<FORMAT>(amplitudes, size);
135 }
136
137 // fast float power computation for ARM processors that support NEON.
138 #ifdef USE_NEON
139
140 template <>
energyMono(const void * amplitudes,size_t size)141 inline float energyMono<AUDIO_FORMAT_PCM_FLOAT>(const void *amplitudes, size_t size)
142 {
143 float32x4_t *famplitudes = (float32x4_t *)amplitudes;
144
145 // clear accumulator
146 float32x4_t accum = vdupq_n_f32(0);
147
148 // iterate over array getting sum of squares in 4 lanes.
149 size_t i;
150 for (i = 0; i < (size & ~3); i += 4) {
151 accum = vmlaq_f32(accum, *famplitudes, *famplitudes);
152 ++famplitudes;
153 }
154
155 // narrow 4 lanes of floats
156 float32x2_t accum2 = vadd_f32(vget_low_f32(accum), vget_high_f32(accum)); // get stereo volume
157 accum2 = vpadd_f32(accum2, accum2); // combine to mono
158
159 // accumulate remainder
160 float value = vget_lane_f32(accum2, 0);
161 for (; i < size; ++i) {
162 const float amplitude = ((float *)amplitudes)[i];
163 value += amplitude * amplitude;
164 }
165
166 return value;
167 }
168
169 template <>
energyMono(const void * amplitudes,size_t size)170 inline float energyMono<AUDIO_FORMAT_PCM_16_BIT>(const void *amplitudes, size_t size)
171 {
172 int16x4_t *samplitudes = (int16x4_t *)amplitudes;
173
174 // clear accumulator
175 float32x4_t accum = vdupq_n_f32(0);
176
177 // iterate over array getting sum of squares in 4 lanes.
178 size_t i;
179 for (i = 0; i < (size & ~3); i += 4) {
180 // expand s16 to s32
181 int32x4_t amplitude = vmovl_s16(*samplitudes);
182 ++samplitudes;
183 // convert s32 to f32
184 float32x4_t famplitude = vcvtq_f32_s32(amplitude);
185 accum = vmlaq_f32(accum, famplitude, famplitude);
186 }
187
188 // narrow 4 lanes of floats
189 float32x2_t accum2 = vadd_f32(vget_low_f32(accum), vget_high_f32(accum)); // get stereo volume
190 accum2 = vpadd_f32(accum2, accum2); // combine to mono
191
192 // accumulate remainder
193 float value = vget_lane_f32(accum2, 0);
194 for (; i < size; ++i) {
195 const float amplitude = (float)((int16_t *)amplitudes)[i];
196 value += amplitude * amplitude;
197 }
198
199 return value * normalizeEnergy<AUDIO_FORMAT_PCM_16_BIT>();
200 }
201
202 // fast int32_t power computation for PCM_32
203 template <>
energyMono(const void * amplitudes,size_t size)204 inline float energyMono<AUDIO_FORMAT_PCM_32_BIT>(const void *amplitudes, size_t size)
205 {
206 int32x4_t *samplitudes = (int32x4_t *)amplitudes;
207
208 // clear accumulator
209 float32x4_t accum = vdupq_n_f32(0);
210
211 // iterate over array getting sum of squares in 4 lanes.
212 size_t i;
213 for (i = 0; i < (size & ~3); i += 4) {
214 // convert s32 to f32
215 float32x4_t famplitude = vcvtq_f32_s32(*samplitudes);
216 ++samplitudes;
217 accum = vmlaq_f32(accum, famplitude, famplitude);
218 }
219
220 // narrow 4 lanes of floats
221 float32x2_t accum2 = vadd_f32(vget_low_f32(accum), vget_high_f32(accum)); // get stereo volume
222 accum2 = vpadd_f32(accum2, accum2); // combine to mono
223
224 // accumulate remainder
225 float value = vget_lane_f32(accum2, 0);
226 for (; i < size; ++i) {
227 const float amplitude = (float)((int32_t *)amplitudes)[i];
228 value += amplitude * amplitude;
229 }
230
231 return value * normalizeEnergy<AUDIO_FORMAT_PCM_32_BIT>();
232 }
233
234 // fast int32_t power computation for PCM_8_24 (essentially identical to PCM_32 above)
235 template <>
energyMono(const void * amplitudes,size_t size)236 inline float energyMono<AUDIO_FORMAT_PCM_8_24_BIT>(const void *amplitudes, size_t size)
237 {
238 int32x4_t *samplitudes = (int32x4_t *)amplitudes;
239
240 // clear accumulator
241 float32x4_t accum = vdupq_n_f32(0);
242
243 // iterate over array getting sum of squares in 4 lanes.
244 size_t i;
245 for (i = 0; i < (size & ~3); i += 4) {
246 // convert s32 to f32
247 float32x4_t famplitude = vcvtq_f32_s32(*samplitudes);
248 ++samplitudes;
249 accum = vmlaq_f32(accum, famplitude, famplitude);
250 }
251
252 // narrow 4 lanes of floats
253 float32x2_t accum2 = vadd_f32(vget_low_f32(accum), vget_high_f32(accum)); // get stereo volume
254 accum2 = vpadd_f32(accum2, accum2); // combine to mono
255
256 // accumulate remainder
257 float value = vget_lane_f32(accum2, 0);
258 for (; i < size; ++i) {
259 const float amplitude = (float)((int32_t *)amplitudes)[i];
260 value += amplitude * amplitude;
261 }
262
263 return value * normalizeEnergy<AUDIO_FORMAT_PCM_8_24_BIT>();
264 }
265
266 #endif // USE_NEON
267
268 } // namespace
269
audio_utils_compute_energy_mono(const void * buffer,audio_format_t format,size_t samples)270 float audio_utils_compute_energy_mono(const void *buffer, audio_format_t format, size_t samples)
271 {
272 switch (format) {
273 case AUDIO_FORMAT_PCM_8_BIT:
274 return energyMono<AUDIO_FORMAT_PCM_8_BIT>(buffer, samples);
275
276 case AUDIO_FORMAT_PCM_16_BIT:
277 return energyMono<AUDIO_FORMAT_PCM_16_BIT>(buffer, samples);
278
279 case AUDIO_FORMAT_PCM_24_BIT_PACKED:
280 return energyMono<AUDIO_FORMAT_PCM_24_BIT_PACKED>(buffer, samples);
281
282 case AUDIO_FORMAT_PCM_8_24_BIT:
283 return energyMono<AUDIO_FORMAT_PCM_8_24_BIT>(buffer, samples);
284
285 case AUDIO_FORMAT_PCM_32_BIT:
286 return energyMono<AUDIO_FORMAT_PCM_32_BIT>(buffer, samples);
287
288 case AUDIO_FORMAT_PCM_FLOAT:
289 return energyMono<AUDIO_FORMAT_PCM_FLOAT>(buffer, samples);
290
291 default:
292 LOG_ALWAYS_FATAL("invalid format: %#x", format);
293 }
294 }
295
audio_utils_compute_power_mono(const void * buffer,audio_format_t format,size_t samples)296 float audio_utils_compute_power_mono(const void *buffer, audio_format_t format, size_t samples)
297 {
298 return audio_utils_power_from_energy(
299 audio_utils_compute_energy_mono(buffer, format, samples) / samples);
300 }
301
audio_utils_is_compute_power_format_supported(audio_format_t format)302 bool audio_utils_is_compute_power_format_supported(audio_format_t format)
303 {
304 return isFormatSupported(format);
305 }
306
307