proteus_lib/playback/player/
controls.rs1use std::sync::{Arc, Mutex};
8use std::thread;
9use std::time::{Duration, Instant};
10
11use log::{info, warn};
12
13use crate::diagnostics::reporter::{Report, Reporter};
14
15use super::{Player, PlayerState};
16
17impl Player {
18 pub fn play_at(&mut self, ts: f64) {
24 let mut timestamp = self.ts.lock().unwrap();
25 *timestamp = ts;
26 drop(timestamp);
27
28 self.request_effects_reset();
29 self.clear_inline_effects_update();
30 self.kill_current();
31 self.initialize_thread(Some(ts));
32
33 self.resume();
34
35 self.wait_for_audio_heard(Duration::from_secs(5));
36 }
37
38 pub fn play(&mut self) {
42 info!("Playing audio");
43 let thread_exists = self
44 .playback_thread_exists
45 .load(std::sync::atomic::Ordering::SeqCst);
46
47 if !thread_exists {
48 self.initialize_thread(None);
49 }
50
51 self.resume();
52
53 self.wait_for_audio_heard(Duration::from_secs(5));
54 }
55
56 pub fn pause(&self) {
58 self.state.lock().unwrap().clone_from(&PlayerState::Pausing);
59 }
60
61 pub fn resume(&self) {
63 self.state
64 .lock()
65 .unwrap()
66 .clone_from(&PlayerState::Resuming);
67 }
68
69 pub fn kill_current(&self) {
73 self.state
74 .lock()
75 .unwrap()
76 .clone_from(&PlayerState::Stopping);
77 {
78 let sink = self.sink.lock().unwrap();
79 sink.stop();
80 }
81 self.abort.store(true, std::sync::atomic::Ordering::SeqCst);
82
83 while !self.thread_finished() {
84 thread::sleep(Duration::from_millis(10));
85 }
86
87 self.state.lock().unwrap().clone_from(&PlayerState::Stopped);
88 }
89
90 pub fn stop(&self) {
92 self.kill_current();
93 self.ts.lock().unwrap().clone_from(&0.0);
94 }
95
96 pub fn is_playing(&self) -> bool {
98 let state = self.state.lock().unwrap();
99 *state == PlayerState::Playing
100 }
101
102 pub fn is_paused(&self) -> bool {
104 let state = self.state.lock().unwrap();
105 *state == PlayerState::Paused
106 }
107
108 pub fn get_time(&self) -> f64 {
110 let ts = self.ts.lock().unwrap();
111 *ts
112 }
113
114 pub(super) fn thread_finished(&self) -> bool {
116 let playback_thread_exists = self
117 .playback_thread_exists
118 .load(std::sync::atomic::Ordering::SeqCst);
119 !playback_thread_exists
120 }
121
122 pub fn is_finished(&self) -> bool {
124 self.thread_finished()
125 }
126
127 pub fn sleep_until_end(&self) {
129 loop {
130 if self.thread_finished() {
131 break;
132 }
133 thread::sleep(Duration::from_millis(100));
134 }
135 }
136
137 pub fn get_duration(&self) -> f64 {
139 let duration = self.duration.lock().unwrap();
140 *duration
141 }
142
143 pub fn seek(&mut self, ts: f64) {
152 let mut timestamp = self.ts.lock().unwrap();
153 *timestamp = ts;
154 drop(timestamp);
155
156 let state = self.state.lock().unwrap().clone();
157 let was_active = matches!(state, PlayerState::Playing | PlayerState::Resuming);
158 let (seek_fade_out_ms, seek_fade_in_ms) = {
159 let settings = self.buffer_settings.lock().unwrap();
160 (settings.seek_fade_out_ms, settings.seek_fade_in_ms)
161 };
162 if was_active && seek_fade_out_ms > 0.0 {
163 self.fade_current_sink_out(seek_fade_out_ms);
164 }
165 self.request_effects_reset();
166 self.clear_inline_effects_update();
167
168 self.kill_current();
169 self.initialize_thread(Some(ts));
170 if was_active {
171 *self.next_resume_fade_ms.lock().unwrap() = Some(seek_fade_in_ms);
172 self.resume();
173 } else {
174 self.state.lock().unwrap().clone_from(&state);
175 }
176 }
177
178 fn fade_current_sink_out(&self, fade_ms: f32) {
184 let steps = ((fade_ms / 5.0).ceil() as u32).max(1);
185 let step_ms = (fade_ms / steps as f32).max(1.0) as u64;
186 let sink = self.sink.lock().unwrap();
187 let start_volume = sink.volume().max(0.0);
188 if start_volume <= 0.0 {
189 return;
190 }
191 for step in 1..=steps {
192 let t = step as f32 / steps as f32;
193 let gain = start_volume * (1.0 - t);
194 sink.set_volume(gain.max(0.0));
195 thread::sleep(Duration::from_millis(step_ms));
196 }
197 }
198
199 pub fn refresh_tracks(&mut self) {
204 let mut prot = self.prot.lock().unwrap();
205 prot.refresh_tracks();
206 if let Some(spec) = self.impulse_response_override.clone() {
207 prot.set_impulse_response_spec(spec);
208 }
209 if let Some(tail_db) = self.impulse_response_tail_override {
210 prot.set_impulse_response_tail_db(tail_db);
211 }
212 drop(prot);
213
214 self.request_effects_reset();
215 self.clear_inline_effects_update();
216 if self.thread_finished() {
217 return;
218 }
219
220 let ts = self.get_time();
221 self.seek(ts);
222
223 if self.is_playing() {
224 self.resume();
225 }
226
227 self.wait_for_audio_heard(Duration::from_secs(5));
228 }
229
230 pub(super) fn wait_for_audio_heard(&self, timeout: Duration) -> bool {
241 let start = Instant::now();
242 loop {
243 if self.audio_heard.load(std::sync::atomic::Ordering::Relaxed) {
244 return true;
245 }
246 if self.thread_finished() {
247 warn!("playback thread ended before audio was heard");
248 return false;
249 }
250 if start.elapsed() >= timeout {
251 warn!("timed out waiting for audio to start");
252 return false;
253 }
254 thread::sleep(Duration::from_millis(10));
255 }
256 }
257
258 pub fn shuffle(&mut self) {
260 self.refresh_tracks();
261 }
262
263 pub fn set_volume(&mut self, new_volume: f32) {
269 let sink = self.sink.lock().unwrap();
270 sink.set_volume(new_volume);
271 drop(sink);
272
273 let mut volume = self.volume.lock().unwrap();
274 *volume = new_volume;
275 drop(volume);
276 }
277
278 pub fn get_volume(&self) -> f32 {
280 *self.volume.lock().unwrap()
281 }
282
283 pub fn get_ids(&self) -> Vec<String> {
285 let prot = self.prot.lock().unwrap();
286 prot.get_ids()
287 }
288
289 pub fn get_shuffle_schedule(&self) -> Vec<(f64, Vec<String>)> {
293 let prot = self.prot.lock().unwrap();
294 prot.get_shuffle_schedule()
295 }
296
297 pub fn set_reporting(
306 &mut self,
307 reporting: Arc<Mutex<dyn Fn(Report) + Send>>,
308 reporting_interval: Duration,
309 ) {
310 if self.reporter.is_some() {
311 self.reporter.as_ref().unwrap().lock().unwrap().stop();
312 }
313
314 let reporter = Arc::new(Mutex::new(Reporter::new(
315 Arc::new(Mutex::new(self.clone())),
316 reporting,
317 reporting_interval,
318 )));
319
320 reporter.lock().unwrap().start();
321
322 self.reporter = Some(reporter);
323 }
324}