mecomp_tui/state/
audio.rs1use std::time::Duration;
7
8use tokio::sync::{
9 broadcast,
10 mpsc::{UnboundedReceiver, UnboundedSender, unbounded_channel},
11};
12
13use mecomp_core::{
14 state::{Percent, StateAudio},
15 udp::StateChange,
16};
17use mecomp_prost::{
18 MusicPlayerClient, PlaybackRepeatRequest, PlaybackSeekRequest, PlaybackSkipRequest,
19 PlaybackVolumeAdjustRequest, QueueRemoveRangeRequest, QueueSetIndexRequest, RecordIdList,
20};
21
22use crate::termination::Interrupted;
23
24use super::action::{AudioAction, PlaybackAction, QueueAction, VolumeAction};
25
26pub const TICK_RATE: Duration = Duration::from_millis(100);
27
28#[derive(Debug, Clone)]
30#[allow(clippy::module_name_repetitions)]
31pub struct AudioState {
32 state_tx: UnboundedSender<StateAudio>,
33}
34
35impl AudioState {
36 #[must_use]
38 pub fn new() -> (Self, UnboundedReceiver<StateAudio>) {
39 let (state_tx, state_rx) = unbounded_channel::<StateAudio>();
40
41 (Self { state_tx }, state_rx)
42 }
43
44 pub async fn main_loop(
50 &self,
51 mut daemon: MusicPlayerClient,
52 mut action_rx: UnboundedReceiver<AudioAction>,
53 mut interrupt_rx: broadcast::Receiver<Interrupted>,
54 ) -> anyhow::Result<Interrupted> {
55 let mut state = get_state(&mut daemon).await?;
56 let mut update_needed = false;
57
58 self.state_tx.send(state.clone())?;
60
61 let mut ticker = tokio::time::interval(TICK_RATE);
63 ticker.set_missed_tick_behavior(tokio::time::MissedTickBehavior::Delay);
64
65 let mut time_last = tokio::time::Instant::now();
66
67 let result = loop {
68 tokio::select! {
69 Some(action) = action_rx.recv() => {
72 match action {
73 AudioAction::Playback(action) => handle_playback(&mut daemon, action).await?,
74 AudioAction::Queue(action) => handle_queue(&mut daemon, action).await?,
75 AudioAction::StateChange(state_change) => {
76 match state_change {
77 StateChange::Muted => state.muted = true,
78 StateChange::Unmuted => state.muted = false,
79 StateChange::VolumeChanged(volume) => state.volume = volume,
80 StateChange::TrackChanged(_) | StateChange::QueueChanged => {
81 update_needed = true;
83 },
84 StateChange::RepeatModeChanged(repeat_mode) => state.repeat_mode = repeat_mode,
85 StateChange::Seeked(seek_position) => if let Some(runtime) = &mut state.runtime {
86 runtime.seek_percent =
87 Percent::new(seek_position.as_secs_f32() / runtime.duration.as_secs_f32() * 100.0);
88 runtime.seek_position = seek_position;
89 },
90 StateChange::StatusChanged(status) => state.status = status,
91 }
92 }
93 }
94 },
95 _ = ticker.tick() => {
97 if state.paused() {
98 continue;
99 }
100 if let Some(runtime) = &mut state.runtime {
101 runtime.seek_position+= time_last.elapsed();
103 runtime.seek_percent =
104 Percent::new(runtime.seek_position.as_secs_f32() / runtime.duration.as_secs_f32() * 100.0);
105 }
106 },
107 Ok(interrupted) = interrupt_rx.recv() => {
109 break interrupted;
110 }
111 }
112 if update_needed {
113 state = get_state(&mut daemon).await?;
114 update_needed = false;
115 }
116 self.state_tx.send(state.clone())?;
117 time_last = tokio::time::Instant::now();
118 };
119
120 Ok(result)
121 }
122}
123
124async fn get_state(daemon: &mut MusicPlayerClient) -> anyhow::Result<StateAudio> {
126 Ok(daemon
127 .state_audio(())
128 .await?
129 .into_inner()
130 .state
131 .unwrap_or_default()
132 .into())
133}
134
135async fn handle_playback(
137 daemon: &mut MusicPlayerClient,
138 action: PlaybackAction,
139) -> anyhow::Result<()> {
140 match action {
141 PlaybackAction::Toggle => daemon.playback_toggle(()).await?.into_inner(),
142 PlaybackAction::Next => daemon
143 .playback_skip_forward(PlaybackSkipRequest::new(1))
144 .await?
145 .into_inner(),
146 PlaybackAction::Previous => daemon
147 .playback_skip_backward(PlaybackSkipRequest::new(1))
148 .await?
149 .into_inner(),
150 PlaybackAction::Seek(seek_type, duration) => daemon
151 .playback_seek(PlaybackSeekRequest::new(seek_type, duration))
152 .await?
153 .into_inner(),
154 PlaybackAction::Volume(VolumeAction::Increase(amount)) => daemon
155 .playback_volume_up(PlaybackVolumeAdjustRequest::new(amount))
156 .await?
157 .into_inner(),
158 PlaybackAction::Volume(VolumeAction::Decrease(amount)) => daemon
159 .playback_volume_down(PlaybackVolumeAdjustRequest::new(amount))
160 .await?
161 .into_inner(),
162 PlaybackAction::ToggleMute => daemon.playback_toggle_mute(()).await?.into_inner(),
163 }
164
165 Ok(())
166}
167
168async fn handle_queue(daemon: &mut MusicPlayerClient, action: QueueAction) -> anyhow::Result<()> {
170 match action {
171 QueueAction::Add(ids) => daemon
172 .queue_add_list(RecordIdList::new(ids))
173 .await?
174 .into_inner(),
175 QueueAction::Remove(index) => daemon
176 .queue_remove_range(QueueRemoveRangeRequest::new(index, index + 1))
177 .await?
178 .into_inner(),
179 QueueAction::SetPosition(index) => daemon
180 .queue_set_index(QueueSetIndexRequest::new(index))
181 .await?
182 .into_inner(),
183 QueueAction::Shuffle => daemon.playback_shuffle(()).await?.into_inner(),
184 QueueAction::Clear => daemon.playback_clear(()).await?.into_inner(),
185 QueueAction::SetRepeatMode(mode) => daemon
186 .playback_repeat(PlaybackRepeatRequest::new(mode))
187 .await?
188 .into_inner(),
189 }
190
191 Ok(())
192}