1#![allow(clippy::module_name_repetitions)]
2
3use std::{
4 fs::File,
5 io::BufReader,
6 ops::Range,
7 sync::{
8 atomic::AtomicBool,
9 mpsc::{Receiver, Sender},
10 Arc, Mutex,
11 },
12 time::Duration,
13};
14
15use log::{debug, error};
16use rodio::{source::SeekError, Decoder, Source};
17use tracing::instrument;
18
19use crate::{
20 errors::LibraryError,
21 format_duration,
22 state::{Percent, SeekType, StateAudio, StateRuntime, Status},
23 udp::StateChange,
24};
25use mecomp_storage::db::schemas::song::Song;
26use one_or_many::OneOrMany;
27
28pub mod commands;
29pub mod queue;
30
31use commands::{AudioCommand, QueueCommand, VolumeCommand};
32use queue::Queue;
33
34const DURATION_WATCHER_TICK_MS: u64 = 50;
35const DURATION_WATCHER_NEXT_SONG_THRESHOLD_MS: u64 = 100;
36
37const MIN_VOLUME: f32 = 0.0;
39const MAX_VOLUME: f32 = 10.0;
41
42#[derive(Debug, Clone)]
43pub struct AudioKernelSender {
44 tx: Sender<(AudioCommand, tracing::Span)>,
45}
46
47impl AudioKernelSender {
48 #[must_use]
59 pub fn start(event_tx: Sender<StateChange>) -> Arc<Self> {
60 let (tx, rx) = std::sync::mpsc::channel();
61 let tx_clone = tx.clone();
62 std::thread::Builder::new()
63 .name(String::from("Audio Kernel"))
64 .spawn(move || {
65 let kernel = AudioKernel::new(event_tx);
66 kernel.init(tx_clone, rx);
67 })
68 .unwrap();
69 Arc::new(Self::new(tx))
70 }
71
72 #[must_use]
73 pub(crate) const fn new(tx: Sender<(AudioCommand, tracing::Span)>) -> Self {
74 Self { tx }
75 }
76
77 #[instrument(skip(self))]
79 pub fn send(&self, command: AudioCommand) {
80 let ctx =
81 tracing::info_span!("Sending Audio Command to Kernel", command = ?command).or_current();
82
83 if let Err(e) = self.tx.send((command, ctx)) {
84 error!("Failed to send command to audio kernel: {e}");
85 panic!("Failed to send command to audio kernel: {e}");
86 }
87 }
88}
89
90#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Default)]
91struct DurationInfo {
92 time_played: Duration,
93 current_duration: Duration,
94}
95
96pub(crate) struct AudioKernel {
97 #[cfg(not(feature = "mock_playback"))]
99 _music_output: (rodio::OutputStream, rodio::OutputStreamHandle),
100 #[cfg(feature = "mock_playback")]
101 queue_rx_end_tx: tokio::sync::oneshot::Sender<()>,
102 player: Arc<rodio::Sink>,
106 queue: Arc<Mutex<Queue>>,
108 volume: Arc<Mutex<f32>>,
110 muted: Arc<AtomicBool>,
112 duration_info: Arc<Mutex<DurationInfo>>,
114 status: Arc<Mutex<Status>>,
116 event_tx: Sender<StateChange>,
118}
119
120impl AudioKernel {
121 #[must_use]
127 #[cfg(not(feature = "mock_playback"))]
128 pub fn new(event_tx: Sender<StateChange>) -> Self {
129 let (stream, stream_handle) = rodio::OutputStream::try_default().unwrap();
130
131 let sink = rodio::Sink::try_new(&stream_handle).unwrap();
132 sink.pause();
133 let queue = Queue::new();
134
135 Self {
136 _music_output: (stream, stream_handle),
137 player: sink.into(),
138 queue: Arc::new(Mutex::new(queue)),
139 volume: Arc::new(Mutex::new(1.0)),
140 muted: Arc::new(AtomicBool::new(false)),
141 duration_info: Arc::new(Mutex::new(DurationInfo::default())),
142 status: Arc::new(Mutex::new(Status::Stopped)),
143 event_tx,
144 }
145 }
146
147 #[must_use]
155 #[cfg(feature = "mock_playback")]
156 pub fn new(event_tx: Sender<StateChange>) -> Self {
157 let (sink, mut queue_rx) = rodio::Sink::new_idle();
158
159 let (tx, rx) = tokio::sync::oneshot::channel();
161
162 std::thread::spawn(move || {
163 tokio::runtime::Builder::new_current_thread()
165 .enable_time()
166 .build()
167 .unwrap()
168 .block_on(async {
169 tokio::select! {
170 _ = rx => {},
171 () = async {
172 loop {
173 queue_rx.next();
174 tokio::time::sleep(std::time::Duration::from_millis(1)).await;
175 }
176 } => {},
177 }
178 });
179 });
180
181 sink.pause();
182
183 Self {
184 player: sink.into(),
185 queue_rx_end_tx: tx,
186 queue: Arc::new(Mutex::new(Queue::new())),
187 volume: Arc::new(Mutex::new(1.0)),
188 muted: Arc::new(AtomicBool::new(false)),
189 duration_info: Arc::new(Mutex::new(DurationInfo::default())),
190 status: Arc::new(Mutex::new(Status::Stopped)),
191 event_tx,
192 }
193 }
194
195 pub fn init(
211 self,
212 tx: Sender<(AudioCommand, tracing::Span)>,
213 rx: Receiver<(AudioCommand, tracing::Span)>,
214 ) {
215 let (dw_tx, dw_rx) = tokio::sync::oneshot::channel();
217
218 let duration_info = self.duration_info.clone();
220 let status = self.status.clone();
221
222 let _duration_watcher = std::thread::Builder::new().name(String::from("Duration Watcher")).spawn(move || {
226 let sleep_time = std::time::Duration::from_millis(DURATION_WATCHER_TICK_MS);
227 let duration_threshold =
228 std::time::Duration::from_millis(DURATION_WATCHER_NEXT_SONG_THRESHOLD_MS);
229
230 tokio::runtime::Builder::new_current_thread()
231 .enable_time()
232 .build()
233 .unwrap()
234 .block_on(async {
235 log::info!("Duration Watcher started");
236 tokio::select! {
237 _ = dw_rx => {},
238 () = async {
239 loop {
240 tokio::time::sleep(sleep_time).await;
241 let mut duration_info = duration_info.lock().unwrap();
242 if *status.lock().unwrap() == Status::Playing {
243 duration_info.time_played += sleep_time;
245 if duration_info.time_played >= duration_info.current_duration.saturating_sub(duration_threshold) {
247 if let Err(e) = tx.send((AudioCommand::Queue(QueueCommand::PlayNextSong), tracing::Span::current())) {
248 error!("Failed to send command to audio kernel: {e}");
249 panic!("Failed to send command to audio kernel: {e}");
250 }
251 }
252 }
253 }
254 } => {},
255 }
256 });
257 });
258
259 for (command, ctx) in rx {
260 let _guard = ctx.enter();
261
262 let prev_status = *self.status.lock().unwrap();
263
264 match command {
265 AudioCommand::Play => {
266 self.play();
267 }
268 AudioCommand::Pause => {
269 self.pause();
270 }
271 AudioCommand::TogglePlayback => {
272 self.toggle_playback();
273 }
274 AudioCommand::RestartSong => {
275 self.restart_song();
276 let _ = self
277 .event_tx
278 .send(StateChange::Seeked(Duration::from_secs(0)));
279 }
280 AudioCommand::ClearPlayer => {
281 self.clear_player();
282 }
283 AudioCommand::Queue(command) => self.queue_control(command),
284 AudioCommand::Exit => break,
285 AudioCommand::ReportStatus(tx) => {
286 let state = self.state();
287
288 if let Err(e) = tx.send(state) {
289 error!("Audio Kernel failed to send state to the receiver, state receiver likely has been dropped. State: {e}");
292 break;
293 }
294 }
295 AudioCommand::Volume(command) => self.volume_control(command),
296 AudioCommand::Seek(seek, duration) => {
297 self.seek(seek, duration);
298 let _ = self.event_tx.send(StateChange::Seeked(
299 self.duration_info.lock().unwrap().time_played,
300 ));
301 }
302 AudioCommand::Stop if prev_status != Status::Stopped => {
303 self.stop();
304 let _ = self
305 .event_tx
306 .send(StateChange::Seeked(Duration::from_secs(0)));
307 }
308 AudioCommand::Stop => {}
309 }
310
311 let new_status = *self.status.lock().unwrap();
312
313 if prev_status != new_status {
314 let _ = self.event_tx.send(StateChange::StatusChanged(new_status));
315 }
316 }
317
318 #[cfg(feature = "mock_playback")]
319 self.queue_rx_end_tx.send(()).unwrap();
320 dw_tx.send(()).unwrap();
321 }
322
323 #[instrument(skip(self))]
324 fn play(&self) {
325 if self.player.empty() {
326 return;
327 }
328 self.player.play();
329 *self.status.lock().unwrap() = Status::Playing;
330 }
331
332 #[instrument(skip(self))]
333 fn pause(&self) {
334 self.player.pause();
335 *self.status.lock().unwrap() = Status::Paused;
336 }
337
338 #[instrument(skip(self))]
339 fn stop(&self) {
340 self.player.pause();
341 self.seek(SeekType::Absolute, Duration::from_secs(0));
342 *self.status.lock().unwrap() = Status::Stopped;
343 }
344
345 #[instrument(skip(self))]
346 fn toggle_playback(&self) {
347 if self.player.is_paused() {
348 self.play();
349 } else {
350 self.pause();
351 }
352 }
353
354 #[instrument(skip(self))]
355 fn restart_song(&self) {
356 let status = *self.status.lock().unwrap();
357 self.clear_player();
358
359 if let Some(song) = self.queue.lock().unwrap().current_song() {
360 if let Err(e) = self.append_song_to_player(song) {
361 error!("Failed to append song to player: {}", e);
362 }
363
364 match status {
365 Status::Stopped => {}
367 Status::Paused => self.pause(),
369 Status::Playing => self.play(),
371 }
372 }
373 }
374
375 #[instrument(skip(self))]
376 fn clear(&self) {
377 self.clear_player();
378 self.queue.lock().unwrap().clear();
379 }
380
381 #[instrument(skip(self))]
382 fn clear_player(&self) {
383 self.player.clear();
384 *self.status.lock().unwrap() = Status::Stopped;
385 *self.duration_info.lock().unwrap() = DurationInfo::default();
386 }
387
388 #[instrument(skip(self))]
389 fn queue_control(&self, command: QueueCommand) {
390 let prev_song = self.queue.lock().unwrap().current_song().cloned();
391 match command {
392 QueueCommand::Clear => self.clear(),
393 QueueCommand::PlayNextSong => self.start_next_song(),
394 QueueCommand::SkipForward(n) => self.skip_forward(n),
395 QueueCommand::SkipBackward(n) => self.skip_backward(n),
396 QueueCommand::SetPosition(n) => self.set_position(n),
397 QueueCommand::Shuffle => self.queue.lock().unwrap().shuffle(),
398 QueueCommand::AddToQueue(song_box) => match *song_box {
399 OneOrMany::None => {}
400 OneOrMany::One(song) => self.add_song_to_queue(song),
401 OneOrMany::Many(songs) => self.add_songs_to_queue(songs),
402 },
403 QueueCommand::RemoveRange(range) => self.remove_range_from_queue(range),
404 QueueCommand::SetRepeatMode(mode) => {
405 self.queue.lock().unwrap().set_repeat_mode(mode);
406 let _ = self.event_tx.send(StateChange::RepeatModeChanged(mode));
407 }
408 }
409
410 let new_song = self.queue.lock().unwrap().current_song().cloned();
411
412 if prev_song != new_song {
413 let _ = self
414 .event_tx
415 .send(StateChange::TrackChanged(new_song.map(|s| s.id.into())));
416 }
417 }
418
419 #[instrument(skip(self))]
420 fn state(&self) -> StateAudio {
421 let queue = self.queue.lock().unwrap();
422 let queue_position = queue.current_index();
423 let current_song = queue.current_song().cloned();
424 let repeat_mode = queue.get_repeat_mode();
425 let runtime = current_song.as_ref().map(|_| {
426 let duration_info = self.duration_info.lock().unwrap();
427 let seek_position = duration_info.time_played;
428 let duration = duration_info.current_duration;
429 drop(duration_info);
430 let seek_percent =
431 Percent::new(seek_position.as_secs_f32() / duration.as_secs_f32() * 100.0);
432 StateRuntime {
433 seek_position,
434 seek_percent,
435 duration,
436 }
437 });
438 let status = *self.status.lock().unwrap();
439 let status = if self.player.is_paused() {
440 debug_assert!(matches!(status, Status::Paused | Status::Stopped));
441 status
442 } else {
443 debug_assert_eq!(status, Status::Playing);
444 Status::Playing
445 };
446
447 let muted = self.muted.load(std::sync::atomic::Ordering::Relaxed);
448 let volume = *self.volume.lock().unwrap();
449
450 let queued_songs = queue.queued_songs();
451 drop(queue);
452
453 StateAudio {
454 queue: queued_songs,
455 queue_position,
456 current_song,
457 repeat_mode,
458 runtime,
459 status,
460 muted,
461 volume,
462 }
463 }
464
465 #[instrument(skip(self))]
466 fn start_next_song(&self) {
467 self.clear_player();
468 let next_song = self.queue.lock().unwrap().next_song().cloned();
469
470 if let Some(song) = next_song {
471 if let Err(e) = self.append_song_to_player(&song) {
472 error!("Failed to append song to player: {e}");
473 }
474
475 let binding = self.queue.lock().unwrap();
476 if binding.get_repeat_mode().is_all() || binding.current_index().is_some() {
479 self.play();
480 }
481 }
482 }
483
484 #[instrument(skip(self))]
485 fn skip_forward(&self, n: usize) {
486 let status = *self.status.lock().unwrap();
487 self.clear_player();
488
489 let next_song = self.queue.lock().unwrap().skip_forward(n).cloned();
490
491 if let Some(song) = next_song {
492 if let Err(e) = self.append_song_to_player(&song) {
493 error!("Failed to append song to player: {}", e);
494 }
495
496 let binding = self.queue.lock().unwrap();
497 match status {
498 Status::Paused => self.pause(),
499 Status::Playing
502 if binding.get_repeat_mode().is_all() || binding.current_index().is_some() =>
503 {
504 self.play();
505 }
506 _ => {}
507 }
508 }
509 }
510
511 #[instrument(skip(self))]
512 fn skip_backward(&self, n: usize) {
513 let status = *self.status.lock().unwrap();
514 self.clear_player();
515
516 let next_song = self.queue.lock().unwrap().skip_backward(n).cloned();
517
518 if let Some(song) = next_song {
519 if let Err(e) = self.append_song_to_player(&song) {
520 error!("Failed to append song to player: {}", e);
521 }
522 match status {
523 Status::Stopped => {}
524 Status::Paused => self.pause(),
525 Status::Playing => self.play(),
526 }
527 }
528 }
529
530 #[instrument(skip(self))]
531 fn set_position(&self, n: usize) {
532 let status = *self.status.lock().unwrap();
533 self.clear_player();
534
535 let mut binding = self.queue.lock().unwrap();
536 binding.set_current_index(n);
537 let next_song = binding.current_song().cloned();
538 drop(binding);
539
540 if let Some(song) = next_song {
541 if let Err(e) = self.append_song_to_player(&song) {
542 error!("Failed to append song to player: {e}");
543 }
544
545 match status {
546 Status::Stopped => {}
547 Status::Paused => self.pause(),
548 Status::Playing => self.play(),
549 }
550 }
551 }
552
553 #[instrument(skip(self))]
554 fn add_song_to_queue(&self, song: Song) {
555 self.queue.lock().unwrap().add_song(song);
556
557 if self.player.empty() {
559 let current_index = self.queue.lock().unwrap().current_index();
560
561 if let Some(song) =
562 current_index.map_or_else(|| self.get_next_song(), |_| self.get_current_song())
563 {
564 if let Err(e) = self.append_song_to_player(&song) {
565 error!("Failed to append song to player: {e}");
566 }
567 self.play();
568 }
569 }
570 }
571
572 #[instrument(skip(self))]
573 fn add_songs_to_queue(&self, songs: Vec<Song>) {
574 self.queue.lock().unwrap().add_songs(songs);
575
576 if self.player.empty() {
578 let current_index = self.queue.lock().unwrap().current_index();
579
580 if let Some(song) =
581 current_index.map_or_else(|| self.get_next_song(), |_| self.get_current_song())
582 {
583 if let Err(e) = self.append_song_to_player(&song) {
584 error!("Failed to append song to player: {e}");
585 }
586 self.play();
587 }
588 }
589 }
590
591 #[instrument(skip(self))]
592 fn remove_range_from_queue(&self, range: Range<usize>) {
593 let current_to_be_removed = self
595 .queue
596 .lock()
597 .unwrap()
598 .current_index()
599 .is_some_and(|current_index| range.contains(¤t_index));
600
601 self.queue.lock().unwrap().remove_range(range);
602
603 if current_to_be_removed {
605 self.clear_player();
606 if let Some(song) = self.get_current_song() {
607 if let Err(e) = self.append_song_to_player(&song) {
608 error!("Failed to append song to player: {e}");
609 }
610 }
611 }
612 }
613
614 #[instrument(skip(self))]
615 fn get_current_song(&self) -> Option<Song> {
616 self.queue.lock().unwrap().current_song().cloned()
617 }
618
619 #[instrument(skip(self))]
620 fn get_next_song(&self) -> Option<Song> {
621 self.queue.lock().unwrap().next_song().cloned()
622 }
623
624 #[instrument(skip(self, source))]
625 fn append_to_player<T>(&self, source: T)
626 where
627 T: Source<Item = f32> + Send + 'static,
628 {
629 if let Some(duration) = source.total_duration() {
630 *self.duration_info.lock().unwrap() = DurationInfo {
631 time_played: Duration::from_secs(0),
632 current_duration: duration,
633 };
634 }
635 self.player.append(source);
636 }
637
638 #[instrument(skip(self))]
639 fn append_song_to_player(&self, song: &Song) -> Result<(), LibraryError> {
640 let source = Decoder::new(BufReader::new(File::open(&song.path)?))?.convert_samples();
641 *self.duration_info.lock().unwrap() = DurationInfo {
642 time_played: Duration::from_secs(0),
643 current_duration: song.runtime,
644 };
645 self.append_to_player(source);
646
647 Ok(())
648 }
649
650 #[instrument(skip(self))]
651 fn volume_control(&self, command: VolumeCommand) {
652 match command {
653 VolumeCommand::Up(percent) => {
654 let mut volume = self.volume.lock().unwrap();
655 let percent = (*volume + percent).clamp(MIN_VOLUME, MAX_VOLUME);
656 if (*volume - percent).abs() > 0.0001 {
657 *volume = percent;
658 let _ = self.event_tx.send(StateChange::VolumeChanged(*volume));
659 }
660 }
661 VolumeCommand::Down(percent) => {
662 let mut volume = self.volume.lock().unwrap();
663 let percent = (*volume - percent).clamp(MIN_VOLUME, MAX_VOLUME);
664 if (*volume - percent).abs() > 0.0001 {
665 *volume = percent;
666 let _ = self.event_tx.send(StateChange::VolumeChanged(*volume));
667 }
668 }
669 VolumeCommand::Set(percent) => {
670 let mut volume = self.volume.lock().unwrap();
671 let percent = percent.clamp(MIN_VOLUME, MAX_VOLUME);
672 if (*volume - percent).abs() > 0.0001 {
673 *volume = percent;
674 let _ = self.event_tx.send(StateChange::VolumeChanged(*volume));
675 }
676 }
677 VolumeCommand::Mute => {
678 self.muted.store(true, std::sync::atomic::Ordering::Relaxed);
679 let _ = self.event_tx.send(StateChange::Muted);
680 }
681 VolumeCommand::Unmute => {
682 self.muted
683 .store(false, std::sync::atomic::Ordering::Relaxed);
684 let _ = self.event_tx.send(StateChange::Unmuted);
685 }
686 VolumeCommand::ToggleMute => {
687 self.muted.store(
688 !self.muted.load(std::sync::atomic::Ordering::Relaxed),
689 std::sync::atomic::Ordering::Relaxed,
690 );
691 if self.muted.load(std::sync::atomic::Ordering::Relaxed) {
692 let _ = self.event_tx.send(StateChange::Muted);
693 } else {
694 let _ = self.event_tx.send(StateChange::Unmuted);
695 }
696 }
697 }
698
699 if self.muted.load(std::sync::atomic::Ordering::Relaxed) {
700 self.player.set_volume(0.0);
701 } else {
702 self.player
703 .set_volume(self.volume.lock().unwrap().to_owned());
704 }
705 }
706
707 #[instrument(skip(self))]
708 fn seek(&self, seek: SeekType, duration: Duration) {
709 let mut duration_info = self.duration_info.lock().unwrap();
711 let new_time = match seek {
713 SeekType::Absolute => duration,
714 SeekType::RelativeForwards => duration_info.time_played.saturating_add(duration),
715 SeekType::RelativeBackwards => duration_info.time_played.saturating_sub(duration),
716 };
717 let new_time = if new_time > duration_info.current_duration {
718 duration_info.current_duration
719 } else if new_time < Duration::from_secs(0) {
720 Duration::from_secs(0)
721 } else {
722 new_time
723 };
724
725 match self.player.try_seek(new_time) {
729 Ok(()) => {
730 debug!("Seek to {} successful", format_duration(&new_time));
731 duration_info.time_played = new_time;
732 drop(duration_info);
733 let mut status = self.status.lock().unwrap();
734 if new_time > Duration::from_secs(0) && *status == Status::Stopped {
735 *status = Status::Paused;
736 drop(status);
737 }
738 }
739 Err(SeekError::NotSupported { underlying_source }) => {
740 error!("Seek not supported by source: {underlying_source}");
741 }
742 Err(err) => {
743 error!("Seeking failed with error: {err}");
744 }
745 }
746 }
747}
748
749impl Default for AudioKernel {
750 fn default() -> Self {
752 let (tx, _) = std::sync::mpsc::channel();
753 Self::new(tx)
754 }
755}
756
757#[cfg(test)]
758mod tests {
759 use pretty_assertions::assert_eq;
760 use rstest::{fixture, rstest};
761
762 use crate::test_utils::init;
763
764 use super::*;
765 use std::sync::mpsc;
766 use std::time::Duration;
767
768 #[fixture]
769 fn audio_kernel() -> AudioKernel {
770 AudioKernel::default()
771 }
772
773 #[fixture]
774 fn audio_kernel_sender() -> Arc<AudioKernelSender> {
775 let (tx, _) = mpsc::channel();
776 AudioKernelSender::start(tx)
777 }
778
779 async fn get_state(sender: Arc<AudioKernelSender>) -> StateAudio {
780 let (tx, rx) = tokio::sync::oneshot::channel::<StateAudio>();
781 sender.send(AudioCommand::ReportStatus(tx));
782 rx.await.unwrap()
783 }
784
785 #[fixture]
786 fn sound() -> impl Source<Item = f32> + Send + 'static {
787 rodio::source::SineWave::new(440.0)
788 }
789
790 #[test]
791 fn test_audio_kernel_sender_send() {
792 let (tx, rx) = mpsc::channel();
793 let sender = AudioKernelSender::new(tx);
794 sender.send(AudioCommand::Play);
795 let (recv, _) = rx.recv().unwrap();
796 assert_eq!(recv, AudioCommand::Play);
797 }
798
799 #[test]
800 #[should_panic]
801 fn test_audio_kernel_send_closed_channel() {
802 let (tx, _) = mpsc::channel();
803 let sender = AudioKernelSender::new(tx);
804 sender.send(AudioCommand::Play);
805 }
806
807 #[rstest]
808 #[timeout(Duration::from_secs(3))] fn test_audio_player_kernel_spawn_and_exit(
810 #[from(audio_kernel_sender)] sender: Arc<AudioKernelSender>,
811 ) {
812 init();
813
814 sender.send(AudioCommand::Exit);
815 }
816
817 #[rstest]
818 fn test_volume_control(audio_kernel: AudioKernel) {
819 audio_kernel.volume_control(VolumeCommand::Up(0.1));
820 assert_eq!(*audio_kernel.volume.lock().unwrap(), 1.1);
821
822 audio_kernel.volume_control(VolumeCommand::Down(0.1));
823 assert_eq!(*audio_kernel.volume.lock().unwrap(), 1.0);
824
825 audio_kernel.volume_control(VolumeCommand::Set(0.5));
826 assert_eq!(*audio_kernel.volume.lock().unwrap(), 0.5);
827
828 audio_kernel.volume_control(VolumeCommand::Mute);
829 assert_eq!(
830 audio_kernel
831 .muted
832 .load(std::sync::atomic::Ordering::Relaxed),
833 true
834 );
835
836 audio_kernel.volume_control(VolumeCommand::Unmute);
837 assert_eq!(
838 audio_kernel
839 .muted
840 .load(std::sync::atomic::Ordering::Relaxed),
841 false
842 );
843
844 audio_kernel.volume_control(VolumeCommand::ToggleMute);
845 assert_eq!(
846 audio_kernel
847 .muted
848 .load(std::sync::atomic::Ordering::Relaxed),
849 true
850 );
851
852 audio_kernel.volume_control(VolumeCommand::ToggleMute);
853 assert_eq!(
854 audio_kernel
855 .muted
856 .load(std::sync::atomic::Ordering::Relaxed),
857 false
858 );
859 }
860
861 mod playback_tests {
862 use mecomp_storage::test_utils::{arb_song_case, create_song_metadata, init_test_database};
867 use pretty_assertions::assert_eq;
868 use rstest::rstest;
869
870 use crate::test_utils::init;
871
872 use super::{super::*, audio_kernel, audio_kernel_sender, get_state, sound};
873
874 #[rstest]
875 fn test_audio_kernel_play_pause(
876 audio_kernel: AudioKernel,
877 sound: impl Source<Item = f32> + Send + 'static,
878 ) {
879 audio_kernel.player.append(sound);
880 audio_kernel.play();
881 assert!(!audio_kernel.player.is_paused());
882 audio_kernel.pause();
883 assert!(audio_kernel.player.is_paused());
884 }
885
886 #[rstest]
887 fn test_audio_kernel_toggle_playback(
888 audio_kernel: AudioKernel,
889 sound: impl Source<Item = f32> + Send + 'static,
890 ) {
891 audio_kernel.player.append(sound);
892 audio_kernel.play();
893 assert!(!audio_kernel.player.is_paused());
894 audio_kernel.toggle_playback();
895 assert!(audio_kernel.player.is_paused());
896 audio_kernel.toggle_playback();
897 assert!(!audio_kernel.player.is_paused());
898 }
899
900 #[rstest]
901 #[timeout(Duration::from_secs(5))] #[tokio::test]
903 async fn test_play_pause_toggle_restart(
904 #[from(audio_kernel_sender)] sender: Arc<AudioKernelSender>,
905 ) {
906 init();
907 let db = init_test_database().await.unwrap();
908 let tempdir = tempfile::tempdir().unwrap();
909
910 let song = Song::try_load_into_db(
911 &db,
912 create_song_metadata(&tempdir, arb_song_case()()).unwrap(),
913 )
914 .await
915 .unwrap();
916
917 sender.send(AudioCommand::Queue(QueueCommand::AddToQueue(Box::new(
918 OneOrMany::One(song.clone()),
919 ))));
920
921 let state = get_state(sender.clone()).await;
922 assert_eq!(state.queue_position, Some(0));
923 assert_eq!(state.status, Status::Playing);
924
925 sender.send(AudioCommand::Pause);
926 let state = get_state(sender.clone()).await;
927 assert_eq!(state.status, Status::Paused);
928
929 sender.send(AudioCommand::Play);
930 let state = get_state(sender.clone()).await;
931 assert_eq!(state.status, Status::Playing);
932
933 sender.send(AudioCommand::RestartSong);
934 let state = get_state(sender.clone()).await;
935 assert_eq!(state.status, Status::Playing); sender.send(AudioCommand::TogglePlayback);
938 let state = get_state(sender.clone()).await;
939 assert_eq!(state.status, Status::Paused);
940
941 sender.send(AudioCommand::RestartSong);
942 let state = get_state(sender.clone()).await;
943 assert_eq!(state.status, Status::Paused); sender.send(AudioCommand::Exit);
946 }
947
948 #[rstest]
949 fn test_audio_kernel_stop(audio_kernel: AudioKernel) {
950 init();
951 audio_kernel.player.append(sound());
952 audio_kernel.play();
953 assert!(!audio_kernel.player.is_paused());
954 audio_kernel.stop();
955 assert!(audio_kernel.player.is_paused());
956 assert_eq!(
957 audio_kernel.duration_info.lock().unwrap().time_played,
958 Duration::from_secs(0)
959 );
960 assert_eq!(*audio_kernel.status.lock().unwrap(), Status::Stopped);
961 }
962
963 #[rstest]
964 #[timeout(Duration::from_secs(5))] #[tokio::test]
966 async fn test_audio_kernel_skip_forward(audio_kernel: AudioKernel) {
967 init();
968 let db = init_test_database().await.unwrap();
969 let tempdir = tempfile::tempdir().unwrap();
970
971 let state = audio_kernel.state();
972 assert_eq!(state.queue_position, None);
973 assert!(state.paused());
974 assert_eq!(state.status, Status::Stopped);
975
976 audio_kernel.queue_control(QueueCommand::AddToQueue(Box::new(OneOrMany::Many(vec![
977 Song::try_load_into_db(
978 &db,
979 create_song_metadata(&tempdir, arb_song_case()()).unwrap(),
980 )
981 .await
982 .unwrap(),
983 Song::try_load_into_db(
984 &db,
985 create_song_metadata(&tempdir, arb_song_case()()).unwrap(),
986 )
987 .await
988 .unwrap(),
989 Song::try_load_into_db(
990 &db,
991 create_song_metadata(&tempdir, arb_song_case()()).unwrap(),
992 )
993 .await
994 .unwrap(),
995 ]))));
996
997 let state = audio_kernel.state();
999 assert_eq!(state.queue_position, Some(0));
1000 assert!(!state.paused());
1001 assert_eq!(state.status, Status::Playing);
1002
1003 audio_kernel.queue_control(QueueCommand::SkipForward(1));
1004
1005 let state = audio_kernel.state();
1007 assert_eq!(state.queue_position, Some(1));
1008 assert!(!state.paused());
1009 assert_eq!(state.status, Status::Playing);
1010
1011 audio_kernel.queue_control(QueueCommand::SkipForward(1));
1012
1013 let state = audio_kernel.state();
1015 assert_eq!(state.queue_position, Some(2));
1016 assert!(!state.paused());
1017 assert_eq!(state.status, Status::Playing);
1018
1019 audio_kernel.queue_control(QueueCommand::SkipForward(1));
1020
1021 let state = audio_kernel.state();
1023 assert_eq!(state.queue_position, None);
1024 assert!(state.paused());
1025 assert_eq!(state.status, Status::Stopped);
1026 }
1027
1028 #[rstest]
1029 #[timeout(Duration::from_secs(6))] #[tokio::test]
1031 async fn test_audio_kernel_skip_forward_sender(
1032 #[from(audio_kernel_sender)] sender: Arc<AudioKernelSender>,
1033 ) {
1034 init();
1036
1037 let db = init_test_database().await.unwrap();
1038 let tempdir = tempfile::tempdir().unwrap();
1039
1040 let state = get_state(sender.clone()).await;
1041 assert_eq!(state.queue_position, None);
1042 assert!(state.paused());
1043 assert_eq!(state.status, Status::Stopped);
1044
1045 sender.send(AudioCommand::Queue(QueueCommand::AddToQueue(Box::new(
1046 OneOrMany::Many(vec![
1047 Song::try_load_into_db(
1048 &db,
1049 create_song_metadata(&tempdir, arb_song_case()()).unwrap(),
1050 )
1051 .await
1052 .unwrap(),
1053 Song::try_load_into_db(
1054 &db,
1055 create_song_metadata(&tempdir, arb_song_case()()).unwrap(),
1056 )
1057 .await
1058 .unwrap(),
1059 Song::try_load_into_db(
1060 &db,
1061 create_song_metadata(&tempdir, arb_song_case()()).unwrap(),
1062 )
1063 .await
1064 .unwrap(),
1065 ]),
1066 ))));
1067 let state = get_state(sender.clone()).await;
1069 assert_eq!(state.queue_position, Some(0));
1070 assert!(!state.paused());
1071 assert_eq!(state.status, Status::Playing);
1072
1073 sender.send(AudioCommand::Queue(QueueCommand::SkipForward(1)));
1074 let state = get_state(sender.clone()).await;
1076 assert_eq!(state.queue_position, Some(1));
1077 assert!(!state.paused());
1078 assert_eq!(state.status, Status::Playing);
1079
1080 sender.send(AudioCommand::Queue(QueueCommand::SkipForward(1)));
1081 let state = get_state(sender.clone()).await;
1083 assert_eq!(state.queue_position, Some(2));
1084 assert!(!state.paused());
1085 assert_eq!(state.status, Status::Playing);
1086
1087 sender.send(AudioCommand::Queue(QueueCommand::SkipForward(1)));
1088 let state = get_state(sender.clone()).await;
1090 assert_eq!(state.queue_position, None);
1091 assert!(state.paused());
1092 assert_eq!(state.status, Status::Stopped);
1093
1094 sender.send(AudioCommand::Exit);
1095 }
1096
1097 #[rstest]
1098 #[timeout(Duration::from_secs(5))] #[tokio::test]
1100 async fn test_remove_range_from_queue(
1101 #[from(audio_kernel_sender)] sender: Arc<AudioKernelSender>,
1102 ) {
1103 init();
1104 let db = init_test_database().await.unwrap();
1105 let tempdir = tempfile::tempdir().unwrap();
1106 let song1 = Song::try_load_into_db(
1107 &db,
1108 create_song_metadata(&tempdir, arb_song_case()()).unwrap(),
1109 )
1110 .await
1111 .unwrap();
1112 let song2 = Song::try_load_into_db(
1113 &db,
1114 create_song_metadata(&tempdir, arb_song_case()()).unwrap(),
1115 )
1116 .await
1117 .unwrap();
1118
1119 sender.send(AudioCommand::Queue(QueueCommand::AddToQueue(Box::new(
1121 OneOrMany::Many(vec![song1.clone(), song2.clone()]),
1122 ))));
1123 let state = get_state(sender.clone()).await;
1124 assert_eq!(state.queue_position, Some(0));
1125 assert!(!state.paused());
1126 assert_eq!(state.status, Status::Playing);
1127
1128 sender.send(AudioCommand::Pause);
1130
1131 sender.send(AudioCommand::Queue(QueueCommand::RemoveRange(0..1)));
1133 let state = get_state(sender.clone()).await;
1134 assert_eq!(state.queue_position, Some(0));
1135 assert!(state.paused());
1136 assert_eq!(state.status, Status::Stopped);
1137 assert_eq!(state.queue.len(), 1);
1138 assert_eq!(state.queue[0], song2);
1139
1140 sender.send(AudioCommand::Play);
1142
1143 sender.send(AudioCommand::Queue(QueueCommand::AddToQueue(Box::new(
1145 OneOrMany::One(song1.clone()),
1146 ))));
1147 let state = get_state(sender.clone()).await;
1148 assert_eq!(state.queue_position, Some(0));
1149 assert!(!state.paused());
1150 assert_eq!(state.status, Status::Playing);
1151 assert_eq!(state.queue.len(), 2);
1152 assert_eq!(state.queue[0], song2);
1153 assert_eq!(state.queue[1], song1);
1154
1155 sender.send(AudioCommand::Queue(QueueCommand::RemoveRange(1..2)));
1157 let state = get_state(sender.clone()).await;
1158 assert_eq!(state.queue_position, Some(0));
1159 assert!(!state.paused());
1160 assert_eq!(state.status, Status::Playing);
1161 assert_eq!(state.queue.len(), 1);
1162 assert_eq!(state.queue[0], song2);
1163
1164 sender.send(AudioCommand::Exit);
1165 }
1166
1167 #[rstest]
1168 #[timeout(Duration::from_secs(10))] #[tokio::test]
1170 async fn test_audio_kernel_skip_backward(
1171 #[from(audio_kernel_sender)] sender: Arc<AudioKernelSender>,
1172 ) {
1173 init();
1174 let db = init_test_database().await.unwrap();
1175 let tempdir = tempfile::tempdir().unwrap();
1176
1177 let state = get_state(sender.clone()).await;
1178 assert_eq!(state.queue_position, None);
1179 assert!(state.paused());
1180 assert_eq!(state.status, Status::Stopped);
1181
1182 sender.send(AudioCommand::Queue(QueueCommand::AddToQueue(Box::new(
1183 OneOrMany::Many(vec![
1184 Song::try_load_into_db(
1185 &db,
1186 create_song_metadata(&tempdir, arb_song_case()()).unwrap(),
1187 )
1188 .await
1189 .unwrap(),
1190 Song::try_load_into_db(
1191 &db,
1192 create_song_metadata(&tempdir, arb_song_case()()).unwrap(),
1193 )
1194 .await
1195 .unwrap(),
1196 Song::try_load_into_db(
1197 &db,
1198 create_song_metadata(&tempdir, arb_song_case()()).unwrap(),
1199 )
1200 .await
1201 .unwrap(),
1202 ]),
1203 ))));
1204
1205 let state = get_state(sender.clone()).await;
1207 assert_eq!(state.queue_position, Some(0));
1208 assert!(!state.paused());
1209 assert_eq!(state.status, Status::Playing);
1210
1211 sender.send(AudioCommand::Queue(QueueCommand::SkipForward(2)));
1212
1213 let state = get_state(sender.clone()).await;
1215 assert_eq!(state.queue_position, Some(2));
1216 assert!(!state.paused());
1217 assert_eq!(state.status, Status::Playing);
1218
1219 sender.send(AudioCommand::Queue(QueueCommand::SkipBackward(1)));
1220
1221 let state = get_state(sender.clone()).await;
1223 assert_eq!(state.queue_position, Some(1));
1224 assert!(!state.paused());
1225 assert_eq!(state.status, Status::Playing);
1226
1227 sender.send(AudioCommand::Queue(QueueCommand::SkipBackward(1)));
1228
1229 let state = get_state(sender.clone()).await;
1231 assert_eq!(state.queue_position, Some(0));
1232 assert!(!state.paused());
1233
1234 sender.send(AudioCommand::Queue(QueueCommand::SkipBackward(1)));
1235
1236 let state = get_state(sender.clone()).await;
1238 assert_eq!(state.queue_position, None);
1239 assert!(state.paused());
1240 assert_eq!(state.status, Status::Stopped);
1241
1242 sender.send(AudioCommand::Exit);
1243 }
1244
1245 #[rstest]
1246 #[timeout(Duration::from_secs(10))] #[tokio::test]
1248 async fn test_audio_kernel_set_position(
1249 #[from(audio_kernel_sender)] sender: Arc<AudioKernelSender>,
1250 ) {
1251 init();
1252 let db = init_test_database().await.unwrap();
1253 let tempdir = tempfile::tempdir().unwrap();
1254
1255 let state = get_state(sender.clone()).await;
1256 assert_eq!(state.queue_position, None);
1257 assert!(state.paused());
1258 assert_eq!(state.status, Status::Stopped);
1259
1260 sender.send(AudioCommand::Queue(QueueCommand::AddToQueue(Box::new(
1261 OneOrMany::Many(vec![
1262 Song::try_load_into_db(
1263 &db,
1264 create_song_metadata(&tempdir, arb_song_case()()).unwrap(),
1265 )
1266 .await
1267 .unwrap(),
1268 Song::try_load_into_db(
1269 &db,
1270 create_song_metadata(&tempdir, arb_song_case()()).unwrap(),
1271 )
1272 .await
1273 .unwrap(),
1274 Song::try_load_into_db(
1275 &db,
1276 create_song_metadata(&tempdir, arb_song_case()()).unwrap(),
1277 )
1278 .await
1279 .unwrap(),
1280 ]),
1281 ))));
1282 let state = get_state(sender.clone()).await;
1284 assert_eq!(state.queue_position, Some(0));
1285 assert!(!state.paused());
1286 assert_eq!(state.status, Status::Playing);
1287
1288 sender.send(AudioCommand::Queue(QueueCommand::SetPosition(1)));
1289 let state = get_state(sender.clone()).await;
1291 assert_eq!(state.queue_position, Some(1));
1292 assert!(!state.paused());
1293 assert_eq!(state.status, Status::Playing);
1294
1295 sender.send(AudioCommand::Queue(QueueCommand::SetPosition(2)));
1296 let state = get_state(sender.clone()).await;
1298 assert_eq!(state.queue_position, Some(2));
1299 assert!(!state.paused());
1300 assert_eq!(state.status, Status::Playing);
1301
1302 sender.send(AudioCommand::Queue(QueueCommand::SetPosition(0)));
1303 let state = get_state(sender.clone()).await;
1305 assert_eq!(state.queue_position, Some(0));
1306 assert!(!state.paused());
1307 assert_eq!(state.status, Status::Playing);
1308
1309 sender.send(AudioCommand::Queue(QueueCommand::SetPosition(3)));
1310 let state = get_state(sender.clone()).await;
1312 assert_eq!(state.queue_position, Some(2));
1313 assert!(!state.paused());
1314 assert_eq!(state.status, Status::Playing);
1315
1316 sender.send(AudioCommand::Exit);
1317 }
1318
1319 #[rstest]
1320 #[timeout(Duration::from_secs(5))] #[tokio::test]
1322 async fn test_audio_kernel_clear(
1323 #[from(audio_kernel_sender)] sender: Arc<AudioKernelSender>,
1324 ) {
1325 init();
1326 let db = init_test_database().await.unwrap();
1327 let tempdir = tempfile::tempdir().unwrap();
1328
1329 let state = get_state(sender.clone()).await;
1330 assert_eq!(state.queue_position, None);
1331 assert!(state.paused());
1332 assert_eq!(state.status, Status::Stopped);
1333
1334 sender.send(AudioCommand::Queue(QueueCommand::AddToQueue(Box::new(
1335 OneOrMany::Many(vec![
1336 Song::try_load_into_db(
1337 &db,
1338 create_song_metadata(&tempdir, arb_song_case()()).unwrap(),
1339 )
1340 .await
1341 .unwrap(),
1342 Song::try_load_into_db(
1343 &db,
1344 create_song_metadata(&tempdir, arb_song_case()()).unwrap(),
1345 )
1346 .await
1347 .unwrap(),
1348 Song::try_load_into_db(
1349 &db,
1350 create_song_metadata(&tempdir, arb_song_case()()).unwrap(),
1351 )
1352 .await
1353 .unwrap(),
1354 ]),
1355 ))));
1356 let state = get_state(sender.clone()).await;
1358 assert_eq!(state.queue_position, Some(0));
1359 assert_eq!(state.queue.len(), 3);
1360 assert!(!state.paused());
1361 assert_eq!(state.status, Status::Playing);
1362
1363 sender.send(AudioCommand::ClearPlayer);
1364 let state = get_state(sender.clone()).await;
1366 assert_eq!(state.queue_position, Some(0));
1367 assert_eq!(state.queue.len(), 3);
1368 assert!(state.paused());
1369 assert_eq!(state.status, Status::Stopped);
1370
1371 sender.send(AudioCommand::Queue(QueueCommand::Clear));
1372 let state = get_state(sender.clone()).await;
1374 assert_eq!(state.queue_position, None);
1375 assert_eq!(state.queue.len(), 0);
1376 assert!(state.paused());
1377 assert_eq!(state.status, Status::Stopped);
1378
1379 sender.send(AudioCommand::Exit);
1380 }
1381
1382 #[rstest]
1383 #[timeout(Duration::from_secs(5))] #[tokio::test]
1385 async fn test_audio_kernel_shuffle(
1386 #[from(audio_kernel_sender)] sender: Arc<AudioKernelSender>,
1387 ) {
1388 init();
1389 let db = init_test_database().await.unwrap();
1390 let tempdir = tempfile::tempdir().unwrap();
1391
1392 let state = get_state(sender.clone()).await;
1393 assert_eq!(state.queue_position, None);
1394 assert!(state.paused());
1395 assert_eq!(state.status, Status::Stopped);
1396
1397 sender.send(AudioCommand::Queue(QueueCommand::AddToQueue(Box::new(
1398 OneOrMany::Many(vec![
1399 Song::try_load_into_db(
1400 &db,
1401 create_song_metadata(&tempdir, arb_song_case()()).unwrap(),
1402 )
1403 .await
1404 .unwrap(),
1405 Song::try_load_into_db(
1406 &db,
1407 create_song_metadata(&tempdir, arb_song_case()()).unwrap(),
1408 )
1409 .await
1410 .unwrap(),
1411 Song::try_load_into_db(
1412 &db,
1413 create_song_metadata(&tempdir, arb_song_case()()).unwrap(),
1414 )
1415 .await
1416 .unwrap(),
1417 ]),
1418 ))));
1419 let state = get_state(sender.clone()).await;
1421 assert_eq!(state.queue_position, Some(0));
1422 assert_eq!(state.queue.len(), 3);
1423 assert!(!state.paused());
1424 assert_eq!(state.status, Status::Playing);
1425
1426 sender.send(AudioCommand::Queue(QueueCommand::SkipForward(1)));
1428 let state = get_state(sender.clone()).await;
1429 assert_eq!(state.queue_position, Some(1));
1430 assert_eq!(state.queue.len(), 3);
1431 assert!(!state.paused());
1432 assert_eq!(state.status, Status::Playing);
1433
1434 sender.send(AudioCommand::Queue(QueueCommand::Shuffle));
1436 let state = get_state(sender.clone()).await;
1438 assert_eq!(state.queue_position, Some(0));
1439 assert_eq!(state.queue.len(), 3);
1440 assert!(!state.paused());
1441 assert_eq!(state.status, Status::Playing);
1442
1443 sender.send(AudioCommand::Exit);
1444 }
1445
1446 #[rstest]
1447 #[timeout(Duration::from_secs(5))] #[tokio::test]
1449 async fn test_volume_commands(#[from(audio_kernel_sender)] sender: Arc<AudioKernelSender>) {
1450 init();
1451
1452 let state = get_state(sender.clone()).await;
1453 assert_eq!(state.volume, 1.0);
1454 assert!(!state.muted);
1455
1456 sender.send(AudioCommand::Volume(VolumeCommand::Up(0.1)));
1457 let state = get_state(sender.clone()).await;
1458 assert_eq!(state.volume, 1.1);
1459 assert!(!state.muted);
1460
1461 sender.send(AudioCommand::Volume(VolumeCommand::Down(0.1)));
1462 let state = get_state(sender.clone()).await;
1463 assert_eq!(state.volume, 1.0);
1464 assert!(!state.muted);
1465
1466 sender.send(AudioCommand::Volume(VolumeCommand::Set(0.5)));
1467 let state = get_state(sender.clone()).await;
1468 assert_eq!(state.volume, 0.5);
1469 assert!(!state.muted);
1470
1471 sender.send(AudioCommand::Volume(VolumeCommand::Mute));
1472 let state = get_state(sender.clone()).await;
1473 assert_eq!(state.volume, 0.5); assert!(state.muted);
1475
1476 sender.send(AudioCommand::Volume(VolumeCommand::Unmute));
1477 let state = get_state(sender.clone()).await;
1478 assert_eq!(state.volume, 0.5);
1479 assert!(!state.muted);
1480
1481 sender.send(AudioCommand::Volume(VolumeCommand::ToggleMute));
1482 let state = get_state(sender.clone()).await;
1483 assert_eq!(state.volume, 0.5);
1484 assert!(state.muted);
1485
1486 sender.send(AudioCommand::Volume(VolumeCommand::ToggleMute));
1487 let state = get_state(sender.clone()).await;
1488 assert_eq!(state.volume, 0.5);
1489 assert!(!state.muted);
1490
1491 sender.send(AudioCommand::Exit);
1492 }
1493
1494 #[rstest]
1495 #[timeout(Duration::from_secs(5))] #[tokio::test]
1497 async fn test_volume_out_of_bounds(
1498 #[from(audio_kernel_sender)] sender: Arc<AudioKernelSender>,
1499 ) {
1500 init();
1501
1502 sender.send(AudioCommand::Volume(VolumeCommand::Up(MAX_VOLUME + 0.5)));
1504 let state = get_state(sender.clone()).await;
1505 assert_eq!(state.volume, MAX_VOLUME);
1506 assert!(!state.muted);
1507 sender.send(AudioCommand::Volume(VolumeCommand::Down(
1508 MAX_VOLUME + 0.5 - MIN_VOLUME,
1509 )));
1510 let state = get_state(sender.clone()).await;
1511 assert_eq!(state.volume, MIN_VOLUME);
1512 assert!(!state.muted);
1513
1514 sender.send(AudioCommand::Volume(VolumeCommand::Set(MAX_VOLUME + 0.5)));
1516 let state = get_state(sender.clone()).await;
1517 assert_eq!(state.volume, MAX_VOLUME);
1518 assert!(!state.muted);
1519 sender.send(AudioCommand::Volume(VolumeCommand::Set(MIN_VOLUME - 0.5)));
1520 let state = get_state(sender.clone()).await;
1521 assert_eq!(state.volume, MIN_VOLUME);
1522 assert!(!state.muted);
1523
1524 sender.send(AudioCommand::Exit);
1525 }
1526
1527 #[rstest]
1528 #[timeout(Duration::from_secs(9))] #[tokio::test]
1530 async fn test_seek_commands(#[from(audio_kernel_sender)] sender: Arc<AudioKernelSender>) {
1531 init();
1532 let db = init_test_database().await.unwrap();
1533 let tempdir = tempfile::tempdir().unwrap();
1534
1535 let song = Song::try_load_into_db(
1536 &db,
1537 create_song_metadata(&tempdir, arb_song_case()()).unwrap(),
1538 )
1539 .await
1540 .unwrap();
1541
1542 sender.send(AudioCommand::Queue(QueueCommand::AddToQueue(Box::new(
1545 OneOrMany::One(song.clone()),
1546 ))));
1547 sender.send(AudioCommand::Stop);
1548 let state: StateAudio = get_state(sender.clone()).await;
1549 sender.send(AudioCommand::Seek(
1550 SeekType::Absolute,
1551 Duration::from_secs(0),
1552 ));
1553 assert_eq!(state.queue_position, Some(0));
1554 assert_eq!(state.status, Status::Stopped);
1555 assert_eq!(
1556 state.runtime.unwrap().duration,
1557 Duration::from_secs(10) + Duration::from_nanos(6)
1558 );
1559
1560 sender.send(AudioCommand::Seek(
1562 SeekType::RelativeForwards,
1563 Duration::from_secs(2),
1564 ));
1565 let state = get_state(sender.clone()).await;
1566 assert_eq!(state.runtime.unwrap().seek_position, Duration::from_secs(2));
1567 assert_eq!(state.current_song, Some(song.clone()));
1568 assert_eq!(state.status, Status::Paused);
1569
1570 sender.send(AudioCommand::Seek(
1572 SeekType::RelativeBackwards,
1573 Duration::from_secs(1),
1574 ));
1575 let state = get_state(sender.clone()).await;
1576 assert_eq!(state.runtime.unwrap().seek_position, Duration::from_secs(1));
1577 assert_eq!(state.current_song, Some(song.clone()));
1578 assert_eq!(state.status, Status::Paused);
1579
1580 sender.send(AudioCommand::Seek(
1582 SeekType::Absolute,
1583 Duration::from_secs(9),
1584 ));
1585 let state = get_state(sender.clone()).await;
1586 assert_eq!(state.runtime.unwrap().seek_position, Duration::from_secs(9));
1587 assert_eq!(state.current_song, Some(song.clone()));
1588 assert_eq!(state.status, Status::Paused);
1589
1590 sender.send(AudioCommand::Play);
1592 tokio::time::sleep(Duration::from_millis(1001)).await;
1593 let state = get_state(sender.clone()).await;
1594 assert_eq!(state.queue_position, None);
1595 assert_eq!(state.status, Status::Stopped);
1596
1597 sender.send(AudioCommand::Exit);
1598 }
1599 }
1600}