robot36_encoder/
encoder.rs

1use crate::image::Robot36Image;
2use core::ops::Mul;
3use num_complex::Complex;
4
5/// Encoder that will encode the image into an sampled audio.
6pub struct Encoder {
7    image: Robot36Image,
8    rate: u64,
9    y_ticks: u64,
10    uv_ticks: u64,
11    sync_porch_ticks: u64,
12    porch_ticks: u64,
13    horizontal_sync_ticks: u64,
14    seperator_ticks: u64,
15}
16
17const SYNC_PORCH_SECS: f64 = 0.003;
18const PORCH_SECS: f64 = 0.0015;
19const Y_SECS: f64 = 0.088;
20const UV_SECS: f64 = 0.044;
21const HORIZONTAL_SYNC_SECS: f64 = 0.009;
22const SEPARATOR_SECS: f64 = 0.0045;
23
24impl Encoder {
25    /// Creates a new encoder.
26    /// The rate is the sampling rate of the output audio
27    pub fn new(image: Robot36Image, rate: u64) -> Self {
28        Encoder {
29            image,
30            rate,
31            y_ticks: (rate as f64 * Y_SECS) as u64,
32            uv_ticks: (rate as f64 * UV_SECS) as u64,
33            sync_porch_ticks: (rate as f64 * SYNC_PORCH_SECS) as u64,
34            porch_ticks: (rate as f64 * PORCH_SECS) as u64,
35            horizontal_sync_ticks: (rate as f64 * HORIZONTAL_SYNC_SECS) as u64,
36            seperator_ticks: (rate as f64 * SEPARATOR_SECS) as u64,
37        }
38    }
39
40    /// Encodes the image into an iterator of i16. Where each number is
41    /// a audio sample.
42    pub fn encode(&self) -> impl Iterator<Item = i16> + '_ {
43        Signal::new(
44            ComplexOscilator::new(self.rate),
45            self.vis_header().chain(self.codificated_image()),
46        )
47    }
48
49    fn vis_header(&self) -> impl Iterator<Item = f64> + '_ {
50        let header_frequencies: Vec<(f64, f64)> = vec![
51            (self.rate as f64 * 0.3, 0.0),
52            (1900.0, 0.3),
53            (1200.0, 0.01),
54            (1900.0, 0.3),
55            (1200.0, 0.03),
56            (1300.0, 0.03),
57            (1300.0, 0.03),
58            (1300.0, 0.03),
59            (1100.0, 0.03),
60            (1300.0, 0.03),
61            (1300.0, 0.03),
62            (1300.0, 0.03),
63            (1100.0, 0.03),
64            (1200.0, 0.03),
65        ];
66        header_frequencies.into_iter().flat_map(|frequency| {
67            get_frequency_iter((self.rate as f64 * frequency.1) as u64, frequency.0)
68        })
69    }
70
71    fn codificated_image(&self) -> impl Iterator<Item = f64> + '_ {
72        (0..self.image.get_height()).step_by(2).flat_map(|y| {
73            get_frequency_iter(self.horizontal_sync_ticks, 1200.0)
74                .chain(get_frequency_iter(self.sync_porch_ticks, 1500.0))
75                .chain(self.add_y_scan(y))
76                .chain(get_frequency_iter(self.seperator_ticks, 1500.0))
77                .chain(get_frequency_iter(self.porch_ticks, 1900.0))
78                .chain(self.add_v_scan(y))
79                .chain(get_frequency_iter(self.horizontal_sync_ticks, 1200.0))
80                .chain(get_frequency_iter(self.sync_porch_ticks, 1500.0))
81                .chain(self.add_y_scan(y + 1))
82                .chain(get_frequency_iter(self.seperator_ticks, 2300.0))
83                .chain(get_frequency_iter(self.porch_ticks, 1900.0))
84                .chain(self.add_u_scan(y + 1))
85        })
86    }
87
88    fn add_y_scan(&self, y: usize) -> impl Iterator<Item = f64> + '_ {
89        (0..self.y_ticks).map(move |tick| {
90            let x = self.get_x_position(tick, self.y_ticks);
91            1500.0 + 800.0 * f64::from(self.image.get_y(x, y)) / 255.0
92        })
93    }
94
95    fn add_v_scan(&self, y: usize) -> impl Iterator<Item = f64> + '_ {
96        (0..self.uv_ticks).map(move |tick| {
97            let x0 = self.get_x_position(tick, self.uv_ticks);
98            let x1 = (x0 + 1).max(self.image.get_width() - 1);
99            let yuv_v = ((u16::from(self.image.get_v(x0, y)) + u16::from(self.image.get_v(x1, y)))
100                / 2) as f64;
101            1500.0 + 800.0 * (yuv_v / 255.0)
102        })
103    }
104
105    fn add_u_scan(&self, y: usize) -> impl Iterator<Item = f64> + '_ {
106        (0..self.uv_ticks).map(move |tick| {
107            let x0 = self.get_x_position(tick, self.uv_ticks);
108            let x1 = (x0 + 1).max(self.image.get_width() - 1);
109            let yuv_u = ((u16::from(self.image.get_u(x0, y - 1))
110                + u16::from(self.image.get_u(x1, y - 1)))
111                / 2) as f64;
112            1500.0 + 800.0 * (yuv_u / 255.0)
113        })
114    }
115
116    fn get_x_position(&self, tick: u64, ticks: u64) -> usize {
117        ((self.image.get_width() - 1) as f64 * tick as f64 / ticks as f64) as usize
118    }
119}
120
121fn get_frequency_iter(number_of_ticks: u64, frequency: f64) -> impl Iterator<Item = f64> {
122    (0..number_of_ticks).map(move |_| frequency)
123}
124
125struct ComplexOscilator {
126    complex_number: Complex<f64>,
127    hz_2_rad: f64,
128}
129
130impl ComplexOscilator {
131    pub fn new(rate: u64) -> Self {
132        Self {
133            complex_number: Complex::new(0.5, 0.5),
134            hz_2_rad: (2.0 * std::f64::consts::PI as f64) / rate as f64,
135        }
136    }
137
138    pub fn add_freq(&mut self, freq: f64) {
139        let exponetial: Complex<f64> = Complex::new(0.0, freq * self.hz_2_rad);
140        self.complex_number = self.complex_number.mul(exponetial.exp());
141    }
142}
143
144struct Signal<T>
145where
146    T: Iterator<Item = f64>,
147{
148    complex_oscilator: ComplexOscilator,
149    frequencies: T,
150}
151
152impl<T> Signal<T>
153where
154    T: Iterator<Item = f64>,
155{
156    pub fn new(complex_oscilator: ComplexOscilator, frequencies: T) -> Self {
157        Self {
158            complex_oscilator,
159            frequencies,
160        }
161    }
162}
163
164impl<T> Iterator for Signal<T>
165where
166    T: Iterator<Item = f64>,
167{
168    type Item = i16;
169    fn next(&mut self) -> Option<Self::Item> {
170        match self.frequencies.next() {
171            None => None,
172            Some(freq) => {
173                self.complex_oscilator.add_freq(freq);
174                Some((self.complex_oscilator.complex_number.re * std::i16::MAX as f64) as i16)
175            }
176        }
177    }
178}