timecode_coder/ltc_decoder/
mod.rs

1use core::fmt::Display;
2
3use num_traits::{FromPrimitive, ToPrimitive, Zero};
4
5use crate::ltc_decoder::bit_decoder::{BitDecoder, BitVal};
6use crate::ltc_frame::LtcFrame;
7use crate::TimecodeFrame;
8
9mod bit_decoder;
10
11//pub trait Sample: Copy + Zero + std::ops::Div<f64>+ FromPrimitive + Ord + Sync + Send + 'static {}
12//pub trait Sample: Zero + Ord + Clone + Copy + 'static {}
13
14pub trait Sample: Zero + Ord + Clone + Copy + FromPrimitive + ToPrimitive + Display + 'static {}
15
16impl<T> Sample for T where T: Zero + Ord + Clone + Copy + FromPrimitive + ToPrimitive + Display + 'static {}
17
18pub struct LtcDecoder<T: Sample> {
19    ltc_frame: LtcFrame,
20    bit_decoder: BitDecoder<T>,
21    sampling_rate: f32,
22}
23
24impl<T: Sample> LtcDecoder<T> {
25    pub fn new<S: ToPrimitive>(sampling_rate: S) -> Self {
26        Self {
27            ltc_frame: LtcFrame::new_empty(),
28            bit_decoder: BitDecoder::new(),
29            sampling_rate: sampling_rate.to_f32().expect("Invalid sampling rate"),
30        }
31    }
32}
33
34impl<T: Sample> LtcDecoder<T> {
35    /// Push received audio-sample-point one after another in this function. From time to time
36    /// a Timecode-Frame will be returned to tell the current received timecode
37    pub fn get_timecode_frame(&mut self, sample: T) -> Option<TimecodeFrame> {
38        self.ltc_frame.sample_received();
39        match self.bit_decoder.get_bit(sample) {
40            BitVal::None => { return None; }
41            BitVal::Invalid => {
42                self.invalidate();
43                return None;
44            }
45            BitVal::True => { self.ltc_frame.shift_bit(true); }
46            BitVal::False => { self.ltc_frame.shift_bit(false); }
47        }
48        if let Some((data, samples_for_frame)) = self.ltc_frame.get_data() {
49            Some(data.make_ltc_frame(self.sample_count_to_duration_s(samples_for_frame)))
50        } else {
51            None
52        }
53    }
54    fn sample_count_to_duration_s(&self, sample_count: usize) -> f32 {
55        (sample_count as f32) / self.sampling_rate
56    }
57
58    /// In case some unexpected data is received, this function invalidates the decoder to restart
59    /// synchronizing on the heartbeat of the data
60    fn invalidate(&mut self) {
61        self.ltc_frame.invalidate();
62        self.bit_decoder.invalidate();
63    }
64}
65
66#[cfg(test)]
67mod tests {
68    use core::ops::Shl;
69    use std::fs::File;
70    use std::io;
71    use std::io::Read;
72
73    use num_traits::Zero;
74    use wav::BitDepth;
75
76    use crate::ltc_decoder::{LtcDecoder, Sample};
77    use crate::{TimecodeFrame};
78    use crate::FramesPerSecond::{Thirty, TwentyFive, TwentyFour};
79
80    #[test]
81    fn test_sample_trait() {
82        test_zero(0_i64);
83        test_zero(0_i32);
84        test_zero(0_i16);
85        test_zero(0_i8);
86        test_zero(0_u64);
87        test_zero(0_u32);
88        test_zero(0_u16);
89        test_zero(0_u8);
90
91        test_ord(0_i64);
92        test_ord(0_i32);
93        test_ord(0_i16);
94        test_ord(0_i8);
95        test_ord(0_u64);
96        test_ord(0_u32);
97        test_ord(0_u16);
98        test_ord(0_u8);
99
100        test_clone(0_i64);
101        test_clone(0_i32);
102        test_clone(0_i16);
103        test_clone(0_i8);
104        test_clone(0_u64);
105        test_clone(0_u32);
106        test_clone(0_u16);
107        test_clone(0_u8);
108
109        test_copy(0_i64);
110        test_copy(0_i32);
111        test_copy(0_i16);
112        test_copy(0_i8);
113        test_copy(0_u64);
114        test_copy(0_u32);
115        test_copy(0_u16);
116        test_copy(0_u8);
117
118        test_shl(0_i64);
119        test_shl(0_i32);
120        test_shl(0_i16);
121        test_shl(0_i8);
122        test_shl(0_u64);
123        test_shl(0_u32);
124        test_shl(0_u16);
125        test_shl(0_u8);
126
127        test_sample(0_i64);
128        test_sample(0_i32);
129        test_sample(0_i16);
130        test_sample(0_i8);
131        test_sample(0_u64);
132        test_sample(0_u32);
133        test_sample(0_u16);
134        test_sample(0_u8);
135    }
136
137    fn test_zero<T: Zero>(_s: T) {
138        assert!(true);
139    }
140
141    fn test_ord<T: Ord>(_s: T) {
142        assert!(true);
143    }
144
145    fn test_clone<T: Clone>(_s: T) {
146        assert!(true);
147    }
148
149    fn test_copy<T: Copy>(_s: T) {
150        assert!(true);
151    }
152
153    fn test_sample<T: Sample>(_s: T) {
154        assert!(true);
155    }
156
157    fn test_shl<T: Shl>(_s: T) {
158        assert!(true);
159    }
160
161    #[test]
162    fn test_ltc_00100000_2mins_25fps_44100x8() {
163        test_timecode_file("testfiles/LTC_00100000_2mins_25fps_44100x8.wav",
164                           TimecodeFrame::new(0, 10, 0, 1, TwentyFive),
165                           TimecodeFrame::new(0, 12, 1, 0, TwentyFive))
166    }
167
168
169    #[test]
170    fn test_ltc_00500000_2mins_30fps_44100x8() {
171        test_timecode_file("testfiles/LTC_00500000_2mins_30fps_44100x8.wav",
172                           TimecodeFrame::new(0, 50, 0, 1, Thirty),
173                           TimecodeFrame::new(0, 52, 1, 0, Thirty))
174    }
175
176    #[test]
177    fn test_ltc_10000000_2mins_24fps_44100x16() {
178        test_timecode_file("testfiles/LTC_10000000_2mins_24fps_44100x16.wav",
179                           TimecodeFrame::new(10, 0, 0, 1, TwentyFour),
180                           TimecodeFrame::new(10, 2, 1, 0, TwentyFour))
181    }
182
183    #[test]
184    fn test_ltc_10100000_2mins_25fps_44100x16() {
185        test_timecode_file("testfiles/LTC_10100000_2mins_25fps_44100x16.wav",
186                           TimecodeFrame::new(10, 10, 0, 1, TwentyFive),
187                           TimecodeFrame::new(10, 12, 1, 0, TwentyFive))
188    }
189
190    #[test]
191    fn test_ltc_10400000_2mins_30fps_44100x16() {
192        test_timecode_file("testfiles/LTC_10400000_2mins_30fps_44100x16.wav",
193                           TimecodeFrame::new(10, 40, 0, 1, Thirty),
194                           TimecodeFrame::new(10, 42, 1, 0, Thirty))
195    }
196
197    #[test]
198    fn test_ltc_10500000_2mins_24fps_48000x16() {
199        test_timecode_file("testfiles/LTC_10500000_2mins_24fps_48000x16.wav",
200                           TimecodeFrame::new(10, 50, 0, 1, TwentyFour),
201                           TimecodeFrame::new(10, 52, 1, 0, TwentyFour))
202    }
203
204    #[test]
205    fn test_ltc_11000000_2mins_25fps_48000x16() {
206        test_timecode_file("testfiles/LTC_11000000_2mins_25fps_48000x16.wav",
207                           TimecodeFrame::new(11, 0, 0, 1, TwentyFive),
208                           TimecodeFrame::new(11, 2, 1, 0, TwentyFive))
209    }
210
211    #[test]
212    fn test_ltc_11300000_2mins_30fps_48000x16() {
213        test_timecode_file("testfiles/LTC_11300000_2mins_30fps_48000x16.wav",
214                           TimecodeFrame::new(11, 30, 0, 1, Thirty),
215                           TimecodeFrame::new(11, 32, 1, 0, Thirty))
216    }
217
218    #[test]
219    fn test_ltc_11400000_2mins_24fps_44100x16() {
220        test_timecode_file("testfiles/LTC_11400000_2mins_24fps_44100x16.wav",
221                           TimecodeFrame::new(11, 40, 0, 1, TwentyFour),
222                           TimecodeFrame::new(11, 42, 1, 0, TwentyFour))
223    }
224
225    #[test]
226    fn test_ltc_11500000_2mins_25fps_44100x16() {
227        test_timecode_file("testfiles/LTC_11500000_2mins_25fps_44100x16.wav",
228                           TimecodeFrame::new(11, 50, 0, 1, TwentyFive),
229                           TimecodeFrame::new(11, 52, 1, 0, TwentyFive))
230    }
231
232    #[test]
233    fn test_ltc_12200000_2mins_30fps_44100x16() {
234        test_timecode_file("testfiles/LTC_12200000_2mins_30fps_44100x16.wav",
235                           TimecodeFrame::new(12, 20, 0, 1, Thirty),
236                           TimecodeFrame::new(12, 22, 1, 0, Thirty))
237    }
238
239
240    /// runs a test on decoding timecode sample by sample with specifing the first expected decoded
241    /// Frame (usually 1 frame above the start of the audio, because the lib needs some tim to sync)
242    /// and the last expected decoded Frame
243    fn test_timecode_file(file: &str, first_tc: TimecodeFrame, last_tc: TimecodeFrame) {
244        let mut file = File::open(file).expect("File not found");
245        let (sampling_rate, data) = get_timecode_file_data(&mut file);
246        match data {
247            BitDepth::Eight(samples) => test_timecode_frames(sampling_rate, samples, first_tc, last_tc),
248            BitDepth::Sixteen(samples) => test_timecode_frames(sampling_rate, samples, first_tc, last_tc),
249            BitDepth::TwentyFour(samples) => test_timecode_frames(sampling_rate, samples, first_tc, last_tc),
250            BitDepth::ThirtyTwoFloat(_) => panic!("Unsupported format"),
251            BitDepth::Empty => panic!("File is empty")
252        }
253    }
254
255    /// runs a test on decoding timecode sample by sample with specifing the first expected decoded
256    /// Frame (usually 1 frame above the start of the audio, because the lib needs some tim to sync)
257    /// and the last expected decoded Frame
258    fn test_timecode_frames<T: Sample>(sampling_rate: u32, samples: Vec<T>, first_tc: TimecodeFrame, last_tc: TimecodeFrame) {
259        let mut decoder = LtcDecoder::<T>::new(sampling_rate);
260        let mut timecode = first_tc.clone();
261        for sample in samples {
262            if let Some(tc) = decoder.get_timecode_frame(sample) {
263                assert_eq!(tc, timecode);
264                timecode.add_frame();
265            }
266        }
267        assert_eq!(timecode, last_tc);
268    }
269
270    /// Returns sample rate and data from a wav file that contains timecode data for testing
271    fn get_timecode_file_data<R>(file: &mut R) -> (u32, BitDepth)
272        where R: io::Seek + Read, {
273        let (header, data) = wav::read(file).expect("could not open timecode file");
274        let data = get_left_channel(header.channel_count, data);
275        (header.sampling_rate, data)
276    }
277
278    /// Handles if a file is stereo
279    fn get_left_channel(channel_count: u16, samples: BitDepth) -> BitDepth {
280        if channel_count == 1 {
281            return samples;
282        }
283        if channel_count > 2 {
284            panic!("No more than two channels supported");
285        }
286        match samples {
287            BitDepth::Eight(samples) => BitDepth::Eight(samples.iter().skip(1).step_by(2).copied().collect()),
288            BitDepth::Sixteen(samples) => BitDepth::Sixteen(samples.iter().skip(1).step_by(2).copied().collect()),
289            BitDepth::TwentyFour(samples) => BitDepth::TwentyFour(samples.iter().skip(1).step_by(2).copied().collect()),
290            BitDepth::ThirtyTwoFloat(samples) => BitDepth::ThirtyTwoFloat(samples.iter().skip(1).step_by(2).copied().collect()),
291            BitDepth::Empty => BitDepth::Empty
292        }
293    }
294}