diff --git a/Cargo.toml b/Cargo.toml index 8bb4836745..1703a0500e 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1043,6 +1043,16 @@ description = "Shows how to play spatial audio, and moving the emitter in 3D" category = "Audio" wasm = true +[[example]] +name = "pitch" +path = "examples/audio/pitch.rs" + +[package.metadata.example.pitch] +name = "Pitch" +description = "Shows how to directly play a simple pitch" +category = "Audio" +wasm = true + # Diagnostics [[example]] name = "log_diagnostics" diff --git a/crates/bevy_audio/src/lib.rs b/crates/bevy_audio/src/lib.rs index 11b3699096..3b3c8de10a 100644 --- a/crates/bevy_audio/src/lib.rs +++ b/crates/bevy_audio/src/lib.rs @@ -27,6 +27,7 @@ mod audio; mod audio_output; mod audio_source; +mod pitch; mod sinks; #[allow(missing_docs)] @@ -34,13 +35,14 @@ pub mod prelude { #[doc(hidden)] pub use crate::{ AudioBundle, AudioSink, AudioSinkPlayback, AudioSource, AudioSourceBundle, Decodable, - GlobalVolume, PlaybackSettings, SpatialAudioBundle, SpatialAudioSink, - SpatialAudioSourceBundle, SpatialSettings, + GlobalVolume, Pitch, PitchBundle, PlaybackSettings, SpatialAudioBundle, SpatialAudioSink, + SpatialAudioSourceBundle, SpatialPitchBundle, SpatialSettings, }; } pub use audio::*; pub use audio_source::*; +pub use pitch::*; pub use rodio::cpal::Sample as CpalSample; pub use rodio::source::Source; @@ -77,6 +79,8 @@ impl Plugin for AudioPlugin { app.add_audio_source::(); app.init_asset_loader::(); } + + app.add_audio_source::(); } } diff --git a/crates/bevy_audio/src/pitch.rs b/crates/bevy_audio/src/pitch.rs new file mode 100644 index 0000000000..08423e2822 --- /dev/null +++ b/crates/bevy_audio/src/pitch.rs @@ -0,0 +1,38 @@ +use crate::{AudioSourceBundle, Decodable, SpatialAudioSourceBundle}; +use bevy_reflect::{TypePath, TypeUuid}; +use rodio::{source::SineWave, source::TakeDuration, Source}; + +/// A source of sine wave sound +#[derive(Debug, Clone, TypeUuid, TypePath)] +#[uuid = "cbc63be3-b0b9-4d2c-a03c-88b58f1a19ef"] +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 = ::Item; + type Decoder = TakeDuration; + + fn decoder(&self) -> Self::Decoder { + SineWave::new(self.frequency).take_duration(self.duration) + } +} + +/// Bundle for playing a bevy note sound +pub type PitchBundle = AudioSourceBundle; + +/// Bundle for playing a bevy note sound with a 3D position +pub type SpatialPitchBundle = SpatialAudioSourceBundle; diff --git a/examples/README.md b/examples/README.md index 382f477e15..3770750414 100644 --- a/examples/README.md +++ b/examples/README.md @@ -195,6 +195,7 @@ Example | Description [Audio](../examples/audio/audio.rs) | Shows how to load and play an audio file [Audio Control](../examples/audio/audio_control.rs) | Shows how to load and play an audio file, and control how it's played [Decodable](../examples/audio/decodable.rs) | Shows how to create and register a custom audio source by implementing the `Decodable` type. +[Pitch](../examples/audio/pitch.rs) | Shows how to directly play a simple pitch [Spatial Audio 2D](../examples/audio/spatial_audio_2d.rs) | Shows how to play spatial audio, and moving the emitter in 2D [Spatial Audio 3D](../examples/audio/spatial_audio_3d.rs) | Shows how to play spatial audio, and moving the emitter in 3D diff --git a/examples/audio/pitch.rs b/examples/audio/pitch.rs new file mode 100644 index 0000000000..083e4a359d --- /dev/null +++ b/examples/audio/pitch.rs @@ -0,0 +1,55 @@ +//! This example illustrates how to play a single-frequency sound (aka a pitch) + +use bevy::prelude::*; +use std::time::Duration; + +fn main() { + App::new() + .add_plugins(DefaultPlugins) + .add_event::() + .add_systems(Startup, setup) + .add_systems(Update, (play_pitch, keyboard_input_system)) + .run(); +} + +#[derive(Event, Default)] +struct PlayPitch; + +#[derive(Resource)] +struct PitchFrequency(f32); + +fn setup(mut commands: Commands) { + commands.insert_resource(PitchFrequency(220.0)); +} + +fn play_pitch( + mut pitch_assets: ResMut>, + frequency: Res, + mut events: EventReader, + mut commands: Commands, +) { + for _ in events.iter() { + info!("playing pitch with frequency: {}", frequency.0); + commands.spawn(PitchBundle { + source: pitch_assets.add(Pitch::new(frequency.0, Duration::new(1, 0))), + settings: PlaybackSettings::DESPAWN, + }); + info!("number of pitch assets: {}", pitch_assets.len()); + } +} + +fn keyboard_input_system( + keyboard_input: Res>, + mut frequency: ResMut, + mut events: EventWriter, +) { + if keyboard_input.just_pressed(KeyCode::Up) { + frequency.0 *= 2.0f32.powf(1.0 / 12.0); + } + if keyboard_input.just_pressed(KeyCode::Down) { + frequency.0 /= 2.0f32.powf(1.0 / 12.0); + } + if keyboard_input.just_pressed(KeyCode::Space) { + events.send(PlayPitch); + } +}