1#![allow(clippy::module_name_repetitions)]
2
3use std::{
4 fs::File,
5 io::BufReader,
6 ops::Range,
7 sync::{
8 Arc, Mutex,
9 atomic::AtomicBool,
10 mpsc::{Receiver, Sender},
11 },
12 time::Duration,
13};
14
15use log::{debug, error};
16use rodio::{Decoder, Source, source::SeekError};
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: Duration = Duration::from_millis(50);
35const DURATION_WATCHER_NEXT_SONG_THRESHOLD: Duration = Duration::from_millis(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, mut rx) = tokio::sync::oneshot::channel();
163
164 std::thread::spawn(move || {
165 loop {
167 if matches!(
168 rx.try_recv(),
169 Ok(()) | Err(tokio::sync::oneshot::error::TryRecvError::Closed)
170 ) {
171 break;
172 }
173 queue_rx.next();
174 std::thread::sleep(Duration::from_millis(1));
175 }
176 });
177
178 sink.pause();
179
180 Self {
181 player: sink.into(),
182 queue_rx_end_tx: tx,
183 queue: Arc::new(Mutex::new(Queue::new())),
184 volume: Arc::new(Mutex::new(1.0)),
185 muted: Arc::new(AtomicBool::new(false)),
186 duration_info: Arc::new(Mutex::new(DurationInfo::default())),
187 status: Arc::new(Mutex::new(Status::Stopped)),
188 event_tx,
189 }
190 }
191
192 pub fn init(
208 self,
209 tx: Sender<(AudioCommand, tracing::Span)>,
210 rx: Receiver<(AudioCommand, tracing::Span)>,
211 ) {
212 let (dw_tx, mut dw_rx) = tokio::sync::oneshot::channel();
214
215 let duration_info = self.duration_info.clone();
218 let status = self.status.clone();
219
220 let _duration_watcher = std::thread::Builder::new()
227 .name(String::from("Duration Watcher"))
228 .spawn(move || {
229 const NEXT_SONG_COMMAND: AudioCommand =
230 AudioCommand::Queue(QueueCommand::PlayNextSong);
231
232 log::info!("Duration Watcher started");
233
234 loop {
235 if matches!(
236 dw_rx.try_recv(),
237 Ok(()) | Err(tokio::sync::oneshot::error::TryRecvError::Closed)
238 ) {
239 break;
240 }
241 let mut duration_info = duration_info.lock().unwrap();
242 if *status.lock().unwrap() == Status::Playing {
243 duration_info.time_played += DURATION_WATCHER_TICK;
245 let time_to_start_next_song = duration_info
248 .current_duration
249 .saturating_sub(DURATION_WATCHER_NEXT_SONG_THRESHOLD);
250 if duration_info.time_played >= time_to_start_next_song {
251 if let Err(e) = tx.send((NEXT_SONG_COMMAND, tracing::Span::current())) {
252 error!("Failed to send command to audio kernel: {e}");
253 panic!("Failed to send command to audio kernel: {e}");
254 }
255 }
256 }
257 drop(duration_info);
258 std::thread::sleep(DURATION_WATCHER_TICK);
259 }
260 });
261
262 for (command, ctx) in rx {
263 let _guard = ctx.enter();
264
265 let prev_status = *self.status.lock().unwrap();
266
267 match command {
268 AudioCommand::Play => self.play(),
269 AudioCommand::Pause => self.pause(),
270 AudioCommand::TogglePlayback => self.toggle_playback(),
271 AudioCommand::RestartSong => {
272 self.restart_song();
273 let _ = self
274 .event_tx
275 .send(StateChange::Seeked(Duration::from_secs(0)));
276 }
277 AudioCommand::ClearPlayer => self.clear_player(),
278 AudioCommand::Queue(command) => self.queue_control(command),
279 AudioCommand::Exit => break,
280 AudioCommand::ReportStatus(tx) => {
281 let state = self.state();
282
283 if let Err(e) = tx.send(state) {
284 error!(
289 "Audio Kernel failed to send state to the receiver, state receiver likely has been dropped. State: {e}"
290 );
291 break;
292 }
293 }
294 AudioCommand::Volume(command) => self.volume_control(command),
295 AudioCommand::Seek(seek, duration) => {
296 self.seek(seek, duration);
297 let _ = self.event_tx.send(StateChange::Seeked(
298 self.duration_info.lock().unwrap().time_played,
299 ));
300 }
301 AudioCommand::Stop if prev_status != Status::Stopped => {
302 self.stop();
303 let _ = self
304 .event_tx
305 .send(StateChange::Seeked(Duration::from_secs(0)));
306 }
307 AudioCommand::Stop => {}
308 }
309
310 let new_status = *self.status.lock().unwrap();
311
312 if prev_status != new_status {
313 let _ = self.event_tx.send(StateChange::StatusChanged(new_status));
314 }
315 }
316
317 #[cfg(feature = "mock_playback")]
318 self.queue_rx_end_tx.send(()).unwrap();
319 dw_tx.send(()).unwrap();
320 }
321
322 #[instrument(skip(self))]
323 fn play(&self) {
324 if self.player.empty() {
325 return;
326 }
327 self.player.play();
328 *self.status.lock().unwrap() = Status::Playing;
329 }
330
331 #[instrument(skip(self))]
332 fn pause(&self) {
333 self.player.pause();
334 *self.status.lock().unwrap() = Status::Paused;
335 }
336
337 #[instrument(skip(self))]
338 fn stop(&self) {
339 self.player.pause();
340 self.seek(SeekType::Absolute, Duration::from_secs(0));
341 *self.status.lock().unwrap() = Status::Stopped;
342 }
343
344 #[instrument(skip(self))]
345 fn toggle_playback(&self) {
346 if self.player.is_paused() {
347 self.play();
348 } else {
349 self.pause();
350 }
351 }
352
353 #[instrument(skip(self))]
354 fn restart_song(&self) {
355 let status = *self.status.lock().unwrap();
356 self.clear_player();
357
358 if let Some(song) = self.queue.lock().unwrap().current_song() {
359 if let Err(e) = self.append_song_to_player(song) {
360 error!("Failed to append song to player: {e}");
361 }
362
363 match status {
364 Status::Stopped => {}
366 Status::Paused => self.pause(),
368 Status::Playing => self.play(),
370 }
371 }
372 }
373
374 #[instrument(skip(self))]
375 fn clear(&self) {
376 self.clear_player();
377 self.queue.lock().unwrap().clear();
378 }
379
380 #[instrument(skip(self))]
381 fn clear_player(&self) {
382 self.player.clear();
383 *self.status.lock().unwrap() = Status::Stopped;
384 *self.duration_info.lock().unwrap() = DurationInfo::default();
385 }
386
387 #[instrument(skip(self))]
388 fn queue_control(&self, command: QueueCommand) {
389 let prev_song = self.queue.lock().unwrap().current_song().cloned();
390 match command {
391 QueueCommand::Clear => self.clear(),
392 QueueCommand::PlayNextSong => self.start_next_song(),
393 QueueCommand::SkipForward(n) => self.skip_forward(n),
394 QueueCommand::SkipBackward(n) => self.skip_backward(n),
395 QueueCommand::SetPosition(n) => self.set_position(n),
396 QueueCommand::Shuffle => self.queue.lock().unwrap().shuffle(),
397 QueueCommand::AddToQueue(song_box) => match *song_box {
398 OneOrMany::None => {}
399 OneOrMany::One(song) => self.add_song_to_queue(song),
400 OneOrMany::Many(songs) => self.add_songs_to_queue(songs),
401 },
402 QueueCommand::RemoveRange(range) => self.remove_range_from_queue(range),
403 QueueCommand::SetRepeatMode(mode) => {
404 self.queue.lock().unwrap().set_repeat_mode(mode);
405 let _ = self.event_tx.send(StateChange::RepeatModeChanged(mode));
406 }
407 }
408
409 let new_song = self.queue.lock().unwrap().current_song().cloned();
410
411 if prev_song != new_song {
412 let _ = self
413 .event_tx
414 .send(StateChange::TrackChanged(new_song.map(|s| s.id.into())));
415 }
416 }
417
418 #[instrument(skip(self))]
419 fn state(&self) -> StateAudio {
420 let queue = self.queue.lock().unwrap();
421 let queue_position = queue.current_index();
422 let current_song = queue.current_song().cloned();
423 let repeat_mode = queue.get_repeat_mode();
424 let runtime = current_song.as_ref().map(|_| {
425 let duration_info = self.duration_info.lock().unwrap();
426 let seek_position = duration_info.time_played;
427 let duration = duration_info.current_duration;
428 drop(duration_info);
429 let seek_percent =
430 Percent::new(seek_position.as_secs_f32() / duration.as_secs_f32() * 100.0);
431 StateRuntime {
432 seek_position,
433 seek_percent,
434 duration,
435 }
436 });
437 let status = *self.status.lock().unwrap();
438 let status = if self.player.is_paused() {
439 debug_assert!(matches!(status, Status::Paused | Status::Stopped));
440 status
441 } else {
442 debug_assert_eq!(status, Status::Playing);
443 Status::Playing
444 };
445
446 let muted = self.muted.load(std::sync::atomic::Ordering::Relaxed);
447 let volume = *self.volume.lock().unwrap();
448
449 let queued_songs = queue.queued_songs();
450 drop(queue);
451
452 StateAudio {
453 queue: queued_songs,
454 queue_position,
455 current_song,
456 repeat_mode,
457 runtime,
458 status,
459 muted,
460 volume,
461 }
462 }
463
464 #[instrument(skip(self))]
465 fn start_next_song(&self) {
466 self.clear_player();
467 let next_song = self.queue.lock().unwrap().next_song().cloned();
468
469 if let Some(song) = next_song {
470 if let Err(e) = self.append_song_to_player(&song) {
471 error!("Failed to append song to player: {e}");
472 }
473
474 let binding = self.queue.lock().unwrap();
475 if binding.get_repeat_mode().is_all() || binding.current_index().is_some() {
478 self.play();
479 }
480 }
481 }
482
483 #[instrument(skip(self))]
484 fn skip_forward(&self, n: usize) {
485 let status = *self.status.lock().unwrap();
486 self.clear_player();
487
488 let next_song = self.queue.lock().unwrap().skip_forward(n).cloned();
489
490 if let Some(song) = next_song {
491 if let Err(e) = self.append_song_to_player(&song) {
492 error!("Failed to append song to player: {e}");
493 }
494
495 let binding = self.queue.lock().unwrap();
496 match status {
497 Status::Paused => self.pause(),
498 Status::Playing
501 if binding.get_repeat_mode().is_all() || binding.current_index().is_some() =>
502 {
503 self.play();
504 }
505 _ => {}
506 }
507 }
508 }
509
510 #[instrument(skip(self))]
511 fn skip_backward(&self, n: usize) {
512 let status = *self.status.lock().unwrap();
513 self.clear_player();
514
515 let next_song = self.queue.lock().unwrap().skip_backward(n).cloned();
516
517 if let Some(song) = next_song {
518 if let Err(e) = self.append_song_to_player(&song) {
519 error!("Failed to append song to player: {e}");
520 }
521 match status {
522 Status::Stopped => {}
523 Status::Paused => self.pause(),
524 Status::Playing => self.play(),
525 }
526 }
527 }
528
529 #[instrument(skip(self))]
530 fn set_position(&self, n: usize) {
531 let status = *self.status.lock().unwrap();
532 self.clear_player();
533
534 let mut binding = self.queue.lock().unwrap();
535 binding.set_current_index(n);
536 let next_song = binding.current_song().cloned();
537 drop(binding);
538
539 if let Some(song) = next_song {
540 if let Err(e) = self.append_song_to_player(&song) {
541 error!("Failed to append song to player: {e}");
542 }
543
544 match status {
545 Status::Stopped => {}
546 Status::Paused => self.pause(),
547 Status::Playing => self.play(),
548 }
549 }
550 }
551
552 #[instrument(skip(self))]
553 fn add_song_to_queue(&self, song: Song) {
554 self.queue.lock().unwrap().add_song(song);
555
556 if self.player.empty() {
558 let current_index = self.queue.lock().unwrap().current_index();
559
560 if let Some(song) =
561 current_index.map_or_else(|| self.get_next_song(), |_| self.get_current_song())
562 {
563 if let Err(e) = self.append_song_to_player(&song) {
564 error!("Failed to append song to player: {e}");
565 }
566 self.play();
567 }
568 }
569 }
570
571 #[instrument(skip(self))]
572 fn add_songs_to_queue(&self, songs: Vec<Song>) {
573 self.queue.lock().unwrap().add_songs(songs);
574
575 if self.player.empty() {
577 let current_index = self.queue.lock().unwrap().current_index();
578
579 if let Some(song) =
580 current_index.map_or_else(|| self.get_next_song(), |_| self.get_current_song())
581 {
582 if let Err(e) = self.append_song_to_player(&song) {
583 error!("Failed to append song to player: {e}");
584 }
585 self.play();
586 }
587 }
588 }
589
590 #[instrument(skip(self))]
591 fn remove_range_from_queue(&self, range: Range<usize>) {
592 let current_to_be_removed = self
594 .queue
595 .lock()
596 .unwrap()
597 .current_index()
598 .is_some_and(|current_index| range.contains(¤t_index));
599
600 self.queue.lock().unwrap().remove_range(range);
601
602 if current_to_be_removed {
604 self.clear_player();
605 if let Some(song) = self.get_current_song() {
606 if let Err(e) = self.append_song_to_player(&song) {
607 error!("Failed to append song to player: {e}");
608 }
609 }
610 }
611 }
612
613 #[instrument(skip(self))]
614 fn get_current_song(&self) -> Option<Song> {
615 self.queue.lock().unwrap().current_song().cloned()
616 }
617
618 #[instrument(skip(self))]
619 fn get_next_song(&self) -> Option<Song> {
620 self.queue.lock().unwrap().next_song().cloned()
621 }
622
623 #[instrument(skip(self, source))]
624 fn append_to_player<T>(&self, source: T)
625 where
626 T: Source<Item = f32> + Send + 'static,
627 {
628 if let Some(duration) = source.total_duration() {
629 *self.duration_info.lock().unwrap() = DurationInfo {
630 time_played: Duration::from_secs(0),
631 current_duration: duration,
632 };
633 }
634 self.player.append(source);
635 }
636
637 #[instrument(skip(self))]
638 fn append_song_to_player(&self, song: &Song) -> Result<(), LibraryError> {
639 let source = Decoder::new(BufReader::new(File::open(&song.path)?))?.convert_samples();
640 *self.duration_info.lock().unwrap() = DurationInfo {
641 time_played: Duration::from_secs(0),
642 current_duration: song.runtime,
643 };
644 self.append_to_player(source);
645
646 Ok(())
647 }
648
649 #[instrument(skip(self))]
650 fn volume_control(&self, command: VolumeCommand) {
651 match command {
652 VolumeCommand::Up(percent) => {
653 let mut volume = self.volume.lock().unwrap();
654 let percent = (*volume + percent).clamp(MIN_VOLUME, MAX_VOLUME);
655 if (*volume - percent).abs() > 0.0001 {
656 *volume = percent;
657 let _ = self.event_tx.send(StateChange::VolumeChanged(*volume));
658 }
659 }
660 VolumeCommand::Down(percent) => {
661 let mut volume = self.volume.lock().unwrap();
662 let percent = (*volume - percent).clamp(MIN_VOLUME, MAX_VOLUME);
663 if (*volume - percent).abs() > 0.0001 {
664 *volume = percent;
665 let _ = self.event_tx.send(StateChange::VolumeChanged(*volume));
666 }
667 }
668 VolumeCommand::Set(percent) => {
669 let mut volume = self.volume.lock().unwrap();
670 let percent = percent.clamp(MIN_VOLUME, MAX_VOLUME);
671 if (*volume - percent).abs() > 0.0001 {
672 *volume = percent;
673 let _ = self.event_tx.send(StateChange::VolumeChanged(*volume));
674 }
675 }
676 VolumeCommand::Mute => {
677 self.muted.store(true, std::sync::atomic::Ordering::Relaxed);
678 let _ = self.event_tx.send(StateChange::Muted);
679 }
680 VolumeCommand::Unmute => {
681 self.muted
682 .store(false, std::sync::atomic::Ordering::Relaxed);
683 let _ = self.event_tx.send(StateChange::Unmuted);
684 }
685 VolumeCommand::ToggleMute => {
686 self.muted.store(
687 !self.muted.load(std::sync::atomic::Ordering::Relaxed),
688 std::sync::atomic::Ordering::Relaxed,
689 );
690 if self.muted.load(std::sync::atomic::Ordering::Relaxed) {
691 let _ = self.event_tx.send(StateChange::Muted);
692 } else {
693 let _ = self.event_tx.send(StateChange::Unmuted);
694 }
695 }
696 }
697
698 if self.muted.load(std::sync::atomic::Ordering::Relaxed) {
699 self.player.set_volume(0.0);
700 } else {
701 self.player
702 .set_volume(self.volume.lock().unwrap().to_owned());
703 }
704 }
705
706 #[instrument(skip(self))]
707 fn seek(&self, seek: SeekType, duration: Duration) {
708 let mut duration_info = self.duration_info.lock().unwrap();
710 let new_time = match seek {
712 SeekType::Absolute => duration,
713 SeekType::RelativeForwards => duration_info.time_played.saturating_add(duration),
714 SeekType::RelativeBackwards => duration_info.time_played.saturating_sub(duration),
715 };
716 let new_time = new_time.min(duration_info.current_duration);
717
718 match self.player.try_seek(new_time) {
722 Ok(()) => {
723 debug!("Seek to {} successful", format_duration(&new_time));
724 duration_info.time_played = new_time;
725 drop(duration_info);
726 let mut status = self.status.lock().unwrap();
727 if new_time > Duration::from_secs(0) && *status == Status::Stopped {
728 *status = Status::Paused;
729 drop(status);
730 }
731 }
732 Err(SeekError::NotSupported { underlying_source }) => {
733 error!("Seek not supported by source: {underlying_source}");
734 }
735 Err(err) => {
736 error!("Seeking failed with error: {err}");
737 }
738 }
739 }
740}
741
742impl Default for AudioKernel {
743 fn default() -> Self {
745 let (tx, _) = std::sync::mpsc::channel();
746 Self::new(tx)
747 }
748}
749
750#[cfg(test)]
751mod tests {
752 use pretty_assertions::assert_eq;
753 use rstest::{fixture, rstest};
754
755 use crate::test_utils::init;
756
757 use super::*;
758 use std::sync::mpsc;
759 use std::time::Duration;
760
761 #[fixture]
762 fn audio_kernel() -> AudioKernel {
763 AudioKernel::default()
764 }
765
766 #[fixture]
767 fn audio_kernel_sender() -> Arc<AudioKernelSender> {
768 let (tx, _) = mpsc::channel();
769 AudioKernelSender::start(tx)
770 }
771
772 async fn get_state(sender: Arc<AudioKernelSender>) -> StateAudio {
773 let (tx, rx) = tokio::sync::oneshot::channel::<StateAudio>();
774 sender.send(AudioCommand::ReportStatus(tx));
775 rx.await.unwrap()
776 }
777
778 #[fixture]
779 fn sound() -> impl Source<Item = f32> + Send + 'static {
780 rodio::source::SineWave::new(440.0)
781 }
782
783 #[test]
784 fn test_audio_kernel_sender_send() {
785 let (tx, rx) = mpsc::channel();
786 let sender = AudioKernelSender::new(tx);
787 sender.send(AudioCommand::Play);
788 let (recv, _) = rx.recv().unwrap();
789 assert_eq!(recv, AudioCommand::Play);
790 }
791
792 #[test]
793 #[should_panic = "Failed to send command to audio kernel: sending on a closed channel"]
794 fn test_audio_kernel_send_closed_channel() {
795 let (tx, _) = mpsc::channel();
796 let sender = AudioKernelSender::new(tx);
797 sender.send(AudioCommand::Play);
798 }
799
800 #[rstest]
801 #[timeout(Duration::from_secs(3))] fn test_audio_player_kernel_spawn_and_exit(
803 #[from(audio_kernel_sender)] sender: Arc<AudioKernelSender>,
804 ) {
805 init();
806
807 sender.send(AudioCommand::Exit);
808 }
809
810 #[rstest]
811 fn test_volume_control(audio_kernel: AudioKernel) {
812 audio_kernel.volume_control(VolumeCommand::Up(0.1));
813 let volume = *audio_kernel.volume.lock().unwrap();
814 assert!(f32::EPSILON > (volume - 1.1).abs(), "{volume} != 1.1");
815
816 audio_kernel.volume_control(VolumeCommand::Down(0.1));
817 let volume = *audio_kernel.volume.lock().unwrap();
818 assert!(f32::EPSILON > (volume - 1.0).abs(), "{volume} != 1.0");
819
820 audio_kernel.volume_control(VolumeCommand::Set(0.5));
821 let volume = *audio_kernel.volume.lock().unwrap();
822 assert!(f32::EPSILON > (volume - 0.5).abs(), "{volume} != 0.5");
823
824 audio_kernel.volume_control(VolumeCommand::Mute);
825 assert_eq!(
826 audio_kernel
827 .muted
828 .load(std::sync::atomic::Ordering::Relaxed),
829 true
830 );
831
832 audio_kernel.volume_control(VolumeCommand::Unmute);
833 assert_eq!(
834 audio_kernel
835 .muted
836 .load(std::sync::atomic::Ordering::Relaxed),
837 false
838 );
839
840 audio_kernel.volume_control(VolumeCommand::ToggleMute);
841 assert_eq!(
842 audio_kernel
843 .muted
844 .load(std::sync::atomic::Ordering::Relaxed),
845 true
846 );
847
848 audio_kernel.volume_control(VolumeCommand::ToggleMute);
849 assert_eq!(
850 audio_kernel
851 .muted
852 .load(std::sync::atomic::Ordering::Relaxed),
853 false
854 );
855 }
856
857 mod playback_tests {
858 use mecomp_storage::test_utils::{arb_song_case, create_song_metadata, init_test_database};
863 use pretty_assertions::assert_eq;
864 use rstest::rstest;
865
866 use crate::test_utils::init;
867
868 use super::{super::*, audio_kernel, audio_kernel_sender, get_state, sound};
869
870 #[rstest]
871 fn test_audio_kernel_play_pause(
872 audio_kernel: AudioKernel,
873 sound: impl Source<Item = f32> + Send + 'static,
874 ) {
875 audio_kernel.player.append(sound);
876 audio_kernel.play();
877 assert!(!audio_kernel.player.is_paused());
878 audio_kernel.pause();
879 assert!(audio_kernel.player.is_paused());
880 }
881
882 #[rstest]
883 fn test_audio_kernel_toggle_playback(
884 audio_kernel: AudioKernel,
885 sound: impl Source<Item = f32> + Send + 'static,
886 ) {
887 audio_kernel.player.append(sound);
888 audio_kernel.play();
889 assert!(!audio_kernel.player.is_paused());
890 audio_kernel.toggle_playback();
891 assert!(audio_kernel.player.is_paused());
892 audio_kernel.toggle_playback();
893 assert!(!audio_kernel.player.is_paused());
894 }
895
896 #[rstest]
897 #[timeout(Duration::from_secs(5))] #[tokio::test]
899 async fn test_play_pause_toggle_restart(
900 #[from(audio_kernel_sender)] sender: Arc<AudioKernelSender>,
901 ) {
902 init();
903 let db = init_test_database().await.unwrap();
904 let tempdir = tempfile::tempdir().unwrap();
905
906 let song = Song::try_load_into_db(
907 &db,
908 create_song_metadata(&tempdir, arb_song_case()()).unwrap(),
909 )
910 .await
911 .unwrap();
912
913 sender.send(AudioCommand::Queue(QueueCommand::AddToQueue(Box::new(
914 OneOrMany::One(song.clone()),
915 ))));
916
917 let state = get_state(sender.clone()).await;
918 assert_eq!(state.queue_position, Some(0));
919 assert_eq!(state.status, Status::Playing);
920
921 sender.send(AudioCommand::Pause);
922 let state = get_state(sender.clone()).await;
923 assert_eq!(state.status, Status::Paused);
924
925 sender.send(AudioCommand::Play);
926 let state = get_state(sender.clone()).await;
927 assert_eq!(state.status, Status::Playing);
928
929 sender.send(AudioCommand::RestartSong);
930 let state = get_state(sender.clone()).await;
931 assert_eq!(state.status, Status::Playing); sender.send(AudioCommand::TogglePlayback);
934 let state = get_state(sender.clone()).await;
935 assert_eq!(state.status, Status::Paused);
936
937 sender.send(AudioCommand::RestartSong);
938 let state = get_state(sender.clone()).await;
939 assert_eq!(state.status, Status::Paused); sender.send(AudioCommand::Exit);
942 }
943
944 #[rstest]
945 fn test_audio_kernel_stop(audio_kernel: AudioKernel) {
946 init();
947 audio_kernel.player.append(sound());
948 audio_kernel.play();
949 assert!(!audio_kernel.player.is_paused());
950 audio_kernel.stop();
951 assert!(audio_kernel.player.is_paused());
952 assert_eq!(
953 audio_kernel.duration_info.lock().unwrap().time_played,
954 Duration::from_secs(0)
955 );
956 assert_eq!(*audio_kernel.status.lock().unwrap(), Status::Stopped);
957 }
958
959 #[rstest]
960 #[timeout(Duration::from_secs(5))] #[tokio::test]
962 async fn test_audio_kernel_skip_forward(audio_kernel: AudioKernel) {
963 init();
964 let db = init_test_database().await.unwrap();
965 let tempdir = tempfile::tempdir().unwrap();
966
967 let state = audio_kernel.state();
968 assert_eq!(state.queue_position, None);
969 assert!(state.paused());
970 assert_eq!(state.status, Status::Stopped);
971
972 audio_kernel.queue_control(QueueCommand::AddToQueue(Box::new(OneOrMany::Many(vec![
973 Song::try_load_into_db(
974 &db,
975 create_song_metadata(&tempdir, arb_song_case()()).unwrap(),
976 )
977 .await
978 .unwrap(),
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 ]))));
992
993 let state = audio_kernel.state();
995 assert_eq!(state.queue_position, Some(0));
996 assert!(!state.paused());
997 assert_eq!(state.status, Status::Playing);
998
999 audio_kernel.queue_control(QueueCommand::SkipForward(1));
1000
1001 let state = audio_kernel.state();
1003 assert_eq!(state.queue_position, Some(1));
1004 assert!(!state.paused());
1005 assert_eq!(state.status, Status::Playing);
1006
1007 audio_kernel.queue_control(QueueCommand::SkipForward(1));
1008
1009 let state = audio_kernel.state();
1011 assert_eq!(state.queue_position, Some(2));
1012 assert!(!state.paused());
1013 assert_eq!(state.status, Status::Playing);
1014
1015 audio_kernel.queue_control(QueueCommand::SkipForward(1));
1016
1017 let state = audio_kernel.state();
1019 assert_eq!(state.queue_position, None);
1020 assert!(state.paused());
1021 assert_eq!(state.status, Status::Stopped);
1022 }
1023
1024 #[rstest]
1025 #[timeout(Duration::from_secs(6))] #[tokio::test]
1027 async fn test_audio_kernel_skip_forward_sender(
1028 #[from(audio_kernel_sender)] sender: Arc<AudioKernelSender>,
1029 ) {
1030 init();
1032
1033 let db = init_test_database().await.unwrap();
1034 let tempdir = tempfile::tempdir().unwrap();
1035
1036 let state = get_state(sender.clone()).await;
1037 assert_eq!(state.queue_position, None);
1038 assert!(state.paused());
1039 assert_eq!(state.status, Status::Stopped);
1040
1041 sender.send(AudioCommand::Queue(QueueCommand::AddToQueue(Box::new(
1042 OneOrMany::Many(vec![
1043 Song::try_load_into_db(
1044 &db,
1045 create_song_metadata(&tempdir, arb_song_case()()).unwrap(),
1046 )
1047 .await
1048 .unwrap(),
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 ]),
1062 ))));
1063 let state = get_state(sender.clone()).await;
1065 assert_eq!(state.queue_position, Some(0));
1066 assert!(!state.paused());
1067 assert_eq!(state.status, Status::Playing);
1068
1069 sender.send(AudioCommand::Queue(QueueCommand::SkipForward(1)));
1070 let state = get_state(sender.clone()).await;
1072 assert_eq!(state.queue_position, Some(1));
1073 assert!(!state.paused());
1074 assert_eq!(state.status, Status::Playing);
1075
1076 sender.send(AudioCommand::Queue(QueueCommand::SkipForward(1)));
1077 let state = get_state(sender.clone()).await;
1079 assert_eq!(state.queue_position, Some(2));
1080 assert!(!state.paused());
1081 assert_eq!(state.status, Status::Playing);
1082
1083 sender.send(AudioCommand::Queue(QueueCommand::SkipForward(1)));
1084 let state = get_state(sender.clone()).await;
1086 assert_eq!(state.queue_position, None);
1087 assert!(state.paused());
1088 assert_eq!(state.status, Status::Stopped);
1089
1090 sender.send(AudioCommand::Exit);
1091 }
1092
1093 #[rstest]
1094 #[timeout(Duration::from_secs(5))] #[tokio::test]
1096 async fn test_remove_range_from_queue(
1097 #[from(audio_kernel_sender)] sender: Arc<AudioKernelSender>,
1098 ) {
1099 init();
1100 let db = init_test_database().await.unwrap();
1101 let tempdir = tempfile::tempdir().unwrap();
1102 let song1 = Song::try_load_into_db(
1103 &db,
1104 create_song_metadata(&tempdir, arb_song_case()()).unwrap(),
1105 )
1106 .await
1107 .unwrap();
1108 let song2 = Song::try_load_into_db(
1109 &db,
1110 create_song_metadata(&tempdir, arb_song_case()()).unwrap(),
1111 )
1112 .await
1113 .unwrap();
1114
1115 sender.send(AudioCommand::Queue(QueueCommand::AddToQueue(Box::new(
1117 OneOrMany::Many(vec![song1.clone(), song2.clone()]),
1118 ))));
1119 let state = get_state(sender.clone()).await;
1120 assert_eq!(state.queue_position, Some(0));
1121 assert!(!state.paused());
1122 assert_eq!(state.status, Status::Playing);
1123
1124 sender.send(AudioCommand::Pause);
1126
1127 sender.send(AudioCommand::Queue(QueueCommand::RemoveRange(0..1)));
1129 let state = get_state(sender.clone()).await;
1130 assert_eq!(state.queue_position, Some(0));
1131 assert!(state.paused());
1132 assert_eq!(state.status, Status::Stopped);
1133 assert_eq!(state.queue.len(), 1);
1134 assert_eq!(state.queue[0], song2);
1135
1136 sender.send(AudioCommand::Play);
1138
1139 sender.send(AudioCommand::Queue(QueueCommand::AddToQueue(Box::new(
1141 OneOrMany::One(song1.clone()),
1142 ))));
1143 let state = get_state(sender.clone()).await;
1144 assert_eq!(state.queue_position, Some(0));
1145 assert!(!state.paused());
1146 assert_eq!(state.status, Status::Playing);
1147 assert_eq!(state.queue.len(), 2);
1148 assert_eq!(state.queue[0], song2);
1149 assert_eq!(state.queue[1], song1);
1150
1151 sender.send(AudioCommand::Queue(QueueCommand::RemoveRange(1..2)));
1153 let state = get_state(sender.clone()).await;
1154 assert_eq!(state.queue_position, Some(0));
1155 assert!(!state.paused());
1156 assert_eq!(state.status, Status::Playing);
1157 assert_eq!(state.queue.len(), 1);
1158 assert_eq!(state.queue[0], song2);
1159
1160 sender.send(AudioCommand::Exit);
1161 }
1162
1163 #[rstest]
1164 #[timeout(Duration::from_secs(10))] #[tokio::test]
1166 async fn test_audio_kernel_skip_backward(
1167 #[from(audio_kernel_sender)] sender: Arc<AudioKernelSender>,
1168 ) {
1169 init();
1170 let db = init_test_database().await.unwrap();
1171 let tempdir = tempfile::tempdir().unwrap();
1172
1173 let state = get_state(sender.clone()).await;
1174 assert_eq!(state.queue_position, None);
1175 assert!(state.paused());
1176 assert_eq!(state.status, Status::Stopped);
1177
1178 sender.send(AudioCommand::Queue(QueueCommand::AddToQueue(Box::new(
1179 OneOrMany::Many(vec![
1180 Song::try_load_into_db(
1181 &db,
1182 create_song_metadata(&tempdir, arb_song_case()()).unwrap(),
1183 )
1184 .await
1185 .unwrap(),
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 ]),
1199 ))));
1200
1201 let state = get_state(sender.clone()).await;
1203 assert_eq!(state.queue_position, Some(0));
1204 assert!(!state.paused());
1205 assert_eq!(state.status, Status::Playing);
1206
1207 sender.send(AudioCommand::Queue(QueueCommand::SkipForward(2)));
1208
1209 let state = get_state(sender.clone()).await;
1211 assert_eq!(state.queue_position, Some(2));
1212 assert!(!state.paused());
1213 assert_eq!(state.status, Status::Playing);
1214
1215 sender.send(AudioCommand::Queue(QueueCommand::SkipBackward(1)));
1216
1217 let state = get_state(sender.clone()).await;
1219 assert_eq!(state.queue_position, Some(1));
1220 assert!(!state.paused());
1221 assert_eq!(state.status, Status::Playing);
1222
1223 sender.send(AudioCommand::Queue(QueueCommand::SkipBackward(1)));
1224
1225 let state = get_state(sender.clone()).await;
1227 assert_eq!(state.queue_position, Some(0));
1228 assert!(!state.paused());
1229
1230 sender.send(AudioCommand::Queue(QueueCommand::SkipBackward(1)));
1231
1232 let state = get_state(sender.clone()).await;
1234 assert_eq!(state.queue_position, None);
1235 assert!(state.paused());
1236 assert_eq!(state.status, Status::Stopped);
1237
1238 sender.send(AudioCommand::Exit);
1239 }
1240
1241 #[rstest]
1242 #[timeout(Duration::from_secs(10))] #[tokio::test]
1244 async fn test_audio_kernel_set_position(
1245 #[from(audio_kernel_sender)] sender: Arc<AudioKernelSender>,
1246 ) {
1247 init();
1248 let db = init_test_database().await.unwrap();
1249 let tempdir = tempfile::tempdir().unwrap();
1250
1251 let state = get_state(sender.clone()).await;
1252 assert_eq!(state.queue_position, None);
1253 assert!(state.paused());
1254 assert_eq!(state.status, Status::Stopped);
1255
1256 sender.send(AudioCommand::Queue(QueueCommand::AddToQueue(Box::new(
1257 OneOrMany::Many(vec![
1258 Song::try_load_into_db(
1259 &db,
1260 create_song_metadata(&tempdir, arb_song_case()()).unwrap(),
1261 )
1262 .await
1263 .unwrap(),
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 ]),
1277 ))));
1278 let state = get_state(sender.clone()).await;
1280 assert_eq!(state.queue_position, Some(0));
1281 assert!(!state.paused());
1282 assert_eq!(state.status, Status::Playing);
1283
1284 sender.send(AudioCommand::Queue(QueueCommand::SetPosition(1)));
1285 let state = get_state(sender.clone()).await;
1287 assert_eq!(state.queue_position, Some(1));
1288 assert!(!state.paused());
1289 assert_eq!(state.status, Status::Playing);
1290
1291 sender.send(AudioCommand::Queue(QueueCommand::SetPosition(2)));
1292 let state = get_state(sender.clone()).await;
1294 assert_eq!(state.queue_position, Some(2));
1295 assert!(!state.paused());
1296 assert_eq!(state.status, Status::Playing);
1297
1298 sender.send(AudioCommand::Queue(QueueCommand::SetPosition(0)));
1299 let state = get_state(sender.clone()).await;
1301 assert_eq!(state.queue_position, Some(0));
1302 assert!(!state.paused());
1303 assert_eq!(state.status, Status::Playing);
1304
1305 sender.send(AudioCommand::Queue(QueueCommand::SetPosition(3)));
1306 let state = get_state(sender.clone()).await;
1308 assert_eq!(state.queue_position, Some(2));
1309 assert!(!state.paused());
1310 assert_eq!(state.status, Status::Playing);
1311
1312 sender.send(AudioCommand::Exit);
1313 }
1314
1315 #[rstest]
1316 #[timeout(Duration::from_secs(5))] #[tokio::test]
1318 async fn test_audio_kernel_clear(
1319 #[from(audio_kernel_sender)] sender: Arc<AudioKernelSender>,
1320 ) {
1321 init();
1322 let db = init_test_database().await.unwrap();
1323 let tempdir = tempfile::tempdir().unwrap();
1324
1325 let state = get_state(sender.clone()).await;
1326 assert_eq!(state.queue_position, None);
1327 assert!(state.paused());
1328 assert_eq!(state.status, Status::Stopped);
1329
1330 sender.send(AudioCommand::Queue(QueueCommand::AddToQueue(Box::new(
1331 OneOrMany::Many(vec![
1332 Song::try_load_into_db(
1333 &db,
1334 create_song_metadata(&tempdir, arb_song_case()()).unwrap(),
1335 )
1336 .await
1337 .unwrap(),
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 ]),
1351 ))));
1352 let state = get_state(sender.clone()).await;
1354 assert_eq!(state.queue_position, Some(0));
1355 assert_eq!(state.queue.len(), 3);
1356 assert!(!state.paused());
1357 assert_eq!(state.status, Status::Playing);
1358
1359 sender.send(AudioCommand::ClearPlayer);
1360 let state = get_state(sender.clone()).await;
1362 assert_eq!(state.queue_position, Some(0));
1363 assert_eq!(state.queue.len(), 3);
1364 assert!(state.paused());
1365 assert_eq!(state.status, Status::Stopped);
1366
1367 sender.send(AudioCommand::Queue(QueueCommand::Clear));
1368 let state = get_state(sender.clone()).await;
1370 assert_eq!(state.queue_position, None);
1371 assert_eq!(state.queue.len(), 0);
1372 assert!(state.paused());
1373 assert_eq!(state.status, Status::Stopped);
1374
1375 sender.send(AudioCommand::Exit);
1376 }
1377
1378 #[rstest]
1379 #[timeout(Duration::from_secs(5))] #[tokio::test]
1381 async fn test_audio_kernel_shuffle(
1382 #[from(audio_kernel_sender)] sender: Arc<AudioKernelSender>,
1383 ) {
1384 init();
1385 let db = init_test_database().await.unwrap();
1386 let tempdir = tempfile::tempdir().unwrap();
1387
1388 let state = get_state(sender.clone()).await;
1389 assert_eq!(state.queue_position, None);
1390 assert!(state.paused());
1391 assert_eq!(state.status, Status::Stopped);
1392
1393 sender.send(AudioCommand::Queue(QueueCommand::AddToQueue(Box::new(
1394 OneOrMany::Many(vec![
1395 Song::try_load_into_db(
1396 &db,
1397 create_song_metadata(&tempdir, arb_song_case()()).unwrap(),
1398 )
1399 .await
1400 .unwrap(),
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 ]),
1414 ))));
1415 let state = get_state(sender.clone()).await;
1417 assert_eq!(state.queue_position, Some(0));
1418 assert_eq!(state.queue.len(), 3);
1419 assert!(!state.paused());
1420 assert_eq!(state.status, Status::Playing);
1421
1422 sender.send(AudioCommand::Queue(QueueCommand::SkipForward(1)));
1424 let state = get_state(sender.clone()).await;
1425 assert_eq!(state.queue_position, Some(1));
1426 assert_eq!(state.queue.len(), 3);
1427 assert!(!state.paused());
1428 assert_eq!(state.status, Status::Playing);
1429
1430 sender.send(AudioCommand::Queue(QueueCommand::Shuffle));
1432 let state = get_state(sender.clone()).await;
1434 assert_eq!(state.queue_position, Some(0));
1435 assert_eq!(state.queue.len(), 3);
1436 assert!(!state.paused());
1437 assert_eq!(state.status, Status::Playing);
1438
1439 sender.send(AudioCommand::Exit);
1440 }
1441
1442 #[rstest]
1443 #[timeout(Duration::from_secs(5))] #[tokio::test]
1445 async fn test_volume_commands(#[from(audio_kernel_sender)] sender: Arc<AudioKernelSender>) {
1446 init();
1447
1448 let state = get_state(sender.clone()).await;
1449 assert!(
1450 f32::EPSILON > (state.volume - 1.0).abs(),
1451 "{} != 1.0",
1452 state.volume
1453 );
1454 assert!(!state.muted);
1455
1456 sender.send(AudioCommand::Volume(VolumeCommand::Up(0.1)));
1457 let state = get_state(sender.clone()).await;
1458 assert!(
1459 f32::EPSILON > (state.volume - 1.1).abs(),
1460 "{} != 1.1",
1461 state.volume
1462 );
1463 assert!(!state.muted);
1464
1465 sender.send(AudioCommand::Volume(VolumeCommand::Down(0.1)));
1466 let state = get_state(sender.clone()).await;
1467 assert!(
1468 f32::EPSILON > (state.volume - 1.0).abs(),
1469 "{} != 1.0",
1470 state.volume
1471 );
1472 assert!(!state.muted);
1473
1474 sender.send(AudioCommand::Volume(VolumeCommand::Set(0.5)));
1475 let state = get_state(sender.clone()).await;
1476 assert!(
1477 f32::EPSILON > (state.volume - 0.5).abs(),
1478 "{} != 0.5",
1479 state.volume
1480 );
1481 assert!(!state.muted);
1482
1483 sender.send(AudioCommand::Volume(VolumeCommand::Mute));
1484 let state = get_state(sender.clone()).await;
1485 assert!(
1486 f32::EPSILON > (state.volume - 0.5).abs(),
1487 "{} != 0.5",
1488 state.volume
1489 ); assert!(state.muted);
1491
1492 sender.send(AudioCommand::Volume(VolumeCommand::Unmute));
1493 let state = get_state(sender.clone()).await;
1494 assert!(
1495 f32::EPSILON > (state.volume - 0.5).abs(),
1496 "{} != 0.5",
1497 state.volume
1498 );
1499 assert!(!state.muted);
1500
1501 sender.send(AudioCommand::Volume(VolumeCommand::ToggleMute));
1502 let state = get_state(sender.clone()).await;
1503 assert!(
1504 f32::EPSILON > (state.volume - 0.5).abs(),
1505 "{} != 0.5",
1506 state.volume
1507 );
1508 assert!(state.muted);
1509
1510 sender.send(AudioCommand::Volume(VolumeCommand::ToggleMute));
1511 let state = get_state(sender.clone()).await;
1512 assert!(
1513 f32::EPSILON > (state.volume - 0.5).abs(),
1514 "{} != 0.5",
1515 state.volume
1516 );
1517 assert!(!state.muted);
1518
1519 sender.send(AudioCommand::Exit);
1520 }
1521
1522 #[rstest]
1523 #[timeout(Duration::from_secs(5))] #[tokio::test]
1525 async fn test_volume_out_of_bounds(
1526 #[from(audio_kernel_sender)] sender: Arc<AudioKernelSender>,
1527 ) {
1528 init();
1529
1530 sender.send(AudioCommand::Volume(VolumeCommand::Up(MAX_VOLUME + 0.5)));
1532 let state = get_state(sender.clone()).await;
1533 assert!(
1534 f32::EPSILON > (state.volume - MAX_VOLUME).abs(),
1535 "{} != {}",
1536 state.volume,
1537 MAX_VOLUME
1538 );
1539 assert!(!state.muted);
1540 sender.send(AudioCommand::Volume(VolumeCommand::Down(
1541 MAX_VOLUME + 0.5 - MIN_VOLUME,
1542 )));
1543 let state = get_state(sender.clone()).await;
1544 assert!(
1545 f32::EPSILON > (state.volume - MIN_VOLUME).abs(),
1546 "{} != {}",
1547 state.volume,
1548 MIN_VOLUME
1549 );
1550 assert!(!state.muted);
1551
1552 sender.send(AudioCommand::Volume(VolumeCommand::Set(MAX_VOLUME + 0.5)));
1554 let state = get_state(sender.clone()).await;
1555 assert!(
1556 f32::EPSILON > (state.volume - MAX_VOLUME).abs(),
1557 "{} != {}",
1558 state.volume,
1559 MAX_VOLUME
1560 );
1561 assert!(!state.muted);
1562 sender.send(AudioCommand::Volume(VolumeCommand::Set(MIN_VOLUME - 0.5)));
1563 let state = get_state(sender.clone()).await;
1564 assert!(
1565 f32::EPSILON > (state.volume - MIN_VOLUME).abs(),
1566 "{} != {}",
1567 state.volume,
1568 MIN_VOLUME
1569 );
1570 assert!(!state.muted);
1571
1572 sender.send(AudioCommand::Exit);
1573 }
1574
1575 #[rstest]
1576 #[timeout(Duration::from_secs(9))] #[tokio::test]
1578 async fn test_seek_commands(#[from(audio_kernel_sender)] sender: Arc<AudioKernelSender>) {
1579 init();
1580 let db = init_test_database().await.unwrap();
1581 let tempdir = tempfile::tempdir().unwrap();
1582
1583 let song = Song::try_load_into_db(
1584 &db,
1585 create_song_metadata(&tempdir, arb_song_case()()).unwrap(),
1586 )
1587 .await
1588 .unwrap();
1589
1590 sender.send(AudioCommand::Queue(QueueCommand::AddToQueue(Box::new(
1593 OneOrMany::One(song.clone()),
1594 ))));
1595 sender.send(AudioCommand::Stop);
1596 sender.send(AudioCommand::Seek(
1597 SeekType::Absolute,
1598 Duration::from_secs(0),
1599 ));
1600 let state: StateAudio = get_state(sender.clone()).await;
1601 assert_eq!(state.queue_position, Some(0));
1602 assert_eq!(state.status, Status::Stopped);
1603 assert_eq!(
1604 state.runtime.unwrap().duration,
1605 Duration::from_secs(10) + Duration::from_nanos(6)
1606 );
1607 assert_eq!(state.runtime.unwrap().seek_position, Duration::from_secs(0));
1608
1609 sender.send(AudioCommand::Seek(
1611 SeekType::RelativeForwards,
1612 Duration::from_secs(2),
1613 ));
1614 let state = get_state(sender.clone()).await;
1615 assert_eq!(state.runtime.unwrap().seek_position, Duration::from_secs(2));
1616 assert_eq!(state.current_song, Some(song.clone()));
1617 assert_eq!(state.status, Status::Paused);
1618
1619 sender.send(AudioCommand::Seek(
1621 SeekType::RelativeBackwards,
1622 Duration::from_secs(1),
1623 ));
1624 let state = get_state(sender.clone()).await;
1625 assert_eq!(state.runtime.unwrap().seek_position, Duration::from_secs(1));
1626 assert_eq!(state.current_song, Some(song.clone()));
1627 assert_eq!(state.status, Status::Paused);
1628
1629 sender.send(AudioCommand::Seek(
1631 SeekType::Absolute,
1632 Duration::from_secs(9),
1633 ));
1634 let state = get_state(sender.clone()).await;
1635 assert_eq!(state.runtime.unwrap().seek_position, Duration::from_secs(9));
1636 assert_eq!(state.current_song, Some(song.clone()));
1637 assert_eq!(state.status, Status::Paused);
1638
1639 sender.send(AudioCommand::Play);
1641 tokio::time::sleep(Duration::from_millis(1001)).await;
1642 let state = get_state(sender.clone()).await;
1643 assert_eq!(state.queue_position, None);
1644 assert_eq!(state.status, Status::Stopped);
1645
1646 sender.send(AudioCommand::Exit);
1647 }
1648 }
1649}