use super::constants::*;
use crate::{FrameRate, Timecode, TimecodeError};
pub struct LtcEncoder {
#[allow(dead_code)]
sample_rate: u32,
#[allow(dead_code)]
frame_rate: FrameRate,
amplitude: f32,
samples_per_bit: f32,
#[allow(dead_code)]
phase: f32,
polarity: bool,
}
impl LtcEncoder {
pub fn new(sample_rate: u32, frame_rate: FrameRate, amplitude: f32) -> Self {
let fps = frame_rate.as_float();
let bits_per_second = fps * BITS_PER_FRAME as f64;
let samples_per_bit = sample_rate as f64 / bits_per_second;
LtcEncoder {
sample_rate,
frame_rate,
amplitude: amplitude.clamp(0.0, 1.0),
samples_per_bit: samples_per_bit as f32,
phase: 0.0,
polarity: false,
}
}
pub fn encode_frame(&mut self, timecode: &Timecode) -> Result<Vec<f32>, TimecodeError> {
let bits = self.timecode_to_bits(timecode)?;
let samples = self.bits_to_audio(&bits);
Ok(samples)
}
fn timecode_to_bits(
&self,
timecode: &Timecode,
) -> Result<[bool; BITS_PER_FRAME], TimecodeError> {
let mut bits = [false; BITS_PER_FRAME];
let frame_units = timecode.frames % 10;
let frame_tens = timecode.frames / 10;
let second_units = timecode.seconds % 10;
let second_tens = timecode.seconds / 10;
let minute_units = timecode.minutes % 10;
let minute_tens = timecode.minutes / 10;
let hour_units = timecode.hours % 10;
let hour_tens = timecode.hours / 10;
self.encode_bcd(&mut bits, 0, frame_units);
self.encode_nibble(&mut bits, 4, (timecode.user_bits & 0xF) as u8);
self.encode_bcd(&mut bits, 8, frame_tens);
bits[10] = timecode.frame_rate.drop_frame;
bits[11] = false;
self.encode_nibble(&mut bits, 12, ((timecode.user_bits >> 4) & 0xF) as u8);
self.encode_bcd(&mut bits, 16, second_units);
self.encode_nibble(&mut bits, 20, ((timecode.user_bits >> 8) & 0xF) as u8);
self.encode_bcd(&mut bits, 24, second_tens);
bits[27] = self.calculate_even_parity(&bits[0..27]);
self.encode_nibble(&mut bits, 28, ((timecode.user_bits >> 12) & 0xF) as u8);
self.encode_bcd(&mut bits, 32, minute_units);
self.encode_nibble(&mut bits, 36, ((timecode.user_bits >> 16) & 0xF) as u8);
self.encode_bcd(&mut bits, 40, minute_tens);
bits[43] = false;
self.encode_nibble(&mut bits, 44, ((timecode.user_bits >> 20) & 0xF) as u8);
self.encode_bcd(&mut bits, 48, hour_units);
self.encode_nibble(&mut bits, 52, ((timecode.user_bits >> 24) & 0xF) as u8);
self.encode_bcd(&mut bits, 56, hour_tens);
bits[58] = false;
self.encode_nibble(&mut bits, 59, ((timecode.user_bits >> 28) & 0xF) as u8);
bits[63] = false;
self.encode_sync_word(&mut bits);
Ok(bits)
}
fn encode_bcd(&self, bits: &mut [bool; BITS_PER_FRAME], start: usize, value: u8) {
for i in 0..4 {
if start + i < BITS_PER_FRAME {
bits[start + i] = (value & (1 << i)) != 0;
}
}
}
fn encode_nibble(&self, bits: &mut [bool; BITS_PER_FRAME], start: usize, value: u8) {
for i in 0..4 {
if start + i < BITS_PER_FRAME {
bits[start + i] = (value & (1 << i)) != 0;
}
}
}
fn calculate_even_parity(&self, bits: &[bool]) -> bool {
let count = bits.iter().filter(|&&b| b).count();
count % 2 != 0
}
fn encode_sync_word(&self, bits: &mut [bool; BITS_PER_FRAME]) {
let sync_word = SYNC_WORD;
for i in 0..SYNC_BITS {
bits[DATA_BITS + i] = (sync_word & (1 << i)) != 0;
}
}
fn bits_to_audio(&mut self, bits: &[bool; BITS_PER_FRAME]) -> Vec<f32> {
let total_samples = (self.samples_per_bit * BITS_PER_FRAME as f32) as usize;
let mut samples = Vec::with_capacity(total_samples);
for &bit in bits.iter() {
let bit_samples = self.encode_bit_bmc(bit);
samples.extend_from_slice(&bit_samples);
}
samples
}
fn encode_bit_bmc(&mut self, bit: bool) -> Vec<f32> {
let samples_per_bit = self.samples_per_bit as usize;
let mut samples = Vec::with_capacity(samples_per_bit);
if bit {
for _ in 0..(samples_per_bit / 2) {
samples.push(if self.polarity {
self.amplitude
} else {
-self.amplitude
});
}
self.polarity = !self.polarity;
for _ in (samples_per_bit / 2)..samples_per_bit {
samples.push(if self.polarity {
self.amplitude
} else {
-self.amplitude
});
}
self.polarity = !self.polarity;
} else {
for _ in 0..samples_per_bit {
samples.push(if self.polarity {
self.amplitude
} else {
-self.amplitude
});
}
self.polarity = !self.polarity;
}
samples
}
pub fn reset(&mut self) {
self.phase = 0.0;
self.polarity = false;
}
pub fn set_amplitude(&mut self, amplitude: f32) {
self.amplitude = amplitude.clamp(0.0, 1.0);
}
pub fn amplitude(&self) -> f32 {
self.amplitude
}
}
#[allow(dead_code)]
struct WaveformShaper {
rise_time: usize,
transition_progress: usize,
target_level: f32,
current_level: f32,
}
impl WaveformShaper {
#[allow(dead_code)]
fn new(sample_rate: u32, rise_time_us: f32) -> Self {
let rise_time = ((rise_time_us / 1_000_000.0) * sample_rate as f32) as usize;
WaveformShaper {
rise_time: rise_time.max(1),
transition_progress: 0,
target_level: 0.0,
current_level: 0.0,
}
}
#[allow(dead_code)]
fn set_target(&mut self, level: f32) {
if (level - self.target_level).abs() > 0.001 {
self.target_level = level;
self.transition_progress = 0;
}
}
#[allow(dead_code)]
fn next_sample(&mut self) -> f32 {
if self.transition_progress < self.rise_time {
let progress = self.transition_progress as f32 / self.rise_time as f32;
self.current_level =
self.current_level * (1.0 - progress) + self.target_level * progress;
self.transition_progress += 1;
} else {
self.current_level = self.target_level;
}
self.current_level
}
#[allow(dead_code)]
fn reset(&mut self) {
self.transition_progress = 0;
self.current_level = 0.0;
}
}
#[allow(dead_code)]
struct PreEmphasisFilter {
alpha: f32,
prev_input: f32,
prev_output: f32,
}
impl PreEmphasisFilter {
#[allow(dead_code)]
fn new(time_constant_us: f32, sample_rate: u32) -> Self {
let tc = time_constant_us / 1_000_000.0;
let dt = 1.0 / sample_rate as f32;
let alpha = tc / (tc + dt);
PreEmphasisFilter {
alpha,
prev_input: 0.0,
prev_output: 0.0,
}
}
#[allow(dead_code)]
fn process(&mut self, input: f32) -> f32 {
let output = self.alpha * (self.prev_output + input - self.prev_input);
self.prev_input = input;
self.prev_output = output;
output
}
#[allow(dead_code)]
fn reset(&mut self) {
self.prev_input = 0.0;
self.prev_output = 0.0;
}
}
#[allow(dead_code)]
struct DcBlocker {
alpha: f32,
prev_input: f32,
prev_output: f32,
}
impl DcBlocker {
#[allow(dead_code)]
fn new(cutoff_hz: f32, sample_rate: u32) -> Self {
let rc = 1.0 / (2.0 * std::f32::consts::PI * cutoff_hz);
let dt = 1.0 / sample_rate as f32;
let alpha = rc / (rc + dt);
DcBlocker {
alpha,
prev_input: 0.0,
prev_output: 0.0,
}
}
#[allow(dead_code)]
fn process(&mut self, input: f32) -> f32 {
let output = self.alpha * (self.prev_output + input - self.prev_input);
self.prev_input = input;
self.prev_output = output;
output
}
#[allow(dead_code)]
fn reset(&mut self) {
self.prev_input = 0.0;
self.prev_output = 0.0;
}
}
#[allow(dead_code)]
struct Limiter {
threshold: f32,
attack_samples: usize,
release_samples: usize,
gain_reduction: f32,
}
impl Limiter {
#[allow(dead_code)]
fn new(threshold: f32, attack_ms: f32, release_ms: f32, sample_rate: u32) -> Self {
let attack_samples = ((attack_ms / 1000.0) * sample_rate as f32) as usize;
let release_samples = ((release_ms / 1000.0) * sample_rate as f32) as usize;
Limiter {
threshold,
attack_samples: attack_samples.max(1),
release_samples: release_samples.max(1),
gain_reduction: 1.0,
}
}
#[allow(dead_code)]
fn process(&mut self, input: f32) -> f32 {
let abs_input = input.abs();
if abs_input > self.threshold {
let target_gain = self.threshold / abs_input;
let attack_coefficient = 1.0 / self.attack_samples as f32;
self.gain_reduction += (target_gain - self.gain_reduction) * attack_coefficient;
} else {
let release_coefficient = 1.0 / self.release_samples as f32;
self.gain_reduction += (1.0 - self.gain_reduction) * release_coefficient;
}
input * self.gain_reduction
}
#[allow(dead_code)]
fn reset(&mut self) {
self.gain_reduction = 1.0;
}
}
pub struct LtcFrameBuffer {
sample_rate: u32,
frame_rate: FrameRate,
amplitude: f32,
buffer: Vec<f32>,
current_timecode: Option<Timecode>,
}
impl LtcFrameBuffer {
pub fn new(sample_rate: u32, frame_rate: FrameRate, amplitude: f32) -> Self {
LtcFrameBuffer {
sample_rate,
frame_rate,
amplitude,
buffer: Vec::new(),
current_timecode: None,
}
}
pub fn set_timecode(&mut self, timecode: Timecode) {
self.current_timecode = Some(timecode);
}
pub fn generate_frame(&mut self) -> Result<Vec<f32>, TimecodeError> {
if let Some(ref mut tc) = self.current_timecode {
let mut encoder = LtcEncoder::new(self.sample_rate, self.frame_rate, self.amplitude);
let samples = encoder.encode_frame(tc)?;
tc.increment()?;
Ok(samples)
} else {
Err(TimecodeError::InvalidConfiguration)
}
}
pub fn fill_buffer(&mut self, target_samples: usize) -> Result<(), TimecodeError> {
while self.buffer.len() < target_samples {
let frame_samples = self.generate_frame()?;
self.buffer.extend_from_slice(&frame_samples);
}
Ok(())
}
pub fn read_samples(&mut self, count: usize) -> Vec<f32> {
let available = self.buffer.len().min(count);
let samples: Vec<f32> = self.buffer.drain(..available).collect();
samples
}
pub fn buffer_level(&self) -> usize {
self.buffer.len()
}
}
pub struct UserBitsEncoder;
impl UserBitsEncoder {
pub fn encode_ascii(text: &str) -> u32 {
let bytes = text.as_bytes();
let mut user_bits = 0u32;
for (i, &byte) in bytes.iter().take(4).enumerate() {
user_bits |= (byte as u32) << (i * 8);
}
user_bits
}
pub fn encode_date(month: u8, day: u8, year: u16) -> u32 {
let mut user_bits = 0u32;
user_bits |= (month / 10) as u32;
user_bits |= ((month % 10) as u32) << 4;
user_bits |= ((day / 10) as u32) << 8;
user_bits |= ((day % 10) as u32) << 12;
let year_short = (year % 100) as u8;
user_bits |= ((year_short / 10) as u32) << 16;
user_bits |= ((year_short % 10) as u32) << 20;
user_bits
}
pub fn encode_binary(data: u32) -> u32 {
data
}
}
pub struct SignalQualityMetrics {
pub peak_amplitude: f32,
pub rms_amplitude: f32,
pub crest_factor: f32,
pub dc_offset: f32,
}
impl SignalQualityMetrics {
pub fn from_samples(samples: &[f32]) -> Self {
let mut sum = 0.0;
let mut sum_squared = 0.0;
let mut peak: f32 = 0.0;
for &sample in samples {
sum += sample;
sum_squared += sample * sample;
peak = peak.max(sample.abs());
}
let dc_offset = sum / samples.len() as f32;
let rms = (sum_squared / samples.len() as f32).sqrt();
let crest_factor = if rms > 0.0 { peak / rms } else { 0.0 };
SignalQualityMetrics {
peak_amplitude: peak,
rms_amplitude: rms,
crest_factor,
dc_offset,
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_encoder_creation() {
let encoder = LtcEncoder::new(48000, FrameRate::Fps25, 0.5);
assert_eq!(encoder.amplitude(), 0.5);
}
#[test]
fn test_encode_frame() {
let mut encoder = LtcEncoder::new(48000, FrameRate::Fps25, 0.5);
let timecode = Timecode::new(1, 2, 3, 4, FrameRate::Fps25).expect("valid timecode");
let samples = encoder
.encode_frame(&timecode)
.expect("encode should succeed");
assert!(!samples.is_empty());
}
#[test]
fn test_user_bits_ascii() {
let user_bits = UserBitsEncoder::encode_ascii("TEST");
assert_ne!(user_bits, 0);
}
#[test]
fn test_user_bits_date() {
let user_bits = UserBitsEncoder::encode_date(12, 31, 2023);
assert_ne!(user_bits, 0);
}
#[test]
fn test_even_parity() {
let encoder = LtcEncoder::new(48000, FrameRate::Fps25, 0.5);
let bits = [true, false, true]; assert!(!encoder.calculate_even_parity(&bits));
let bits = [true, false, false]; assert!(encoder.calculate_even_parity(&bits));
}
}