1use std::path::Path;
8
9use dspk_core::{DspError, DspResult, RationalTime, RecordingSource, SampleFormat, SampleRate, SignalUnit};
10
11#[derive(Debug, Clone, Copy, PartialEq, Eq)]
12pub enum SourceKind {
13 Electrical,
15 Other,
17}
18
19impl SourceKind {
20 pub fn from_unit(unit: &SignalUnit) -> Self {
22 match unit {
23 SignalUnit::Volt | SignalUnit::Millivolt | SignalUnit::Microvolt => SourceKind::Electrical,
24 _ => SourceKind::Other,
25 }
26 }
27}
28
29#[derive(Debug, Clone, PartialEq)]
31pub struct SourceEntry {
32 pub id: String,
34 pub name: String,
35 pub kind: SourceKind,
36 pub channels: usize,
37 pub samples: u64,
38 pub sample_rate: SampleRate,
39 pub start_time: RationalTime,
40 pub format: SampleFormat,
42 pub unit: SignalUnit,
44}
45
46impl SourceEntry {
47 pub fn duration_sec(&self) -> f64 {
48 self.samples as f64 / self.sample_rate.rate_hz()
49 }
50
51 pub fn summary(&self) -> String {
53 let hz = self.sample_rate.rate_hz();
54 let rate = if hz >= 1000.0 { format!("{:.1} kHz", hz / 1000.0) } else { format!("{hz:.1} Hz") };
55 format!("{} · {} ch · {rate} · {}", self.name, self.channels, self.unit.symbol())
56 }
57}
58
59pub const MAIN: &str = "main";
61
62pub fn single_source(rec: &dyn RecordingSource) -> SourceEntry {
64 let info = rec.info();
65 let unit = info.channels.first().map(|c| c.unit.clone()).unwrap_or_default();
66 SourceEntry {
67 id: MAIN.into(),
68 name: info.name.clone(),
69 kind: SourceKind::from_unit(&unit),
70 channels: info.channel_count(),
71 samples: info.samples,
72 sample_rate: info.sample_rate,
73 start_time: info.start_time,
74 format: info.format,
75 unit,
76 }
77}
78
79pub fn require_main(path: &Path, id: &str) -> DspResult<()> {
81 if id == MAIN {
82 Ok(())
83 } else {
84 Err(DspError::InvalidConfig(format!("{} has no source {id}", path.display())))
85 }
86}
87
88pub fn default_source(list: &[SourceEntry]) -> Option<&SourceEntry> {
90 let size = |s: &&SourceEntry| (s.samples * s.channels as u64, s.sample_rate.as_ratio());
92 list.iter().filter(|s| s.kind == SourceKind::Electrical).max_by_key(size).or_else(|| list.iter().max_by_key(size))
93}