1use super::constants::*;
11use crate::{FrameRate, Timecode, TimecodeError};
12
13#[derive(Debug, Clone, Copy, PartialEq, Eq)]
15#[allow(dead_code)]
16enum DecoderState {
17 Searching,
19 Locked,
21 LostSync,
23}
24
25pub struct LtcDecoder {
27 #[allow(dead_code)]
29 sample_rate: u32,
30 frame_rate: FrameRate,
32 min_amplitude: f32,
34 state: DecoderState,
36 bit_buffer: [bool; BITS_PER_FRAME],
38 bit_position: usize,
40 zero_crossing: ZeroCrossingDetector,
42 bit_sync: BitSynchronizer,
44 last_timecode: Option<Timecode>,
46 sync_confidence: u32,
48 error_count: u32,
50}
51
52impl LtcDecoder {
53 pub fn new(sample_rate: u32, frame_rate: FrameRate, min_amplitude: f32) -> Self {
55 LtcDecoder {
56 sample_rate,
57 frame_rate,
58 min_amplitude,
59 state: DecoderState::Searching,
60 bit_buffer: [false; BITS_PER_FRAME],
61 bit_position: 0,
62 zero_crossing: ZeroCrossingDetector::new(sample_rate, frame_rate),
63 bit_sync: BitSynchronizer::new(sample_rate, frame_rate),
64 last_timecode: None,
65 sync_confidence: 0,
66 error_count: 0,
67 }
68 }
69
70 pub fn process_samples(&mut self, samples: &[f32]) -> Result<Option<Timecode>, TimecodeError> {
72 let mut result = None;
73
74 for &sample in samples {
75 if let Some(transition) = self
77 .zero_crossing
78 .process_sample(sample, self.min_amplitude)
79 {
80 if let Some(bit) = self.bit_sync.process_transition(transition) {
82 if let Some(tc) = self.process_bit(bit)? {
84 result = Some(tc);
85 }
86 }
87 }
88 }
89
90 Ok(result)
91 }
92
93 fn process_bit(&mut self, bit: bool) -> Result<Option<Timecode>, TimecodeError> {
95 self.bit_buffer[self.bit_position] = bit;
97 self.bit_position += 1;
98
99 if self.bit_position >= BITS_PER_FRAME {
101 self.bit_position = 0;
102
103 match self.decode_frame() {
105 Ok(timecode) => {
106 self.state = DecoderState::Locked;
107 self.sync_confidence = self.sync_confidence.saturating_add(1).min(100);
108 self.error_count = 0;
109 self.last_timecode = Some(timecode);
110 return Ok(Some(timecode));
111 }
112 Err(_) => {
113 self.error_count += 1;
114 if self.error_count > 10 {
115 self.state = DecoderState::LostSync;
116 self.sync_confidence = 0;
117 }
118 }
119 }
120 }
121
122 Ok(None)
123 }
124
125 fn decode_frame(&self) -> Result<Timecode, TimecodeError> {
127 let sync_pos = self.find_sync_word()?;
129
130 let mut data_bits = [false; DATA_BITS];
132 for (i, data_bit) in data_bits.iter_mut().enumerate().take(DATA_BITS) {
133 let pos = (sync_pos + BITS_PER_FRAME - SYNC_BITS - DATA_BITS + i) % BITS_PER_FRAME;
134 *data_bit = self.bit_buffer[pos];
135 }
136
137 self.decode_timecode_from_bits(&data_bits)
139 }
140
141 fn find_sync_word(&self) -> Result<usize, TimecodeError> {
143 let sync_bits = self.u16_to_bits(SYNC_WORD);
145
146 for start_pos in 0..BITS_PER_FRAME {
148 let mut match_count = 0;
149 for (i, &sync_bit) in sync_bits.iter().enumerate().take(SYNC_BITS) {
150 let pos = (start_pos + i) % BITS_PER_FRAME;
151 if self.bit_buffer[pos] == sync_bit {
152 match_count += 1;
153 }
154 }
155
156 if match_count >= SYNC_BITS - 2 {
158 return Ok(start_pos);
159 }
160 }
161
162 Err(TimecodeError::SyncNotFound)
163 }
164
165 fn u16_to_bits(&self, value: u16) -> [bool; 16] {
167 let mut bits = [false; 16];
168 for (i, bit) in bits.iter_mut().enumerate() {
169 *bit = (value & (1 << i)) != 0;
170 }
171 bits
172 }
173
174 fn decode_timecode_from_bits(
176 &self,
177 bits: &[bool; DATA_BITS],
178 ) -> Result<Timecode, TimecodeError> {
179 let frame_units = self.bits_to_u8(&bits[0..4]);
202 let frame_tens = self.bits_to_u8(&bits[8..10]);
203 let frames = frame_tens * 10 + frame_units;
204
205 let second_units = self.bits_to_u8(&bits[16..20]);
206 let second_tens = self.bits_to_u8(&bits[24..27]);
207 let seconds = second_tens * 10 + second_units;
208
209 let minute_units = self.bits_to_u8(&bits[32..36]);
210 let minute_tens = self.bits_to_u8(&bits[40..43]);
211 let minutes = minute_tens * 10 + minute_units;
212
213 let hour_units = self.bits_to_u8(&bits[48..52]);
214 let hour_tens = self.bits_to_u8(&bits[56..58]);
215 let hours = hour_tens * 10 + hour_units;
216
217 let drop_frame = bits[10];
219
220 let user_bits = self.extract_user_bits(bits);
222
223 let frame_rate = if drop_frame && self.frame_rate == FrameRate::Fps2997NDF {
225 FrameRate::Fps2997DF
226 } else {
227 self.frame_rate
228 };
229
230 let mut timecode = Timecode::new(hours, minutes, seconds, frames, frame_rate)?;
231 timecode.user_bits = user_bits;
232
233 Ok(timecode)
234 }
235
236 fn bits_to_u8(&self, bits: &[bool]) -> u8 {
238 let mut value = 0u8;
239 for (i, &bit) in bits.iter().enumerate() {
240 if bit {
241 value |= 1 << i;
242 }
243 }
244 value
245 }
246
247 fn extract_user_bits(&self, bits: &[bool; DATA_BITS]) -> u32 {
249 let mut user_bits = 0u32;
250
251 user_bits |= self.bits_to_u8(&bits[4..8]) as u32;
254 user_bits |= (self.bits_to_u8(&bits[12..16]) as u32) << 4;
256 user_bits |= (self.bits_to_u8(&bits[20..24]) as u32) << 8;
258 user_bits |= (self.bits_to_u8(&bits[28..32]) as u32) << 12;
260 user_bits |= (self.bits_to_u8(&bits[36..40]) as u32) << 16;
262 user_bits |= (self.bits_to_u8(&bits[44..48]) as u32) << 20;
264 user_bits |= (self.bits_to_u8(&bits[52..56]) as u32) << 24;
266 user_bits |= (self.bits_to_u8(&bits[59..63]) as u32) << 28;
268
269 user_bits
270 }
271
272 pub fn reset(&mut self) {
274 self.state = DecoderState::Searching;
275 self.bit_position = 0;
276 self.sync_confidence = 0;
277 self.error_count = 0;
278 self.zero_crossing.reset();
279 self.bit_sync.reset();
280 }
281
282 pub fn last_decoded_timecode(&self) -> Option<Timecode> {
288 self.last_timecode
289 }
290
291 pub fn is_synchronized(&self) -> bool {
293 self.state == DecoderState::Locked && self.sync_confidence >= 10
294 }
295
296 pub fn sync_confidence(&self) -> f32 {
298 (self.sync_confidence as f32) / 100.0
299 }
300}
301
302#[allow(dead_code)]
304struct ZeroCrossingDetector {
305 prev_sample: f32,
307 sample_count: u64,
309 samples_per_bit: f32,
311}
312
313impl ZeroCrossingDetector {
314 fn new(sample_rate: u32, frame_rate: FrameRate) -> Self {
315 let fps = frame_rate.as_float();
316 let bits_per_second = fps * BITS_PER_FRAME as f64;
317 let samples_per_bit = sample_rate as f64 / bits_per_second;
318
319 ZeroCrossingDetector {
320 prev_sample: 0.0,
321 sample_count: 0,
322 samples_per_bit: samples_per_bit as f32,
323 }
324 }
325
326 fn process_sample(&mut self, sample: f32, min_amplitude: f32) -> Option<Transition> {
328 self.sample_count += 1;
329
330 let transition = if self.prev_sample < -min_amplitude && sample >= min_amplitude {
332 Some(Transition {
333 sample_index: self.sample_count,
334 rising: true,
335 })
336 } else if self.prev_sample > min_amplitude && sample <= -min_amplitude {
337 Some(Transition {
338 sample_index: self.sample_count,
339 rising: false,
340 })
341 } else {
342 None
343 };
344
345 self.prev_sample = sample;
346 transition
347 }
348
349 fn reset(&mut self) {
350 self.prev_sample = 0.0;
351 self.sample_count = 0;
352 }
353}
354
355#[derive(Debug, Clone, Copy)]
357#[allow(dead_code)]
358struct Transition {
359 sample_index: u64,
360 rising: bool,
361}
362
363struct BitSynchronizer {
365 last_transition: Option<u64>,
367 samples_per_bit: f32,
369 bit_phase: f32,
371 bit_clock: f32,
373 pll_filter: PllFilter,
375}
376
377impl BitSynchronizer {
378 fn new(sample_rate: u32, frame_rate: FrameRate) -> Self {
379 let fps = frame_rate.as_float();
380 let bits_per_second = fps * BITS_PER_FRAME as f64;
381 let samples_per_bit = sample_rate as f64 / bits_per_second;
382
383 BitSynchronizer {
384 last_transition: None,
385 samples_per_bit: samples_per_bit as f32,
386 bit_phase: 0.0,
387 bit_clock: 0.0,
388 pll_filter: PllFilter::new(0.1),
389 }
390 }
391
392 fn process_transition(&mut self, transition: Transition) -> Option<bool> {
394 let sample_index = transition.sample_index;
395
396 if let Some(last_idx) = self.last_transition {
397 let samples_since_last = (sample_index - last_idx) as f32;
398
399 let phase_error = samples_since_last - self.samples_per_bit;
401 let correction = self.pll_filter.update(phase_error);
402 self.samples_per_bit += correction;
403
404 let is_half_bit = samples_since_last < (self.samples_per_bit * 0.75);
406
407 self.last_transition = Some(sample_index);
408
409 if is_half_bit {
410 self.bit_clock = 0.5;
412 return Some(true);
413 } else {
414 self.bit_clock = 0.0;
416 return Some(false);
417 }
418 }
419
420 self.last_transition = Some(sample_index);
421 None
422 }
423
424 fn reset(&mut self) {
425 self.last_transition = None;
426 self.bit_phase = 0.0;
427 self.bit_clock = 0.0;
428 self.pll_filter.reset();
429 }
430}
431
432struct PllFilter {
434 gain: f32,
436 integrator: f32,
438}
439
440impl PllFilter {
441 fn new(gain: f32) -> Self {
442 PllFilter {
443 gain,
444 integrator: 0.0,
445 }
446 }
447
448 fn update(&mut self, phase_error: f32) -> f32 {
450 let proportional = phase_error * self.gain;
452 self.integrator += phase_error * self.gain * 0.01;
453
454 self.integrator = self.integrator.clamp(-10.0, 10.0);
456
457 proportional + self.integrator
458 }
459
460 fn reset(&mut self) {
461 self.integrator = 0.0;
462 }
463}
464
465#[allow(dead_code)]
467struct SignalFilter {
468 alpha: f32,
470 prev_value: f32,
472}
473
474impl SignalFilter {
475 #[allow(dead_code)]
476 fn new(cutoff_freq: f32, sample_rate: f32) -> Self {
477 let rc = 1.0 / (2.0 * std::f32::consts::PI * cutoff_freq);
478 let dt = 1.0 / sample_rate;
479 let alpha = dt / (rc + dt);
480
481 SignalFilter {
482 alpha,
483 prev_value: 0.0,
484 }
485 }
486
487 #[allow(dead_code)]
489 fn process(&mut self, sample: f32) -> f32 {
490 let filtered = self.alpha * sample + (1.0 - self.alpha) * self.prev_value;
491 self.prev_value = filtered;
492 filtered
493 }
494
495 #[allow(dead_code)]
496 fn reset(&mut self) {
497 self.prev_value = 0.0;
498 }
499}
500
501#[allow(dead_code)]
503struct WaveformAnalyzer {
504 rms_accumulator: f32,
506 rms_count: u32,
508 peak_positive: f32,
510 peak_negative: f32,
512}
513
514impl WaveformAnalyzer {
515 #[allow(dead_code)]
516 fn new() -> Self {
517 WaveformAnalyzer {
518 rms_accumulator: 0.0,
519 rms_count: 0,
520 peak_positive: 0.0,
521 peak_negative: 0.0,
522 }
523 }
524
525 #[allow(dead_code)]
527 fn process_sample(&mut self, sample: f32) {
528 self.rms_accumulator += sample * sample;
529 self.rms_count += 1;
530
531 if sample > self.peak_positive {
532 self.peak_positive = sample;
533 }
534 if sample < self.peak_negative {
535 self.peak_negative = sample;
536 }
537 }
538
539 #[allow(dead_code)]
541 fn get_rms(&self) -> f32 {
542 if self.rms_count > 0 {
543 (self.rms_accumulator / self.rms_count as f32).sqrt()
544 } else {
545 0.0
546 }
547 }
548
549 #[allow(dead_code)]
551 fn get_peak_to_peak(&self) -> f32 {
552 self.peak_positive - self.peak_negative
553 }
554
555 #[allow(dead_code)]
556 fn reset(&mut self) {
557 self.rms_accumulator = 0.0;
558 self.rms_count = 0;
559 self.peak_positive = 0.0;
560 self.peak_negative = 0.0;
561 }
562}
563
564#[allow(dead_code)]
566struct DropFrameCalculator;
567
568impl DropFrameCalculator {
569 #[allow(dead_code)]
571 fn is_valid_drop_frame(minutes: u8, seconds: u8, frames: u8) -> bool {
572 if seconds == 0 && frames < 2 && !minutes.is_multiple_of(10) {
574 return false;
575 }
576 true
577 }
578
579 #[allow(dead_code)]
581 fn adjust_for_drop_frame(minutes: u8, seconds: u8, frames: u8) -> (u8, u8, u8) {
582 if seconds == 0 && frames < 2 && !minutes.is_multiple_of(10) {
583 (minutes, seconds, 2)
585 } else {
586 (minutes, seconds, frames)
587 }
588 }
589}
590
591#[allow(dead_code)]
593struct ErrorCorrector {
594 history: Vec<Timecode>,
596 max_history: usize,
598}
599
600impl ErrorCorrector {
601 #[allow(dead_code)]
602 fn new(max_history: usize) -> Self {
603 ErrorCorrector {
604 history: Vec::with_capacity(max_history),
605 max_history,
606 }
607 }
608
609 #[allow(dead_code)]
611 fn add_timecode(&mut self, timecode: Timecode) {
612 self.history.push(timecode);
613 if self.history.len() > self.max_history {
614 self.history.remove(0);
615 }
616 }
617
618 #[allow(dead_code)]
620 fn correct_timecode(&self, timecode: &Timecode) -> Option<Timecode> {
621 if self.history.is_empty() {
622 return Some(*timecode);
623 }
624
625 if let Some(last) = self.history.last() {
627 let mut expected = *last;
628 if expected.increment().is_ok() {
629 if Self::is_close(timecode, &expected) {
631 return Some(*timecode);
632 }
633 }
634 }
635
636 Some(*timecode)
638 }
639
640 #[allow(dead_code)]
642 fn is_close(tc1: &Timecode, tc2: &Timecode) -> bool {
643 let diff = (tc1.to_frames() as i64 - tc2.to_frames() as i64).abs();
644 diff <= 5
645 }
646
647 #[allow(dead_code)]
648 fn reset(&mut self) {
649 self.history.clear();
650 }
651}
652
653#[allow(dead_code)]
655struct BitPatternValidator;
656
657impl BitPatternValidator {
658 #[allow(dead_code)]
660 fn validate_timecode_bits(bits: &[bool; DATA_BITS]) -> bool {
661 let frame_tens = Self::bits_to_u8(&bits[8..10]);
663 let second_tens = Self::bits_to_u8(&bits[24..27]);
664 let minute_tens = Self::bits_to_u8(&bits[40..43]);
665 let hour_tens = Self::bits_to_u8(&bits[56..58]);
666
667 if frame_tens > 5 {
669 return false;
670 }
671
672 if second_tens > 5 {
674 return false;
675 }
676
677 if minute_tens > 5 {
679 return false;
680 }
681
682 if hour_tens > 2 {
684 return false;
685 }
686
687 true
688 }
689
690 #[allow(dead_code)]
691 fn bits_to_u8(bits: &[bool]) -> u8 {
692 let mut value = 0u8;
693 for (i, &bit) in bits.iter().enumerate() {
694 if bit {
695 value |= 1 << i;
696 }
697 }
698 value
699 }
700}
701
702#[allow(dead_code)]
704struct SpeedDetector {
705 bit_periods: Vec<f32>,
707 max_history: usize,
709}
710
711impl SpeedDetector {
712 #[allow(dead_code)]
713 fn new(max_history: usize) -> Self {
714 SpeedDetector {
715 bit_periods: Vec::with_capacity(max_history),
716 max_history,
717 }
718 }
719
720 #[allow(dead_code)]
722 fn add_period(&mut self, period: f32) {
723 self.bit_periods.push(period);
724 if self.bit_periods.len() > self.max_history {
725 self.bit_periods.remove(0);
726 }
727 }
728
729 #[allow(dead_code)]
731 fn get_average_period(&self) -> Option<f32> {
732 if self.bit_periods.is_empty() {
733 return None;
734 }
735
736 let sum: f32 = self.bit_periods.iter().sum();
737 Some(sum / self.bit_periods.len() as f32)
738 }
739
740 #[allow(dead_code)]
742 fn get_speed_ratio(&self, nominal_period: f32) -> f32 {
743 if let Some(avg) = self.get_average_period() {
744 nominal_period / avg
745 } else {
746 1.0
747 }
748 }
749
750 #[allow(dead_code)]
751 fn reset(&mut self) {
752 self.bit_periods.clear();
753 }
754}
755
756#[cfg(test)]
757mod tests {
758 use super::*;
759
760 #[test]
761 fn test_decoder_creation() {
762 let decoder = LtcDecoder::new(48000, FrameRate::Fps25, 0.1);
763 assert!(!decoder.is_synchronized());
764 }
765
766 #[test]
767 fn test_zero_crossing_detector() {
768 let mut detector = ZeroCrossingDetector::new(48000, FrameRate::Fps25);
769
770 let t1 = detector.process_sample(-0.5, 0.1);
772 assert!(t1.is_none());
773
774 let t2 = detector.process_sample(0.5, 0.1);
775 assert!(t2.is_some());
776 }
777
778 #[test]
779 fn test_bits_to_u8() {
780 let decoder = LtcDecoder::new(48000, FrameRate::Fps25, 0.1);
781 let bits = [true, false, true, false]; assert_eq!(decoder.bits_to_u8(&bits), 5);
783 }
784
785 #[test]
786 fn test_drop_frame_validator() {
787 assert!(!DropFrameCalculator::is_valid_drop_frame(1, 0, 0));
788 assert!(!DropFrameCalculator::is_valid_drop_frame(1, 0, 1));
789 assert!(DropFrameCalculator::is_valid_drop_frame(1, 0, 2));
790 assert!(DropFrameCalculator::is_valid_drop_frame(10, 0, 0));
791 }
792}