Compare commits

..

No commits in common. "main" and "v0.5.0" have entirely different histories.
main ... v0.5.0

13 changed files with 310 additions and 536 deletions

47
.drone.yml Normal file
View File

@ -0,0 +1,47 @@
kind: pipeline
type: docker
name: default
environment:
DEPENDENCIES: cmake pkg-config libx11-dev libasound2-dev libudev-dev libxcb-xfixes0-dev libwayland-dev libxkbcommon-dev libvulkan-dev libpulse-dev
steps:
- name: test
image: rust:bullseye
pull: always
commands:
- apt-get update -qq
- apt-get install -qqy $DEPENDENCIES
- rustup component add clippy rustfmt
- cargo fmt --check
- cargo test
- cargo clippy
- name: release
image: rust:bullseye
pull: always
commands:
- apt-get update -qq
- apt-get install -qqy $DEPENDENCIES
- cargo publish
when:
ref:
- refs/tags/v*
environment:
CARGO_REGISTRY_TOKEN:
from_secret: cargo_registry_token
- name: discord notification
image: appleboy/drone-discord
when:
status: [success, failure]
settings:
webhook_id:
from_secret: discord_webhook_id
webhook_token:
from_secret: discord_webhook_token
tts: true
message: >
{{#success build.status}}
{{repo.name}} build {{build.number}} succeeded: <{{build.link}}>
{{else}}
{{repo.name}} build {{build.number}} failed: <{{build.link}}>
{{/success}}

1
.envrc
View File

@ -1 +0,0 @@
use flake

View File

@ -1,39 +0,0 @@
name: Release
on:
push:
tags:
- "v[0-9]+.[0-9]+.[0-9]+*"
workflow_dispatch:
jobs:
release:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/cache@v4
with:
path: ~/.cache/pre-commit
key: pre-commit|${{ env.PY }}|${{ hashFiles('.pre-commit-config.yaml') }}
- uses: actions/cache@v4
with:
path: |
~/.cargo/bin/
~/.cargo/registry/index/
~/.cargo/registry/cache/
~/.cargo/git/db/
target/
key: ${{ runner.os }}-cargo-${{ hashFiles('**/Cargo.lock') }}
- uses: dtolnay/rust-toolchain@master
with:
toolchain: stable
components: rustfmt, clippy
- name: install Linux build dependencies
run: sudo apt-get update; sudo apt-get install -y --no-install-recommends libasound2-dev libudev-dev libwayland-dev libclang-dev cmake
if: runner.os == 'linux'
- uses: actions/setup-python@v3
- uses: pre-commit/action@v3.0.1
- name: Publish
run: cargo publish
env:
CARGO_REGISTRY_TOKEN: ${{ secrets.CARGO_REGISTRY_TOKEN }}

View File

@ -1,28 +0,0 @@
name: Test
on:
pull_request:
push:
jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/cache@v4
with:
path: ~/.cache/pre-commit
key: pre-commit|${{ env.PY }}|${{ hashFiles('.pre-commit-config.yaml') }}
- uses: actions/cache@v4
with:
path: |
~/.cargo/bin/
~/.cargo/registry/index/
~/.cargo/registry/cache/
~/.cargo/git/db/
target/
key: ${{ runner.os }}-cargo-${{ hashFiles('**/Cargo.lock') }}
- name: Install Nix
uses: https://github.com/cachix/install-nix-action@v31
- name: Test
run: nix develop --command pre-commit run -a

1
.gitignore vendored
View File

@ -1,4 +1,3 @@
/target /target
*.dll *.dll
Cargo.lock Cargo.lock
.direnv

View File

@ -1,10 +0,0 @@
fail_fast: true
repos:
- repo: https://github.com/doublify/pre-commit-rust
rev: v1.0
hooks:
- id: fmt
args: [--, --check]
- id: cargo-check
args: [--bins, --examples]
- id: clippy

View File

@ -2,53 +2,6 @@
All notable changes to this project will be documented in this file. All notable changes to this project will be documented in this file.
## Version 0.10.0 - 2025-05-16
### Miscellaneous Tasks
- Add Nix-based build and CI configuration.
- Update to Bevy 0.16.
## Version 0.9.1 - 2025-01-07
### Miscellaneous Tasks
- Don't set position if values are NaN.
## Version 0.9.0 - 2024-12-06
### Miscellaneous Tasks
- Upgrade to Bevy 0.15.
## Version 0.8.0 - 2024-12-02
### Bug Fixes
- Clear generator when source is cleared, and improve handling for changing source types.
### Features
- Add `Sound.playback_position` to support initializing new buffers at non-zero playback position.
### Miscellaneous Tasks
- Clean up code.
## Version 0.7.0 - 2024-07-07
### Miscellaneous Tasks
- Add pre-commit.
- Switch to Gitea Actions.
- Upgrade to Bevy 0.14.
## Version 0.6.0 - 2024-03-14
### Miscellaneous Tasks
- Upgrade Bevy to v0.13.
## Version 0.5.0 - 2024-02-09 ## Version 0.5.0 - 2024-02-09
### Bug Fixes ### Bug Fixes

View File

@ -1,6 +1,6 @@
[package] [package]
name = "bevy_synthizer" name = "bevy_synthizer"
version = "0.10.0" version = "0.5.0"
authors = ["Nolan Darilek <nolan@thewordnerd.info>"] authors = ["Nolan Darilek <nolan@thewordnerd.info>"]
description = "A Bevy plugin for Synthizer, a library for 3D audio and synthesis with a focus on games and VR applications" description = "A Bevy plugin for Synthizer, a library for 3D audio and synthesis with a focus on games and VR applications"
license = "MIT OR Apache-2.0" license = "MIT OR Apache-2.0"
@ -10,12 +10,11 @@ repository = "https://labs.lightsout.games/projects/bevy_synthizer"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies] [dependencies]
bevy = { version = "0.16", default-features = false, features = ["bevy_asset"] } bevy = { version = "0.12", default-features = false, features = ["bevy_asset"] }
synthizer = "0.5.6" synthizer = "0.5.6"
thiserror = "2"
[dev-dependencies] [dev-dependencies]
bevy = { version = "0.16", default-features = true } bevy = { version = "0.12", default-features = true }
[package.metadata.release] [package.metadata.release]
publish = false publish = false

View File

@ -31,10 +31,14 @@ fn load_and_create(
if !listeners.is_empty() { if !listeners.is_empty() {
return; return;
} }
commands.spawn((Transform::default(), Listener, RotationTimer::default())); commands.spawn((
TransformBundle::default(),
Listener,
RotationTimer::default(),
));
let handle = asset_server.load("footstep.wav"); let handle = asset_server.load("footstep.wav");
commands.spawn(( commands.spawn((
Transform::from_translation(Vec3::new(10., 0., 0.)), TransformBundle::from(Transform::from_translation(Vec3::new(10., 0., 0.))),
Source::default(), Source::default(),
Sound { Sound {
audio: handle.into(), audio: handle.into(),
@ -47,7 +51,7 @@ fn load_and_create(
fn rotate_listener(time: Res<Time>, mut query: Query<(&mut RotationTimer, &mut Transform)>) { fn rotate_listener(time: Res<Time>, mut query: Query<(&mut RotationTimer, &mut Transform)>) {
for (mut timer, mut transform) in query.iter_mut() { for (mut timer, mut transform) in query.iter_mut() {
timer.tick(time.delta()); timer.tick(time.delta());
let angle = f32::consts::PI * 2. * timer.fraction(); let angle = f32::consts::PI * 2. * timer.percent();
transform.rotation = Quat::from_rotation_z(angle); transform.rotation = Quat::from_rotation_z(angle);
} }
} }
@ -64,6 +68,9 @@ fn main() {
)) ))
.init_resource::<AssetHandles>() .init_resource::<AssetHandles>()
.add_systems(Startup, setup) .add_systems(Startup, setup)
.add_systems(Update, (load_and_create, rotate_listener)) .add_systems(
Update,
(bevy::window::close_on_esc, load_and_create, rotate_listener),
)
.run(); .run();
} }

View File

@ -13,12 +13,16 @@ impl Default for RotationTimer {
} }
fn setup(mut commands: Commands, context: Res<Context>) { fn setup(mut commands: Commands, context: Res<Context>) {
commands.spawn((Transform::default(), Listener, RotationTimer::default())); commands.spawn((
TransformBundle::default(),
Listener,
RotationTimer::default(),
));
let generator: syz::Generator = syz::FastSineBankGenerator::new_sine(&context, 440.) let generator: syz::Generator = syz::FastSineBankGenerator::new_sine(&context, 440.)
.expect("Failed to create generator") .expect("Failed to create generator")
.into(); .into();
commands.spawn(( commands.spawn((
Transform::from_translation(Vec3::new(10., 0., 0.)), TransformBundle::from(Transform::from_translation(Vec3::new(10., 0., 0.))),
Source::default(), Source::default(),
Sound { Sound {
audio: generator.into(), audio: generator.into(),
@ -31,7 +35,7 @@ fn setup(mut commands: Commands, context: Res<Context>) {
fn rotate_listener(time: Res<Time>, mut query: Query<(&mut RotationTimer, &mut Transform)>) { fn rotate_listener(time: Res<Time>, mut query: Query<(&mut RotationTimer, &mut Transform)>) {
for (mut timer, mut transform) in query.iter_mut() { for (mut timer, mut transform) in query.iter_mut() {
timer.tick(time.delta()); timer.tick(time.delta());
let angle = f32::consts::PI * 2. * timer.fraction(); let angle = f32::consts::PI * 2. * timer.percent();
transform.rotation = Quat::from_rotation_z(angle); transform.rotation = Quat::from_rotation_z(angle);
} }
} }
@ -47,6 +51,6 @@ fn main() {
}, },
)) ))
.add_systems(Startup, setup) .add_systems(Startup, setup)
.add_systems(Update, rotate_listener) .add_systems(Update, (bevy::window::close_on_esc, rotate_listener))
.run(); .run();
} }

View File

@ -1,61 +0,0 @@
{
"nodes": {
"nixpkgs": {
"locked": {
"lastModified": 1747312588,
"narHash": "sha256-MmJvj6mlWzeRwKGLcwmZpKaOPZ5nJb/6al5CXqJsgjo=",
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "b1bebd0fe266bbd1820019612ead889e96a8fa2d",
"type": "github"
},
"original": {
"owner": "NixOS",
"ref": "nixpkgs-unstable",
"repo": "nixpkgs",
"type": "github"
}
},
"root": {
"inputs": {
"nixpkgs": "nixpkgs",
"utils": "utils"
}
},
"systems": {
"locked": {
"lastModified": 1681028828,
"narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=",
"owner": "nix-systems",
"repo": "default",
"rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e",
"type": "github"
},
"original": {
"owner": "nix-systems",
"repo": "default",
"type": "github"
}
},
"utils": {
"inputs": {
"systems": "systems"
},
"locked": {
"lastModified": 1731533236,
"narHash": "sha256-l0KFg5HjrsfsO/JpG+r7fRrqm12kzFHyUHqHCVpMMbI=",
"owner": "numtide",
"repo": "flake-utils",
"rev": "11707dc2f618dd54ca8739b309ec4fc024de578b",
"type": "github"
},
"original": {
"owner": "numtide",
"repo": "flake-utils",
"type": "github"
}
}
},
"root": "root",
"version": 7
}

View File

@ -1,54 +0,0 @@
{
inputs = {
nixpkgs.url = "github:NixOS/nixpkgs/nixpkgs-unstable";
utils.url = "github:numtide/flake-utils";
};
outputs =
{
self,
nixpkgs,
utils,
}:
utils.lib.eachDefaultSystem (
system:
let
pkgs = import nixpkgs { inherit system; };
in
{
devShell =
with pkgs;
mkShell.override { stdenv = pkgs.clangStdenv; } rec {
nativeBuildInputs = [
cargo
rustc
rustfmt
rustPackages.clippy
cmake
pkg-config
pre-commit
git-cliff
cargo-release
cargo-outdated
];
buildInputs = [
udev
alsa-lib
vulkan-loader
xorg.libX11
xorg.libXcursor
xorg.libXi
xorg.libXrandr
libxkbcommon
wayland
];
shellHook = ''
export LIBCLANG_PATH="${pkgs.libclang.lib}/lib"
export RUSTFLAGS="-C link-arg=-Wl,-rpath,${lib.makeLibraryPath buildInputs}"
pre-commit install
'';
RUST_SRC_PATH = rustPlatform.rustLibSrc;
};
}
);
}

View File

@ -2,12 +2,16 @@
use std::collections::HashMap; use std::collections::HashMap;
use bevy::{ use bevy::{
asset::{io::Reader, AssetLoader, LoadContext}, asset::{io::Reader, AssetLoader, AsyncReadExt, LoadContext},
prelude::*, prelude::*,
reflect::TypePath,
transform::TransformSystem, transform::TransformSystem,
utils::{
thiserror::{self, Error},
BoxedFuture,
},
}; };
pub use synthizer as syz; pub use synthizer as syz;
use thiserror::Error;
#[derive(Asset, Clone, Debug, Deref, DerefMut, PartialEq, Eq, TypePath)] #[derive(Asset, Clone, Debug, Deref, DerefMut, PartialEq, Eq, TypePath)]
pub struct Buffer(syz::Buffer); pub struct Buffer(syz::Buffer);
@ -29,16 +33,18 @@ impl AssetLoader for BufferAssetLoader {
type Settings = (); type Settings = ();
type Error = BufferAssetLoaderError; type Error = BufferAssetLoaderError;
async fn load( fn load<'a>(
&self, &'a self,
reader: &mut dyn Reader, reader: &'a mut Reader,
_settings: &(), _settings: &'a (),
_load_context: &mut LoadContext<'_>, _load_context: &'a mut LoadContext,
) -> Result<Self::Asset, Self::Error> { ) -> BoxedFuture<'a, Result<Self::Asset, Self::Error>> {
Box::pin(async move {
let mut bytes = Vec::new(); let mut bytes = Vec::new();
reader.read_to_end(&mut bytes).await?; reader.read_to_end(&mut bytes).await?;
let buffer = syz::Buffer::from_encoded_data(&bytes).map(Buffer)?; let buffer = syz::Buffer::from_encoded_data(&bytes).map(Buffer)?;
Ok(buffer) Ok(buffer)
})
} }
fn extensions(&self) -> &[&str] { fn extensions(&self) -> &[&str] {
@ -155,7 +161,6 @@ pub struct Sound {
pub gain: f64, pub gain: f64,
pub pitch: f64, pub pitch: f64,
pub looping: bool, pub looping: bool,
pub playback_position: f64,
pub paused: bool, pub paused: bool,
pub generator: Option<syz::Generator>, pub generator: Option<syz::Generator>,
} }
@ -167,7 +172,6 @@ impl Default for Sound {
gain: 1., gain: 1.,
pitch: 1., pitch: 1.,
looping: false, looping: false,
playback_position: default(),
paused: false, paused: false,
generator: None, generator: None,
} }
@ -188,7 +192,7 @@ fn update_listener(
context: ResMut<Context>, context: ResMut<Context>,
listener: Query<Option<&GlobalTransform>, With<Listener>>, listener: Query<Option<&GlobalTransform>, With<Listener>>,
) { ) {
if let Ok(transform) = listener.single() { if let Ok(transform) = listener.get_single() {
let transform: Transform = transform let transform: Transform = transform
.map(|v| { .map(|v| {
let transform: Transform = (*v).into(); let transform: Transform = (*v).into();
@ -230,9 +234,7 @@ fn add_source_handle(
)>, )>,
) { ) {
for (mut source, panner_strategy, transform, angular_pan, scalar_pan) in &mut query { for (mut source, panner_strategy, transform, angular_pan, scalar_pan) in &mut query {
if source.handle.is_some() { if source.handle.is_none() {
continue;
}
let panner_strategy = panner_strategy.cloned().unwrap_or_default(); let panner_strategy = panner_strategy.cloned().unwrap_or_default();
let handle: syz::Source = if let Some(transform) = transform { let handle: syz::Source = if let Some(transform) = transform {
let translation = transform.translation(); let translation = transform.translation();
@ -267,30 +269,29 @@ fn add_source_handle(
}; };
source.handle = Some(handle); source.handle = Some(handle);
} }
}
} }
fn add_generator( fn add_generator(
context: Res<Context>, context: Res<Context>,
buffers: Res<Assets<Buffer>>, buffers: Res<Assets<Buffer>>,
mut query: Query<(Entity, Option<&ChildOf>, &mut Sound)>, mut query: Query<(Entity, Option<&Parent>, &mut Sound)>,
mut sources: Query<&mut Source>, mut sources: Query<&mut Source>,
parents: Query<&ChildOf>, parents: Query<&Parent>,
) { ) {
for (entity, parent, mut sound) in &mut query { for (entity, parent, mut sound) in &mut query {
if sound.generator.is_some() { if sound.generator.is_none() {
continue;
}
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 parent.is_some() { } else if parent.is_some() {
let mut parent = parent; let mut parent = parent;
let mut target = None; let mut target = None;
while let Some(p) = parent { while let Some(p) = parent {
if sources.get(p.parent()).is_ok() { if sources.get(**p).is_ok() {
target = Some(p.parent()); target = Some(**p);
break; break;
} }
parent = parents.get(p.parent()).ok(); parent = parents.get(**p).ok();
} }
target.map(|v| sources.get_mut(v).unwrap()) target.map(|v| sources.get_mut(v).unwrap())
} else { } else {
@ -304,11 +305,6 @@ fn add_generator(
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");
assert!(sound.playback_position >= 0.);
generator
.playback_position()
.set(sound.playback_position)
.expect("Failed to set playback position");
Some(generator.into()) Some(generator.into())
} else { } else {
None None
@ -316,9 +312,7 @@ fn add_generator(
} }
Audio::Generator(generator) => Some(generator.clone()), Audio::Generator(generator) => Some(generator.clone()),
}; };
let Some(generator) = generator else { if let Some(generator) = generator {
continue;
};
assert!(sound.gain >= 0.); assert!(sound.gain >= 0.);
generator generator
.gain() .gain()
@ -336,12 +330,14 @@ fn add_generator(
} }
} }
} }
}
}
} }
fn add_sound_without_source( fn add_sound_without_source(
mut commands: Commands, mut commands: Commands,
query: Query<Entity, (Added<Sound>, Without<Source>)>, query: Query<Entity, (Added<Sound>, Without<Source>)>,
parents: Query<(&ChildOf, Option<&Source>)>, parents: Query<(&Parent, Option<&Source>)>,
) { ) {
for entity in &query { for entity in &query {
let mut has_source = false; let mut has_source = false;
@ -351,7 +347,7 @@ fn add_sound_without_source(
has_source = true; has_source = true;
break; break;
} }
target = parent.parent(); target = **parent;
} }
if !has_source { if !has_source {
commands.entity(entity).insert(Source::default()); commands.entity(entity).insert(Source::default());
@ -370,7 +366,6 @@ fn swap_buffers(
if let Some(l) = last_audio.get(&entity) { if let Some(l) = last_audio.get(&entity) {
if sound.generator.is_some() && sound.audio != *l { if sound.generator.is_some() && sound.audio != *l {
sound.generator = None; sound.generator = None;
sound.playback_position = 0.;
} }
} }
last_audio.insert(entity, sound.audio.clone()); last_audio.insert(entity, sound.audio.clone());
@ -392,13 +387,12 @@ fn change_panner_strategy(
check.push(entity); check.push(entity);
} }
for entity in check.iter() { for entity in check.iter() {
let Ok(mut source) = sources.get_mut(*entity) else { if let Ok(mut source) = sources.get_mut(*entity) {
continue;
};
if source.handle.is_some() { if source.handle.is_some() {
source.handle = None; source.handle = None;
} }
} }
}
} }
fn update_source_properties( fn update_source_properties(
@ -414,7 +408,6 @@ fn update_source_properties(
Option<&AngularPan>, Option<&AngularPan>,
Option<&ScalarPan>, Option<&ScalarPan>,
Option<&GlobalTransform>, Option<&GlobalTransform>,
Option<&mut Sound>,
)>, )>,
) { ) {
for ( for (
@ -428,20 +421,16 @@ fn update_source_properties(
angular_pan, angular_pan,
scalar_pan, scalar_pan,
transform, transform,
sound,
) in &mut query ) in &mut query
{ {
let Source { gain, .. } = *source; let Source { gain, .. } = *source;
assert!(gain >= 0.); assert!(gain >= 0.);
let Some(handle) = source.handle.as_mut() else { if let Some(handle) = source.handle.as_mut() {
continue;
};
handle.gain().set(gain).expect("Failed to set gain"); handle.gain().set(gain).expect("Failed to set gain");
let mut clear_source = false; let mut clear_source = false;
if let Some(source) = handle.cast_to::<syz::Source3D>().expect("Failed to cast") {
if let Some(transform) = transform { if let Some(transform) = transform {
if let Some(source) = handle.cast_to::<syz::Source3D>().expect("Failed to cast") {
let translation = transform.translation(); let translation = transform.translation();
if !translation.x.is_nan() && !translation.y.is_nan() && !translation.z.is_nan() {
source source
.position() .position()
.set(( .set((
@ -450,7 +439,6 @@ fn update_source_properties(
translation.z as f64, translation.z as f64,
)) ))
.expect("Failed to set position"); .expect("Failed to set position");
}
let distance_model = distance_model let distance_model = distance_model
.cloned() .cloned()
.map(|v| *v) .map(|v| *v)
@ -479,7 +467,6 @@ fn update_source_properties(
.map(|v| **v) .map(|v| **v)
.unwrap_or_else(|| context.default_rolloff().get().unwrap()); .unwrap_or_else(|| context.default_rolloff().get().unwrap());
assert!(rolloff >= 0.); assert!(rolloff >= 0.);
assert!(rolloff >= 0.);
source source
.rolloff() .rolloff()
.set(rolloff) .set(rolloff)
@ -492,9 +479,10 @@ fn update_source_properties(
.closeness_boost() .closeness_boost()
.set(closeness_boost) .set(closeness_boost)
.expect("Failed to set closeness_boost"); .expect("Failed to set closeness_boost");
let closeness_boost_distance = closeness_boost_distance let closeness_boost_distance =
.map(|v| **v) closeness_boost_distance.map(|v| **v).unwrap_or_else(|| {
.unwrap_or_else(|| context.default_closeness_boost_distance().get().unwrap()); context.default_closeness_boost_distance().get().unwrap()
});
assert!(closeness_boost_distance >= 0.); assert!(closeness_boost_distance >= 0.);
source source
.closeness_boost_distance() .closeness_boost_distance()
@ -503,11 +491,11 @@ fn update_source_properties(
} else { } else {
clear_source = true; clear_source = true;
} }
} else if let Some(source) = handle } else if let Some(angular_pan) = angular_pan {
if let Some(source) = handle
.cast_to::<syz::AngularPannedSource>() .cast_to::<syz::AngularPannedSource>()
.expect("Failed to cast") .expect("Failed to cast")
{ {
if let Some(angular_pan) = angular_pan {
assert!(angular_pan.azimuth >= 0. && angular_pan.azimuth <= 360.); assert!(angular_pan.azimuth >= 0. && angular_pan.azimuth <= 360.);
source source
.azimuth() .azimuth()
@ -521,11 +509,11 @@ fn update_source_properties(
} else { } else {
clear_source = true; clear_source = true;
} }
} else if let Some(source) = handle } else if let Some(scalar_pan) = scalar_pan {
if let Some(source) = handle
.cast_to::<syz::ScalarPannedSource>() .cast_to::<syz::ScalarPannedSource>()
.expect("Failed to cast") .expect("Failed to cast")
{ {
if let Some(scalar_pan) = scalar_pan {
assert!(**scalar_pan >= -1. && **scalar_pan <= 1.); assert!(**scalar_pan >= -1. && **scalar_pan <= 1.);
source source
.panning_scalar() .panning_scalar()
@ -534,22 +522,9 @@ fn update_source_properties(
} else { } else {
clear_source = true; clear_source = true;
} }
} else if handle
.cast_to::<syz::DirectSource>()
.expect("Failed to cast")
.is_some()
{
if transform.is_some() || angular_pan.is_some() || scalar_pan.is_some() {
clear_source = true;
}
} else if source.handle.is_some() {
clear_source = true;
} }
if clear_source { if clear_source {
source.handle = None; source.handle = None;
if let Some(mut sound) = sound {
sound.generator = None;
sound.playback_position = 0.;
} }
} }
} }
@ -565,21 +540,7 @@ fn update_sound_properties(mut query: Query<&mut Sound>) {
} = *sound; } = *sound;
assert!(gain >= 0.); assert!(gain >= 0.);
assert!(pitch > 0. && pitch <= 2.); assert!(pitch > 0. && pitch <= 2.);
let Some(generator) = &sound.generator else { if let Some(generator) = sound.generator.as_mut() {
continue;
};
if let Some(generator) = generator
.cast_to::<syz::BufferGenerator>()
.expect("Failed to cast")
{
sound.playback_position = generator
.playback_position()
.get()
.expect("Failed to getplayback position");
}
let Some(generator) = sound.generator.as_mut() else {
continue;
};
generator.gain().set(gain).expect("Failed to set gain"); generator.gain().set(gain).expect("Failed to set gain");
generator generator
.pitch_bend() .pitch_bend()
@ -595,32 +556,31 @@ fn update_sound_properties(mut query: Query<&mut Sound>) {
.expect("Failed to set looping"); .expect("Failed to set looping");
} }
} }
}
} }
fn update_source_playback_state(query: Query<&Source>) { fn update_source_playback_state(query: Query<&Source>) {
for source in &query { for source in &query {
let Some(handle) = &source.handle else { if let Some(handle) = &source.handle {
continue;
};
if source.paused { if source.paused {
handle.pause().expect("Failed to pause"); handle.pause().expect("Failed to pause");
} else { } else {
handle.play().expect("Failed to play"); handle.play().expect("Failed to play");
} }
} }
}
} }
fn update_sound_playback_state(query: Query<&Sound>) { fn update_sound_playback_state(query: Query<&Sound>) {
for sound in &query { for sound in &query {
let Some(generator) = &sound.generator else { if let Some(generator) = &sound.generator {
continue;
};
if sound.paused { if sound.paused {
generator.pause().expect("Failed to pause"); generator.pause().expect("Failed to pause");
} else { } else {
generator.play().expect("Failed to play"); generator.play().expect("Failed to play");
} }
} }
}
} }
fn remove_sound(mut last_buffer: ResMut<LastAudio>, mut removed: RemovedComponents<Sound>) { fn remove_sound(mut last_buffer: ResMut<LastAudio>, mut removed: RemovedComponents<Sound>) {
@ -699,26 +659,24 @@ fn events(
mut output: EventWriter<SynthizerEvent>, mut output: EventWriter<SynthizerEvent>,
) { ) {
context.get_events().for_each(|event| { context.get_events().for_each(|event| {
let Ok(event) = event else { if let Ok(event) = event {
return;
};
for (entity, sound) in &sounds { for (entity, sound) in &sounds {
let Some(generator) = &sound.generator else { if let Some(generator) = &sound.generator {
continue;
};
if *generator.handle() == event.source { if *generator.handle() == event.source {
match event.r#type { match event.r#type {
syz::EventType::Finished => { syz::EventType::Finished => {
output.write(SynthizerEvent::Finished(entity)); output.send(SynthizerEvent::Finished(entity));
} }
syz::EventType::Looped => { syz::EventType::Looped => {
output.write(SynthizerEvent::Looped(entity)); output.send(SynthizerEvent::Looped(entity));
} }
_ => {} _ => {}
} }
break; break;
} }
} }
}
}
}); });
} }
@ -749,10 +707,10 @@ pub struct SynthizerPlugin {
impl Plugin for SynthizerPlugin { impl Plugin for SynthizerPlugin {
fn build(&self, app: &mut App) { fn build(&self, app: &mut App) {
if !app.world().contains_resource::<SynthizerPlugin>() { if !app.world.contains_resource::<SynthizerPlugin>() {
app.insert_resource(*self); app.insert_resource(*self);
} }
let config = *app.world().get_resource::<SynthizerPlugin>().unwrap(); let config = *app.world.get_resource::<SynthizerPlugin>().unwrap();
let mut syz_config = syz::LibraryConfig::new(); let mut syz_config = syz::LibraryConfig::new();
syz_config.log_level(config.log_level); syz_config.log_level(config.log_level);
if config.log_to_stderr { if config.log_to_stderr {