blackout/src/sound/pitch_shift.rs

169 lines
5.1 KiB
Rust
Raw Normal View History

2022-02-11 17:39:50 +00:00
use std::collections::HashMap;
use bevy::prelude::*;
use crate::{
2022-05-23 18:35:25 +00:00
bevy_synthizer::{Listener, Sound},
2022-02-11 17:39:50 +00:00
commands::RunIfExistsExt,
sound::SoundIcon,
};
2022-12-19 20:08:31 +00:00
#[derive(Component, Clone, Copy, Debug, Default, Reflect)]
#[reflect(Component)]
2022-02-11 17:39:50 +00:00
struct Behind;
fn tag_behind(
mut commands: Commands,
config: Res<PitchShiftPlugin>,
2022-02-11 17:39:50 +00:00
sounds: Query<(Entity, &GlobalTransform, Option<&Sound>, Option<&SoundIcon>)>,
listener: Query<&GlobalTransform, With<Listener>>,
behind: Query<Entity, With<Behind>>,
) {
if config.downshift_behind {
if let Ok(listener_transform) = listener.get_single() {
2022-08-01 16:38:15 +00:00
let listener_forward = listener_transform.forward();
2023-03-28 17:13:23 +00:00
for (entity, transform, sound, icon) in &sounds {
2022-02-11 17:39:50 +00:00
if icon.is_none() && sound.is_none() {
continue;
}
2022-08-01 16:38:15 +00:00
let v = transform.translation() - listener_transform.translation();
2022-02-11 17:39:50 +00:00
let dot = v.dot(listener_forward);
let is_behind = dot <= 0.;
if is_behind {
commands.run_if_exists(entity, |mut entity| {
entity.insert(Behind);
});
} else if !is_behind {
commands.run_if_exists(entity, |mut entity| {
entity.remove::<Behind>();
});
}
}
}
} else {
2023-03-28 17:13:23 +00:00
for entity in &behind {
2022-02-11 17:39:50 +00:00
commands.run_if_exists(entity, |mut entity| {
entity.remove::<Behind>();
});
}
}
}
2022-12-19 20:08:31 +00:00
#[derive(Resource, Clone, Debug, Default, Deref, DerefMut)]
2022-05-23 18:35:25 +00:00
struct LastIconPitch(HashMap<Entity, f64>);
2022-02-11 17:39:50 +00:00
2022-12-19 20:08:31 +00:00
#[derive(Resource, Clone, Debug, Default, Deref, DerefMut)]
2022-05-23 18:35:25 +00:00
struct LastSoundPitch(HashMap<Entity, f64>);
2022-02-11 17:39:50 +00:00
fn behind_added(
config: Res<PitchShiftPlugin>,
2022-02-11 17:39:50 +00:00
mut last_icon_pitch: ResMut<LastIconPitch>,
mut last_sound_pitch: ResMut<LastSoundPitch>,
mut query: Query<(Entity, Option<&mut SoundIcon>, Option<&mut Sound>), Added<Behind>>,
) {
2023-03-28 17:13:23 +00:00
for (entity, icon, sound) in &mut query {
2022-02-11 17:39:50 +00:00
if let Some(mut icon) = icon {
icon.pitch *= config.downshift;
last_icon_pitch.insert(entity, icon.pitch);
} else if let Some(mut sound) = sound {
sound.pitch *= config.downshift;
last_sound_pitch.insert(entity, sound.pitch);
}
}
}
fn behind_removed(
config: Res<PitchShiftPlugin>,
2022-02-11 17:39:50 +00:00
mut last_icon_pitch: ResMut<LastIconPitch>,
mut last_sound_pitch: ResMut<LastSoundPitch>,
2023-03-28 16:57:37 +00:00
mut removed: RemovedComponents<Behind>,
2022-02-11 17:39:50 +00:00
mut icons: Query<&mut SoundIcon>,
mut sounds: Query<&mut Sound>,
) {
let downshift = 1. / config.downshift;
2023-03-28 16:57:37 +00:00
for entity in &mut removed {
2022-02-11 17:39:50 +00:00
if let Ok(mut icon) = icons.get_mut(entity) {
icon.pitch *= downshift;
last_icon_pitch.remove(&entity);
} else if let Ok(mut sound) = sounds.get_mut(entity) {
sound.pitch *= downshift;
last_sound_pitch.remove(&entity);
}
}
}
fn sound_icon_changed(
config: Res<PitchShiftPlugin>,
2022-02-11 17:39:50 +00:00
mut last_icon_pitch: ResMut<LastIconPitch>,
mut icons: Query<(Entity, &mut SoundIcon), (With<Behind>, Changed<SoundIcon>)>,
) {
2023-03-28 17:13:23 +00:00
for (entity, mut icon) in &mut icons {
2022-02-11 17:39:50 +00:00
let should_change = if let Some(pitch) = last_icon_pitch.get(&entity) {
*pitch != icon.pitch
} else {
false
};
if should_change {
icon.pitch *= config.downshift;
last_icon_pitch.insert(entity, icon.pitch);
}
}
}
fn sound_changed(
config: Res<PitchShiftPlugin>,
2022-02-11 17:39:50 +00:00
mut last_sound_pitch: ResMut<LastSoundPitch>,
mut sounds: Query<(Entity, &mut Sound), (With<Behind>, Without<SoundIcon>, Changed<Sound>)>,
) {
2023-03-28 17:13:23 +00:00
for (entity, mut sound) in &mut sounds {
2022-02-11 17:39:50 +00:00
let should_change = if let Some(pitch) = last_sound_pitch.get(&entity) {
*pitch != sound.pitch
} else {
false
};
if should_change {
sound.pitch *= config.downshift;
last_sound_pitch.insert(entity, sound.pitch);
}
}
}
fn sync_config(
mut commands: Commands,
config: Res<PitchShiftPlugin>,
2022-02-11 17:39:50 +00:00
behind: Query<Entity, With<Behind>>,
) {
if config.is_changed() {
2023-03-28 17:13:23 +00:00
for entity in &behind {
2022-02-11 17:39:50 +00:00
commands.entity(entity).remove::<Behind>();
}
}
}
#[derive(Resource, Clone, Copy, Debug)]
pub struct PitchShiftPlugin {
pub downshift_behind: bool,
pub downshift: f64,
}
impl Default for PitchShiftPlugin {
fn default() -> Self {
Self {
downshift_behind: false,
downshift: 0.95,
}
}
}
2022-02-11 17:39:50 +00:00
impl Plugin for PitchShiftPlugin {
fn build(&self, app: &mut App) {
app.insert_resource(*self)
.register_type::<Behind>()
2022-02-11 17:39:50 +00:00
.init_resource::<LastIconPitch>()
.init_resource::<LastSoundPitch>()
2023-03-28 16:57:37 +00:00
.add_system(tag_behind.in_base_set(CoreSet::PreUpdate))
.add_systems((behind_added, sound_icon_changed, sound_changed, sync_config))
.add_system(behind_removed.in_base_set(CoreSet::PostUpdate));
2022-02-11 17:39:50 +00:00
}
}