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}