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