use std::path::Path;
use dspk_core::{DspError, DspResult, RationalTime, RecordingSource, SampleFormat, SampleRate, SignalUnit};
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum SourceKind {
Electrical,
Other,
}
impl SourceKind {
pub fn from_unit(unit: &SignalUnit) -> Self {
match unit {
SignalUnit::Volt | SignalUnit::Millivolt | SignalUnit::Microvolt => SourceKind::Electrical,
_ => SourceKind::Other,
}
}
}
#[derive(Debug, Clone, PartialEq)]
pub struct SourceEntry {
pub id: String,
pub name: String,
pub kind: SourceKind,
pub channels: usize,
pub samples: u64,
pub sample_rate: SampleRate,
pub start_time: RationalTime,
pub format: SampleFormat,
pub unit: SignalUnit,
}
impl SourceEntry {
pub fn duration_sec(&self) -> f64 {
self.samples as f64 / self.sample_rate.rate_hz()
}
pub fn summary(&self) -> String {
let hz = self.sample_rate.rate_hz();
let rate = if hz >= 1000.0 { format!("{:.1} kHz", hz / 1000.0) } else { format!("{hz:.1} Hz") };
format!("{} · {} ch · {rate} · {}", self.name, self.channels, self.unit.symbol())
}
}
pub const MAIN: &str = "main";
pub fn single_source(rec: &dyn RecordingSource) -> SourceEntry {
let info = rec.info();
let unit = info.channels.first().map(|c| c.unit.clone()).unwrap_or_default();
SourceEntry {
id: MAIN.into(),
name: info.name.clone(),
kind: SourceKind::from_unit(&unit),
channels: info.channel_count(),
samples: info.samples,
sample_rate: info.sample_rate,
start_time: info.start_time,
format: info.format,
unit,
}
}
pub fn require_main(path: &Path, id: &str) -> DspResult<()> {
if id == MAIN {
Ok(())
} else {
Err(DspError::InvalidConfig(format!("{} has no source {id}", path.display())))
}
}
pub fn default_source(list: &[SourceEntry]) -> Option<&SourceEntry> {
let size = |s: &&SourceEntry| (s.samples * s.channels as u64, s.sample_rate.as_ratio());
list.iter().filter(|s| s.kind == SourceKind::Electrical).max_by_key(size).or_else(|| list.iter().max_by_key(size))
}