File size: 4,497 Bytes
e3e7558
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
//! Marine algorithm configuration
//!
//! Tunable parameters for jitter detection. These have been calibrated
//! for speech/audio processing but can be adjusted for specific use cases.

#![cfg_attr(not(feature = "std"), no_std)]

/// Configuration for Marine salience detection
///
/// These parameters control sensitivity and behavior of the jitter detector.
/// The defaults are tuned for speech processing at common sample rates.
#[derive(Debug, Clone, Copy)]
#[cfg_attr(feature = "std", derive(serde::Serialize, serde::Deserialize))]
pub struct MarineConfig {
    /// Minimum amplitude to consider a sample (gating threshold)
    /// Samples below this are ignored as noise
    /// Default: 1e-3 (~-60dB)
    pub clip_threshold: f32,

    /// EMA smoothing factor for period tracking (0..1)
    /// Lower = smoother, slower adaptation
    /// Default: 0.01
    pub ema_period_alpha: f32,

    /// EMA smoothing factor for amplitude tracking (0..1)
    /// Default: 0.01
    pub ema_amp_alpha: f32,

    /// Minimum inter-peak period in samples
    /// Rejects peaks closer than this (filters high-frequency noise)
    /// Default: sample_rate / 4000 (~4kHz upper F0)
    pub min_period: u32,

    /// Maximum inter-peak period in samples
    /// Rejects peaks farther than this (filters very low frequencies)
    /// Default: sample_rate / 60 (~60Hz lower F0)
    pub max_period: u32,

    /// Threshold below which jitter is considered "low" (stable)
    /// Default: 0.02
    pub jitter_low: f32,

    /// Threshold above which jitter is considered "high" (unstable)
    /// Default: 0.60
    pub jitter_high: f32,
}

impl MarineConfig {
    /// Create config optimized for speech at given sample rate
    ///
    /// # Arguments
    /// * `sample_rate` - Audio sample rate in Hz (e.g., 22050, 44100)
    ///
    /// # Example
    /// ```
    /// use marine_salience::MarineConfig;
    /// let config = MarineConfig::speech_default(22050);
    /// assert!(config.min_period < config.max_period);
    /// ```
    pub const fn speech_default(sample_rate: u32) -> Self {
        // F0 range: ~60Hz (low male) to ~4kHz (includes harmonics)
        let min_period = sample_rate / 4000; // Upper bound
        let max_period = sample_rate / 60;   // Lower bound

        Self {
            clip_threshold: 1e-3,
            ema_period_alpha: 0.01,
            ema_amp_alpha: 0.01,
            min_period,
            max_period,
            jitter_low: 0.02,
            jitter_high: 0.60,
        }
    }

    /// Create config for high-sensitivity detection
    /// More peaks detected, faster adaptation
    pub const fn high_sensitivity(sample_rate: u32) -> Self {
        let min_period = sample_rate / 8000;
        let max_period = sample_rate / 40;

        Self {
            clip_threshold: 5e-4,
            ema_period_alpha: 0.05,
            ema_amp_alpha: 0.05,
            min_period,
            max_period,
            jitter_low: 0.01,
            jitter_high: 0.50,
        }
    }

    /// Create config for TTS output validation
    /// Tuned to detect synthetic artifacts
    pub const fn tts_validation(sample_rate: u32) -> Self {
        let min_period = sample_rate / 4000;
        let max_period = sample_rate / 80;

        Self {
            clip_threshold: 1e-3,
            ema_period_alpha: 0.02,
            ema_amp_alpha: 0.02,
            min_period,
            max_period,
            jitter_low: 0.015, // Stricter for synthetic speech
            jitter_high: 0.40, // More sensitive to artifacts
        }
    }
}

impl Default for MarineConfig {
    fn default() -> Self {
        // Default to 22050 Hz (common TTS sample rate)
        Self::speech_default(22050)
    }
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn test_speech_default_periods() {
        let config = MarineConfig::speech_default(22050);
        assert!(config.min_period < config.max_period);
        assert_eq!(config.min_period, 22050 / 4000); // 5 samples
        assert_eq!(config.max_period, 22050 / 60);   // 367 samples
    }

    #[test]
    fn test_different_sample_rates() {
        let config_22k = MarineConfig::speech_default(22050);
        let config_44k = MarineConfig::speech_default(44100);
        let config_48k = MarineConfig::speech_default(48000);

        // Higher sample rates = more samples per period
        assert!(config_44k.max_period > config_22k.max_period);
        assert!(config_48k.max_period > config_44k.max_period);
    }
}