1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
//! This library allows for decoding linear time code audio into individual timecode frames. Encoding support is not currently offered.
//! 
//! Decoding centers around the [`LTCDecoder`] struct, representing the state of a single decoding process
//! that produces a series of [`LTCFrame`] into an internal queue. Frames can be extracted from this
//! queue using [`LTCDecoder`]'s [`IntoIterator`](https://doc.rust-lang.org/std/iter/trait.IntoIterator.html) implementation.
//! 
//! # Examples
//! 
//! ```
//! use ltc::LTCDecoder;
//! 
//! // First argument specifies "samples per frame" which should
//! // be the approximate number of audio samples that a single
//! // frame takes up. Automatically adjusted to the rate of the
//! // data internally with an exponential smoothing function.
//! let decoder = LTCDecoder::with_capacity(48000.0 / 30.0, 10);
//! 
//! let buffer: &[f32] = ...;
//! 
//! if decoder.write_samples(buffer) {
//!     println!("Decoded LTC frames!")
//! 
//!     for frame in &mut decoder {
//!         println!("Got frame with time {}", frame.format_time())
//!     }
//! }
//! 
//! ```
//! 
//! # License
//! 
//! ```text
//! Copyright (c) 2020, Danny Wensley
//! All rights reserved.
//! 
//! Redistribution and use in source and binary forms, with or without
//! modification, are permitted provided that the following conditions are met:
//!     * Redistributions of source code must retain the above copyright
//!       notice, this list of conditions and the following disclaimer.
//!     * Redistributions in binary form must reproduce the above copyright
//!       notice, this list of conditions and the following disclaimer in the
//!       documentation and/or other materials provided with the distribution.
//!     * Neither the name of the the copyright holder nor the
//!       names of its contributors may be used to endorse or promote products
//!       derived from this software without specific prior written permission.
//! 
//! THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
//! ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
//! WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
//! DISCLAIMED. IN NO EVENT SHALL Danny Wensley BE LIABLE FOR ANY
//! DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
//! (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
//! LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
//! ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
//! (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
//! SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
//! ```

use std::collections::VecDeque;
use std::fmt;

/// Represents a single LTC frame obtained from an [`LTCDecoder`].
/// 
/// This struct represents the encoded data almost 1:1, that is, no extra processing is done. The one exception is that
/// the hour, minute, second and frame are all decode for ease of use.
/// 
/// # Examples
/// 
/// ```
/// let frame: LTCFrame = ...;
/// 
/// println!("Frame time: {}", frame.format_time());
/// ```
#[derive(Debug, PartialEq, Eq)]
pub struct LTCFrame {
    pub frame: u8,
    pub second: u8,
    pub minute: u8,
    pub hour: u8,

    pub user1: u8,
    pub user2: u8,
    pub user3: u8,
    pub user4: u8,
    pub user5: u8,
    pub user6: u8,
    pub user7: u8,
    pub user8: u8,

    pub drop_frame: bool,
    pub color_frame: bool,

    pub flag27: bool,
    pub flag43: bool,
    pub flag59: bool,

    pub synchronised_externally: bool,

    pub samples_length: u32,
}

impl LTCFrame {
    /// Format the time of the frame as a string.
    pub fn format_time(&self) -> String {
        format!("{}:{}:{}:{}", self.hour, self.minute, self.second, self.frame)
    }

    /// Estimate the framerate of the frame.
    /// 
    /// By taking in a sample rate, this function uses the recorded length of the frame in samples
    /// in order to make an estimation as to the framerate that this frame was part of. Does not
    /// currently take into account drop rate timecode.
    /// 
    /// # Arguments
    /// 
    /// * `sample_rate` - The rate (in samples per second) that samples were fed to the decoder that
    ///   produced this frame. Used to make an estimation of the framerate.
    pub fn estimate_framerate(&self, sample_rate: f32) -> FramerateEstimate {
        use FramerateEstimate::*;

        match (sample_rate) / (self.samples_length as f32) {
            r if 20.0 <= r && r < 24.5 => F24,
            r if 24.5 <= r && r < 27.5 => F25,
            r if 27.5 <= r && r < 35.0 => F30,
            r => Unknown(r)
        }
    }
}

/// Represents an estimation of a framerate as produced by [`LTCFrame::estimate_framerate`]
#[derive(Debug, PartialEq)]
pub enum FramerateEstimate {
    /// 24 frames per second (film, ATSC, 2k, 4k, 6k)
    F24,
    /// 25 frames per second (PAL, SECAM, DVB, ATSC)
    F25,
    /// 30 frames per second (ATSC, lighting, audio)
    F30,

    /// The estimated frame rate was not close enough to any known rates
    Unknown(f32)
}

impl fmt::Display for FramerateEstimate {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        use FramerateEstimate::*;

        match self {
            F24 => write!(f, "24"),
            F25 => write!(f, "25"),
            F30 => write!(f, "30"),
            Unknown(r) => write!(f, "Unknown (~{})", r)
        }
    }
}

/// A decoder of LTC data. See top-level documentation for more information.
pub struct LTCDecoder {
    queue: VecDeque<LTCFrame>,

    samples_per_period: f32,
    change_rate_boundary: u32,

    current_state: bool,
    samples_since_change: u32,
    samples_decoded: u32,

    recognised_half_cycle: bool,

    bit_buffer: u128,

    lock_in: LockIn
}

enum LockIn {
    None,
    Forward,
}

const BITS_PER_FRAME: u8 = 80;
const SIGNAL_THRESHOLD: f32 = 0.0;

const SYNC_WORD_FORWARD: u16 = 0b0011_1111_1111_1101;

impl LTCDecoder {
    /// Initialises a new decoder.
    /// 
    /// # Arguments
    /// 
    /// * `samples_per_frame` - An approximate length of each frame based on the sample rate
    ///   and the expected framerate. Should theoretically be fine as long as it is within
    ///   30% of the actual value, as it is continuously adjusted with an exponential
    ///   smoothing function at the end of each state-change.
    /// * `queue` - An initial value for the internal queue. You likely want to supply an
    ///   empty `VecDeque` here.
    pub fn new(samples_per_frame: f32, queue: VecDeque<LTCFrame>) -> LTCDecoder {
        let samples_per_period = samples_per_frame / BITS_PER_FRAME as f32;

        LTCDecoder {
            queue: queue,
            
            samples_per_period: samples_per_period,
            change_rate_boundary: ((samples_per_period * 3.0) / 4.0) as u32,

            current_state: false,
            samples_since_change: std::u32::MAX,
            samples_decoded: 0,

            recognised_half_cycle: false,

            bit_buffer: 0,

            lock_in: LockIn::None,
        }
    }

    /// Initialises a decoder with a specific queue capacity.
    pub fn with_capacity(samples_per_frame: f32, capacity: usize) -> LTCDecoder {
        Self::new(samples_per_frame, VecDeque::with_capacity(capacity))
    }

    /// Writes a new block of samples to the decoder.
    /// 
    /// Return value indicates whether the invocation has caused new frames to be added to the internal queue.
    /// These can be then extracted by using `LTCDecoder`'s `IntoIterator` implementation to iterate over the
    /// decoder.
    /// 
    /// This does not ever store a reference *or* copy of any of the samples.
    pub fn write_samples(&mut self, samples: &[f32]) -> bool {
        let mut parsed_frames = false;
        
        for sample in samples {
            let new_state = *sample >= SIGNAL_THRESHOLD;

            if self.current_state == new_state {
                // We've not had a state change
                // Increase time since last change
                if self.samples_since_change < std::u32::MAX / 2 {
                    self.samples_since_change += 1;
                }
            }else{
                // We've had a state change
                // Store the new state
                self.current_state = new_state;

                // If it's been more than four times a cycle length, ignore the change and reset the decoder, assuming dropped data
                if self.samples_since_change > (self.samples_per_period * 4.0) as u32 {
                    self.samples_since_change = 0;
                    self.samples_decoded = 0;
                    self.recognised_half_cycle = false;
                    self.bit_buffer = 0;
                    self.lock_in = LockIn::None;

                    continue;
                }

                // Decide if it's been a half-bit cycle or a full-bit cycle since the last change
                let is_half_cycle = self.samples_since_change < self.change_rate_boundary;

                // ---- Update the change_rate_boundary

                // First calculate the implied cycle length based on number of samples since previous state change
                let current_cycle_length =  if is_half_cycle {
                    self.samples_since_change * 2
                }else{
                    self.samples_since_change
                };

                // Then update the samples_per_period based on an exponential smoothing function
                self.samples_per_period = (self.samples_per_period * 3.0 + current_cycle_length as f32) / 4.0;

                // Finally update change_rate_boundary based on the new value of samples_per_period
                self.change_rate_boundary = ((self.samples_per_period * 3.0) / 4.0) as u32;

                // Make note of how many samples we just decoded
                self.samples_decoded += self.samples_since_change;

                // Also reset the counter so that we know we're at the start of a new state
                self.samples_since_change = 0;

                // ---- Interpret the state transition

                if is_half_cycle {
                    // The just-detected state-change represents the end of a half-cycle
                    // Is this already halfway through a cycle
                    if self.recognised_half_cycle {
                        // The just-detected state-change is the second in a pair representing a 1
                        // Shift the buffer and add the one
                        self.bit_buffer = (self.bit_buffer << 1) + 1;
                        // Reset the flag
                        self.recognised_half_cycle = false;
                    }else{
                        // The just-detected state-change must be through the middle of a bit. Set a flag to remember it.
                        self.recognised_half_cycle = true;

                        // We don't want to try and parse the buffer, so continue
                        continue;
                    }
                }else{
                    // The just detected state-change represents the end of a full cycle
                    // Sanity check if we were halfway through a cycle
                    if self.recognised_half_cycle {
                        // We thought we were halfway through a cycle, but we must've been off! Ignore the change and reset the decoder.

                        self.samples_decoded = 0;
                        self.recognised_half_cycle = false;
                        self.bit_buffer = 0;
                        self.lock_in = LockIn::None;

                        continue;
                    }else{
                        // We've just recognised a full-cycle state period, meaning we should decode a 0
                        // Shift buffer to the left by 1 bit, automatically filling in a zero
                        self.bit_buffer <<= 1;
                    }
                }

                // If we've got to here, then we've just successfully decoded a bit
                // Delegate in order to attempt parsing an LTC frame!

                parsed_frames |= self.try_parse_frame();
            }
        }

        parsed_frames
    }

    fn try_parse_frame(&mut self) -> bool {
        let latest_word = (self.bit_buffer & 0xFF_FF) as u16;
        if latest_word == SYNC_WORD_FORWARD {
            // We've just seen the sync word moving forwards
            // Check whether we're locked in
            match self.lock_in {
                LockIn::Forward => {
                    // We're locked in, meaning we can successfully decode a full LTC frame!

                    let hour =
                        if self.bit_buffer >> 22 & 0x1 > 0 { 20 } else { 0 } +
                        if self.bit_buffer >> 23 & 0x1 > 0 { 10 } else { 0 } +
                        if self.bit_buffer >> 28 & 0x1 > 0 { 8 } else { 0 } +
                        if self.bit_buffer >> 29 & 0x1 > 0 { 4 } else { 0 } +
                        if self.bit_buffer >> 30 & 0x1 > 0 { 2 } else { 0 } +
                        (self.bit_buffer >> 31 & 0x1) as u8;
                    let minute =
                        if self.bit_buffer >> 37 & 0x1 > 0 { 40 } else { 0 } +
                        if self.bit_buffer >> 38 & 0x1 > 0 { 20 } else { 0 } +
                        if self.bit_buffer >> 39 & 0x1 > 0 { 10 } else { 0 } +
                        if self.bit_buffer >> 44 & 0x1 > 0 { 8 } else { 0 } +
                        if self.bit_buffer >> 45 & 0x1 > 0 { 4 } else { 0 } +
                        if self.bit_buffer >> 46 & 0x1 > 0 { 2 } else { 0 } +
                        (self.bit_buffer >> 47 & 0x1) as u8;
                    let second =
                        if self.bit_buffer >> 53 & 0x1 > 0 { 40 } else { 0 } +
                        if self.bit_buffer >> 54 & 0x1 > 0 { 20 } else { 0 } +
                        if self.bit_buffer >> 55 & 0x1 > 0 { 10 } else { 0 } +
                        if self.bit_buffer >> 60 & 0x1 > 0 { 8 } else { 0 } +
                        if self.bit_buffer >> 61 & 0x1 > 0 { 4 } else { 0 } +
                        if self.bit_buffer >> 62 & 0x1 > 0 { 2 } else { 0 } +
                        (self.bit_buffer >> 63 & 0x1) as u8;
                    let frame =
                        if self.bit_buffer >> 70 & 0x1 > 0 { 20 } else { 0 } +
                        if self.bit_buffer >> 71 & 0x1 > 0 { 10 } else { 0 } +
                        if self.bit_buffer >> 76 & 0x1 > 0 { 8 } else { 0 } +
                        if self.bit_buffer >> 77 & 0x1 > 0 { 4 } else { 0 } +
                        if self.bit_buffer >> 78 & 0x1 > 0 { 2 } else { 0 } +
                        (self.bit_buffer >> 79 & 0x1) as u8;

                    self.queue.push_back(LTCFrame {
                        hour, minute, second, frame,

                        user1: Self::reverse_user_data((self.bit_buffer >> 72) as u8 & 0xF),

                        drop_frame: self.bit_buffer >> 69 & 0x1 > 0,
                        color_frame: self.bit_buffer >> 68 & 0x1 > 0,

                        user2: Self::reverse_user_data((self.bit_buffer >> 64) as u8 & 0xF),
                        user3: Self::reverse_user_data((self.bit_buffer >> 56) as u8 & 0xF),

                        flag27: self.bit_buffer >> 52 & 0x1 > 0,

                        user4: Self::reverse_user_data((self.bit_buffer >> 48) as u8 & 0xF),
                        user5: Self::reverse_user_data((self.bit_buffer >> 40) as u8 & 0xF),

                        flag43: self.bit_buffer >> 36 & 0x1 > 0,

                        user6: Self::reverse_user_data((self.bit_buffer >> 32) as u8 & 0xF),
                        user7: Self::reverse_user_data((self.bit_buffer >> 24) as u8 & 0xF),
                        
                        synchronised_externally: self.bit_buffer >> 21 & 0x1 > 0,
                        flag59: self.bit_buffer >> 20 & 0x1 > 0,

                        user8: Self::reverse_user_data((self.bit_buffer >> 16) as u8 & 0xF),

                        samples_length: self.samples_decoded
                    });

                    // We've just decoded a frame, reset number of samples decoded
                    self.samples_decoded = 0;

                    return true
                },
                _ => {
                    // We're not currently locked in, but we can lock in forwards as we've just seen the sync word
                    self.lock_in = LockIn::Forward;

                    // We've not decoded anything, but we've essentially discarded all current data,
                    // so we should still reset the counter
                    self.samples_decoded = 0;

                    return false
                }
            }
        }else{
            // We can't see the sync word. Likely just means we're in the middle of a frame.
            // TODO: Potentially check for a backward sync word?
            return false
        }
    }

    fn reverse_user_data(data: u8) -> u8 {
        return if data & 0b0001 > 0 { 0b1000 } else { 0 } +
            if data & 0b0010 > 0 { 0b0100 } else { 0 } +
            if data & 0b0100 > 0 { 0b0010 } else { 0 } +
            if data & 0b1000 > 0 { 0b0001 } else { 0 }
    }
}

/// Converts a mutable reference to a decoder into an iterator.
/// 
/// Mutability is needed as any items returned from the iterator are removed
/// from the internal queue.
impl<'a> IntoIterator for &'a mut LTCDecoder {
    type Item = LTCFrame;
    type IntoIter = LTCDecoderIter<'a>;

    fn into_iter(self) -> LTCDecoderIter<'a> {
        LTCDecoderIter {
            queue: &mut self.queue
        }
    }
}

/// Represents an iterator of the internal queue of an [`LTCDecoder`].
/// 
/// Any items returned from iterating over this are removed from the queue
/// of the parent LTCDecoder.
/// 
/// The lifetime `'a` represents the decoder this iterator is derived from.
pub struct LTCDecoderIter<'a> {
    queue: &'a mut VecDeque<LTCFrame>
}

impl<'a> Iterator for LTCDecoderIter<'a> {
    type Item = LTCFrame;

    fn next(&mut self) -> Option<LTCFrame> {
        self.queue.pop_front()
    }
}