feat: Sound
components can now get audio from either buffers or generators.
This commit is contained in:
parent
f498565215
commit
35f132d858
|
@ -12,7 +12,7 @@ repository = "https://labs.lightsout.games/projects/bevy_synthizer"
|
||||||
[dependencies]
|
[dependencies]
|
||||||
anyhow = "1"
|
anyhow = "1"
|
||||||
bevy = { version = "0.9", default-features = false, features = ["bevy_asset"] }
|
bevy = { version = "0.9", default-features = false, features = ["bevy_asset"] }
|
||||||
synthizer = "0.5"
|
synthizer = "0.5.6"
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
bevy = { version = "0.9", default-features = true }
|
bevy = { version = "0.9", default-features = true }
|
||||||
|
|
|
@ -45,7 +45,7 @@ fn load_and_create(
|
||||||
TransformBundle::from(Transform::from_translation(Vec3::new(10., 0., 0.))),
|
TransformBundle::from(Transform::from_translation(Vec3::new(10., 0., 0.))),
|
||||||
Source::default(),
|
Source::default(),
|
||||||
Sound {
|
Sound {
|
||||||
buffer,
|
audio: buffer.into(),
|
||||||
looping: true,
|
looping: true,
|
||||||
..default()
|
..default()
|
||||||
},
|
},
|
55
examples/generator.rs
Normal file
55
examples/generator.rs
Normal file
|
@ -0,0 +1,55 @@
|
||||||
|
use std::f32;
|
||||||
|
|
||||||
|
use bevy::prelude::*;
|
||||||
|
use bevy_synthizer::*;
|
||||||
|
|
||||||
|
#[derive(Component, Deref, DerefMut)]
|
||||||
|
struct RotationTimer(Timer);
|
||||||
|
|
||||||
|
impl Default for RotationTimer {
|
||||||
|
fn default() -> Self {
|
||||||
|
Self(Timer::from_seconds(30., TimerMode::Repeating))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn setup(mut commands: Commands, context: Res<Context>) {
|
||||||
|
commands.spawn((
|
||||||
|
TransformBundle::default(),
|
||||||
|
Listener,
|
||||||
|
RotationTimer::default(),
|
||||||
|
));
|
||||||
|
let generator: syz::Generator = syz::FastSineBankGenerator::new_sine(&context, 440.)
|
||||||
|
.expect("Failed to create generator")
|
||||||
|
.into();
|
||||||
|
commands.spawn((
|
||||||
|
TransformBundle::from(Transform::from_translation(Vec3::new(10., 0., 0.))),
|
||||||
|
Source::default(),
|
||||||
|
Sound {
|
||||||
|
audio: generator.into(),
|
||||||
|
looping: true,
|
||||||
|
..default()
|
||||||
|
},
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
fn rotate_listener(time: Res<Time>, mut query: Query<(&mut RotationTimer, &mut Transform)>) {
|
||||||
|
for (mut timer, mut transform) in query.iter_mut() {
|
||||||
|
timer.tick(time.delta());
|
||||||
|
let angle = f32::consts::PI * 2. * timer.percent();
|
||||||
|
transform.rotation = Quat::from_rotation_z(angle);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
App::new()
|
||||||
|
.add_plugins(DefaultPlugins)
|
||||||
|
.add_plugin(SynthizerPlugin {
|
||||||
|
default_panner_strategy: Some(bevy_synthizer::syz::PannerStrategy::Hrtf),
|
||||||
|
default_distance_model: Some(bevy_synthizer::syz::DistanceModel::Inverse),
|
||||||
|
..default()
|
||||||
|
})
|
||||||
|
.add_system(bevy::window::close_on_esc)
|
||||||
|
.add_startup_system(setup)
|
||||||
|
.add_system(rotate_listener)
|
||||||
|
.run();
|
||||||
|
}
|
105
src/lib.rs
105
src/lib.rs
|
@ -122,23 +122,45 @@ impl ScalarPan {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Component, Clone, Debug, Reflect)]
|
#[derive(Component, Clone, Debug, PartialEq, Eq)]
|
||||||
#[reflect(Component)]
|
pub enum Audio {
|
||||||
|
Buffer(Handle<Buffer>),
|
||||||
|
Generator(syz::Generator),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for Audio {
|
||||||
|
fn default() -> Self {
|
||||||
|
Self::Buffer(default())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<Handle<Buffer>> for Audio {
|
||||||
|
fn from(value: Handle<Buffer>) -> Self {
|
||||||
|
Audio::Buffer(value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<syz::Generator> for Audio {
|
||||||
|
fn from(value: syz::Generator) -> Self {
|
||||||
|
Self::Generator(value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Component, Clone, Debug)]
|
||||||
pub struct Sound {
|
pub struct Sound {
|
||||||
pub buffer: Handle<Buffer>,
|
pub audio: Audio,
|
||||||
pub gain: f64,
|
pub gain: f64,
|
||||||
pub pitch: f64,
|
pub pitch: f64,
|
||||||
pub looping: bool,
|
pub looping: bool,
|
||||||
pub paused: bool,
|
pub paused: bool,
|
||||||
pub restart: bool,
|
pub restart: bool,
|
||||||
#[reflect(ignore)]
|
|
||||||
pub generator: Option<syz::Generator>,
|
pub generator: Option<syz::Generator>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Default for Sound {
|
impl Default for Sound {
|
||||||
fn default() -> Self {
|
fn default() -> Self {
|
||||||
Self {
|
Self {
|
||||||
buffer: default(),
|
audio: default(),
|
||||||
gain: 1.,
|
gain: 1.,
|
||||||
pitch: 1.,
|
pitch: 1.,
|
||||||
looping: false,
|
looping: false,
|
||||||
|
@ -251,42 +273,52 @@ fn add_generator(
|
||||||
) {
|
) {
|
||||||
for (entity, parent, mut sound) in &mut query {
|
for (entity, parent, mut sound) in &mut query {
|
||||||
if sound.generator.is_none() {
|
if sound.generator.is_none() {
|
||||||
if let Some(b) = buffers.get(&sound.buffer) {
|
let mut source = if let Ok(s) = sources.get_mut(entity) {
|
||||||
let mut source = if let Ok(s) = sources.get_mut(entity) {
|
Some(s)
|
||||||
Some(s)
|
} else if let Some(parent) = parent {
|
||||||
} else if let Some(parent) = parent {
|
let mut parent: Option<&Parent> = Some(parent);
|
||||||
let mut parent: Option<&Parent> = Some(parent);
|
let mut target = None;
|
||||||
let mut target = None;
|
while let Some(p) = parent {
|
||||||
while let Some(p) = parent {
|
if sources.get(**p).is_ok() {
|
||||||
if sources.get(**p).is_ok() {
|
target = Some(**p);
|
||||||
target = Some(**p);
|
break;
|
||||||
break;
|
|
||||||
}
|
|
||||||
parent = parents.get(**p).ok();
|
|
||||||
}
|
}
|
||||||
target.map(|v| sources.get_mut(v).unwrap())
|
parent = parents.get(**p).ok();
|
||||||
} else {
|
}
|
||||||
None
|
target.map(|v| sources.get_mut(v).unwrap())
|
||||||
};
|
} else {
|
||||||
if let Some(source) = source.as_mut() {
|
None
|
||||||
if let Some(handle) = source.handle.as_mut() {
|
};
|
||||||
let generator = syz::BufferGenerator::new(&context)
|
if let Some(source) = source.as_mut() {
|
||||||
.expect("Failed to create generator");
|
if let Some(handle) = source.handle.as_mut() {
|
||||||
generator.buffer().set(&**b).expect("Unable to set buffer");
|
let generator: Option<syz::Generator> = match &sound.audio {
|
||||||
|
Audio::Buffer(buffer) => {
|
||||||
|
if let Some(b) = buffers.get(buffer) {
|
||||||
|
let generator = syz::BufferGenerator::new(&context)
|
||||||
|
.expect("Failed to create generator");
|
||||||
|
generator.buffer().set(&**b).expect("Unable to set buffer");
|
||||||
|
Some(generator.into())
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Audio::Generator(generator) => Some(generator.clone()),
|
||||||
|
};
|
||||||
|
if let Some(generator) = generator {
|
||||||
assert!(sound.gain >= 0.);
|
assert!(sound.gain >= 0.);
|
||||||
assert!(sound.pitch > 0. && sound.pitch <= 2.);
|
|
||||||
generator
|
generator
|
||||||
.gain()
|
.gain()
|
||||||
.set(sound.gain)
|
.set(sound.gain)
|
||||||
.expect("Failed to set gain");
|
.expect("Failed to set gain");
|
||||||
|
assert!(sound.pitch > 0. && sound.pitch <= 2.);
|
||||||
generator
|
generator
|
||||||
.pitch_bend()
|
.pitch_bend()
|
||||||
.set(sound.pitch)
|
.set(sound.pitch)
|
||||||
.expect("Failed to set pitch");
|
.expect("Failed to set pitch");
|
||||||
handle
|
handle
|
||||||
.add_generator(&generator)
|
.add_generator(generator.handle())
|
||||||
.expect("Unable to add generator");
|
.expect("Unable to add generator");
|
||||||
sound.generator = Some(generator.into());
|
sound.generator = Some(generator);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -316,19 +348,19 @@ fn add_sound_without_source(
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Resource, Default, Deref, DerefMut)]
|
#[derive(Resource, Default, Deref, DerefMut)]
|
||||||
struct LastBuffer(HashMap<Entity, Handle<Buffer>>);
|
struct LastAudio(HashMap<Entity, Audio>);
|
||||||
|
|
||||||
fn swap_buffers(
|
fn swap_buffers(
|
||||||
mut last_buffer: ResMut<LastBuffer>,
|
mut last_audio: ResMut<LastAudio>,
|
||||||
mut query: Query<(Entity, &mut Sound), Changed<Sound>>,
|
mut query: Query<(Entity, &mut Sound), Changed<Sound>>,
|
||||||
) {
|
) {
|
||||||
for (entity, mut sound) in &mut query {
|
for (entity, mut sound) in &mut query {
|
||||||
if let Some(l) = last_buffer.get(&entity) {
|
if let Some(l) = last_audio.get(&entity) {
|
||||||
if sound.buffer != *l {
|
if sound.audio != *l {
|
||||||
sound.generator = None;
|
sound.generator = None;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
last_buffer.insert(entity, sound.buffer.clone());
|
last_audio.insert(entity, sound.audio.clone());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -557,7 +589,7 @@ fn update_sound_playback_state(query: Query<&Sound>) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn remove_sound(mut last_buffer: ResMut<LastBuffer>, removed: RemovedComponents<Source>) {
|
fn remove_sound(mut last_buffer: ResMut<LastAudio>, removed: RemovedComponents<Source>) {
|
||||||
for entity in removed.iter() {
|
for entity in removed.iter() {
|
||||||
last_buffer.remove(&entity);
|
last_buffer.remove(&entity);
|
||||||
}
|
}
|
||||||
|
@ -718,11 +750,10 @@ impl Plugin for SynthizerPlugin {
|
||||||
.register_type::<AngularPan>()
|
.register_type::<AngularPan>()
|
||||||
.register_type::<ScalarPan>()
|
.register_type::<ScalarPan>()
|
||||||
.register_type::<Source>()
|
.register_type::<Source>()
|
||||||
.register_type::<Sound>()
|
|
||||||
.register_type::<Listener>()
|
.register_type::<Listener>()
|
||||||
.insert_resource(guard)
|
.insert_resource(guard)
|
||||||
.insert_resource(context)
|
.insert_resource(context)
|
||||||
.init_resource::<LastBuffer>()
|
.init_resource::<LastAudio>()
|
||||||
.insert_resource(defaults)
|
.insert_resource(defaults)
|
||||||
.add_event::<SynthizerEvent>()
|
.add_event::<SynthizerEvent>()
|
||||||
.add_system_to_stage(CoreStage::PreUpdate, sync_config)
|
.add_system_to_stage(CoreStage::PreUpdate, sync_config)
|
||||||
|
|
Loading…
Reference in New Issue
Block a user