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 is_synchronized(&self) -> bool {
284 self.state == DecoderState::Locked && self.sync_confidence >= 10
285 }
286
287 pub fn sync_confidence(&self) -> f32 {
289 (self.sync_confidence as f32) / 100.0
290 }
291}
292
293#[allow(dead_code)]
295struct ZeroCrossingDetector {
296 prev_sample: f32,
298 sample_count: u64,
300 samples_per_bit: f32,
302}
303
304impl ZeroCrossingDetector {
305 fn new(sample_rate: u32, frame_rate: FrameRate) -> Self {
306 let fps = frame_rate.as_float();
307 let bits_per_second = fps * BITS_PER_FRAME as f64;
308 let samples_per_bit = sample_rate as f64 / bits_per_second;
309
310 ZeroCrossingDetector {
311 prev_sample: 0.0,
312 sample_count: 0,
313 samples_per_bit: samples_per_bit as f32,
314 }
315 }
316
317 fn process_sample(&mut self, sample: f32, min_amplitude: f32) -> Option<Transition> {
319 self.sample_count += 1;
320
321 let transition = if self.prev_sample < -min_amplitude && sample >= min_amplitude {
323 Some(Transition {
324 sample_index: self.sample_count,
325 rising: true,
326 })
327 } else if self.prev_sample > min_amplitude && sample <= -min_amplitude {
328 Some(Transition {
329 sample_index: self.sample_count,
330 rising: false,
331 })
332 } else {
333 None
334 };
335
336 self.prev_sample = sample;
337 transition
338 }
339
340 fn reset(&mut self) {
341 self.prev_sample = 0.0;
342 self.sample_count = 0;
343 }
344}
345
346#[derive(Debug, Clone, Copy)]
348#[allow(dead_code)]
349struct Transition {
350 sample_index: u64,
351 rising: bool,
352}
353
354struct BitSynchronizer {
356 last_transition: Option<u64>,
358 samples_per_bit: f32,
360 bit_phase: f32,
362 bit_clock: f32,
364 pll_filter: PllFilter,
366}
367
368impl BitSynchronizer {
369 fn new(sample_rate: u32, frame_rate: FrameRate) -> Self {
370 let fps = frame_rate.as_float();
371 let bits_per_second = fps * BITS_PER_FRAME as f64;
372 let samples_per_bit = sample_rate as f64 / bits_per_second;
373
374 BitSynchronizer {
375 last_transition: None,
376 samples_per_bit: samples_per_bit as f32,
377 bit_phase: 0.0,
378 bit_clock: 0.0,
379 pll_filter: PllFilter::new(0.1),
380 }
381 }
382
383 fn process_transition(&mut self, transition: Transition) -> Option<bool> {
385 let sample_index = transition.sample_index;
386
387 if let Some(last_idx) = self.last_transition {
388 let samples_since_last = (sample_index - last_idx) as f32;
389
390 let phase_error = samples_since_last - self.samples_per_bit;
392 let correction = self.pll_filter.update(phase_error);
393 self.samples_per_bit += correction;
394
395 let is_half_bit = samples_since_last < (self.samples_per_bit * 0.75);
397
398 self.last_transition = Some(sample_index);
399
400 if is_half_bit {
401 self.bit_clock = 0.5;
403 return Some(true);
404 } else {
405 self.bit_clock = 0.0;
407 return Some(false);
408 }
409 }
410
411 self.last_transition = Some(sample_index);
412 None
413 }
414
415 fn reset(&mut self) {
416 self.last_transition = None;
417 self.bit_phase = 0.0;
418 self.bit_clock = 0.0;
419 self.pll_filter.reset();
420 }
421}
422
423struct PllFilter {
425 gain: f32,
427 integrator: f32,
429}
430
431impl PllFilter {
432 fn new(gain: f32) -> Self {
433 PllFilter {
434 gain,
435 integrator: 0.0,
436 }
437 }
438
439 fn update(&mut self, phase_error: f32) -> f32 {
441 let proportional = phase_error * self.gain;
443 self.integrator += phase_error * self.gain * 0.01;
444
445 self.integrator = self.integrator.clamp(-10.0, 10.0);
447
448 proportional + self.integrator
449 }
450
451 fn reset(&mut self) {
452 self.integrator = 0.0;
453 }
454}
455
456#[allow(dead_code)]
458struct SignalFilter {
459 alpha: f32,
461 prev_value: f32,
463}
464
465impl SignalFilter {
466 #[allow(dead_code)]
467 fn new(cutoff_freq: f32, sample_rate: f32) -> Self {
468 let rc = 1.0 / (2.0 * std::f32::consts::PI * cutoff_freq);
469 let dt = 1.0 / sample_rate;
470 let alpha = dt / (rc + dt);
471
472 SignalFilter {
473 alpha,
474 prev_value: 0.0,
475 }
476 }
477
478 #[allow(dead_code)]
480 fn process(&mut self, sample: f32) -> f32 {
481 let filtered = self.alpha * sample + (1.0 - self.alpha) * self.prev_value;
482 self.prev_value = filtered;
483 filtered
484 }
485
486 #[allow(dead_code)]
487 fn reset(&mut self) {
488 self.prev_value = 0.0;
489 }
490}
491
492#[allow(dead_code)]
494struct WaveformAnalyzer {
495 rms_accumulator: f32,
497 rms_count: u32,
499 peak_positive: f32,
501 peak_negative: f32,
503}
504
505impl WaveformAnalyzer {
506 #[allow(dead_code)]
507 fn new() -> Self {
508 WaveformAnalyzer {
509 rms_accumulator: 0.0,
510 rms_count: 0,
511 peak_positive: 0.0,
512 peak_negative: 0.0,
513 }
514 }
515
516 #[allow(dead_code)]
518 fn process_sample(&mut self, sample: f32) {
519 self.rms_accumulator += sample * sample;
520 self.rms_count += 1;
521
522 if sample > self.peak_positive {
523 self.peak_positive = sample;
524 }
525 if sample < self.peak_negative {
526 self.peak_negative = sample;
527 }
528 }
529
530 #[allow(dead_code)]
532 fn get_rms(&self) -> f32 {
533 if self.rms_count > 0 {
534 (self.rms_accumulator / self.rms_count as f32).sqrt()
535 } else {
536 0.0
537 }
538 }
539
540 #[allow(dead_code)]
542 fn get_peak_to_peak(&self) -> f32 {
543 self.peak_positive - self.peak_negative
544 }
545
546 #[allow(dead_code)]
547 fn reset(&mut self) {
548 self.rms_accumulator = 0.0;
549 self.rms_count = 0;
550 self.peak_positive = 0.0;
551 self.peak_negative = 0.0;
552 }
553}
554
555#[allow(dead_code)]
557struct DropFrameCalculator;
558
559impl DropFrameCalculator {
560 #[allow(dead_code)]
562 fn is_valid_drop_frame(minutes: u8, seconds: u8, frames: u8) -> bool {
563 if seconds == 0 && frames < 2 && !minutes.is_multiple_of(10) {
565 return false;
566 }
567 true
568 }
569
570 #[allow(dead_code)]
572 fn adjust_for_drop_frame(minutes: u8, seconds: u8, frames: u8) -> (u8, u8, u8) {
573 if seconds == 0 && frames < 2 && !minutes.is_multiple_of(10) {
574 (minutes, seconds, 2)
576 } else {
577 (minutes, seconds, frames)
578 }
579 }
580}
581
582#[allow(dead_code)]
584struct ErrorCorrector {
585 history: Vec<Timecode>,
587 max_history: usize,
589}
590
591impl ErrorCorrector {
592 #[allow(dead_code)]
593 fn new(max_history: usize) -> Self {
594 ErrorCorrector {
595 history: Vec::with_capacity(max_history),
596 max_history,
597 }
598 }
599
600 #[allow(dead_code)]
602 fn add_timecode(&mut self, timecode: Timecode) {
603 self.history.push(timecode);
604 if self.history.len() > self.max_history {
605 self.history.remove(0);
606 }
607 }
608
609 #[allow(dead_code)]
611 fn correct_timecode(&self, timecode: &Timecode) -> Option<Timecode> {
612 if self.history.is_empty() {
613 return Some(*timecode);
614 }
615
616 if let Some(last) = self.history.last() {
618 let mut expected = *last;
619 if expected.increment().is_ok() {
620 if Self::is_close(timecode, &expected) {
622 return Some(*timecode);
623 }
624 }
625 }
626
627 Some(*timecode)
629 }
630
631 #[allow(dead_code)]
633 fn is_close(tc1: &Timecode, tc2: &Timecode) -> bool {
634 let diff = (tc1.to_frames() as i64 - tc2.to_frames() as i64).abs();
635 diff <= 5
636 }
637
638 #[allow(dead_code)]
639 fn reset(&mut self) {
640 self.history.clear();
641 }
642}
643
644#[allow(dead_code)]
646struct BitPatternValidator;
647
648impl BitPatternValidator {
649 #[allow(dead_code)]
651 fn validate_timecode_bits(bits: &[bool; DATA_BITS]) -> bool {
652 let frame_tens = Self::bits_to_u8(&bits[8..10]);
654 let second_tens = Self::bits_to_u8(&bits[24..27]);
655 let minute_tens = Self::bits_to_u8(&bits[40..43]);
656 let hour_tens = Self::bits_to_u8(&bits[56..58]);
657
658 if frame_tens > 5 {
660 return false;
661 }
662
663 if second_tens > 5 {
665 return false;
666 }
667
668 if minute_tens > 5 {
670 return false;
671 }
672
673 if hour_tens > 2 {
675 return false;
676 }
677
678 true
679 }
680
681 #[allow(dead_code)]
682 fn bits_to_u8(bits: &[bool]) -> u8 {
683 let mut value = 0u8;
684 for (i, &bit) in bits.iter().enumerate() {
685 if bit {
686 value |= 1 << i;
687 }
688 }
689 value
690 }
691}
692
693#[allow(dead_code)]
695struct SpeedDetector {
696 bit_periods: Vec<f32>,
698 max_history: usize,
700}
701
702impl SpeedDetector {
703 #[allow(dead_code)]
704 fn new(max_history: usize) -> Self {
705 SpeedDetector {
706 bit_periods: Vec::with_capacity(max_history),
707 max_history,
708 }
709 }
710
711 #[allow(dead_code)]
713 fn add_period(&mut self, period: f32) {
714 self.bit_periods.push(period);
715 if self.bit_periods.len() > self.max_history {
716 self.bit_periods.remove(0);
717 }
718 }
719
720 #[allow(dead_code)]
722 fn get_average_period(&self) -> Option<f32> {
723 if self.bit_periods.is_empty() {
724 return None;
725 }
726
727 let sum: f32 = self.bit_periods.iter().sum();
728 Some(sum / self.bit_periods.len() as f32)
729 }
730
731 #[allow(dead_code)]
733 fn get_speed_ratio(&self, nominal_period: f32) -> f32 {
734 if let Some(avg) = self.get_average_period() {
735 nominal_period / avg
736 } else {
737 1.0
738 }
739 }
740
741 #[allow(dead_code)]
742 fn reset(&mut self) {
743 self.bit_periods.clear();
744 }
745}
746
747#[cfg(test)]
748mod tests {
749 use super::*;
750
751 #[test]
752 fn test_decoder_creation() {
753 let decoder = LtcDecoder::new(48000, FrameRate::Fps25, 0.1);
754 assert!(!decoder.is_synchronized());
755 }
756
757 #[test]
758 fn test_zero_crossing_detector() {
759 let mut detector = ZeroCrossingDetector::new(48000, FrameRate::Fps25);
760
761 let t1 = detector.process_sample(-0.5, 0.1);
763 assert!(t1.is_none());
764
765 let t2 = detector.process_sample(0.5, 0.1);
766 assert!(t2.is_some());
767 }
768
769 #[test]
770 fn test_bits_to_u8() {
771 let decoder = LtcDecoder::new(48000, FrameRate::Fps25, 0.1);
772 let bits = [true, false, true, false]; assert_eq!(decoder.bits_to_u8(&bits), 5);
774 }
775
776 #[test]
777 fn test_drop_frame_validator() {
778 assert!(!DropFrameCalculator::is_valid_drop_frame(1, 0, 0));
779 assert!(!DropFrameCalculator::is_valid_drop_frame(1, 0, 1));
780 assert!(DropFrameCalculator::is_valid_drop_frame(1, 0, 2));
781 assert!(DropFrameCalculator::is_valid_drop_frame(10, 0, 0));
782 }
783}