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_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!(
294 "Audio Kernel failed to send state to the receiver, state receiver likely has been dropped. State: {e}"
295 );
296 break;
297 }
298 }
299 AudioCommand::Volume(command) => self.volume_control(command),
300 AudioCommand::Seek(seek, duration) => {
301 self.seek(seek, duration);
302 let _ = self.event_tx.send(StateChange::Seeked(
303 self.duration_info.lock().unwrap().time_played,
304 ));
305 }
306 AudioCommand::Stop if prev_status != Status::Stopped => {
307 self.stop();
308 let _ = self
309 .event_tx
310 .send(StateChange::Seeked(Duration::from_secs(0)));
311 }
312 AudioCommand::Stop => {}
313 }
314
315 let new_status = *self.status.lock().unwrap();
316
317 if prev_status != new_status {
318 let _ = self.event_tx.send(StateChange::StatusChanged(new_status));
319 }
320 }
321
322 #[cfg(feature = "mock_playback")]
323 self.queue_rx_end_tx.send(()).unwrap();
324 dw_tx.send(()).unwrap();
325 }
326
327 #[instrument(skip(self))]
328 fn play(&self) {
329 if self.player.empty() {
330 return;
331 }
332 self.player.play();
333 *self.status.lock().unwrap() = Status::Playing;
334 }
335
336 #[instrument(skip(self))]
337 fn pause(&self) {
338 self.player.pause();
339 *self.status.lock().unwrap() = Status::Paused;
340 }
341
342 #[instrument(skip(self))]
343 fn stop(&self) {
344 self.player.pause();
345 self.seek(SeekType::Absolute, Duration::from_secs(0));
346 *self.status.lock().unwrap() = Status::Stopped;
347 }
348
349 #[instrument(skip(self))]
350 fn toggle_playback(&self) {
351 if self.player.is_paused() {
352 self.play();
353 } else {
354 self.pause();
355 }
356 }
357
358 #[instrument(skip(self))]
359 fn restart_song(&self) {
360 let status = *self.status.lock().unwrap();
361 self.clear_player();
362
363 if let Some(song) = self.queue.lock().unwrap().current_song() {
364 if let Err(e) = self.append_song_to_player(song) {
365 error!("Failed to append song to player: {e}");
366 }
367
368 match status {
369 Status::Stopped => {}
371 Status::Paused => self.pause(),
373 Status::Playing => self.play(),
375 }
376 }
377 }
378
379 #[instrument(skip(self))]
380 fn clear(&self) {
381 self.clear_player();
382 self.queue.lock().unwrap().clear();
383 }
384
385 #[instrument(skip(self))]
386 fn clear_player(&self) {
387 self.player.clear();
388 *self.status.lock().unwrap() = Status::Stopped;
389 *self.duration_info.lock().unwrap() = DurationInfo::default();
390 }
391
392 #[instrument(skip(self))]
393 fn queue_control(&self, command: QueueCommand) {
394 let prev_song = self.queue.lock().unwrap().current_song().cloned();
395 match command {
396 QueueCommand::Clear => self.clear(),
397 QueueCommand::PlayNextSong => self.start_next_song(),
398 QueueCommand::SkipForward(n) => self.skip_forward(n),
399 QueueCommand::SkipBackward(n) => self.skip_backward(n),
400 QueueCommand::SetPosition(n) => self.set_position(n),
401 QueueCommand::Shuffle => self.queue.lock().unwrap().shuffle(),
402 QueueCommand::AddToQueue(song_box) => match *song_box {
403 OneOrMany::None => {}
404 OneOrMany::One(song) => self.add_song_to_queue(song),
405 OneOrMany::Many(songs) => self.add_songs_to_queue(songs),
406 },
407 QueueCommand::RemoveRange(range) => self.remove_range_from_queue(range),
408 QueueCommand::SetRepeatMode(mode) => {
409 self.queue.lock().unwrap().set_repeat_mode(mode);
410 let _ = self.event_tx.send(StateChange::RepeatModeChanged(mode));
411 }
412 }
413
414 let new_song = self.queue.lock().unwrap().current_song().cloned();
415
416 if prev_song != new_song {
417 let _ = self
418 .event_tx
419 .send(StateChange::TrackChanged(new_song.map(|s| s.id.into())));
420 }
421 }
422
423 #[instrument(skip(self))]
424 fn state(&self) -> StateAudio {
425 let queue = self.queue.lock().unwrap();
426 let queue_position = queue.current_index();
427 let current_song = queue.current_song().cloned();
428 let repeat_mode = queue.get_repeat_mode();
429 let runtime = current_song.as_ref().map(|_| {
430 let duration_info = self.duration_info.lock().unwrap();
431 let seek_position = duration_info.time_played;
432 let duration = duration_info.current_duration;
433 drop(duration_info);
434 let seek_percent =
435 Percent::new(seek_position.as_secs_f32() / duration.as_secs_f32() * 100.0);
436 StateRuntime {
437 seek_position,
438 seek_percent,
439 duration,
440 }
441 });
442 let status = *self.status.lock().unwrap();
443 let status = if self.player.is_paused() {
444 debug_assert!(matches!(status, Status::Paused | Status::Stopped));
445 status
446 } else {
447 debug_assert_eq!(status, Status::Playing);
448 Status::Playing
449 };
450
451 let muted = self.muted.load(std::sync::atomic::Ordering::Relaxed);
452 let volume = *self.volume.lock().unwrap();
453
454 let queued_songs = queue.queued_songs();
455 drop(queue);
456
457 StateAudio {
458 queue: queued_songs,
459 queue_position,
460 current_song,
461 repeat_mode,
462 runtime,
463 status,
464 muted,
465 volume,
466 }
467 }
468
469 #[instrument(skip(self))]
470 fn start_next_song(&self) {
471 self.clear_player();
472 let next_song = self.queue.lock().unwrap().next_song().cloned();
473
474 if let Some(song) = next_song {
475 if let Err(e) = self.append_song_to_player(&song) {
476 error!("Failed to append song to player: {e}");
477 }
478
479 let binding = self.queue.lock().unwrap();
480 if binding.get_repeat_mode().is_all() || binding.current_index().is_some() {
483 self.play();
484 }
485 }
486 }
487
488 #[instrument(skip(self))]
489 fn skip_forward(&self, n: usize) {
490 let status = *self.status.lock().unwrap();
491 self.clear_player();
492
493 let next_song = self.queue.lock().unwrap().skip_forward(n).cloned();
494
495 if let Some(song) = next_song {
496 if let Err(e) = self.append_song_to_player(&song) {
497 error!("Failed to append song to player: {e}");
498 }
499
500 let binding = self.queue.lock().unwrap();
501 match status {
502 Status::Paused => self.pause(),
503 Status::Playing
506 if binding.get_repeat_mode().is_all() || binding.current_index().is_some() =>
507 {
508 self.play();
509 }
510 _ => {}
511 }
512 }
513 }
514
515 #[instrument(skip(self))]
516 fn skip_backward(&self, n: usize) {
517 let status = *self.status.lock().unwrap();
518 self.clear_player();
519
520 let next_song = self.queue.lock().unwrap().skip_backward(n).cloned();
521
522 if let Some(song) = next_song {
523 if let Err(e) = self.append_song_to_player(&song) {
524 error!("Failed to append song to player: {e}");
525 }
526 match status {
527 Status::Stopped => {}
528 Status::Paused => self.pause(),
529 Status::Playing => self.play(),
530 }
531 }
532 }
533
534 #[instrument(skip(self))]
535 fn set_position(&self, n: usize) {
536 let status = *self.status.lock().unwrap();
537 self.clear_player();
538
539 let mut binding = self.queue.lock().unwrap();
540 binding.set_current_index(n);
541 let next_song = binding.current_song().cloned();
542 drop(binding);
543
544 if let Some(song) = next_song {
545 if let Err(e) = self.append_song_to_player(&song) {
546 error!("Failed to append song to player: {e}");
547 }
548
549 match status {
550 Status::Stopped => {}
551 Status::Paused => self.pause(),
552 Status::Playing => self.play(),
553 }
554 }
555 }
556
557 #[instrument(skip(self))]
558 fn add_song_to_queue(&self, song: Song) {
559 self.queue.lock().unwrap().add_song(song);
560
561 if self.player.empty() {
563 let current_index = self.queue.lock().unwrap().current_index();
564
565 if let Some(song) =
566 current_index.map_or_else(|| self.get_next_song(), |_| self.get_current_song())
567 {
568 if let Err(e) = self.append_song_to_player(&song) {
569 error!("Failed to append song to player: {e}");
570 }
571 self.play();
572 }
573 }
574 }
575
576 #[instrument(skip(self))]
577 fn add_songs_to_queue(&self, songs: Vec<Song>) {
578 self.queue.lock().unwrap().add_songs(songs);
579
580 if self.player.empty() {
582 let current_index = self.queue.lock().unwrap().current_index();
583
584 if let Some(song) =
585 current_index.map_or_else(|| self.get_next_song(), |_| self.get_current_song())
586 {
587 if let Err(e) = self.append_song_to_player(&song) {
588 error!("Failed to append song to player: {e}");
589 }
590 self.play();
591 }
592 }
593 }
594
595 #[instrument(skip(self))]
596 fn remove_range_from_queue(&self, range: Range<usize>) {
597 let current_to_be_removed = self
599 .queue
600 .lock()
601 .unwrap()
602 .current_index()
603 .is_some_and(|current_index| range.contains(¤t_index));
604
605 self.queue.lock().unwrap().remove_range(range);
606
607 if current_to_be_removed {
609 self.clear_player();
610 if let Some(song) = self.get_current_song() {
611 if let Err(e) = self.append_song_to_player(&song) {
612 error!("Failed to append song to player: {e}");
613 }
614 }
615 }
616 }
617
618 #[instrument(skip(self))]
619 fn get_current_song(&self) -> Option<Song> {
620 self.queue.lock().unwrap().current_song().cloned()
621 }
622
623 #[instrument(skip(self))]
624 fn get_next_song(&self) -> Option<Song> {
625 self.queue.lock().unwrap().next_song().cloned()
626 }
627
628 #[instrument(skip(self, source))]
629 fn append_to_player<T>(&self, source: T)
630 where
631 T: Source<Item = f32> + Send + 'static,
632 {
633 if let Some(duration) = source.total_duration() {
634 *self.duration_info.lock().unwrap() = DurationInfo {
635 time_played: Duration::from_secs(0),
636 current_duration: duration,
637 };
638 }
639 self.player.append(source);
640 }
641
642 #[instrument(skip(self))]
643 fn append_song_to_player(&self, song: &Song) -> Result<(), LibraryError> {
644 let source = Decoder::new(BufReader::new(File::open(&song.path)?))?.convert_samples();
645 *self.duration_info.lock().unwrap() = DurationInfo {
646 time_played: Duration::from_secs(0),
647 current_duration: song.runtime,
648 };
649 self.append_to_player(source);
650
651 Ok(())
652 }
653
654 #[instrument(skip(self))]
655 fn volume_control(&self, command: VolumeCommand) {
656 match command {
657 VolumeCommand::Up(percent) => {
658 let mut volume = self.volume.lock().unwrap();
659 let percent = (*volume + percent).clamp(MIN_VOLUME, MAX_VOLUME);
660 if (*volume - percent).abs() > 0.0001 {
661 *volume = percent;
662 let _ = self.event_tx.send(StateChange::VolumeChanged(*volume));
663 }
664 }
665 VolumeCommand::Down(percent) => {
666 let mut volume = self.volume.lock().unwrap();
667 let percent = (*volume - percent).clamp(MIN_VOLUME, MAX_VOLUME);
668 if (*volume - percent).abs() > 0.0001 {
669 *volume = percent;
670 let _ = self.event_tx.send(StateChange::VolumeChanged(*volume));
671 }
672 }
673 VolumeCommand::Set(percent) => {
674 let mut volume = self.volume.lock().unwrap();
675 let percent = percent.clamp(MIN_VOLUME, MAX_VOLUME);
676 if (*volume - percent).abs() > 0.0001 {
677 *volume = percent;
678 let _ = self.event_tx.send(StateChange::VolumeChanged(*volume));
679 }
680 }
681 VolumeCommand::Mute => {
682 self.muted.store(true, std::sync::atomic::Ordering::Relaxed);
683 let _ = self.event_tx.send(StateChange::Muted);
684 }
685 VolumeCommand::Unmute => {
686 self.muted
687 .store(false, std::sync::atomic::Ordering::Relaxed);
688 let _ = self.event_tx.send(StateChange::Unmuted);
689 }
690 VolumeCommand::ToggleMute => {
691 self.muted.store(
692 !self.muted.load(std::sync::atomic::Ordering::Relaxed),
693 std::sync::atomic::Ordering::Relaxed,
694 );
695 if self.muted.load(std::sync::atomic::Ordering::Relaxed) {
696 let _ = self.event_tx.send(StateChange::Muted);
697 } else {
698 let _ = self.event_tx.send(StateChange::Unmuted);
699 }
700 }
701 }
702
703 if self.muted.load(std::sync::atomic::Ordering::Relaxed) {
704 self.player.set_volume(0.0);
705 } else {
706 self.player
707 .set_volume(self.volume.lock().unwrap().to_owned());
708 }
709 }
710
711 #[instrument(skip(self))]
712 fn seek(&self, seek: SeekType, duration: Duration) {
713 let mut duration_info = self.duration_info.lock().unwrap();
715 let new_time = match seek {
717 SeekType::Absolute => duration,
718 SeekType::RelativeForwards => duration_info.time_played.saturating_add(duration),
719 SeekType::RelativeBackwards => duration_info.time_played.saturating_sub(duration),
720 };
721 let new_time = if new_time > duration_info.current_duration {
722 duration_info.current_duration
723 } else if new_time < Duration::from_secs(0) {
724 Duration::from_secs(0)
725 } else {
726 new_time
727 };
728
729 match self.player.try_seek(new_time) {
733 Ok(()) => {
734 debug!("Seek to {} successful", format_duration(&new_time));
735 duration_info.time_played = new_time;
736 drop(duration_info);
737 let mut status = self.status.lock().unwrap();
738 if new_time > Duration::from_secs(0) && *status == Status::Stopped {
739 *status = Status::Paused;
740 drop(status);
741 }
742 }
743 Err(SeekError::NotSupported { underlying_source }) => {
744 error!("Seek not supported by source: {underlying_source}");
745 }
746 Err(err) => {
747 error!("Seeking failed with error: {err}");
748 }
749 }
750 }
751}
752
753impl Default for AudioKernel {
754 fn default() -> Self {
756 let (tx, _) = std::sync::mpsc::channel();
757 Self::new(tx)
758 }
759}
760
761#[cfg(test)]
762mod tests {
763 use pretty_assertions::assert_eq;
764 use rstest::{fixture, rstest};
765
766 use crate::test_utils::init;
767
768 use super::*;
769 use std::sync::mpsc;
770 use std::time::Duration;
771
772 #[fixture]
773 fn audio_kernel() -> AudioKernel {
774 AudioKernel::default()
775 }
776
777 #[fixture]
778 fn audio_kernel_sender() -> Arc<AudioKernelSender> {
779 let (tx, _) = mpsc::channel();
780 AudioKernelSender::start(tx)
781 }
782
783 async fn get_state(sender: Arc<AudioKernelSender>) -> StateAudio {
784 let (tx, rx) = tokio::sync::oneshot::channel::<StateAudio>();
785 sender.send(AudioCommand::ReportStatus(tx));
786 rx.await.unwrap()
787 }
788
789 #[fixture]
790 fn sound() -> impl Source<Item = f32> + Send + 'static {
791 rodio::source::SineWave::new(440.0)
792 }
793
794 #[test]
795 fn test_audio_kernel_sender_send() {
796 let (tx, rx) = mpsc::channel();
797 let sender = AudioKernelSender::new(tx);
798 sender.send(AudioCommand::Play);
799 let (recv, _) = rx.recv().unwrap();
800 assert_eq!(recv, AudioCommand::Play);
801 }
802
803 #[test]
804 #[should_panic = "Failed to send command to audio kernel: sending on a closed channel"]
805 fn test_audio_kernel_send_closed_channel() {
806 let (tx, _) = mpsc::channel();
807 let sender = AudioKernelSender::new(tx);
808 sender.send(AudioCommand::Play);
809 }
810
811 #[rstest]
812 #[timeout(Duration::from_secs(3))] fn test_audio_player_kernel_spawn_and_exit(
814 #[from(audio_kernel_sender)] sender: Arc<AudioKernelSender>,
815 ) {
816 init();
817
818 sender.send(AudioCommand::Exit);
819 }
820
821 #[rstest]
822 fn test_volume_control(audio_kernel: AudioKernel) {
823 audio_kernel.volume_control(VolumeCommand::Up(0.1));
824 let volume = *audio_kernel.volume.lock().unwrap();
825 assert!(f32::EPSILON > (volume - 1.1).abs(), "{volume} != 1.1");
826
827 audio_kernel.volume_control(VolumeCommand::Down(0.1));
828 let volume = *audio_kernel.volume.lock().unwrap();
829 assert!(f32::EPSILON > (volume - 1.0).abs(), "{volume} != 1.0");
830
831 audio_kernel.volume_control(VolumeCommand::Set(0.5));
832 let volume = *audio_kernel.volume.lock().unwrap();
833 assert!(f32::EPSILON > (volume - 0.5).abs(), "{volume} != 0.5");
834
835 audio_kernel.volume_control(VolumeCommand::Mute);
836 assert_eq!(
837 audio_kernel
838 .muted
839 .load(std::sync::atomic::Ordering::Relaxed),
840 true
841 );
842
843 audio_kernel.volume_control(VolumeCommand::Unmute);
844 assert_eq!(
845 audio_kernel
846 .muted
847 .load(std::sync::atomic::Ordering::Relaxed),
848 false
849 );
850
851 audio_kernel.volume_control(VolumeCommand::ToggleMute);
852 assert_eq!(
853 audio_kernel
854 .muted
855 .load(std::sync::atomic::Ordering::Relaxed),
856 true
857 );
858
859 audio_kernel.volume_control(VolumeCommand::ToggleMute);
860 assert_eq!(
861 audio_kernel
862 .muted
863 .load(std::sync::atomic::Ordering::Relaxed),
864 false
865 );
866 }
867
868 mod playback_tests {
869 use mecomp_storage::test_utils::{arb_song_case, create_song_metadata, init_test_database};
874 use pretty_assertions::assert_eq;
875 use rstest::rstest;
876
877 use crate::test_utils::init;
878
879 use super::{super::*, audio_kernel, audio_kernel_sender, get_state, sound};
880
881 #[rstest]
882 fn test_audio_kernel_play_pause(
883 audio_kernel: AudioKernel,
884 sound: impl Source<Item = f32> + Send + 'static,
885 ) {
886 audio_kernel.player.append(sound);
887 audio_kernel.play();
888 assert!(!audio_kernel.player.is_paused());
889 audio_kernel.pause();
890 assert!(audio_kernel.player.is_paused());
891 }
892
893 #[rstest]
894 fn test_audio_kernel_toggle_playback(
895 audio_kernel: AudioKernel,
896 sound: impl Source<Item = f32> + Send + 'static,
897 ) {
898 audio_kernel.player.append(sound);
899 audio_kernel.play();
900 assert!(!audio_kernel.player.is_paused());
901 audio_kernel.toggle_playback();
902 assert!(audio_kernel.player.is_paused());
903 audio_kernel.toggle_playback();
904 assert!(!audio_kernel.player.is_paused());
905 }
906
907 #[rstest]
908 #[timeout(Duration::from_secs(5))] #[tokio::test]
910 async fn test_play_pause_toggle_restart(
911 #[from(audio_kernel_sender)] sender: Arc<AudioKernelSender>,
912 ) {
913 init();
914 let db = init_test_database().await.unwrap();
915 let tempdir = tempfile::tempdir().unwrap();
916
917 let song = Song::try_load_into_db(
918 &db,
919 create_song_metadata(&tempdir, arb_song_case()()).unwrap(),
920 )
921 .await
922 .unwrap();
923
924 sender.send(AudioCommand::Queue(QueueCommand::AddToQueue(Box::new(
925 OneOrMany::One(song.clone()),
926 ))));
927
928 let state = get_state(sender.clone()).await;
929 assert_eq!(state.queue_position, Some(0));
930 assert_eq!(state.status, Status::Playing);
931
932 sender.send(AudioCommand::Pause);
933 let state = get_state(sender.clone()).await;
934 assert_eq!(state.status, Status::Paused);
935
936 sender.send(AudioCommand::Play);
937 let state = get_state(sender.clone()).await;
938 assert_eq!(state.status, Status::Playing);
939
940 sender.send(AudioCommand::RestartSong);
941 let state = get_state(sender.clone()).await;
942 assert_eq!(state.status, Status::Playing); sender.send(AudioCommand::TogglePlayback);
945 let state = get_state(sender.clone()).await;
946 assert_eq!(state.status, Status::Paused);
947
948 sender.send(AudioCommand::RestartSong);
949 let state = get_state(sender.clone()).await;
950 assert_eq!(state.status, Status::Paused); sender.send(AudioCommand::Exit);
953 }
954
955 #[rstest]
956 fn test_audio_kernel_stop(audio_kernel: AudioKernel) {
957 init();
958 audio_kernel.player.append(sound());
959 audio_kernel.play();
960 assert!(!audio_kernel.player.is_paused());
961 audio_kernel.stop();
962 assert!(audio_kernel.player.is_paused());
963 assert_eq!(
964 audio_kernel.duration_info.lock().unwrap().time_played,
965 Duration::from_secs(0)
966 );
967 assert_eq!(*audio_kernel.status.lock().unwrap(), Status::Stopped);
968 }
969
970 #[rstest]
971 #[timeout(Duration::from_secs(5))] #[tokio::test]
973 async fn test_audio_kernel_skip_forward(audio_kernel: AudioKernel) {
974 init();
975 let db = init_test_database().await.unwrap();
976 let tempdir = tempfile::tempdir().unwrap();
977
978 let state = audio_kernel.state();
979 assert_eq!(state.queue_position, None);
980 assert!(state.paused());
981 assert_eq!(state.status, Status::Stopped);
982
983 audio_kernel.queue_control(QueueCommand::AddToQueue(Box::new(OneOrMany::Many(vec![
984 Song::try_load_into_db(
985 &db,
986 create_song_metadata(&tempdir, arb_song_case()()).unwrap(),
987 )
988 .await
989 .unwrap(),
990 Song::try_load_into_db(
991 &db,
992 create_song_metadata(&tempdir, arb_song_case()()).unwrap(),
993 )
994 .await
995 .unwrap(),
996 Song::try_load_into_db(
997 &db,
998 create_song_metadata(&tempdir, arb_song_case()()).unwrap(),
999 )
1000 .await
1001 .unwrap(),
1002 ]))));
1003
1004 let state = audio_kernel.state();
1006 assert_eq!(state.queue_position, Some(0));
1007 assert!(!state.paused());
1008 assert_eq!(state.status, Status::Playing);
1009
1010 audio_kernel.queue_control(QueueCommand::SkipForward(1));
1011
1012 let state = audio_kernel.state();
1014 assert_eq!(state.queue_position, Some(1));
1015 assert!(!state.paused());
1016 assert_eq!(state.status, Status::Playing);
1017
1018 audio_kernel.queue_control(QueueCommand::SkipForward(1));
1019
1020 let state = audio_kernel.state();
1022 assert_eq!(state.queue_position, Some(2));
1023 assert!(!state.paused());
1024 assert_eq!(state.status, Status::Playing);
1025
1026 audio_kernel.queue_control(QueueCommand::SkipForward(1));
1027
1028 let state = audio_kernel.state();
1030 assert_eq!(state.queue_position, None);
1031 assert!(state.paused());
1032 assert_eq!(state.status, Status::Stopped);
1033 }
1034
1035 #[rstest]
1036 #[timeout(Duration::from_secs(6))] #[tokio::test]
1038 async fn test_audio_kernel_skip_forward_sender(
1039 #[from(audio_kernel_sender)] sender: Arc<AudioKernelSender>,
1040 ) {
1041 init();
1043
1044 let db = init_test_database().await.unwrap();
1045 let tempdir = tempfile::tempdir().unwrap();
1046
1047 let state = get_state(sender.clone()).await;
1048 assert_eq!(state.queue_position, None);
1049 assert!(state.paused());
1050 assert_eq!(state.status, Status::Stopped);
1051
1052 sender.send(AudioCommand::Queue(QueueCommand::AddToQueue(Box::new(
1053 OneOrMany::Many(vec![
1054 Song::try_load_into_db(
1055 &db,
1056 create_song_metadata(&tempdir, arb_song_case()()).unwrap(),
1057 )
1058 .await
1059 .unwrap(),
1060 Song::try_load_into_db(
1061 &db,
1062 create_song_metadata(&tempdir, arb_song_case()()).unwrap(),
1063 )
1064 .await
1065 .unwrap(),
1066 Song::try_load_into_db(
1067 &db,
1068 create_song_metadata(&tempdir, arb_song_case()()).unwrap(),
1069 )
1070 .await
1071 .unwrap(),
1072 ]),
1073 ))));
1074 let state = get_state(sender.clone()).await;
1076 assert_eq!(state.queue_position, Some(0));
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(1));
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, Some(2));
1091 assert!(!state.paused());
1092 assert_eq!(state.status, Status::Playing);
1093
1094 sender.send(AudioCommand::Queue(QueueCommand::SkipForward(1)));
1095 let state = get_state(sender.clone()).await;
1097 assert_eq!(state.queue_position, None);
1098 assert!(state.paused());
1099 assert_eq!(state.status, Status::Stopped);
1100
1101 sender.send(AudioCommand::Exit);
1102 }
1103
1104 #[rstest]
1105 #[timeout(Duration::from_secs(5))] #[tokio::test]
1107 async fn test_remove_range_from_queue(
1108 #[from(audio_kernel_sender)] sender: Arc<AudioKernelSender>,
1109 ) {
1110 init();
1111 let db = init_test_database().await.unwrap();
1112 let tempdir = tempfile::tempdir().unwrap();
1113 let song1 = Song::try_load_into_db(
1114 &db,
1115 create_song_metadata(&tempdir, arb_song_case()()).unwrap(),
1116 )
1117 .await
1118 .unwrap();
1119 let song2 = Song::try_load_into_db(
1120 &db,
1121 create_song_metadata(&tempdir, arb_song_case()()).unwrap(),
1122 )
1123 .await
1124 .unwrap();
1125
1126 sender.send(AudioCommand::Queue(QueueCommand::AddToQueue(Box::new(
1128 OneOrMany::Many(vec![song1.clone(), song2.clone()]),
1129 ))));
1130 let state = get_state(sender.clone()).await;
1131 assert_eq!(state.queue_position, Some(0));
1132 assert!(!state.paused());
1133 assert_eq!(state.status, Status::Playing);
1134
1135 sender.send(AudioCommand::Pause);
1137
1138 sender.send(AudioCommand::Queue(QueueCommand::RemoveRange(0..1)));
1140 let state = get_state(sender.clone()).await;
1141 assert_eq!(state.queue_position, Some(0));
1142 assert!(state.paused());
1143 assert_eq!(state.status, Status::Stopped);
1144 assert_eq!(state.queue.len(), 1);
1145 assert_eq!(state.queue[0], song2);
1146
1147 sender.send(AudioCommand::Play);
1149
1150 sender.send(AudioCommand::Queue(QueueCommand::AddToQueue(Box::new(
1152 OneOrMany::One(song1.clone()),
1153 ))));
1154 let state = get_state(sender.clone()).await;
1155 assert_eq!(state.queue_position, Some(0));
1156 assert!(!state.paused());
1157 assert_eq!(state.status, Status::Playing);
1158 assert_eq!(state.queue.len(), 2);
1159 assert_eq!(state.queue[0], song2);
1160 assert_eq!(state.queue[1], song1);
1161
1162 sender.send(AudioCommand::Queue(QueueCommand::RemoveRange(1..2)));
1164 let state = get_state(sender.clone()).await;
1165 assert_eq!(state.queue_position, Some(0));
1166 assert!(!state.paused());
1167 assert_eq!(state.status, Status::Playing);
1168 assert_eq!(state.queue.len(), 1);
1169 assert_eq!(state.queue[0], song2);
1170
1171 sender.send(AudioCommand::Exit);
1172 }
1173
1174 #[rstest]
1175 #[timeout(Duration::from_secs(10))] #[tokio::test]
1177 async fn test_audio_kernel_skip_backward(
1178 #[from(audio_kernel_sender)] sender: Arc<AudioKernelSender>,
1179 ) {
1180 init();
1181 let db = init_test_database().await.unwrap();
1182 let tempdir = tempfile::tempdir().unwrap();
1183
1184 let state = get_state(sender.clone()).await;
1185 assert_eq!(state.queue_position, None);
1186 assert!(state.paused());
1187 assert_eq!(state.status, Status::Stopped);
1188
1189 sender.send(AudioCommand::Queue(QueueCommand::AddToQueue(Box::new(
1190 OneOrMany::Many(vec![
1191 Song::try_load_into_db(
1192 &db,
1193 create_song_metadata(&tempdir, arb_song_case()()).unwrap(),
1194 )
1195 .await
1196 .unwrap(),
1197 Song::try_load_into_db(
1198 &db,
1199 create_song_metadata(&tempdir, arb_song_case()()).unwrap(),
1200 )
1201 .await
1202 .unwrap(),
1203 Song::try_load_into_db(
1204 &db,
1205 create_song_metadata(&tempdir, arb_song_case()()).unwrap(),
1206 )
1207 .await
1208 .unwrap(),
1209 ]),
1210 ))));
1211
1212 let state = get_state(sender.clone()).await;
1214 assert_eq!(state.queue_position, Some(0));
1215 assert!(!state.paused());
1216 assert_eq!(state.status, Status::Playing);
1217
1218 sender.send(AudioCommand::Queue(QueueCommand::SkipForward(2)));
1219
1220 let state = get_state(sender.clone()).await;
1222 assert_eq!(state.queue_position, Some(2));
1223 assert!(!state.paused());
1224 assert_eq!(state.status, Status::Playing);
1225
1226 sender.send(AudioCommand::Queue(QueueCommand::SkipBackward(1)));
1227
1228 let state = get_state(sender.clone()).await;
1230 assert_eq!(state.queue_position, Some(1));
1231 assert!(!state.paused());
1232 assert_eq!(state.status, Status::Playing);
1233
1234 sender.send(AudioCommand::Queue(QueueCommand::SkipBackward(1)));
1235
1236 let state = get_state(sender.clone()).await;
1238 assert_eq!(state.queue_position, Some(0));
1239 assert!(!state.paused());
1240
1241 sender.send(AudioCommand::Queue(QueueCommand::SkipBackward(1)));
1242
1243 let state = get_state(sender.clone()).await;
1245 assert_eq!(state.queue_position, None);
1246 assert!(state.paused());
1247 assert_eq!(state.status, Status::Stopped);
1248
1249 sender.send(AudioCommand::Exit);
1250 }
1251
1252 #[rstest]
1253 #[timeout(Duration::from_secs(10))] #[tokio::test]
1255 async fn test_audio_kernel_set_position(
1256 #[from(audio_kernel_sender)] sender: Arc<AudioKernelSender>,
1257 ) {
1258 init();
1259 let db = init_test_database().await.unwrap();
1260 let tempdir = tempfile::tempdir().unwrap();
1261
1262 let state = get_state(sender.clone()).await;
1263 assert_eq!(state.queue_position, None);
1264 assert!(state.paused());
1265 assert_eq!(state.status, Status::Stopped);
1266
1267 sender.send(AudioCommand::Queue(QueueCommand::AddToQueue(Box::new(
1268 OneOrMany::Many(vec![
1269 Song::try_load_into_db(
1270 &db,
1271 create_song_metadata(&tempdir, arb_song_case()()).unwrap(),
1272 )
1273 .await
1274 .unwrap(),
1275 Song::try_load_into_db(
1276 &db,
1277 create_song_metadata(&tempdir, arb_song_case()()).unwrap(),
1278 )
1279 .await
1280 .unwrap(),
1281 Song::try_load_into_db(
1282 &db,
1283 create_song_metadata(&tempdir, arb_song_case()()).unwrap(),
1284 )
1285 .await
1286 .unwrap(),
1287 ]),
1288 ))));
1289 let state = get_state(sender.clone()).await;
1291 assert_eq!(state.queue_position, Some(0));
1292 assert!(!state.paused());
1293 assert_eq!(state.status, Status::Playing);
1294
1295 sender.send(AudioCommand::Queue(QueueCommand::SetPosition(1)));
1296 let state = get_state(sender.clone()).await;
1298 assert_eq!(state.queue_position, Some(1));
1299 assert!(!state.paused());
1300 assert_eq!(state.status, Status::Playing);
1301
1302 sender.send(AudioCommand::Queue(QueueCommand::SetPosition(2)));
1303 let state = get_state(sender.clone()).await;
1305 assert_eq!(state.queue_position, Some(2));
1306 assert!(!state.paused());
1307 assert_eq!(state.status, Status::Playing);
1308
1309 sender.send(AudioCommand::Queue(QueueCommand::SetPosition(0)));
1310 let state = get_state(sender.clone()).await;
1312 assert_eq!(state.queue_position, Some(0));
1313 assert!(!state.paused());
1314 assert_eq!(state.status, Status::Playing);
1315
1316 sender.send(AudioCommand::Queue(QueueCommand::SetPosition(3)));
1317 let state = get_state(sender.clone()).await;
1319 assert_eq!(state.queue_position, Some(2));
1320 assert!(!state.paused());
1321 assert_eq!(state.status, Status::Playing);
1322
1323 sender.send(AudioCommand::Exit);
1324 }
1325
1326 #[rstest]
1327 #[timeout(Duration::from_secs(5))] #[tokio::test]
1329 async fn test_audio_kernel_clear(
1330 #[from(audio_kernel_sender)] sender: Arc<AudioKernelSender>,
1331 ) {
1332 init();
1333 let db = init_test_database().await.unwrap();
1334 let tempdir = tempfile::tempdir().unwrap();
1335
1336 let state = get_state(sender.clone()).await;
1337 assert_eq!(state.queue_position, None);
1338 assert!(state.paused());
1339 assert_eq!(state.status, Status::Stopped);
1340
1341 sender.send(AudioCommand::Queue(QueueCommand::AddToQueue(Box::new(
1342 OneOrMany::Many(vec![
1343 Song::try_load_into_db(
1344 &db,
1345 create_song_metadata(&tempdir, arb_song_case()()).unwrap(),
1346 )
1347 .await
1348 .unwrap(),
1349 Song::try_load_into_db(
1350 &db,
1351 create_song_metadata(&tempdir, arb_song_case()()).unwrap(),
1352 )
1353 .await
1354 .unwrap(),
1355 Song::try_load_into_db(
1356 &db,
1357 create_song_metadata(&tempdir, arb_song_case()()).unwrap(),
1358 )
1359 .await
1360 .unwrap(),
1361 ]),
1362 ))));
1363 let state = get_state(sender.clone()).await;
1365 assert_eq!(state.queue_position, Some(0));
1366 assert_eq!(state.queue.len(), 3);
1367 assert!(!state.paused());
1368 assert_eq!(state.status, Status::Playing);
1369
1370 sender.send(AudioCommand::ClearPlayer);
1371 let state = get_state(sender.clone()).await;
1373 assert_eq!(state.queue_position, Some(0));
1374 assert_eq!(state.queue.len(), 3);
1375 assert!(state.paused());
1376 assert_eq!(state.status, Status::Stopped);
1377
1378 sender.send(AudioCommand::Queue(QueueCommand::Clear));
1379 let state = get_state(sender.clone()).await;
1381 assert_eq!(state.queue_position, None);
1382 assert_eq!(state.queue.len(), 0);
1383 assert!(state.paused());
1384 assert_eq!(state.status, Status::Stopped);
1385
1386 sender.send(AudioCommand::Exit);
1387 }
1388
1389 #[rstest]
1390 #[timeout(Duration::from_secs(5))] #[tokio::test]
1392 async fn test_audio_kernel_shuffle(
1393 #[from(audio_kernel_sender)] sender: Arc<AudioKernelSender>,
1394 ) {
1395 init();
1396 let db = init_test_database().await.unwrap();
1397 let tempdir = tempfile::tempdir().unwrap();
1398
1399 let state = get_state(sender.clone()).await;
1400 assert_eq!(state.queue_position, None);
1401 assert!(state.paused());
1402 assert_eq!(state.status, Status::Stopped);
1403
1404 sender.send(AudioCommand::Queue(QueueCommand::AddToQueue(Box::new(
1405 OneOrMany::Many(vec![
1406 Song::try_load_into_db(
1407 &db,
1408 create_song_metadata(&tempdir, arb_song_case()()).unwrap(),
1409 )
1410 .await
1411 .unwrap(),
1412 Song::try_load_into_db(
1413 &db,
1414 create_song_metadata(&tempdir, arb_song_case()()).unwrap(),
1415 )
1416 .await
1417 .unwrap(),
1418 Song::try_load_into_db(
1419 &db,
1420 create_song_metadata(&tempdir, arb_song_case()()).unwrap(),
1421 )
1422 .await
1423 .unwrap(),
1424 ]),
1425 ))));
1426 let state = get_state(sender.clone()).await;
1428 assert_eq!(state.queue_position, Some(0));
1429 assert_eq!(state.queue.len(), 3);
1430 assert!(!state.paused());
1431 assert_eq!(state.status, Status::Playing);
1432
1433 sender.send(AudioCommand::Queue(QueueCommand::SkipForward(1)));
1435 let state = get_state(sender.clone()).await;
1436 assert_eq!(state.queue_position, Some(1));
1437 assert_eq!(state.queue.len(), 3);
1438 assert!(!state.paused());
1439 assert_eq!(state.status, Status::Playing);
1440
1441 sender.send(AudioCommand::Queue(QueueCommand::Shuffle));
1443 let state = get_state(sender.clone()).await;
1445 assert_eq!(state.queue_position, Some(0));
1446 assert_eq!(state.queue.len(), 3);
1447 assert!(!state.paused());
1448 assert_eq!(state.status, Status::Playing);
1449
1450 sender.send(AudioCommand::Exit);
1451 }
1452
1453 #[rstest]
1454 #[timeout(Duration::from_secs(5))] #[tokio::test]
1456 async fn test_volume_commands(#[from(audio_kernel_sender)] sender: Arc<AudioKernelSender>) {
1457 init();
1458
1459 let state = get_state(sender.clone()).await;
1460 assert!(
1461 f32::EPSILON > (state.volume - 1.0).abs(),
1462 "{} != 1.0",
1463 state.volume
1464 );
1465 assert!(!state.muted);
1466
1467 sender.send(AudioCommand::Volume(VolumeCommand::Up(0.1)));
1468 let state = get_state(sender.clone()).await;
1469 assert!(
1470 f32::EPSILON > (state.volume - 1.1).abs(),
1471 "{} != 1.1",
1472 state.volume
1473 );
1474 assert!(!state.muted);
1475
1476 sender.send(AudioCommand::Volume(VolumeCommand::Down(0.1)));
1477 let state = get_state(sender.clone()).await;
1478 assert!(
1479 f32::EPSILON > (state.volume - 1.0).abs(),
1480 "{} != 1.0",
1481 state.volume
1482 );
1483 assert!(!state.muted);
1484
1485 sender.send(AudioCommand::Volume(VolumeCommand::Set(0.5)));
1486 let state = get_state(sender.clone()).await;
1487 assert!(
1488 f32::EPSILON > (state.volume - 0.5).abs(),
1489 "{} != 0.5",
1490 state.volume
1491 );
1492 assert!(!state.muted);
1493
1494 sender.send(AudioCommand::Volume(VolumeCommand::Mute));
1495 let state = get_state(sender.clone()).await;
1496 assert!(
1497 f32::EPSILON > (state.volume - 0.5).abs(),
1498 "{} != 0.5",
1499 state.volume
1500 ); assert!(state.muted);
1502
1503 sender.send(AudioCommand::Volume(VolumeCommand::Unmute));
1504 let state = get_state(sender.clone()).await;
1505 assert!(
1506 f32::EPSILON > (state.volume - 0.5).abs(),
1507 "{} != 0.5",
1508 state.volume
1509 );
1510 assert!(!state.muted);
1511
1512 sender.send(AudioCommand::Volume(VolumeCommand::ToggleMute));
1513 let state = get_state(sender.clone()).await;
1514 assert!(
1515 f32::EPSILON > (state.volume - 0.5).abs(),
1516 "{} != 0.5",
1517 state.volume
1518 );
1519 assert!(state.muted);
1520
1521 sender.send(AudioCommand::Volume(VolumeCommand::ToggleMute));
1522 let state = get_state(sender.clone()).await;
1523 assert!(
1524 f32::EPSILON > (state.volume - 0.5).abs(),
1525 "{} != 0.5",
1526 state.volume
1527 );
1528 assert!(!state.muted);
1529
1530 sender.send(AudioCommand::Exit);
1531 }
1532
1533 #[rstest]
1534 #[timeout(Duration::from_secs(5))] #[tokio::test]
1536 async fn test_volume_out_of_bounds(
1537 #[from(audio_kernel_sender)] sender: Arc<AudioKernelSender>,
1538 ) {
1539 init();
1540
1541 sender.send(AudioCommand::Volume(VolumeCommand::Up(MAX_VOLUME + 0.5)));
1543 let state = get_state(sender.clone()).await;
1544 assert!(
1545 f32::EPSILON > (state.volume - MAX_VOLUME).abs(),
1546 "{} != {}",
1547 state.volume,
1548 MAX_VOLUME
1549 );
1550 assert!(!state.muted);
1551 sender.send(AudioCommand::Volume(VolumeCommand::Down(
1552 MAX_VOLUME + 0.5 - MIN_VOLUME,
1553 )));
1554 let state = get_state(sender.clone()).await;
1555 assert!(
1556 f32::EPSILON > (state.volume - MIN_VOLUME).abs(),
1557 "{} != {}",
1558 state.volume,
1559 MIN_VOLUME
1560 );
1561 assert!(!state.muted);
1562
1563 sender.send(AudioCommand::Volume(VolumeCommand::Set(MAX_VOLUME + 0.5)));
1565 let state = get_state(sender.clone()).await;
1566 assert!(
1567 f32::EPSILON > (state.volume - MAX_VOLUME).abs(),
1568 "{} != {}",
1569 state.volume,
1570 MAX_VOLUME
1571 );
1572 assert!(!state.muted);
1573 sender.send(AudioCommand::Volume(VolumeCommand::Set(MIN_VOLUME - 0.5)));
1574 let state = get_state(sender.clone()).await;
1575 assert!(
1576 f32::EPSILON > (state.volume - MIN_VOLUME).abs(),
1577 "{} != {}",
1578 state.volume,
1579 MIN_VOLUME
1580 );
1581 assert!(!state.muted);
1582
1583 sender.send(AudioCommand::Exit);
1584 }
1585
1586 #[rstest]
1587 #[timeout(Duration::from_secs(9))] #[tokio::test]
1589 async fn test_seek_commands(#[from(audio_kernel_sender)] sender: Arc<AudioKernelSender>) {
1590 init();
1591 let db = init_test_database().await.unwrap();
1592 let tempdir = tempfile::tempdir().unwrap();
1593
1594 let song = Song::try_load_into_db(
1595 &db,
1596 create_song_metadata(&tempdir, arb_song_case()()).unwrap(),
1597 )
1598 .await
1599 .unwrap();
1600
1601 sender.send(AudioCommand::Queue(QueueCommand::AddToQueue(Box::new(
1604 OneOrMany::One(song.clone()),
1605 ))));
1606 sender.send(AudioCommand::Stop);
1607 let state: StateAudio = get_state(sender.clone()).await;
1608 sender.send(AudioCommand::Seek(
1609 SeekType::Absolute,
1610 Duration::from_secs(0),
1611 ));
1612 assert_eq!(state.queue_position, Some(0));
1613 assert_eq!(state.status, Status::Stopped);
1614 assert_eq!(
1615 state.runtime.unwrap().duration,
1616 Duration::from_secs(10) + Duration::from_nanos(6)
1617 );
1618
1619 sender.send(AudioCommand::Seek(
1621 SeekType::RelativeForwards,
1622 Duration::from_secs(2),
1623 ));
1624 let state = get_state(sender.clone()).await;
1625 assert_eq!(state.runtime.unwrap().seek_position, Duration::from_secs(2));
1626 assert_eq!(state.current_song, Some(song.clone()));
1627 assert_eq!(state.status, Status::Paused);
1628
1629 sender.send(AudioCommand::Seek(
1631 SeekType::RelativeBackwards,
1632 Duration::from_secs(1),
1633 ));
1634 let state = get_state(sender.clone()).await;
1635 assert_eq!(state.runtime.unwrap().seek_position, Duration::from_secs(1));
1636 assert_eq!(state.current_song, Some(song.clone()));
1637 assert_eq!(state.status, Status::Paused);
1638
1639 sender.send(AudioCommand::Seek(
1641 SeekType::Absolute,
1642 Duration::from_secs(9),
1643 ));
1644 let state = get_state(sender.clone()).await;
1645 assert_eq!(state.runtime.unwrap().seek_position, Duration::from_secs(9));
1646 assert_eq!(state.current_song, Some(song.clone()));
1647 assert_eq!(state.status, Status::Paused);
1648
1649 sender.send(AudioCommand::Play);
1651 tokio::time::sleep(Duration::from_millis(1001)).await;
1652 let state = get_state(sender.clone()).await;
1653 assert_eq!(state.queue_position, None);
1654 assert_eq!(state.status, Status::Stopped);
1655
1656 sender.send(AudioCommand::Exit);
1657 }
1658 }
1659}