bevy/crates/bevy_audio/src/sinks.rs
radiish 1efc762924
reflect: stable type path v2 (#7184)
# Objective

- Introduce a stable alternative to
[`std::any::type_name`](https://doc.rust-lang.org/std/any/fn.type_name.html).
- Rewrite of #5805 with heavy inspiration in design.
- On the path to #5830.
- Part of solving #3327.


## Solution

- Add a `TypePath` trait for static stable type path/name information.
- Add a `TypePath` derive macro.
- Add a `impl_type_path` macro for implementing internal and foreign
types in `bevy_reflect`.

---

## Changelog

- Added `TypePath` trait.
- Added `DynamicTypePath` trait and `get_type_path` method to `Reflect`.
- Added a `TypePath` derive macro.
- Added a `bevy_reflect::impl_type_path` for implementing `TypePath` on
internal and foreign types in `bevy_reflect`.
- Changed `bevy_reflect::utility::(Non)GenericTypeInfoCell` to
`(Non)GenericTypedCell<T>` which allows us to be generic over both
`TypeInfo` and `TypePath`.
- `TypePath` is now a supertrait of `Asset`, `Material` and
`Material2d`.
- `impl_reflect_struct` needs a `#[type_path = "..."]` attribute to be
specified.
- `impl_reflect_value` needs to either specify path starting with a
double colon (`::core::option::Option`) or an `in my_crate::foo`
declaration.
- Added `bevy_reflect_derive::ReflectTypePath`.
- Most uses of `Ident` in `bevy_reflect_derive` changed to use
`ReflectTypePath`.

## Migration Guide

- Implementors of `Asset`, `Material` and `Material2d` now also need to
derive `TypePath`.
- Manual implementors of `Reflect` will need to implement the new
`get_type_path` method.

## Open Questions
- [x] ~This PR currently does not migrate any usages of
`std::any::type_name` to use `bevy_reflect::TypePath` to ease the review
process. Should it?~ Migration will be left to a follow-up PR.
- [ ] This PR adds a lot of `#[derive(TypePath)]` and `T: TypePath` to
satisfy new bounds, mostly when deriving `TypeUuid`. Should we make
`TypePath` a supertrait of `TypeUuid`? [Should we remove `TypeUuid` in
favour of
`TypePath`?](2afbd85532 (r961067892))
2023-06-05 20:31:20 +00:00

236 lines
6.5 KiB
Rust

use bevy_math::Vec3;
use bevy_reflect::{TypePath, TypeUuid};
use bevy_transform::prelude::Transform;
use rodio::{Sink, SpatialSink};
/// Common interactions with an audio sink.
pub trait AudioSinkPlayback {
/// Gets the volume of the sound.
///
/// The value `1.0` is the "normal" volume (unfiltered input). Any value other than `1.0`
/// will multiply each sample by this value.
fn volume(&self) -> f32;
/// Changes the volume of the sound.
///
/// The value `1.0` is the "normal" volume (unfiltered input). Any value other than `1.0`
/// will multiply each sample by this value.
fn set_volume(&self, volume: f32);
/// Gets the speed of the sound.
///
/// The value `1.0` is the "normal" speed (unfiltered input). Any value other than `1.0`
/// will change the play speed of the sound.
fn speed(&self) -> f32;
/// Changes the speed of the sound.
///
/// The value `1.0` is the "normal" speed (unfiltered input). Any value other than `1.0`
/// will change the play speed of the sound.
fn set_speed(&self, speed: f32);
/// Resumes playback of a paused sink.
///
/// No effect if not paused.
fn play(&self);
/// Pauses playback of this sink.
///
/// No effect if already paused.
/// A paused sink can be resumed with [`play`](Self::play).
fn pause(&self);
/// Toggles the playback of this sink.
///
/// Will pause if playing, and will be resumed if paused.
fn toggle(&self) {
if self.is_paused() {
self.play();
} else {
self.pause();
}
}
/// Is this sink paused?
///
/// Sinks can be paused and resumed using [`pause`](Self::pause) and [`play`](Self::play).
fn is_paused(&self) -> bool;
/// Stops the sink.
///
/// It won't be possible to restart it afterwards.
fn stop(&self);
/// Returns true if this sink has no more sounds to play.
fn empty(&self) -> bool;
}
/// Asset controlling the playback of a sound
///
/// ```
/// # use bevy_ecs::system::{Local, Res};
/// # use bevy_asset::{Assets, Handle};
/// # use bevy_audio::{AudioSink, AudioSinkPlayback};
/// // Execution of this system should be controlled by a state or input,
/// // otherwise it would just toggle between play and pause every frame.
/// fn pause(
/// audio_sinks: Res<Assets<AudioSink>>,
/// music_controller: Local<Handle<AudioSink>>,
/// ) {
/// if let Some(sink) = audio_sinks.get(&*music_controller) {
/// if sink.is_paused() {
/// sink.play()
/// } else {
/// sink.pause()
/// }
/// }
/// }
/// ```
///
#[derive(TypePath, TypeUuid)]
#[uuid = "8BEE570C-57C2-4FC0-8CFB-983A22F7D981"]
pub struct AudioSink {
// This field is an Option in order to allow us to have a safe drop that will detach the sink.
// It will never be None during its life
pub(crate) sink: Option<Sink>,
}
impl Drop for AudioSink {
fn drop(&mut self) {
self.sink.take().unwrap().detach();
}
}
impl AudioSinkPlayback for AudioSink {
fn volume(&self) -> f32 {
self.sink.as_ref().unwrap().volume()
}
fn set_volume(&self, volume: f32) {
self.sink.as_ref().unwrap().set_volume(volume);
}
fn speed(&self) -> f32 {
self.sink.as_ref().unwrap().speed()
}
fn set_speed(&self, speed: f32) {
self.sink.as_ref().unwrap().set_speed(speed);
}
fn play(&self) {
self.sink.as_ref().unwrap().play();
}
fn pause(&self) {
self.sink.as_ref().unwrap().pause();
}
fn is_paused(&self) -> bool {
self.sink.as_ref().unwrap().is_paused()
}
fn stop(&self) {
self.sink.as_ref().unwrap().stop();
}
fn empty(&self) -> bool {
self.sink.as_ref().unwrap().empty()
}
}
/// Asset controlling the playback of a sound, or the locations of its listener and emitter.
///
/// ```
/// # use bevy_ecs::system::{Local, Res};
/// # use bevy_asset::{Assets, Handle};
/// # use bevy_audio::SpatialAudioSink;
/// # use bevy_math::Vec3;
/// // Execution of this system should be controlled by a state or input,
/// // otherwise it would just trigger every frame.
/// fn pause(
/// spatial_audio_sinks: Res<Assets<SpatialAudioSink>>,
/// audio_controller: Local<Handle<SpatialAudioSink>>,
/// ) {
/// if let Some(spatial_sink) = spatial_audio_sinks.get(&*audio_controller) {
/// spatial_sink.set_emitter_position(Vec3::new(1.0, 0.5, 1.0));
/// }
/// }
/// ```
///
#[derive(TypePath, TypeUuid)]
#[uuid = "F3CA4C47-595E-453B-96A7-31C3DDF2A177"]
pub struct SpatialAudioSink {
// This field is an Option in order to allow us to have a safe drop that will detach the sink.
// It will never be None during its life
pub(crate) sink: Option<SpatialSink>,
}
impl Drop for SpatialAudioSink {
fn drop(&mut self) {
self.sink.take().unwrap().detach();
}
}
impl AudioSinkPlayback for SpatialAudioSink {
fn volume(&self) -> f32 {
self.sink.as_ref().unwrap().volume()
}
fn set_volume(&self, volume: f32) {
self.sink.as_ref().unwrap().set_volume(volume);
}
fn speed(&self) -> f32 {
self.sink.as_ref().unwrap().speed()
}
fn set_speed(&self, speed: f32) {
self.sink.as_ref().unwrap().set_speed(speed);
}
fn play(&self) {
self.sink.as_ref().unwrap().play();
}
fn pause(&self) {
self.sink.as_ref().unwrap().pause();
}
fn is_paused(&self) -> bool {
self.sink.as_ref().unwrap().is_paused()
}
fn stop(&self) {
self.sink.as_ref().unwrap().stop();
}
fn empty(&self) -> bool {
self.sink.as_ref().unwrap().empty()
}
}
impl SpatialAudioSink {
/// Set the two ears position.
pub fn set_ears_position(&self, left_position: Vec3, right_position: Vec3) {
let sink = self.sink.as_ref().unwrap();
sink.set_left_ear_position(left_position.to_array());
sink.set_right_ear_position(right_position.to_array());
}
/// Set the listener position, with an ear on each side separated by `gap`.
pub fn set_listener_position(&self, position: Transform, gap: f32) {
self.set_ears_position(
position.translation + position.left() * gap / 2.0,
position.translation + position.right() * gap / 2.0,
);
}
/// Set the emitter position.
pub fn set_emitter_position(&self, position: Vec3) {
self.sink
.as_ref()
.unwrap()
.set_emitter_position(position.to_array());
}
}