2727 * signal delay (FilterSize/2) to properly align.
2828 */
2929template <std::size_t FilterSize>
30- struct PhaseShifterT {
30+ class PhaseShifterT {
3131 static_assert (FilterSize >= 16 , " FilterSize needs to be at least 16" );
3232 static_assert ((FilterSize&(FilterSize-1 )) == 0 , " FilterSize needs to be power-of-two" );
3333
3434 alignas (16 ) std::array<float ,FilterSize/2 > mCoeffs {};
3535
36- PhaseShifterT () noexcept
37- {
38- /* Every other coefficient is 0, so we only need to calculate and store
39- * the non-0 terms and double-step over the input to apply it. The
40- * calculated coefficients are in reverse to make applying in the time-
41- * domain more efficient.
42- */
43- for (const auto i : std::views::iota (0_uz, FilterSize/2 ))
44- {
45- const auto k = gsl::narrow_cast<int >(i*2 + 1 ) - int {FilterSize/2 };
46-
47- /* Calculate the Blackman window value for this coefficient. */
48- const auto w = 2.0 *std::numbers::pi/double {FilterSize}
49- * gsl::narrow_cast<double >(i*2 + 1 );
50- const auto window = 0.3635819 - 0.4891775 *std::cos (w) + 0.1365995 *std::cos (2.0 *w)
51- - 0.0106411 *std::cos (3.0 *w);
52-
53- const auto pk = std::numbers::pi * gsl::narrow_cast<double >(k);
54- mCoeffs [i] = gsl::narrow_cast<float >(window * (1.0 -std::cos (pk)) / pk);
55- }
56- }
57-
58- void process (const std::span<float > dst, std::span<const float > src) const ;
59-
60- private:
6136#if HAVE_NEON
6237 static auto load4 (float32_t a, float32_t b, float32_t c, float32_t d) -> float32x4_t
6338 {
@@ -79,131 +54,153 @@ struct PhaseShifterT {
7954 x3 = u1_.val [1 ];
8055 }
8156#endif
82- };
8357
84- template <std::size_t S> NOINLINE inline
85- void PhaseShifterT<S>::process(const std::span<float > dst, std::span<const float > src) const
86- {
87- #if HAVE_SSE_INTRINSICS
88- /* NOLINTBEGIN(cppcoreguidelines-pro-type-reinterpret-cast)
89- * Need to be able to cast floats to SIMD float types.
90- */
91- if (const auto todo = dst.size ()>>2_uz)
58+ public:
59+ PhaseShifterT () noexcept
9260 {
93- auto out = std::span{reinterpret_cast <__m128*>(dst.data ()), todo};
94- std::ranges::generate (out, [&src,this ]
61+ /* Every other coefficient is 0, so we only need to calculate and store
62+ * the non-0 terms and double-step over the input to apply it. The
63+ * calculated coefficients are in reverse to make applying in the time-
64+ * domain more efficient.
65+ */
66+ for (const auto i : std::views::iota (0_uz, FilterSize/2 ))
9567 {
96- auto r0 = _mm_setzero_ps ();
97- auto r1 = _mm_setzero_ps ();
98- auto r2 = _mm_setzero_ps ();
99- auto r3 = _mm_setzero_ps ();
100- for (auto j = 0_uz;j < mCoeffs .size ();j+=4 )
101- {
102- const auto coeffs = _mm_load_ps (&mCoeffs [j]);
103- const auto s0 = _mm_loadu_ps (&src[j*2 ]);
104- const auto s1 = _mm_loadu_ps (&src[j*2 + 4 ]);
105- const auto s2 = _mm_movehl_ps (_mm_movelh_ps (s1, s1), s0);
106- const auto s3 = _mm_loadh_pi (_mm_movehl_ps (s1, s1),
107- reinterpret_cast <const __m64*>(&src[j*2 + 8 ]));
108-
109- auto s = _mm_shuffle_ps (s0, s1, _MM_SHUFFLE (2 , 0 , 2 , 0 ));
110- r0 = _mm_add_ps (r0, _mm_mul_ps (s, coeffs));
111-
112- s = _mm_shuffle_ps (s0, s1, _MM_SHUFFLE (3 , 1 , 3 , 1 ));
113- r1 = _mm_add_ps (r1, _mm_mul_ps (s, coeffs));
114-
115- s = _mm_shuffle_ps (s2, s3, _MM_SHUFFLE (2 , 0 , 2 , 0 ));
116- r2 = _mm_add_ps (r2, _mm_mul_ps (s, coeffs));
68+ const auto k = gsl::narrow_cast<int >(i*2 + 1 ) - int {FilterSize/2 };
11769
118- s = _mm_shuffle_ps (s2, s3, _MM_SHUFFLE (3 , 1 , 3 , 1 ));
119- r3 = _mm_add_ps (r3, _mm_mul_ps (s, coeffs));
120- }
121- src = src.subspan (4 );
70+ /* Calculate the Blackman window value for this coefficient. */
71+ const auto w = 2.0 *std::numbers::pi/double {FilterSize}
72+ * gsl::narrow_cast<double >(i*2 + 1 );
73+ const auto window = 0.3635819 - 0.4891775 *std::cos (w) + 0.1365995 *std::cos (2.0 *w)
74+ - 0.0106411 *std::cos (3.0 *w);
12275
123- _MM_TRANSPOSE4_PS (r0, r1, r2, r3 );
124- return _mm_add_ps ( _mm_add_ps (r0, r1), _mm_add_ps (r2, r3) );
125- });
76+ const auto pk = std::numbers::pi * gsl::narrow_cast< double >(k );
77+ mCoeffs [i] = gsl::narrow_cast< float >(window * ( 1.0 - std::cos (pk)) / pk );
78+ }
12679 }
127- if (const auto todo = dst.size ()&3 )
80+
81+ NOINLINE void process (const std::span<float > dst, std::span<const float > src) const
12882 {
129- std::ranges::generate (dst.last (todo), [&src,this ]
83+ #if HAVE_SSE_INTRINSICS
84+ /* NOLINTBEGIN(cppcoreguidelines-pro-type-reinterpret-cast)
85+ * Need to be able to cast floats to SIMD float types.
86+ */
87+ if (const auto todo = dst.size ()>>2_uz)
13088 {
131- auto r4 = _mm_setzero_ps () ;
132- for ( auto j = 0_uz;j < mCoeffs . size ();j+= 4 )
89+ auto out = std::span{ reinterpret_cast <__m128*>(dst. data ()), todo} ;
90+ std::ranges::generate (out, [&src, this ]
13391 {
134- const auto coeffs = _mm_load_ps (&mCoeffs [j]);
135- const auto s = _mm_setr_ps (src[j*2 ], src[j*2 + 2 ], src[j*2 + 4 ], src[j*2 + 6 ]);
136- r4 = _mm_add_ps (r4, _mm_mul_ps (s, coeffs));
137- }
138- src = src.subspan (1 );
139-
140- r4 = _mm_add_ps (r4, _mm_shuffle_ps (r4, r4, _MM_SHUFFLE (0 , 1 , 2 , 3 )));
141- r4 = _mm_add_ps (r4, _mm_movehl_ps (r4, r4));
142- return _mm_cvtss_f32 (r4);
143- });
144- }
92+ auto r0 = _mm_setzero_ps ();
93+ auto r1 = _mm_setzero_ps ();
94+ auto r2 = _mm_setzero_ps ();
95+ auto r3 = _mm_setzero_ps ();
96+ for (auto j = 0_uz;j < mCoeffs .size ();j+=4 )
97+ {
98+ const auto coeffs = _mm_load_ps (&mCoeffs [j]);
99+ const auto s0 = _mm_loadu_ps (&src[j*2 ]);
100+ const auto s1 = _mm_loadu_ps (&src[j*2 + 4 ]);
101+ const auto s2 = _mm_movehl_ps (_mm_movelh_ps (s1, s1), s0);
102+ const auto s3 = _mm_loadh_pi (_mm_movehl_ps (s1, s1),
103+ reinterpret_cast <const __m64*>(&src[j*2 + 8 ]));
104+
105+ auto s = _mm_shuffle_ps (s0, s1, _MM_SHUFFLE (2 , 0 , 2 , 0 ));
106+ r0 = _mm_add_ps (r0, _mm_mul_ps (s, coeffs));
107+
108+ s = _mm_shuffle_ps (s0, s1, _MM_SHUFFLE (3 , 1 , 3 , 1 ));
109+ r1 = _mm_add_ps (r1, _mm_mul_ps (s, coeffs));
110+
111+ s = _mm_shuffle_ps (s2, s3, _MM_SHUFFLE (2 , 0 , 2 , 0 ));
112+ r2 = _mm_add_ps (r2, _mm_mul_ps (s, coeffs));
113+
114+ s = _mm_shuffle_ps (s2, s3, _MM_SHUFFLE (3 , 1 , 3 , 1 ));
115+ r3 = _mm_add_ps (r3, _mm_mul_ps (s, coeffs));
116+ }
117+ src = src.subspan (4 );
118+
119+ _MM_TRANSPOSE4_PS (r0, r1, r2, r3);
120+ return _mm_add_ps (_mm_add_ps (r0, r1), _mm_add_ps (r2, r3));
121+ });
122+ }
123+ if (const auto todo = dst.size ()&3 )
124+ {
125+ std::ranges::generate (dst.last (todo), [&src,this ]
126+ {
127+ auto r4 = _mm_setzero_ps ();
128+ for (auto j = 0_uz;j < mCoeffs .size ();j+=4 )
129+ {
130+ const auto coeffs = _mm_load_ps (&mCoeffs [j]);
131+ const auto s = _mm_setr_ps (src[j*2 ], src[j*2 + 2 ], src[j*2 + 4 ], src[j*2 + 6 ]);
132+ r4 = _mm_add_ps (r4, _mm_mul_ps (s, coeffs));
133+ }
134+ src = src.subspan (1 );
135+
136+ r4 = _mm_add_ps (r4, _mm_shuffle_ps (r4, r4, _MM_SHUFFLE (0 , 1 , 2 , 3 )));
137+ r4 = _mm_add_ps (r4, _mm_movehl_ps (r4, r4));
138+ return _mm_cvtss_f32 (r4);
139+ });
140+ }
145141
146142#elif HAVE_NEON
147143
148- if (const std::size_t todo{dst.size ()>>2 })
149- {
150- auto out = std::span{reinterpret_cast <float32x4_t *>(dst.data ()), todo};
151- std::generate (out.begin (), out.end (), [&src,this ]
144+ if (const std::size_t todo{dst.size ()>>2 })
152145 {
153- auto r0 = vdupq_n_f32 (0 .0f );
154- auto r1 = vdupq_n_f32 (0 .0f );
155- auto r2 = vdupq_n_f32 (0 .0f );
156- auto r3 = vdupq_n_f32 (0 .0f );
157- for (auto j = 0_uz;j < mCoeffs .size ();j+=4 )
146+ auto out = std::span{reinterpret_cast <float32x4_t *>(dst.data ()), todo};
147+ std::generate (out.begin (), out.end (), [&src,this ]
158148 {
159- const auto coeffs = vld1q_f32 (&mCoeffs [j]);
160- const auto s0 = vld1q_f32 (&src[j*2 ]);
161- const auto s1 = vld1q_f32 (&src[j*2 + 4 ]);
162- const auto s2 = vcombine_f32 (vget_high_f32 (s0), vget_low_f32 (s1));
163- const auto s3 = vcombine_f32 (vget_high_f32 (s1), vld1_f32 (&src[j*2 + 8 ]));
164- const auto values0 = vuzpq_f32 (s0, s1);
165- const auto values1 = vuzpq_f32 (s2, s3);
166-
167- r0 = vmlaq_f32 (r0, values0.val [0 ], coeffs);
168- r1 = vmlaq_f32 (r1, values0.val [1 ], coeffs);
169- r2 = vmlaq_f32 (r2, values1.val [0 ], coeffs);
170- r3 = vmlaq_f32 (r3, values1.val [1 ], coeffs);
171- }
172- src = src.subspan (4 );
173-
174- vtranspose4 (r0, r1, r2, r3);
175- return vaddq_f32 (vaddq_f32 (r0, r1), vaddq_f32 (r2, r3));
176- });
177- }
178- if (const auto todo = dst.size ()&3 )
179- {
180- std::ranges::generate (dst.last (todo), [&src,this ]
149+ auto r0 = vdupq_n_f32 (0 .0f );
150+ auto r1 = vdupq_n_f32 (0 .0f );
151+ auto r2 = vdupq_n_f32 (0 .0f );
152+ auto r3 = vdupq_n_f32 (0 .0f );
153+ for (auto j = 0_uz;j < mCoeffs .size ();j+=4 )
154+ {
155+ const auto coeffs = vld1q_f32 (&mCoeffs [j]);
156+ const auto s0 = vld1q_f32 (&src[j*2 ]);
157+ const auto s1 = vld1q_f32 (&src[j*2 + 4 ]);
158+ const auto s2 = vcombine_f32 (vget_high_f32 (s0), vget_low_f32 (s1));
159+ const auto s3 = vcombine_f32 (vget_high_f32 (s1), vld1_f32 (&src[j*2 + 8 ]));
160+ const auto values0 = vuzpq_f32 (s0, s1);
161+ const auto values1 = vuzpq_f32 (s2, s3);
162+
163+ r0 = vmlaq_f32 (r0, values0.val [0 ], coeffs);
164+ r1 = vmlaq_f32 (r1, values0.val [1 ], coeffs);
165+ r2 = vmlaq_f32 (r2, values1.val [0 ], coeffs);
166+ r3 = vmlaq_f32 (r3, values1.val [1 ], coeffs);
167+ }
168+ src = src.subspan (4 );
169+
170+ vtranspose4 (r0, r1, r2, r3);
171+ return vaddq_f32 (vaddq_f32 (r0, r1), vaddq_f32 (r2, r3));
172+ });
173+ }
174+ if (const auto todo = dst.size ()&3 )
181175 {
182- auto r4 = vdupq_n_f32 (0 .0f );
183- for (auto j = 0_uz;j < mCoeffs .size ();j+=4 )
176+ std::ranges::generate (dst.last (todo), [&src,this ]
184177 {
185- const auto coeffs = vld1q_f32 (&mCoeffs [j]);
186- const auto s = load4 (src[j*2 ], src[j*2 + 2 ], src[j*2 + 4 ], src[j*2 + 6 ]);
187- r4 = vmlaq_f32 (r4, s, coeffs);
188- }
189- src = src.subspan (1 );
190- r4 = vaddq_f32 (r4, vrev64q_f32 (r4));
191- return vget_lane_f32 (vadd_f32 (vget_low_f32 (r4), vget_high_f32 (r4)), 0 );
192- });
193- }
194- /* NOLINTEND(cppcoreguidelines-pro-type-reinterpret-cast) */
178+ auto r4 = vdupq_n_f32 (0 .0f );
179+ for (auto j = 0_uz;j < mCoeffs .size ();j+=4 )
180+ {
181+ const auto coeffs = vld1q_f32 (&mCoeffs [j]);
182+ const auto s = load4 (src[j*2 ], src[j*2 + 2 ], src[j*2 + 4 ], src[j*2 + 6 ]);
183+ r4 = vmlaq_f32 (r4, s, coeffs);
184+ }
185+ src = src.subspan (1 );
186+ r4 = vaddq_f32 (r4, vrev64q_f32 (r4));
187+ return vget_lane_f32 (vadd_f32 (vget_low_f32 (r4), vget_high_f32 (r4)), 0 );
188+ });
189+ }
190+ /* NOLINTEND(cppcoreguidelines-pro-type-reinterpret-cast) */
195191
196192#else
197193
198- std::ranges::generate (dst, [&src,this ]
199- {
200- auto ret = 0 .0f ;
201- for (auto j = 0_uz;j < mCoeffs .size ();++j)
202- ret += src[j*2 ] * mCoeffs [j];
203- src = src.subspan (1 );
204- return ret;
205- });
194+ std::ranges::generate (dst, [&src,this ]
195+ {
196+ auto ret = 0 .0f ;
197+ for (auto j = 0_uz;j < mCoeffs .size ();++j)
198+ ret += src[j*2 ] * mCoeffs [j];
199+ src = src.subspan (1 );
200+ return ret;
201+ });
206202#endif
207- }
203+ }
204+ };
208205
209206#endif /* PHASE_SHIFTER_H */
0 commit comments