Skip to main content

record_player/
state.rs

1use serde::{Deserialize, Serialize};
2
3use crate::mixer::DEFAULT_SHARP_CROSSFADER_WIDTH;
4
5#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
6#[serde(rename_all = "lowercase")]
7pub enum DeckId {
8    A,
9    B,
10}
11
12impl DeckId {
13    pub const ALL: [Self; 2] = [Self::A, Self::B];
14    pub const fn index(self) -> usize {
15        match self {
16            Self::A => 0,
17            Self::B => 1,
18        }
19    }
20}
21
22#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
23#[serde(rename_all = "snake_case")]
24pub enum LoadStatus {
25    Empty,
26    Loading,
27    Ready,
28    Failed,
29}
30
31#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
32#[serde(rename_all = "snake_case")]
33pub enum SurfaceRegion {
34    Programme,
35    LeadIn,
36    Deadwax,
37}
38
39#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
40pub struct PlaybackState {
41    pub load_status: LoadStatus,
42    pub duration_seconds: f64,
43    pub current_seconds: f64,
44    /// The programme PCM clock. Scratch positions are expressed in source
45    /// frames, so this must not be confused with the AudioContext output rate.
46    pub source_sample_rate: f64,
47    pub playing: bool,
48    pub pending_play_when_ready: bool,
49    pub suspended_at_seconds: Option<f64>,
50    pub playback_rate: f32,
51}
52
53impl Default for PlaybackState {
54    fn default() -> Self {
55        Self {
56            load_status: LoadStatus::Empty,
57            duration_seconds: 0.0,
58            current_seconds: 0.0,
59            source_sample_rate: 48_000.0,
60            playing: false,
61            pending_play_when_ready: false,
62            suspended_at_seconds: None,
63            playback_rate: 1.0,
64        }
65    }
66}
67
68#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
69pub struct TransportState {
70    pub motor_on: bool,
71}
72impl Default for TransportState {
73    fn default() -> Self {
74        Self { motor_on: false }
75    }
76}
77
78#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
79pub struct NeedleState {
80    pub lifted: bool,
81}
82impl Default for NeedleState {
83    fn default() -> Self {
84        Self { lifted: true }
85    }
86}
87
88#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
89pub struct MixerState {
90    pub channel_gain: f32,
91    pub crossfader: f32,
92}
93impl Default for MixerState {
94    fn default() -> Self {
95        Self {
96            channel_gain: 1.0,
97            crossfader: 0.5,
98        }
99    }
100}
101
102#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
103pub struct ScratchState {
104    pub active: bool,
105    pub pointer_id: Option<i32>,
106    pub was_playing: bool,
107    pub started_at_seconds: f64,
108    pub target_position_frames: f64,
109    pub rendered_position_frames: f64,
110    pub target_rate: f32,
111    pub grip: f32,
112    pub base_rotation_degrees: f64,
113}
114impl Default for ScratchState {
115    fn default() -> Self {
116        Self {
117            active: false,
118            pointer_id: None,
119            was_playing: false,
120            started_at_seconds: 0.0,
121            target_position_frames: 0.0,
122            rendered_position_frames: 0.0,
123            target_rate: 0.0,
124            grip: 0.0,
125            base_rotation_degrees: 0.0,
126        }
127    }
128}
129
130#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
131pub struct DeckState {
132    pub loaded: bool,
133    pub transport: TransportState,
134    pub needle: NeedleState,
135    pub playback: PlaybackState,
136    pub mixer: MixerState,
137    pub scratch: ScratchState,
138}
139impl Default for DeckState {
140    fn default() -> Self {
141        Self {
142            loaded: false,
143            transport: TransportState::default(),
144            needle: NeedleState::default(),
145            playback: PlaybackState::default(),
146            mixer: MixerState::default(),
147            scratch: ScratchState::default(),
148        }
149    }
150}
151
152#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
153pub struct TimedRegionState {
154    pub active: bool,
155    pub completed: bool,
156    pub started_at_ms: f64,
157    pub duration_ms: f64,
158}
159impl Default for TimedRegionState {
160    fn default() -> Self {
161        Self {
162            active: false,
163            completed: false,
164            started_at_ms: 0.0,
165            duration_ms: 0.0,
166        }
167    }
168}
169
170#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
171pub struct ClipLoopState {
172    pub active: bool,
173    pub clip_id: Option<String>,
174}
175impl Default for ClipLoopState {
176    fn default() -> Self {
177        Self {
178            active: false,
179            clip_id: None,
180        }
181    }
182}
183
184#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
185pub struct PlayerConfig {
186    pub sample_rate: f64,
187    pub sharp_crossfader_width: f32,
188}
189impl Default for PlayerConfig {
190    fn default() -> Self {
191        Self {
192            sample_rate: 48_000.0,
193            sharp_crossfader_width: DEFAULT_SHARP_CROSSFADER_WIDTH,
194        }
195    }
196}
197
198#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
199pub struct PlayerState {
200    pub decks: [DeckState; 2],
201    pub active_deck: DeckId,
202    pub lead_in: TimedRegionState,
203    pub deadwax: TimedRegionState,
204    pub clip_loop: ClipLoopState,
205}
206impl Default for PlayerState {
207    fn default() -> Self {
208        Self {
209            decks: [DeckState::default(), DeckState::default()],
210            active_deck: DeckId::A,
211            lead_in: TimedRegionState::default(),
212            deadwax: TimedRegionState::default(),
213            clip_loop: ClipLoopState::default(),
214        }
215    }
216}