Skip to content
File

Blob: archive/fft-benchmark/firmware/crates/radio-core/src/playback.rs

rust92 lines
1//! Absolute playback deadlines. Late packets are skipped, never sent in a burst.
2use crate::{music::FRAME_MS, protocol::Action};
3use std::num::NonZeroU32;
4 
5const FRAME_US: u64 = FRAME_MS as u64 * 1000;
6 
7#[derive(Debug)]
8pub struct Playback {
9 frames: NonZeroU32,
10 index: u32,
11 elapsed: u64,
12 next_us: u64,
13 paused: bool,
14 skipped: u64,
15}
16 
17#[derive(Debug, Clone, Copy, PartialEq, Eq)]
18pub struct Due {
19 pub index: u32,
20 pub pts_ms: u32,
21 pub paused: bool,
22 pub spectrum: bool,
23}
24 
25impl Playback {
26 pub fn new(frames: NonZeroU32, now_us: u64) -> Self {
27 Self {
28 frames,
29 index: 0,
30 elapsed: 0,
31 next_us: now_us,
32 paused: false,
33 skipped: 0,
34 }
35 }
36 
37 /// Returns at most one packet per call; `now_us` comes from a monotonic clock.
38 pub fn due(&mut self, now_us: u64) -> Option<Due> {
39 if now_us < self.next_us {
40 return None;
41 }
42 let late = (now_us - self.next_us) / FRAME_US;
43 self.elapsed = self.elapsed.wrapping_add(late);
44 self.skipped = self.skipped.saturating_add(late);
45 if !self.paused {
46 self.advance(late);
47 }
48 self.next_us = self
49 .next_us
50 .saturating_add((late + 1).saturating_mul(FRAME_US));
51 let due = Due {
52 index: self.index,
53 pts_ms: self.elapsed.wrapping_mul(FRAME_MS as u64) as u32,
54 paused: self.paused,
55 spectrum: self.elapsed % 2 == 0,
56 };
57 self.elapsed = self.elapsed.wrapping_add(1);
58 if !self.paused {
59 self.advance(1);
60 }
61 Some(due)
62 }
63 
64 fn advance(&mut self, count: u64) {
65 self.index = ((self.index as u64 + count % self.frames.get() as u64)
66 % self.frames.get() as u64) as u32;
67 }
68 
69 pub fn apply(&mut self, action: Action) {
70 match action {
71 Action::Play => self.paused = false,
72 Action::Pause => self.paused = true,
73 Action::Restart => {
74 self.index = 0;
75 self.paused = false;
76 }
77 }
78 }
79 pub fn paused(&self) -> bool {
80 self.paused
81 }
82 pub fn position_ms(&self) -> u32 {
83 self.index * FRAME_MS
84 }
85 pub fn duration_ms(&self) -> u32 {
86 self.frames.get() * FRAME_MS
87 }
88 pub fn skipped_frames(&self) -> u64 {
89 self.skipped
90 }
91}