tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

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