149 max_time_ratio_ = sizing.max_time_ratio >= 1.0 ? sizing.max_time_ratio : 1.0;
150 max_pitch_semitones_ = sizing.max_pitch_semitones >= 0.0
151 ? sizing.max_pitch_semitones : 0.0;
164 const double max_pitch_ratio =
165 std::exp2((max_pitch_semitones_ > 0.0 ? max_pitch_semitones_ : 0.0) / 12.0);
166 cfg.
max_time_ratio =
static_cast<float>(max_time_ratio_ * max_pitch_ratio);
176 fft_size_ = sizing.fft_size;
178 latency_anchor_ = calibrate_anchor();
188 static_cast<float>(max_pitch_semitones_ > 0.0 ? max_pitch_semitones_ : 1.0);
197 int channels() const noexcept {
return channels_; }
202 int fft_size() const noexcept {
return fft_size_; }
222 std::vector<double> x(
static_cast<size_t>(frames), 0.0);
224 const SampleType* s = in[
c];
229 for (
auto& v : x) v *= inv;
232 const long W = 512, H = 256;
233 double e_sum = 0.0, e_max = 0.0;
long e_cnt = 0;
234 for (
long i = 0;
i + W <= frames;
i += H) {
236 for (
long j = 0; j < W; ++j) {
const double v = x[
static_cast<size_t>(
i + j)]; acc += v * v; }
237 const double rms = std::sqrt(acc /
static_cast<double>(W) + 1
e-12);
238 e_sum +=
rms; e_max = std::max(e_max,
rms); ++e_cnt;
240 const double crest = e_cnt > 0 ? e_max / (e_sum /
static_cast<double>(e_cnt) + 1
e-12) : 0.0;
244 const double f0 = 200.0, q = 0.7071;
245 const double w0 = 2.0 * 3.14159265358979323846 * f0 /
sample_rate;
246 const double cw = std::cos(w0), sw = std::sin(w0), alpha = sw / (2.0 * q);
247 const double b0 = (1.0 - cw) * 0.5, b1 = 1.0 - cw, b2 = (1.0 - cw) * 0.5;
248 const double a0 = 1.0 + alpha, a1 = -2.0 * cw, a2 = 1.0 - alpha;
249 const double nb0 = b0 / a0, nb1 = b1 / a0, nb2 = b2 / a0, na1 = a1 / a0, na2 = a2 / a0;
250 double z1 = 0.0, z2 = 0.0, lo_e = 0.0, tot_e = 0.0;
251 for (
long i = 0;
i < frames; ++
i) {
252 const double in_s = x[
static_cast<size_t>(
i)];
253 const double out_s = nb0 * in_s + z1;
254 z1 = nb1 * in_s - na1 * out_s + z2;
255 z2 = nb2 * in_s - na2 * out_s;
256 lo_e += out_s * out_s; tot_e += in_s * in_s;
258 const double lo_band = tot_e > 0.0 ? lo_e / tot_e : 0.0;
264 if (crest > 3.0)
return {1024, 128};
265 if (lo_band > 0.30)
return {8192, 512};
275 bool process(
const SampleType*
const* in,
long in_frames,
276 SampleType*
const* out,
long out_frames,
278 std::string* err =
nullptr) {
279 if (!prepared_)
return fail(err,
"OfflineStretch::process called before prepare()");
280 if (in_frames < 0)
return fail(err,
"in_frames must be >= 0");
281 if (out_frames < 0)
return fail(err,
"out_frames must be >= 0");
282 if (in ==
nullptr && in_frames > 0)
return fail(err,
"null input");
283 if (out ==
nullptr && out_frames > 0)
return fail(err,
"null output");
284 if (!(opts.
time_ratio > 0.0))
return fail(err,
"time_ratio must be > 0");
288 return fail(err,
"time_ratio outside the prepared range [1/max, max]; "
289 "widen OfflineStretchOptions::max_time_ratio at prepare()");
291 return fail(err,
"pitch_semitones outside the prepared range; widen "
292 "OfflineStretchOptions::max_pitch_semitones at prepare()");
295 if (out_frames != expected)
296 return fail(err,
"out_frames must equal round(in_frames * time_ratio)");
304 return varispeed_render(in, in_frames, out, out_frames, opts.
time_ratio);
312 for (
int c = 0;
c < channels_; ++
c)
313 for (
long i = 0;
i < out_frames; ++
i)
314 out[
c][
i] = sample_sinc6(in[
c], in_frames,
323 for (
int c = 0;
c < channels_; ++
c)
324 for (
long i = 0;
i < out_frames; ++
i) out[
c][
i] = in[
c][
i];
329 ola_tempo(in, in_frames, out, out_frames, opts.
time_ratio);
332 if (!tempo_stretch(in, in_frames, out, out_frames, opts.
time_ratio))
return false;
336 relocate_transients(in, in_frames, out, out_frames, opts.
time_ratio);
337 restore_onset_head(in, in_frames, out, out_frames);
338 match_spectral_rms(in, in_frames, out, out_frames);
344 if (!pitch_shift(in, in_frames, out, out_frames,
347 match_spectral_rms(in, in_frames, out, out_frames);
352 const int ch = channels_;
361 std::vector<std::vector<SampleType>> inter(
static_cast<size_t>(ch),
362 std::vector<SampleType>(
static_cast<size_t>(inter_len)));
363 std::vector<SampleType*> ip(
static_cast<size_t>(ch));
365 if (!tempo_stretch(in, in_frames, ip.data(), inter_len, eff))
return fail(err,
"stretch failed");
366 for (
int c = 0;
c < ch; ++
c)
367 for (
long j = 0; j < out_frames; ++j)
368 out[
c][j] = sample_sinc6(inter[
static_cast<size_t>(
c)].data(), inter_len,
369 static_cast<double>(j) * P);
370 match_spectral_rms(in, in_frames, out, out_frames);
379 std::vector<std::vector<SampleType>> inter(
static_cast<size_t>(ch),
380 std::vector<SampleType>(
static_cast<size_t>(in_frames)));
381 std::vector<SampleType*> pp(
static_cast<size_t>(ch));
383 if (!pitch_shift(in, in_frames, pp.data(), in_frames,
385 return fail(err,
"pitch stage failed");
386 std::vector<const SampleType*> cp(
static_cast<size_t>(ch));
388 if (!tempo_stretch(cp.data(), in_frames, out, out_frames, opts.
time_ratio))
return false;
389 match_spectral_rms(in, in_frames, out, out_frames);
411 SampleType peak_abs(
const SampleType*
const* b,
long n)
const noexcept {
412 SampleType
peak = SampleType{0};
413 for (
int c = 0;
c < channels_; ++
c)
414 for (
long i = 0;
i < n; ++
i)
peak = std::max(
peak, std::fabs(b[
c][
i]));
417 void match_spectral_rms(
const SampleType*
const* in,
long in_frames,
418 SampleType*
const* out,
long out_frames)
const noexcept {
425 const long fft = engine_.fft_size() > 0 ? engine_.fft_size() : 4096;
426 const long head_guard = std::max<long>(
427 static_cast<long>(std::llround(kHeadMs * sample_rate_)), fft / 2);
428 auto interior_rms = [
this, head_guard](
const SampleType*
const* b,
long n) {
429 const long lo = std::min<long>(std::max<long>(n / 8, head_guard), 4096), hi = n - lo;
430 if (hi <= lo)
return 0.0;
431 double s = 0.0;
long cnt = 0;
432 for (
int c = 0;
c < channels_; ++
c)
433 for (
long i = lo;
i < hi; ++
i) {
const double v = b[
c][
i]; s += v * v; ++cnt; }
434 return cnt > 0 ? std::sqrt(s /
static_cast<double>(cnt)) : 0.0;
439 const double ri = interior_rms(in, in_frames);
440 const double ro = interior_rms(out, out_frames);
441 if (ri > 1
e-9 && ro > 1
e-9) {
442 const double g = ri / ro;
443 if (g > 1.0 + 1
e-4) {
444 const SampleType gf =
static_cast<SampleType
>(
g);
445 for (
int c = 0;
c < channels_; ++
c)
446 for (
long i = 0;
i < out_frames; ++
i) out[
c][
i] *= gf;
454 if (peak_abs(out, out_frames) > kKnee) {
455 for (
int c = 0;
c < channels_; ++
c)
456 for (
long i = 0;
i < out_frames; ++
i) out[
c][
i] = soft_clip(out[
c][
i]);
461 static constexpr SampleType kKnee =
static_cast<SampleType
>(0.9);
462 static constexpr SampleType kCeiling =
static_cast<SampleType
>(0.999);
463 static SampleType soft_clip(SampleType x)
noexcept {
464 const SampleType
a = std::fabs(x);
465 if (
a <= kKnee)
return x;
466 const SampleType
s =
x < SampleType{0} ? SampleType{-1} : SampleType{1};
467 return s * (kKnee + (kCeiling - kKnee) * std::tanh((
a - kKnee) / (kCeiling - kKnee)));
473 static SampleType sample_sinc6(
const SampleType* x,
long n,
double pos) {
474 const long i0 =
static_cast<long>(std::floor(pos));
475 const SampleType frac =
static_cast<SampleType
>(pos -
static_cast<double>(i0));
476 auto at = [&](
long k) -> SampleType {
return (k >= 0 && k < n) ?
x[
k] : SampleType{0}; };
478 at(i0 + 1),
at(i0 + 2),
at(i0 + 3));
486 bool varispeed_render(
const SampleType*
const* in,
long in_frames,
487 SampleType*
const* out,
long out_frames,
double ratio) {
488 for (
int c = 0;
c < channels_; ++
c)
489 for (
long i = 0;
i < out_frames; ++
i)
490 out[
c][
i] = sample_sinc6(in[
c], in_frames,
491 static_cast<double>(
i) /
ratio);
492 apply_head_eq(out, out_frames,
ratio);
498 const double sr = sample_rate_ > 0 ? sample_rate_ : 48000.0;
499 const long fo = std::min<long>(
static_cast<long>(0.010 * sr), out_frames / 4);
500 const long fi = std::min<long>(
static_cast<long>(0.002 * sr), out_frames / 4);
501 constexpr double kPi = 3.14159265358979323846;
502 for (
int c = 0;
c < channels_; ++
c) {
503 for (
long i = 0;
i < fi; ++
i)
504 out[
c][
i] *=
static_cast<SampleType
>(0.5 - 0.5 * std::cos(kPi *
i / fi));
505 for (
long i = 0;
i < fo; ++
i)
506 out[
c][out_frames-1-
i] *=
static_cast<SampleType
>(0.5 - 0.5 * std::cos(kPi *
i / fo));
516 void apply_head_eq(SampleType*
const* out,
long n,
double ratio) {
517 if (
ratio == 1.0 || n <= 0)
return;
518 const double oct = std::log2(
ratio);
519 const double sr = sample_rate_ > 0 ? sample_rate_ : 48000.0;
521 const double low_db = std::clamp(2.0 * oct, -3.0, 5.0);
522 const double hi_db = std::clamp(-3.5 * oct, -10.0, 6.0);
523 Biquad bump = Biquad::peaking(140.0, 0.8, low_db, sr);
524 Biquad shelf = Biquad::high_shelf(4000.0, hi_db, sr);
525 for (
int c = 0;
c < channels_; ++
c) {
526 bump.reset(); shelf.reset();
527 for (
long i = 0;
i <
n; ++
i) {
528 double s =
static_cast<double>(out[
c][
i]);
530 s = shelf.process(s);
531 out[
c][
i] =
static_cast<SampleType
>(
s);
538 double b0 = 1, b1 = 0, b2 = 0, a1 = 0, a2 = 0;
539 double x1 = 0, x2 = 0, y1 = 0, y2 = 0;
540 void reset() {
x1 = x2 = y1 = y2 = 0; }
542 const double y = b0 *
x + b1 *
x1 + b2 * x2 - a1 * y1 - a2 * y2;
543 x2 =
x1;
x1 =
x; y2 = y1; y1 =
y;
return y;
545 static Biquad from(
double b0,
double b1,
double b2,
double a0,
double a1,
double a2) {
546 Biquad q;
q.b0 = b0 / a0;
q.b1 = b1 / a0;
q.b2 = b2 / a0;
547 q.a1 = a1 / a0;
q.a2 = a2 / a0;
return q;
549 static Biquad peaking(
double f0,
double q,
double gain_db,
double sr) {
550 const double A = std::pow(10.0, gain_db / 40.0);
551 const double w0 = 2.0 * 3.14159265358979323846 * f0 / sr;
552 const double cw = std::cos(w0), sw = std::sin(w0),
alpha = sw / (2.0 *
q);
553 return from(1 + alpha * A, -2 * cw, 1 - alpha * A,
554 1 + alpha / A, -2 * cw, 1 - alpha / A);
556 static Biquad high_shelf(
double f0,
double gain_db,
double sr) {
557 const double A = std::pow(10.0, gain_db / 40.0);
558 const double w0 = 2.0 * 3.14159265358979323846 * f0 / sr;
559 const double cw = std::cos(w0), sw = std::sin(w0);
560 const double alpha = sw / 2.0 * std::sqrt((A + 1 / A) * (1 / 0.9 - 1) + 2);
561 const double tsa = 2.0 * std::sqrt(A) *
alpha;
562 return from(A * ((A + 1) + (A - 1) * cw + tsa),
563 -2 * A * ((A - 1) + (A + 1) * cw),
564 A * ((A + 1) + (A - 1) * cw - tsa),
565 (A + 1) - (A - 1) * cw + tsa,
566 2 * ((A - 1) - (A + 1) * cw),
567 (A + 1) - (A - 1) * cw - tsa);
575 double calibrate_anchor() {
576 const int ch = channels_;
577 const int blk = kBlock;
578 const int fft = engine_.fft_size();
579 const float r = 2.0f;
580 const long P =
static_cast<long>(fft) * 2;
581 const long N = P +
static_cast<long>(fft) * 6;
583 engine_.set_time_ratio(r);
585 std::vector<std::vector<SampleType>> in(
static_cast<size_t>(ch),
586 std::vector<SampleType>(
static_cast<size_t>(N), SampleType{0}));
587 std::vector<std::vector<SampleType>> sil(
static_cast<size_t>(ch),
588 std::vector<SampleType>(
static_cast<size_t>(blk), SampleType{0}));
589 std::vector<std::vector<SampleType>> ob(
static_cast<size_t>(ch),
590 std::vector<SampleType>(
static_cast<size_t>(blk)));
592 std::vector<const SampleType*> ip(
static_cast<size_t>(ch));
593 std::vector<const SampleType*> sp(
static_cast<size_t>(ch));
594 std::vector<SampleType*> op(
static_cast<size_t>(ch));
595 for (
int c = 0;
c < ch; ++
c) { sp[
c] = sil[
c].data(); op[
c] = ob[
c].data(); }
597 std::vector<SampleType> acc;
599 while (engine_.available_stretched() > 0) {
600 const int t = std::min(engine_.available_stretched(), blk);
601 engine_.read_stretched(op.data(), t);
602 for (
int i = 0;
i <
t; ++
i) acc.push_back(ob[0][
static_cast<size_t>(
i)]);
605 for (
long i = 0;
i < N;
i += blk) {
607 engine_.feed(ip.data(),
static_cast<int>(std::min<long>(blk, N -
i)));
610 for (
int k = 0;
k < 8; ++
k) { engine_.feed(sp.data(), blk);
drain(); }
613 SampleType mx = SampleType{0};
615 if (std::fabs(acc[
static_cast<size_t>(
i)]) > mx) { mx = std::fabs(acc[
static_cast<size_t>(
i)]); pk =
i; }
617 if (mx <= SampleType{0})
return 0.0;
618 return (
static_cast<double>(P) * r -
static_cast<double>(pk)) / (
static_cast<double>(
r) - 1.0);
626 void ola_tempo(
const SampleType*
const* in,
long in_frames,
627 SampleType*
const* out,
long out_frames,
double ratio) {
628 const int ch = channels_;
629 constexpr int W = 1024;
630 constexpr int Hs = W / 2;
631 constexpr double kTwoPi = 6.28318530717958647692;
632 const double Ha =
static_cast<double>(Hs) /
ratio;
633 for (
int c = 0;
c < ch; ++
c)
634 std::fill(out[
c], out[
c] + out_frames, SampleType{0});
635 for (
long k = 0;; ++
k) {
636 const long sp =
k * Hs;
637 if (sp >= out_frames)
break;
638 const long ap =
static_cast<long>(std::llround(
static_cast<double>(k) * Ha));
639 for (
int j = 0;
j < W; ++
j) {
640 const long so = sp +
j;
641 if (so >= out_frames)
break;
642 const long ii = ap +
j;
643 if (ii < 0 || ii >= in_frames)
continue;
644 const SampleType wnd =
static_cast<SampleType
>(
645 0.5 * (1.0 - std::cos(kTwoPi *
static_cast<double>(j)
646 /
static_cast<double>(W))));
647 for (
int c = 0;
c < ch; ++
c) out[
c][so] += in[
c][ii] * wnd;
659 bool tempo_stretch(
const SampleType*
const* in,
long in_frames,
660 SampleType*
const* out,
long out_frames,
double ratio) {
661 const int ch = channels_;
662 const int blk = kBlock;
663 const int fft = engine_.fft_size();
665 engine_.set_time_ratio(
static_cast<float>(
ratio));
667 const long pad =
static_cast<long>(std::ceil(
static_cast<double>(fft) /
ratio)) + fft;
672 const long lead =
static_cast<long>(std::llround(
673 (
static_cast<double>(pad) - latency_anchor_) *
ratio + latency_anchor_));
675 std::vector<std::vector<SampleType>> accum(
static_cast<size_t>(ch));
676 std::vector<std::vector<SampleType>> outblk(
static_cast<size_t>(ch),
677 std::vector<SampleType>(
static_cast<size_t>(blk)));
678 std::vector<std::vector<SampleType>> silblk(
static_cast<size_t>(ch),
679 std::vector<SampleType>(
static_cast<size_t>(blk), SampleType{0}));
680 std::vector<const SampleType*> inp(
static_cast<size_t>(ch));
681 std::vector<const SampleType*> silp(
static_cast<size_t>(ch));
682 std::vector<SampleType*> outp(
static_cast<size_t>(ch));
683 for (
int c = 0;
c < ch; ++
c) { silp[
c] = silblk[
c].data(); outp[
c] = outblk[
c].data(); }
686 while (engine_.available_stretched() > 0) {
687 const int take = std::min(engine_.available_stretched(), blk);
688 engine_.read_stretched(outp.data(), take);
689 for (
int c = 0;
c < ch; ++
c)
690 accum[
static_cast<size_t>(
c)].insert(
691 accum[
static_cast<size_t>(
c)].
end(),
692 outblk[
static_cast<size_t>(
c)].begin(),
693 outblk[
static_cast<size_t>(
c)].begin() + take);
696 auto feed = [&](
const SampleType*
const* src,
int n) { engine_.feed(src, n);
drain(); };
698 for (
long p = 0;
p < pad;
p += blk)
699 feed(silp.data(),
static_cast<int>(std::min<long>(blk, pad - p)));
700 for (
long i = 0;
i < in_frames;
i += blk) {
701 for (
int c = 0;
c < ch; ++
c) inp[
c] = in[
c] +
i;
702 feed(inp.data(),
static_cast<int>(std::min<long>(blk, in_frames -
i)));
704 const long need = lead + out_frames + fft;
705 const long cap = 2 * (in_frames + pad) + 64L * blk;
706 for (
long g = 0;
static_cast<long>(accum[0].size()) < need && g < cap;
g += blk)
707 feed(silp.data(), blk);
709 for (
int c = 0;
c < ch; ++
c)
710 for (
long i = 0;
i < out_frames; ++
i) {
711 const long idx = lead +
i;
712 out[
c][
i] = (idx >= 0 && idx < static_cast<long>(accum[
static_cast<size_t>(
c)].
size()))
713 ? accum[
static_cast<size_t>(
c)][
static_cast<size_t>(idx)]
722 std::vector<long> detect_onsets(
const SampleType*
const* in,
long n)
const {
723 std::vector<long> onsets;
724 if (n < kOnsetWin)
return onsets;
725 const long hop = kOnsetHop;
726 auto win_energy = [&](
long s) {
728 const long end = std::min<long>(s + kOnsetWin, n);
729 for (
long i = s;
i <
end; ++
i)
730 for (
int c = 0;
c < channels_; ++
c) {
731 const SampleType
v = in[
c][
i];
732 e +=
static_cast<double>(
v) *
static_cast<double>(v);
736 std::vector<double> flux;
737 flux.reserve(
static_cast<size_t>(n / hop + 1));
738 double prev = win_energy(0);
739 for (
long s = hop;
s + kOnsetWin <=
n;
s += hop) {
740 const double e = win_energy(s);
741 flux.push_back(std::max(0.0,
e - prev));
744 if (flux.empty())
return onsets;
745 double mean = 0.0;
for (
double f : flux) mean +=
f; mean /=
static_cast<double>(flux.size());
746 const double thresh = mean * kOnsetThreshMult;
747 long last = -kOnsetMinGap;
748 for (
size_t i = 0;
i < flux.size(); ++
i) {
749 const bool peak = (
i == 0 || flux[
i] >= flux[
i - 1]) &&
750 (
i + 1 >= flux.size() || flux[
i] >= flux[
i + 1]);
751 const long sample =
static_cast<long>(
i + 1) * hop;
752 if (flux[
i] > thresh &&
peak && sample - last > kOnsetMinGap) {
753 onsets.push_back(sample);
761 double abs_sum(
const SampleType*
const* x,
long i,
long n)
const {
779 static void rbj_highpass(
const SampleType* src, SampleType* dst,
long n,
double sr,
double f0) {
780 const double q = 0.7071;
781 const double w0 = 2.0 * 3.14159265358979323846 * f0 / sr;
782 const double cw = std::cos(w0), sw = std::sin(w0),
alpha = sw / (2.0 *
q);
783 const double b0 = (1.0 + cw) * 0.5, b1 = -(1.0 + cw), b2 = (1.0 + cw) * 0.5;
784 const double a0 = 1.0 +
alpha, a1 = -2.0 * cw, a2 = 1.0 -
alpha;
785 const double nb0 = b0 / a0, nb1 = b1 / a0, nb2 = b2 / a0, na1 = a1 / a0, na2 = a2 / a0;
786 double z1 = 0.0, z2 = 0.0;
787 for (
long i = 0;
i <
n; ++
i) {
788 const double in_s = src[
static_cast<size_t>(
i)];
789 const double out_s = nb0 * in_s + z1;
790 z1 = nb1 * in_s - na1 * out_s + z2;
791 z2 = nb2 * in_s - na2 * out_s;
792 dst[
static_cast<size_t>(
i)] =
static_cast<SampleType
>(out_s);
810 void restore_onset_head(
const SampleType*
const* in,
long in_frames,
811 SampleType*
const* out,
long out_frames)
const noexcept {
821 const long fft = engine_.fft_size() > 0 ? engine_.fft_size() : 4096;
822 const long ramp = std::max<long>(1, fft / 2);
823 const long head_span = std::max<long>(
static_cast<long>(std::llround(kHeadMs * sample_rate_)), ramp);
824 const long head = std::min<long>({out_frames, in_frames, head_span});
825 if (head < 2)
return;
826 SampleType in_peak = SampleType{0};
827 SampleType out_peak = SampleType{0};
828 for (
int c = 0;
c < channels_; ++
c)
829 for (
long i = 0;
i < head; ++
i) {
830 in_peak = std::max(in_peak, std::fabs(in[
c][
static_cast<size_t>(
i)]));
831 out_peak = std::max(out_peak, std::fabs(out[
c][
static_cast<size_t>(
i)]));
833 if (in_peak <= kHeadEps)
return;
834 if (in_peak <= out_peak * kHeadRatio)
return;
835 constexpr double kHalfPi = 1.5707963267948966;
836 for (
long i = 0;
i < head; ++
i) {
837 const double cw = std::cos(kHalfPi *
static_cast<double>(
i) /
static_cast<double>(head));
838 const SampleType
w =
static_cast<SampleType
>(cw * cw);
839 for (
int c = 0;
c < channels_; ++
c)
840 out[
c][
static_cast<size_t>(
i)] =
841 w * in[
c][
static_cast<size_t>(
i)]
842 + (SampleType{1} -
w) * out[
c][
static_cast<size_t>(
i)];
846 void relocate_transients(
const SampleType*
const* in,
long in_frames,
847 SampleType*
const* out,
long out_frames,
double ratio)
const {
848 if (in_frames <= 0 || out_frames <= 0)
return;
849 const long W = kReloAttack;
850 const long xf = std::min<long>(kReloXfade, W);
861 std::vector<std::vector<SampleType>> hp_in(
static_cast<size_t>(channels_));
862 std::vector<std::vector<SampleType>> hp_out(
static_cast<size_t>(channels_));
863 for (
int c = 0;
c < channels_; ++
c) {
864 hp_in[
static_cast<size_t>(
c)].resize(
static_cast<size_t>(in_frames));
865 hp_out[
static_cast<size_t>(
c)].resize(
static_cast<size_t>(out_frames));
866 rbj_highpass(in[
c], hp_in[
static_cast<size_t>(
c)].data(), in_frames, sample_rate_, kReloHpHz);
867 rbj_highpass(out[
c], hp_out[
static_cast<size_t>(
c)].data(), out_frames, sample_rate_, kReloHpHz);
873 const long search = std::clamp<long>(
874 std::llround(
static_cast<double>(kOnsetMinGap) *
ratio * 0.4), 128L, kReloSearch);
875 for (
long oi : detect_onsets(in, in_frames)) {
879 long ip = oi;
double ipk = -1.0;
880 for (
long j = std::max(0L, oi - kReloBack);
j < std::min(in_frames, oi + kOnsetWin + W); ++
j) {
881 const double a = abs_sum(in, j, in_frames);
882 if (
a > ipk) { ipk =
a; ip =
j; }
884 if (ipk <= 0.0)
continue;
886 const long op0 = std::llround(
static_cast<double>(oi) *
ratio);
887 long op = op0;
double opk = -1.0;
888 for (
long p = std::max(0L, op0 - search);
p <= std::min(out_frames - 1, op0 + search); ++
p) {
889 const double a = abs_sum(out, p, out_frames);
890 if (
a > opk) { opk =
a; op =
p; }
894 for (
long k = -W;
k <= W; ++
k) {
895 const long si = ip +
k, di = op +
k;
896 if (si < 0 || si >= in_frames || di < 0 || di >= out_frames)
continue;
897 const long ak = std::llabs(k);
898 SampleType
w = SampleType{1};
900 w =
static_cast<SampleType
>(W - ak) /
static_cast<SampleType
>(xf);
904 for (
int c = 0;
c < channels_; ++
c)
905 out[
c][di] += w * (hp_in[
static_cast<size_t>(
c)][
static_cast<size_t>(si)] -
906 hp_out[
static_cast<size_t>(
c)][
static_cast<size_t>(di)]);
916 bool pitch_shift(
const SampleType*
const* in,
long in_frames,
917 SampleType*
const* out,
long out_frames,
919 const int ch = channels_;
920 const int blk = kBlock;
921 pitch_engine_.set_pitch_semitones(
static_cast<float>(semitones));
925 pitch_engine_.set_formant_semitones(0.0f);
break;
928 pitch_engine_.set_formant_semitones(0.0f);
break;
931 pitch_engine_.set_formant_semitones(
static_cast<float>(fsemis));
break;
933 pitch_engine_.reset();
935 const long lat = pitch_engine_.latency_samples();
936 std::vector<std::vector<SampleType>> accum(
static_cast<size_t>(ch));
937 std::vector<std::vector<SampleType>> ob(
static_cast<size_t>(ch),
938 std::vector<SampleType>(
static_cast<size_t>(blk)));
939 std::vector<std::vector<SampleType>> sil(
static_cast<size_t>(ch),
940 std::vector<SampleType>(
static_cast<size_t>(blk), SampleType{0}));
941 std::vector<const SampleType*> ip(
static_cast<size_t>(ch));
942 std::vector<const SampleType*> sp(
static_cast<size_t>(ch));
943 std::vector<SampleType*> op(
static_cast<size_t>(ch));
944 for (
int c = 0;
c < ch; ++
c) { sp[
c] = sil[
c].data(); op[
c] = ob[
c].data(); }
946 auto pump = [&](
const SampleType*
const* src,
int nn) {
947 pitch_engine_.process(src, op.data(), nn);
948 for (
int c = 0;
c < ch; ++
c)
949 accum[
static_cast<size_t>(
c)].insert(
950 accum[
static_cast<size_t>(
c)].
end(),
951 ob[
static_cast<size_t>(
c)].begin(),
952 ob[
static_cast<size_t>(
c)].begin() + nn);
954 for (
long i = 0;
i < in_frames;
i += blk) {
955 for (
int c = 0;
c < ch; ++
c) ip[
c] = in[
c] +
i;
956 pump(ip.data(),
static_cast<int>(std::min<long>(blk, in_frames -
i)));
958 for (
long f = 0;
f < lat;
f += blk)
959 pump(sp.data(),
static_cast<int>(std::min<long>(blk, lat - f)));
961 for (
int c = 0;
c < ch; ++
c)
962 for (
long i = 0;
i < out_frames; ++
i) {
963 const long idx = lat +
i;
964 out[
c][
i] = (idx >= 0 && idx < static_cast<long>(accum[
static_cast<size_t>(
c)].
size()))
965 ? accum[
static_cast<size_t>(
c)][
static_cast<size_t>(idx)]
971 static bool fail(std::string* err,
const char* msg) {
976 static constexpr int kBlock = 4096;
979 static constexpr long kOnsetWin = 512;
980 static constexpr long kOnsetHop = 128;
981 static constexpr double kOnsetThreshMult = 4.0;
982 static constexpr long kOnsetMinGap = 1920;
983 static constexpr long kReloAttack = 256;
984 static constexpr long kReloXfade = 64;
985 static constexpr long kReloBack = 768;
986 static constexpr long kReloSearch = 1536;
987 static constexpr double kHeadMs = 0.010;
988 static constexpr SampleType kHeadEps =
static_cast<SampleType
>(1
e-3);
989 static constexpr SampleType kHeadRatio =
static_cast<SampleType
>(1.1);
990 static constexpr double kReloHpHz = 300.0;
991 RealtimePitchTimeProcessorT<SampleType> engine_;
992 RealtimePitchTimeProcessorT<SampleType> pitch_engine_;
993 double latency_anchor_ = 0.0;
994 double sample_rate_ = 0.0;
996 double max_time_ratio_ = 4.0;
997 double max_pitch_semitones_ = 24.0;
999 bool prepared_ =
false;