1
- use crate :: { AudioSink , AudioSource , Decodable } ;
1
+ use crate :: { AudioSink , AudioSource , Decodable , SpatialAudioSink } ;
2
2
use bevy_asset:: { Asset , Handle , HandleId } ;
3
3
use bevy_ecs:: system:: Resource ;
4
+ use bevy_math:: Vec3 ;
5
+ use bevy_transform:: prelude:: Transform ;
4
6
use parking_lot:: RwLock ;
5
7
use std:: { collections:: VecDeque , fmt} ;
6
8
60
62
///
61
63
/// Returns a weak [`Handle`] to the [`AudioSink`]. If this handle isn't changed to a
62
64
/// strong one, the sink will be detached and the sound will continue playing. Changing it
63
- /// to a strong handle allows for control on the playback through the [`AudioSink`] asset.
65
+ /// to a strong handle allows you to control the playback through the [`AudioSink`] asset.
64
66
///
65
67
/// ```
66
68
/// # use bevy_ecs::system::Res;
83
85
settings : PlaybackSettings :: ONCE ,
84
86
sink_handle : id,
85
87
source_handle : audio_source,
88
+ spatial : None ,
86
89
} ;
87
90
self . queue . write ( ) . push_back ( config) ;
88
91
Handle :: < AudioSink > :: weak ( id)
@@ -115,14 +118,141 @@ where
115
118
settings,
116
119
sink_handle : id,
117
120
source_handle : audio_source,
121
+ spatial : None ,
118
122
} ;
119
123
self . queue . write ( ) . push_back ( config) ;
120
124
Handle :: < AudioSink > :: weak ( id)
121
125
}
126
+
127
+ /// Play audio from a [`Handle`] to the audio source, placing the listener at the given
128
+ /// transform, an ear on each side separated by `gap`. The audio emitter will placed at
129
+ /// `emitter`.
130
+ ///
131
+ /// `bevy_audio` is not using HRTF for spatial audio, but is transforming the sound to a mono
132
+ /// track, and then changing the level of each stereo channel according to the distance between
133
+ /// the emitter and each ear by amplifying the difference between what the two ears hear.
134
+ ///
135
+ /// ```
136
+ /// # use bevy_ecs::system::Res;
137
+ /// # use bevy_asset::AssetServer;
138
+ /// # use bevy_audio::Audio;
139
+ /// # use bevy_math::Vec3;
140
+ /// # use bevy_transform::prelude::Transform;
141
+ /// fn play_spatial_audio_system(asset_server: Res<AssetServer>, audio: Res<Audio>) {
142
+ /// // Sound will be to the left and behind the listener
143
+ /// audio.play_spatial(
144
+ /// asset_server.load("my_sound.ogg"),
145
+ /// Transform::IDENTITY,
146
+ /// 1.0,
147
+ /// Vec3::new(-2.0, 0.0, 1.0),
148
+ /// );
149
+ /// }
150
+ /// ```
151
+ ///
152
+ /// Returns a weak [`Handle`] to the [`SpatialAudioSink`]. If this handle isn't changed to a
153
+ /// strong one, the sink will be detached and the sound will continue playing. Changing it
154
+ /// to a strong handle allows you to control the playback, or move the listener and emitter
155
+ /// through the [`SpatialAudioSink`] asset.
156
+ ///
157
+ /// ```
158
+ /// # use bevy_ecs::system::Res;
159
+ /// # use bevy_asset::{AssetServer, Assets};
160
+ /// # use bevy_audio::{Audio, SpatialAudioSink};
161
+ /// # use bevy_math::Vec3;
162
+ /// # use bevy_transform::prelude::Transform;
163
+ /// fn play_spatial_audio_system(
164
+ /// asset_server: Res<AssetServer>,
165
+ /// audio: Res<Audio>,
166
+ /// spatial_audio_sinks: Res<Assets<SpatialAudioSink>>,
167
+ /// ) {
168
+ /// // This is a weak handle, and can't be used to control playback.
169
+ /// let weak_handle = audio.play_spatial(
170
+ /// asset_server.load("my_sound.ogg"),
171
+ /// Transform::IDENTITY,
172
+ /// 1.0,
173
+ /// Vec3::new(-2.0, 0.0, 1.0),
174
+ /// );
175
+ /// // This is now a strong handle, and can be used to control playback, or move the emitter.
176
+ /// let strong_handle = spatial_audio_sinks.get_handle(weak_handle);
177
+ /// }
178
+ /// ```
179
+ pub fn play_spatial (
180
+ & self ,
181
+ audio_source : Handle < Source > ,
182
+ listener : Transform ,
183
+ gap : f32 ,
184
+ emitter : Vec3 ,
185
+ ) -> Handle < SpatialAudioSink > {
186
+ let id = HandleId :: random :: < SpatialAudioSink > ( ) ;
187
+ let config = AudioToPlay {
188
+ settings : PlaybackSettings :: ONCE ,
189
+ sink_handle : id,
190
+ source_handle : audio_source,
191
+ spatial : Some ( SpatialSettings {
192
+ left_ear : ( listener. translation + listener. left ( ) * gap / 2.0 ) . to_array ( ) ,
193
+ right_ear : ( listener. translation + listener. right ( ) * gap / 2.0 ) . to_array ( ) ,
194
+ emitter : emitter. to_array ( ) ,
195
+ } ) ,
196
+ } ;
197
+ self . queue . write ( ) . push_back ( config) ;
198
+ Handle :: < SpatialAudioSink > :: weak ( id)
199
+ }
200
+
201
+ /// Play spatial audio from a [`Handle`] to the audio source with [`PlaybackSettings`] that
202
+ /// allows looping or changing volume from the start. The listener is placed at the given
203
+ /// transform, an ear on each side separated by `gap`. The audio emitter is placed at
204
+ /// `emitter`.
205
+ ///
206
+ /// `bevy_audio` is not using HRTF for spatial audio, but is transforming the sound to a mono
207
+ /// track, and then changing the level of each stereo channel according to the distance between
208
+ /// the emitter and each ear by amplifying the difference between what the two ears hear.
209
+ ///
210
+ /// ```
211
+ /// # use bevy_ecs::system::Res;
212
+ /// # use bevy_asset::AssetServer;
213
+ /// # use bevy_audio::Audio;
214
+ /// # use bevy_audio::PlaybackSettings;
215
+ /// # use bevy_math::Vec3;
216
+ /// # use bevy_transform::prelude::Transform;
217
+ /// fn play_spatial_audio_system(asset_server: Res<AssetServer>, audio: Res<Audio>) {
218
+ /// audio.play_spatial_with_settings(
219
+ /// asset_server.load("my_sound.ogg"),
220
+ /// PlaybackSettings::LOOP.with_volume(0.75),
221
+ /// Transform::IDENTITY,
222
+ /// 1.0,
223
+ /// Vec3::new(-2.0, 0.0, 1.0),
224
+ /// );
225
+ /// }
226
+ /// ```
227
+ ///
228
+ /// See [`Self::play_spatial`] on how to control playback once it's started, or how to move
229
+ /// the listener or the emitter.
230
+ pub fn play_spatial_with_settings (
231
+ & self ,
232
+ audio_source : Handle < Source > ,
233
+ settings : PlaybackSettings ,
234
+ listener : Transform ,
235
+ gap : f32 ,
236
+ emitter : Vec3 ,
237
+ ) -> Handle < SpatialAudioSink > {
238
+ let id = HandleId :: random :: < SpatialAudioSink > ( ) ;
239
+ let config = AudioToPlay {
240
+ settings,
241
+ sink_handle : id,
242
+ source_handle : audio_source,
243
+ spatial : Some ( SpatialSettings {
244
+ left_ear : ( listener. translation + listener. left ( ) * gap / 2.0 ) . to_array ( ) ,
245
+ right_ear : ( listener. translation + listener. right ( ) * gap / 2.0 ) . to_array ( ) ,
246
+ emitter : emitter. to_array ( ) ,
247
+ } ) ,
248
+ } ;
249
+ self . queue . write ( ) . push_back ( config) ;
250
+ Handle :: < SpatialAudioSink > :: weak ( id)
251
+ }
122
252
}
123
253
124
254
/// Settings to control playback from the start.
125
- #[ derive( Clone , Debug ) ]
255
+ #[ derive( Clone , Copy , Debug ) ]
126
256
pub struct PlaybackSettings {
127
257
/// Play in repeat
128
258
pub repeat : bool ,
@@ -166,6 +296,13 @@ impl PlaybackSettings {
166
296
}
167
297
}
168
298
299
+ #[ derive( Clone ) ]
300
+ pub ( crate ) struct SpatialSettings {
301
+ pub ( crate ) left_ear : [ f32 ; 3 ] ,
302
+ pub ( crate ) right_ear : [ f32 ; 3 ] ,
303
+ pub ( crate ) emitter : [ f32 ; 3 ] ,
304
+ }
305
+
169
306
#[ derive( Clone ) ]
170
307
pub ( crate ) struct AudioToPlay < Source >
171
308
where
@@ -174,6 +311,7 @@ where
174
311
pub ( crate ) sink_handle : HandleId ,
175
312
pub ( crate ) source_handle : Handle < Source > ,
176
313
pub ( crate ) settings : PlaybackSettings ,
314
+ pub ( crate ) spatial : Option < SpatialSettings > ,
177
315
}
178
316
179
317
impl < Source > fmt:: Debug for AudioToPlay < Source >
0 commit comments