 39c68e3f92
			
		
	
	
		39c68e3f92
		
			
		
	
	
	
	
		
			
			# Objective
Spatial audio was heroically thrown together at the last minute for Bevy
0.10, but right now it's a bit of a pain to use -- users need to
manually update audio sinks with the position of the listener / emitter.
Hopefully the migration guide entry speaks for itself.
## Solution
Add a new `SpatialListener` component and automatically update sinks
with the position of the listener and and emitter.
## Changelog
`SpatialAudioSink`s are now automatically updated with positions of
emitters and listeners.
## Migration Guide
Spatial audio now automatically uses the transform of the `AudioBundle`
and of an entity with a `SpatialListener` component.
If you were manually scaling emitter/listener positions, you can use the
`spatial_scale` field of `AudioPlugin` instead.
```rust
// Old
commands.spawn(
    SpatialAudioBundle {
        source: asset_server.load("sounds/Windless Slopes.ogg"),
        settings: PlaybackSettings::LOOP,
        spatial: SpatialSettings::new(listener_position, gap, emitter_position),
    },
);
fn update(
    emitter_query: Query<(&Transform, &SpatialAudioSink)>,
    listener_query: Query<&Transform, With<Listener>>,
) {
    let listener = listener_query.single();
    for (transform, sink) in &emitter_query {
        sink.set_emitter_position(transform.translation);
        sink.set_listener_position(*listener, gap);
    }
}
// New
commands.spawn((
    SpatialBundle::from_transform(Transform::from_translation(emitter_position)),
    AudioBundle {
        source: asset_server.load("sounds/Windless Slopes.ogg"),
        settings: PlaybackSettings::LOOP.with_spatial(true),
    },
));
commands.spawn((
    SpatialBundle::from_transform(Transform::from_translation(listener_position)),
    SpatialListener::new(gap),
));
```
## Discussion
I removed `SpatialAudioBundle` because the `SpatialSettings` component
was made mostly redundant, and without that it was identical to
`AudioBundle`.
`SpatialListener` is a bare component and not a bundle which is feeling
like a maybe a strange choice. That happened from a natural aversion
both to nested bundles and to duplicating `Transform` etc in bundles and
from figuring that it is likely to just be tacked on to some other
bundle (player, head, camera) most of the time.
Let me know what you think about these things / everything else.
---------
Co-authored-by: Mike <mike.hsu@gmail.com>
		
	
			
		
			
				
	
	
		
			36 lines
		
	
	
		
			933 B
		
	
	
	
		
			Rust
		
	
	
	
	
	
			
		
		
	
	
			36 lines
		
	
	
		
			933 B
		
	
	
	
		
			Rust
		
	
	
	
	
	
| use crate::{AudioSourceBundle, Decodable};
 | |
| use bevy_asset::Asset;
 | |
| use bevy_reflect::TypePath;
 | |
| use rodio::{source::SineWave, source::TakeDuration, Source};
 | |
| 
 | |
| /// A source of sine wave sound
 | |
| #[derive(Asset, Debug, Clone, TypePath)]
 | |
| pub struct Pitch {
 | |
|     /// Frequency at which sound will be played
 | |
|     pub frequency: f32,
 | |
|     /// Duration for which sound will be played
 | |
|     pub duration: std::time::Duration,
 | |
| }
 | |
| 
 | |
| impl Pitch {
 | |
|     /// Creates a new note
 | |
|     pub fn new(frequency: f32, duration: std::time::Duration) -> Self {
 | |
|         Pitch {
 | |
|             frequency,
 | |
|             duration,
 | |
|         }
 | |
|     }
 | |
| }
 | |
| 
 | |
| impl Decodable for Pitch {
 | |
|     type DecoderItem = <SineWave as Iterator>::Item;
 | |
|     type Decoder = TakeDuration<SineWave>;
 | |
| 
 | |
|     fn decoder(&self) -> Self::Decoder {
 | |
|         SineWave::new(self.frequency).take_duration(self.duration)
 | |
|     }
 | |
| }
 | |
| 
 | |
| /// Bundle for playing a bevy note sound
 | |
| pub type PitchBundle = AudioSourceBundle<Pitch>;
 |