96 assert(factor >= 1 && (factor & (factor - 1)) == 0 &&
"Factor must be a power of 2");
97 assert(factor <= (1 << kMaxStages) &&
"Factor must be <= 16 (kMaxStages stages)");
104 factor = std::clamp(factor, 1, 1 << kMaxStages);
106 while ((1 << (numStages_ + 1)) <= factor)
108 factor_ = 1 << numStages_;
121 const int taps = tapsForQuality(quality_);
122 const T beta = betaForQuality(quality_);
124 for (
int stage = 0; stage < numStages_; ++stage)
128 filters_[stage].design(taps, beta, spec.
numChannels, stageMaxSamples);
141 for (
int i = 0; i < numStages_; ++i)
146 [[nodiscard]]
int getFactor() const noexcept {
return factor_; }
157 if (numStages_ == 0)
return 0;
158 const int halfOrder = (tapsForQuality(quality_) - 1) / 2;
165 return 2 * (halfOrder + 1) * (factor_ - 1) / factor_;
180 const int nCh = std::min(input.getNumChannels(), upBuffer_.
getNumChannels());
183 const int nS = std::min(input.getNumSamples(), baseSpec_.
maxBlockSize);
187 for (
int ch = 0; ch < nCh; ++ch)
188 std::memcpy(upBuffer_.
getChannel(ch), input.getChannel(ch),
static_cast<std::size_t
>(nS) *
sizeof(T));
189 return upBuffer_.
toView().getSubView(0, nS);
193 filters_[0].processUpsample(input, upBuffer_.
toView(), nCh, nS);
196 int currentLen = nS * 2;
197 for (
int stage = 1; stage < numStages_; ++stage)
199 filters_[stage].processUpsampleInPlace(upBuffer_.
toView(), nCh, currentLen);
203 return upBuffer_.
toView().getSubView(0, nS * factor_);
218 const int nCh = std::min(output.getNumChannels(), upBuffer_.
getNumChannels());
219 const int nS = std::min(output.getNumSamples(), baseSpec_.
maxBlockSize);
223 for (
int ch = 0; ch < nCh; ++ch)
224 std::memcpy(output.getChannel(ch), upBuffer_.
getChannel(ch),
static_cast<std::size_t
>(nS) *
sizeof(T));
228 int currentLen = nS * factor_;
230 for (
int stage = numStages_ - 1; stage > 0; --stage)
232 filters_[stage].processDownsampleInPlace(upBuffer_.
toView(), nCh, currentLen);
237 filters_[0].processDownsample(upBuffer_.
toView(), output, nCh, currentLen);
247 static constexpr int kMaxStages = 4;
256 struct PolyphaseHalfBand
258 std::vector<T> evenTaps;
261 int delaySamples = 0;
271 std::vector<ChannelState> upChannels;
272 std::vector<ChannelState> downChannels;
273 std::vector<T> scratch;
282 void design(
int taps, T beta,
int numChannels,
int maxBlockSamples)
284 halfOrder = (taps - 1) / 2;
290 assert((halfOrder & 1) == 1 &&
"half-band polyphase requires odd halfOrder");
295 delaySamples = (halfOrder + 1) / 2;
298 centerTap = fullCoeffs[
static_cast<std::size_t
>(halfOrder)];
304 for (
int i = 0; i < taps; i += 2)
305 evenTaps.push_back(fullCoeffs[
static_cast<std::size_t
>(i)]);
310 const int upHistorySize =
static_cast<int>(evenTaps.size()) + maxBlockSamples;
311 const int downHistorySize = halfOrder + maxBlockSamples;
313 upChannels.resize(
static_cast<std::size_t
>(numChannels));
314 downChannels.resize(
static_cast<std::size_t
>(numChannels));
316 for (
int ch = 0; ch < numChannels; ++ch)
318 upChannels[ch].history.assign(
static_cast<std::size_t
>(upHistorySize), T(0));
319 downChannels[ch].evenHist.assign(
static_cast<std::size_t
>(downHistorySize), T(0));
320 downChannels[ch].oddHist.assign(
static_cast<std::size_t
>(downHistorySize), T(0));
322 scratch.assign(
static_cast<std::size_t
>(maxBlockSamples), T(0));
328 void reset() noexcept
330 for (
auto& ch : upChannels)
331 std::fill(ch.history.begin(), ch.history.end(), T(0));
332 for (
auto& ch : downChannels)
334 std::fill(ch.evenHist.begin(), ch.evenHist.end(), T(0));
335 std::fill(ch.oddHist.begin(), ch.oddHist.end(), T(0));
344 void upsampleChannel(
const T* src, T* dst, ChannelState& state,
int n)
noexcept
346 constexpr int W = simd::kVecWidth<T>;
347 using O = simd::Vec<T, W>;
348 const int numTaps =
static_cast<int>(evenTaps.size());
349 auto& hist = state.history;
352 std::memmove(hist.data() + numTaps, src,
static_cast<std::size_t
>(n) *
sizeof(T));
356 const T* centre = hist.data() + numTaps - delaySamples;
357 const T centre2 = centerTap * T(2);
358 const auto c2 = O::set1(centre2);
360 for (; i + W <= n; i += W)
361 O::storeInterleave2(dst + 2 * i, O::load(scratch.data() + i),
362 O::mul(O::load(centre + i), c2));
365 dst[2 * i] = scratch[
static_cast<std::size_t
>(i)];
366 dst[2 * i + 1] = centre[i] * centre2;
371 std::memmove(hist.data(), hist.data() + n,
static_cast<std::size_t
>(numTaps) *
sizeof(T));
380 void downsampleChannel(
const T* src, T* dst, ChannelState& state,
int len)
noexcept
382 constexpr int W = simd::kVecWidth<T>;
383 using O = simd::Vec<T, W>;
384 const int outLen = len / 2;
385 const int numTaps =
static_cast<int>(evenTaps.size());
386 T* even = state.evenHist.data();
387 T* odd = state.oddHist.data();
391 for (; m + W <= outLen; m += W)
394 O::loadDeinterleave(src + 2 * m, e, o);
395 O::store(even + halfOrder + m, e);
396 O::store(odd + halfOrder + m, o);
398 for (; m < outLen; ++m)
400 even[halfOrder + m] = src[2 * m];
401 odd[halfOrder + m] = src[2 * m + 1];
407 const T* centre = odd + (halfOrder - 1) / 2;
408 const auto c = O::set1(centerTap);
410 for (; k + W <= outLen; k += W)
411 O::store(dst + k, O::madd(c, O::load(centre + k), O::load(dst + k)));
412 for (; k < outLen; ++k)
416 O::store(lanes, O::madd(c, O::set1(centre[k]), O::set1(dst[k])));
420 std::memmove(even, even + outLen,
static_cast<std::size_t
>(halfOrder) *
sizeof(T));
421 std::memmove(odd, odd + outLen,
static_cast<std::size_t
>(halfOrder) *
sizeof(T));
424 void processUpsample(AudioBufferView<const T> input, AudioBufferView<T> output,
int nCh,
int nS)
noexcept
426 for (
int ch = 0; ch < nCh; ++ch)
427 upsampleChannel(input.getChannel(ch), output.getChannel(ch),
428 upChannels[
static_cast<std::size_t
>(ch)], nS);
431 void processUpsampleInPlace(AudioBufferView<T> buffer,
int nCh,
int currentLen)
noexcept
433 for (
int ch = 0; ch < nCh; ++ch)
434 upsampleChannel(buffer.getChannel(ch), buffer.getChannel(ch),
435 upChannels[
static_cast<std::size_t
>(ch)], currentLen);
438 void processDownsample(AudioBufferView<const T> input, AudioBufferView<T> output,
int nCh,
int currentLen)
noexcept
440 for (
int ch = 0; ch < nCh; ++ch)
441 downsampleChannel(input.getChannel(ch), output.getChannel(ch),
442 downChannels[
static_cast<std::size_t
>(ch)], currentLen);
445 void processDownsampleInPlace(AudioBufferView<T> buffer,
int nCh,
int currentLen)
noexcept
447 for (
int ch = 0; ch < nCh; ++ch)
448 downsampleChannel(buffer.getChannel(ch), buffer.getChannel(ch),
449 downChannels[
static_cast<std::size_t
>(ch)], currentLen);
453 static constexpr int tapsForQuality(
Quality q)
noexcept
465 static constexpr T betaForQuality(
Quality q)
noexcept
480 AudioSpec baseSpec_ {};
481 AudioBuffer<T, MaxChannels> upBuffer_;
483 std::array<PolyphaseHalfBand, kMaxStages> filters_;