Skip to main content

bevy_audio/
audio.rs

1use crate::{AudioSource, Decodable, Volume};
2use bevy_asset::{Asset, Handle};
3use bevy_ecs::prelude::*;
4use bevy_math::Vec3;
5use bevy_reflect::prelude::*;
6use bevy_transform::components::Transform;
7
8/// The way Bevy manages the sound playback.
9#[derive(Debug, Clone, Copy, Reflect)]
10#[reflect(Clone)]
11pub enum PlaybackMode {
12    /// Play the sound once. Do nothing when it ends.
13    ///
14    /// Note: It is not possible to reuse an [`AudioPlayer`] after it has finished playing and
15    /// the underlying [`AudioSink`](crate::AudioSink) or [`SpatialAudioSink`](crate::SpatialAudioSink) has been drained.
16    ///
17    /// To replay a sound, the audio components provided by [`AudioPlayer`] must be removed and
18    /// added again.
19    Once,
20    /// Repeat the sound forever.
21    Loop,
22    /// Despawn the entity and its children when the sound finishes playing.
23    Despawn,
24    /// Remove the audio components from the entity, when the sound finishes playing.
25    Remove,
26}
27
28/// Initial settings to be used when audio starts playing.
29///
30/// If you would like to control the audio while it is playing, query for the
31/// [`AudioSink`](crate::AudioSink) or [`SpatialAudioSink`](crate::SpatialAudioSink)
32/// components. Changes to this component will *not* be applied to already-playing audio.
33#[derive(Component, Clone, Copy, Debug, Reflect)]
34#[reflect(Clone, Default, Component, Debug)]
35pub struct PlaybackSettings {
36    /// The desired playback behavior.
37    pub mode: PlaybackMode,
38    /// Volume to play at.
39    pub volume: Volume,
40    /// Speed to play at.
41    pub speed: f32,
42    /// Create the sink in paused state.
43    /// Useful for "deferred playback", if you want to prepare
44    /// the entity, but hear the sound later.
45    pub paused: bool,
46    /// Whether to create the sink in muted state or not.
47    ///
48    /// This is useful for audio that should be initially muted. You can still
49    /// set the initial volume and it is applied when the audio is unmuted.
50    pub muted: bool,
51    /// Enables spatial audio for this source.
52    ///
53    /// See also: [`SpatialListener`].
54    ///
55    /// Note: Bevy does not currently support HRTF or any other high-quality 3D sound rendering
56    /// features. Spatial audio is implemented via simple left-right stereo panning.
57    pub spatial: bool,
58    /// Optional scale factor applied to the positions of this audio source and the listener,
59    /// overriding the default value configured on [`AudioPlugin::default_spatial_scale`](crate::AudioPlugin::default_spatial_scale).
60    pub spatial_scale: Option<SpatialScale>,
61    /// The point in time in the audio clip where playback should start. If set to `None`, it will
62    /// play from the beginning of the clip.
63    ///
64    /// If the playback mode is set to `Loop`, each loop will start from this position.
65    pub start_position: Option<core::time::Duration>,
66    /// How long the audio should play before stopping. If set, the clip will play for at most
67    /// the specified duration. If set to `None`, it will play for as long as it can.
68    ///
69    /// If the playback mode is set to `Loop`, each loop will last for this duration.
70    pub duration: Option<core::time::Duration>,
71}
72
73impl Default for PlaybackSettings {
74    fn default() -> Self {
75        Self::ONCE
76    }
77}
78
79impl PlaybackSettings {
80    /// Will play the associated audio source once.
81    ///
82    /// Note: It is not possible to reuse an [`AudioPlayer`] after it has finished playing and
83    /// the underlying [`AudioSink`](crate::AudioSink) or [`SpatialAudioSink`](crate::SpatialAudioSink) has been drained.
84    ///
85    /// To replay a sound, the audio components provided by [`AudioPlayer`] must be removed and
86    /// added again.
87    pub const ONCE: PlaybackSettings = PlaybackSettings {
88        mode: PlaybackMode::Once,
89        volume: Volume::Linear(1.0),
90        speed: 1.0,
91        paused: false,
92        muted: false,
93        spatial: false,
94        spatial_scale: None,
95        start_position: None,
96        duration: None,
97    };
98
99    /// Will play the associated audio source in a loop.
100    pub const LOOP: PlaybackSettings = PlaybackSettings {
101        mode: PlaybackMode::Loop,
102        ..PlaybackSettings::ONCE
103    };
104
105    /// Will play the associated audio source once and despawn the entity afterwards.
106    pub const DESPAWN: PlaybackSettings = PlaybackSettings {
107        mode: PlaybackMode::Despawn,
108        ..PlaybackSettings::ONCE
109    };
110
111    /// Will play the associated audio source once and remove the audio components afterwards.
112    pub const REMOVE: PlaybackSettings = PlaybackSettings {
113        mode: PlaybackMode::Remove,
114        ..PlaybackSettings::ONCE
115    };
116
117    /// Helper to start in a paused state.
118    pub const fn paused(mut self) -> Self {
119        self.paused = true;
120        self
121    }
122
123    /// Helper to start muted.
124    pub const fn muted(mut self) -> Self {
125        self.muted = true;
126        self
127    }
128
129    /// Helper to set the volume from start of playback.
130    pub const fn with_volume(mut self, volume: Volume) -> Self {
131        self.volume = volume;
132        self
133    }
134
135    /// Helper to set the speed from start of playback.
136    pub const fn with_speed(mut self, speed: f32) -> Self {
137        self.speed = speed;
138        self
139    }
140
141    /// Helper to enable or disable spatial audio.
142    pub const fn with_spatial(mut self, spatial: bool) -> Self {
143        self.spatial = spatial;
144        self
145    }
146
147    /// Helper to use a custom spatial scale.
148    pub const fn with_spatial_scale(mut self, spatial_scale: SpatialScale) -> Self {
149        self.spatial_scale = Some(spatial_scale);
150        self
151    }
152
153    /// Helper to use a custom playback start position.
154    pub const fn with_start_position(mut self, start_position: core::time::Duration) -> Self {
155        self.start_position = Some(start_position);
156        self
157    }
158
159    /// Helper to use a custom playback duration.
160    pub const fn with_duration(mut self, duration: core::time::Duration) -> Self {
161        self.duration = Some(duration);
162        self
163    }
164}
165
166/// Settings for the listener for spatial audio sources.
167///
168/// This is accompanied by [`Transform`] and [`GlobalTransform`](bevy_transform::prelude::GlobalTransform).
169/// Only one entity with a [`SpatialListener`] should be present at any given time.
170#[derive(Component, Clone, Debug, Reflect)]
171#[require(Transform)]
172#[reflect(Clone, Default, Component, Debug)]
173pub struct SpatialListener {
174    /// Left ear position relative to the [`GlobalTransform`](bevy_transform::prelude::GlobalTransform).
175    pub left_ear_offset: Vec3,
176    /// Right ear position relative to the [`GlobalTransform`](bevy_transform::prelude::GlobalTransform).
177    pub right_ear_offset: Vec3,
178}
179
180impl Default for SpatialListener {
181    fn default() -> Self {
182        Self::new(4.)
183    }
184}
185
186impl SpatialListener {
187    /// Creates a new [`SpatialListener`] component.
188    ///
189    /// `gap` is the distance between the left and right "ears" of the listener. Ears are
190    /// positioned on the x axis.
191    pub fn new(gap: f32) -> Self {
192        SpatialListener {
193            left_ear_offset: Vec3::X * gap / -2.0,
194            right_ear_offset: Vec3::X * gap / 2.0,
195        }
196    }
197}
198
199/// A scale factor applied to the positions of audio sources and listeners for
200/// spatial audio.
201///
202/// Default is `Vec3::ONE`.
203#[derive(Clone, Copy, Debug, Reflect)]
204#[reflect(Clone, Default)]
205pub struct SpatialScale(pub Vec3);
206
207impl SpatialScale {
208    /// Create a new [`SpatialScale`] with the same value for all 3 dimensions.
209    pub const fn new(scale: f32) -> Self {
210        Self(Vec3::splat(scale))
211    }
212
213    /// Create a new [`SpatialScale`] with the same value for `x` and `y`, and `0.0`
214    /// for `z`.
215    pub const fn new_2d(scale: f32) -> Self {
216        Self(Vec3::new(scale, scale, 0.0))
217    }
218}
219
220impl Default for SpatialScale {
221    fn default() -> Self {
222        Self(Vec3::ONE)
223    }
224}
225
226/// The default scale factor applied to the positions of audio sources and listeners for
227/// spatial audio. Can be overridden for individual sounds in [`PlaybackSettings`].
228///
229/// You may need to adjust this scale to fit your world's units.
230///
231/// Default is `Vec3::ONE`.
232#[derive(Resource, Default, Clone, Copy, Reflect)]
233#[reflect(Resource, Default, Clone)]
234pub struct DefaultSpatialScale(pub SpatialScale);
235
236/// A component for playing a sound.
237///
238/// Insert this component onto an entity to trigger an audio source to begin playing.
239///
240/// If the handle refers to an unavailable asset (such as if it has not finished loading yet),
241/// the audio will not begin playing immediately. The audio will play when the asset is ready.
242///
243/// When Bevy begins the audio playback, an [`AudioSink`](crate::AudioSink) component will be
244/// added to the entity. You can use that component to control the audio settings during playback.
245///
246/// Playback can be configured using the [`PlaybackSettings`] component. Note that changes to the
247/// [`PlaybackSettings`] component will *not* affect already-playing audio.
248#[derive(Component, Reflect)]
249#[reflect(Component, Clone)]
250#[require(PlaybackSettings)]
251pub struct AudioPlayer<Source = AudioSource>(pub Handle<Source>)
252where
253    Source: Asset + Decodable;
254
255impl<Source> Clone for AudioPlayer<Source>
256where
257    Source: Asset + Decodable,
258{
259    fn clone(&self) -> Self {
260        Self(self.0.clone())
261    }
262}
263
264impl AudioPlayer<AudioSource> {
265    /// Creates a new [`AudioPlayer`] with the given [`Handle<AudioSource>`].
266    ///
267    /// For convenience reasons, this hard-codes the [`AudioSource`] type. If you want to
268    /// initialize an [`AudioPlayer`] with a different type, just initialize it directly using normal
269    /// tuple struct syntax.
270    pub fn new(source: Handle<AudioSource>) -> Self {
271        Self(source)
272    }
273}