pub mod batch;
pub mod conv_stem;
pub mod decode;
#[path = "mel_filterbank_data_generated.rs"]
#[allow(clippy::all)]
pub mod mel_filterbank_data;
mod resampler;
mod ring_buffer;
mod streaming;
pub mod wav;
pub use aprender::audio::{MelConfig, MelFilterbank};
pub use batch::{split_into_chunks, AudioBatch, BatchMelResult, BatchPreprocessor};
pub use conv_stem::{ConvStem, GroupNorm, CONV_STEM_TOTAL_STRIDE};
pub use resampler::{Resampler, SincResampler};
pub use ring_buffer::RingBuffer;
pub use streaming::{
LatencyMode, ProcessorState, ProcessorStats, StreamingConfig, StreamingEvent,
StreamingProcessor, DEFAULT_CHUNK_DURATION, DEFAULT_CHUNK_OVERLAP, LOW_LATENCY_BUFFER_DURATION,
LOW_LATENCY_CHUNK_DURATION, LOW_LATENCY_CHUNK_OVERLAP, LOW_LATENCY_FRAME_SIZE_MS,
LOW_LATENCY_MIN_SPEECH_MS, LOW_LATENCY_PARTIAL_THRESHOLD, MIN_SPEECH_DURATION_MS,
};
pub use decode::{
decode_with_ffmpeg, is_supported_extension, load_audio_file, load_audio_samples,
AudioDecodeError, SUPPORTED_EXTENSIONS,
};
pub use crate::vad::{
SpeechSegment, StreamingVad, VadConfig, VadEvent, VadState, VoiceActivityDetector,
};
pub const SAMPLE_RATE: u32 = 16000;
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_default_mel_config() {
let config = MelConfig::default();
assert_eq!(config.sample_rate, 16000);
assert_eq!(config.n_mels, 80);
assert_eq!(config.n_fft, 400);
assert_eq!(config.hop_length, 160);
}
#[test]
fn test_vad_reexport_config() {
let config = VadConfig::new()
.with_energy_threshold(2.5)
.with_frame_size(320);
assert!((config.energy_threshold - 2.5).abs() < f32::EPSILON);
assert_eq!(config.frame_size, 320);
}
#[test]
fn test_vad_reexport_detector() {
let vad = VoiceActivityDetector::default();
assert_eq!(vad.state(), VadState::Silence);
}
#[test]
fn test_vad_reexport_streaming() {
let vad = StreamingVad::default();
assert!(!vad.is_in_speech());
}
#[test]
fn test_vad_with_resampled_audio() {
let mut vad = VoiceActivityDetector::new(VadConfig::default());
let silence = vec![0.0; 4800]; let segments = vad.detect(&silence);
assert!(segments.is_empty());
}
#[test]
fn test_vad_config_matches_audio_sample_rate() {
let config = VadConfig::default();
assert_eq!(config.sample_rate, SAMPLE_RATE);
}
#[test]
fn test_streaming_processor_vad_integration() {
let config = StreamingConfig {
input_sample_rate: 16000,
output_sample_rate: 16000,
enable_vad: true,
..Default::default()
};
let mut processor = StreamingProcessor::new(config);
let silence = vec![0.0; 4800];
processor.push_audio(&silence);
processor.process();
assert_eq!(processor.state(), ProcessorState::WaitingForSpeech);
}
#[test]
fn test_vad_event_types() {
let event = VadEvent::Continue;
assert_eq!(event, VadEvent::Continue);
let start = VadEvent::SpeechStart;
assert_eq!(start, VadEvent::SpeechStart);
let end = VadEvent::SpeechEnd;
assert_eq!(end, VadEvent::SpeechEnd);
}
#[test]
fn test_speech_segment_from_vad() {
let segment = SpeechSegment {
start: 1.0,
end: 2.5,
energy: 0.3,
};
assert!((segment.duration() - 1.5).abs() < f32::EPSILON);
}
#[test]
fn test_batch_audio_with_vad() {
let mut vad = VoiceActivityDetector::default();
let segment1 = vec![0.0; 1600];
let segment2 = vec![0.0; 1600];
vad.reset();
let segments1 = vad.detect(&segment1);
assert!(segments1.is_empty());
vad.reset();
let segments2 = vad.detect(&segment2);
assert!(segments2.is_empty());
}
#[test]
fn test_vad_low_latency_config_for_streaming() {
let config = VadConfig::low_latency();
assert_eq!(config.frame_size, 160); assert_eq!(config.frame_duration_ms(), 10.0);
}
#[test]
fn test_vad_high_accuracy_config_for_batch() {
let config = VadConfig::high_accuracy();
assert_eq!(config.frame_size, 800); assert_eq!(config.frame_duration_ms(), 50.0);
}
}