skua_voice/driver/tasks/mixer/
state.rs

1use crate::{
2    constants::OPUS_PASSTHROUGH_STRIKE_LIMIT,
3    driver::tasks::message::*,
4    input::{Compose, Input, LiveInput, Metadata, Parsed},
5    tracks::{ReadyState, SeekRequest},
6};
7use flume::Receiver;
8use rubato::FftFixedOut;
9use std::time::Instant;
10
11pub enum InputState {
12    NotReady(Input),
13    Preparing(PreparingInfo),
14    Ready(Parsed, Option<Box<dyn Compose>>),
15}
16
17impl InputState {
18    pub fn metadata(&mut self) -> Option<Metadata<'_>> {
19        if let Self::Ready(parsed, _) = self {
20            Some(parsed.into())
21        } else {
22            None
23        }
24    }
25
26    pub fn ready_state(&self) -> ReadyState {
27        match self {
28            Self::NotReady(_) => ReadyState::Uninitialised,
29            Self::Preparing(_) => ReadyState::Preparing,
30            Self::Ready(_, _) => ReadyState::Playable,
31        }
32    }
33}
34
35impl From<Input> for InputState {
36    fn from(val: Input) -> Self {
37        match val {
38            a @ Input::Lazy(_) => Self::NotReady(a),
39            Input::Live(live, rec) => match live {
40                LiveInput::Parsed(p) => Self::Ready(p, rec),
41                other => Self::NotReady(Input::Live(other, rec)),
42            },
43        }
44    }
45}
46
47pub struct PreparingInfo {
48    #[allow(dead_code)]
49    /// Time this request was fired.
50    pub time: Instant,
51    /// Used to handle seek requests fired while a track was being created (or a seek was in progress).
52    pub queued_seek: Option<SeekRequest>,
53    /// Callback from the thread pool to indicate the result of creating/parsing this track.
54    pub callback: Receiver<MixerInputResultMessage>,
55}
56
57pub struct DecodeState {
58    pub inner_pos: usize,
59    pub resampler: Option<(usize, FftFixedOut<f32>, Vec<Vec<f32>>)>,
60    pub passthrough: Passthrough,
61    pub passthrough_violations: u8,
62}
63
64impl DecodeState {
65    pub fn reset(&mut self) {
66        self.inner_pos = 0;
67        self.resampler = None;
68    }
69
70    pub fn record_and_check_passthrough_strike_final(&mut self, fatal: bool) -> bool {
71        self.passthrough_violations = self.passthrough_violations.saturating_add(1);
72        let blocked = fatal || self.passthrough_violations > OPUS_PASSTHROUGH_STRIKE_LIMIT;
73        if blocked {
74            self.passthrough = Passthrough::Block;
75        }
76        blocked
77    }
78}
79
80impl Default for DecodeState {
81    fn default() -> Self {
82        Self {
83            inner_pos: 0,
84            resampler: None,
85            passthrough: Passthrough::Inactive,
86            passthrough_violations: 0,
87        }
88    }
89}
90
91/// Simple state to manage decoder resets etc.
92///
93/// Inactive->Active transitions should trigger a reset.
94///
95/// Block should be used if a source contains known-bad packets:
96/// it's unlikely that packet sizes will vary, but if they do then
97/// we can't passthrough (and every attempt will trigger a codec reset,
98/// which probably won't sound too smooth).
99#[derive(Clone, Copy, Eq, PartialEq)]
100pub enum Passthrough {
101    Active,
102    Inactive,
103    Block,
104}