use crate::Volume;
use bevy_ecs::component::Component;
use bevy_math::Vec3;
use bevy_transform::prelude::Transform;
use core::time::Duration;
pub use rodio::source::SeekError;
use rodio::{Sink, SpatialSink};
pub trait AudioSinkPlayback {
fn volume(&self) -> Volume;
fn set_volume(&mut self, volume: Volume);
fn speed(&self) -> f32;
fn set_speed(&self, speed: f32);
fn play(&self);
fn position(&self) -> Duration;
fn try_seek(&self, pos: Duration) -> Result<(), SeekError>;
fn pause(&self);
fn toggle_playback(&self) {
if self.is_paused() {
self.play();
} else {
self.pause();
}
}
fn is_paused(&self) -> bool;
fn stop(&self);
fn empty(&self) -> bool;
fn is_muted(&self) -> bool;
fn mute(&mut self);
fn unmute(&mut self);
fn toggle_mute(&mut self) {
if self.is_muted() {
self.unmute();
} else {
self.mute();
}
}
}
#[derive(Component)]
pub struct AudioSink {
pub(crate) sink: Sink,
pub(crate) managed_volume: Option<Volume>,
}
impl AudioSink {
pub fn new(sink: Sink) -> Self {
Self {
sink,
managed_volume: None,
}
}
}
impl AudioSinkPlayback for AudioSink {
fn volume(&self) -> Volume {
self.managed_volume
.unwrap_or_else(|| Volume::Linear(self.sink.volume()))
}
fn set_volume(&mut self, volume: Volume) {
if self.is_muted() {
self.managed_volume = Some(volume);
} else {
self.sink.set_volume(volume.to_linear());
}
}
fn speed(&self) -> f32 {
self.sink.speed()
}
fn set_speed(&self, speed: f32) {
self.sink.set_speed(speed);
}
fn play(&self) {
self.sink.play();
}
fn position(&self) -> Duration {
self.sink.get_pos()
}
fn try_seek(&self, pos: Duration) -> Result<(), SeekError> {
self.sink.try_seek(pos)
}
fn pause(&self) {
self.sink.pause();
}
fn is_paused(&self) -> bool {
self.sink.is_paused()
}
fn stop(&self) {
self.sink.stop();
}
fn empty(&self) -> bool {
self.sink.empty()
}
fn is_muted(&self) -> bool {
self.managed_volume.is_some()
}
fn mute(&mut self) {
self.managed_volume = Some(self.volume());
self.sink.set_volume(0.0);
}
fn unmute(&mut self) {
if let Some(volume) = self.managed_volume.take() {
self.sink.set_volume(volume.to_linear());
}
}
}
#[derive(Component)]
pub struct SpatialAudioSink {
pub(crate) sink: SpatialSink,
pub(crate) managed_volume: Option<Volume>,
}
impl SpatialAudioSink {
pub fn new(sink: SpatialSink) -> Self {
Self {
sink,
managed_volume: None,
}
}
}
impl AudioSinkPlayback for SpatialAudioSink {
fn volume(&self) -> Volume {
self.managed_volume
.unwrap_or_else(|| Volume::Linear(self.sink.volume()))
}
fn set_volume(&mut self, volume: Volume) {
if self.is_muted() {
self.managed_volume = Some(volume);
} else {
self.sink.set_volume(volume.to_linear());
}
}
fn speed(&self) -> f32 {
self.sink.speed()
}
fn set_speed(&self, speed: f32) {
self.sink.set_speed(speed);
}
fn play(&self) {
self.sink.play();
}
fn position(&self) -> Duration {
self.sink.get_pos()
}
fn try_seek(&self, pos: Duration) -> Result<(), SeekError> {
self.sink.try_seek(pos)
}
fn pause(&self) {
self.sink.pause();
}
fn is_paused(&self) -> bool {
self.sink.is_paused()
}
fn stop(&self) {
self.sink.stop();
}
fn empty(&self) -> bool {
self.sink.empty()
}
fn is_muted(&self) -> bool {
self.managed_volume.is_some()
}
fn mute(&mut self) {
self.managed_volume = Some(self.volume());
self.sink.set_volume(0.0);
}
fn unmute(&mut self) {
if let Some(volume) = self.managed_volume.take() {
self.sink.set_volume(volume.to_linear());
}
}
}
impl SpatialAudioSink {
pub fn set_ears_position(&self, left_position: Vec3, right_position: Vec3) {
self.sink.set_left_ear_position(left_position.to_array());
self.sink.set_right_ear_position(right_position.to_array());
}
pub fn set_listener_position(&self, position: Transform, gap: f32) {
self.set_ears_position(
position.translation + position.left() * gap / 2.0,
position.translation + position.right() * gap / 2.0,
);
}
pub fn set_emitter_position(&self, position: Vec3) {
self.sink.set_emitter_position(position.to_array());
}
}
#[cfg(test)]
mod tests {
use rodio::Sink;
use super::*;
fn test_audio_sink_playback<T: AudioSinkPlayback>(mut audio_sink: T) {
assert_eq!(audio_sink.volume(), Volume::Linear(1.0)); audio_sink.set_volume(Volume::Linear(0.5));
assert_eq!(audio_sink.volume(), Volume::Linear(0.5));
audio_sink.set_volume(Volume::Linear(1.0));
assert_eq!(audio_sink.volume(), Volume::Linear(1.0));
assert_eq!(audio_sink.speed(), 1.0); audio_sink.set_speed(0.5);
assert_eq!(audio_sink.speed(), 0.5);
audio_sink.set_speed(1.0);
assert_eq!(audio_sink.speed(), 1.0);
assert!(!audio_sink.is_paused()); audio_sink.pause();
assert!(audio_sink.is_paused());
audio_sink.play();
assert!(!audio_sink.is_paused());
audio_sink.pause(); audio_sink.toggle_playback();
assert!(!audio_sink.is_paused());
audio_sink.toggle_playback();
assert!(audio_sink.is_paused());
assert!(!audio_sink.is_muted()); audio_sink.mute();
assert!(audio_sink.is_muted());
audio_sink.unmute();
assert!(!audio_sink.is_muted());
audio_sink.set_volume(Volume::Linear(0.5));
audio_sink.mute();
assert_eq!(audio_sink.volume(), Volume::Linear(0.5)); audio_sink.unmute();
assert_eq!(audio_sink.volume(), Volume::Linear(0.5));
audio_sink.toggle_mute();
assert!(audio_sink.is_muted());
audio_sink.toggle_mute();
assert!(!audio_sink.is_muted());
}
#[test]
fn test_audio_sink() {
let (sink, _queue_rx) = Sink::new_idle();
let audio_sink = AudioSink::new(sink);
test_audio_sink_playback(audio_sink);
}
}