108 void prepare(
double sourceRate,
double targetRate,
111 prepareTransactional(sourceRate, targetRate, quality,
112 channelStates_.size());
127 const size_t requestedChannels = spec.
numChannels > 0
130 prepareTransactional(spec.
sampleRate, targetRate, quality,
131 std::max(channelStates_.size(), requestedChannels));
142 resetChannelState(mono_);
143 for (
auto& cs : channelStates_)
144 resetChannelState(cs);
158 [[nodiscard]] std::vector<T>
process(
const T* input,
int inputLength)
160 if (table_.empty())
return {};
164 const double outLenD =
165 std::ceil(
static_cast<double>(inputLength) * ratio_);
166 const int outputLength =
static_cast<int>(
167 std::min(outLenD,
static_cast<double>(std::numeric_limits<int>::max())));
169 std::vector<T> output(
static_cast<size_t>(outputLength));
170 for (
int outIdx = 0; outIdx < outputLength; ++outIdx)
178 const int64_t num =
static_cast<int64_t
>(outIdx) * stepM_;
179 intPos = num / phasesL_;
180 phase = num % phasesL_;
184 const double srcPos =
static_cast<double>(outIdx) / ratio_;
185 intPos =
static_cast<int64_t
>(srcPos);
186 frac = srcPos -
static_cast<double>(intPos);
188 if (intPos >= inputLength)
break;
189 output[
static_cast<size_t>(outIdx)] =
190 interpolateOffline(input, inputLength,
static_cast<int>(intPos), phase, frac);
208 [[nodiscard]] std::vector<T>
processRange(
const T* input,
int inputLength,
209 int64_t firstOutput, int64_t count)
211 if (table_.empty() || input ==
nullptr || inputLength <= 0 || count <= 0)
return {};
212 std::vector<T> output(
static_cast<size_t>(count));
213 for (int64_t i = 0; i < count; ++i)
215 const int64_t outIdx = firstOutput + i;
222 const int64_t num = outIdx * stepM_;
223 intPos = num / phasesL_;
224 if (num % phasesL_ != 0 && num < 0) --intPos;
225 phase = num - intPos * phasesL_;
229 const double srcPos =
static_cast<double>(outIdx) / ratio_;
230 const double fl = std::floor(srcPos);
231 intPos =
static_cast<int64_t
>(fl);
235 const int64_t firstTap = intPos - halfTaps_ + 1;
236 if (firstTap + taps_ <= 0 || firstTap >= inputLength)
continue;
237 output[
static_cast<size_t>(i)] =
238 interpolateOffline(input, inputLength,
static_cast<int>(intPos), phase, frac);
251 return static_cast<int64_t
>(std::ceil((taps_ - halfTaps_ + 1) * ratio_)) + 1;
264 return processChannel(input, inputLength, output, mono_);
278 int numCh = std::min(input.getNumChannels(), output.getNumChannels());
279 int inLen = input.getNumSamples();
281 assert(
static_cast<int>(channelStates_.size()) >= numCh &&
282 "Resampler channels not allocated! Call prepare(spec...) first.");
285 numCh = std::min(numCh,
static_cast<int>(channelStates_.size()));
288 for (
int ch = 0; ch < numCh; ++ch)
290 outCount = processChannel(input.getChannel(ch), inLen,
291 output.getChannel(ch),
292 channelStates_[
static_cast<size_t>(ch)]);
305 const double maxOut =
306 std::ceil(
static_cast<double>(inputLength) * ratio_) + 2.0;
307 return static_cast<int>(
308 std::min(maxOut,
static_cast<double>(std::numeric_limits<int>::max())));
312 [[nodiscard]]
double getRatio() const noexcept {
return ratio_; }
327 return static_cast<int>(std::round(
static_cast<double>(halfTaps_) * ratio_));
332 static constexpr int kTablePhases = 512;
335 static constexpr int64_t kMaxExactPhases = 4096;
336 static constexpr int64_t kMaxExactCoefficients = int64_t(1) << 21;
340 std::vector<T> history;
342 double fractionalPos = 0.0;
346 struct Spec {
double passband;
double attenuationDb; };
348 static_assert(std::is_nothrow_copy_assignable_v<T>,
349 "Resampler sample storage must be reset without throwing");
350 static_assert(std::is_nothrow_swappable_v<ChannelState>,
351 "Resampler state commit must be no-throw");
352 static_assert(std::is_nothrow_swappable_v<std::vector<T>>,
353 "Resampler table commit must be no-throw");
354 static_assert(std::is_nothrow_swappable_v<std::vector<ChannelState>>,
355 "Resampler channel commit must be no-throw");
357 static Spec qualitySpec(
Quality q)
noexcept
366 return { 0.90, 100.0 };
370 [[nodiscard]]
static double kaiserBeta(
double a)
noexcept
372 if (a > 50.0)
return 0.1102 * (a - 8.7);
373 if (a >= 21.0)
return 0.5842 * std::pow(a - 21.0, 0.4) + 0.07886 * (a - 21.0);
378 [[nodiscard]]
static double besselI0(
double x)
noexcept
380 double sum = 1.0, term = 1.0;
381 for (
int k = 1; k <= 200; ++k)
383 const double half = x / (2.0 * k);
386 if (term < 1e-17 * sum)
break;
397 bool identity =
false;
400 [[nodiscard]]
static Design design(
double ratio,
Quality quality)
noexcept
408 const Spec spec = qualitySpec(quality);
409 const double scale = std::min(1.0, ratio);
412 const double transition = 0.5 * scale * (1.0 - spec.passband);
413 d.cutoff = scale * 0.5 * (1.0 + spec.passband);
414 d.beta = kaiserBeta(spec.attenuationDb);
416 const double n = (spec.attenuationDb - 7.95) / (14.36 * transition);
417 const double capped = std::min(n,
static_cast<double>(
kMaxTaps));
418 d.taps = 2 *
static_cast<int>(std::ceil(0.5 * capped));
419 d.taps = std::max(d.taps, 4);
432 static void buildPhase(T* dst,
const Design& d,
double frac)
434 const int half = d.taps / 2;
437 for (
int j = 0; j < d.taps; ++j)
438 dst[j] = (j == half - 1) ? T(1) : T(0);
441 constexpr double kPi = std::numbers::pi;
442 const double i0Beta = besselI0(d.beta);
445 for (
int j = 0; j < d.taps; ++j)
447 const double t =
static_cast<double>(j - half + 1) - frac;
448 const double x = t * d.cutoff;
449 const double sincVal = (std::abs(x) < 1e-12)
451 : d.cutoff * std::sin(kPi * x) / (kPi * x);
452 const double wx = t /
static_cast<double>(half);
453 const double win = (std::abs(wx) >= 1.0)
455 : besselI0(d.beta * std::sqrt(1.0 - wx * wx)) / i0Beta;
456 dst[j] =
static_cast<T
>(sincVal * win);
457 sum += sincVal * win;
459 if (std::abs(sum) > 1e-12)
461 const double inv = 1.0 / sum;
462 for (
int j = 0; j < d.taps; ++j)
463 dst[j] =
static_cast<T
>(
static_cast<double>(dst[j]) * inv);
471 [[nodiscard]]
static std::pair<int64_t, int64_t> rationalRatio(
472 double sourceRate,
double targetRate)
noexcept
475 const double rs = std::round(sourceRate), rt = std::round(targetRate);
476 if (std::abs(rs - sourceRate) < 1e-9 && std::abs(rt - targetRate) < 1e-9
477 && rs < 1e12 && rt < 1e12)
479 int64_t a =
static_cast<int64_t
>(rt), b =
static_cast<int64_t
>(rs);
480 int64_t x = a, y = b;
481 while (y != 0) {
const int64_t r = x % y; x = y; y = r; }
482 if (x > 0)
return { a / x, b / x };
486 const double r = targetRate / sourceRate;
487 int64_t h0 = 0, h1 = 1, k0 = 1, k1 = 0;
489 for (
int it = 0; it < 64; ++it)
491 const double fl = std::floor(v);
492 if (fl > 1e12)
break;
493 const auto an =
static_cast<int64_t
>(fl);
494 const int64_t h2 = an * h1 + h0, k2 = an * k1 + k0;
495 if (k2 > (int64_t(1) << 32) || h2 > (int64_t(1) << 32))
break;
496 h0 = h1; h1 = h2; k0 = k1; k1 = k2;
497 if (std::abs(
static_cast<double>(h1) /
static_cast<double>(k1) - r) <= 1e-15 * r)
499 const double rem = v - fl;
500 if (rem < 1e-15)
break;
506 static void initialiseChannelState(ChannelState& state,
int taps)
508 state.history.assign(
static_cast<size_t>(taps * 2), T(0));
510 state.fractionalPos = 0.0;
514 void prepareTransactional(
double sourceRate,
double targetRate,
515 Quality quality,
size_t channelCount)
520 const double stagedSourceRate = std::max(sourceRate, 1.0);
521 const double stagedTargetRate = std::max(targetRate, 1.0);
522 const double stagedRatio = stagedTargetRate / stagedSourceRate;
523 const Design d = design(stagedRatio, quality);
525 auto [l, m] = rationalRatio(stagedSourceRate, stagedTargetRate);
526 const bool exact = d.identity
527 || (l > 0 && l <= kMaxExactPhases
528 && l *
static_cast<int64_t
>(d.taps) <= kMaxExactCoefficients);
529 if (d.identity) { l = 1; m = 1; }
534 std::vector<T> stagedTable;
538 stagedTable.resize(
static_cast<size_t>(l * d.taps));
539 for (int64_t p = 0; p < l; ++p)
540 buildPhase(stagedTable.data() + p * d.taps, d,
541 static_cast<double>(p) /
static_cast<double>(l));
548 stagedTable.resize(
static_cast<size_t>((kTablePhases + 3) * d.taps));
549 for (
int p = 0; p < kTablePhases + 3; ++p)
550 buildPhase(stagedTable.data() +
static_cast<size_t>(p) *
static_cast<size_t>(d.taps), d,
551 static_cast<double>(p - 1) /
static_cast<double>(kTablePhases));
553 ChannelState stagedMono;
554 initialiseChannelState(stagedMono, d.taps);
555 std::vector<ChannelState> stagedChannels(channelCount);
556 for (
auto& state : stagedChannels)
557 initialiseChannelState(state, d.taps);
562 sourceRate_ = stagedSourceRate;
563 targetRate_ = stagedTargetRate;
564 ratio_ = stagedRatio;
565 srcStep_ = 1.0 / stagedRatio;
567 halfTaps_ = d.taps / 2;
569 phasesL_ = exact ? l : 0;
570 stepM_ = exact ? m : 0;
571 table_.swap(stagedTable);
573 swap(mono_, stagedMono);
574 channelStates_.swap(stagedChannels);
578 T interpolateContiguous(
const T* src, int64_t phase,
double frac)
const noexcept
585 const double exactPhase = frac *
static_cast<double>(kTablePhases);
586 int p0 =
static_cast<int>(exactPhase);
587 if (p0 > kTablePhases - 1) p0 = kTablePhases - 1;
588 const double u = exactPhase -
static_cast<double>(p0);
589 const T* k = table_.data() +
static_cast<size_t>(p0) *
static_cast<size_t>(taps_);
595 const double wm = -u * (u - 1.0) * (u - 2.0) / 6.0;
596 const double w0 = (u + 1.0) * (u - 1.0) * (u - 2.0) / 2.0;
597 const double w1 = -(u + 1.0) * u * (u - 2.0) / 2.0;
598 const double w2 = (u + 1.0) * u * (u - 1.0) / 6.0;
599 return static_cast<T
>(wm * sm + w0 * s0 + w1 * s1 + w2 * s2);
603 T interpolateOffline(
const T* data,
int length,
int intPos,
604 int64_t phase,
double frac)
607 const int firstSrc = intPos - halfTaps_ + 1;
608 if (firstSrc >= 0 && firstSrc + taps_ <= length)
609 return interpolateContiguous(data + firstSrc, phase, frac);
613 std::vector<T>& w = edgeScratch_;
614 w.assign(
static_cast<size_t>(taps_), T(0));
615 for (
int j = 0; j < taps_; ++j)
617 const int srcIdx = firstSrc + j;
618 if (srcIdx >= 0 && srcIdx < length)
619 w[
static_cast<size_t>(j)] = data[srcIdx];
621 return interpolateContiguous(w.data(), phase, frac);
624 static void resetChannelState(ChannelState& cs)
noexcept
626 std::fill(cs.history.begin(), cs.history.end(), T(0));
628 cs.fractionalPos = 0.0;
632 int processChannel(
const T* input,
int inputLength, T* output,
633 ChannelState& state)
noexcept
635 if (state.history.empty())
return 0;
643 T*
const hist = state.history.data();
645 int writePos = state.writePos;
650 const int64_t l = phasesL_, m = stepM_;
651 int64_t phase = state.phase;
652 for (
int i = 0; i < inputLength; ++i)
656 const T x = input[i];
658 hist[writePos + n] = x;
664 output[outIdx++] = interpolateContiguous(hist + writePos, phase, 0.0);
673 const double step = srcStep_;
674 double frac = state.fractionalPos;
675 for (
int i = 0; i < inputLength; ++i)
677 const T x = input[i];
679 hist[writePos + n] = x;
685 output[outIdx++] = interpolateContiguous(hist + writePos, 0, frac);
690 state.fractionalPos = frac;
693 state.writePos = writePos;
697 double sourceRate_ = 44100.0;
698 double targetRate_ = 48000.0;
700 double srcStep_ = 1.0;
705 int64_t phasesL_ = 0;
708 std::vector<T> table_;
709 std::vector<T> edgeScratch_;
711 std::vector<ChannelState> channelStates_;