mirror of
https://codeberg.org/armin/ambivalence.git
synced 2026-09-01 04:10:48 +02:00
104 lines
No EOL
3.6 KiB
C++
104 lines
No EOL
3.6 KiB
C++
#pragma once
|
|
#include "DSPConstants.h"
|
|
#include <array>
|
|
#include <atomic>
|
|
#include <vector> // <- 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<float> 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<float> d50{ 0.0f };
|
|
std::atomic<float> c50{ 0.0f };
|
|
std::atomic<float> c80{ 0.0f };
|
|
std::atomic<float> edt{ 0.0f };
|
|
|
|
// -- update --
|
|
// sample compute ,
|
|
// sample interval update
|
|
int updateCounter{ 0 };
|
|
static constexpr int kUpdateInterval = 1024; // about 21ms @ 48kHz
|
|
};
|
|
|
|
} // namespace FDNReverb
|