Fixes for pegasus flying wind sound effect

This fixes several aspects of the wind sound effect:
-Interpolation of FadeOutSoundInstance breaking the effect's volume controls
-Attenuation and panning (3D audio) should now work correctly for other clients
-New attenuation curve for velocity based attenuation
-New pitch controls same as above
-Made effect a continuous loop
-Added rate limiter so that sudden movements won't cause volume and pitch jolts (storms?)
This commit is contained in:
Slipstream 2022-10-03 20:30:43 +02:00
parent 69c8ceac45
commit e117fe44ea
6 changed files with 55 additions and 14 deletions

View file

@ -10,6 +10,7 @@ public interface USounds {
SoundEvent ENTITY_PLAYER_BATPONY_SCREECH = register("entity.player.batpony.screech"); SoundEvent ENTITY_PLAYER_BATPONY_SCREECH = register("entity.player.batpony.screech");
SoundEvent ENTITY_PLAYER_REBOUND = register("entity.player.rebound"); SoundEvent ENTITY_PLAYER_REBOUND = register("entity.player.rebound");
SoundEvent ENTITY_PLAYER_PEGASUS_WINGSFLAP = register("entity.player.pegasus.wingsflap"); SoundEvent ENTITY_PLAYER_PEGASUS_WINGSFLAP = register("entity.player.pegasus.wingsflap");
SoundEvent ENTITY_PLAYER_PEGASUS_FLYING = register("entity.player.pegasus.flying");
SoundEvent ENTITY_PLAYER_PEGASUS_DASH = register("entity.player.pegasus.dash"); SoundEvent ENTITY_PLAYER_PEGASUS_DASH = register("entity.player.pegasus.dash");
SoundEvent ENTITY_PLAYER_PEGASUS_MOLT = register("entity.player.pegasus.molt"); SoundEvent ENTITY_PLAYER_PEGASUS_MOLT = register("entity.player.pegasus.molt");
SoundEvent ENTITY_PLAYER_CHANGELING_BUZZ = register("entity.player.changeling.buzz"); SoundEvent ENTITY_PLAYER_CHANGELING_BUZZ = register("entity.player.changeling.buzz");

View file

@ -75,8 +75,10 @@ public class ClientInteractionManager extends InteractionManager {
PlayerPhysics physics = Pony.of(e).getPhysics(); PlayerPhysics physics = Pony.of(e).getPhysics();
return physics.isFlying() && physics.getFlightType() == FlightType.INSECTOID; return physics.isFlying() && physics.getFlightType() == FlightType.INSECTOID;
}, USounds.ENTITY_PLAYER_CHANGELING_BUZZ, 1F, 1F, Random.create(seed))); }, USounds.ENTITY_PLAYER_CHANGELING_BUZZ, 1F, 1F, Random.create(seed)));
} else if (type == SOUND_GLIDING && source instanceof PlayerEntity) { } else if (type == SOUND_GLIDING && source instanceof PlayerEntity && isClientPlayer((PlayerEntity) source)) {
soundManager.play(new MotionBasedSoundInstance(SoundEvents.ITEM_ELYTRA_FLYING, (PlayerEntity)source, Random.create(seed))); soundManager.play(new MotionBasedSoundInstance(SoundEvents.ITEM_ELYTRA_FLYING, (PlayerEntity)source, Random.create(seed)));
} else if (type == SOUND_GLIDING && source instanceof PlayerEntity) {
soundManager.play(new MotionBasedSoundInstance(USounds.ENTITY_PLAYER_PEGASUS_FLYING, (PlayerEntity)source, Random.create(seed)));
} else if (type == SOUND_MAGIC_BEAM) { } else if (type == SOUND_MAGIC_BEAM) {
soundManager.play(new LoopedEntityTrackingSoundInstance(USounds.SPELL_CAST_SHOOT, 0.3F, 1F, source, seed)); soundManager.play(new LoopedEntityTrackingSoundInstance(USounds.SPELL_CAST_SHOOT, 0.3F, 1F, source, seed));
} }

View file

@ -81,6 +81,12 @@ public abstract class FadeOutSoundInstance extends MovingSoundInstance {
progress = 0; progress = 0;
} }
protected void setVolume(float vol){
sourceVolume = vol;
targetVolume = vol;
progress = 0;
}
private float getLerpedVolume() { private float getLerpedVolume() {
float delta = MinecraftClient.getInstance().getTickDelta(); float delta = MinecraftClient.getInstance().getTickDelta();
float interpolate = MathHelper.clamp(MathHelper.lerp(delta, prevProgress, progress) / transitionTicks, 0, 1); float interpolate = MathHelper.clamp(MathHelper.lerp(delta, prevProgress, progress) / transitionTicks, 0, 1);

View file

@ -2,6 +2,7 @@ package com.minelittlepony.unicopia.client.sound;
import com.minelittlepony.unicopia.entity.player.Pony; import com.minelittlepony.unicopia.entity.player.Pony;
import net.minecraft.client.MinecraftClient;
import net.minecraft.entity.player.PlayerEntity; import net.minecraft.entity.player.PlayerEntity;
import net.minecraft.sound.SoundEvent; import net.minecraft.sound.SoundEvent;
import net.minecraft.util.math.MathHelper; import net.minecraft.util.math.MathHelper;
@ -11,18 +12,30 @@ public class MotionBasedSoundInstance extends FadeOutSoundInstance {
private final PlayerEntity player; private final PlayerEntity player;
// Tune these if you want to change the way this sounds!
// Currently just hardcoded for flying because nothing else uses this class
private static final float MAX_VELOCITY = 1.5f; // Max velocity before we clamp volume (units/tick?)
private static final float VOLUME_AT_MAX = 1.0f;
private static final float ATTEN_EXPO = 2.0f; // Exponent for velocity based attenuation
private static final int FADEIN_TICKS = 20; // Ticks for fade-in
private static final float MIN_PITCH = 0.7f; // Pitch at 0-speed
private static final float MAX_PITCH = 2.6f; // Pitch at reference speed MAX_VELOCITY
private static final float MAX_RATE_TICKS = 20.0f; // How many ticks it takes to go from 0 to max volume (filter!)
private int tickCount; private int tickCount;
private float currentVal; // Cache last tick's curve value
public MotionBasedSoundInstance(SoundEvent sound, PlayerEntity player, Random random) { public MotionBasedSoundInstance(SoundEvent sound, PlayerEntity player, Random random) {
super(sound, player.getSoundCategory(), 0.1F, random); super(sound, player.getSoundCategory(), 0.1F, random);
this.player = player; this.player = player;
currentVal = 0.0f;
} }
@Override @Override
protected boolean shouldKeepPlaying() { protected boolean shouldKeepPlaying() {
++tickCount; ++tickCount;
if (player.isRemoved() || tickCount > 200) { if (player.isRemoved()) {
return false; return false;
} }
@ -32,30 +45,41 @@ public class MotionBasedSoundInstance extends FadeOutSoundInstance {
return false; return false;
} }
// Update effect position
x = ((float)player.getX()); x = ((float)player.getX());
y = ((float)player.getY()); y = ((float)player.getY());
z = ((float)this.player.getZ()); z = ((float)player.getZ());
// Get velocity
float f = (float)player.getVelocity().horizontalLength(); float f = (float)player.getVelocity().horizontalLength();
float lastVal = currentVal;
// First we normalise the volume to the maximum velocity we're targeting, then we make it a curve.
// Drag is not linear, and neither is the woosh it produces, so a curve makes it sound more natural.
currentVal = (float) Math.pow(MathHelper.clamp(f / MAX_VELOCITY, 0, 1),ATTEN_EXPO);
// Primitive lowpass filter/rate limiter thingy to rule out sudden jolts
currentVal = lastVal + MathHelper.clamp(currentVal - lastVal, -(1/MAX_RATE_TICKS), 1/MAX_RATE_TICKS);
if (f >= 1.0E-7D) { if (f >= 1.0E-7D) {
volume = MathHelper.clamp(f / 4F, 0, 1); // Multiply output volume by reference volume for overall gain control.
volume = currentVal * VOLUME_AT_MAX;
} else { } else {
volume = 0.0F; volume = 0.0F;
} }
if (tickCount < 20) { // If we only just started playing, fade in!
volume = 0; if (tickCount < FADEIN_TICKS) {
} else if (tickCount < 40) { volume *= ((tickCount) / (float)FADEIN_TICKS);
volume = (float)(volume * ((tickCount - 20) / 20D));
} }
if (volume > 0.8F) { // Control pitch with velocity
pitch = 1 + (volume - 0.8F); pitch = MathHelper.lerp(currentVal,MIN_PITCH,MAX_PITCH);
} else {
pitch = 1;
}
setTargetVolume(volume); // Setting target volume every frame breaks interpolation. We set volume directly,
// so that FadeOutSoundInstance only handles stopping the sound with an actual fade out!
setVolume(volume);
return true; return true;
} }

View file

@ -21,6 +21,14 @@
"unicopia:woosh/woosh5" "unicopia:woosh/woosh5"
] ]
}, },
"entity.player.pegasus.flying": {
"sounds": [
{
"name": "unicopia:wind/flying_loop",
"volume": 0.6
}
]
},
"item.magic.aura": { "item.magic.aura": {
"category": "player", "category": "player",
"subtitle": "unicopia.subtitle.magic_aura", "subtitle": "unicopia.subtitle.magic_aura",