Function bodies 463 total
does_not_bridge_breathing_pause function · rust · L117-L139 (23 LOC)core/src/dsp/mpt.rs
fn does_not_bridge_breathing_pause() {
// A 600ms gap (> 500ms) indicates the patient stopped to breathe.
// This should NOT be bridged — it's two separate phonation attempts.
let mut contour: Vec<PitchFrame> = Vec::new();
// 5s voiced
for i in 0..500 {
contour.push(frame(i as f32 * 0.01, Some(100.0)));
}
// 600ms gap (60 frames) — breathing pause
for i in 500..560 {
contour.push(frame(i as f32 * 0.01, None));
}
// 3s voiced
for i in 560..860 {
contour.push(frame(i as f32 * 0.01, Some(100.0)));
}
let mpt = max_phonation_time_secs(&contour, 10.0, 250.0);
assert!(
(mpt - 5.0).abs() < 0.1,
"Breathing pause (>500ms) should not be bridged, expected ~5.0s, got {mpt:.3}s"
);
}does_not_bridge_300ms_gap function · rust · L142-L161 (20 LOC)core/src/dsp/mpt.rs
fn does_not_bridge_300ms_gap() {
// 300ms > 250ms threshold — should NOT bridge
let mut contour: Vec<PitchFrame> = Vec::new();
for i in 0..500 {
contour.push(frame(i as f32 * 0.01, Some(100.0)));
}
// 300ms gap (30 frames)
for i in 500..530 {
contour.push(frame(i as f32 * 0.01, None));
}
for i in 530..830 {
contour.push(frame(i as f32 * 0.01, Some(100.0)));
}
let mpt = max_phonation_time_secs(&contour, 10.0, 250.0);
assert!(
(mpt - 5.0).abs() < 0.1,
"300ms gap should NOT be bridged at 250ms threshold, expected ~5.0s, got {mpt:.3}s"
);
}bridges_200ms_gap function · rust · L164-L183 (20 LOC)core/src/dsp/mpt.rs
fn bridges_200ms_gap() {
// 200ms < 250ms threshold — should bridge
let mut contour: Vec<PitchFrame> = Vec::new();
for i in 0..500 {
contour.push(frame(i as f32 * 0.01, Some(100.0)));
}
// 200ms gap (20 frames)
for i in 500..520 {
contour.push(frame(i as f32 * 0.01, None));
}
for i in 520..820 {
contour.push(frame(i as f32 * 0.01, Some(100.0)));
}
let mpt = max_phonation_time_secs(&contour, 10.0, 250.0);
assert!(
mpt > 8.0,
"200ms gap should be bridged at 250ms threshold, got {mpt:.3}s"
);
}all_unvoiced function · rust · L186-L189 (4 LOC)core/src/dsp/mpt.rs
fn all_unvoiced() {
let contour = vec![frame(0.0, None), frame(0.01, None)];
assert_eq!(max_phonation_time_secs(&contour, 10.0, 250.0), 0.0);
}empty_contour function · rust · L192-L194 (3 LOC)core/src/dsp/mpt.rs
fn empty_contour() {
assert_eq!(max_phonation_time_secs(&[], 10.0, 250.0), 0.0);
}compute_periodicity function · rust · L18-L66 (49 LOC)core/src/dsp/periodicity.rs
pub fn compute_periodicity(
samples: &[f32],
sample_rate: u32,
contour: &[PitchFrame],
active_frames: &[bool],
hop_size_ms: f32,
) -> Option<f32> {
let sr = sample_rate as f32;
let hop_samples = (hop_size_ms / 1000.0 * sr) as usize;
let mut values: Vec<f32> = Vec::new();
for (i, frame) in contour.iter().enumerate() {
let Some(f0) = frame.frequency else {
continue;
};
// Only use frames that are also active (have energy)
if i < active_frames.len() && !active_frames[i] {
continue;
}
let period_samples = (sr / f0).round() as usize;
if period_samples == 0 {
continue;
}
// Need at least 2 periods of audio for autocorrelation
let center = i * hop_samples;
let window_size = period_samples * 3;
let start = center.saturating_sub(window_size / 2);
let end = (start + window_size).min(samples.len());
if end - sframe function · rust · L72-L78 (7 LOC)core/src/dsp/periodicity.rs
fn frame(time: f32, freq: Option<f32>) -> PitchFrame {
PitchFrame {
time,
frequency: freq,
}
}Repobility · MCP-ready · https://repobility.com
sine_wave function · rust · L79-L85 (7 LOC)core/src/dsp/periodicity.rs
fn sine_wave(freq: f32, sample_rate: u32, duration: f32) -> Vec<f32> {
let n = (sample_rate as f32 * duration) as usize;
(0..n)
.map(|i| (2.0 * PI * freq * i as f32 / sample_rate as f32).sin())
.collect()
}sine_high_periodicity function · rust · L88-L103 (16 LOC)core/src/dsp/periodicity.rs
fn sine_high_periodicity() {
let sr = 44100;
let samples = sine_wave(100.0, sr, 1.0);
let hop_ms = 10.0;
let num_frames = 80;
let contour: Vec<_> = (0..num_frames)
.map(|i| frame(i as f32 * hop_ms / 1000.0, Some(100.0)))
.collect();
let active = vec![true; num_frames];
let p = compute_periodicity(&samples, sr, &contour, &active, hop_ms).unwrap();
assert!(
p > 0.95,
"Pure sine should have periodicity near 1.0, got {p:.3}"
);
}noise_low_periodicity function · rust · L106-L129 (24 LOC)core/src/dsp/periodicity.rs
fn noise_low_periodicity() {
let sr = 44100;
let n = (sr as f32 * 1.0) as usize;
let mut rng_state: u32 = 42;
let samples: Vec<f32> = (0..n)
.map(|_| {
rng_state = rng_state.wrapping_mul(1103515245).wrapping_add(12345);
(rng_state as f32 / u32::MAX as f32) * 2.0 - 1.0
})
.collect();
let hop_ms = 10.0;
let num_frames = 80;
let contour: Vec<_> = (0..num_frames)
.map(|i| frame(i as f32 * hop_ms / 1000.0, Some(100.0)))
.collect();
let active = vec![true; num_frames];
let p = compute_periodicity(&samples, sr, &contour, &active, hop_ms).unwrap();
assert!(
p < 0.3,
"Pure noise should have low periodicity, got {p:.3}"
);
}no_voiced_frames function · rust · L132-L136 (5 LOC)core/src/dsp/periodicity.rs
fn no_voiced_frames() {
let contour = vec![frame(0.0, None), frame(0.01, None)];
let active = vec![true; 2];
assert!(compute_periodicity(&[0.0; 44100], 44100, &contour, &active, 10.0).is_none());
}inactive_frames_skipped function · rust · L139-L151 (13 LOC)core/src/dsp/periodicity.rs
fn inactive_frames_skipped() {
let sr = 44100;
let samples = sine_wave(100.0, sr, 1.0);
let hop_ms = 10.0;
let num_frames = 80;
let contour: Vec<_> = (0..num_frames)
.map(|i| frame(i as f32 * hop_ms / 1000.0, Some(100.0)))
.collect();
// All frames inactive
let active = vec![false; num_frames];
assert!(compute_periodicity(&samples, sr, &contour, &active, hop_ms).is_none());
}default function · rust · L60-L74 (15 LOC)core/src/dsp/pitch.rs
fn default() -> Self {
Self {
pitch_floor_hz: 30.0,
// 1000 Hz covers falsetto, head voice, and the elevated pitch
// common in vocal cord paralysis recovery.
pitch_ceiling_hz: 1000.0,
frame_size_ms: 30.0,
hop_size_ms: 10.0,
// Low power threshold to handle quiet/breathy voices typical
// in vocal cord paralysis — these signals are often 20-30 dB
// below normal speech levels.
power_threshold: 0.2,
clarity_threshold: 0.2,
}
}energy_based_contour function · rust · L100-L142 (43 LOC)core/src/dsp/pitch.rs
pub fn energy_based_contour(
samples: &[f32],
sample_rate: u32,
config: &PitchConfig,
estimated_f0: f32,
rms_threshold_db: f32,
) -> Vec<PitchFrame> {
let sr = sample_rate as f32;
let frame_size = (config.frame_size_ms / 1000.0 * sr) as usize;
let hop_size = (config.hop_size_ms / 1000.0 * sr) as usize;
// Match the same stepping as extract_pitch_contour so frame indices
// map to the same audio positions for shimmer/HNR.
let min_buffer = (2.0 * sr / config.pitch_floor_hz).ceil() as usize;
let detector_size = min_buffer.next_power_of_two().max(frame_size);
let mut contour = Vec::new();
let mut pos = 0;
while pos + detector_size <= samples.len() {
let time = pos as f32 / sr;
let end = (pos + frame_size).min(samples.len());
let frame = &samples[pos..end];
let rms = frame_rms(frame);
let rms_db = if rms > 0.0 {
20.0 * rms.log10()
} else {
f32::NEG_INFINITYframe_rms function · rust · L145-L151 (7 LOC)core/src/dsp/pitch.rs
fn frame_rms(samples: &[f32]) -> f32 {
if samples.is_empty() {
return 0.0;
}
let sum_sq: f32 = samples.iter().map(|&s| s * s).sum();
(sum_sq / samples.len() as f32).sqrt()
}About: code-quality intelligence by Repobility · https://repobility.com
extract_contour_with_fallback function · rust · L160-L243 (84 LOC)core/src/dsp/pitch.rs
pub fn extract_contour_with_fallback(
samples: &[f32],
sample_rate: u32,
config: &PitchConfig,
) -> ContourResult {
// Tier 1: Standard pitch detection
let tier1_contour = extract_pitch_contour(samples, sample_rate, config);
let voiced_frac = voiced_fraction(&tier1_contour);
// Tier 2: Relaxed thresholds (always computed for tier tracking)
let relaxed = PitchConfig {
pitch_floor_hz: config.pitch_floor_hz,
pitch_ceiling_hz: config.pitch_ceiling_hz,
frame_size_ms: config.frame_size_ms,
hop_size_ms: config.hop_size_ms,
power_threshold: 0.01,
clarity_threshold: 0.05,
};
let tier2_contour = extract_pitch_contour(samples, sample_rate, &relaxed);
let relaxed_frac = voiced_fraction(&tier2_contour);
// Build per-frame tier tracking
let n_frames = tier1_contour.len();
let mut frame_tiers = vec![3u8; n_frames]; // default to tier 3
let mut tier_counts = [0usize; 3];
for i in 0..n_fraextract_pitch_contour function · rust · L254-L313 (60 LOC)core/src/dsp/pitch.rs
pub fn extract_pitch_contour(
samples: &[f32],
sample_rate: u32,
config: &PitchConfig,
) -> Vec<PitchFrame> {
let sr = sample_rate as f32;
// Convert milliseconds to samples.
// e.g., 30ms at 44100 Hz = 1323 samples
let frame_size = (config.frame_size_ms / 1000.0 * sr) as usize;
let hop_size = (config.hop_size_ms / 1000.0 * sr) as usize;
// The McLeod detector needs a buffer large enough to capture at least
// 2 full cycles of the lowest frequency we want to detect.
// At 30 Hz and 44100 Hz sample rate: period = 44100/30 = 1470 samples.
// We need 2x that = 2940. Round up to next power of 2 for FFT efficiency.
let min_buffer = (2.0 * sr / config.pitch_floor_hz).ceil() as usize;
let detector_size = min_buffer.next_power_of_two().max(frame_size);
// Padding helps with edge effects in the autocorrelation.
// Half the detector size is standard.
let padding = detector_size / 2;
let mut contour = Vec::new();
let detect_pitch_frame function · rust · L320-L337 (18 LOC)core/src/dsp/pitch.rs
pub fn detect_pitch_frame(samples: &[f32], sample_rate: u32) -> Option<f32> {
let size = samples.len();
let padding = size / 2;
let windowed = windowing::hanning(samples);
let padded: Vec<f64> = windowed.iter().map(|&s| s as f64).collect();
let mut detector = McLeodDetector::new(size, padding);
let pitch = detector.get_pitch(
&padded,
sample_rate as usize,
0.2, // power threshold
0.2, // clarity threshold
);
pitch
.map(|p| p.frequency as f32)
.filter(|&f| f >= 50.0 && f <= 1000.0)
}freq_to_note function · rust · L343-L359 (17 LOC)core/src/dsp/pitch.rs
pub fn freq_to_note(freq_hz: f32) -> (&'static str, i32, f32) {
const NOTE_NAMES: [&str; 12] = [
"C", "C#", "D", "D#", "E", "F", "F#", "G", "G#", "A", "A#", "B",
];
// Semitones from A4 (440 Hz)
let semitones_from_a4 = 12.0 * (freq_hz / 440.0).log2();
let nearest_semitone = semitones_from_a4.round() as i32;
let cents = (semitones_from_a4 - nearest_semitone as f32) * 100.0;
// A4 is MIDI note 69 → note index 9 in octave 4
let midi = 69 + nearest_semitone;
let note_index = ((midi % 12) + 12) % 12; // handle negatives
let octave = (midi / 12) - 1;
(NOTE_NAMES[note_index as usize], octave, cents)
}voiced_frequencies function · rust · L364-L369 (6 LOC)core/src/dsp/pitch.rs
pub fn voiced_frequencies(contour: &[PitchFrame]) -> Vec<f32> {
contour
.iter()
.filter_map(|frame| frame.frequency)
.collect()
}voiced_fraction function · rust · L373-L379 (7 LOC)core/src/dsp/pitch.rs
pub fn voiced_fraction(contour: &[PitchFrame]) -> f32 {
if contour.is_empty() {
return 0.0;
}
let voiced = contour.iter().filter(|f| f.frequency.is_some()).count();
voiced as f32 / contour.len() as f32
}sine_wave function · rust · L388-L396 (9 LOC)core/src/dsp/pitch.rs
fn sine_wave(freq_hz: f32, sample_rate: u32, duration_secs: f32) -> Vec<f32> {
let num_samples = (sample_rate as f32 * duration_secs) as usize;
(0..num_samples)
.map(|i| {
let t = i as f32 / sample_rate as f32;
(2.0 * PI * freq_hz * t).sin()
})
.collect()
}detects_100hz_sine function · rust · L399-L415 (17 LOC)core/src/dsp/pitch.rs
fn detects_100hz_sine() {
let samples = sine_wave(100.0, 44100, 0.5);
let config = PitchConfig::default();
let contour = extract_pitch_contour(&samples, 44100, &config);
let frequencies = voiced_frequencies(&contour);
assert!(
!frequencies.is_empty(),
"Should detect pitch in a pure sine wave"
);
let mean: f32 = frequencies.iter().sum::<f32>() / frequencies.len() as f32;
assert!(
(mean - 100.0).abs() < 5.0,
"Mean pitch should be ~100 Hz, got {mean:.1} Hz"
);
}Repobility · severity-and-effort ranking · https://repobility.com
detects_low_pitch_50hz function · rust · L418-L435 (18 LOC)core/src/dsp/pitch.rs
fn detects_low_pitch_50hz() {
// This tests our 30 Hz floor — most detectors would miss this
let samples = sine_wave(50.0, 44100, 1.0);
let config = PitchConfig::default();
let contour = extract_pitch_contour(&samples, 44100, &config);
let frequencies = voiced_frequencies(&contour);
assert!(
!frequencies.is_empty(),
"Should detect 50 Hz with our low floor"
);
let mean: f32 = frequencies.iter().sum::<f32>() / frequencies.len() as f32;
assert!(
(mean - 50.0).abs() < 5.0,
"Mean pitch should be ~50 Hz, got {mean:.1} Hz"
);
}silence_is_unvoiced function · rust · L438-L448 (11 LOC)core/src/dsp/pitch.rs
fn silence_is_unvoiced() {
let samples = vec![0.0; 44100]; // 1 second of silence
let config = PitchConfig::default();
let contour = extract_pitch_contour(&samples, 44100, &config);
let vf = voiced_fraction(&contour);
assert!(
vf < 0.1,
"Silence should be mostly unvoiced, got {vf:.2}"
);
}voiced_fraction_empty function · rust · L451-L453 (3 LOC)core/src/dsp/pitch.rs
fn voiced_fraction_empty() {
assert_eq!(voiced_fraction(&[]), 0.0);
}contour_timestamps_increase function · rust · L456-L467 (12 LOC)core/src/dsp/pitch.rs
fn contour_timestamps_increase() {
let samples = sine_wave(100.0, 44100, 0.5);
let config = PitchConfig::default();
let contour = extract_pitch_contour(&samples, 44100, &config);
for pair in contour.windows(2) {
assert!(
pair[1].time > pair[0].time,
"Timestamps should be strictly increasing"
);
}
}energy_contour_detects_signal function · rust · L470-L480 (11 LOC)core/src/dsp/pitch.rs
fn energy_contour_detects_signal() {
let samples = sine_wave(100.0, 44100, 0.5);
let config = PitchConfig::default();
let contour = energy_based_contour(&samples, 44100, &config, 100.0, -45.0);
let vf = voiced_fraction(&contour);
assert!(
vf > 0.8,
"Energy detector should find most of a sine wave, got {vf:.2}"
);
}energy_contour_rejects_silence function · rust · L483-L493 (11 LOC)core/src/dsp/pitch.rs
fn energy_contour_rejects_silence() {
let samples = vec![0.0; 44100];
let config = PitchConfig::default();
let contour = energy_based_contour(&samples, 44100, &config, 100.0, -45.0);
let vf = voiced_fraction(&contour);
assert!(
vf < 0.01,
"Energy detector should reject silence, got {vf:.2}"
);
}energy_contour_matches_pitch_contour_length function · rust · L496-L507 (12 LOC)core/src/dsp/pitch.rs
fn energy_contour_matches_pitch_contour_length() {
let samples = sine_wave(100.0, 44100, 1.0);
let config = PitchConfig::default();
let pitch_contour = extract_pitch_contour(&samples, 44100, &config);
let energy_contour = energy_based_contour(&samples, 44100, &config, 100.0, -45.0);
assert_eq!(
pitch_contour.len(),
energy_contour.len(),
"Energy and pitch contours should have the same number of frames"
);
}fallback_uses_tier1_for_clean_signal function · rust · L510-L522 (13 LOC)core/src/dsp/pitch.rs
fn fallback_uses_tier1_for_clean_signal() {
// A strong sine wave should be detected by tier 1 — no energy fallback.
let samples = sine_wave(100.0, 44100, 1.0);
let config = PitchConfig::default();
let result = extract_contour_with_fallback(&samples, 44100, &config);
assert!(
!result.used_energy_fallback,
"Clean sine wave should not trigger energy fallback"
);
let vf = voiced_fraction(&result.contour);
assert!(vf > 0.5, "Should detect most frames, got {vf:.2}");
}Open data scored by Repobility · https://repobility.com
fallback_uses_energy_for_silence function · rust · L525-L537 (13 LOC)core/src/dsp/pitch.rs
fn fallback_uses_energy_for_silence() {
// Near-silence should exhaust tiers 1+2 and land on energy fallback.
// With all-zero samples the energy detector also finds nothing,
// so used_energy_fallback=true but voiced fraction is ~0.
let samples = vec![0.0; 44100];
let config = PitchConfig::default();
let result = extract_contour_with_fallback(&samples, 44100, &config);
assert!(
result.used_energy_fallback,
"Silence should trigger energy fallback"
);
}detect_pitch_frame_sine function · rust · L540-L549 (10 LOC)core/src/dsp/pitch.rs
fn detect_pitch_frame_sine() {
let samples = sine_wave(220.0, 44100, 0.1);
let hz = detect_pitch_frame(&samples[..2048], 44100);
assert!(hz.is_some(), "Should detect pitch from a sine frame");
let freq = hz.unwrap();
assert!(
(freq - 220.0).abs() < 10.0,
"Expected ~220 Hz, got {freq:.1} Hz"
);
}detect_pitch_frame_silence function · rust · L552-L555 (4 LOC)core/src/dsp/pitch.rs
fn detect_pitch_frame_silence() {
let samples = vec![0.0; 2048];
assert!(detect_pitch_frame(&samples, 44100).is_none());
}freq_to_note_a4 function · rust · L558-L563 (6 LOC)core/src/dsp/pitch.rs
fn freq_to_note_a4() {
let (note, octave, cents) = freq_to_note(440.0);
assert_eq!(note, "A");
assert_eq!(octave, 4);
assert!(cents.abs() < 1.0, "440 Hz should be in tune, got {cents:.1} cents");
}freq_to_note_c4 function · rust · L566-L572 (7 LOC)core/src/dsp/pitch.rs
fn freq_to_note_c4() {
// C4 = 261.63 Hz
let (note, octave, cents) = freq_to_note(261.63);
assert_eq!(note, "C");
assert_eq!(octave, 4);
assert!(cents.abs() < 2.0);
}freq_to_note_sharp function · rust · L575-L581 (7 LOC)core/src/dsp/pitch.rs
fn freq_to_note_sharp() {
// Slightly above A4 — should be A4 with positive cents
let (note, octave, cents) = freq_to_note(445.0);
assert_eq!(note, "A");
assert_eq!(octave, 4);
assert!(cents > 0.0, "445 Hz should be sharp");
}freq_to_note_flat function · rust · L584-L590 (7 LOC)core/src/dsp/pitch.rs
fn freq_to_note_flat() {
// Slightly below A4 — should be A4 with negative cents
let (note, octave, cents) = freq_to_note(435.0);
assert_eq!(note, "A");
assert_eq!(octave, 4);
assert!(cents < 0.0, "435 Hz should be flat");
}local_shimmer_percent function · rust · L19-L81 (63 LOC)core/src/dsp/shimmer.rs
pub fn local_shimmer_percent(
samples: &[f32],
sample_rate: u32,
contour: &[PitchFrame],
hop_size_ms: f32,
) -> Option<f32> {
let sr = sample_rate as f32;
let hop_samples = (hop_size_ms / 1000.0 * sr) as usize;
// For each voiced frame, compute the peak amplitude over one pitch period.
// We pair up the amplitude with whether this frame is consecutive to the
// previous voiced frame (no unvoiced gap in between).
let mut amplitudes: Vec<f32> = Vec::new();
let mut perturbations: Vec<f32> = Vec::new();
let mut prev_amp: Option<f32> = None;
for (i, frame) in contour.iter().enumerate() {
match frame.frequency {
Some(f0) => {
// One pitch period in samples: at 100 Hz and 44100 Hz SR, that's 441 samples
let period_samples = (sr / f0).round() as usize;
// Position of this frame in the audio buffer
let start = i * hop_samples;
let end = (starRepobility · MCP-ready · https://repobility.com
local_shimmer_percent_gated function · rust · L93-L168 (76 LOC)core/src/dsp/shimmer.rs
pub fn local_shimmer_percent_gated(
samples: &[f32],
sample_rate: u32,
contour: &[PitchFrame],
frame_tiers: &[u8],
hop_size_ms: f32,
) -> Option<f32> {
if contour.len() != frame_tiers.len() {
return None;
}
let sr = sample_rate as f32;
let hop_samples = (hop_size_ms / 1000.0 * sr) as usize;
let mut amplitudes: Vec<f32> = Vec::new();
let mut perturbations: Vec<f32> = Vec::new();
let mut prev_amp: Option<f32> = None;
let mut consecutive = 0_usize;
let mut max_consecutive = 0_usize;
let mut total_gated_frames = 0_usize;
for (i, (frame, &tier)) in contour.iter().zip(frame_tiers.iter()).enumerate() {
match frame.frequency {
Some(f0) if tier <= 2 => {
let period_samples = (sr / f0).round() as usize;
let start = i * hop_samples;
let end = (start + period_samples).min(samples.len());
if start >= samples.len() || start >= end {
frame function · rust · L174-L180 (7 LOC)core/src/dsp/shimmer.rs
fn frame(time: f32, freq: Option<f32>) -> PitchFrame {
PitchFrame {
time,
frequency: freq,
}
}sine_wave function · rust · L183-L188 (6 LOC)core/src/dsp/shimmer.rs
fn sine_wave(freq: f32, sample_rate: u32, duration: f32) -> Vec<f32> {
let n = (sample_rate as f32 * duration) as usize;
(0..n)
.map(|i| (2.0 * PI * freq * i as f32 / sample_rate as f32).sin())
.collect()
}constant_amplitude_low_shimmer function · rust · L191-L207 (17 LOC)core/src/dsp/shimmer.rs
fn constant_amplitude_low_shimmer() {
let sr = 44100;
let samples = sine_wave(100.0, sr, 0.5);
// Build contour: all frames at 100 Hz, 10ms hop
let hop_ms = 10.0;
let num_frames = (500.0 / hop_ms) as usize;
let contour: Vec<_> = (0..num_frames)
.map(|i| frame(i as f32 * hop_ms / 1000.0, Some(100.0)))
.collect();
let shimmer = local_shimmer_percent(&samples, sr, &contour, hop_ms).unwrap();
assert!(
shimmer < 5.0,
"Constant-amplitude sine should have low shimmer, got {shimmer:.2}%"
);
}amplitude_modulated_high_shimmer function · rust · L210-L235 (26 LOC)core/src/dsp/shimmer.rs
fn amplitude_modulated_high_shimmer() {
let sr = 44100;
// Create a signal where amplitude alternates between 0.5 and 1.0
let n = (sr as f32 * 0.5) as usize;
let hop_ms = 10.0;
let hop_samples = (hop_ms / 1000.0 * sr as f32) as usize;
let samples: Vec<f32> = (0..n)
.map(|i| {
let frame_idx = i / hop_samples;
let amp = if frame_idx % 2 == 0 { 1.0 } else { 0.5 };
amp * (2.0 * PI * 100.0 * i as f32 / sr as f32).sin()
})
.collect();
let num_frames = n / hop_samples;
let contour: Vec<_> = (0..num_frames)
.map(|i| frame(i as f32 * hop_ms / 1000.0, Some(100.0)))
.collect();
let shimmer = local_shimmer_percent(&samples, sr, &contour, hop_ms).unwrap();
assert!(
shimmer > 20.0,
"Alternating amplitude should show high shimmer, got {shimmer:.2}%"
);
}insufficient_data function · rust · L238-L241 (4 LOC)core/src/dsp/shimmer.rs
fn insufficient_data() {
let contour = vec![frame(0.0, Some(100.0))];
assert!(local_shimmer_percent(&[0.0; 1000], 44100, &contour, 10.0).is_none());
}count_voice_breaks function · rust · L18-L47 (30 LOC)core/src/dsp/voice_breaks.rs
pub fn count_voice_breaks(contour: &[PitchFrame], hop_size_ms: f32, max_break_ms: f32) -> usize {
let runs = contour::voiced_runs(contour);
if runs.len() < 2 {
return 0;
}
let min_gap_ms = 50.0;
let max_gap_ms = max_break_ms;
let mut breaks = 0;
// Look at the gaps between consecutive voiced runs.
// Each gap is the space between one run ending and the next one starting.
for pair in runs.windows(2) {
let (_, end_of_prev) = pair[0];
let (start_of_next, _) = pair[1];
// Gap in frames: from the frame after the previous run ends
// to the frame before the next run starts
let gap_frames = start_of_next - end_of_prev - 1;
let gap_ms = gap_frames as f32 * hop_size_ms;
if gap_ms >= min_gap_ms && gap_ms <= max_gap_ms {
breaks += 1;
}
}
breaks
}frame function · rust · L52-L58 (7 LOC)core/src/dsp/voice_breaks.rs
fn frame(time: f32, freq: Option<f32>) -> PitchFrame {
PitchFrame {
time,
frequency: freq,
}
}About: code-quality intelligence by Repobility · https://repobility.com
no_breaks_continuous_voicing function · rust · L75-L78 (4 LOC)core/src/dsp/voice_breaks.rs
fn no_breaks_continuous_voicing() {
let contour = make_contour(&[(100, true)], 10.0);
assert_eq!(count_voice_breaks(&contour, 10.0, 250.0), 0);
}one_break_80ms_gap function · rust · L81-L85 (5 LOC)core/src/dsp/voice_breaks.rs
fn one_break_80ms_gap() {
// voiced (20 frames) → 80ms gap (8 frames) → voiced (20 frames)
let contour = make_contour(&[(20, true), (8, false), (20, true)], 10.0);
assert_eq!(count_voice_breaks(&contour, 10.0, 250.0), 1);
}short_gap_not_a_break function · rust · L88-L92 (5 LOC)core/src/dsp/voice_breaks.rs
fn short_gap_not_a_break() {
// 30ms gap (3 frames at 10ms) — normal consonant, not a break
let contour = make_contour(&[(20, true), (3, false), (20, true)], 10.0);
assert_eq!(count_voice_breaks(&contour, 10.0, 250.0), 0);
}