#pragma once #include "DSPConstants.h" #include #include #include // <- 1 row added namespace FDNReverb { // ----------------------------------------------------------------------------- // AcousticMetrics class // ----------------------------------------------------------------------------- // Computes real-time acoustic metrics (D50, C50, C80, EDT). // // Principle: // Accumulate the squared energy of the input signal in a ring buffer, // then compare it against the energy from 50 ms / 80 ms ago, // and compute the D50 / C50 / C80 values. // // Sample-rate support: // times (ms) are converted to sample counts, // so 44.1 kHz through 192 kHz are supported automatically. // // CPU: // O(1) per-sample computation (energy accumulation) // CPU overhead: below ~0.5% // ----------------------------------------------------------------------------- class AcousticMetrics { public: AcousticMetrics() = default; // -- initialize -- // sampleRate: sample rate (Hz) // analysisWindowMs: analysis window (ms). 2000 ms (2 s) void prepare(double sampleRate, float analysisWindowMs = 2000.0f); // -- per-sample state update -- // sample: current Wet signal sample (mono) void processSample(float sample) noexcept; // -- value getters -- // ranges: // D50: 0.0 ~ 1.0 ( 0.3~0.9) // C50: -10 ~ +30 dB // C80: -10 ~ +30 dB // EDT: 0.0 ~ 5.0 (s) float getD50() const noexcept { return d50.load(std::memory_order_relaxed); } float getC50() const noexcept { return c50.load(std::memory_order_relaxed); } float getC80() const noexcept { return c80.load(std::memory_order_relaxed); } float getEDT() const noexcept { return edt.load(std::memory_order_relaxed); } // --- added: expose energy history for drawing --- // get the instantaneous energy (squared) at a time offset in the past float getEnergyAtTimeOffset(float secondsAgo) const noexcept; // input activity detection (energy over the last 50 ms) bool isActive() const noexcept; // -- reset -- void reset() noexcept; private: // -- compute -- void updateMetrics() noexcept; // -- parameter -- double sampleRate{ 48000.0 }; float analysisWindowMs{ 2000.0f }; // 50ms / 80ms sample count ( sample rate depends on ) int samples50ms{ 2400 }; // @ 48kHz int samples80ms{ 3840 }; // @ 48kHz int analysisWindowSamples{ 96000 }; // 2000ms @ 48kHz // -- buffer -- // energy history ( squared value ) std::vector energyHistory; int historyWritePos{ 0 }; // -- cumulative energy value -- // 50ms cumulative energy ( time ) double recent50msEnergy{ 0.0 }; // 80ms cumulative energy ( time ) double recent80msEnergy{ 0.0 }; // entire cumulative energy double totalEnergy{ 0.0 }; // EDT : energy decay tracking float energyPeak{ 0.0f }; int energyPeakPos{ 0 }; // -- output value (atomic for thread safety) -- std::atomic d50{ 0.0f }; std::atomic c50{ 0.0f }; std::atomic c80{ 0.0f }; std::atomic edt{ 0.0f }; // -- update -- // sample compute , // sample interval update int updateCounter{ 0 }; static constexpr int kUpdateInterval = 1024; // about 21ms @ 48kHz }; } // namespace FDNReverb