#[derive(Debug, Clone)]
pub struct RirSegment {
pub onset_sample: usize,
pub end_sample: usize,
pub toa_sample: usize,
pub doa: Option<[f32; 3]>,
pub peak_energy: f64,
pub is_direct_sound: bool,
}
impl RirSegment {
pub fn len(&self) -> usize {
self.end_sample.saturating_sub(self.onset_sample)
}
pub fn is_empty(&self) -> bool {
self.len() == 0
}
pub fn duration_secs(&self, sample_rate: f64) -> f64 {
self.len() as f64 / sample_rate
}
pub fn duration_ms(&self, sample_rate: f64) -> f64 {
self.duration_secs(sample_rate) * 1000.0
}
pub fn toa_ms(&self, sample_rate: f64) -> f64 {
self.toa_sample as f64 / sample_rate * 1000.0
}
pub fn azimuth_deg(&self) -> Option<f32> {
self.doa.map(|d| d[1].atan2(d[0]).to_degrees())
}
pub fn elevation_deg(&self) -> Option<f32> {
self.doa
.map(|d| d[2].atan2((d[0] * d[0] + d[1] * d[1]).sqrt()).to_degrees())
}
}
#[derive(Debug, Clone)]
pub struct SsirResult {
pub segments: Vec<RirSegment>,
pub mixing_time_samples: usize,
pub sample_rate: f64,
}
impl SsirResult {
pub fn num_events(&self) -> usize {
self.segments.len()
}
pub fn num_reflections(&self) -> usize {
self.segments.len().saturating_sub(1)
}
pub fn mixing_time_ms(&self) -> f64 {
self.mixing_time_samples as f64 / self.sample_rate * 1000.0
}
pub fn reflections(&self) -> impl Iterator<Item = &RirSegment> {
self.segments.iter().filter(|s| !s.is_direct_sound)
}
pub fn direct_sound(&self) -> Option<&RirSegment> {
self.segments.first().filter(|s| s.is_direct_sound)
}
pub fn direct_sound_doa(&self) -> Option<[f32; 3]> {
self.direct_sound().and_then(|s| s.doa)
}
}