92 detector_.prepare(spec.
sampleRate, windowSize, windowSize / 4);
93 monoScratch_.assign(
static_cast<size_t>(std::max(spec.
maxBlockSize, 1)), T(0));
94 prepared_.store(
true, std::memory_order_relaxed);
110 samplesSinceValid_ = std::numeric_limits<int64_t>::max() / 2;
111 smoothedHz_.store(T(0), std::memory_order_relaxed);
112 tracking_.store(
false, std::memory_order_relaxed);
124 if (!std::isfinite(minHz) || !std::isfinite(maxHz))
return;
125 minHz = std::max(minHz, T(10));
126 maxHz = std::max(maxHz, minHz);
127 minHz_.store(minHz, std::memory_order_relaxed);
128 maxHz_.store(maxHz, std::memory_order_relaxed);
134 if (!std::isfinite(threshold))
return;
135 confidence_.store(std::clamp(threshold, T(0), T(1)), std::memory_order_relaxed);
145 if (!std::isfinite(msPerOctave))
return;
146 glideMs_.store(std::max(msPerOctave, T(0)), std::memory_order_relaxed);
150 [[nodiscard]] T
getMinHz() const noexcept {
return minHz_.load(std::memory_order_relaxed); }
153 [[nodiscard]] T
getMaxHz() const noexcept {
return maxHz_.load(std::memory_order_relaxed); }
159 [[nodiscard]] T
getGlide() const noexcept {
return glideMs_.load(std::memory_order_relaxed); }
166 return smoothedHz_.load(std::memory_order_relaxed);
170 [[nodiscard]] T
getRawHz() const noexcept {
return detector_.getFrequencyHz(); }
173 [[nodiscard]] T
getConfidence() const noexcept {
return detector_.getConfidence(); }
178 return tracking_.load(std::memory_order_relaxed);
189 if (!prepared_.load(std::memory_order_relaxed))
return;
190 const int nCh = buffer.getNumChannels();
191 const int nS = buffer.getNumSamples();
192 if (nCh <= 0 || nS <= 0)
return;
197 const int chunk = std::min(nS - i,
static_cast<int>(monoScratch_.size()));
200 const T* src = buffer.getChannel(0) + i;
201 std::copy(src, src + chunk, monoScratch_.begin());
205 const T invCh = T(1) /
static_cast<T
>(nCh);
206 std::fill(monoScratch_.begin(), monoScratch_.begin() + chunk, T(0));
207 for (
int ch = 0; ch < nCh; ++ch)
209 const T* src = buffer.getChannel(ch) + i;
210 for (
int k = 0; k < chunk; ++k)
211 monoScratch_[
static_cast<size_t>(k)] += src[k] * invCh;
214 pushSamples({ monoScratch_.data(),
static_cast<size_t>(chunk) });
225 if (!prepared_.load(std::memory_order_relaxed) || samples.empty())
return;
226 detector_.pushSamples(samples);
227 update(
static_cast<int>(samples.size()));
232 void update(
int numSamples)
noexcept
234 const T raw = detector_.getFrequencyHz();
235 const T conf = detector_.getConfidence();
236 const bool valid = conf >= confidence_.load(std::memory_order_relaxed)
237 && raw >= minHz_.load(std::memory_order_relaxed)
238 && raw <= maxHz_.load(std::memory_order_relaxed);
242 double st = 12.0 * std::log2(
static_cast<double>(raw) / 440.0);
246 targetSt_ = currentSt_ = st;
251 double dev = st - targetSt_;
252 if (std::abs(dev) > 7.0)
256 for (
const double oct : { -24.0, -12.0, 12.0, 24.0 })
258 if (std::abs(st + oct - targetSt_) < 1.5)
261 dev = st - targetSt_;
267 if (std::abs(dev) > 7.0)
270 if (pendingCount_ > 0 && std::abs(st - pendingSt_) < 1.0)
272 if (++pendingCount_ >= 3)
290 samplesSinceValid_ = 0;
294 samplesSinceValid_ += numSamples;
299 const double glideMs =
static_cast<double>(glideMs_.load(std::memory_order_relaxed));
302 currentSt_ = targetSt_;
307 const double maxStep = 12.0 *
static_cast<double>(numSamples)
308 / (glideMs * 0.001 * sampleRate_);
309 currentSt_ += std::clamp(targetSt_ - currentSt_, -maxStep, maxStep);
311 smoothedHz_.store(
static_cast<T
>(440.0 * std::exp2(currentSt_ / 12.0)),
312 std::memory_order_relaxed);
315 tracking_.store(hasTarget_
316 && samplesSinceValid_ <
static_cast<int64_t
>(0.25 * sampleRate_),
317 std::memory_order_relaxed);
321 double sampleRate_ = 48000.0;
322 std::atomic<bool> prepared_{
false };
324 PitchDetector<T> detector_;
325 std::vector<T> monoScratch_;
327 bool hasTarget_ =
false;
328 double targetSt_ = 0.0;
329 double currentSt_ = 0.0;
330 double pendingSt_ = 0.0;
331 int pendingCount_ = 0;
332 int64_t samplesSinceValid_ = 0;
334 std::atomic<T> minHz_ { T(60) };
335 std::atomic<T> maxHz_ { T(1200) };
336 std::atomic<T> confidence_ { T(0.85) };
337 std::atomic<T> glideMs_ { T(60) };
339 std::atomic<T> smoothedHz_ { T(0) };
340 std::atomic<bool> tracking_ {
false };