From ca1802b77465189559519dc8fc1c0599772b743c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois?= Date: Mon, 20 Feb 2023 15:31:07 +0000 Subject: [PATCH] Basic spatial audio (#6028) # Objective - Add basic spatial audio support to Bevy - this is what rodio supports, so no HRTF, just simple stereo channel manipulation - no "built-in" ECS support: `Emitter` and `Listener` should be components that would automatically update the positions This PR goal is to just expose rodio functionality, made possible with the recent update to rodio 0.16. A proper ECS integration opens a lot more questions, and would probably require an RFC Also updates rodio and fixes #6122 --- Cargo.toml | 20 +++ crates/bevy_audio/Cargo.toml | 8 +- crates/bevy_audio/src/audio.rs | 144 ++++++++++++++++- crates/bevy_audio/src/audio_output.rs | 189 ++++++++-------------- crates/bevy_audio/src/audio_source.rs | 3 +- crates/bevy_audio/src/lib.rs | 9 +- crates/bevy_audio/src/sinks.rs | 225 ++++++++++++++++++++++++++ deny.toml | 15 +- examples/README.md | 2 + examples/audio/audio_control.rs | 2 +- examples/audio/spatial_audio_2d.rs | 91 +++++++++++ examples/audio/spatial_audio_3d.rs | 98 +++++++++++ 12 files changed, 662 insertions(+), 144 deletions(-) create mode 100644 crates/bevy_audio/src/sinks.rs create mode 100644 examples/audio/spatial_audio_2d.rs create mode 100644 examples/audio/spatial_audio_3d.rs diff --git a/Cargo.toml b/Cargo.toml index 023a418968..0b1427689d 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -830,6 +830,26 @@ description = "Shows how to create and register a custom audio source by impleme category = "Audio" wasm = true +[[example]] +name = "spatial_audio_2d" +path = "examples/audio/spatial_audio_2d.rs" + +[package.metadata.example.spatial_audio_2d] +name = "Spatial Audio 2D" +description = "Shows how to play spatial audio, and moving the emitter in 2D" +category = "Audio" +wasm = true + +[[example]] +name = "spatial_audio_3d" +path = "examples/audio/spatial_audio_3d.rs" + +[package.metadata.example.spatial_audio_3d] +name = "Spatial Audio 3D" +description = "Shows how to play spatial audio, and moving the emitter in 3D" +category = "Audio" +wasm = true + # Diagnostics [[example]] name = "log_diagnostics" diff --git a/crates/bevy_audio/Cargo.toml b/crates/bevy_audio/Cargo.toml index 8ea7dee8d6..43902b12aa 100644 --- a/crates/bevy_audio/Cargo.toml +++ b/crates/bevy_audio/Cargo.toml @@ -13,19 +13,21 @@ keywords = ["bevy"] bevy_app = { path = "../bevy_app", version = "0.9.0" } bevy_asset = { path = "../bevy_asset", version = "0.9.0" } bevy_ecs = { path = "../bevy_ecs", version = "0.9.0" } +bevy_math = { path = "../bevy_math", version = "0.9.0" } bevy_reflect = { path = "../bevy_reflect", version = "0.9.0", features = ["bevy"] } +bevy_transform = { path = "../bevy_transform", version = "0.9.0" } bevy_utils = { path = "../bevy_utils", version = "0.9.0" } # other anyhow = "1.0.4" -rodio = { version = "0.16", default-features = false } +rodio = { version = "0.17", default-features = false } parking_lot = "0.12.1" [target.'cfg(target_os = "android")'.dependencies] -oboe = { version = "0.4", optional = true } +oboe = { version = "0.5", optional = true } [target.'cfg(target_arch = "wasm32")'.dependencies] -rodio = { version = "0.16", default-features = false, features = ["wasm-bindgen"] } +rodio = { version = "0.17", default-features = false, features = ["wasm-bindgen"] } [features] diff --git a/crates/bevy_audio/src/audio.rs b/crates/bevy_audio/src/audio.rs index d7ac8d6157..fa08641c73 100644 --- a/crates/bevy_audio/src/audio.rs +++ b/crates/bevy_audio/src/audio.rs @@ -1,6 +1,8 @@ -use crate::{AudioSink, AudioSource, Decodable}; +use crate::{AudioSink, AudioSource, Decodable, SpatialAudioSink}; use bevy_asset::{Asset, Handle, HandleId}; use bevy_ecs::system::Resource; +use bevy_math::Vec3; +use bevy_transform::prelude::Transform; use parking_lot::RwLock; use std::{collections::VecDeque, fmt}; @@ -60,7 +62,7 @@ where /// /// Returns a weak [`Handle`] to the [`AudioSink`]. If this handle isn't changed to a /// strong one, the sink will be detached and the sound will continue playing. Changing it - /// to a strong handle allows for control on the playback through the [`AudioSink`] asset. + /// to a strong handle allows you to control the playback through the [`AudioSink`] asset. /// /// ``` /// # use bevy_ecs::system::Res; @@ -83,6 +85,7 @@ where settings: PlaybackSettings::ONCE, sink_handle: id, source_handle: audio_source, + spatial: None, }; self.queue.write().push_back(config); Handle::::weak(id) @@ -115,14 +118,141 @@ where settings, sink_handle: id, source_handle: audio_source, + spatial: None, }; self.queue.write().push_back(config); Handle::::weak(id) } + + /// Play audio from a [`Handle`] to the audio source, placing the listener at the given + /// transform, an ear on each side separated by `gap`. The audio emitter will placed at + /// `emitter`. + /// + /// `bevy_audio` is not using HRTF for spatial audio, but is transforming the sound to a mono + /// track, and then changing the level of each stereo channel according to the distance between + /// the emitter and each ear by amplifying the difference between what the two ears hear. + /// + /// ``` + /// # use bevy_ecs::system::Res; + /// # use bevy_asset::AssetServer; + /// # use bevy_audio::Audio; + /// # use bevy_math::Vec3; + /// # use bevy_transform::prelude::Transform; + /// fn play_spatial_audio_system(asset_server: Res, audio: Res