🛠️🐜 Antkeeper superbuild with dependencies included https://antkeeper.com
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

307 lines
11 KiB

  1. #include "config.h"
  2. #include <arm_neon.h>
  3. #include <cmath>
  4. #include <limits>
  5. #include "alnumeric.h"
  6. #include "core/bsinc_defs.h"
  7. #include "defs.h"
  8. #include "hrtfbase.h"
  9. struct NEONTag;
  10. struct LerpTag;
  11. struct BSincTag;
  12. struct FastBSincTag;
  13. #if defined(__GNUC__) && !defined(__clang__) && !defined(__ARM_NEON)
  14. #pragma GCC target("fpu=neon")
  15. #endif
  16. namespace {
  17. inline float32x4_t set_f4(float l0, float l1, float l2, float l3)
  18. {
  19. float32x4_t ret{vmovq_n_f32(l0)};
  20. ret = vsetq_lane_f32(l1, ret, 1);
  21. ret = vsetq_lane_f32(l2, ret, 2);
  22. ret = vsetq_lane_f32(l3, ret, 3);
  23. return ret;
  24. }
  25. constexpr uint FracPhaseBitDiff{MixerFracBits - BSincPhaseBits};
  26. constexpr uint FracPhaseDiffOne{1 << FracPhaseBitDiff};
  27. inline void ApplyCoeffs(float2 *RESTRICT Values, const size_t IrSize, const ConstHrirSpan Coeffs,
  28. const float left, const float right)
  29. {
  30. float32x4_t leftright4;
  31. {
  32. float32x2_t leftright2{vmov_n_f32(left)};
  33. leftright2 = vset_lane_f32(right, leftright2, 1);
  34. leftright4 = vcombine_f32(leftright2, leftright2);
  35. }
  36. ASSUME(IrSize >= MinIrLength);
  37. for(size_t c{0};c < IrSize;c += 2)
  38. {
  39. float32x4_t vals = vld1q_f32(&Values[c][0]);
  40. float32x4_t coefs = vld1q_f32(&Coeffs[c][0]);
  41. vals = vmlaq_f32(vals, coefs, leftright4);
  42. vst1q_f32(&Values[c][0], vals);
  43. }
  44. }
  45. } // namespace
  46. template<>
  47. float *Resample_<LerpTag,NEONTag>(const InterpState*, float *RESTRICT src, uint frac,
  48. uint increment, const al::span<float> dst)
  49. {
  50. const int32x4_t increment4 = vdupq_n_s32(static_cast<int>(increment*4));
  51. const float32x4_t fracOne4 = vdupq_n_f32(1.0f/MixerFracOne);
  52. const int32x4_t fracMask4 = vdupq_n_s32(MixerFracMask);
  53. alignas(16) uint pos_[4], frac_[4];
  54. int32x4_t pos4, frac4;
  55. InitPosArrays(frac, increment, frac_, pos_);
  56. frac4 = vld1q_s32(reinterpret_cast<int*>(frac_));
  57. pos4 = vld1q_s32(reinterpret_cast<int*>(pos_));
  58. auto dst_iter = dst.begin();
  59. for(size_t todo{dst.size()>>2};todo;--todo)
  60. {
  61. const int pos0{vgetq_lane_s32(pos4, 0)};
  62. const int pos1{vgetq_lane_s32(pos4, 1)};
  63. const int pos2{vgetq_lane_s32(pos4, 2)};
  64. const int pos3{vgetq_lane_s32(pos4, 3)};
  65. const float32x4_t val1{set_f4(src[pos0], src[pos1], src[pos2], src[pos3])};
  66. const float32x4_t val2{set_f4(src[pos0+1], src[pos1+1], src[pos2+1], src[pos3+1])};
  67. /* val1 + (val2-val1)*mu */
  68. const float32x4_t r0{vsubq_f32(val2, val1)};
  69. const float32x4_t mu{vmulq_f32(vcvtq_f32_s32(frac4), fracOne4)};
  70. const float32x4_t out{vmlaq_f32(val1, mu, r0)};
  71. vst1q_f32(dst_iter, out);
  72. dst_iter += 4;
  73. frac4 = vaddq_s32(frac4, increment4);
  74. pos4 = vaddq_s32(pos4, vshrq_n_s32(frac4, MixerFracBits));
  75. frac4 = vandq_s32(frac4, fracMask4);
  76. }
  77. if(size_t todo{dst.size()&3})
  78. {
  79. src += static_cast<uint>(vgetq_lane_s32(pos4, 0));
  80. frac = static_cast<uint>(vgetq_lane_s32(frac4, 0));
  81. do {
  82. *(dst_iter++) = lerpf(src[0], src[1], static_cast<float>(frac) * (1.0f/MixerFracOne));
  83. frac += increment;
  84. src += frac>>MixerFracBits;
  85. frac &= MixerFracMask;
  86. } while(--todo);
  87. }
  88. return dst.data();
  89. }
  90. template<>
  91. float *Resample_<BSincTag,NEONTag>(const InterpState *state, float *RESTRICT src, uint frac,
  92. uint increment, const al::span<float> dst)
  93. {
  94. const float *const filter{state->bsinc.filter};
  95. const float32x4_t sf4{vdupq_n_f32(state->bsinc.sf)};
  96. const size_t m{state->bsinc.m};
  97. ASSUME(m > 0);
  98. src -= state->bsinc.l;
  99. for(float &out_sample : dst)
  100. {
  101. // Calculate the phase index and factor.
  102. const uint pi{frac >> FracPhaseBitDiff};
  103. const float pf{static_cast<float>(frac & (FracPhaseDiffOne-1)) * (1.0f/FracPhaseDiffOne)};
  104. // Apply the scale and phase interpolated filter.
  105. float32x4_t r4{vdupq_n_f32(0.0f)};
  106. {
  107. const float32x4_t pf4{vdupq_n_f32(pf)};
  108. const float *RESTRICT fil{filter + m*pi*2};
  109. const float *RESTRICT phd{fil + m};
  110. const float *RESTRICT scd{fil + BSincPhaseCount*2*m};
  111. const float *RESTRICT spd{scd + m};
  112. size_t td{m >> 2};
  113. size_t j{0u};
  114. do {
  115. /* f = ((fil + sf*scd) + pf*(phd + sf*spd)) */
  116. const float32x4_t f4 = vmlaq_f32(
  117. vmlaq_f32(vld1q_f32(&fil[j]), sf4, vld1q_f32(&scd[j])),
  118. pf4, vmlaq_f32(vld1q_f32(&phd[j]), sf4, vld1q_f32(&spd[j])));
  119. /* r += f*src */
  120. r4 = vmlaq_f32(r4, f4, vld1q_f32(&src[j]));
  121. j += 4;
  122. } while(--td);
  123. }
  124. r4 = vaddq_f32(r4, vrev64q_f32(r4));
  125. out_sample = vget_lane_f32(vadd_f32(vget_low_f32(r4), vget_high_f32(r4)), 0);
  126. frac += increment;
  127. src += frac>>MixerFracBits;
  128. frac &= MixerFracMask;
  129. }
  130. return dst.data();
  131. }
  132. template<>
  133. float *Resample_<FastBSincTag,NEONTag>(const InterpState *state, float *RESTRICT src, uint frac,
  134. uint increment, const al::span<float> dst)
  135. {
  136. const float *const filter{state->bsinc.filter};
  137. const size_t m{state->bsinc.m};
  138. ASSUME(m > 0);
  139. src -= state->bsinc.l;
  140. for(float &out_sample : dst)
  141. {
  142. // Calculate the phase index and factor.
  143. const uint pi{frac >> FracPhaseBitDiff};
  144. const float pf{static_cast<float>(frac & (FracPhaseDiffOne-1)) * (1.0f/FracPhaseDiffOne)};
  145. // Apply the phase interpolated filter.
  146. float32x4_t r4{vdupq_n_f32(0.0f)};
  147. {
  148. const float32x4_t pf4{vdupq_n_f32(pf)};
  149. const float *RESTRICT fil{filter + m*pi*2};
  150. const float *RESTRICT phd{fil + m};
  151. size_t td{m >> 2};
  152. size_t j{0u};
  153. do {
  154. /* f = fil + pf*phd */
  155. const float32x4_t f4 = vmlaq_f32(vld1q_f32(&fil[j]), pf4, vld1q_f32(&phd[j]));
  156. /* r += f*src */
  157. r4 = vmlaq_f32(r4, f4, vld1q_f32(&src[j]));
  158. j += 4;
  159. } while(--td);
  160. }
  161. r4 = vaddq_f32(r4, vrev64q_f32(r4));
  162. out_sample = vget_lane_f32(vadd_f32(vget_low_f32(r4), vget_high_f32(r4)), 0);
  163. frac += increment;
  164. src += frac>>MixerFracBits;
  165. frac &= MixerFracMask;
  166. }
  167. return dst.data();
  168. }
  169. template<>
  170. void MixHrtf_<NEONTag>(const float *InSamples, float2 *AccumSamples, const uint IrSize,
  171. const MixHrtfFilter *hrtfparams, const size_t BufferSize)
  172. { MixHrtfBase<ApplyCoeffs>(InSamples, AccumSamples, IrSize, hrtfparams, BufferSize); }
  173. template<>
  174. void MixHrtfBlend_<NEONTag>(const float *InSamples, float2 *AccumSamples, const uint IrSize,
  175. const HrtfFilter *oldparams, const MixHrtfFilter *newparams, const size_t BufferSize)
  176. {
  177. MixHrtfBlendBase<ApplyCoeffs>(InSamples, AccumSamples, IrSize, oldparams, newparams,
  178. BufferSize);
  179. }
  180. template<>
  181. void MixDirectHrtf_<NEONTag>(const FloatBufferSpan LeftOut, const FloatBufferSpan RightOut,
  182. const al::span<const FloatBufferLine> InSamples, float2 *AccumSamples,
  183. float *TempBuf, HrtfChannelState *ChanState, const size_t IrSize, const size_t BufferSize)
  184. {
  185. MixDirectHrtfBase<ApplyCoeffs>(LeftOut, RightOut, InSamples, AccumSamples, TempBuf, ChanState,
  186. IrSize, BufferSize);
  187. }
  188. template<>
  189. void Mix_<NEONTag>(const al::span<const float> InSamples, const al::span<FloatBufferLine> OutBuffer,
  190. float *CurrentGains, const float *TargetGains, const size_t Counter, const size_t OutPos)
  191. {
  192. const float delta{(Counter > 0) ? 1.0f / static_cast<float>(Counter) : 0.0f};
  193. const auto min_len = minz(Counter, InSamples.size());
  194. const auto aligned_len = minz((min_len+3) & ~size_t{3}, InSamples.size()) - min_len;
  195. for(FloatBufferLine &output : OutBuffer)
  196. {
  197. float *RESTRICT dst{al::assume_aligned<16>(output.data()+OutPos)};
  198. float gain{*CurrentGains};
  199. const float step{(*TargetGains-gain) * delta};
  200. size_t pos{0};
  201. if(!(std::abs(step) > std::numeric_limits<float>::epsilon()))
  202. gain = *TargetGains;
  203. else
  204. {
  205. float step_count{0.0f};
  206. /* Mix with applying gain steps in aligned multiples of 4. */
  207. if(size_t todo{min_len >> 2})
  208. {
  209. const float32x4_t four4{vdupq_n_f32(4.0f)};
  210. const float32x4_t step4{vdupq_n_f32(step)};
  211. const float32x4_t gain4{vdupq_n_f32(gain)};
  212. float32x4_t step_count4{vdupq_n_f32(0.0f)};
  213. step_count4 = vsetq_lane_f32(1.0f, step_count4, 1);
  214. step_count4 = vsetq_lane_f32(2.0f, step_count4, 2);
  215. step_count4 = vsetq_lane_f32(3.0f, step_count4, 3);
  216. do {
  217. const float32x4_t val4 = vld1q_f32(&InSamples[pos]);
  218. float32x4_t dry4 = vld1q_f32(&dst[pos]);
  219. dry4 = vmlaq_f32(dry4, val4, vmlaq_f32(gain4, step4, step_count4));
  220. step_count4 = vaddq_f32(step_count4, four4);
  221. vst1q_f32(&dst[pos], dry4);
  222. pos += 4;
  223. } while(--todo);
  224. /* NOTE: step_count4 now represents the next four counts after
  225. * the last four mixed samples, so the lowest element
  226. * represents the next step count to apply.
  227. */
  228. step_count = vgetq_lane_f32(step_count4, 0);
  229. }
  230. /* Mix with applying left over gain steps that aren't aligned multiples of 4. */
  231. for(size_t leftover{min_len&3};leftover;++pos,--leftover)
  232. {
  233. dst[pos] += InSamples[pos] * (gain + step*step_count);
  234. step_count += 1.0f;
  235. }
  236. if(pos == Counter)
  237. gain = *TargetGains;
  238. else
  239. gain += step*step_count;
  240. /* Mix until pos is aligned with 4 or the mix is done. */
  241. for(size_t leftover{aligned_len&3};leftover;++pos,--leftover)
  242. dst[pos] += InSamples[pos] * gain;
  243. }
  244. *CurrentGains = gain;
  245. ++CurrentGains;
  246. ++TargetGains;
  247. if(!(std::abs(gain) > GainSilenceThreshold))
  248. continue;
  249. if(size_t todo{(InSamples.size()-pos) >> 2})
  250. {
  251. const float32x4_t gain4 = vdupq_n_f32(gain);
  252. do {
  253. const float32x4_t val4 = vld1q_f32(&InSamples[pos]);
  254. float32x4_t dry4 = vld1q_f32(&dst[pos]);
  255. dry4 = vmlaq_f32(dry4, val4, gain4);
  256. vst1q_f32(&dst[pos], dry4);
  257. pos += 4;
  258. } while(--todo);
  259. }
  260. for(size_t leftover{(InSamples.size()-pos)&3};leftover;++pos,--leftover)
  261. dst[pos] += InSamples[pos] * gain;
  262. }
  263. }