86 const double target = sampleRate * (2048.0 / 48000.0);
87 while (size < 16384 &&
static_cast<double>(size) < target)
99 return (8 *
static_cast<std::size_t
>(fftSize) + 4) *
sizeof(T) +
sizeof(
FFTReal<T>) + 2048;
108 window_.assign(
static_cast<size_t>(fftSize_), T(0));
110 time_.assign(
static_cast<size_t>(fftSize_), T(0));
111 spectrum_.assign(
static_cast<size_t>(fftSize_) + 2, T(0));
112 magnitude_.assign(
static_cast<size_t>(fftSize_ / 2 + 1), T(0));
113 phase_.assign(magnitude_.size(), T(0));
114 fft_ = std::make_unique<FFTReal<T>>(
static_cast<size_t>(fftSize_));
122 void compute(
const T *input,
bool withPhase =
true) noexcept
127 for (
size_t k = 0; k < magnitude_.size(); ++k)
129 const T re = spectrum_[2 * k], im = spectrum_[2 * k + 1];
130 magnitude_[k] = binMagnitude(k);
132 phase_[k] = std::atan2(im, re);
134 phaseValid_ = withPhase;
147 if (!fft_ || frame.getNumSamples() != fftSize_ || frame.getNumChannels() < 1 ||
148 frame.getNumChannels() > 2)
150 for (
int c = 0; c < frame.getNumChannels(); ++c)
151 if (!frame.getChannel(c))
153 compute(frame.getChannel(0),
false);
154 if (frame.getNumChannels() == 2)
156 transform(frame.getChannel(1));
157 for (
size_t k = 0; k < magnitude_.size(); ++k)
159 const T other = binMagnitude(k);
160 if (magnitude_[k] != other)
161 magnitude_[k] = std::hypot(magnitude_[k], other) * invSqrt2<T>;
174 [[nodiscard]] std::span<const T>
phases() const noexcept
176 return phaseValid_ ? std::span<const T>(phase_) : std::span<const T>();
180 void transform(
const T *input)
noexcept
182 for (
int k = 0; k < fftSize_; ++k)
183 time_[
static_cast<size_t>(k)] = input[k] * window_[
static_cast<size_t>(k)];
184 fft_->forward(time_.data(), spectrum_.data());
186 [[nodiscard]] T binMagnitude(
size_t k)
const noexcept
188 const T re = spectrum_[2 * k], im = spectrum_[2 * k + 1];
189 return std::sqrt(re * re + im * im);
192 bool phaseValid_ =
false;
193 std::unique_ptr<FFTReal<T>> fft_;
194 std::vector<T> window_, time_, spectrum_, magnitude_, phase_;
221 return (3 *
static_cast<std::size_t
>(fftSize) + 4 + 4 * kBandCapacity) *
sizeof(T) +
222 5 * kBandCapacity *
sizeof(
int);
225 void prepare(
double sampleRate,
int fftSize,
bool boundedWorkspace =
false)
228 sampleRate_ = sampleRate;
230 numBins_ = fftSize / 2 + 1;
231 odfScale_ =
static_cast<T
>(kOdfRefFrame /
static_cast<double>(fftSize_));
232 prevPhase_.assign(
static_cast<size_t>(numBins_), T(0));
233 prevPhase2_.assign(
static_cast<size_t>(numBins_), T(0));
234 prevMag_.assign(
static_cast<size_t>(numBins_), T(0));
235 whitenPeak_.assign(
static_cast<size_t>(numBins_), T(0));
236 buildFilterBank(boundedWorkspace);
237 bandCur_.assign(
static_cast<size_t>(numBands_), T(0));
238 bandPrev_.assign(
static_cast<size_t>(numBands_), T(0));
239 bandMaxPrev_.assign(
static_cast<size_t>(numBands_), T(0));
247 std::fill(prevMag_.begin(), prevMag_.end(), T(0));
248 std::fill(prevPhase_.begin(), prevPhase_.end(), T(0));
249 std::fill(prevPhase2_.begin(), prevPhase2_.end(), T(0));
250 std::fill(whitenPeak_.begin(), whitenPeak_.end(), T(0));
251 std::fill(bandPrev_.begin(), bandPrev_.end(), T(0));
252 std::fill(bandMaxPrev_.begin(), bandMaxPrev_.end(), T(0));
253 curRegisters_.fill(T(0));
254 phaseHistoryValid_ =
true;
268 bool whiten =
false) noexcept
271 if (!prepared_ || mag.size() !=
static_cast<size_t>(numBins_) ||
272 (!phase.empty() && phase.size() != mag.size()) ||
277 curRegisters_.fill(T(0));
284 result.
value = computeComplexFlux(mag, phase);
290 result.
value = computeSuperFlux(mag, whiten);
295 std::copy(prevPhase_.begin(), prevPhase_.end(), prevPhase2_.begin());
296 std::copy(phase.begin(), phase.end(), prevPhase_.begin());
299 phaseHistoryValid_ =
false;
300 std::copy(mag.begin(), mag.end(), prevMag_.begin());
307 T computeSpectralFlux(std::span<const T> mag)
const noexcept
310 for (
int k = 0; k < numBins_; ++k)
312 const T d = mag[
static_cast<size_t>(k)] - prevMag_[
static_cast<size_t>(k)];
316 odf /=
static_cast<T
>(numBins_);
319 T computeComplexFlux(std::span<const T> mag, std::span<const T> phase)
const noexcept
324 for (
int k = 0; k < numBins_; ++k)
326 const T target = princArg(T(2) * prevPhase_[
static_cast<size_t>(k)] -
327 prevPhase2_[
static_cast<size_t>(k)]);
328 const T pm = prevMag_[
static_cast<size_t>(k)];
329 const T cm = mag[
static_cast<size_t>(k)];
330 const T re = cm * std::cos(phase[
static_cast<size_t>(k)]) - pm * std::cos(target);
331 const T im = cm * std::sin(phase[
static_cast<size_t>(k)]) - pm * std::sin(target);
333 odf += std::sqrt(re * re + im * im);
335 odf /=
static_cast<T
>(numBins_);
338 T computeSuperFlux(std::span<const T> mag,
bool whiten)
noexcept
352 filterLogBands(mag, bandCur_, whiten ? T(1) : odfScale_);
353 curRegisters_.fill(T(0));
354 for (
int b = 0; b < numBands_; ++b)
356 const T d = bandCur_[
static_cast<size_t>(b)] - bandMaxPrev_[
static_cast<size_t>(b)];
360 if (b <
static_cast<int>(bandRegister_.size()))
361 curRegisters_[
static_cast<size_t>(bandRegister_[
static_cast<size_t>(b)])] += d;
364 odf /=
static_cast<T
>(numBands_);
366 if (registerBands_[
static_cast<size_t>(g)] > 0)
367 curRegisters_[
static_cast<size_t>(g)] /=
368 static_cast<T
>(registerBands_[
static_cast<size_t>(g)]);
371 bandPrev_ = bandCur_;
372 maxFilterFreq(bandPrev_, bandMaxPrev_);
375 void applyWhitening(std::span<T> mag)
noexcept
377 for (
int k = 0; k < numBins_; ++k)
379 T &pk = whitenPeak_[
static_cast<size_t>(k)];
380 const T decayed = pk * kWhitenDecay;
381 const T m = mag[
static_cast<size_t>(k)];
382 pk = std::max({m, kWhitenFloor, decayed});
383 mag[
static_cast<size_t>(k)] = m / pk;
387 void buildFilterBank(
bool boundedWorkspace)
392 bandRegister_.clear();
393 registerBands_.fill(0);
395 const double binHz = sampleRate_ /
static_cast<double>(fftSize_);
396 const double fMax = std::min(kFMaxHz, sampleRate_ * 0.5 * 0.999);
399 std::vector<int> centres;
400 if (boundedWorkspace)
406 centres.reserve(kBandCapacity);
407 fbStart_.reserve(kBandCapacity);
408 fbOffset_.reserve(kBandCapacity);
409 fbCount_.reserve(kBandCapacity);
410 bandRegister_.reserve(kBandCapacity);
411 fbWeights_.reserve(
static_cast<size_t>(fftSize_) + kBandCapacity);
413 for (
int i = 0;; ++i)
415 const double f = kFMin * std::pow(2.0,
static_cast<double>(i) /
416 static_cast<double>(kBandsPerOctave));
419 int bin =
static_cast<int>(std::lround(f / binHz));
420 bin = std::clamp(bin, 0, numBins_ - 1);
421 if (centres.empty() || bin > centres.back())
422 centres.push_back(bin);
427 for (
size_t j = 1; j + 1 < centres.size(); ++j)
429 const int lo = centres[j - 1];
430 const int ce = centres[j];
431 const int hi = centres[j + 1];
432 if (!(lo < ce && ce < hi))
436 const double fc =
static_cast<double>(ce) * binHz;
437 const int g = fc < 200.0 ? 0 : fc < 800.0 ? 1 : fc < 3200.0 ? 2 : 3;
438 bandRegister_.push_back(g);
439 ++registerBands_[
static_cast<size_t>(g)];
441 fbStart_.push_back(lo);
442 fbOffset_.push_back(
static_cast<int>(fbWeights_.size()));
443 for (
int k = lo; k <= hi; ++k)
447 wv =
static_cast<T
>(
static_cast<double>(k - lo) /
static_cast<double>(ce - lo));
449 wv =
static_cast<T
>(
static_cast<double>(hi - k) /
static_cast<double>(hi - ce));
450 fbWeights_.push_back(wv);
455 for (
int b = 0; b < numBands_; ++b)
457 const int off = fbOffset_[
static_cast<size_t>(b)];
458 const int nextOff = (b + 1 < numBands_) ? fbOffset_[
static_cast<size_t>(b + 1)]
459 :
static_cast<int>(fbWeights_.size());
460 fbCount_.push_back(nextOff - off);
466 void filterLogBands(std::span<const T> mag, std::vector<T> &out, T scale)
noexcept
468 for (
int b = 0; b < numBands_ && b < static_cast<int>(fbStart_.size()); ++b)
470 const int start = fbStart_[
static_cast<size_t>(b)];
471 const int off = fbOffset_[
static_cast<size_t>(b)];
472 const int cnt = fbCount_[
static_cast<size_t>(b)];
474 for (
int i = 0; i < cnt; ++i)
476 const int k = start + i;
477 if (k >= 0 && k < numBins_)
478 acc += mag[
static_cast<size_t>(k)] * fbWeights_[
static_cast<size_t>(off + i)];
480 out[
static_cast<size_t>(b)] = std::log10(acc * scale + T(1));
484 void maxFilterFreq(
const std::vector<T> &in, std::vector<T> &out)
const noexcept
486 for (
int b = 0; b < numBands_; ++b)
488 T mx = in[
static_cast<size_t>(b)];
490 mx = std::max(mx, in[
static_cast<size_t>(b - 1)]);
491 if (b + 1 < numBands_)
492 mx = std::max(mx, in[
static_cast<size_t>(b + 1)]);
493 out[
static_cast<size_t>(b)] = mx;
497 static T princArg(T x)
noexcept
500 const T twoPiT = twoPi<T>;
501 T y = x - twoPiT * std::floor(x / twoPiT + T(0.5));
505 static constexpr double kOdfRefFrame = 2048.0;
506 static constexpr double kFMin = 27.5;
507 static constexpr double kFMaxHz = 16000.0;
508 static constexpr int kBandsPerOctave = 24;
509 static constexpr std::size_t kBandCapacity = 256;
510 static constexpr T kWhitenDecay = T(0.9995), kWhitenFloor = T(1e-4);
511 double sampleRate_ = 44100;
512 int fftSize_ = 0, numBins_ = 0, numBands_ = 1;
513 bool phaseHistoryValid_ =
true;
514 bool prepared_ =
false;
516 std::vector<T> prevPhase_, prevPhase2_, prevMag_, whitenPeak_;
517 std::vector<int> fbStart_, fbOffset_, fbCount_, bandRegister_;
518 std::vector<T> fbWeights_, bandCur_, bandPrev_, bandMaxPrev_;
519 std::array<T, kNumRegisters> curRegisters_{};
520 std::array<int, kNumRegisters> registerBands_{};