#[derive(Debug, Clone, Copy, PartialEq, Default)]
#[non_exhaustive]
pub enum GaplessMode {
Disabled,
#[default]
MediaOnly,
CodecPriming,
SilenceTrim(SilenceTrimParams),
}
#[derive(Debug, Clone, Copy, PartialEq)]
pub struct SilenceTrimParams {
pub trim_trailing: bool,
pub threshold_db: f32,
pub min_trim_frames: u64,
pub scan_window_frames: u64,
}
impl Default for SilenceTrimParams {
fn default() -> Self {
Self {
threshold_db: 45.0,
min_trim_frames: 256,
scan_window_frames: 4096,
trim_trailing: false,
}
}
}
impl SilenceTrimParams {
#[must_use]
pub fn threshold_amplitude(&self) -> f32 {
if !self.threshold_db.is_finite() || self.threshold_db <= 0.0 {
return 1.0;
}
10f32.powf(-self.threshold_db / 20.0)
}
}
#[cfg(test)]
mod tests {
use kithara_test_utils::kithara;
use super::*;
fn approx(a: f32, b: f32, eps: f32) -> bool {
(a - b).abs() < eps
}
#[kithara::test]
#[case::db_40(40.0, 1.0e-2, 1e-8)]
#[case::db_60(60.0, 1.0e-3, 1e-9)]
#[case::db_80(80.0, 1.0e-4, 1e-10)]
fn threshold_db_maps_to_amplitude(
#[case] threshold_db: f32,
#[case] expected_amplitude: f32,
#[case] eps: f32,
) {
let params = SilenceTrimParams {
threshold_db,
..Default::default()
};
assert!(approx(
params.threshold_amplitude(),
expected_amplitude,
eps
));
}
#[kithara::test]
fn non_positive_db_disables_trim() {
for db in [-1.0, 0.0, f32::NAN] {
let params = SilenceTrimParams {
threshold_db: db,
..Default::default()
};
assert_eq!(
params.threshold_amplitude(),
1.0,
"db={db} must yield amplitude=1.0 (no trim)"
);
}
}
#[kithara::test]
fn defaults_match_documented_values() {
let p = SilenceTrimParams::default();
assert_eq!(p.threshold_db, 45.0);
assert_eq!(p.min_trim_frames, 256);
assert_eq!(p.scan_window_frames, 4096);
assert!(!p.trim_trailing);
}
}