mirror of
https://github.com/bevyengine/bevy
synced 2024-11-10 07:04:33 +00:00
Optional override for global spatial scale (#10419)
# Objective Fixes #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() })); ```
This commit is contained in:
parent
2ebf5a303e
commit
29224768e4
4 changed files with 61 additions and 33 deletions
|
@ -67,6 +67,9 @@ pub struct PlaybackSettings {
|
|||
/// Note: Bevy does not currently support HRTF or any other high-quality 3D sound rendering
|
||||
/// features. Spatial audio is implemented via simple left-right stereo panning.
|
||||
pub spatial: bool,
|
||||
/// Optional scale factor applied to the positions of this audio source and the listener,
|
||||
/// overriding the default value configured on [`AudioPlugin::default_spatial_scale`](crate::AudioPlugin::default_spatial_scale).
|
||||
pub spatial_scale: Option<SpatialScale>,
|
||||
}
|
||||
|
||||
impl Default for PlaybackSettings {
|
||||
|
@ -84,6 +87,7 @@ impl PlaybackSettings {
|
|||
speed: 1.0,
|
||||
paused: false,
|
||||
spatial: false,
|
||||
spatial_scale: None,
|
||||
};
|
||||
|
||||
/// Will play the associated audio source in a loop.
|
||||
|
@ -127,6 +131,12 @@ impl PlaybackSettings {
|
|||
self.spatial = spatial;
|
||||
self
|
||||
}
|
||||
|
||||
/// Helper to use a custom spatial scale.
|
||||
pub const fn with_spatial_scale(mut self, spatial_scale: SpatialScale) -> Self {
|
||||
self.spatial_scale = Some(spatial_scale);
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
/// Settings for the listener for spatial audio sources.
|
||||
|
@ -180,25 +190,22 @@ impl GlobalVolume {
|
|||
}
|
||||
}
|
||||
|
||||
/// The scale factor applied to the positions of audio sources and listeners for
|
||||
/// A scale factor applied to the positions of audio sources and listeners for
|
||||
/// spatial audio.
|
||||
///
|
||||
/// You may need to adjust this scale to fit your world's units.
|
||||
///
|
||||
/// Default is `Vec3::ONE`.
|
||||
#[derive(Resource, Clone, Copy, Reflect)]
|
||||
#[reflect(Resource)]
|
||||
#[derive(Clone, Copy, Debug, Reflect)]
|
||||
pub struct SpatialScale(pub Vec3);
|
||||
|
||||
impl SpatialScale {
|
||||
/// Create a new `SpatialScale` with the same value for all 3 dimensions.
|
||||
pub fn new(scale: f32) -> Self {
|
||||
pub const fn new(scale: f32) -> Self {
|
||||
Self(Vec3::splat(scale))
|
||||
}
|
||||
|
||||
/// Create a new `SpatialScale` with the same value for `x` and `y`, and `0.0`
|
||||
/// for `z`.
|
||||
pub fn new_2d(scale: f32) -> Self {
|
||||
pub const fn new_2d(scale: f32) -> Self {
|
||||
Self(Vec3::new(scale, scale, 0.0))
|
||||
}
|
||||
}
|
||||
|
@ -209,6 +216,16 @@ impl Default for SpatialScale {
|
|||
}
|
||||
}
|
||||
|
||||
/// The default scale factor applied to the positions of audio sources and listeners for
|
||||
/// spatial audio. Can be overridden for individual sounds in [`PlaybackSettings`].
|
||||
///
|
||||
/// You may need to adjust this scale to fit your world's units.
|
||||
///
|
||||
/// Default is `Vec3::ONE`.
|
||||
#[derive(Resource, Default, Clone, Copy, Reflect)]
|
||||
#[reflect(Resource)]
|
||||
pub struct DefaultSpatialScale(pub SpatialScale);
|
||||
|
||||
/// Bundle for playing a standard bevy audio asset
|
||||
pub type AudioBundle = AudioSourceBundle<AudioSource>;
|
||||
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
use crate::{
|
||||
AudioSourceBundle, Decodable, GlobalVolume, PlaybackMode, PlaybackSettings, SpatialAudioSink,
|
||||
SpatialListener, SpatialScale,
|
||||
AudioSourceBundle, Decodable, DefaultSpatialScale, GlobalVolume, PlaybackMode,
|
||||
PlaybackSettings, SpatialAudioSink, SpatialListener,
|
||||
};
|
||||
use bevy_asset::{Asset, Assets, Handle};
|
||||
use bevy_ecs::{prelude::*, system::SystemParam};
|
||||
|
@ -56,10 +56,9 @@ pub struct PlaybackRemoveMarker;
|
|||
#[derive(SystemParam)]
|
||||
pub(crate) struct EarPositions<'w, 's> {
|
||||
pub(crate) query: Query<'w, 's, (Entity, &'static GlobalTransform, &'static SpatialListener)>,
|
||||
pub(crate) scale: Res<'w, SpatialScale>,
|
||||
}
|
||||
impl<'w, 's> EarPositions<'w, 's> {
|
||||
/// Gets a set of transformed and scaled ear positions.
|
||||
/// Gets a set of transformed ear positions.
|
||||
///
|
||||
/// If there are no listeners, use the default values. If a user has added multiple
|
||||
/// listeners for whatever reason, we will return the first value.
|
||||
|
@ -70,16 +69,13 @@ impl<'w, 's> EarPositions<'w, 's> {
|
|||
.next()
|
||||
.map(|(_, transform, settings)| {
|
||||
(
|
||||
transform.transform_point(settings.left_ear_offset) * self.scale.0,
|
||||
transform.transform_point(settings.right_ear_offset) * self.scale.0,
|
||||
transform.transform_point(settings.left_ear_offset),
|
||||
transform.transform_point(settings.right_ear_offset),
|
||||
)
|
||||
})
|
||||
.unwrap_or_else(|| {
|
||||
let settings = SpatialListener::default();
|
||||
(
|
||||
settings.left_ear_offset * self.scale.0,
|
||||
settings.right_ear_offset * self.scale.0,
|
||||
)
|
||||
(settings.left_ear_offset, settings.right_ear_offset)
|
||||
});
|
||||
|
||||
(left_ear, right_ear)
|
||||
|
@ -112,6 +108,7 @@ pub(crate) fn play_queued_audio_system<Source: Asset + Decodable>(
|
|||
(Without<AudioSink>, Without<SpatialAudioSink>),
|
||||
>,
|
||||
ear_positions: EarPositions,
|
||||
default_spatial_scale: Res<DefaultSpatialScale>,
|
||||
mut commands: Commands,
|
||||
) where
|
||||
f32: rodio::cpal::FromSample<Source::DecoderItem>,
|
||||
|
@ -138,8 +135,10 @@ pub(crate) fn play_queued_audio_system<Source: Asset + Decodable>(
|
|||
);
|
||||
}
|
||||
|
||||
let scale = settings.spatial_scale.unwrap_or(default_spatial_scale.0).0;
|
||||
|
||||
let emitter_translation = if let Some(emitter_transform) = maybe_emitter_transform {
|
||||
(emitter_transform.translation() * ear_positions.scale.0).into()
|
||||
(emitter_transform.translation() * scale).into()
|
||||
} else {
|
||||
warn!("Spatial AudioBundle with no GlobalTransform component. Using zero.");
|
||||
Vec3::ZERO.into()
|
||||
|
@ -148,8 +147,8 @@ pub(crate) fn play_queued_audio_system<Source: Asset + Decodable>(
|
|||
let sink = match SpatialSink::try_new(
|
||||
stream_handle,
|
||||
emitter_translation,
|
||||
left_ear.into(),
|
||||
right_ear.into(),
|
||||
(left_ear * scale).into(),
|
||||
(right_ear * scale).into(),
|
||||
) {
|
||||
Ok(sink) => sink,
|
||||
Err(err) => {
|
||||
|
@ -285,34 +284,46 @@ pub(crate) fn audio_output_available(audio_output: Res<AudioOutput>) -> bool {
|
|||
|
||||
/// Updates spatial audio sinks when emitter positions change.
|
||||
pub(crate) fn update_emitter_positions(
|
||||
mut emitters: Query<(&GlobalTransform, &SpatialAudioSink), Changed<GlobalTransform>>,
|
||||
spatial_scale: Res<SpatialScale>,
|
||||
mut emitters: Query<
|
||||
(&GlobalTransform, &SpatialAudioSink, &PlaybackSettings),
|
||||
Or<(Changed<GlobalTransform>, Changed<PlaybackSettings>)>,
|
||||
>,
|
||||
default_spatial_scale: Res<DefaultSpatialScale>,
|
||||
) {
|
||||
for (transform, sink) in emitters.iter_mut() {
|
||||
let translation = transform.translation() * spatial_scale.0;
|
||||
for (transform, sink, settings) in emitters.iter_mut() {
|
||||
let scale = settings.spatial_scale.unwrap_or(default_spatial_scale.0).0;
|
||||
|
||||
let translation = transform.translation() * scale;
|
||||
sink.set_emitter_position(translation);
|
||||
}
|
||||
}
|
||||
|
||||
/// Updates spatial audio sink ear positions when spatial listeners change.
|
||||
pub(crate) fn update_listener_positions(
|
||||
mut emitters: Query<&SpatialAudioSink>,
|
||||
mut emitters: Query<(&SpatialAudioSink, &PlaybackSettings)>,
|
||||
changed_listener: Query<
|
||||
(),
|
||||
(
|
||||
Or<(Changed<SpatialListener>, Changed<GlobalTransform>)>,
|
||||
Or<(
|
||||
Changed<SpatialListener>,
|
||||
Changed<GlobalTransform>,
|
||||
Changed<PlaybackSettings>,
|
||||
)>,
|
||||
With<SpatialListener>,
|
||||
),
|
||||
>,
|
||||
ear_positions: EarPositions,
|
||||
default_spatial_scale: Res<DefaultSpatialScale>,
|
||||
) {
|
||||
if !ear_positions.scale.is_changed() && changed_listener.is_empty() {
|
||||
if !default_spatial_scale.is_changed() && changed_listener.is_empty() {
|
||||
return;
|
||||
}
|
||||
|
||||
let (left_ear, right_ear) = ear_positions.get();
|
||||
|
||||
for sink in emitters.iter_mut() {
|
||||
sink.set_ears_position(left_ear, right_ear);
|
||||
for (sink, settings) in emitters.iter_mut() {
|
||||
let scale = settings.spatial_scale.unwrap_or(default_spatial_scale.0).0;
|
||||
|
||||
sink.set_ears_position(left_ear * scale, right_ear * scale);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -67,7 +67,7 @@ pub struct AudioPlugin {
|
|||
pub global_volume: GlobalVolume,
|
||||
/// The scale factor applied to the positions of audio sources and listeners for
|
||||
/// spatial audio.
|
||||
pub spatial_scale: SpatialScale,
|
||||
pub default_spatial_scale: SpatialScale,
|
||||
}
|
||||
|
||||
impl Plugin for AudioPlugin {
|
||||
|
@ -75,11 +75,11 @@ impl Plugin for AudioPlugin {
|
|||
app.register_type::<Volume>()
|
||||
.register_type::<GlobalVolume>()
|
||||
.register_type::<SpatialListener>()
|
||||
.register_type::<SpatialScale>()
|
||||
.register_type::<DefaultSpatialScale>()
|
||||
.register_type::<PlaybackMode>()
|
||||
.register_type::<PlaybackSettings>()
|
||||
.insert_resource(self.global_volume)
|
||||
.insert_resource(self.spatial_scale)
|
||||
.insert_resource(DefaultSpatialScale(self.default_spatial_scale))
|
||||
.configure_sets(
|
||||
PostUpdate,
|
||||
AudioPlaySet
|
||||
|
|
|
@ -13,7 +13,7 @@ const AUDIO_SCALE: f32 = 1. / 100.0;
|
|||
fn main() {
|
||||
App::new()
|
||||
.add_plugins(DefaultPlugins.set(AudioPlugin {
|
||||
spatial_scale: SpatialScale::new_2d(AUDIO_SCALE),
|
||||
default_spatial_scale: SpatialScale::new_2d(AUDIO_SCALE),
|
||||
..default()
|
||||
}))
|
||||
.add_systems(Startup, setup)
|
||||
|
|
Loading…
Reference in a new issue