Skip to content

Commit ff4719c

Browse files
committed
Combine PhaseShifterT::process's declaration and definition
1 parent b26080c commit ff4719c

1 file changed

Lines changed: 129 additions & 132 deletions

File tree

common/phase_shifter.h

Lines changed: 129 additions & 132 deletions
Original file line numberDiff line numberDiff line change
@@ -27,37 +27,12 @@
2727
* signal delay (FilterSize/2) to properly align.
2828
*/
2929
template<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

Comments
 (0)