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 = "Failed to send command to audio kernel: sending on a closed channel"]
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 let volume = *audio_kernel.volume.lock().unwrap();
823 assert!(f32::EPSILON > (volume - 1.1).abs(), "{volume} != 1.1");
824
825 audio_kernel.volume_control(VolumeCommand::Down(0.1));
826 let volume = *audio_kernel.volume.lock().unwrap();
827 assert!(f32::EPSILON > (volume - 1.0).abs(), "{volume} != 1.0");
828
829 audio_kernel.volume_control(VolumeCommand::Set(0.5));
830 let volume = *audio_kernel.volume.lock().unwrap();
831 assert!(f32::EPSILON > (volume - 0.5).abs(), "{volume} != 0.5");
832
833 audio_kernel.volume_control(VolumeCommand::Mute);
834 assert_eq!(
835 audio_kernel
836 .muted
837 .load(std::sync::atomic::Ordering::Relaxed),
838 true
839 );
840
841 audio_kernel.volume_control(VolumeCommand::Unmute);
842 assert_eq!(
843 audio_kernel
844 .muted
845 .load(std::sync::atomic::Ordering::Relaxed),
846 false
847 );
848
849 audio_kernel.volume_control(VolumeCommand::ToggleMute);
850 assert_eq!(
851 audio_kernel
852 .muted
853 .load(std::sync::atomic::Ordering::Relaxed),
854 true
855 );
856
857 audio_kernel.volume_control(VolumeCommand::ToggleMute);
858 assert_eq!(
859 audio_kernel
860 .muted
861 .load(std::sync::atomic::Ordering::Relaxed),
862 false
863 );
864 }
865
866 mod playback_tests {
867 use mecomp_storage::test_utils::{arb_song_case, create_song_metadata, init_test_database};
872 use pretty_assertions::assert_eq;
873 use rstest::rstest;
874
875 use crate::test_utils::init;
876
877 use super::{super::*, audio_kernel, audio_kernel_sender, get_state, sound};
878
879 #[rstest]
880 fn test_audio_kernel_play_pause(
881 audio_kernel: AudioKernel,
882 sound: impl Source<Item = f32> + Send + 'static,
883 ) {
884 audio_kernel.player.append(sound);
885 audio_kernel.play();
886 assert!(!audio_kernel.player.is_paused());
887 audio_kernel.pause();
888 assert!(audio_kernel.player.is_paused());
889 }
890
891 #[rstest]
892 fn test_audio_kernel_toggle_playback(
893 audio_kernel: AudioKernel,
894 sound: impl Source<Item = f32> + Send + 'static,
895 ) {
896 audio_kernel.player.append(sound);
897 audio_kernel.play();
898 assert!(!audio_kernel.player.is_paused());
899 audio_kernel.toggle_playback();
900 assert!(audio_kernel.player.is_paused());
901 audio_kernel.toggle_playback();
902 assert!(!audio_kernel.player.is_paused());
903 }
904
905 #[rstest]
906 #[timeout(Duration::from_secs(5))] #[tokio::test]
908 async fn test_play_pause_toggle_restart(
909 #[from(audio_kernel_sender)] sender: Arc<AudioKernelSender>,
910 ) {
911 init();
912 let db = init_test_database().await.unwrap();
913 let tempdir = tempfile::tempdir().unwrap();
914
915 let song = Song::try_load_into_db(
916 &db,
917 create_song_metadata(&tempdir, arb_song_case()()).unwrap(),
918 )
919 .await
920 .unwrap();
921
922 sender.send(AudioCommand::Queue(QueueCommand::AddToQueue(Box::new(
923 OneOrMany::One(song.clone()),
924 ))));
925
926 let state = get_state(sender.clone()).await;
927 assert_eq!(state.queue_position, Some(0));
928 assert_eq!(state.status, Status::Playing);
929
930 sender.send(AudioCommand::Pause);
931 let state = get_state(sender.clone()).await;
932 assert_eq!(state.status, Status::Paused);
933
934 sender.send(AudioCommand::Play);
935 let state = get_state(sender.clone()).await;
936 assert_eq!(state.status, Status::Playing);
937
938 sender.send(AudioCommand::RestartSong);
939 let state = get_state(sender.clone()).await;
940 assert_eq!(state.status, Status::Playing); sender.send(AudioCommand::TogglePlayback);
943 let state = get_state(sender.clone()).await;
944 assert_eq!(state.status, Status::Paused);
945
946 sender.send(AudioCommand::RestartSong);
947 let state = get_state(sender.clone()).await;
948 assert_eq!(state.status, Status::Paused); sender.send(AudioCommand::Exit);
951 }
952
953 #[rstest]
954 fn test_audio_kernel_stop(audio_kernel: AudioKernel) {
955 init();
956 audio_kernel.player.append(sound());
957 audio_kernel.play();
958 assert!(!audio_kernel.player.is_paused());
959 audio_kernel.stop();
960 assert!(audio_kernel.player.is_paused());
961 assert_eq!(
962 audio_kernel.duration_info.lock().unwrap().time_played,
963 Duration::from_secs(0)
964 );
965 assert_eq!(*audio_kernel.status.lock().unwrap(), Status::Stopped);
966 }
967
968 #[rstest]
969 #[timeout(Duration::from_secs(5))] #[tokio::test]
971 async fn test_audio_kernel_skip_forward(audio_kernel: AudioKernel) {
972 init();
973 let db = init_test_database().await.unwrap();
974 let tempdir = tempfile::tempdir().unwrap();
975
976 let state = audio_kernel.state();
977 assert_eq!(state.queue_position, None);
978 assert!(state.paused());
979 assert_eq!(state.status, Status::Stopped);
980
981 audio_kernel.queue_control(QueueCommand::AddToQueue(Box::new(OneOrMany::Many(vec![
982 Song::try_load_into_db(
983 &db,
984 create_song_metadata(&tempdir, arb_song_case()()).unwrap(),
985 )
986 .await
987 .unwrap(),
988 Song::try_load_into_db(
989 &db,
990 create_song_metadata(&tempdir, arb_song_case()()).unwrap(),
991 )
992 .await
993 .unwrap(),
994 Song::try_load_into_db(
995 &db,
996 create_song_metadata(&tempdir, arb_song_case()()).unwrap(),
997 )
998 .await
999 .unwrap(),
1000 ]))));
1001
1002 let state = audio_kernel.state();
1004 assert_eq!(state.queue_position, Some(0));
1005 assert!(!state.paused());
1006 assert_eq!(state.status, Status::Playing);
1007
1008 audio_kernel.queue_control(QueueCommand::SkipForward(1));
1009
1010 let state = audio_kernel.state();
1012 assert_eq!(state.queue_position, Some(1));
1013 assert!(!state.paused());
1014 assert_eq!(state.status, Status::Playing);
1015
1016 audio_kernel.queue_control(QueueCommand::SkipForward(1));
1017
1018 let state = audio_kernel.state();
1020 assert_eq!(state.queue_position, Some(2));
1021 assert!(!state.paused());
1022 assert_eq!(state.status, Status::Playing);
1023
1024 audio_kernel.queue_control(QueueCommand::SkipForward(1));
1025
1026 let state = audio_kernel.state();
1028 assert_eq!(state.queue_position, None);
1029 assert!(state.paused());
1030 assert_eq!(state.status, Status::Stopped);
1031 }
1032
1033 #[rstest]
1034 #[timeout(Duration::from_secs(6))] #[tokio::test]
1036 async fn test_audio_kernel_skip_forward_sender(
1037 #[from(audio_kernel_sender)] sender: Arc<AudioKernelSender>,
1038 ) {
1039 init();
1041
1042 let db = init_test_database().await.unwrap();
1043 let tempdir = tempfile::tempdir().unwrap();
1044
1045 let state = get_state(sender.clone()).await;
1046 assert_eq!(state.queue_position, None);
1047 assert!(state.paused());
1048 assert_eq!(state.status, Status::Stopped);
1049
1050 sender.send(AudioCommand::Queue(QueueCommand::AddToQueue(Box::new(
1051 OneOrMany::Many(vec![
1052 Song::try_load_into_db(
1053 &db,
1054 create_song_metadata(&tempdir, arb_song_case()()).unwrap(),
1055 )
1056 .await
1057 .unwrap(),
1058 Song::try_load_into_db(
1059 &db,
1060 create_song_metadata(&tempdir, arb_song_case()()).unwrap(),
1061 )
1062 .await
1063 .unwrap(),
1064 Song::try_load_into_db(
1065 &db,
1066 create_song_metadata(&tempdir, arb_song_case()()).unwrap(),
1067 )
1068 .await
1069 .unwrap(),
1070 ]),
1071 ))));
1072 let state = get_state(sender.clone()).await;
1074 assert_eq!(state.queue_position, Some(0));
1075 assert!(!state.paused());
1076 assert_eq!(state.status, Status::Playing);
1077
1078 sender.send(AudioCommand::Queue(QueueCommand::SkipForward(1)));
1079 let state = get_state(sender.clone()).await;
1081 assert_eq!(state.queue_position, Some(1));
1082 assert!(!state.paused());
1083 assert_eq!(state.status, Status::Playing);
1084
1085 sender.send(AudioCommand::Queue(QueueCommand::SkipForward(1)));
1086 let state = get_state(sender.clone()).await;
1088 assert_eq!(state.queue_position, Some(2));
1089 assert!(!state.paused());
1090 assert_eq!(state.status, Status::Playing);
1091
1092 sender.send(AudioCommand::Queue(QueueCommand::SkipForward(1)));
1093 let state = get_state(sender.clone()).await;
1095 assert_eq!(state.queue_position, None);
1096 assert!(state.paused());
1097 assert_eq!(state.status, Status::Stopped);
1098
1099 sender.send(AudioCommand::Exit);
1100 }
1101
1102 #[rstest]
1103 #[timeout(Duration::from_secs(5))] #[tokio::test]
1105 async fn test_remove_range_from_queue(
1106 #[from(audio_kernel_sender)] sender: Arc<AudioKernelSender>,
1107 ) {
1108 init();
1109 let db = init_test_database().await.unwrap();
1110 let tempdir = tempfile::tempdir().unwrap();
1111 let song1 = Song::try_load_into_db(
1112 &db,
1113 create_song_metadata(&tempdir, arb_song_case()()).unwrap(),
1114 )
1115 .await
1116 .unwrap();
1117 let song2 = Song::try_load_into_db(
1118 &db,
1119 create_song_metadata(&tempdir, arb_song_case()()).unwrap(),
1120 )
1121 .await
1122 .unwrap();
1123
1124 sender.send(AudioCommand::Queue(QueueCommand::AddToQueue(Box::new(
1126 OneOrMany::Many(vec![song1.clone(), song2.clone()]),
1127 ))));
1128 let state = get_state(sender.clone()).await;
1129 assert_eq!(state.queue_position, Some(0));
1130 assert!(!state.paused());
1131 assert_eq!(state.status, Status::Playing);
1132
1133 sender.send(AudioCommand::Pause);
1135
1136 sender.send(AudioCommand::Queue(QueueCommand::RemoveRange(0..1)));
1138 let state = get_state(sender.clone()).await;
1139 assert_eq!(state.queue_position, Some(0));
1140 assert!(state.paused());
1141 assert_eq!(state.status, Status::Stopped);
1142 assert_eq!(state.queue.len(), 1);
1143 assert_eq!(state.queue[0], song2);
1144
1145 sender.send(AudioCommand::Play);
1147
1148 sender.send(AudioCommand::Queue(QueueCommand::AddToQueue(Box::new(
1150 OneOrMany::One(song1.clone()),
1151 ))));
1152 let state = get_state(sender.clone()).await;
1153 assert_eq!(state.queue_position, Some(0));
1154 assert!(!state.paused());
1155 assert_eq!(state.status, Status::Playing);
1156 assert_eq!(state.queue.len(), 2);
1157 assert_eq!(state.queue[0], song2);
1158 assert_eq!(state.queue[1], song1);
1159
1160 sender.send(AudioCommand::Queue(QueueCommand::RemoveRange(1..2)));
1162 let state = get_state(sender.clone()).await;
1163 assert_eq!(state.queue_position, Some(0));
1164 assert!(!state.paused());
1165 assert_eq!(state.status, Status::Playing);
1166 assert_eq!(state.queue.len(), 1);
1167 assert_eq!(state.queue[0], song2);
1168
1169 sender.send(AudioCommand::Exit);
1170 }
1171
1172 #[rstest]
1173 #[timeout(Duration::from_secs(10))] #[tokio::test]
1175 async fn test_audio_kernel_skip_backward(
1176 #[from(audio_kernel_sender)] sender: Arc<AudioKernelSender>,
1177 ) {
1178 init();
1179 let db = init_test_database().await.unwrap();
1180 let tempdir = tempfile::tempdir().unwrap();
1181
1182 let state = get_state(sender.clone()).await;
1183 assert_eq!(state.queue_position, None);
1184 assert!(state.paused());
1185 assert_eq!(state.status, Status::Stopped);
1186
1187 sender.send(AudioCommand::Queue(QueueCommand::AddToQueue(Box::new(
1188 OneOrMany::Many(vec![
1189 Song::try_load_into_db(
1190 &db,
1191 create_song_metadata(&tempdir, arb_song_case()()).unwrap(),
1192 )
1193 .await
1194 .unwrap(),
1195 Song::try_load_into_db(
1196 &db,
1197 create_song_metadata(&tempdir, arb_song_case()()).unwrap(),
1198 )
1199 .await
1200 .unwrap(),
1201 Song::try_load_into_db(
1202 &db,
1203 create_song_metadata(&tempdir, arb_song_case()()).unwrap(),
1204 )
1205 .await
1206 .unwrap(),
1207 ]),
1208 ))));
1209
1210 let state = get_state(sender.clone()).await;
1212 assert_eq!(state.queue_position, Some(0));
1213 assert!(!state.paused());
1214 assert_eq!(state.status, Status::Playing);
1215
1216 sender.send(AudioCommand::Queue(QueueCommand::SkipForward(2)));
1217
1218 let state = get_state(sender.clone()).await;
1220 assert_eq!(state.queue_position, Some(2));
1221 assert!(!state.paused());
1222 assert_eq!(state.status, Status::Playing);
1223
1224 sender.send(AudioCommand::Queue(QueueCommand::SkipBackward(1)));
1225
1226 let state = get_state(sender.clone()).await;
1228 assert_eq!(state.queue_position, Some(1));
1229 assert!(!state.paused());
1230 assert_eq!(state.status, Status::Playing);
1231
1232 sender.send(AudioCommand::Queue(QueueCommand::SkipBackward(1)));
1233
1234 let state = get_state(sender.clone()).await;
1236 assert_eq!(state.queue_position, Some(0));
1237 assert!(!state.paused());
1238
1239 sender.send(AudioCommand::Queue(QueueCommand::SkipBackward(1)));
1240
1241 let state = get_state(sender.clone()).await;
1243 assert_eq!(state.queue_position, None);
1244 assert!(state.paused());
1245 assert_eq!(state.status, Status::Stopped);
1246
1247 sender.send(AudioCommand::Exit);
1248 }
1249
1250 #[rstest]
1251 #[timeout(Duration::from_secs(10))] #[tokio::test]
1253 async fn test_audio_kernel_set_position(
1254 #[from(audio_kernel_sender)] sender: Arc<AudioKernelSender>,
1255 ) {
1256 init();
1257 let db = init_test_database().await.unwrap();
1258 let tempdir = tempfile::tempdir().unwrap();
1259
1260 let state = get_state(sender.clone()).await;
1261 assert_eq!(state.queue_position, None);
1262 assert!(state.paused());
1263 assert_eq!(state.status, Status::Stopped);
1264
1265 sender.send(AudioCommand::Queue(QueueCommand::AddToQueue(Box::new(
1266 OneOrMany::Many(vec![
1267 Song::try_load_into_db(
1268 &db,
1269 create_song_metadata(&tempdir, arb_song_case()()).unwrap(),
1270 )
1271 .await
1272 .unwrap(),
1273 Song::try_load_into_db(
1274 &db,
1275 create_song_metadata(&tempdir, arb_song_case()()).unwrap(),
1276 )
1277 .await
1278 .unwrap(),
1279 Song::try_load_into_db(
1280 &db,
1281 create_song_metadata(&tempdir, arb_song_case()()).unwrap(),
1282 )
1283 .await
1284 .unwrap(),
1285 ]),
1286 ))));
1287 let state = get_state(sender.clone()).await;
1289 assert_eq!(state.queue_position, Some(0));
1290 assert!(!state.paused());
1291 assert_eq!(state.status, Status::Playing);
1292
1293 sender.send(AudioCommand::Queue(QueueCommand::SetPosition(1)));
1294 let state = get_state(sender.clone()).await;
1296 assert_eq!(state.queue_position, Some(1));
1297 assert!(!state.paused());
1298 assert_eq!(state.status, Status::Playing);
1299
1300 sender.send(AudioCommand::Queue(QueueCommand::SetPosition(2)));
1301 let state = get_state(sender.clone()).await;
1303 assert_eq!(state.queue_position, Some(2));
1304 assert!(!state.paused());
1305 assert_eq!(state.status, Status::Playing);
1306
1307 sender.send(AudioCommand::Queue(QueueCommand::SetPosition(0)));
1308 let state = get_state(sender.clone()).await;
1310 assert_eq!(state.queue_position, Some(0));
1311 assert!(!state.paused());
1312 assert_eq!(state.status, Status::Playing);
1313
1314 sender.send(AudioCommand::Queue(QueueCommand::SetPosition(3)));
1315 let state = get_state(sender.clone()).await;
1317 assert_eq!(state.queue_position, Some(2));
1318 assert!(!state.paused());
1319 assert_eq!(state.status, Status::Playing);
1320
1321 sender.send(AudioCommand::Exit);
1322 }
1323
1324 #[rstest]
1325 #[timeout(Duration::from_secs(5))] #[tokio::test]
1327 async fn test_audio_kernel_clear(
1328 #[from(audio_kernel_sender)] sender: Arc<AudioKernelSender>,
1329 ) {
1330 init();
1331 let db = init_test_database().await.unwrap();
1332 let tempdir = tempfile::tempdir().unwrap();
1333
1334 let state = get_state(sender.clone()).await;
1335 assert_eq!(state.queue_position, None);
1336 assert!(state.paused());
1337 assert_eq!(state.status, Status::Stopped);
1338
1339 sender.send(AudioCommand::Queue(QueueCommand::AddToQueue(Box::new(
1340 OneOrMany::Many(vec![
1341 Song::try_load_into_db(
1342 &db,
1343 create_song_metadata(&tempdir, arb_song_case()()).unwrap(),
1344 )
1345 .await
1346 .unwrap(),
1347 Song::try_load_into_db(
1348 &db,
1349 create_song_metadata(&tempdir, arb_song_case()()).unwrap(),
1350 )
1351 .await
1352 .unwrap(),
1353 Song::try_load_into_db(
1354 &db,
1355 create_song_metadata(&tempdir, arb_song_case()()).unwrap(),
1356 )
1357 .await
1358 .unwrap(),
1359 ]),
1360 ))));
1361 let state = get_state(sender.clone()).await;
1363 assert_eq!(state.queue_position, Some(0));
1364 assert_eq!(state.queue.len(), 3);
1365 assert!(!state.paused());
1366 assert_eq!(state.status, Status::Playing);
1367
1368 sender.send(AudioCommand::ClearPlayer);
1369 let state = get_state(sender.clone()).await;
1371 assert_eq!(state.queue_position, Some(0));
1372 assert_eq!(state.queue.len(), 3);
1373 assert!(state.paused());
1374 assert_eq!(state.status, Status::Stopped);
1375
1376 sender.send(AudioCommand::Queue(QueueCommand::Clear));
1377 let state = get_state(sender.clone()).await;
1379 assert_eq!(state.queue_position, None);
1380 assert_eq!(state.queue.len(), 0);
1381 assert!(state.paused());
1382 assert_eq!(state.status, Status::Stopped);
1383
1384 sender.send(AudioCommand::Exit);
1385 }
1386
1387 #[rstest]
1388 #[timeout(Duration::from_secs(5))] #[tokio::test]
1390 async fn test_audio_kernel_shuffle(
1391 #[from(audio_kernel_sender)] sender: Arc<AudioKernelSender>,
1392 ) {
1393 init();
1394 let db = init_test_database().await.unwrap();
1395 let tempdir = tempfile::tempdir().unwrap();
1396
1397 let state = get_state(sender.clone()).await;
1398 assert_eq!(state.queue_position, None);
1399 assert!(state.paused());
1400 assert_eq!(state.status, Status::Stopped);
1401
1402 sender.send(AudioCommand::Queue(QueueCommand::AddToQueue(Box::new(
1403 OneOrMany::Many(vec![
1404 Song::try_load_into_db(
1405 &db,
1406 create_song_metadata(&tempdir, arb_song_case()()).unwrap(),
1407 )
1408 .await
1409 .unwrap(),
1410 Song::try_load_into_db(
1411 &db,
1412 create_song_metadata(&tempdir, arb_song_case()()).unwrap(),
1413 )
1414 .await
1415 .unwrap(),
1416 Song::try_load_into_db(
1417 &db,
1418 create_song_metadata(&tempdir, arb_song_case()()).unwrap(),
1419 )
1420 .await
1421 .unwrap(),
1422 ]),
1423 ))));
1424 let state = get_state(sender.clone()).await;
1426 assert_eq!(state.queue_position, Some(0));
1427 assert_eq!(state.queue.len(), 3);
1428 assert!(!state.paused());
1429 assert_eq!(state.status, Status::Playing);
1430
1431 sender.send(AudioCommand::Queue(QueueCommand::SkipForward(1)));
1433 let state = get_state(sender.clone()).await;
1434 assert_eq!(state.queue_position, Some(1));
1435 assert_eq!(state.queue.len(), 3);
1436 assert!(!state.paused());
1437 assert_eq!(state.status, Status::Playing);
1438
1439 sender.send(AudioCommand::Queue(QueueCommand::Shuffle));
1441 let state = get_state(sender.clone()).await;
1443 assert_eq!(state.queue_position, Some(0));
1444 assert_eq!(state.queue.len(), 3);
1445 assert!(!state.paused());
1446 assert_eq!(state.status, Status::Playing);
1447
1448 sender.send(AudioCommand::Exit);
1449 }
1450
1451 #[rstest]
1452 #[timeout(Duration::from_secs(5))] #[tokio::test]
1454 async fn test_volume_commands(#[from(audio_kernel_sender)] sender: Arc<AudioKernelSender>) {
1455 init();
1456
1457 let state = get_state(sender.clone()).await;
1458 assert!(
1459 f32::EPSILON > (state.volume - 1.0).abs(),
1460 "{} != 1.0",
1461 state.volume
1462 );
1463 assert!(!state.muted);
1464
1465 sender.send(AudioCommand::Volume(VolumeCommand::Up(0.1)));
1466 let state = get_state(sender.clone()).await;
1467 assert!(
1468 f32::EPSILON > (state.volume - 1.1).abs(),
1469 "{} != 1.1",
1470 state.volume
1471 );
1472 assert!(!state.muted);
1473
1474 sender.send(AudioCommand::Volume(VolumeCommand::Down(0.1)));
1475 let state = get_state(sender.clone()).await;
1476 assert!(
1477 f32::EPSILON > (state.volume - 1.0).abs(),
1478 "{} != 1.0",
1479 state.volume
1480 );
1481 assert!(!state.muted);
1482
1483 sender.send(AudioCommand::Volume(VolumeCommand::Set(0.5)));
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 );
1490 assert!(!state.muted);
1491
1492 sender.send(AudioCommand::Volume(VolumeCommand::Mute));
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 ); assert!(state.muted);
1500
1501 sender.send(AudioCommand::Volume(VolumeCommand::Unmute));
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::Volume(VolumeCommand::ToggleMute));
1520 let state = get_state(sender.clone()).await;
1521 assert!(
1522 f32::EPSILON > (state.volume - 0.5).abs(),
1523 "{} != 0.5",
1524 state.volume
1525 );
1526 assert!(!state.muted);
1527
1528 sender.send(AudioCommand::Exit);
1529 }
1530
1531 #[rstest]
1532 #[timeout(Duration::from_secs(5))] #[tokio::test]
1534 async fn test_volume_out_of_bounds(
1535 #[from(audio_kernel_sender)] sender: Arc<AudioKernelSender>,
1536 ) {
1537 init();
1538
1539 sender.send(AudioCommand::Volume(VolumeCommand::Up(MAX_VOLUME + 0.5)));
1541 let state = get_state(sender.clone()).await;
1542 assert!(
1543 f32::EPSILON > (state.volume - MAX_VOLUME).abs(),
1544 "{} != {}",
1545 state.volume,
1546 MAX_VOLUME
1547 );
1548 assert!(!state.muted);
1549 sender.send(AudioCommand::Volume(VolumeCommand::Down(
1550 MAX_VOLUME + 0.5 - MIN_VOLUME,
1551 )));
1552 let state = get_state(sender.clone()).await;
1553 assert!(
1554 f32::EPSILON > (state.volume - MIN_VOLUME).abs(),
1555 "{} != {}",
1556 state.volume,
1557 MIN_VOLUME
1558 );
1559 assert!(!state.muted);
1560
1561 sender.send(AudioCommand::Volume(VolumeCommand::Set(MAX_VOLUME + 0.5)));
1563 let state = get_state(sender.clone()).await;
1564 assert!(
1565 f32::EPSILON > (state.volume - MAX_VOLUME).abs(),
1566 "{} != {}",
1567 state.volume,
1568 MAX_VOLUME
1569 );
1570 assert!(!state.muted);
1571 sender.send(AudioCommand::Volume(VolumeCommand::Set(MIN_VOLUME - 0.5)));
1572 let state = get_state(sender.clone()).await;
1573 assert!(
1574 f32::EPSILON > (state.volume - MIN_VOLUME).abs(),
1575 "{} != {}",
1576 state.volume,
1577 MIN_VOLUME
1578 );
1579 assert!(!state.muted);
1580
1581 sender.send(AudioCommand::Exit);
1582 }
1583
1584 #[rstest]
1585 #[timeout(Duration::from_secs(9))] #[tokio::test]
1587 async fn test_seek_commands(#[from(audio_kernel_sender)] sender: Arc<AudioKernelSender>) {
1588 init();
1589 let db = init_test_database().await.unwrap();
1590 let tempdir = tempfile::tempdir().unwrap();
1591
1592 let song = Song::try_load_into_db(
1593 &db,
1594 create_song_metadata(&tempdir, arb_song_case()()).unwrap(),
1595 )
1596 .await
1597 .unwrap();
1598
1599 sender.send(AudioCommand::Queue(QueueCommand::AddToQueue(Box::new(
1602 OneOrMany::One(song.clone()),
1603 ))));
1604 sender.send(AudioCommand::Stop);
1605 let state: StateAudio = get_state(sender.clone()).await;
1606 sender.send(AudioCommand::Seek(
1607 SeekType::Absolute,
1608 Duration::from_secs(0),
1609 ));
1610 assert_eq!(state.queue_position, Some(0));
1611 assert_eq!(state.status, Status::Stopped);
1612 assert_eq!(
1613 state.runtime.unwrap().duration,
1614 Duration::from_secs(10) + Duration::from_nanos(6)
1615 );
1616
1617 sender.send(AudioCommand::Seek(
1619 SeekType::RelativeForwards,
1620 Duration::from_secs(2),
1621 ));
1622 let state = get_state(sender.clone()).await;
1623 assert_eq!(state.runtime.unwrap().seek_position, Duration::from_secs(2));
1624 assert_eq!(state.current_song, Some(song.clone()));
1625 assert_eq!(state.status, Status::Paused);
1626
1627 sender.send(AudioCommand::Seek(
1629 SeekType::RelativeBackwards,
1630 Duration::from_secs(1),
1631 ));
1632 let state = get_state(sender.clone()).await;
1633 assert_eq!(state.runtime.unwrap().seek_position, Duration::from_secs(1));
1634 assert_eq!(state.current_song, Some(song.clone()));
1635 assert_eq!(state.status, Status::Paused);
1636
1637 sender.send(AudioCommand::Seek(
1639 SeekType::Absolute,
1640 Duration::from_secs(9),
1641 ));
1642 let state = get_state(sender.clone()).await;
1643 assert_eq!(state.runtime.unwrap().seek_position, Duration::from_secs(9));
1644 assert_eq!(state.current_song, Some(song.clone()));
1645 assert_eq!(state.status, Status::Paused);
1646
1647 sender.send(AudioCommand::Play);
1649 tokio::time::sleep(Duration::from_millis(1001)).await;
1650 let state = get_state(sender.clone()).await;
1651 assert_eq!(state.queue_position, None);
1652 assert_eq!(state.status, Status::Stopped);
1653
1654 sender.send(AudioCommand::Exit);
1655 }
1656 }
1657}