use crate::{AudioSource, Decodable, Volume};1use bevy_asset::{Asset, Handle};2use bevy_ecs::prelude::*;3use bevy_math::Vec3;4use bevy_reflect::prelude::*;5use bevy_transform::components::Transform;67/// The way Bevy manages the sound playback.8#[derive(Debug, Clone, Copy, Reflect)]9#[reflect(Clone)]10pub enum PlaybackMode {11/// Play the sound once. Do nothing when it ends.12///13/// Note: It is not possible to reuse an [`AudioPlayer`] after it has finished playing and14/// the underlying [`AudioSink`](crate::AudioSink) or [`SpatialAudioSink`](crate::SpatialAudioSink) has been drained.15///16/// To replay a sound, the audio components provided by [`AudioPlayer`] must be removed and17/// added again.18Once,19/// Repeat the sound forever.20Loop,21/// Despawn the entity and its children when the sound finishes playing.22Despawn,23/// Remove the audio components from the entity, when the sound finishes playing.24Remove,25}2627/// Initial settings to be used when audio starts playing.28///29/// If you would like to control the audio while it is playing, query for the30/// [`AudioSink`](crate::AudioSink) or [`SpatialAudioSink`](crate::SpatialAudioSink)31/// components. Changes to this component will *not* be applied to already-playing audio.32#[derive(Component, Clone, Copy, Debug, Reflect)]33#[reflect(Clone, Default, Component, Debug)]34pub struct PlaybackSettings {35/// The desired playback behavior.36pub mode: PlaybackMode,37/// Volume to play at.38pub volume: Volume,39/// Speed to play at.40pub speed: f32,41/// Create the sink in paused state.42/// Useful for "deferred playback", if you want to prepare43/// the entity, but hear the sound later.44pub paused: bool,45/// Whether to create the sink in muted state or not.46///47/// This is useful for audio that should be initially muted. You can still48/// set the initial volume and it is applied when the audio is unmuted.49pub muted: bool,50/// Enables spatial audio for this source.51///52/// See also: [`SpatialListener`].53///54/// Note: Bevy does not currently support HRTF or any other high-quality 3D sound rendering55/// features. Spatial audio is implemented via simple left-right stereo panning.56pub spatial: bool,57/// Optional scale factor applied to the positions of this audio source and the listener,58/// overriding the default value configured on [`AudioPlugin::default_spatial_scale`](crate::AudioPlugin::default_spatial_scale).59pub spatial_scale: Option<SpatialScale>,60/// The point in time in the audio clip where playback should start. If set to `None`, it will61/// play from the beginning of the clip.62///63/// If the playback mode is set to `Loop`, each loop will start from this position.64pub start_position: Option<core::time::Duration>,65/// How long the audio should play before stopping. If set, the clip will play for at most66/// the specified duration. If set to `None`, it will play for as long as it can.67///68/// If the playback mode is set to `Loop`, each loop will last for this duration.69pub duration: Option<core::time::Duration>,70}7172impl Default for PlaybackSettings {73fn default() -> Self {74Self::ONCE75}76}7778impl PlaybackSettings {79/// Will play the associated audio source once.80///81/// Note: It is not possible to reuse an [`AudioPlayer`] after it has finished playing and82/// the underlying [`AudioSink`](crate::AudioSink) or [`SpatialAudioSink`](crate::SpatialAudioSink) has been drained.83///84/// To replay a sound, the audio components provided by [`AudioPlayer`] must be removed and85/// added again.86pub const ONCE: PlaybackSettings = PlaybackSettings {87mode: PlaybackMode::Once,88volume: Volume::Linear(1.0),89speed: 1.0,90paused: false,91muted: false,92spatial: false,93spatial_scale: None,94start_position: None,95duration: None,96};9798/// Will play the associated audio source in a loop.99pub const LOOP: PlaybackSettings = PlaybackSettings {100mode: PlaybackMode::Loop,101..PlaybackSettings::ONCE102};103104/// Will play the associated audio source once and despawn the entity afterwards.105pub const DESPAWN: PlaybackSettings = PlaybackSettings {106mode: PlaybackMode::Despawn,107..PlaybackSettings::ONCE108};109110/// Will play the associated audio source once and remove the audio components afterwards.111pub const REMOVE: PlaybackSettings = PlaybackSettings {112mode: PlaybackMode::Remove,113..PlaybackSettings::ONCE114};115116/// Helper to start in a paused state.117pub const fn paused(mut self) -> Self {118self.paused = true;119self120}121122/// Helper to start muted.123pub const fn muted(mut self) -> Self {124self.muted = true;125self126}127128/// Helper to set the volume from start of playback.129pub const fn with_volume(mut self, volume: Volume) -> Self {130self.volume = volume;131self132}133134/// Helper to set the speed from start of playback.135pub const fn with_speed(mut self, speed: f32) -> Self {136self.speed = speed;137self138}139140/// Helper to enable or disable spatial audio.141pub const fn with_spatial(mut self, spatial: bool) -> Self {142self.spatial = spatial;143self144}145146/// Helper to use a custom spatial scale.147pub const fn with_spatial_scale(mut self, spatial_scale: SpatialScale) -> Self {148self.spatial_scale = Some(spatial_scale);149self150}151152/// Helper to use a custom playback start position.153pub const fn with_start_position(mut self, start_position: core::time::Duration) -> Self {154self.start_position = Some(start_position);155self156}157158/// Helper to use a custom playback duration.159pub const fn with_duration(mut self, duration: core::time::Duration) -> Self {160self.duration = Some(duration);161self162}163}164165/// Settings for the listener for spatial audio sources.166///167/// This is accompanied by [`Transform`] and [`GlobalTransform`](bevy_transform::prelude::GlobalTransform).168/// Only one entity with a [`SpatialListener`] should be present at any given time.169#[derive(Component, Clone, Debug, Reflect)]170#[require(Transform)]171#[reflect(Clone, Default, Component, Debug)]172pub struct SpatialListener {173/// Left ear position relative to the [`GlobalTransform`](bevy_transform::prelude::GlobalTransform).174pub left_ear_offset: Vec3,175/// Right ear position relative to the [`GlobalTransform`](bevy_transform::prelude::GlobalTransform).176pub right_ear_offset: Vec3,177}178179impl Default for SpatialListener {180fn default() -> Self {181Self::new(4.)182}183}184185impl SpatialListener {186/// Creates a new [`SpatialListener`] component.187///188/// `gap` is the distance between the left and right "ears" of the listener. Ears are189/// positioned on the x axis.190pub fn new(gap: f32) -> Self {191SpatialListener {192left_ear_offset: Vec3::X * gap / -2.0,193right_ear_offset: Vec3::X * gap / 2.0,194}195}196}197198/// A scale factor applied to the positions of audio sources and listeners for199/// spatial audio.200///201/// Default is `Vec3::ONE`.202#[derive(Clone, Copy, Debug, Reflect)]203#[reflect(Clone, Default)]204pub struct SpatialScale(pub Vec3);205206impl SpatialScale {207/// Create a new [`SpatialScale`] with the same value for all 3 dimensions.208pub const fn new(scale: f32) -> Self {209Self(Vec3::splat(scale))210}211212/// Create a new [`SpatialScale`] with the same value for `x` and `y`, and `0.0`213/// for `z`.214pub const fn new_2d(scale: f32) -> Self {215Self(Vec3::new(scale, scale, 0.0))216}217}218219impl Default for SpatialScale {220fn default() -> Self {221Self(Vec3::ONE)222}223}224225/// The default scale factor applied to the positions of audio sources and listeners for226/// spatial audio. Can be overridden for individual sounds in [`PlaybackSettings`].227///228/// You may need to adjust this scale to fit your world's units.229///230/// Default is `Vec3::ONE`.231#[derive(Resource, Default, Clone, Copy, Reflect)]232#[reflect(Resource, Default, Clone)]233pub struct DefaultSpatialScale(pub SpatialScale);234235/// A component for playing a sound.236///237/// Insert this component onto an entity to trigger an audio source to begin playing.238///239/// If the handle refers to an unavailable asset (such as if it has not finished loading yet),240/// the audio will not begin playing immediately. The audio will play when the asset is ready.241///242/// When Bevy begins the audio playback, an [`AudioSink`](crate::AudioSink) component will be243/// added to the entity. You can use that component to control the audio settings during playback.244///245/// Playback can be configured using the [`PlaybackSettings`] component. Note that changes to the246/// [`PlaybackSettings`] component will *not* affect already-playing audio.247#[derive(Component, Reflect)]248#[reflect(Component, Clone)]249#[require(PlaybackSettings)]250pub struct AudioPlayer<Source = AudioSource>(pub Handle<Source>)251where252Source: Asset + Decodable;253254impl<Source> Clone for AudioPlayer<Source>255where256Source: Asset + Decodable,257{258fn clone(&self) -> Self {259Self(self.0.clone())260}261}262263impl AudioPlayer<AudioSource> {264/// Creates a new [`AudioPlayer`] with the given [`Handle<AudioSource>`].265///266/// For convenience reasons, this hard-codes the [`AudioSource`] type. If you want to267/// initialize an [`AudioPlayer`] with a different type, just initialize it directly using normal268/// tuple struct syntax.269pub fn new(source: Handle<AudioSource>) -> Self {270Self(source)271}272}273274275