29template <
typename SampleType =
float>
50 static constexpr int kHop = 256;
58 sample_rate_ = (std::isfinite(sample_rate) && sample_rate > 0.0)
62 tau_min_ =
static_cast<int>(std::ceil(sample_rate_ / f0_max_));
63 tau_max_ =
static_cast<int>(std::ceil(sample_rate_ / f0_min_));
64 tau_min_ = std::max(tau_min_, 2);
65 tau_max_ = std::max(tau_max_, tau_min_ + 1);
66 window_ = 2 * tau_max_;
75 integration_ = window_ - tau_max_;
77 ring_.assign(
static_cast<std::size_t
>(window_), 0.0);
78 scratch_.assign(
static_cast<std::size_t
>(window_), 0.0);
79 diff_.assign(
static_cast<std::size_t
>(tau_max_) + 1, 0.0);
80 cmnd_.assign(
static_cast<std::size_t
>(tau_max_) + 1, 0.0);
85 std::fill(ring_.begin(), ring_.end(), 0.0);
115 if (!std::isfinite(min_hz) || !std::isfinite(max_hz))
return;
118 if (hi <= lo)
return;
129 if (window_ <= 0)
return false;
130 if (!std::isfinite(
static_cast<double>(x))) {
134 ring_[
static_cast<std::size_t
>(write_)] =
static_cast<double>(x);
135 write_ = (write_ + 1) % window_;
136 valid_samples_ = std::min(valid_samples_ + 1, window_);
137 if (++hop_counter_ <
kHop)
return false;
145 double f0_hz()
const {
return f0_hz_; }
170 return static_cast<double>(tau_max_) *
static_cast<double>(integration_) /
171 static_cast<double>(
kHop);
180 const int missing = window_ - valid_samples_;
181 for (
int i = 0;
i < window_; ++
i) {
182 scratch_[
static_cast<std::size_t
>(
i)] =
184 : ring_[static_cast<
std::size_t>((write_ +
i) % window_)];
192 for (
int tau = 1; tau <= tau_max_; ++tau) {
194 for (
int j = 0; j < integration_; ++j) {
195 const double d = scratch_[
static_cast<std::size_t
>(j)] -
196 scratch_[
static_cast<std::size_t
>(j + tau)];
199 diff_[
static_cast<std::size_t
>(tau)] = sum;
207 for (
int tau = 1; tau <= tau_max_; ++tau) {
208 running += diff_[
static_cast<std::size_t
>(tau)];
209 cmnd_[
static_cast<std::size_t
>(tau)] =
210 running > 0.0 ? diff_[
static_cast<std::size_t
>(tau)] *
211 static_cast<double>(tau) /
running
219 for (
int tau = tau_min_; tau <= tau_max_; ++tau) {
221 while (tau + 1 <= tau_max_ &&
222 cmnd_[
static_cast<std::size_t
>(tau + 1)] <
223 cmnd_[
static_cast<std::size_t
>(tau)])
230 double lowest = cmnd_[
static_cast<std::size_t
>(tau_min_)];
233 for (
int tau = tau_min_ + 1; tau <= tau_max_; ++tau) {
234 if (cmnd_[
static_cast<std::size_t
>(tau)] < lowest) {
235 lowest = cmnd_[
static_cast<std::size_t
>(tau)];
241 lowest = cmnd_[
static_cast<std::size_t
>(best)];
247 if (!voiced_)
return;
250 const double refined = parabolic(best);
253 median_buffer_[
static_cast<std::size_t
>(median_index_)] = refined;
257 tau_ = median_of_buffer();
258 f0_hz_ = tau_ > 0.0 ? sample_rate_ / tau_ : 0.0;
264 double parabolic(
int tau)
const {
265 if (tau <= tau_min_ || tau >= tau_max_)
return static_cast<double>(tau);
266 const double a = cmnd_[
static_cast<std::size_t
>(tau - 1)];
267 const double b = cmnd_[
static_cast<std::size_t
>(tau)];
268 const double c = cmnd_[
static_cast<std::size_t
>(tau + 1)];
269 const double denom =
a - 2.0 *
b +
c;
270 if (!(std::abs(denom) > 1
e-18))
return static_cast<double>(tau);
271 const double offset = 0.5 * (
a -
c) / denom;
274 if (!(std::abs(offset) <= 1.0))
return static_cast<double>(tau);
275 return static_cast<double>(tau) + offset;
278 double median_of_buffer()
const {
280 const int n = median_count_;
281 for (
int i = 0;
i <
n; ++
i)
282 sorted[
i] = median_buffer_[
static_cast<std::size_t
>(
i)];
283 std::sort(sorted, sorted + n);
284 return sorted[
n / 2];
287 double sample_rate_ = 48000.0;
294 int integration_ = 0;
296 std::vector<double> ring_{};
297 std::vector<double> scratch_{};
298 std::vector<double> diff_{};
299 std::vector<double> cmnd_{};
302 int valid_samples_ = 0;
303 int hop_counter_ = 0;
306 int median_index_ = 0;
307 int median_count_ = 0;
311 double min_cmnd_ = 1.0;
312 bool voiced_ =
false;
Monophonic fundamental-frequency estimator, after de Cheveigné & Kawahara 2002 [1],...
Definition yin_tracker.hpp:30
void prepare(double sample_rate)
Definition yin_tracker.hpp:57
int integration_samples() const
Definition yin_tracker.hpp:162
static constexpr int kMedianTaps
Definition yin_tracker.hpp:54
static constexpr double kYinThreshold
Definition yin_tracker.hpp:46
int tau_max() const
Definition yin_tracker.hpp:164
void reset()
Definition yin_tracker.hpp:84
double cost_mac_per_sample() const
Amortized analysis cost, so a caller can see before profiling that this is a one-per-track block rath...
Definition yin_tracker.hpp:169
double f0_max_hz() const
Definition yin_tracker.hpp:124
void discard_history() noexcept
Constant-time logical reset for audio-thread fault recovery.
Definition yin_tracker.hpp:98
static constexpr int hop_samples()
Definition yin_tracker.hpp:165
double f0_min_hz() const
Definition yin_tracker.hpp:123
static constexpr double kF0MinLimit
Definition yin_tracker.hpp:36
double tau_samples() const
The refined period in samples, before the Hz conversion.
Definition yin_tracker.hpp:148
static constexpr double kF0MinDefault
Definition yin_tracker.hpp:35
int window_samples() const
Definition yin_tracker.hpp:161
bool process(SampleType x)
Feeds one sample.
Definition yin_tracker.hpp:128
double f0_hz() const
The current estimate.
Definition yin_tracker.hpp:145
static constexpr int kHop
Definition yin_tracker.hpp:50
void set_f0_range(double min_hz, double max_hz)
Sets the tracked range.
Definition yin_tracker.hpp:114
static constexpr int kMaxMedianTaps
Definition yin_tracker.hpp:55
double min_cmnd() const
The frame's minimum CMND — the tracker's own confidence reading.
Definition yin_tracker.hpp:154
bool voiced() const
False when the frame carried no stable period (min d' > kYinThreshold).
Definition yin_tracker.hpp:151
int tau_min() const
Definition yin_tracker.hpp:163
int latency_samples() const
Series law 5: the estimate for a frame needs the whole window, so this is W.
Definition yin_tracker.hpp:159
static constexpr double kF0MaxLimit
Definition yin_tracker.hpp:37
static constexpr double kF0MaxDefault
Definition yin_tracker.hpp:41
@ b
Definition pitch.hpp:31
T arg(const std::complex< T > &z) noexcept
Counted std::arg (an atan2). Bit-identical result.
Definition rt_work_counter.hpp:161
Definition additive_bank.hpp:218
YinTrackerT< float > YinTracker
Definition yin_tracker.hpp:315
@ c
Definition spectrum_trace.hpp:35
YinTrackerT< double > YinTracker64
Definition yin_tracker.hpp:316
@ a
Definition formant_filter_bank.hpp:20
@ i
Definition formant_filter_bank.hpp:20
@ e
Definition formant_filter_bank.hpp:20
@ running
Maintain running sums as frames arrive; the latch only divides.
Definition freeze_hold.hpp:136
@ n
Definition input_events.hpp:40