audio_duration/
lib.rs

1use std::convert::Into;
2use std::convert::TryFrom;
3use std::fmt;
4use std::time::Duration;
5
6pub const DAYS_IN_A_WEEK: u64 = 7;
7pub const HOURS_IN_A_DAY: u64 = 24;
8pub const MINUTES_IN_A_HOUR: u64 = 60;
9pub const SECONDS_IN_A_MINUTE: u64 = 60;
10pub const SECONDS_IN_A_HOUR: u64 = 3600;
11pub const SECONDS_IN_A_DAY: u64 = 86400;
12pub const SECONDS_IN_A_WEEK: u64 = 604800;
13
14/// Telephone narrow-band, very low quality.
15pub const SAMPLING_FREQUENCY_8000: u32 = 8000;
16
17/// Low quality, subwoofer analysis.
18pub const SAMPLING_FREQUENCY_11025: u32 = 11025;
19
20/// Telephone wide-band, used in VoIP.
21pub const SAMPLING_FREQUENCY_16000: u32 = 16000;
22
23/// Low quality, low frequency analysis.
24pub const SAMPLING_FREQUENCY_22050: u32 = 22050;
25
26/// MiniDV, 4-channel audio for DVCAM, long-play DAT.
27pub const SAMPLING_FREQUENCY_32000: u32 = 32000;
28
29/// Audio CD.
30pub const SAMPLING_FREQUENCY_44100: u32 = 44100;
31
32/// DVD, TV and Film.
33pub const SAMPLING_FREQUENCY_48000: u32 = 48000;
34
35/// Pro-audio recording for CD.
36pub const SAMPLING_FREQUENCY_88200: u32 = 88200;
37
38/// DVD-Audio, BD-ROM, HD DVD.
39pub const SAMPLING_FREQUENCY_96000: u32 = 96000;
40
41/// Pro-audio recording for CD.
42pub const SAMPLING_FREQUENCY_176400: u32 = 176400;
43
44/// DVD-Audio, BD-ROM, HD DVD.
45pub const SAMPLING_FREQUENCY_192000: u32 = 192000;
46
47/// Digital eXtreme Definition, used for recording SACDs.
48pub const SAMPLING_FREQUENCY_352800: u32 = 352800;
49
50/// Very high PCM sampling frequency on current DACs.
51pub const SAMPLING_FREQUENCY_384000: u32 = 384000;
52
53/// Digital eXtreme Definition, used for recording SACDs.
54pub const SAMPLING_FREQUENCY_705600: u32 = 705600;
55
56/// Highest PCM sampling frequency on current DACs.
57pub const SAMPLING_FREQUENCY_768000: u32 = 768000;
58
59/// DSD rate as used in SACDs, also known as DSD64
60pub const SAMPLING_FREQUENCY_2822400: u32 = 2822400;
61
62/// Doube-rate DSD, also known as DSD128
63pub const SAMPLING_FREQUENCY_5644800: u32 = 5644800;
64
65/// Quad-rate DSD, also known as DSD256.
66pub const SAMPLING_FREQUENCY_11289600: u32 = 11289600;
67
68/// Octuple-rate DSD, also known as DSD512.
69pub const SAMPLING_FREQUENCY_22579200: u32 = 22579200;
70
71pub struct AudioDuration {
72    samples: u64,
73    sampling_frequency: u32,
74}
75impl AudioDuration {
76    /// Make a new `AudioDuration`.
77    pub fn new(samples: u64, sampling_frequency: u32) -> AudioDuration {
78        AudioDuration {
79            samples,
80            sampling_frequency,
81        }
82    }
83
84    /// Return the number of samples in this `AudioDuration`.
85    pub fn samples(&self) -> u64 {
86        self.samples
87    }
88
89    /// Set the samples for this `AudioDuration`.
90    pub fn set_samples(&mut self, samples: u64) {
91        self.samples = samples
92    }
93
94    /// Return the sampling frequency of this `AudioDuration`.
95    pub fn sampling_frequency(&self) -> u32 {
96        self.sampling_frequency
97    }
98
99    /// Return the whole number of seconds in this `AudioDuration`.
100    pub fn as_secs(&self) -> u64 {
101        self.samples / self.sampling_frequency as u64
102    }
103
104    /// Return the whole number of minutes in this `AudioDuration`.
105    pub fn as_mins(&self) -> u64 {
106        self.as_secs() / SECONDS_IN_A_MINUTE
107    }
108
109    /// Return the whole number of hours in this `AudioDuration`.
110    pub fn as_hours(&self) -> u64 {
111        self.as_secs() / SECONDS_IN_A_HOUR
112    }
113
114    /// Return the whole number of days in this `AudioDuration`.
115    pub fn as_days(&self) -> u64 {
116        self.as_secs() / SECONDS_IN_A_DAY
117    }
118
119    /// Return the whole number of weeks in this `AudioDuration`.
120    pub fn as_weeks(&self) -> u64 {
121        self.as_secs() / SECONDS_IN_A_WEEK
122    }
123
124    /// Return the fractional part of this `AudioDuration` in samples.
125    ///
126    /// This will return a value in the range `0 <= subsec_samples < sampling_frequency`.
127    pub fn subsec_samples(&self) -> u32 {
128        let remainder = self.samples % self.sampling_frequency as u64;
129
130        u32::try_from(remainder).unwrap()
131    }
132
133    /// Return the fractional part of this `AudioDuration` as a whole
134    /// number of nanoseconds.
135    pub fn subsec_nanos(&self) -> u32 {
136        let nanos_as_f64 = self.subsec_secs() * 1000000000.0;
137
138        nanos_as_f64 as u32
139    }
140
141    /// Return the fractional part of this `AudioDuration` as seconds.
142    ///
143    /// This will return a value in the range `0.0 <= subsec_secs < 1.0`.
144    pub fn subsec_secs(&self) -> f64 {
145        self.subsec_samples() as f64 / self.sampling_frequency as f64
146    }
147
148    /// Return the fractional part of minutes in seconds.
149    pub fn submin_secs(&self) -> u64 {
150        self.as_secs() % SECONDS_IN_A_MINUTE
151    }
152
153    /// Return the fractional part of hours in minutes.
154    pub fn subhour_mins(&self) -> u64 {
155        self.as_mins() % MINUTES_IN_A_HOUR
156    }
157
158    /// Return the fractional part of days in hours.
159    pub fn subday_hours(&self) -> u64 {
160        self.as_hours() % HOURS_IN_A_DAY
161    }
162
163    /// Return the fractional part of days in hours.
164    pub fn subweek_days(&self) -> u64 {
165        self.as_days() % DAYS_IN_A_WEEK
166    }
167
168    /// Return this `AudioDuration` as a [Duration](std::time::Duration).
169    pub fn duration(&self) -> Duration {
170        Duration::new(self.as_secs(), self.subsec_nanos())
171    }
172
173    /// Return the duration as a `String` formatted as `hh:mm:ss;samples`.
174    pub fn fmt_hhmmss_samples(&self) -> String {
175        format!(
176            "{:02}:{:02}:{:02};{}",
177            self.as_hours(),
178            self.subhour_mins(),
179            self.submin_secs(),
180            self.subsec_samples(),
181        )
182    }
183
184    /// Return the duration as a `String` formatted as `hh:mm:ss`.
185    pub fn fmt_hhmmss(&self) -> String {
186        format!(
187            "{:02}:{:02}:{:02}",
188            self.as_hours(),
189            self.subhour_mins(),
190            self.submin_secs(),
191        )
192    }
193
194    /// Return the duration formatted as `mm:ss` if the duration is
195    /// less than an hour, as `hh:mm:ss` otherwise.
196    pub fn fmt_opt_hh_mmss(&self) -> String {
197        if self.as_hours() == 0 {
198            format!("{:02}:{:02}", self.subhour_mins(), self.submin_secs())
199        } else {
200            self.fmt_hhmmss()
201        }
202    }
203}
204impl fmt::Display for AudioDuration {
205    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
206        write!(f, "{}", self.fmt_hhmmss_samples())
207    }
208}
209impl Into<Duration> for AudioDuration {
210    fn into(self) -> Duration {
211        self.duration()
212    }
213}
214
215#[cfg(test)]
216mod tests {
217    use super::*;
218
219    #[test]
220    fn new_and_getters() {
221        let samples: u64 = 32572394598;
222        let audio_duration = AudioDuration::new(samples, SAMPLING_FREQUENCY_44100);
223
224        assert_eq!(audio_duration.samples(), samples);
225        assert_eq!(
226            audio_duration.sampling_frequency(),
227            SAMPLING_FREQUENCY_44100
228        );
229        assert_eq!(audio_duration.as_secs(), 738603);
230        assert_eq!(audio_duration.as_mins(), 12310);
231        assert_eq!(audio_duration.as_hours(), 205);
232        assert_eq!(audio_duration.as_days(), 8);
233        assert_eq!(audio_duration.as_weeks(), 1);
234        assert_eq!(audio_duration.subsec_samples(), 2298);
235        assert_eq!(audio_duration.subsec_nanos(), 52108843);
236        assert_eq!(audio_duration.subsec_secs(), 0.05210884353741497);
237        assert_eq!(audio_duration.submin_secs(), 3);
238        assert_eq!(audio_duration.subhour_mins(), 10);
239        assert_eq!(audio_duration.subday_hours(), 13);
240        assert_eq!(audio_duration.subweek_days(), 1);
241
242        assert_eq!(audio_duration.to_string(), "205:10:03;2298");
243    }
244
245    #[test]
246    fn duration() {
247        let samples: u64 = 52394599;
248        let audio_duration = AudioDuration::new(samples, SAMPLING_FREQUENCY_44100);
249        let duration: Duration = audio_duration.into();
250
251        assert_eq!(duration, Duration::new(1188, 86145124));
252    }
253
254    #[test]
255    fn fmt() {
256        let samples: u64 = 52394599;
257        let audio_duration = AudioDuration::new(samples, SAMPLING_FREQUENCY_44100);
258        assert_eq!(audio_duration.fmt_hhmmss(), "00:19:48");
259        assert_eq!(audio_duration.fmt_opt_hh_mmss(), "19:48");
260
261        let samples: u64 = 952394599;
262        let audio_duration = AudioDuration::new(samples, SAMPLING_FREQUENCY_44100);
263        assert_eq!(audio_duration.fmt_hhmmss(), "05:59:56");
264        assert_eq!(audio_duration.fmt_opt_hh_mmss(), "05:59:56");
265    }
266}