feat: Sound components can now get audio from either buffers or generators.

This commit is contained in:
Nolan Darilek 2023-03-06 13:59:53 -06:00
parent f498565215
commit 35f132d858
4 changed files with 125 additions and 39 deletions

View File

@ -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 }

View File

@ -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
View 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();
}

View File

@ -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,7 +273,6 @@ 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 {
@ -270,23 +291,34 @@ fn add_generator(
}; };
if let Some(source) = source.as_mut() { if let Some(source) = source.as_mut() {
if let Some(handle) = source.handle.as_mut() { if let Some(handle) = source.handle.as_mut() {
let generator: Option<syz::Generator> = match &sound.audio {
Audio::Buffer(buffer) => {
if let Some(b) = buffers.get(buffer) {
let generator = syz::BufferGenerator::new(&context) let generator = syz::BufferGenerator::new(&context)
.expect("Failed to create generator"); .expect("Failed to create generator");
generator.buffer().set(&**b).expect("Unable to set buffer"); 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)