Add RTA hold and fall mode

This commit is contained in:
Mikei386
2026-07-22 09:09:04 +02:00
parent 3a0bdbb5ad
commit fbcafd43f9
10 changed files with 83 additions and 20 deletions
+28 -2
View File
@@ -207,6 +207,7 @@ impl RtaPeakHold {
sample_rate: u32,
config: &PhoenixRtaConfig,
) {
let previous_clock = self.sample_clock;
self.sample_clock = self.sample_clock.saturating_add(advanced_samples as u64);
let mode = normalize_rta_peak_hold_mode(&config.rta_peak_hold_mode);
let hold_samples = (f64::from(sample_rate.max(1))
@@ -242,7 +243,7 @@ impl RtaPeakHold {
};
if incoming >= self.values[index] {
self.values[index] = incoming;
if mode == "auto" {
if mode == "auto" || mode == "fall" {
self.expires_at_sample[index] = self.sample_clock.saturating_add(hold_samples);
}
} else if mode == "auto" && self.sample_clock >= self.expires_at_sample[index] {
@@ -250,6 +251,14 @@ impl RtaPeakHold {
// value. There is deliberately no second, continuous peak decay.
self.values[index] = incoming;
self.expires_at_sample[index] = self.sample_clock.saturating_add(hold_samples);
} else if mode == "fall" && self.sample_clock >= self.expires_at_sample[index] {
let decay_start = previous_clock.max(self.expires_at_sample[index]);
let decay_samples = self.sample_clock.saturating_sub(decay_start);
let decay_db = config.rta_peak_decay_db_per_second * decay_samples as f32
/ sample_rate.max(1) as f32;
self.values[index] = (self.values[index] - decay_db)
.max(incoming)
.max(RTA_PEAK_FLOOR_DB);
}
}
}
@@ -1944,8 +1953,10 @@ fn normalize_rta_integration(value: &str) -> &'static str {
fn normalize_rta_peak_hold_mode(value: &str) -> &'static str {
match value.trim().to_ascii_lowercase().as_str() {
"off" => "off",
"fall" => "fall",
"manual" => "manual",
_ => "auto",
"auto" => "auto",
_ => "fall",
}
}
@@ -2317,6 +2328,7 @@ mod tests {
#[test]
fn rta_peak_hold_has_timed_manual_off_and_reset_semantics() {
let mut config = PhoenixRtaConfig::default();
config.rta_peak_hold_mode = "auto".to_string();
config.rta_peak_hold_seconds = 0.1;
let mut hold = RtaPeakHold::new(1);
@@ -2329,6 +2341,20 @@ mod tests {
"timed hold must release directly to current"
);
config.rta_peak_hold_mode = "fall".to_string();
config.rta_peak_decay_db_per_second = 20.0;
hold.update(&[-10.0], 1, 1_000, &config);
hold.update(&[-40.0], 100, 1_000, &config);
assert_eq!(
hold.values[0], -10.0,
"fall mode must first observe the hold time"
);
hold.update(&[-40.0], 500, 1_000, &config);
assert!(
(hold.values[0] - -20.0).abs() < 1.0e-6,
"fall must use the configured dB/s rate"
);
config.rta_peak_hold_mode = "manual".to_string();
hold.update(&[-20.0], 1, 1_000, &config);
hold.update(&[-5.0], 1, 1_000, &config);
+3 -1
View File
@@ -70,6 +70,7 @@ pub struct PhoenixRtaConfig {
pub layout: String,
pub rta_peak_hold_mode: String,
pub rta_peak_hold_seconds: f32,
pub rta_peak_decay_db_per_second: f32,
pub rta_peak_reset_token: u64,
pub input_offset_db_l: f32,
pub input_offset_db_r: f32,
@@ -103,8 +104,9 @@ impl Default for PhoenixRtaConfig {
tau_slow: 1.0,
integration: "fast".to_string(),
layout: "rtw".to_string(),
rta_peak_hold_mode: "auto".to_string(),
rta_peak_hold_mode: "fall".to_string(),
rta_peak_hold_seconds: 2.5,
rta_peak_decay_db_per_second: 20.0,
rta_peak_reset_token: 0,
input_offset_db_l: -5.0,
input_offset_db_r: -5.0,
+12 -3
View File
@@ -414,8 +414,10 @@ fn normalize_rta_config(mut config: PhoenixRtaConfig) -> PhoenixRtaConfig {
.as_str()
{
"off" => "off".to_string(),
"fall" => "fall".to_string(),
"manual" => "manual".to_string(),
_ => "auto".to_string(),
"auto" => "auto".to_string(),
_ => "fall".to_string(),
};
let requested_hold = if config.rta_peak_hold_seconds.is_finite() {
config.rta_peak_hold_seconds
@@ -431,6 +433,11 @@ fn normalize_rta_config(mut config: PhoenixRtaConfig) -> PhoenixRtaConfig {
} else {
requested_hold.clamp(0.0, 30.0)
};
config.rta_peak_decay_db_per_second = if config.rta_peak_decay_db_per_second.is_finite() {
config.rta_peak_decay_db_per_second.clamp(1.0, 60.0)
} else {
20.0
};
let offset_l = if config.input_offset_db_l.is_finite() {
config.input_offset_db_l
} else {
@@ -792,16 +799,18 @@ mod tests {
}
#[test]
fn rta_peak_hold_config_is_normalized_without_decay_parameter() {
fn rta_peak_hold_config_is_normalized() {
let rtw = normalize_rta_config(PhoenixRtaConfig {
rta_peak_hold_mode: "MANUAL".to_string(),
rta_peak_hold_seconds: 3.7,
rta_peak_decay_db_per_second: 25.0,
rta_peak_reset_token: 12,
layout: "rtw".to_string(),
..PhoenixRtaConfig::default()
});
assert_eq!(rtw.rta_peak_hold_mode, "manual");
assert_eq!(rtw.rta_peak_hold_seconds, 4.0);
assert_eq!(rtw.rta_peak_decay_db_per_second, 25.0);
assert_eq!(rtw.rta_peak_reset_token, 12);
let extension = normalize_rta_config(PhoenixRtaConfig {
@@ -810,7 +819,7 @@ mod tests {
layout: "iec".to_string(),
..PhoenixRtaConfig::default()
});
assert_eq!(extension.rta_peak_hold_mode, "auto");
assert_eq!(extension.rta_peak_hold_mode, "fall");
assert_eq!(extension.rta_peak_hold_seconds, 30.0);
}