Compare commits

...

2 Commits

Author SHA1 Message Date
b381e1501f chore: Update to Bevy 0.10.
All checks were successful
continuous-integration/drone/push Build is passing
2023-03-06 14:24:23 -06:00
35f132d858 feat: Sound components can now get audio from either buffers or generators. 2023-03-06 13:59:53 -06:00
4 changed files with 171 additions and 80 deletions

View File

@ -11,11 +11,11 @@ repository = "https://labs.lightsout.games/projects/bevy_synthizer"
[dependencies]
anyhow = "1"
bevy = { version = "0.9", default-features = false, features = ["bevy_asset"] }
synthizer = "0.5"
bevy = { version = "0.10", default-features = false, features = ["bevy_asset"] }
synthizer = "0.5.6"
[dev-dependencies]
bevy = { version = "0.9", default-features = true }
bevy = { version = "0.10", default-features = true }
[package.metadata.release]
publish = false

View File

@ -31,7 +31,7 @@ fn load_and_create(
return;
}
handles.loaded = asset_server
.get_group_load_state(handles.sounds.iter().map(|handle| handle.id))
.get_group_load_state(handles.sounds.iter().map(|handle| handle.id()))
== LoadState::Loaded;
if handles.loaded {
commands.spawn((
@ -45,7 +45,7 @@ fn load_and_create(
TransformBundle::from(Transform::from_translation(Vec3::new(10., 0., 0.))),
Source::default(),
Sound {
buffer,
audio: buffer.into(),
looping: true,
..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)]
#[reflect(Component)]
#[derive(Component, Clone, Debug, PartialEq, Eq)]
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 buffer: Handle<Buffer>,
pub audio: Audio,
pub gain: f64,
pub pitch: f64,
pub looping: bool,
pub paused: bool,
pub restart: bool,
#[reflect(ignore)]
pub generator: Option<syz::Generator>,
}
impl Default for Sound {
fn default() -> Self {
Self {
buffer: default(),
audio: default(),
gain: 1.,
pitch: 1.,
looping: false,
@ -251,7 +273,6 @@ fn add_generator(
) {
for (entity, parent, mut sound) in &mut query {
if sound.generator.is_none() {
if let Some(b) = buffers.get(&sound.buffer) {
let mut source = if let Ok(s) = sources.get_mut(entity) {
Some(s)
} else if let Some(parent) = parent {
@ -270,23 +291,34 @@ fn add_generator(
};
if let Some(source) = source.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)
.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.pitch > 0. && sound.pitch <= 2.);
generator
.gain()
.set(sound.gain)
.expect("Failed to set gain");
assert!(sound.pitch > 0. && sound.pitch <= 2.);
generator
.pitch_bend()
.set(sound.pitch)
.expect("Failed to set pitch");
handle
.add_generator(&generator)
.add_generator(generator.handle())
.expect("Unable to add generator");
sound.generator = Some(generator.into());
sound.generator = Some(generator);
}
}
}
@ -316,25 +348,25 @@ fn add_sound_without_source(
}
#[derive(Resource, Default, Deref, DerefMut)]
struct LastBuffer(HashMap<Entity, Handle<Buffer>>);
struct LastAudio(HashMap<Entity, Audio>);
fn swap_buffers(
mut last_buffer: ResMut<LastBuffer>,
mut last_audio: ResMut<LastAudio>,
mut query: Query<(Entity, &mut Sound), Changed<Sound>>,
) {
for (entity, mut sound) in &mut query {
if let Some(l) = last_buffer.get(&entity) {
if sound.buffer != *l {
if let Some(l) = last_audio.get(&entity) {
if sound.audio != *l {
sound.generator = None;
}
}
last_buffer.insert(entity, sound.buffer.clone());
last_audio.insert(entity, sound.audio.clone());
}
}
fn change_panner_strategy(
changed: Query<(Entity, ChangeTrackers<PannerStrategy>)>,
removed: RemovedComponents<PannerStrategy>,
changed: Query<(Entity, Ref<PannerStrategy>)>,
mut removed: RemovedComponents<PannerStrategy>,
mut sources: Query<&mut Source>,
) {
let mut check = vec![];
@ -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>, mut removed: RemovedComponents<Source>) {
for entity in removed.iter() {
last_buffer.remove(&entity);
}
@ -659,7 +691,7 @@ fn events(
});
}
#[derive(SystemLabel, Clone, Hash, Debug, PartialEq, Eq)]
#[derive(SystemSet, Clone, Hash, Debug, PartialEq, Eq)]
pub enum SynthizerSystems {
UpdateHandles,
UpdateProperties,
@ -718,72 +750,76 @@ impl Plugin for SynthizerPlugin {
.register_type::<AngularPan>()
.register_type::<ScalarPan>()
.register_type::<Source>()
.register_type::<Sound>()
.register_type::<Listener>()
.insert_resource(guard)
.insert_resource(context)
.init_resource::<LastBuffer>()
.init_resource::<LastAudio>()
.insert_resource(defaults)
.add_event::<SynthizerEvent>()
.add_system_to_stage(CoreStage::PreUpdate, sync_config)
.add_system_to_stage(
CoreStage::PostUpdate,
swap_buffers.before(SynthizerSystems::UpdateHandles),
.add_system(sync_config.in_base_set(CoreSet::PreUpdate))
.add_system(
swap_buffers
.in_base_set(CoreSet::PostUpdate)
.before(SynthizerSystems::UpdateHandles),
)
.add_system_to_stage(
CoreStage::PostUpdate,
change_panner_strategy.before(SynthizerSystems::UpdateHandles),
.add_system(
change_panner_strategy
.in_base_set(CoreSet::PostUpdate)
.before(SynthizerSystems::UpdateHandles),
)
.add_system_to_stage(
CoreStage::PostUpdate,
.add_system(
add_source_handle
.label(SynthizerSystems::UpdateHandles)
.in_base_set(CoreSet::PostUpdate)
.in_set(SynthizerSystems::UpdateHandles)
.before(SynthizerSystems::UpdateProperties),
)
.add_system_to_stage(
CoreStage::PostUpdate,
.add_system(
add_generator
.label(SynthizerSystems::UpdateHandles)
.in_base_set(CoreSet::PostUpdate)
.in_set(SynthizerSystems::UpdateHandles)
.before(SynthizerSystems::UpdateProperties),
)
.add_system_to_stage(
CoreStage::PostUpdate,
.add_system(
add_sound_without_source
.label(SynthizerSystems::UpdateHandles)
.in_base_set(CoreSet::PostUpdate)
.in_set(SynthizerSystems::UpdateHandles)
.before(SynthizerSystems::UpdateProperties),
)
.add_system_to_stage(
CoreStage::PostUpdate,
.add_system(
update_listener
.label(SynthizerSystems::UpdateProperties)
.in_base_set(CoreSet::PostUpdate)
.in_set(SynthizerSystems::UpdateProperties)
.after(TransformSystem::TransformPropagate)
.before(SynthizerSystems::UpdateState),
)
.add_system_to_stage(
CoreStage::PostUpdate,
.add_system(
update_source_properties
.label(SynthizerSystems::UpdateProperties)
.in_base_set(CoreSet::PostUpdate)
.in_set(SynthizerSystems::UpdateProperties)
.after(TransformSystem::TransformPropagate)
.before(SynthizerSystems::UpdateState),
)
.add_system_to_stage(
CoreStage::PostUpdate,
.add_system(
update_sound_properties
.label(SynthizerSystems::UpdateProperties)
.in_base_set(CoreSet::PostUpdate)
.in_set(SynthizerSystems::UpdateProperties)
.before(SynthizerSystems::UpdateState),
)
.add_system_to_stage(
CoreStage::PostUpdate,
update_source_playback_state.label(SynthizerSystems::UpdateState),
.add_system(
update_source_playback_state
.in_base_set(CoreSet::PostUpdate)
.in_set(SynthizerSystems::UpdateState),
)
.add_system_to_stage(
CoreStage::PostUpdate,
update_sound_playback_state.label(SynthizerSystems::UpdateState),
.add_system(
update_sound_playback_state
.in_base_set(CoreSet::PostUpdate)
.in_set(SynthizerSystems::UpdateState),
)
.add_system_to_stage(CoreStage::PostUpdate, remove_sound)
.add_system_to_stage(
CoreStage::PostUpdate,
events.after(SynthizerSystems::UpdateState),
.add_system(remove_sound.in_base_set(CoreSet::PostUpdate))
.add_system(
events
.in_base_set(CoreSet::PostUpdate)
.after(SynthizerSystems::UpdateState),
);
}
}