timecode_coder/ltc_decoder/
mod.rs1use 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
11pub 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 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 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 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 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 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 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}