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>(&amplitudes);
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>(&amplitudes);
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