Skip to content

Commit 2922476

Browse files
authored
Optional override for global spatial scale (bevyengine#10419)
# Objective Fixes bevyengine#10414. That issue and its comments do a great job of laying out the case for this. ## Solution Added an optional `spatial_scale` field to `PlaybackSettings`, which overrides the default value set on `AudioPlugin`. ## Changelog - `AudioPlugin::spatial_scale` has been renamed to `default_spatial_scale`. - `SpatialScale` is no longer a resource and is wrapped by `DefaultSpatialScale`. - Added an optional `spatial_scale` to `PlaybackSettings`. ## Migration Guide `AudioPlugin::spatial_scale` has been renamed to `default_spatial_scale` and the default spatial scale can now be overridden on individual audio sources with `PlaybackSettings::spatial_scale`. If you were modifying or reading `SpatialScale` at run time, use `DefaultSpatialScale` instead. ```rust // before app.add_plugins(DefaultPlugins.set(AudioPlugin { spatial_scale: SpatialScale::new(AUDIO_SCALE), ..default() })); // after app.add_plugins(DefaultPlugins.set(AudioPlugin { default_spatial_scale: SpatialScale::new(AUDIO_SCALE), ..default() })); ```
1 parent 2ebf5a3 commit 2922476

File tree

4 files changed

+61
-33
lines changed

4 files changed

+61
-33
lines changed

crates/bevy_audio/src/audio.rs

Lines changed: 24 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,9 @@ pub struct PlaybackSettings {
6767
/// Note: Bevy does not currently support HRTF or any other high-quality 3D sound rendering
6868
/// features. Spatial audio is implemented via simple left-right stereo panning.
6969
pub spatial: bool,
70+
/// Optional scale factor applied to the positions of this audio source and the listener,
71+
/// overriding the default value configured on [`AudioPlugin::default_spatial_scale`](crate::AudioPlugin::default_spatial_scale).
72+
pub spatial_scale: Option<SpatialScale>,
7073
}
7174

7275
impl Default for PlaybackSettings {
@@ -84,6 +87,7 @@ impl PlaybackSettings {
8487
speed: 1.0,
8588
paused: false,
8689
spatial: false,
90+
spatial_scale: None,
8791
};
8892

8993
/// Will play the associated audio source in a loop.
@@ -127,6 +131,12 @@ impl PlaybackSettings {
127131
self.spatial = spatial;
128132
self
129133
}
134+
135+
/// Helper to use a custom spatial scale.
136+
pub const fn with_spatial_scale(mut self, spatial_scale: SpatialScale) -> Self {
137+
self.spatial_scale = Some(spatial_scale);
138+
self
139+
}
130140
}
131141

132142
/// Settings for the listener for spatial audio sources.
@@ -180,25 +190,22 @@ impl GlobalVolume {
180190
}
181191
}
182192

183-
/// The scale factor applied to the positions of audio sources and listeners for
193+
/// A scale factor applied to the positions of audio sources and listeners for
184194
/// spatial audio.
185195
///
186-
/// You may need to adjust this scale to fit your world's units.
187-
///
188196
/// Default is `Vec3::ONE`.
189-
#[derive(Resource, Clone, Copy, Reflect)]
190-
#[reflect(Resource)]
197+
#[derive(Clone, Copy, Debug, Reflect)]
191198
pub struct SpatialScale(pub Vec3);
192199

193200
impl SpatialScale {
194201
/// Create a new `SpatialScale` with the same value for all 3 dimensions.
195-
pub fn new(scale: f32) -> Self {
202+
pub const fn new(scale: f32) -> Self {
196203
Self(Vec3::splat(scale))
197204
}
198205

199206
/// Create a new `SpatialScale` with the same value for `x` and `y`, and `0.0`
200207
/// for `z`.
201-
pub fn new_2d(scale: f32) -> Self {
208+
pub const fn new_2d(scale: f32) -> Self {
202209
Self(Vec3::new(scale, scale, 0.0))
203210
}
204211
}
@@ -209,6 +216,16 @@ impl Default for SpatialScale {
209216
}
210217
}
211218

219+
/// The default scale factor applied to the positions of audio sources and listeners for
220+
/// spatial audio. Can be overridden for individual sounds in [`PlaybackSettings`].
221+
///
222+
/// You may need to adjust this scale to fit your world's units.
223+
///
224+
/// Default is `Vec3::ONE`.
225+
#[derive(Resource, Default, Clone, Copy, Reflect)]
226+
#[reflect(Resource)]
227+
pub struct DefaultSpatialScale(pub SpatialScale);
228+
212229
/// Bundle for playing a standard bevy audio asset
213230
pub type AudioBundle = AudioSourceBundle<AudioSource>;
214231

crates/bevy_audio/src/audio_output.rs

Lines changed: 33 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
use crate::{
2-
AudioSourceBundle, Decodable, GlobalVolume, PlaybackMode, PlaybackSettings, SpatialAudioSink,
3-
SpatialListener, SpatialScale,
2+
AudioSourceBundle, Decodable, DefaultSpatialScale, GlobalVolume, PlaybackMode,
3+
PlaybackSettings, SpatialAudioSink, SpatialListener,
44
};
55
use bevy_asset::{Asset, Assets, Handle};
66
use bevy_ecs::{prelude::*, system::SystemParam};
@@ -56,10 +56,9 @@ pub struct PlaybackRemoveMarker;
5656
#[derive(SystemParam)]
5757
pub(crate) struct EarPositions<'w, 's> {
5858
pub(crate) query: Query<'w, 's, (Entity, &'static GlobalTransform, &'static SpatialListener)>,
59-
pub(crate) scale: Res<'w, SpatialScale>,
6059
}
6160
impl<'w, 's> EarPositions<'w, 's> {
62-
/// Gets a set of transformed and scaled ear positions.
61+
/// Gets a set of transformed ear positions.
6362
///
6463
/// If there are no listeners, use the default values. If a user has added multiple
6564
/// listeners for whatever reason, we will return the first value.
@@ -70,16 +69,13 @@ impl<'w, 's> EarPositions<'w, 's> {
7069
.next()
7170
.map(|(_, transform, settings)| {
7271
(
73-
transform.transform_point(settings.left_ear_offset) * self.scale.0,
74-
transform.transform_point(settings.right_ear_offset) * self.scale.0,
72+
transform.transform_point(settings.left_ear_offset),
73+
transform.transform_point(settings.right_ear_offset),
7574
)
7675
})
7776
.unwrap_or_else(|| {
7877
let settings = SpatialListener::default();
79-
(
80-
settings.left_ear_offset * self.scale.0,
81-
settings.right_ear_offset * self.scale.0,
82-
)
78+
(settings.left_ear_offset, settings.right_ear_offset)
8379
});
8480

8581
(left_ear, right_ear)
@@ -112,6 +108,7 @@ pub(crate) fn play_queued_audio_system<Source: Asset + Decodable>(
112108
(Without<AudioSink>, Without<SpatialAudioSink>),
113109
>,
114110
ear_positions: EarPositions,
111+
default_spatial_scale: Res<DefaultSpatialScale>,
115112
mut commands: Commands,
116113
) where
117114
f32: rodio::cpal::FromSample<Source::DecoderItem>,
@@ -138,8 +135,10 @@ pub(crate) fn play_queued_audio_system<Source: Asset + Decodable>(
138135
);
139136
}
140137

138+
let scale = settings.spatial_scale.unwrap_or(default_spatial_scale.0).0;
139+
141140
let emitter_translation = if let Some(emitter_transform) = maybe_emitter_transform {
142-
(emitter_transform.translation() * ear_positions.scale.0).into()
141+
(emitter_transform.translation() * scale).into()
143142
} else {
144143
warn!("Spatial AudioBundle with no GlobalTransform component. Using zero.");
145144
Vec3::ZERO.into()
@@ -148,8 +147,8 @@ pub(crate) fn play_queued_audio_system<Source: Asset + Decodable>(
148147
let sink = match SpatialSink::try_new(
149148
stream_handle,
150149
emitter_translation,
151-
left_ear.into(),
152-
right_ear.into(),
150+
(left_ear * scale).into(),
151+
(right_ear * scale).into(),
153152
) {
154153
Ok(sink) => sink,
155154
Err(err) => {
@@ -285,34 +284,46 @@ pub(crate) fn audio_output_available(audio_output: Res<AudioOutput>) -> bool {
285284

286285
/// Updates spatial audio sinks when emitter positions change.
287286
pub(crate) fn update_emitter_positions(
288-
mut emitters: Query<(&GlobalTransform, &SpatialAudioSink), Changed<GlobalTransform>>,
289-
spatial_scale: Res<SpatialScale>,
287+
mut emitters: Query<
288+
(&GlobalTransform, &SpatialAudioSink, &PlaybackSettings),
289+
Or<(Changed<GlobalTransform>, Changed<PlaybackSettings>)>,
290+
>,
291+
default_spatial_scale: Res<DefaultSpatialScale>,
290292
) {
291-
for (transform, sink) in emitters.iter_mut() {
292-
let translation = transform.translation() * spatial_scale.0;
293+
for (transform, sink, settings) in emitters.iter_mut() {
294+
let scale = settings.spatial_scale.unwrap_or(default_spatial_scale.0).0;
295+
296+
let translation = transform.translation() * scale;
293297
sink.set_emitter_position(translation);
294298
}
295299
}
296300

297301
/// Updates spatial audio sink ear positions when spatial listeners change.
298302
pub(crate) fn update_listener_positions(
299-
mut emitters: Query<&SpatialAudioSink>,
303+
mut emitters: Query<(&SpatialAudioSink, &PlaybackSettings)>,
300304
changed_listener: Query<
301305
(),
302306
(
303-
Or<(Changed<SpatialListener>, Changed<GlobalTransform>)>,
307+
Or<(
308+
Changed<SpatialListener>,
309+
Changed<GlobalTransform>,
310+
Changed<PlaybackSettings>,
311+
)>,
304312
With<SpatialListener>,
305313
),
306314
>,
307315
ear_positions: EarPositions,
316+
default_spatial_scale: Res<DefaultSpatialScale>,
308317
) {
309-
if !ear_positions.scale.is_changed() && changed_listener.is_empty() {
318+
if !default_spatial_scale.is_changed() && changed_listener.is_empty() {
310319
return;
311320
}
312321

313322
let (left_ear, right_ear) = ear_positions.get();
314323

315-
for sink in emitters.iter_mut() {
316-
sink.set_ears_position(left_ear, right_ear);
324+
for (sink, settings) in emitters.iter_mut() {
325+
let scale = settings.spatial_scale.unwrap_or(default_spatial_scale.0).0;
326+
327+
sink.set_ears_position(left_ear * scale, right_ear * scale);
317328
}
318329
}

crates/bevy_audio/src/lib.rs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -67,19 +67,19 @@ pub struct AudioPlugin {
6767
pub global_volume: GlobalVolume,
6868
/// The scale factor applied to the positions of audio sources and listeners for
6969
/// spatial audio.
70-
pub spatial_scale: SpatialScale,
70+
pub default_spatial_scale: SpatialScale,
7171
}
7272

7373
impl Plugin for AudioPlugin {
7474
fn build(&self, app: &mut App) {
7575
app.register_type::<Volume>()
7676
.register_type::<GlobalVolume>()
7777
.register_type::<SpatialListener>()
78-
.register_type::<SpatialScale>()
78+
.register_type::<DefaultSpatialScale>()
7979
.register_type::<PlaybackMode>()
8080
.register_type::<PlaybackSettings>()
8181
.insert_resource(self.global_volume)
82-
.insert_resource(self.spatial_scale)
82+
.insert_resource(DefaultSpatialScale(self.default_spatial_scale))
8383
.configure_sets(
8484
PostUpdate,
8585
AudioPlaySet

examples/audio/spatial_audio_2d.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ const AUDIO_SCALE: f32 = 1. / 100.0;
1313
fn main() {
1414
App::new()
1515
.add_plugins(DefaultPlugins.set(AudioPlugin {
16-
spatial_scale: SpatialScale::new_2d(AUDIO_SCALE),
16+
default_spatial_scale: SpatialScale::new_2d(AUDIO_SCALE),
1717
..default()
1818
}))
1919
.add_systems(Startup, setup)

0 commit comments

Comments
 (0)