1use crate::rational::Rational;
4
5#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
9pub struct TimeBase(pub Rational);
10
11impl TimeBase {
12 pub const fn new(num: i64, den: i64) -> Self {
13 Self(Rational::new(num, den))
14 }
15
16 pub fn as_rational(&self) -> Rational {
17 self.0
18 }
19
20 pub fn seconds_of(&self, ticks: i64) -> f64 {
22 ticks as f64 * self.0.as_f64()
23 }
24
25 pub fn rescale(&self, ts: i64, target: TimeBase) -> i64 {
27 rescale(ts, self.0, target.0)
28 }
29}
30
31#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
33pub struct Timestamp {
34 pub value: i64,
35 pub base: TimeBase,
36}
37
38impl Timestamp {
39 pub const fn new(value: i64, base: TimeBase) -> Self {
40 Self { value, base }
41 }
42
43 pub fn seconds(&self) -> f64 {
44 self.base.seconds_of(self.value)
45 }
46
47 pub fn rescale(&self, target: TimeBase) -> Self {
48 Self {
49 value: self.base.rescale(self.value, target),
50 base: target,
51 }
52 }
53}
54
55pub fn rescale(value: i64, from: Rational, to: Rational) -> i64 {
60 let num = from.num as i128 * to.den as i128;
63 let den = from.den as i128 * to.num as i128;
64 if den == 0 {
65 return 0;
66 }
67 let prod = value as i128 * num;
68 let half = den.abs() / 2;
69 let rounded = if (prod >= 0) == (den > 0) {
70 (prod + half) / den
71 } else {
72 (prod - half) / den
73 };
74 rounded as i64
75}
76
77#[cfg(test)]
78mod tests {
79 use super::*;
80
81 #[test]
82 fn rescale_samples_to_pts() {
83 assert_eq!(
85 rescale(48000, Rational::new(1, 48000), Rational::new(1, 1000)),
86 1000
87 );
88 }
89
90 #[test]
91 fn timestamp_seconds() {
92 let ts = Timestamp::new(48000, TimeBase::new(1, 48000));
93 assert!((ts.seconds() - 1.0).abs() < 1e-9);
94 }
95
96 #[test]
97 fn rescale_rounds_half_away_from_zero() {
98 assert_eq!(rescale(1, Rational::new(1, 2), Rational::new(1, 1)), 1);
100 assert_eq!(rescale(-1, Rational::new(1, 2), Rational::new(1, 1)), -1);
102 assert_eq!(rescale(3, Rational::new(1, 2), Rational::new(1, 1)), 2);
104 assert_eq!(rescale(-3, Rational::new(1, 2), Rational::new(1, 1)), -2);
105 }
106}