AudioNodeEngineGenericImpl.h (12132B)
1 /* -*- mode: c++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ 2 /* this source code form is subject to the terms of the mozilla public 3 * license, v. 2.0. if a copy of the mpl was not distributed with this file, 4 * You can obtain one at http://mozilla.org/MPL/2.0/. */ 5 6 #ifndef MOZILLA_AUDIONODEENGINEGENERICIMPL_H_ 7 #define MOZILLA_AUDIONODEENGINEGENERICIMPL_H_ 8 9 #include "AlignmentUtils.h" 10 #include "AudioNodeEngineGeneric.h" 11 12 #if defined(__GNUC__) && __GNUC__ > 7 13 # define MOZ_PRAGMA(tokens) _Pragma(#tokens) 14 # define MOZ_UNROLL(factor) MOZ_PRAGMA(GCC unroll factor) 15 #elif defined(__INTEL_COMPILER) || (defined(__clang__) && __clang_major__ > 3) 16 # define MOZ_PRAGMA(tokens) _Pragma(#tokens) 17 # define MOZ_UNROLL(factor) MOZ_PRAGMA(unroll factor) 18 #else 19 # define MOZ_UNROLL(_) 20 #endif 21 22 namespace mozilla { 23 24 template <class Arch> 25 static bool is_aligned(const void* ptr) { 26 return (reinterpret_cast<uintptr_t>(ptr) & 27 ~(static_cast<uintptr_t>(Arch::alignment()) - 1)) == 28 reinterpret_cast<uintptr_t>(ptr); 29 }; 30 31 template <class Arch> 32 void Engine<Arch>::AudioBufferAddWithScale(const float* aInput, float aScale, 33 float* aOutput, uint32_t aSize) { 34 if constexpr (Arch::requires_alignment()) { 35 if (aScale == 1.0f) { 36 while (!is_aligned<Arch>(aInput) || !is_aligned<Arch>(aOutput)) { 37 if (!aSize) return; 38 *aOutput += *aInput; 39 ++aOutput; 40 ++aInput; 41 --aSize; 42 } 43 } else { 44 while (!is_aligned<Arch>(aInput) || !is_aligned<Arch>(aOutput)) { 45 if (!aSize) return; 46 *aOutput += *aInput * aScale; 47 ++aOutput; 48 ++aInput; 49 --aSize; 50 } 51 } 52 } 53 MOZ_ASSERT(is_aligned<Arch>(aInput), "aInput is aligned"); 54 MOZ_ASSERT(is_aligned<Arch>(aOutput), "aOutput is aligned"); 55 56 xsimd::batch<float, Arch> vgain(aScale); 57 58 uint32_t aVSize = aSize & ~(xsimd::batch<float, Arch>::size - 1); 59 MOZ_UNROLL(4) 60 for (unsigned i = 0; i < aVSize; i += xsimd::batch<float, Arch>::size) { 61 auto vin1 = xsimd::batch<float, Arch>::load_aligned(&aInput[i]); 62 auto vin2 = xsimd::batch<float, Arch>::load_aligned(&aOutput[i]); 63 auto vout = xsimd::fma(vin1, vgain, vin2); 64 vout.store_aligned(&aOutput[i]); 65 } 66 67 for (unsigned i = aVSize; i < aSize; ++i) { 68 aOutput[i] += aInput[i] * aScale; 69 } 70 } 71 72 template <class Arch> 73 void Engine<Arch>::AudioBlockCopyChannelWithScale(const float* aInput, 74 float aScale, 75 float* aOutput) { 76 MOZ_ASSERT(is_aligned<Arch>(aInput), "aInput is aligned"); 77 MOZ_ASSERT(is_aligned<Arch>(aOutput), "aOutput is aligned"); 78 79 MOZ_ASSERT((WEBAUDIO_BLOCK_SIZE % xsimd::batch<float, Arch>::size == 0), 80 "requires tail processing"); 81 82 xsimd::batch<float, Arch> vgain = (aScale); 83 84 MOZ_UNROLL(4) 85 for (unsigned i = 0; i < WEBAUDIO_BLOCK_SIZE; 86 i += xsimd::batch<float, Arch>::size) { 87 auto vin = xsimd::batch<float, Arch>::load_aligned(&aInput[i]); 88 auto vout = vin * vgain; 89 vout.store_aligned(&aOutput[i]); 90 } 91 }; 92 93 template <class Arch> 94 void Engine<Arch>::AudioBlockCopyChannelWithScale( 95 const float aInput[WEBAUDIO_BLOCK_SIZE], 96 const float aScale[WEBAUDIO_BLOCK_SIZE], 97 float aOutput[WEBAUDIO_BLOCK_SIZE]) { 98 MOZ_ASSERT(is_aligned<Arch>(aInput), "aInput is aligned"); 99 MOZ_ASSERT(is_aligned<Arch>(aOutput), "aOutput is aligned"); 100 MOZ_ASSERT(is_aligned<Arch>(aScale), "aScale is aligned"); 101 102 MOZ_ASSERT((WEBAUDIO_BLOCK_SIZE % xsimd::batch<float, Arch>::size == 0), 103 "requires tail processing"); 104 105 MOZ_UNROLL(4) 106 for (unsigned i = 0; i < WEBAUDIO_BLOCK_SIZE; 107 i += xsimd::batch<float, Arch>::size) { 108 auto vscaled = xsimd::batch<float, Arch>::load_aligned(&aScale[i]); 109 auto vin = xsimd::batch<float, Arch>::load_aligned(&aInput[i]); 110 auto vout = vin * vscaled; 111 vout.store_aligned(&aOutput[i]); 112 } 113 }; 114 115 template <class Arch> 116 void Engine<Arch>::AudioBufferInPlaceScale(float* aBlock, float aScale, 117 uint32_t aSize) { 118 MOZ_ASSERT(is_aligned<Arch>(aBlock), "aBlock is aligned"); 119 120 xsimd::batch<float, Arch> vgain(aScale); 121 122 uint32_t aVSize = aSize & ~(xsimd::batch<float, Arch>::size - 1); 123 MOZ_UNROLL(4) 124 for (unsigned i = 0; i < aVSize; i += xsimd::batch<float, Arch>::size) { 125 auto vin = xsimd::batch<float, Arch>::load_aligned(&aBlock[i]); 126 auto vout = vin * vgain; 127 vout.store_aligned(&aBlock[i]); 128 } 129 for (unsigned i = aVSize; i < aSize; ++i) aBlock[i] *= aScale; 130 }; 131 132 template <class Arch> 133 void Engine<Arch>::AudioBufferInPlaceScale(float* aBlock, float* aScale, 134 uint32_t aSize) { 135 MOZ_ASSERT(is_aligned<Arch>(aBlock), "aBlock is aligned"); 136 MOZ_ASSERT(is_aligned<Arch>(aScale), "aScale is aligned"); 137 138 uint32_t aVSize = aSize & ~(xsimd::batch<float, Arch>::size - 1); 139 MOZ_UNROLL(4) 140 for (unsigned i = 0; i < aVSize; i += xsimd::batch<float, Arch>::size) { 141 auto vin = xsimd::batch<float, Arch>::load_aligned(&aBlock[i]); 142 auto vgain = xsimd::batch<float, Arch>::load_aligned(&aScale[i]); 143 auto vout = vin * vgain; 144 vout.store_aligned(&aBlock[i]); 145 } 146 for (uint32_t i = aVSize; i < aSize; ++i) { 147 *aBlock++ *= *aScale++; 148 } 149 }; 150 151 template <class Arch> 152 void Engine<Arch>::AudioBlockPanStereoToStereo( 153 const float aInputL[WEBAUDIO_BLOCK_SIZE], 154 const float aInputR[WEBAUDIO_BLOCK_SIZE], float aGainL, float aGainR, 155 bool aIsOnTheLeft, float aOutputL[WEBAUDIO_BLOCK_SIZE], 156 float aOutputR[WEBAUDIO_BLOCK_SIZE]) { 157 MOZ_ASSERT(is_aligned<Arch>(aInputL), "aInputL is aligned"); 158 MOZ_ASSERT(is_aligned<Arch>(aInputR), "aInputR is aligned"); 159 MOZ_ASSERT(is_aligned<Arch>(aOutputL), "aOutputL is aligned"); 160 MOZ_ASSERT(is_aligned<Arch>(aOutputR), "aOutputR is aligned"); 161 162 MOZ_ASSERT((WEBAUDIO_BLOCK_SIZE % xsimd::batch<float, Arch>::size == 0), 163 "requires tail processing"); 164 165 xsimd::batch<float, Arch> vgainl(aGainL); 166 xsimd::batch<float, Arch> vgainr(aGainR); 167 168 if (aIsOnTheLeft) { 169 MOZ_UNROLL(2) 170 for (unsigned i = 0; i < WEBAUDIO_BLOCK_SIZE; 171 i += xsimd::batch<float, Arch>::size) { 172 auto vinl = xsimd::batch<float, Arch>::load_aligned(&aInputL[i]); 173 auto vinr = xsimd::batch<float, Arch>::load_aligned(&aInputR[i]); 174 175 /* left channel : aOutputL = aInputL + aInputR * gainL */ 176 auto vout = xsimd::fma(vinr, vgainl, vinl); 177 vout.store_aligned(&aOutputL[i]); 178 179 /* right channel : aOutputR = aInputR * gainR */ 180 auto vscaled = vinr * vgainr; 181 vscaled.store_aligned(&aOutputR[i]); 182 } 183 } else { 184 MOZ_UNROLL(2) 185 for (unsigned i = 0; i < WEBAUDIO_BLOCK_SIZE; 186 i += xsimd::batch<float, Arch>::size) { 187 auto vinl = xsimd::batch<float, Arch>::load_aligned(&aInputL[i]); 188 auto vinr = xsimd::batch<float, Arch>::load_aligned(&aInputR[i]); 189 190 /* left channel : aInputL * gainL */ 191 auto vscaled = vinl * vgainl; 192 vscaled.store_aligned(&aOutputL[i]); 193 194 /* right channel: aOutputR = aInputR + aInputL * gainR */ 195 auto vout = xsimd::fma(vinl, vgainr, vinr); 196 vout.store_aligned(&aOutputR[i]); 197 } 198 } 199 }; 200 201 template <class Arch> 202 void Engine<Arch>::BufferComplexMultiply(const float* aInput, 203 const float* aScale, float* aOutput, 204 uint32_t aSize) { 205 MOZ_ASSERT(is_aligned<Arch>(aInput), "aInput is aligned"); 206 MOZ_ASSERT(is_aligned<Arch>(aOutput), "aOutput is aligned"); 207 MOZ_ASSERT(is_aligned<Arch>(aScale), "aScale is aligned"); 208 MOZ_ASSERT((aSize % xsimd::batch<float, Arch>::size == 0), 209 "requires tail processing"); 210 211 MOZ_UNROLL(2) 212 for (unsigned i = 0; i < aSize * 2; 213 i += 2 * xsimd::batch<std::complex<float>, Arch>::size) { 214 auto in1 = xsimd::batch<std::complex<float>, Arch>::load_aligned( 215 reinterpret_cast<const std::complex<float>*>(&aInput[i])); 216 auto in2 = xsimd::batch<std::complex<float>, Arch>::load_aligned( 217 reinterpret_cast<const std::complex<float>*>(&aScale[i])); 218 auto out = in1 * in2; 219 out.store_aligned(reinterpret_cast<std::complex<float>*>(&aOutput[i])); 220 } 221 }; 222 223 template <class Arch> 224 float Engine<Arch>::AudioBufferSumOfSquares(const float* aInput, 225 uint32_t aLength) { 226 float sum = 0.f; 227 228 if constexpr (Arch::requires_alignment()) { 229 while (!is_aligned<Arch>(aInput)) { 230 if (!aLength) { 231 return sum; 232 } 233 sum += *aInput * *aInput; 234 ++aInput; 235 --aLength; 236 } 237 } 238 239 MOZ_ASSERT(is_aligned<Arch>(aInput), "aInput is aligned"); 240 241 constexpr uint32_t unroll_factor = 4; 242 xsimd::batch<float, Arch> accs[unroll_factor] = {0.f, 0.f, 0.f, 0.f}; 243 244 uint32_t vLength = 245 aLength & ~(unroll_factor * xsimd::batch<float, Arch>::size - 1); 246 247 for (uint32_t i = 0; i < vLength; 248 i += unroll_factor * xsimd::batch<float, Arch>::size) { 249 MOZ_UNROLL(4) 250 for (uint32_t j = 0; j < unroll_factor; ++j) { 251 auto in = xsimd::batch<float, Arch>::load_aligned( 252 &aInput[i + xsimd::batch<float, Arch>::size * j]); 253 accs[j] = xsimd::fma(in, in, accs[j]); 254 } 255 } 256 257 sum += reduce_add((accs[0] + accs[1]) + (accs[2] + accs[3])); 258 for (uint32_t i = vLength; i < aLength; ++i) sum += aInput[i] * aInput[i]; 259 return sum; 260 }; 261 262 template <class Arch> 263 void Engine<Arch>::NaNToZeroInPlace(float* aSamples, size_t aCount) { 264 if constexpr (Arch::requires_alignment()) { 265 while (!is_aligned<Arch>(aSamples)) { 266 if (!aCount) { 267 return; 268 } 269 if (*aSamples != *aSamples) { 270 *aSamples = 0.0; 271 } 272 ++aSamples; 273 --aCount; 274 } 275 } 276 277 MOZ_ASSERT(is_aligned<Arch>(aSamples), "aSamples is aligned"); 278 279 uint32_t vCount = aCount & ~(xsimd::batch<float, Arch>::size - 1); 280 281 MOZ_UNROLL(4) 282 for (uint32_t i = 0; i < vCount; i += xsimd::batch<float, Arch>::size) { 283 auto vin = xsimd::batch<float, Arch>::load_aligned(&aSamples[i]); 284 auto vout = 285 xsimd::select(xsimd::isnan(vin), xsimd::batch<float, Arch>(0.f), vin); 286 vout.store_aligned(&aSamples[i]); 287 } 288 289 for (uint32_t i = vCount; i < aCount; i++) { 290 if (aSamples[i] != aSamples[i]) { 291 aSamples[i] = 0.0; 292 } 293 } 294 }; 295 296 template <class Arch> 297 void Engine<Arch>::AudioBlockPanStereoToStereo( 298 const float aInputL[WEBAUDIO_BLOCK_SIZE], 299 const float aInputR[WEBAUDIO_BLOCK_SIZE], 300 const float aGainL[WEBAUDIO_BLOCK_SIZE], 301 const float aGainR[WEBAUDIO_BLOCK_SIZE], 302 const bool aIsOnTheLeft[WEBAUDIO_BLOCK_SIZE], 303 float aOutputL[WEBAUDIO_BLOCK_SIZE], float aOutputR[WEBAUDIO_BLOCK_SIZE]) { 304 MOZ_ASSERT(is_aligned<Arch>(aInputL), "aInputL is aligned"); 305 MOZ_ASSERT(is_aligned<Arch>(aInputR), "aInputR is aligned"); 306 MOZ_ASSERT(is_aligned<Arch>(aGainL), "aGainL is aligned"); 307 MOZ_ASSERT(is_aligned<Arch>(aGainR), "aGainR is aligned"); 308 MOZ_ASSERT(is_aligned<Arch>(aIsOnTheLeft), "aIsOnTheLeft is aligned"); 309 MOZ_ASSERT(is_aligned<Arch>(aOutputL), "aOutputL is aligned"); 310 MOZ_ASSERT(is_aligned<Arch>(aOutputR), "aOutputR is aligned"); 311 312 MOZ_ASSERT((WEBAUDIO_BLOCK_SIZE % xsimd::batch<float, Arch>::size == 0), 313 "requires tail processing"); 314 315 MOZ_UNROLL(2) 316 for (uint32_t i = 0; i < WEBAUDIO_BLOCK_SIZE; 317 i += xsimd::batch<float, Arch>::size) { 318 auto mask = xsimd::batch_bool<float, Arch>::load_aligned(&aIsOnTheLeft[i]); 319 320 auto inputL = xsimd::batch<float, Arch>::load_aligned(&aInputL[i]); 321 auto inputR = xsimd::batch<float, Arch>::load_aligned(&aInputR[i]); 322 auto gainL = xsimd::batch<float, Arch>::load_aligned(&aGainL[i]); 323 auto gainR = xsimd::batch<float, Arch>::load_aligned(&aGainR[i]); 324 325 auto outL_true = xsimd::fma(inputR, gainL, inputL); 326 auto outR_true = inputR * gainR; 327 328 auto outL_false = inputL * gainL; 329 auto outR_false = xsimd::fma(inputL, gainR, inputR); 330 331 auto outL = xsimd::select(mask, outL_true, outL_false); 332 auto outR = xsimd::select(mask, outR_true, outR_false); 333 334 outL.store_aligned(&aOutputL[i]); 335 outR.store_aligned(&aOutputR[i]); 336 } 337 } 338 339 } // namespace mozilla 340 341 #endif