use std::sync::atomic::{AtomicBool, AtomicU8, Ordering};
use atomic_float::AtomicF64;
use crate::config::NormalizationMode;
use crate::processor::dsp::db_to_linear;
use super::info::LoudnessInfo;
pub struct AtomicLoudnessState {
pub target_gain_db: AtomicF64,
pub current_gain_db: AtomicF64,
pub smoothing_coeff: AtomicF64,
pub album_gain_db: AtomicF64,
pub preamp_gain_db: AtomicF64,
pub enabled: AtomicBool,
pub mode: AtomicU8,
}
impl AtomicLoudnessState {
pub fn new(smoothing_time_ms: f64, sample_rate: u32) -> Self {
let smoothing_coeff = {
let smoothing_samples = (smoothing_time_ms / 1000.0) * sample_rate as f64;
(-1.0 / smoothing_samples).exp()
};
Self {
target_gain_db: AtomicF64::new(0.0),
current_gain_db: AtomicF64::new(0.0),
smoothing_coeff: AtomicF64::new(smoothing_coeff),
album_gain_db: AtomicF64::new(0.0),
preamp_gain_db: AtomicF64::new(-1.0), enabled: AtomicBool::new(true),
mode: AtomicU8::new(0),
}
}
pub fn set_target_gain(&self, gain_db: f64) {
let value = if gain_db.is_finite() { gain_db } else { 0.0 };
self.target_gain_db.store(value, Ordering::Relaxed);
}
pub fn set_album_gain(&self, gain_db: f64) {
self.album_gain_db.store(gain_db, Ordering::Relaxed);
}
pub fn set_preamp_gain(&self, gain_db: f64) {
self.preamp_gain_db.store(gain_db, Ordering::Relaxed);
}
pub fn set_enabled(&self, enabled: bool) {
self.enabled.store(enabled, Ordering::Relaxed);
}
pub fn set_mode(&self, mode: u8) {
self.mode.store(mode, Ordering::Relaxed);
}
pub fn get_mode(&self) -> NormalizationMode {
match self.mode.load(Ordering::Relaxed) {
0 => NormalizationMode::Track,
1 => NormalizationMode::Album,
2 => NormalizationMode::Streaming,
3 => NormalizationMode::ReplayGainTrack,
4 => NormalizationMode::ReplayGainAlbum,
_ => NormalizationMode::Track,
}
}
pub fn set_smoothing(&self, smoothing_time_ms: f64, sample_rate: u32) {
let smoothing_samples = (smoothing_time_ms / 1000.0) * sample_rate as f64;
let coeff = (-1.0 / smoothing_samples).exp();
self.smoothing_coeff.store(coeff, Ordering::Relaxed);
}
#[inline]
pub fn process_gain(&self, frames: usize) -> f64 {
if !self.enabled.load(Ordering::Relaxed) {
return 1.0;
}
let mode = self.mode.load(Ordering::Relaxed);
let target = self.target_gain_db.load(Ordering::Relaxed);
let current = self.current_gain_db.load(Ordering::Relaxed);
let coeff = self.smoothing_coeff.load(Ordering::Relaxed);
let preamp = self.preamp_gain_db.load(Ordering::Relaxed);
let effective_target = match mode {
1 => self.album_gain_db.load(Ordering::Relaxed), _ => target, };
let effective_target = effective_target + preamp;
let remaining_factor = coeff.powi(frames as i32);
let new_gain = current + (effective_target - current) * (1.0 - remaining_factor);
self.current_gain_db.store(new_gain, Ordering::Relaxed);
db_to_linear(new_gain)
}
pub fn get_info(&self) -> LoudnessInfo {
LoudnessInfo {
integrated_lufs: -70.0,
short_term_lufs: -70.0,
momentary_lufs: -70.0,
loudness_range: 0.0,
true_peak_dbtp: -70.0,
current_gain_db: self.current_gain_db.load(Ordering::Relaxed),
target_gain_db: self.target_gain_db.load(Ordering::Relaxed),
preamp_db: self.preamp_gain_db.load(Ordering::Relaxed),
}
}
}
impl Default for AtomicLoudnessState {
fn default() -> Self {
Self::new(200.0, 44100)
}
}