1use std::time::{Duration, Instant};
4
5pub type AudioDuration = Duration;
7
8pub type AudioInstant = Instant;
10
11#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Default)]
13pub struct AudioTimestamp(u64);
14
15impl AudioTimestamp {
16 pub const ZERO: Self = Self(0);
17 pub const EPOCH: Self = Self(0);
18
19 pub fn from_secs(secs: f64) -> Self {
20 Self((secs * 1_000_000_000.0) as u64)
21 }
22
23 pub fn from_nanos(nanos: u64) -> Self {
24 Self(nanos)
25 }
26
27 pub fn from_samples(samples: u64, sample_rate: u32) -> Self {
28 if sample_rate == 0 {
29 return Self::ZERO;
30 }
31 Self(samples * 1_000_000_000 / sample_rate as u64)
32 }
33
34 pub fn as_secs(&self) -> f64 {
35 self.0 as f64 / 1_000_000_000.0
36 }
37
38 pub fn as_millis(&self) -> f64 {
39 self.0 as f64 / 1_000_000.0
40 }
41
42 pub fn nanos(&self) -> u64 {
43 self.0
44 }
45
46 pub fn add_duration(&self, d: Duration) -> Self {
47 Self(self.0.saturating_add(d.as_nanos() as u64))
48 }
49
50 pub fn duration_since(&self, earlier: Self) -> Option<Duration> {
51 self.0.checked_sub(earlier.0).map(Duration::from_nanos)
52 }
53}
54
55impl std::ops::Add<Duration> for AudioTimestamp {
56 type Output = Self;
57 fn add(self, rhs: Duration) -> Self {
58 self.add_duration(rhs)
59 }
60}
61
62impl std::ops::Sub for AudioTimestamp {
63 type Output = Duration;
64 fn sub(self, rhs: Self) -> Duration {
65 Duration::from_nanos(self.0.saturating_sub(rhs.0))
66 }
67}
68
69impl std::fmt::Display for AudioTimestamp {
70 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
71 write!(f, "{:.3}s", self.as_secs())
72 }
73}
74
75pub fn validate_in_range<T: PartialOrd + std::fmt::Display>(
77 value: T,
78 min: T,
79 max: T,
80 name: &str,
81) -> crate::error::Result<()> {
82 if value < min || value > max {
83 return Err(crate::error::Error::config(format!(
84 "{name} = {value} is outside valid range [{min}, {max}]"
85 )));
86 }
87 Ok(())
88}